/* replace.c by Michael Thorpe 2019-09-20 */ /* * This program opens a file and execs a subprogram. The subprogram is * given pipes for stdin and stdout, which this program uses to send the * file to the subprogram and get data in return. This program replaces * the data in the file with the data returned from the subprogram, being * careful not to overwrite the original data until it has been fed to the * subprogram. If the subprogram exits with an error value or on a signal * and no data has yet been returned from it, the file will not be changed. * Otherwise, the file is truncated after the subprogram exits if more was * read than was written and the -t option was not given. * * Example usage: replace largefile grep -v lines_i_dont_care_about */ #include #include #include #include #include #include #include #include #include #include #include #define BUF_SIZE 16384 struct buffer { struct buffer *next; char *buf; size_t start,stop; }; struct bufhead { int fin,fout; struct buffer *first,*last; }; const char usage[]="usage: replace [-fht] []\n"; const char help[]= " -f truncate file even if program exits non-zero\n" " -h show this help message\n" " -t don't truncate file on subprogram exit\n"; /* This is the pid and exit status of the child. */ pid_t child=0; int child_status=0; /* This comes into play if we get SIGCHLD before recording the child pid */ int need_to_waitpid=0; /* Positions for reading and writing in the file, and the current position. */ size_t readpos=0,writepos=0,curpos=0; /* These are the buffers between to/from the subprogram. */ struct bufhead inbuf,outbuf; /* Have we reached end-of-file? */ int file_eof=0; /* Have we read all that the subprogram has written? */ int subprog_eof=0; /* This controls whether we truncate the file after a non-zero subprogram exit. */ int force_truncate=0; /* This controls whether we truncate the file after the subprogram exits. */ int no_truncate=0; /* * This function returns the index of the first non-option argument, or 0 on * success-but-don't-run or -1 on error. It also makes sure there are at * least 2 non-option arguments. */ int parse_args(int argc,char **argv) { int i,j; for(i=1;ibuf=(char *)malloc(BUF_SIZE); if(!sb->buf) return(-1); if(isfile && curpos != readpos) { if(readpos != lseek(bh->fin,readpos,SEEK_SET)) return(-1); curpos=readpos; } sb->stop=read(bh->fin,sb->buf,BUF_SIZE); if(sb->stop==-1) { perror("read"); return(-1); } if(sb->stop==0) { free(sb->buf); free(sb); if(isfile) file_eof=1; else subprog_eof=1; } else { if(isfile) { readpos+=sb->stop; curpos=readpos; } sb->next=0; sb->start=0; if(bh->last) bh->last->next=sb; bh->last=sb; if(!bh->first) bh->first=sb; } return(0); } /* * Try to write out the buffer. !!isfile if read/writepos should be obeyed. */ int barf(struct bufhead *bh,int isfile) { size_t l; struct buffer *sb; if(!bh->first) return(0); l=bh->first->stop-bh->first->start; if(isfile && !file_eof && l>(readpos-writepos)) l=readpos-writepos; if(!l) return(0); if(isfile && curpos != writepos) { if(writepos != lseek(bh->fout,writepos,SEEK_SET)) return(-1); curpos=writepos; } l=write(bh->fout,bh->first->buf+bh->first->start,l); if(l==-1) { if(errno==EINTR) return(0); return(-1); } bh->first->start+=l; if(isfile) { writepos+=l; curpos=writepos; } if(bh->first->start==bh->first->stop) { free(bh->first->buf); sb=bh->first->next; free(bh->first); bh->first=sb; if(!sb) bh->last=0; } return(0); } /* * This space for rent. */ int do_it() { fd_set r,w; int i,maxfd; while(child || !subprog_eof) { FD_ZERO(&r); FD_SET(outbuf.fin,&r); maxfd=outbuf.fin; if(!file_eof) { FD_ZERO(&w); FD_SET(inbuf.fout,&w); if(maxfdwritepos) if(ftruncate(file,writepos)) return(-1); if(close(file)) return(-1); return(child_status); }