/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */ /* The copyright notice above does not evidence any */ /* actual or intended publication of such source code. */ #ident "@(#)bdiff:bdiff.c 5.11" #include #include #include #include #include #define ONSIG 16 #if sgi /* * Taken stright from * Since is in the SCCS directory this was * easier and cleaner. */ # define FTLMSG 0100000 # define FTLCLN 040000 # define FTLEXIT 01 #endif char *satoi(); char *Mesg[ONSIG]={ 0, 0, /* Hangup */ 0, /* Interrupt */ 0, /* Quit */ "Illegal instruction", "Trace/BPT trap", "IOT trap", "EMT trap", "Floating exception", "Killed", "Bus error", "Memory fault", "Bad system call", "Broken pipe", "Alarm clock" }; /* This program segments two files into pieces of <= seglim lines (which is passed as a third argument or defaulted to some number) and then executes diff upon the pieces. The output of 'diff' is then processed to make it look as if 'diff' had processed the files whole. The reason for all this is that seglim is a reasonable upper limit on the size of files that diff can process. NOTE -- by segmenting the files in this manner, it cannot be guaranteed that the 'diffing' of the segments will generate a minimal set of differences. This process is most definitely not equivalent to 'diffing' the files whole, assuming 'diff' could handle such large files. 'diff' is executed by a child process, generated by forking, and communicates with this program through pipes. */ char Error[128]; int seglim; /* limit of size of file segment to be generated */ FILE *fdopen(); char diff[] = "diff"; char tempskel[] = "/tmp/bdXXXXX"; /* used to generate temp file names */ char tempfile[32]; char otmp[32], ntmp[32]; char *Ffile; int Fflags, olinenum, nlinenum; char *prognam; main(argc,argv) int argc; char *argv[]; { FILE *poldfile, *pnewfile, *maket(), *carryon(); char oline[BUFSIZ], nline[BUFSIZ], diffline[BUFSIZ]; char *olp, *nlp, *dp; int otcnt, ntcnt; pid_t i; int pfd[2]; FILE *poldtemp, *pnewtemp, *pipeinp; int status; char type; prognam = argv[0]; /* Set flags for 'fatal' so that it will clean up, produce a message, and terminate. */ Fflags = FTLMSG | FTLCLN | FTLEXIT; setsig(); if (argc < 3 || argc > 5) fatal("arg count"); if (strcmp(argv[1],"-") == 0 && strcmp(argv[2],"-") == 0) fatal("both files standard input"); if (strcmp(argv[1],"-") == 0) poldfile = stdin; else if((poldfile = fopen(argv[1],"r")) == NULL) { sprintf(Error, "Can not open '%s'", argv[1]); fatal(Error); } if (strcmp(argv[2],"-") == 0) pnewfile = stdin; else if((pnewfile = fopen(argv[2], "r")) == NULL) { sprintf(Error, "Can not open '%s'", argv[2]); fatal(Error); } seglim = 3500; if (argc > 3) { if (argv[3][0] == '-' && argv[3][1] == 's') Fflags &= ~FTLMSG; else { if ((seglim = atoi(argv[3])) == 0) fatal("non-numeric limit"); if (argc == 5 && argv[4][0] == '-' && argv[4][1] == 's') Fflags &= ~FTLMSG; } } olinenum = nlinenum = 0; /* The following while-loop will prevent any lines common to the beginning of both files from being sent to 'diff'. Since the running time of 'diff' is non-linear, this will help improve performance. If, during this process, both files reach EOF, then the files are equal and the program will terminate. If either file reaches EOF before the other, the program will generate the appropriate 'diff' output itself, since this can be easily determined and will avoid executing 'diff' completely. */ while (1) { olp = fgets(oline,BUFSIZ,poldfile); nlp = fgets(nline,BUFSIZ,pnewfile); if (!olp && !nlp) /* files are e == 0qual */ exit(0); if (!olp) { /* The entire old file is a prefix of the new file. Generate the appropriate "append" 'diff'-like output, which is of the form: nan,n where 'n' represents a line-number. */ addgen(nline,pnewfile, 0, NULL, NULL); } if (!nlp) { /* The entire new file is a prefix of the old file. Generate the appropriate "delete" 'diff'-like output, which is of the form: n,ndn where 'n' represents a line-number. */ delgen(oline,poldfile, 0, NULL, NULL); } if (strcmp(olp,nlp) == 0) { olinenum++; nlinenum++; } else break; } /* Here, first 'olinenum' lines are equal. The following while-loop segments both files into seglim segments, forks and executes 'diff' on the segments, and processes the resulting output of 'diff', which is read from a pipe. */ otcnt = ntcnt = 0; while (1) { /* If both files are at EOF, everything is done. */ if (!olp && !otcnt && !nlp && !ntcnt) /* finished */ exit(0); if (!olp && !otcnt) { /* Generate appropriate "append" output without executing 'diff'. */ addgen(nline,pnewfile, ntcnt, pnewtemp, ntmp); } if (!nlp && !ntcnt) { /* Generate appropriate "delete" output without executing 'diff'. */ delgen(oline,poldfile, otcnt, poldtemp, otmp); } /* If no line is carried on from the previous segment, create a temporary file to hold a segment from the old file, and write it. */ if (otcnt == 0) { poldtemp = maket(otmp); otcnt = 0; } while(olp && otcnt < seglim) { fputs(oline,poldtemp); if (ferror(poldtemp) != 0) { Fflags |= FTLMSG; fatal("Can not write to temporary file"); } olp = fgets(oline,BUFSIZ,poldfile); otcnt++; } fclose(poldtemp); /* If no line is carried on from the previous segment, create a temporary file to hold a segment from the new file, and write it. */ if (ntcnt == 0) { pnewtemp = maket(ntmp); ntcnt = 0; } while(nlp && ntcnt < seglim) { fputs(nline,pnewtemp); if (ferror(pnewtemp) != 0) { Fflags |= FTLMSG; fatal("Can not write to temporary file"); } nlp = fgets(nline,BUFSIZ,pnewfile); ntcnt++; } fclose(pnewtemp); /* Create pipes and fork. */ if((pipe(pfd)) == -1) fatal("Can not create pipe"); if ((i = fork()) < (pid_t)0) { close(pfd[0]); close(pfd[1]); fatal("Can not fork, try again"); } else if (i == (pid_t)0) { /* child process */ close(pfd[0]); close(1); dup(pfd[1]); close(pfd[1]); /* Execute 'diff' on the segment files. */ execlp(diff,diff,otmp,ntmp,0); close(1); sprintf(Error,"Can not execute '%s'",diff); fatal(Error); } else { /* parent process */ close(pfd[1]); pipeinp = fdopen(pfd[0], "r"); /* Process 'diff' output. */ while ((dp = fgets(diffline,BUFSIZ,pipeinp))) { if (isdigit(*dp)) { if (fixnum(diffline,&type)) break; } else printf("%s",diffline); } /* Remove temporary files. */ unlink(otmp); unlink(ntmp); olinenum += otcnt; nlinenum += ntcnt; otcnt = ntcnt = 0; if (dp) { switch (type) { case 'a': pnewtemp = carryon(pipeinp,ntmp, &ntcnt,&nlinenum,0); break; case 'd': poldtemp = carryon(pipeinp,otmp, &otcnt,&olinenum,0); break; case 'c': poldtemp = carryon(pipeinp,otmp, &otcnt,&olinenum,1); pnewtemp = carryon(pipeinp,ntmp, &ntcnt,&nlinenum,0); } } fclose(pipeinp); /* EOF on pipe. */ wait(&status); if (status&~0x100) { sprintf(Error,"'%s' failed",diff); fatal(Error); } } } } /* Routine to save remainder of a file. */ saverest(line,iptr,linenum, tcnt, tfp, tfn) char *line, *tfn; FILE *iptr, *tfp; int *linenum, tcnt; { register char *lp; FILE *temptr, *maket(); if (tcnt) { temptr = tfp; strcpy(tempfile, tfn); *linenum += tcnt; } else { temptr = maket(tempfile); } lp = line; while (lp) { fputs(line,temptr); (*linenum)++; lp = fgets(line,BUFSIZ,iptr); } fclose(temptr); } /* Routine to write out data saved by 'saverest' routine and to remove the file. */ putsave(line,type) char *line; char type; { FILE *temptr; if((temptr = fopen(tempfile, "r")) == NULL) { sprintf(Error, "Can not open tempfile ('%s')", tempfile); fatal(Error); } while (fgets(line,BUFSIZ,temptr)) printf("%c %s",type,line); fclose(temptr); unlink(tempfile); } fixnum(lp,type) char *lp, *type; { int nums[4], *num, comma; comma = nums[2] = nums[3] = 0; *type = ' '; num = nums; while (*lp) { switch (*lp) { case 'a': case 'c': case 'd': *type = *lp; lp++; num++; break; case ',': if (*type == ' ') comma = 1; else if (comma == 0) comma = 2; else comma = 3; lp++; num++; break; case '\n': lp++; break; default: lp = satoi(lp,num); } } /* Check the diff output line numbers to see if they reach the segment boundary. */ if (nums[0] == seglim || nums[1] == seglim || nums[2] == seglim || nums[3] == seglim) { /* Check if the whole segment is different from the other segment. */ if (!((nums[0] == 1 && nums[1] == seglim) || (nums[1] == 1 && nums[2] == seglim) || (nums[2] == 1 && nums[3] == seglim))) /* Need to carry the last diff output to next segment to avoid redundant diff output due to insert/delete lines. */ return(1); } /* Print out fixed line numbers */ switch (comma) { case 0: printf("%d%c%d\n",nums[0]+olinenum,*type,nums[1]+nlinenum); break; case 1: printf("%d,%d%c%d\n",nums[0]+olinenum,nums[1]+olinenum,*type, nums[2]+nlinenum); break; case 2: printf("%d%c%d,%d\n",nums[0]+olinenum,*type,nums[1]+nlinenum, nums[2]+nlinenum); break; case 3: printf("%d,%d%c%d,%d\n",nums[0]+olinenum,nums[1]+olinenum, *type,nums[2]+nlinenum,nums[3]+nlinenum); } return(0); } FILE * carryon(pipeinp,fn,cnt,linenum,check) FILE *pipeinp; char *fn; int *cnt, *linenum, check; /* Carry the diff output which reaches the segment boundary. */ { FILE *fp, *maket(); char *dp, linebuf[BUFSIZ]; fp = maket(fn); while ((dp = fgets(linebuf,BUFSIZ,pipeinp))) { if (check) if (*dp == '-') break; fputs(linebuf+2,fp); if (ferror(fp) != 0) { Fflags |= FTLMSG; fatal("Can not write to temporary file"); } (*cnt)++; } *linenum -= *cnt; return(fp); } addgen(lp,fp, tcnt, tfp, tfn) char *lp, *tfn; int tcnt; FILE *fp, *tfp; { int savenum; savenum = nlinenum + 1; printf("%da%d", olinenum, savenum); /* Save lines of new file. */ saverest(lp,fp,&nlinenum, tcnt, tfp, tfn); if (savenum < nlinenum) printf(",%d\n",nlinenum); else printf("\n"); /* Output saved lines, as 'diff' would. */ putsave(lp,'>'); exit(0); } delgen(lp,fp, tcnt, tfp, tfn) char *lp, *tfn; int tcnt; FILE *fp, *tfp; { int savenum; savenum = olinenum + 1; printf("%d", savenum); /* Save lines of old file. */ saverest(lp,fp,&olinenum, tcnt, tfp, tfn); if (savenum < olinenum) printf(",%dd%d\n", olinenum, nlinenum); else printf("d%d\n", nlinenum); /* Output saved lines, as 'diff' would. */ putsave(lp,'<'); exit(0); } clean_up() { unlink(tempfile); unlink(otmp); unlink(ntmp); } FILE * maket(file) char *file; { FILE *iop; register char *cp; char *mktemp(); strcpy(file, tempskel); cp = mktemp(file); if ((iop = fopen(cp, "w+")) == NULL) { sprintf(Error, "Can not open/create temp file ('%s')", cp); fatal(Error); } return(iop); } fatal(msg) char *msg; /* General purpose error handler. The argument to fatal is a pointer to an error message string. The action of this routine is driven completely from the "Fflags" global word (see ). The FTLMSG bit controls the writing of the error message on file descriptor 2. The message is preceded by the string "ERROR: ", unless the global character pointer "Ffile" is non-zero, in which case the message is preceded by the string "ERROR []: ". A newline is written after the user supplied message. If the FTLCLN bit is on, clean_up is called with an argument of 0. */ { if (Fflags & FTLMSG) fprintf(stderr, "%s: %s\n", prognam, msg); if (Fflags & FTLCLN) clean_up(); if (Fflags & FTLEXIT) exit(1); } setsig() /* General-purpose signal setting routine. All non-ignored, non-caught signals are caught. If a signal other than hangup, interrupt, or quit is caught, a "user-oriented" message is printed on file descriptor 2. If hangup, interrupt or quit is caught, that signal is set to ignore. Termination is like that of "fatal", via "clean_up(sig)" (sig is the signal number). */ { void setsig1(); register int j, n; for (j=1; j < ONSIG; j++) { n = (int) signal(j, setsig1); if ((void(*)())n == SIG_ERR) continue; if ((void(*)())n == SIG_DFL) continue; signal(j, (void(*)())n); } } void setsig1(sig) int sig; { signal(sig, SIG_IGN); clean_up(); exit(1); } char *satoi(p,ip) register char *p; register int *ip; { register int sum; sum = 0; while (isdigit(*p)) sum = sum * 10 + (*p++ - '0'); *ip = sum; return(p); }