diff.c

Go to the documentation of this file.
00001 /* diff  - print differences between 2 files      Author: Erik Baalbergen */
00002 
00003 /* Poor man's implementation of diff(1)         - no options available
00004 *       - may give more output than other diffs,
00005 *         due to the straight-forward algorithm
00006 *       - runs out of memory if the differing chunks become too large
00007 *       - input line length should not exceed LINELEN; longer lines are
00008 *         truncated, while only the first LINELEN characters are compared
00009 *
00010 *       - Bug fixes by Rick Thomas Sept. 1989
00011 *
00012 * Please report bugs and suggestions to erikb@cs.vu.nl
00013 *------------------------------------------------------------------------------
00014 * Changed diff to conform to POSIX 1003.2 ( Draft 11) by Thomas Brupbacher
00015 * ( tobr@mw.lpc.ethz.ch).
00016 *
00017 * To incorporate the context diff option -c in the program, the source code
00018 * for the program cdiff has been copied to the end of this program. Only
00019 * slight modifications for the cdiff code to work within the program diff
00020 * were made( e.g. main() -> context_diff()).
00021 *
00022 * New options:
00023 * -c, -C n where n=0,1,...:
00024 *       produces a context diff as the program cdiff. The default is to
00025 *       print 3 lines of context, this value can be changed with -C
00026 *       ( e.g. -C 5 prints five lines of context.)
00027 * -e :  Prints an ed script, so you can convert <file1> to <file2> with
00028 *       the command ed <file1> < `diff -e <file1> <file2>`.
00029 * -b :  Causes trailing blanks to be ignored and spaces of multiple blanks
00030 *       to be reduced to one blank before comparison.
00031 *-----------------------------------------------------------------------------
00032 */
00033 
00034 #include <errno.h>
00035 #include <stdlib.h>
00036 #include <limits.h>             /* NAME_MAX for maximal filename length  */
00037 #include <string.h>             /* string manipulation                   */
00038 #include <sys/types.h>
00039 #include <sys/stat.h>
00040 #include <sys/wait.h>
00041 #include <ctype.h>
00042 #include <time.h>
00043 #include <dirent.h>
00044 #include <unistd.h>
00045 #include <stdio.h>
00046 
00047 /* These definitions are needed only to suppress warning messages. */
00048 #define Nullfp          ((FILE*)0)
00049 #define Nullch          ((char*)0)
00050 #define NullStructLine  ((struct line *)0)
00051 
00052 #define LINELEN 128             /* max line length included in diff      */
00053 
00054 
00055 #define NOT_SET 0               /* Defines to characterise if a flag     */
00056 #define SET     1               /* is set                                */
00057 
00058  /* Indexes of the warning-message array         */
00059 #define EXCLUSIVE_OPTIONS       0
00060 #define CANNOT_OPEN_FILE        1
00061 
00062  /* Used to define the mode              */
00063 typedef enum {
00064   undefined, context, ed_mode
00065 } MODE;
00066 
00067  /* Global variables for the 'normal' diff part  */
00068 char *progname;                 /* program name (on command line)        */
00069 int diffs = 0;                  /* number of differences                 */
00070 MODE mode;                      /* which mode is used                    */
00071 int severe_error;               /* nonzero after severe, non-fatal error */
00072 
00073 /* The following global variables are used with the -r option:
00074  * for every pair of files that are different, a "command line" of the
00075  * form "diff <options> <oldfile> <newfile>" is printed before the real
00076  * output starts.                                                        */
00077 int firstoutput = 1;            /* flag to print one time                */
00078 char options_string[10];        /* string to hold command line options   */
00079 char oldfile[PATH_MAX];         /* first file                            */
00080 char newfile[PATH_MAX];         /* second file                           */
00081 
00082 
00083  /* Global variables for the command-line options */
00084 int trim_blanks = NOT_SET;      /* SET if -b specified                   */
00085 int recursive_dir = NOT_SET;    /* SET if -r specified                   */
00086 int context_lines = 3;          /* numbers of lines in a context         */
00087 static int offset;              /* offset of the actual line number for -e */
00088 
00089  /* Function prototypes for the functions in this file   */
00090 struct f;
00091 _PROTOTYPE(int main, (int argc, char **argv ));
00092 _PROTOTYPE(void process_command_line, (int argc, char **argv ));
00093 _PROTOTYPE(void analyse_input_files, (char *arg1, char *arg2, char *input1, 
00094                                                         char *input2 ));
00095 _PROTOTYPE(void diff, (char *filename1, char *filename2 ));
00096 _PROTOTYPE(FILE *check_file, (char *name ));
00097 _PROTOTYPE(void build_option_string, (void ));
00098 _PROTOTYPE(void fatal_error, (char *fmt, char *s ));
00099 _PROTOTYPE(void warn, (int number, char *string ));
00100 _PROTOTYPE(void trimming_blanks, (char *l_text ));
00101 _PROTOTYPE(char *filename, (char *path_string));
00102 _PROTOTYPE(struct line *new_line, (int size ));
00103 _PROTOTYPE(void free_line, (struct line *l ));
00104 _PROTOTYPE(int equal_line, (struct line *l1, struct line *l2 ));
00105 _PROTOTYPE(int equal_3, (struct line *l1, struct line *l2 ));
00106 _PROTOTYPE(struct line *read_line, (FILE *fp ));
00107 _PROTOTYPE(void advance, (struct f *f ));
00108 _PROTOTYPE(void aside, (struct f *f, struct line *l ));
00109 _PROTOTYPE(struct line *next, (struct f *f ));
00110 _PROTOTYPE(void init_f, (struct f *f, FILE *fp ));
00111 _PROTOTYPE(void update, (struct f *f, char *s ));
00112 _PROTOTYPE(void __diff, (FILE *fp1, FILE *fp2 ));
00113 _PROTOTYPE(void differ, (struct f *f1, struct f *f2 ));
00114 _PROTOTYPE(int wlen, (struct f *f ));
00115 _PROTOTYPE(void range, (int a, int b ));
00116 _PROTOTYPE(void cdiff, (char *old, char *new, FILE *file1, FILE *file2 ));
00117 _PROTOTYPE(void dumphunk, (void ));
00118 _PROTOTYPE(char *getold, (int targ ));
00119 _PROTOTYPE(char *getnew, (int targ ));
00120 _PROTOTYPE(int isdir, (char *path ));
00121 _PROTOTYPE(void diff_recursive, (char *dir1, char *dir2 ));
00122 _PROTOTYPE(void file_type_error, (char *filename1, char *filename2, 
00123                         struct stat *statbuf1, struct stat *statbuf2 ));
00124 _PROTOTYPE(void *xmalloc, (size_t size));
00125 _PROTOTYPE(void *xrealloc, (void *ptr, size_t size));
00126 
00127 int main(argc, argv)
00128 int argc;
00129 char **argv;
00130 {
00131   char file1[PATH_MAX], file2[PATH_MAX];
00132   extern int optind;            /* index of the current string in argv   */
00133 
00134   progname = argv[0];
00135   process_command_line(argc, argv);
00136 
00137   analyse_input_files(argv[optind], argv[optind + 1], file1, file2);
00138   optind++;
00139 
00140   if (recursive_dir == SET) {
00141         build_option_string();
00142         diff_recursive(file1, file2);
00143   } else {
00144         diff(file1, file2);
00145   }
00146 
00147   return(severe_error ? 2 : diffs > 0 ? 1 : 0);
00148 }
00149 
00150 /* Process the command line and set the flags for the different
00151  * options. the processing of the command line is done with the
00152  * getopt() library function. a minimal error processing is done
00153  * for the number of command line arguments.                             */
00154 void process_command_line(argc, argv)
00155 int argc;                       /* number of arguments on command line   */
00156 char **argv;                    /* ** to arguments on command line       */
00157 {
00158   int c;
00159   extern char *optarg;          /* points to string with options         */
00160   extern int optind;            /* index of the current string in argv   */
00161 
00162   /* Are there enough arguments?                 */
00163   if (argc < 3) {
00164         fatal_error("Usage: %s [-c|-e|-C n][-br] file1 file2\n", progname);
00165   }
00166 
00167   /* Process all options using getopt()  */
00168   while ((c = getopt(argc, argv, "ceC:br")) != -1) {
00169         switch (c) {
00170             case 'c':
00171                 if (mode != undefined) warn(EXCLUSIVE_OPTIONS, "c");
00172                 mode = context;
00173                 context_lines = 3;
00174                 break;
00175             case 'e':
00176                 if (mode != undefined) warn(EXCLUSIVE_OPTIONS, "e");
00177                 mode = ed_mode;
00178                 break;
00179             case 'C':
00180                 if (mode != undefined) warn(EXCLUSIVE_OPTIONS, "C");
00181                 mode = context;
00182                 context_lines = atoi(optarg);
00183                 break;
00184             case 'b':   trim_blanks = SET;      break;
00185             case 'r':   recursive_dir = SET;    break;
00186             case '?':
00187                 exit(2);
00188         }
00189   }
00190 
00191   /* We should have two arguments left   */
00192   if ((argc - optind) != 2)
00193         fatal_error("Need exactly two input file-names!\n", "");
00194 }
00195 
00196 /* Analyse_input_files takes the two input files on the command line
00197  * and decides what to do. returns the (corrected) filenames that
00198  * can be used to call diff().
00199  * if two directories are given, then a recursive diff is done.
00200  * one directory and one filename compares the file with <filename>
00201  * in the directory <directory> with <filename>.
00202  * if two filenames are specified, no special action takes place.
00203  */
00204 void analyse_input_files(arg1, arg2, input1, input2)
00205 char *arg1, *arg2;              /* filenames on the command line         */
00206 char *input1, *input2;          /* filenames to be used with diff()      */
00207 {
00208   int stat1 = 0, stat2 = 0;
00209 
00210   if (strcmp(arg1, "-") != 0)
00211         stat1 = isdir(arg1);    /* != 0 <-> arg1 is directory            */
00212   if (strcmp(arg2, "-") != 0) stat2 = isdir(arg2);
00213 #ifdef DEBUG
00214   fprintf(stderr, "%s, stat = %d\n", arg1, stat1);
00215   fprintf(stderr, "%s, stat = %d\n", arg2, stat2);
00216 #endif
00217   if (stat1 && stat2) {         /* both arg1 and arg2 are directories */
00218         recursive_dir = SET;
00219         strcpy(input1, arg1);
00220         strcpy(input2, arg2);
00221         return;
00222   }
00223   if (stat1 != 0) {             /* arg1 is a dir, arg2 not               */
00224         if (strcmp(arg2, "-") != 0) {   /* arg2 != stdin         */
00225                 strcpy(input1, arg1);
00226                 strcat(input1, "/");
00227                 strcat(input1, arg2);
00228                 strcpy(input2, arg2);
00229                 return;
00230         } else {
00231                 fatal_error("cannot compare stdin (-) with a directory!", "");
00232         }
00233   }
00234   if (stat2 != 0) {             /* arg2 is a dir, arg1 not               */
00235         if (strcmp(arg1, "-") != 0) {   /* arg1 != stdin         */
00236                 strcpy(input1, arg1);
00237                 strcpy(input2, arg2);
00238                 strcat(input2, "/");
00239                 strcat(input2, arg1);
00240                 return;
00241         } else {                /* arg1 == stdin                         */
00242                 fatal_error("cannot compare stdin (-) with a directory!", "");
00243         }
00244   }
00245 
00246   /* Both arg1 and arg2 are normal  files        */
00247   strcpy(input1, arg1);
00248   strcpy(input2, arg2);
00249 }
00250 
00251 /* Diff() is the front end for all modes of the program diff, execpt
00252  * the recursive_dir option.
00253  * diff() expects the filenames of the two files to be compared as
00254  * arguments. the mode is determined from the global variable mode.
00255  */
00256 void diff(filename1, filename2)
00257 char *filename1, *filename2;
00258 {
00259   FILE *file1 = check_file(filename1);
00260   FILE *file2 = check_file(filename2);
00261   struct stat statbuf1, statbuf2;
00262 
00263   if ((file1 != Nullfp) && (file2 != Nullfp)) {
00264         /* If we do a recursive diff, then we don't compare block
00265          * special, character special or FIFO special files to any
00266          * file.                          */
00267         fstat(fileno(file1), &statbuf1);
00268         fstat(fileno(file2), &statbuf2);
00269         if ((((statbuf1.st_mode & S_IFREG) != S_IFREG) ||
00270              ((statbuf2.st_mode & S_IFREG) != S_IFREG)) &&
00271             (recursive_dir == SET)) {
00272                 file_type_error(filename1, filename2, &statbuf1, &statbuf2);
00273         } else {
00274                 switch (mode) {
00275                     case context:
00276                         cdiff(filename1, filename2, file1, file2);
00277                         break;
00278                     case ed_mode:
00279                     case undefined:
00280                         __diff(file1, file2);
00281                         if (mode == ed_mode) printf("w\n");
00282                         break;
00283                 }
00284         }
00285   } else
00286         severe_error = 1;
00287   if (file1 != Nullfp) fclose(file1);
00288   if (file2 != Nullfp) fclose(file2);
00289 }
00290 
00291 /* Check_file() opens the fileptr with name <filename>. if <filename>
00292  * equals "-" stdin is associated with the return value.
00293  */
00294 FILE *check_file(name)
00295 char *name;
00296 {
00297   FILE *temp;
00298 
00299   if (strcmp(name, "-") == 0) {
00300         return(stdin);
00301   } else {
00302         temp = fopen(name, "r");
00303         if (temp == Nullfp) warn(CANNOT_OPEN_FILE, name);
00304         return(temp);
00305   }
00306 }
00307 
00308 /* Build_option_string() is called before recursive_dir() is called
00309  * from the main() function. its purpose is to build the string that
00310  * is used on the command line to get the current operation mode.
00311  * e.g. "-C 6 -b".
00312  */
00313 void build_option_string()
00314 {
00315   switch (mode) {
00316             case ed_mode:sprintf(options_string, "-e");
00317         break;
00318       case context:
00319         if (context_lines == 3)
00320                 sprintf(options_string, "-c");
00321         else
00322                 sprintf(options_string, "-C %d", context_lines);
00323         break;
00324   }
00325 
00326 }
00327 
00328 
00329 /* The fatal error handler.
00330  * Expects a format string and a string as arguments. The arguments
00331  * are printed to stderr and the program exits with an error code 2.
00332  */
00333 void fatal_error(fmt, s)
00334 char *fmt;                      /* the format sttring to be printed      */
00335 char *s;                        /* string to be inserted into the format
00336                                  * string                                */
00337 {
00338   fprintf(stderr, "%s: ", progname);
00339   fprintf(stderr, fmt, s);
00340   fprintf(stderr, "\n");
00341   exit(2);
00342 }
00343 
00344 /* This function prints non fatal error messages to stderr.
00345  * Expects the index of the message to be printed and a pointer
00346  * to the (optional) string to be printed.
00347  * Returns no value.
00348  */
00349 void warn(number, string)
00350 int number;                     /* index of the warning                  */
00351 char *string;                   /* string to be inserted to the warning  */
00352 {
00353   static char *warning[] = {
00354     "%s: The options -c, -e, -C n are mutually exclusive! Assuming -%c\n",
00355     "%s: cannot open file %s for reading\n",
00356   };
00357   fprintf(stderr, warning[number], progname, string);
00358 }
00359 
00360 /* Function used with the optione -b, trims the blanks in a input line:
00361  * - blanks between words are reduced to one
00362  * - trailing blanks are eliminated.
00363  */
00364 void trimming_blanks(l_text)
00365 char *l_text;                   /* begin of the char array               */
00366 {
00367   char *line = l_text;
00368   char *copy_to, *copy_from;
00369 
00370   do {
00371         if (*line == ' ') {
00372                 copy_from = line;
00373                 copy_to = line;
00374                 while (*(++copy_from) == ' ');
00375                 if (*copy_from != '\n') copy_to++;
00376                 while (*copy_from != '\0') *(copy_to++) = *(copy_from++);
00377                 *copy_to = '\0';
00378         }
00379   } while (*(++line) != '\0');
00380 }
00381 
00382 
00383 /* Filename separates the filename and the relative path in path_string.
00384  * Returns the filename with a leading /
00385  */
00386 char *filename(path_string)
00387 char *path_string;
00388 {
00389   char name[NAME_MAX + 2];      /* filename plus /                       */
00390   char *ptr;
00391 
00392   name[0] = '/';
00393   ptr = strrchr(path_string, '/');
00394 
00395   if (ptr == 0) {               /* no / in path_string, only a filename  */
00396         strcat(name, path_string);
00397   } else {
00398         strcat(name, ptr);
00399   }
00400 
00401   return(name);
00402 }
00403 
00404 /* The line module: one member in a linked list of lines. */
00405 struct line {
00406   struct line *l_next;          /* pointer to the next line      */
00407   char l_eof;                   /* == 0 if last line in file     */
00408   char *l_text;                 /* array with the text           */
00409 };
00410 
00411 struct line *freelist = 0;
00412 #define stepup(ll) ( ((ll) && ((ll)->l_eof==0)) ? (ll)->l_next : (ll) )
00413 
00414 /* Function to allocate space for a new line containing SIZE chars      */
00415 struct line *new_line(size)
00416 int size;
00417 {
00418   register struct line *l;
00419 
00420   if ((l = freelist) != NullStructLine)
00421         freelist = freelist->l_next;
00422   else {
00423         l = (struct line *) xmalloc(3 * sizeof(void *));
00424         l->l_text = (char *) xmalloc((size + 2) * sizeof(char));
00425         if ((l == 0) || (l->l_text == 0)) fatal_error("Out of memory", "");
00426   }
00427   return l;
00428 }
00429 
00430 
00431 /* Free_line() releases storage allocated for <l>. */
00432 void free_line(l)
00433 register struct line *l;
00434 {
00435   l->l_next = freelist;
00436   freelist = l;
00437 }
00438 
00439 /* Equal_line() compares two lines, <l1> and <l2>.
00440  * the returned value is the result of the strcmp() function.
00441  */
00442 int equal_line(l1, l2)
00443 struct line *l1, *l2;
00444 {
00445   if (l1 == 0 || l2 == 0)
00446         return(0);
00447   else if (l1->l_eof || l2->l_eof)
00448         return(l1->l_eof == l2->l_eof);
00449   else
00450         return(strcmp(l1->l_text, l2->l_text) == 0);
00451 }
00452 
00453 int equal_3(l1, l2)
00454 struct line *l1, *l2;
00455 {
00456   register int i, ansr;
00457 
00458   ansr = 1;
00459 #ifdef DEBUG
00460   if (l1 == 0)
00461         fprintf(stderr, "\t(null)\n");
00462   else if (l1->l_eof)
00463         fprintf(stderr, "\t(eof)\n");
00464   else
00465         fprintf(stderr, "\t%s", l1->l_text);
00466   if (l2 == 0)
00467         fprintf(stderr, "\t(null)\n");
00468   else if (l2->l_eof)
00469         fprintf(stderr, "\t(eof)\n");
00470   else
00471         fprintf(stderr, "\t%s", l2->l_text);
00472 #endif
00473   for (i = 0; i < 3; ++i) {
00474         if (!equal_line(l1, l2)) {
00475                 ansr = 0;
00476                 break;
00477         }
00478         l1 = stepup(l1);
00479         l2 = stepup(l2);
00480   }
00481 #ifdef DEBUG
00482   fprintf(stderr, "\t%d\n", ansr);
00483 #endif
00484   return(ansr);
00485 }
00486 
00487 struct line *
00488  read_line(fp)
00489 FILE *fp;
00490 {
00491   register struct line *l = new_line(LINELEN);
00492   register char *p;
00493   register int c;
00494 
00495   (p = &(l->l_text[LINELEN]))[1] = '\377';
00496   l->l_eof = 0;
00497   if (fgets(l->l_text, LINELEN + 2, fp) == 0) {
00498         l->l_eof = 1;
00499         l->l_text[0] = 0;
00500   } else if ((p[1] & 0377) != 0377 && *p != '\n') {
00501         while ((c = fgetc(fp)) != '\n' && c != EOF) {
00502         }
00503         *p++ = '\n';
00504         *p = '\0';
00505   }
00506   l->l_next = 0;
00507   if (trim_blanks == SET) {
00508 #ifdef DEBUG
00509         printf("xxx %s xxx\n", l->l_text);
00510 #endif
00511         trimming_blanks(l->l_text);
00512 #ifdef DEBUG
00513         printf("xxx %s xxx\n", l->l_text);
00514 #endif
00515   }
00516   return l;
00517 }
00518 
00519 /* File window handler */
00520 struct f {
00521   struct line *f_bwin, *f_ewin;
00522   struct line *f_aside;
00523   int f_linecnt;                /* line number in file of last advanced line */
00524   FILE *f_fp;
00525 };
00526 
00527 void advance(f)
00528 register struct f *f;
00529 {
00530   register struct line *l;
00531 
00532   if ((l = f->f_bwin) != NullStructLine) {
00533         if (f->f_ewin == l)
00534                 f->f_bwin = f->f_ewin = 0;
00535         else
00536                 f->f_bwin = l->l_next;
00537         free_line(l);
00538         (f->f_linecnt)++;
00539   }
00540 }
00541 
00542 void aside(f, l)
00543 struct f *f;
00544 struct line *l;
00545 {
00546   register struct line *ll;
00547 
00548   if (l == 0) return;
00549   if ((ll = l->l_next) != NullStructLine) {
00550         while (ll->l_next) ll = ll->l_next;
00551         ll->l_next = f->f_aside;
00552         f->f_aside = l->l_next;
00553         l->l_next = 0;
00554         f->f_ewin = l;
00555   }
00556 }
00557 
00558 
00559 struct line *next(f)
00560 register struct f *f;
00561 {
00562   register struct line *l;
00563 
00564   if ((l = f->f_aside) != NullStructLine) {
00565         f->f_aside = l->l_next;
00566         l->l_next = 0;
00567   } else
00568         l = read_line(f->f_fp);
00569   if (l) {
00570         if (f->f_bwin == 0)
00571                 f->f_bwin = f->f_ewin = l;
00572         else {
00573                 if (f->f_ewin->l_eof && l->l_eof) {
00574                         free_line(l);
00575                         return(f->f_ewin);
00576                 }
00577                 f->f_ewin->l_next = l;
00578                 f->f_ewin = l;
00579         }
00580   }
00581   return l;
00582 }
00583 
00584 
00585 /* Init_f() initialises a window structure (struct f). <fp> is the
00586  * file associated with <f>.
00587  */
00588 void init_f(f, fp)
00589 register struct f *f;
00590 FILE *fp;
00591 {
00592   f->f_bwin = f->f_ewin = f->f_aside = 0;
00593   f->f_linecnt = 0;
00594   f->f_fp = fp;
00595 }
00596 
00597 
00598 /* Update() prints a window. <f> is a pointer to the window, <s> is the
00599  * string containing the "prefix" to the printout( either "<" or ">").
00600  * after completion of update(), the window is empty.
00601  */
00602 void update(f, s)
00603 register struct f *f;
00604 char *s;
00605 {
00606   char *help;
00607   int only_dot = 0;
00608 
00609   if (firstoutput && (recursive_dir == SET)) {
00610         printf("diff %s %s %s\n", options_string, oldfile, newfile);
00611         firstoutput = 0;
00612   }
00613   while (f->f_bwin && f->f_bwin != f->f_ewin) {
00614         if (mode != ed_mode) {
00615                 printf("%s%s", s, f->f_bwin->l_text);
00616         } else {
00617 #ifdef DEBUG
00618                 printf("ed_mode: test for only dot");
00619                 printf("%s", f->f_bwin->l_text);
00620 #endif
00621                 help = f->f_bwin->l_text;
00622                 while ((*help == ' ') ||
00623                        (*help == '.') ||
00624                        (*help == '\t')) {
00625                         if (*(help++) == '.') only_dot++;
00626                         if (only_dot > 1) break;
00627                 }
00628 
00629                 /* If only_dot is equal 1, there is only one dot on
00630                  * the line, so we have to take special actions.
00631                  * f the line with only one dot is found, we output
00632                  * two dots (".."), terminate the append modus and
00633                  * substitute "." for "..". Afterwards we restart
00634                  * with the append command.                      */
00635                 if (*help == '\n' && only_dot == 1) {
00636                         help = f->f_bwin->l_text;
00637                         while (*help != '\0') {
00638                                 if (*help == '.') printf(".");
00639                                 putchar((int) *(help++));
00640                         }
00641                         printf(".\n");
00642                         printf(".s/\\.\\././\n");
00643                         printf("a\n");
00644                 } else {
00645                         printf("%s%s", s, f->f_bwin->l_text);
00646                 }
00647         }
00648         advance(f);
00649   }
00650 }
00651 
00652 /* __Diff(), performs the "core operation" of the program.
00653  * Expects two file-pointers as arguments. This functions does
00654  * *not* check if the file-pointers are valid.
00655  */
00656 
00657 void __diff(fp1, fp2)
00658 FILE *fp1, *fp2;
00659 {
00660   struct f f1, f2;
00661   struct line *l1, *s1, *b1, *l2, *s2, *b2;
00662   register struct line *ll;
00663 
00664   init_f(&f1, fp1);
00665   init_f(&f2, fp2);
00666   l1 = next(&f1);
00667   l2 = next(&f2);
00668   while ((l1->l_eof == 0) || (l2->l_eof == 0)) {
00669         if (equal_line(l1, l2)) {
00670   equal:
00671                 advance(&f1);
00672                 advance(&f2);
00673                 l1 = next(&f1);
00674                 l2 = next(&f2);
00675                 continue;
00676         }
00677         s1 = b1 = l1;
00678         s2 = b2 = l2;
00679         /* Read several more lines */
00680         next(&f1);
00681         next(&f1);
00682         next(&f2);
00683         next(&f2);
00684         /* Start searching */
00685 search:
00686         next(&f2);
00687         ll = s1;
00688         do {
00689                 if (equal_3(ll, b2)) {
00690                         l1 = ll;
00691                         l2 = b2;
00692                         aside(&f1, ll);
00693                         aside(&f2, b2);
00694                         differ(&f1, &f2);
00695                         goto equal;
00696                 }
00697                 if (ll->l_eof) break;
00698                 ll = stepup(ll);
00699         } while (ll);
00700         b2 = stepup(b2);
00701 
00702         next(&f1);
00703         ll = s2;
00704         do {
00705                 if (equal_3(b1, ll)) {
00706                         l1 = b1;
00707                         l2 = ll;
00708                         aside(&f2, ll);
00709                         aside(&f1, b1);
00710                         differ(&f1, &f2);
00711                         goto equal;
00712                 }
00713                 if (ll->l_eof != 0) break;
00714                 ll = stepup(ll);
00715         } while (ll);
00716         b1 = stepup(b1);
00717 
00718         goto search;
00719   }
00720 
00721   /* Both of the files reached EOF */
00722 }
00723 
00724 /* Differ() prints the differences between files. the arguments <f1> and
00725  * <f2> are pointers to the two windows, where the differences are.
00726  */
00727 void differ(f1, f2)
00728 register struct f *f1, *f2;
00729 {
00730   int cnt1 = f1->f_linecnt, len1 = wlen(f1);
00731   int cnt2 = f2->f_linecnt, len2 = wlen(f2);
00732   if ((len1 != 0) || (len2 != 0)) {
00733         if (len1 == 0) {
00734                 if (mode == ed_mode) {
00735                         cnt1 += offset;
00736                         printf("%d a\n", cnt1);
00737                         update(f2, "");
00738                         printf(".\n");
00739                         offset += len2;
00740                 } else {
00741                         printf("%da", cnt1);
00742                         range(cnt2 + 1, cnt2 + len2);
00743                 }
00744         } else if (len2 == 0) {
00745                 if (mode == ed_mode) {
00746                         cnt1 += offset;
00747                         range(cnt1 + 1, cnt1 + len1);
00748                         printf("d\n");
00749                         offset -= len1;
00750                         while (f1->f_bwin && f1->f_bwin != f1->f_ewin)
00751                                 advance(f1);
00752                 } else {
00753                         range(cnt1 + 1, cnt1 + len1);
00754                         printf("d%d", cnt2);
00755                 }
00756         } else {
00757                 if (mode != ed_mode) {
00758                         range(cnt1 + 1, cnt1 + len1);
00759                         putchar('c');
00760                         range(cnt2 + 1, cnt2 + len2);
00761                 } else {
00762                         cnt1 += offset;
00763                         if (len1 == len2) {
00764                                 range(cnt1 + 1, cnt1 + len1);
00765                                 printf("c\n");
00766                                 update(f2, "");
00767                                 printf(".\n");
00768                         } else {
00769                                 range(cnt1 + 1, cnt1 + len1);
00770                                 printf("d\n");
00771                                 printf("%d a\n", cnt1);
00772                                 update(f2, "");
00773                                 printf(".\n");
00774                                 offset -= len1 - len2;
00775                         }
00776                         while (f1->f_bwin && f1->f_bwin != f1->f_ewin)
00777                                 advance(f1);
00778                 }
00779         }
00780         if (mode != ed_mode) {
00781                 putchar('\n');
00782                 if (len1 != 0) update(f1, "< ");
00783                 if ((len1 != 0) && (len2 != 0)) printf("---\n");
00784                 if (len2 != 0) update(f2, "> ");
00785         }
00786         diffs++;
00787   }
00788 }
00789 
00790 
00791 /* Function wlen() calculates the number of lines in a window. */
00792 int wlen(f)
00793 struct f *f;
00794 {
00795   register cnt = 0;
00796   register struct line *l = f->f_bwin, *e = f->f_ewin;
00797 
00798   while (l && l != e) {
00799         cnt++;
00800         l = l->l_next;
00801   }
00802   return cnt;
00803 }
00804 
00805 
00806 /* Range() prints the line numbers of a range. the arguments <a> and <b>
00807  * are the beginning and the ending line number of the range. if
00808  * <a> == <b>, only one line number is printed. otherwise <a> and <b> are
00809  * separated by a ",".
00810  */
00811 void range(a, b)
00812 int a, b;
00813 {
00814   printf(((a == b) ? "%d" : "%d,%d"), a, b);
00815 }
00816 
00817 /* Here follows the code for option -c.
00818  * This code is from the cdiff program by Larry Wall. I changed it only
00819  * slightly to reflect the POSIX standard and to call the main routine
00820  * as function context_diff().
00821  */
00822 
00823 /* Cdiff - context diff                 Author: Larry Wall */
00824 
00825 /* These global variables are still here from the original cdiff program...
00826  * I was to lazy just to sort them out...
00827  */
00828 char buff[512];
00829 FILE *oldfp, *newfp;
00830 
00831 int oldmin, oldmax, newmin, newmax;
00832 int oldbeg, oldend, newbeg, newend;
00833 int preoldmax, prenewmax;
00834 int preoldbeg, preoldend, prenewbeg, prenewend;
00835 int oldwanted, newwanted;
00836 
00837 char *oldhunk, *newhunk;
00838 size_t oldsize, oldalloc, newsize, newalloc;
00839 
00840 int oldline, newline; /* Jose */
00841 
00842 void cdiff(old, new, file1, file2)
00843 char *old, *new;                /* The names of the two files to be compared */
00844 FILE *file1, *file2;            /* The corresponding file-pointers       */
00845 {
00846   FILE *inputfp;
00847   struct stat statbuf;
00848   register char *s;
00849   char op;
00850   char *newmark, *oldmark;
00851   int len;
00852   char *line;
00853   int i, status;
00854 
00855   oldfp = file1;
00856   newfp = file2;
00857 
00858   oldalloc = 512;
00859   oldhunk = (char *) xmalloc(oldalloc);
00860   newalloc = 512;
00861   newhunk = (char *) xmalloc(newalloc);
00862 
00863 
00864 /* The context diff spawns a new process that executes a normal diff
00865  * and parses the output.
00866  */
00867   if (trim_blanks == SET)
00868         sprintf(buff, "diff -b %s %s", old, new);
00869   else
00870         sprintf(buff, "diff %s %s", old, new);
00871 
00872   inputfp = popen(buff, "r");
00873   if (!inputfp) {
00874         fprintf(stderr, "Can't execute diff %s %s, popen failed with %s\n",
00875                 old, new, strerror(errno));
00876         exit(2);
00877   }
00878   preoldend = -1000;
00879   firstoutput = 1;
00880   oldline = newline = 0;
00881   while (fgets(buff, sizeof buff, inputfp) != Nullch) {
00882         if (firstoutput) {
00883                 if (recursive_dir == SET) {
00884                         printf("diff %s %s %s\n", options_string,
00885                                oldfile, newfile);
00886                 }
00887                 fstat(fileno(oldfp), &statbuf);
00888                 printf("*** %s %s", old, ctime(&statbuf.st_mtime));
00889                 fstat(fileno(newfp), &statbuf);
00890                 printf("--- %s %s", new, ctime(&statbuf.st_mtime));
00891                 firstoutput = 0;
00892         }
00893         if (isdigit(*buff)) {
00894                 oldmin = atoi(buff);
00895                 for (s = buff; isdigit(*s); s++);
00896                 if (*s == ',') {
00897                         s++;
00898                         oldmax = atoi(s);
00899                         for (; isdigit(*s); s++);
00900                 } else {
00901                         oldmax = oldmin;
00902                 }
00903                 if (*s != 'a' && *s != 'd' && *s != 'c') {
00904                         fprintf(stderr, "Unparseable input: %s", s);
00905                         exit(2);
00906                 }
00907                 op = *s;
00908                 s++;
00909                 newmin = atoi(s);
00910                 for (; isdigit(*s); s++);
00911                 if (*s == ',') {
00912                         s++;
00913                         newmax = atoi(s);
00914                         for (; isdigit(*s); s++);
00915                 } else {
00916                         newmax = newmin;
00917                 }
00918                 if (*s != '\n' && *s != ' ') {
00919                         fprintf(stderr, "Unparseable input: %s", s);
00920                         exit(2);
00921                 }
00922                 newmark = oldmark = "! ";
00923                 if (op == 'a') {
00924                         oldmin++;
00925                         newmark = "+ ";
00926                 }
00927                 if (op == 'd') {
00928                         newmin++;
00929                         oldmark = "- ";
00930                 }
00931                 oldbeg = oldmin - context_lines;
00932                 oldend = oldmax + context_lines;
00933                 if (oldbeg < 1) oldbeg = 1;
00934                 newbeg = newmin - context_lines;
00935                 newend = newmax + context_lines;
00936                 if (newbeg < 1) newbeg = 1;
00937 
00938                 if (preoldend < oldbeg - 1) {
00939                         if (preoldend >= 0) {
00940                                 dumphunk();
00941                         }
00942                         preoldbeg = oldbeg;
00943                         prenewbeg = newbeg;
00944                         oldwanted = newwanted = 0;
00945                         oldsize = newsize = 0;
00946                 } else {        /* we want to append to previous hunk */
00947                         oldbeg = preoldmax + 1;
00948                         newbeg = prenewmax + 1;
00949                 }
00950 
00951                 for (i = oldbeg; i <= oldmax; i++) {
00952                         line = getold(i);
00953                         if (!line) {
00954                                 oldend = oldmax = i - 1;
00955                                 break;
00956                         }
00957                         len = strlen(line) + 2;
00958                         if (oldsize + len + 1 >= oldalloc) {
00959                                 oldalloc *= 2;
00960                                 oldhunk = (char *) xrealloc(oldhunk, oldalloc);
00961                         }
00962                         if (i >= oldmin) {
00963                                 strcpy(oldhunk + oldsize, oldmark);
00964                                 oldwanted++;
00965                         } else {
00966                                 strcpy(oldhunk + oldsize, "  ");
00967                         }
00968                         strcpy(oldhunk + oldsize + 2, line);
00969                         oldsize += len;
00970                 }
00971                 preoldmax = oldmax;
00972                 preoldend = oldend;
00973 
00974                 for (i = newbeg; i <= newmax; i++) {
00975                         line = getnew(i);
00976                         if (!line) {
00977                                 newend = newmax = i - 1;
00978                                 break;
00979                         }
00980                         len = strlen(line) + 2;
00981                         if (newsize + len + 1 >= newalloc) {
00982                                 newalloc *= 2;
00983                                 newhunk = (char *) xrealloc(newhunk, newalloc);
00984                         }
00985                         if (i >= newmin) {
00986                                 strcpy(newhunk + newsize, newmark);
00987                                 newwanted++;
00988                         } else {
00989                                 strcpy(newhunk + newsize, "  ");
00990                         }
00991                         strcpy(newhunk + newsize + 2, line);
00992                         newsize += len;
00993                 }
00994                 prenewmax = newmax;
00995                 prenewend = newend;
00996         }
00997   }
00998   status = pclose(inputfp);
00999   if (status != 0) diffs++;
01000   if (!WIFEXITED(status) || WEXITSTATUS(status) > 1) severe_error = 1;
01001 
01002   if (preoldend >= 0) {
01003         dumphunk();
01004   }
01005 }
01006 
01007 void dumphunk()
01008 {
01009   int i;
01010   char *line;
01011   int len;
01012 
01013   for (i = preoldmax + 1; i <= preoldend; i++) {
01014         line = getold(i);
01015         if (!line) {
01016                 preoldend = i - 1;
01017                 break;
01018         }
01019         len = strlen(line) + 2;
01020         if (oldsize + len + 1 >= oldalloc) {
01021                 oldalloc *= 2;
01022                 oldhunk = (char *) xrealloc(oldhunk, oldalloc);
01023         }
01024         strcpy(oldhunk + oldsize, "  ");
01025         strcpy(oldhunk + oldsize + 2, line);
01026         oldsize += len;
01027   }
01028   for (i = prenewmax + 1; i <= prenewend; i++) {
01029         line = getnew(i);
01030         if (!line) {
01031                 prenewend = i - 1;
01032                 break;
01033         }
01034         len = strlen(line) + 2;
01035         if (newsize + len + 1 >= newalloc) {
01036                 newalloc *= 2;
01037                 newhunk = (char *) xrealloc(newhunk, newalloc);
01038         }
01039         strcpy(newhunk + newsize, "  ");
01040         strcpy(newhunk + newsize + 2, line);
01041         newsize += len;
01042   }
01043   fputs("***************\n", stdout);
01044   if (preoldbeg >= preoldend) {
01045         printf("*** %d ****\n", preoldend);
01046   } else {
01047         printf("*** %d,%d ****\n", preoldbeg, preoldend);
01048   }
01049   if (oldwanted) {
01050         fputs(oldhunk, stdout);
01051   }
01052   oldsize = 0;
01053   *oldhunk = '\0';
01054   if (prenewbeg >= prenewend) {
01055         printf("--- %d ----\n", prenewend);
01056   } else {
01057         printf("--- %d,%d ----\n", prenewbeg, prenewend);
01058   }
01059   if (newwanted) {
01060         fputs(newhunk, stdout);
01061   }
01062   newsize = 0;
01063   *newhunk = '\0';
01064 }
01065 
01066 char *getold(targ)
01067 int targ;
01068 {
01069   while (fgets(buff, sizeof buff, oldfp) != Nullch) {
01070         oldline++;
01071         if (oldline == targ) return buff;
01072   }
01073   return Nullch;
01074 }
01075 
01076 char *getnew(targ)
01077 int targ;
01078 {
01079   while (fgets(buff, sizeof buff, newfp) != Nullch) {
01080         newline++;
01081         if (newline == targ) return buff;
01082   }
01083   return Nullch;
01084 }
01085 
01086 
01087 /* Isdir() checks, if <path> is the name of a directory. a return value
01088  * is 0, <path> is a normal file. otherwise the <path> is a directory.
01089  */
01090 int isdir(path)
01091 char *path;
01092 {
01093   struct stat buf;
01094   stat(path, &buf);
01095   if (buf.st_mode & S_IFDIR) {  /* path is a directory           */
01096         return(~0);
01097   } else {
01098         return(0);
01099   }
01100 }
01101 
01102 
01103 
01104 /* This is the "main" function if a diff of two directories has to be
01105  * done. diff_recursive() expects the names of the two directories to
01106  * be compared.                                                          */
01107 void diff_recursive(dir1, dir2)
01108 char *dir1, *dir2;
01109 {
01110   FILE *ls1, *ls2;
01111   char file1[PATH_MAX], file2[PATH_MAX];
01112   char jointfile1[PATH_MAX], jointfile2[PATH_MAX];
01113   char command[PATH_MAX];
01114   int difference, eof1, eof2;
01115 
01116   sprintf(command, "ls %s", dir1);
01117   ls1 = popen(command, "r");
01118   sprintf(command, "ls %s", dir2);
01119   ls2 = popen(command, "r");
01120 
01121   if ((ls1 == NULL) || (ls2 == NULL))
01122         fatal_error("cannot execute ls!", "");
01123 
01124   file1[0] = '\0';
01125   eof1 = fscanf(ls1, "%s\n", file1);
01126   file2[0] = '\0';
01127   eof2 = fscanf(ls2, "%s\n", file2);
01128 
01129   while ((file1[0] != '\0') && (file2[0] != '\0')) {
01130         difference = strcmp(file1, file2);
01131         while (difference != 0) {
01132                 if (difference < 0) {
01133                         printf("Only in %s: %s\n", dir1, file1);
01134                         file1[0] = '\0';
01135                         eof1 = fscanf(ls1, "%s\n", file1);
01136                         if (file1[0] == '\0') break;
01137                 } else {
01138                         printf("Only in %s: %s\n", dir2, file2);
01139                         file2[0] = '\0';
01140                         eof2 = fscanf(ls2, "%s\n", file2);
01141                         if (file2[0] == '\0') break;
01142                 }
01143                 difference = strcmp(file1, file2);
01144         }
01145         if (eof1 != EOF && eof2 != EOF) {
01146                 strcpy(jointfile1, dir1);
01147                 strcat(jointfile1, "/");
01148                 strcat(jointfile1, file1);
01149                 strcpy(jointfile2, dir2);
01150                 strcat(jointfile2, "/");
01151                 strcat(jointfile2, file2);
01152 
01153                 if ((isdir(jointfile1) != 0) && (isdir(jointfile2) != 0)) {
01154                         printf("Common subdirectories: %s and %s\n",
01155                                jointfile1, jointfile2);
01156                         diff_recursive(jointfile1, jointfile2);
01157                 } else {
01158                         firstoutput = 1;
01159                         strcpy(oldfile, jointfile1);
01160                         strcpy(newfile, jointfile2);
01161                         diff(jointfile1, jointfile2);
01162                 }
01163                 file1[0] = '\0';
01164                 eof1 = fscanf(ls1, "%s\n", file1);
01165                 file2[0] = '\0';
01166                 eof2 = fscanf(ls2, "%s\n", file2);
01167         }
01168   }
01169 
01170   if (file1[0] != '\0') {       /* first arg still has files             */
01171         do {
01172                 printf("Only in %s: %s\n", dir1, file1);
01173                 eof1 = fscanf(ls1, " %s\n", file1);
01174         } while (eof1 != EOF);
01175   }
01176   if (file2[0] != '\0') {
01177         do {
01178                 printf("Only in %s: %s\n", dir2, file2);
01179                 eof2 = fscanf(ls2, " %s\n", file2);
01180         } while (eof2 != EOF);
01181   }
01182   if (pclose(ls1) != 0) severe_error = 1;
01183   if (pclose(ls2) != 0) severe_error = 1;
01184 }
01185 
01186 
01187 /* File_type_error is called, if in a recursive diff ( -r) one of the two
01188  * files a block special, a character special or a FIFO special file is.
01189  * The corresponding error message is printed here.                       */
01190 void file_type_error(filename1, filename2, statbuf1, statbuf2)
01191 char *filename1, *filename2;
01192 struct stat *statbuf1, *statbuf2;
01193 {
01194   char type1[25], type2[25];
01195 
01196   switch (statbuf1->st_mode & S_IFMT) { /* select only file mode */
01197       case S_IFREG:
01198         sprintf(type1, "regular file ");
01199         break;
01200       case S_IFBLK:
01201         sprintf(type1, "block special file ");
01202         break;
01203       case S_IFDIR:     sprintf(type1, "directory ");   break;
01204       case S_IFCHR:
01205         sprintf(type1, "character special file ");
01206         break;
01207       case S_IFIFO:
01208         sprintf(type1, "FIFO special file ");
01209         break;
01210   }
01211 
01212   switch (statbuf2->st_mode & S_IFMT) { /* select only file mode */
01213       case S_IFREG:
01214         sprintf(type2, "regular file ");
01215         break;
01216       case S_IFBLK:
01217         sprintf(type2, "block special file ");
01218         break;
01219       case S_IFDIR:     sprintf(type2, "directory ");   break;
01220       case S_IFCHR:
01221         sprintf(type2, "character special file ");
01222         break;
01223       case S_IFIFO:
01224         sprintf(type2, "FIFO special file ");
01225         break;
01226   }
01227   printf("File %s is a %s while file %s is a %s\n",
01228          filename1, type1, filename2, type2);
01229 }
01230 
01231 void *xmalloc(size)
01232 size_t size;
01233 {
01234   void *ptr;
01235 
01236   ptr = malloc(size);
01237   if (ptr == NULL) {
01238         fprintf(stderr, "%s: out of memory\n", progname);
01239         exit(2);
01240   }
01241   return(ptr);
01242 }
01243 
01244 void *xrealloc(ptr, size)
01245 void *ptr;
01246 size_t size;
01247 {
01248   ptr = realloc(ptr, size);
01249   if (ptr == NULL) {
01250         fprintf(stderr, "%s: out of memory\n", progname);
01251         exit(2);
01252   }
01253   return(ptr);
01254 }

Generated on Fri Apr 14 22:57:05 2006 for minix by  doxygen 1.4.6