1 /***********************************************************************
3 * This software is part of the ast package *
4 * Copyright (c) 1992-2010 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Common Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
13 * Information and Software Systems Research *
17 * Glenn Fowler <gsf@research.att.com> *
18 * David Korn <dgk@research.att.com> *
20 ***********************************************************************/
25 * AT&T Bell Laboratories
30 static const char usage
[] =
31 "[-?\n@(#)$Id: cmp (AT&T Research) 2009-01-05 $\n]"
33 "[+NAME?cmp - compare two files]"
34 "[+DESCRIPTION?\bcmp\b compares two files \afile1\a and \afile2\a. "
35 "\bcmp\b writes no output if the files are the same. By default, "
36 "if the files differ, the byte and line number at which the "
37 "first difference occurred are written to standard output. Bytes "
38 "and lines are numbered beginning with 1.]"
39 "[+?If \askip1\a or \askip2\a are specified, or the \b-i\b option is "
40 "specified, initial bytes of the corresponding file are skipped "
41 "before beginning the compare. The skip values are in bytes or "
42 "can have a suffix of \bk\b for kilobytes or \bm\b for megabytes.]"
43 "[+?If either \afile1\a or \afiles2\a is \b-\b, \bcmp\b "
44 "uses standard input starting at the current location.]"
45 "[c:print-chars?Writes control characters as a \b^\b followed by a letter of "
46 "the alphabet and precede characters that have the high bit set with "
47 "\bM-\b as with \bcat\b(1).]"
48 "[i:ignore-initial]#[skip:=0?Sets default skip values for the operands "
49 "\askip1\a and \askip2\a to \askip\a.]"
50 "[l:verbose?Write the decimal byte number and the differing bytes (in octal) "
51 "for each difference.]"
52 "[s:quiet|silent?Write nothing for differing files; return non-zero "
53 "exit status only.] ]"
55 "\nfile1 file2 [skip1 [skip2]]\n"
58 "[+0?The files or portions compared are identical.]"
59 "[+1?The files are different.]"
60 "[+>1?An error occurred.]"
62 "[+SEE ALSO?\bcomm\b(1), \bdiff\b(1), \bcat\b(1)]"
74 #define cntl(x) (x&037)
75 #define printchar(c) ((c) ^ ('A'-cntl('A')))
77 static void outchar(Sfio_t
*out
, register int c
, int delim
)
99 cmp(const char* file1
, Sfio_t
* f1
, const char* file2
, Sfio_t
* f2
, int flags
)
103 register unsigned char* p1
= 0;
104 register unsigned char* p2
= 0;
105 register Sfoff_t lines
= 1;
106 register unsigned char* e1
= 0;
107 register unsigned char* e2
= 0;
114 if ((c1
= e1
- p1
) <= 0)
116 if (!(p1
= (unsigned char*)sfreserve(f1
, SF_UNBOUND
, 0)) || (c1
= sfvalue(f1
)) <= 0)
118 if ((e2
- p2
) > 0 || sfreserve(f2
, SF_UNBOUND
, 0) && sfvalue(f2
) > 0)
121 if (!(flags
& CMP_SILENT
))
122 error(ERROR_exit(1), "EOF on %s", file1
);
128 if ((c2
= e2
- p2
) <= 0)
130 if (!(p2
= (unsigned char*)sfreserve(f2
, SF_UNBOUND
, 0)) || (c2
= sfvalue(f2
)) <= 0)
132 if (!(flags
& CMP_SILENT
))
133 error(ERROR_exit(1), "EOF on %s", file2
);
141 if (flags
& CMP_SILENT
)
143 if (memcmp(p1
, p2
, c1
))
153 if ((c1
= *p1
++) != *p2
++)
160 sfprintf(sfstdout
, "%6I*d ", sizeof(pos
), pos
- (last
- p1
));
161 outchar(sfstdout
,c1
,' ');
162 outchar(sfstdout
,*(p2
-1),'\n');
165 sfprintf(sfstdout
, "%6I*d %3o %3o\n", sizeof(pos
), pos
- (last
- p1
), c1
, *(p2
- 1));
169 sfprintf(sfstdout
, "%s %s differ: char %I*d, line %I*u\n", file1
, file2
, sizeof(pos
), pos
- (last
- p1
), sizeof(lines
), lines
);
181 b_cmp(int argc
, register char** argv
, void* context
)
198 cmdinit(argc
, argv
, context
, ERROR_CATALOG
, 0);
199 while (n
= optget(argv
, usage
)) switch (n
)
202 flags
|= CMP_VERBOSE
;
211 o1
= o2
= opt_info
.num
;
214 error(2, "%s", opt_info
.arg
);
217 error(ERROR_usage(2), "%s", opt_info
.arg
);
220 argv
+= opt_info
.index
;
221 if (error_info
.errors
|| !(file1
= *argv
++) || !(file2
= *argv
++))
222 error(ERROR_usage(2), "%s", optusage(NiL
));
224 if (streq(file1
, "-"))
226 else if (!(f1
= sfopen(NiL
, file1
, "r")))
228 if (!(flags
& CMP_SILENT
))
229 error(ERROR_system(0), "%s: cannot open", file1
);
232 if (streq(file2
, "-"))
234 else if (!(f2
= sfopen(NiL
, file2
, "r")))
236 if (!(flags
& CMP_SILENT
))
237 error(ERROR_system(0), "%s: cannot open", file2
);
242 o1
= strtol(s
, &e
, 0);
245 error(ERROR_exit(0), "%s: %s: invalid skip", file1
, s
);
250 o2
= strtol(s
, &e
, 0);
253 error(ERROR_exit(0), "%s: %s: invalid skip", file2
, s
);
259 error(ERROR_usage(0), "%s", optusage(NiL
));
263 if (o1
&& sfseek(f1
, o1
, SEEK_SET
) != o1
)
265 if (!(flags
& CMP_SILENT
))
266 error(ERROR_exit(0), "EOF on %s", file1
);
270 if (o2
&& sfseek(f2
, o2
, SEEK_SET
) != o2
)
272 if (!(flags
& CMP_SILENT
))
273 error(ERROR_exit(0), "EOF on %s", file2
);
277 if (fstat(sffileno(f1
), &s1
))
278 error(ERROR_system(0), "%s: cannot stat", file1
);
279 else if (fstat(sffileno(f2
), &s2
))
280 error(ERROR_system(0), "%s: cannot stat", file1
);
281 else if (s1
.st_ino
== s2
.st_ino
&& s1
.st_dev
== s2
.st_dev
&& o1
== o2
)
284 n
= ((flags
& CMP_SILENT
) && S_ISREG(s1
.st_mode
) && S_ISREG(s2
.st_mode
) && (s1
.st_size
- o1
) != (s2
.st_size
- o2
)) ? 1 : cmp(file1
, f1
, file2
, f2
, flags
);
286 if (f1
&& f1
!= sfstdin
) sfclose(f1
);
287 if (f2
&& f2
!= sfstdin
) sfclose(f2
);