1 /* diff - print differences between 2 files Author: Erik Baalbergen */
3 /* Poor man's implementation of diff(1) - no options available
4 * - may give more output than other diffs,
5 * due to the straight-forward algorithm
6 * - runs out of memory if the differing chunks become too large
7 * - input line length should not exceed LINELEN; longer lines are
8 * truncated, while only the first LINELEN characters are compared
10 * - Bug fixes by Rick Thomas Sept. 1989
12 * Please report bugs and suggestions to erikb@cs.vu.nl
13 *------------------------------------------------------------------------------
14 * Changed diff to conform to POSIX 1003.2 ( Draft 11) by Thomas Brupbacher
15 * ( tobr@mw.lpc.ethz.ch).
17 * To incorporate the context diff option -c in the program, the source code
18 * for the program cdiff has been copied to the end of this program. Only
19 * slight modifications for the cdiff code to work within the program diff
20 * were made( e.g. main() -> context_diff()).
23 * -c, -C n where n=0,1,...:
24 * produces a context diff as the program cdiff. The default is to
25 * print 3 lines of context, this value can be changed with -C
26 * ( e.g. -C 5 prints five lines of context.)
27 * -e : Prints an ed script, so you can convert <file1> to <file2> with
28 * the command ed <file1> < `diff -e <file1> <file2>`.
29 * -b : Causes trailing blanks to be ignored and spaces of multiple blanks
30 * to be reduced to one blank before comparison.
31 *-----------------------------------------------------------------------------
36 #include <limits.h> /* NAME_MAX for maximal filename length */
37 #include <string.h> /* string manipulation */
38 #include <sys/types.h>
47 /* These definitions are needed only to suppress warning messages. */
48 #define Nullfp ((FILE*)0)
49 #define Nullch ((char*)0)
50 #define NullStructLine ((struct line *)0)
52 #define LINELEN 128 /* max line length included in diff */
55 #define NOT_SET 0 /* Defines to characterise if a flag */
56 #define SET 1 /* is set */
58 /* Indexes of the warning-message array */
59 #define EXCLUSIVE_OPTIONS 0
60 #define CANNOT_OPEN_FILE 1
62 /* Used to define the mode */
64 undefined
, context
, ed_mode
67 /* Global variables for the 'normal' diff part */
68 char *progname
; /* program name (on command line) */
69 int diffs
= 0; /* number of differences */
70 MODE mode
; /* which mode is used */
71 int severe_error
; /* nonzero after severe, non-fatal error */
73 /* The following global variables are used with the -r option:
74 * for every pair of files that are different, a "command line" of the
75 * form "diff <options> <oldfile> <newfile>" is printed before the real
77 int firstoutput
= 1; /* flag to print one time */
78 char options_string
[10]; /* string to hold command line options */
79 char oldfile
[PATH_MAX
]; /* first file */
80 char newfile
[PATH_MAX
]; /* second file */
83 /* Global variables for the command-line options */
84 int trim_blanks
= NOT_SET
; /* SET if -b specified */
85 int recursive_dir
= NOT_SET
; /* SET if -r specified */
86 int context_lines
= 3; /* numbers of lines in a context */
87 static int offset
; /* offset of the actual line number for -e */
89 /* Function prototypes for the functions in this file */
91 _PROTOTYPE(int main
, (int argc
, char **argv
));
92 _PROTOTYPE(void process_command_line
, (int argc
, char **argv
));
93 _PROTOTYPE(void analyse_input_files
, (char *arg1
, char *arg2
, char *input1
,
95 _PROTOTYPE(void diff
, (char *filename1
, char *filename2
));
96 _PROTOTYPE(FILE *check_file
, (char *name
));
97 _PROTOTYPE(void build_option_string
, (void ));
98 _PROTOTYPE(void fatal_error
, (char *fmt
, char *s
));
99 _PROTOTYPE(void warn
, (int number
, char *string
));
100 _PROTOTYPE(void trimming_blanks
, (char *l_text
));
101 _PROTOTYPE(char *filename
, (char *path_string
));
102 _PROTOTYPE(struct line
*new_line
, (int size
));
103 _PROTOTYPE(void free_line
, (struct line
*l
));
104 _PROTOTYPE(int equal_line
, (struct line
*l1
, struct line
*l2
));
105 _PROTOTYPE(int equal_3
, (struct line
*l1
, struct line
*l2
));
106 _PROTOTYPE(struct line
*read_line
, (FILE *fp
));
107 _PROTOTYPE(void advance
, (struct f
*f
));
108 _PROTOTYPE(void aside
, (struct f
*f
, struct line
*l
));
109 _PROTOTYPE(struct line
*next
, (struct f
*f
));
110 _PROTOTYPE(void init_f
, (struct f
*f
, FILE *fp
));
111 _PROTOTYPE(void update
, (struct f
*f
, char *s
));
112 _PROTOTYPE(void __diff
, (FILE *fp1
, FILE *fp2
));
113 _PROTOTYPE(void differ
, (struct f
*f1
, struct f
*f2
));
114 _PROTOTYPE(int wlen
, (struct f
*f
));
115 _PROTOTYPE(void range
, (int a
, int b
));
116 _PROTOTYPE(void cdiff
, (char *old
, char *new, FILE *file1
, FILE *file2
));
117 _PROTOTYPE(void dumphunk
, (void ));
118 _PROTOTYPE(char *getold
, (int targ
));
119 _PROTOTYPE(char *getnew
, (int targ
));
120 _PROTOTYPE(int isdir
, (char *path
));
121 _PROTOTYPE(void diff_recursive
, (char *dir1
, char *dir2
));
122 _PROTOTYPE(void file_type_error
, (char *filename1
, char *filename2
,
123 struct stat
*statbuf1
, struct stat
*statbuf2
));
124 _PROTOTYPE(void *xmalloc
, (size_t size
));
125 _PROTOTYPE(void *xrealloc
, (void *ptr
, size_t size
));
131 char file1
[PATH_MAX
], file2
[PATH_MAX
];
132 extern int optind
; /* index of the current string in argv */
135 process_command_line(argc
, argv
);
137 analyse_input_files(argv
[optind
], argv
[optind
+ 1], file1
, file2
);
140 if (recursive_dir
== SET
) {
141 build_option_string();
142 diff_recursive(file1
, file2
);
147 return(severe_error
? 2 : diffs
> 0 ? 1 : 0);
150 /* Process the command line and set the flags for the different
151 * options. the processing of the command line is done with the
152 * getopt() library function. a minimal error processing is done
153 * for the number of command line arguments. */
154 void process_command_line(argc
, argv
)
155 int argc
; /* number of arguments on command line */
156 char **argv
; /* ** to arguments on command line */
159 extern char *optarg
; /* points to string with options */
160 extern int optind
; /* index of the current string in argv */
162 /* Are there enough arguments? */
164 fatal_error("Usage: %s [-c|-e|-C n][-br] file1 file2\n", progname
);
167 /* Process all options using getopt() */
168 while ((c
= getopt(argc
, argv
, "ceC:br")) != -1) {
171 if (mode
!= undefined
) warn(EXCLUSIVE_OPTIONS
, "c");
176 if (mode
!= undefined
) warn(EXCLUSIVE_OPTIONS
, "e");
180 if (mode
!= undefined
) warn(EXCLUSIVE_OPTIONS
, "C");
182 context_lines
= atoi(optarg
);
184 case 'b': trim_blanks
= SET
; break;
185 case 'r': recursive_dir
= SET
; break;
191 /* We should have two arguments left */
192 if ((argc
- optind
) != 2)
193 fatal_error("Need exactly two input file-names!\n", "");
196 /* Analyse_input_files takes the two input files on the command line
197 * and decides what to do. returns the (corrected) filenames that
198 * can be used to call diff().
199 * if two directories are given, then a recursive diff is done.
200 * one directory and one filename compares the file with <filename>
201 * in the directory <directory> with <filename>.
202 * if two filenames are specified, no special action takes place.
204 void analyse_input_files(arg1
, arg2
, input1
, input2
)
205 char *arg1
, *arg2
; /* filenames on the command line */
206 char *input1
, *input2
; /* filenames to be used with diff() */
208 int stat1
= 0, stat2
= 0;
210 if (strcmp(arg1
, "-") != 0)
211 stat1
= isdir(arg1
); /* != 0 <-> arg1 is directory */
212 if (strcmp(arg2
, "-") != 0) stat2
= isdir(arg2
);
214 fprintf(stderr
, "%s, stat = %d\n", arg1
, stat1
);
215 fprintf(stderr
, "%s, stat = %d\n", arg2
, stat2
);
217 if (stat1
&& stat2
) { /* both arg1 and arg2 are directories */
219 strcpy(input1
, arg1
);
220 strcpy(input2
, arg2
);
223 if (stat1
!= 0) { /* arg1 is a dir, arg2 not */
224 if (strcmp(arg2
, "-") != 0) { /* arg2 != stdin */
225 strcpy(input1
, arg1
);
227 strcat(input1
, arg2
);
228 strcpy(input2
, arg2
);
231 fatal_error("cannot compare stdin (-) with a directory!", "");
234 if (stat2
!= 0) { /* arg2 is a dir, arg1 not */
235 if (strcmp(arg1
, "-") != 0) { /* arg1 != stdin */
236 strcpy(input1
, arg1
);
237 strcpy(input2
, arg2
);
239 strcat(input2
, arg1
);
241 } else { /* arg1 == stdin */
242 fatal_error("cannot compare stdin (-) with a directory!", "");
246 /* Both arg1 and arg2 are normal files */
247 strcpy(input1
, arg1
);
248 strcpy(input2
, arg2
);
251 /* Diff() is the front end for all modes of the program diff, execpt
252 * the recursive_dir option.
253 * diff() expects the filenames of the two files to be compared as
254 * arguments. the mode is determined from the global variable mode.
256 void diff(filename1
, filename2
)
257 char *filename1
, *filename2
;
259 FILE *file1
= check_file(filename1
);
260 FILE *file2
= check_file(filename2
);
261 struct stat statbuf1
, statbuf2
;
263 if ((file1
!= Nullfp
) && (file2
!= Nullfp
)) {
264 /* If we do a recursive diff, then we don't compare block
265 * special, character special or FIFO special files to any
267 fstat(fileno(file1
), &statbuf1
);
268 fstat(fileno(file2
), &statbuf2
);
269 if ((((statbuf1
.st_mode
& S_IFREG
) != S_IFREG
) ||
270 ((statbuf2
.st_mode
& S_IFREG
) != S_IFREG
)) &&
271 (recursive_dir
== SET
)) {
272 file_type_error(filename1
, filename2
, &statbuf1
, &statbuf2
);
276 cdiff(filename1
, filename2
, file1
, file2
);
280 __diff(file1
, file2
);
281 if (mode
== ed_mode
) printf("w\n");
287 if (file1
!= Nullfp
) fclose(file1
);
288 if (file2
!= Nullfp
) fclose(file2
);
291 /* Check_file() opens the fileptr with name <filename>. if <filename>
292 * equals "-" stdin is associated with the return value.
294 FILE *check_file(name
)
299 if (strcmp(name
, "-") == 0) {
302 temp
= fopen(name
, "r");
303 if (temp
== Nullfp
) warn(CANNOT_OPEN_FILE
, name
);
308 /* Build_option_string() is called before recursive_dir() is called
309 * from the main() function. its purpose is to build the string that
310 * is used on the command line to get the current operation mode.
313 void build_option_string()
316 case ed_mode
:sprintf(options_string
, "-e");
319 if (context_lines
== 3)
320 sprintf(options_string
, "-c");
322 sprintf(options_string
, "-C %d", context_lines
);
329 /* The fatal error handler.
330 * Expects a format string and a string as arguments. The arguments
331 * are printed to stderr and the program exits with an error code 2.
333 void fatal_error(fmt
, s
)
334 char *fmt
; /* the format sttring to be printed */
335 char *s
; /* string to be inserted into the format
338 fprintf(stderr
, "%s: ", progname
);
339 fprintf(stderr
, fmt
, s
);
340 fprintf(stderr
, "\n");
344 /* This function prints non fatal error messages to stderr.
345 * Expects the index of the message to be printed and a pointer
346 * to the (optional) string to be printed.
349 void warn(number
, string
)
350 int number
; /* index of the warning */
351 char *string
; /* string to be inserted to the warning */
353 static char *warning
[] = {
354 "%s: The options -c, -e, -C n are mutually exclusive! Assuming -%c\n",
355 "%s: cannot open file %s for reading\n",
357 fprintf(stderr
, warning
[number
], progname
, string
);
360 /* Function used with the optione -b, trims the blanks in a input line:
361 * - blanks between words are reduced to one
362 * - trailing blanks are eliminated.
364 void trimming_blanks(l_text
)
365 char *l_text
; /* begin of the char array */
368 char *copy_to
, *copy_from
;
374 while (*(++copy_from
) == ' ');
375 if (*copy_from
!= '\n') copy_to
++;
376 while (*copy_from
!= '\0') *(copy_to
++) = *(copy_from
++);
379 } while (*(++line
) != '\0');
383 /* Filename separates the filename and the relative path in path_string.
384 * Returns the filename with a leading /
386 char *filename(path_string
)
389 char name
[NAME_MAX
+ 2]; /* filename plus / */
393 ptr
= strrchr(path_string
, '/');
395 if (ptr
== 0) { /* no / in path_string, only a filename */
396 strcat(name
, path_string
);
404 /* The line module: one member in a linked list of lines. */
406 struct line
*l_next
; /* pointer to the next line */
407 char l_eof
; /* == 0 if last line in file */
408 char *l_text
; /* array with the text */
411 struct line
*freelist
= 0;
412 #define stepup(ll) ( ((ll) && ((ll)->l_eof==0)) ? (ll)->l_next : (ll) )
414 /* Function to allocate space for a new line containing SIZE chars */
415 struct line
*new_line(size
)
418 register struct line
*l
;
420 if ((l
= freelist
) != NullStructLine
)
421 freelist
= freelist
->l_next
;
423 l
= (struct line
*) xmalloc(3 * sizeof(void *));
424 l
->l_text
= (char *) xmalloc((size
+ 2) * sizeof(char));
425 if ((l
== 0) || (l
->l_text
== 0)) fatal_error("Out of memory", "");
431 /* Free_line() releases storage allocated for <l>. */
433 register struct line
*l
;
435 l
->l_next
= freelist
;
439 /* Equal_line() compares two lines, <l1> and <l2>.
440 * the returned value is the result of the strcmp() function.
442 int equal_line(l1
, l2
)
443 struct line
*l1
, *l2
;
445 if (l1
== 0 || l2
== 0)
447 else if (l1
->l_eof
|| l2
->l_eof
)
448 return(l1
->l_eof
== l2
->l_eof
);
450 return(strcmp(l1
->l_text
, l2
->l_text
) == 0);
454 struct line
*l1
, *l2
;
456 register int i
, ansr
;
461 fprintf(stderr
, "\t(null)\n");
463 fprintf(stderr
, "\t(eof)\n");
465 fprintf(stderr
, "\t%s", l1
->l_text
);
467 fprintf(stderr
, "\t(null)\n");
469 fprintf(stderr
, "\t(eof)\n");
471 fprintf(stderr
, "\t%s", l2
->l_text
);
473 for (i
= 0; i
< 3; ++i
) {
474 if (!equal_line(l1
, l2
)) {
482 fprintf(stderr
, "\t%d\n", ansr
);
491 register struct line
*l
= new_line(LINELEN
);
495 (p
= &(l
->l_text
[LINELEN
]))[1] = '\377';
497 if (fgets(l
->l_text
, LINELEN
+ 2, fp
) == 0) {
500 } else if ((p
[1] & 0377) != 0377 && *p
!= '\n') {
501 while ((c
= fgetc(fp
)) != '\n' && c
!= EOF
) {
507 if (trim_blanks
== SET
) {
509 printf("xxx %s xxx\n", l
->l_text
);
511 trimming_blanks(l
->l_text
);
513 printf("xxx %s xxx\n", l
->l_text
);
519 /* File window handler */
521 struct line
*f_bwin
, *f_ewin
;
522 struct line
*f_aside
;
523 int f_linecnt
; /* line number in file of last advanced line */
528 register struct f
*f
;
530 register struct line
*l
;
532 if ((l
= f
->f_bwin
) != NullStructLine
) {
534 f
->f_bwin
= f
->f_ewin
= 0;
536 f
->f_bwin
= l
->l_next
;
546 register struct line
*ll
;
549 if ((ll
= l
->l_next
) != NullStructLine
) {
550 while (ll
->l_next
) ll
= ll
->l_next
;
551 ll
->l_next
= f
->f_aside
;
552 f
->f_aside
= l
->l_next
;
560 register struct f
*f
;
562 register struct line
*l
;
564 if ((l
= f
->f_aside
) != NullStructLine
) {
565 f
->f_aside
= l
->l_next
;
568 l
= read_line(f
->f_fp
);
571 f
->f_bwin
= f
->f_ewin
= l
;
573 if (f
->f_ewin
->l_eof
&& l
->l_eof
) {
577 f
->f_ewin
->l_next
= l
;
585 /* Init_f() initialises a window structure (struct f). <fp> is the
586 * file associated with <f>.
589 register struct f
*f
;
592 f
->f_bwin
= f
->f_ewin
= f
->f_aside
= 0;
598 /* Update() prints a window. <f> is a pointer to the window, <s> is the
599 * string containing the "prefix" to the printout( either "<" or ">").
600 * after completion of update(), the window is empty.
603 register struct f
*f
;
609 if (firstoutput
&& (recursive_dir
== SET
)) {
610 printf("diff %s %s %s\n", options_string
, oldfile
, newfile
);
613 while (f
->f_bwin
&& f
->f_bwin
!= f
->f_ewin
) {
614 if (mode
!= ed_mode
) {
615 printf("%s%s", s
, f
->f_bwin
->l_text
);
618 printf("ed_mode: test for only dot");
619 printf("%s", f
->f_bwin
->l_text
);
621 help
= f
->f_bwin
->l_text
;
622 while ((*help
== ' ') ||
625 if (*(help
++) == '.') only_dot
++;
626 if (only_dot
> 1) break;
629 /* If only_dot is equal 1, there is only one dot on
630 * the line, so we have to take special actions.
631 * f the line with only one dot is found, we output
632 * two dots (".."), terminate the append modus and
633 * substitute "." for "..". Afterwards we restart
634 * with the append command. */
635 if (*help
== '\n' && only_dot
== 1) {
636 help
= f
->f_bwin
->l_text
;
637 while (*help
!= '\0') {
638 if (*help
== '.') printf(".");
639 putchar((int) *(help
++));
642 printf(".s/\\.\\././\n");
645 printf("%s%s", s
, f
->f_bwin
->l_text
);
652 /* __Diff(), performs the "core operation" of the program.
653 * Expects two file-pointers as arguments. This functions does
654 * *not* check if the file-pointers are valid.
657 void __diff(fp1
, fp2
)
661 struct line
*l1
, *s1
, *b1
, *l2
, *s2
, *b2
;
662 register struct line
*ll
;
668 while ((l1
->l_eof
== 0) || (l2
->l_eof
== 0)) {
669 if (equal_line(l1
, l2
)) {
679 /* Read several more lines */
684 /* Start searching */
689 if (equal_3(ll
, b2
)) {
697 if (ll
->l_eof
) break;
705 if (equal_3(b1
, ll
)) {
713 if (ll
->l_eof
!= 0) break;
721 /* Both of the files reached EOF */
724 /* Differ() prints the differences between files. the arguments <f1> and
725 * <f2> are pointers to the two windows, where the differences are.
728 register struct f
*f1
, *f2
;
730 int cnt1
= f1
->f_linecnt
, len1
= wlen(f1
);
731 int cnt2
= f2
->f_linecnt
, len2
= wlen(f2
);
732 if ((len1
!= 0) || (len2
!= 0)) {
734 if (mode
== ed_mode
) {
736 printf("%d a\n", cnt1
);
742 range(cnt2
+ 1, cnt2
+ len2
);
744 } else if (len2
== 0) {
745 if (mode
== ed_mode
) {
747 range(cnt1
+ 1, cnt1
+ len1
);
750 while (f1
->f_bwin
&& f1
->f_bwin
!= f1
->f_ewin
)
753 range(cnt1
+ 1, cnt1
+ len1
);
757 if (mode
!= ed_mode
) {
758 range(cnt1
+ 1, cnt1
+ len1
);
760 range(cnt2
+ 1, cnt2
+ len2
);
764 range(cnt1
+ 1, cnt1
+ len1
);
769 range(cnt1
+ 1, cnt1
+ len1
);
771 printf("%d a\n", cnt1
);
774 offset
-= len1
- len2
;
776 while (f1
->f_bwin
&& f1
->f_bwin
!= f1
->f_ewin
)
780 if (mode
!= ed_mode
) {
782 if (len1
!= 0) update(f1
, "< ");
783 if ((len1
!= 0) && (len2
!= 0)) printf("---\n");
784 if (len2
!= 0) update(f2
, "> ");
791 /* Function wlen() calculates the number of lines in a window. */
796 register struct line
*l
= f
->f_bwin
, *e
= f
->f_ewin
;
798 while (l
&& l
!= e
) {
806 /* Range() prints the line numbers of a range. the arguments <a> and <b>
807 * are the beginning and the ending line number of the range. if
808 * <a> == <b>, only one line number is printed. otherwise <a> and <b> are
809 * separated by a ",".
814 printf(((a
== b
) ? "%d" : "%d,%d"), a
, b
);
817 /* Here follows the code for option -c.
818 * This code is from the cdiff program by Larry Wall. I changed it only
819 * slightly to reflect the POSIX standard and to call the main routine
820 * as function context_diff().
823 /* Cdiff - context diff Author: Larry Wall */
825 /* These global variables are still here from the original cdiff program...
826 * I was to lazy just to sort them out...
831 int oldmin
, oldmax
, newmin
, newmax
;
832 int oldbeg
, oldend
, newbeg
, newend
;
833 int preoldmax
, prenewmax
;
834 int preoldbeg
, preoldend
, prenewbeg
, prenewend
;
835 int oldwanted
, newwanted
;
837 char *oldhunk
, *newhunk
;
838 size_t oldsize
, oldalloc
, newsize
, newalloc
;
840 int oldline
, newline
; /* Jose */
842 void cdiff(old
, new, file1
, file2
)
843 char *old
, *new; /* The names of the two files to be compared */
844 FILE *file1
, *file2
; /* The corresponding file-pointers */
850 char *newmark
, *oldmark
;
859 oldhunk
= (char *) xmalloc(oldalloc
);
861 newhunk
= (char *) xmalloc(newalloc
);
864 /* The context diff spawns a new process that executes a normal diff
865 * and parses the output.
867 if (trim_blanks
== SET
)
868 sprintf(buff
, "diff -b %s %s", old
, new);
870 sprintf(buff
, "diff %s %s", old
, new);
872 inputfp
= popen(buff
, "r");
874 fprintf(stderr
, "Can't execute diff %s %s, popen failed with %s\n",
875 old
, new, strerror(errno
));
880 oldline
= newline
= 0;
881 while (fgets(buff
, sizeof buff
, inputfp
) != Nullch
) {
883 if (recursive_dir
== SET
) {
884 printf("diff %s %s %s\n", options_string
,
887 fstat(fileno(oldfp
), &statbuf
);
888 printf("*** %s %s", old
, ctime(&statbuf
.st_mtime
));
889 fstat(fileno(newfp
), &statbuf
);
890 printf("--- %s %s", new, ctime(&statbuf
.st_mtime
));
893 if (isdigit(*buff
)) {
895 for (s
= buff
; isdigit(*s
); s
++);
899 for (; isdigit(*s
); s
++);
903 if (*s
!= 'a' && *s
!= 'd' && *s
!= 'c') {
904 fprintf(stderr
, "Unparseable input: %s", s
);
910 for (; isdigit(*s
); s
++);
914 for (; isdigit(*s
); s
++);
918 if (*s
!= '\n' && *s
!= ' ') {
919 fprintf(stderr
, "Unparseable input: %s", s
);
922 newmark
= oldmark
= "! ";
931 oldbeg
= oldmin
- context_lines
;
932 oldend
= oldmax
+ context_lines
;
933 if (oldbeg
< 1) oldbeg
= 1;
934 newbeg
= newmin
- context_lines
;
935 newend
= newmax
+ context_lines
;
936 if (newbeg
< 1) newbeg
= 1;
938 if (preoldend
< oldbeg
- 1) {
939 if (preoldend
>= 0) {
944 oldwanted
= newwanted
= 0;
945 oldsize
= newsize
= 0;
946 } else { /* we want to append to previous hunk */
947 oldbeg
= preoldmax
+ 1;
948 newbeg
= prenewmax
+ 1;
951 for (i
= oldbeg
; i
<= oldmax
; i
++) {
954 oldend
= oldmax
= i
- 1;
957 len
= strlen(line
) + 2;
958 if (oldsize
+ len
+ 1 >= oldalloc
) {
960 oldhunk
= (char *) xrealloc(oldhunk
, oldalloc
);
963 strcpy(oldhunk
+ oldsize
, oldmark
);
966 strcpy(oldhunk
+ oldsize
, " ");
968 strcpy(oldhunk
+ oldsize
+ 2, line
);
974 for (i
= newbeg
; i
<= newmax
; i
++) {
977 newend
= newmax
= i
- 1;
980 len
= strlen(line
) + 2;
981 if (newsize
+ len
+ 1 >= newalloc
) {
983 newhunk
= (char *) xrealloc(newhunk
, newalloc
);
986 strcpy(newhunk
+ newsize
, newmark
);
989 strcpy(newhunk
+ newsize
, " ");
991 strcpy(newhunk
+ newsize
+ 2, line
);
998 status
= pclose(inputfp
);
999 if (status
!= 0) diffs
++;
1000 if (!WIFEXITED(status
) || WEXITSTATUS(status
) > 1) severe_error
= 1;
1002 if (preoldend
>= 0) {
1013 for (i
= preoldmax
+ 1; i
<= preoldend
; i
++) {
1019 len
= strlen(line
) + 2;
1020 if (oldsize
+ len
+ 1 >= oldalloc
) {
1022 oldhunk
= (char *) xrealloc(oldhunk
, oldalloc
);
1024 strcpy(oldhunk
+ oldsize
, " ");
1025 strcpy(oldhunk
+ oldsize
+ 2, line
);
1028 for (i
= prenewmax
+ 1; i
<= prenewend
; i
++) {
1034 len
= strlen(line
) + 2;
1035 if (newsize
+ len
+ 1 >= newalloc
) {
1037 newhunk
= (char *) xrealloc(newhunk
, newalloc
);
1039 strcpy(newhunk
+ newsize
, " ");
1040 strcpy(newhunk
+ newsize
+ 2, line
);
1043 fputs("***************\n", stdout
);
1044 if (preoldbeg
>= preoldend
) {
1045 printf("*** %d ****\n", preoldend
);
1047 printf("*** %d,%d ****\n", preoldbeg
, preoldend
);
1050 fputs(oldhunk
, stdout
);
1054 if (prenewbeg
>= prenewend
) {
1055 printf("--- %d ----\n", prenewend
);
1057 printf("--- %d,%d ----\n", prenewbeg
, prenewend
);
1060 fputs(newhunk
, stdout
);
1069 while (fgets(buff
, sizeof buff
, oldfp
) != Nullch
) {
1071 if (oldline
== targ
) return buff
;
1079 while (fgets(buff
, sizeof buff
, newfp
) != Nullch
) {
1081 if (newline
== targ
) return buff
;
1087 /* Isdir() checks, if <path> is the name of a directory. a return value
1088 * is 0, <path> is a normal file. otherwise the <path> is a directory.
1095 if (buf
.st_mode
& S_IFDIR
) { /* path is a directory */
1104 /* This is the "main" function if a diff of two directories has to be
1105 * done. diff_recursive() expects the names of the two directories to
1107 void diff_recursive(dir1
, dir2
)
1111 char file1
[PATH_MAX
], file2
[PATH_MAX
];
1112 char jointfile1
[PATH_MAX
], jointfile2
[PATH_MAX
];
1113 char command
[PATH_MAX
];
1114 int difference
, eof1
, eof2
;
1116 sprintf(command
, "ls %s", dir1
);
1117 ls1
= popen(command
, "r");
1118 sprintf(command
, "ls %s", dir2
);
1119 ls2
= popen(command
, "r");
1121 if ((ls1
== NULL
) || (ls2
== NULL
))
1122 fatal_error("cannot execute ls!", "");
1125 eof1
= fscanf(ls1
, "%s\n", file1
);
1127 eof2
= fscanf(ls2
, "%s\n", file2
);
1129 while ((file1
[0] != '\0') && (file2
[0] != '\0')) {
1130 difference
= strcmp(file1
, file2
);
1131 while (difference
!= 0) {
1132 if (difference
< 0) {
1133 printf("Only in %s: %s\n", dir1
, file1
);
1135 eof1
= fscanf(ls1
, "%s\n", file1
);
1136 if (file1
[0] == '\0') break;
1138 printf("Only in %s: %s\n", dir2
, file2
);
1140 eof2
= fscanf(ls2
, "%s\n", file2
);
1141 if (file2
[0] == '\0') break;
1143 difference
= strcmp(file1
, file2
);
1145 if (eof1
!= EOF
&& eof2
!= EOF
) {
1146 strcpy(jointfile1
, dir1
);
1147 strcat(jointfile1
, "/");
1148 strcat(jointfile1
, file1
);
1149 strcpy(jointfile2
, dir2
);
1150 strcat(jointfile2
, "/");
1151 strcat(jointfile2
, file2
);
1153 if ((isdir(jointfile1
) != 0) && (isdir(jointfile2
) != 0)) {
1154 printf("Common subdirectories: %s and %s\n",
1155 jointfile1
, jointfile2
);
1156 diff_recursive(jointfile1
, jointfile2
);
1159 strcpy(oldfile
, jointfile1
);
1160 strcpy(newfile
, jointfile2
);
1161 diff(jointfile1
, jointfile2
);
1164 eof1
= fscanf(ls1
, "%s\n", file1
);
1166 eof2
= fscanf(ls2
, "%s\n", file2
);
1170 if (file1
[0] != '\0') { /* first arg still has files */
1172 printf("Only in %s: %s\n", dir1
, file1
);
1173 eof1
= fscanf(ls1
, " %s\n", file1
);
1174 } while (eof1
!= EOF
);
1176 if (file2
[0] != '\0') {
1178 printf("Only in %s: %s\n", dir2
, file2
);
1179 eof2
= fscanf(ls2
, " %s\n", file2
);
1180 } while (eof2
!= EOF
);
1182 if (pclose(ls1
) != 0) severe_error
= 1;
1183 if (pclose(ls2
) != 0) severe_error
= 1;
1187 /* File_type_error is called, if in a recursive diff ( -r) one of the two
1188 * files a block special, a character special or a FIFO special file is.
1189 * The corresponding error message is printed here. */
1190 void file_type_error(filename1
, filename2
, statbuf1
, statbuf2
)
1191 char *filename1
, *filename2
;
1192 struct stat
*statbuf1
, *statbuf2
;
1194 char type1
[25], type2
[25];
1196 switch (statbuf1
->st_mode
& S_IFMT
) { /* select only file mode */
1198 sprintf(type1
, "regular file ");
1201 sprintf(type1
, "block special file ");
1203 case S_IFDIR
: sprintf(type1
, "directory "); break;
1205 sprintf(type1
, "character special file ");
1208 sprintf(type1
, "FIFO special file ");
1212 switch (statbuf2
->st_mode
& S_IFMT
) { /* select only file mode */
1214 sprintf(type2
, "regular file ");
1217 sprintf(type2
, "block special file ");
1219 case S_IFDIR
: sprintf(type2
, "directory "); break;
1221 sprintf(type2
, "character special file ");
1224 sprintf(type2
, "FIFO special file ");
1227 printf("File %s is a %s while file %s is a %s\n",
1228 filename1
, type1
, filename2
, type2
);
1238 fprintf(stderr
, "%s: out of memory\n", progname
);
1244 void *xrealloc(ptr
, size
)
1248 ptr
= realloc(ptr
, size
);
1250 fprintf(stderr
, "%s: out of memory\n", progname
);