1/***********************************************************************
2*                                                                      *
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                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*            http://www.opensource.org/licenses/cpl1.0.txt             *
11*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*                                                                      *
13*              Information and Software Systems Research               *
14*                            AT&T Research                             *
15*                           Florham Park NJ                            *
16*                                                                      *
17*                 Glenn Fowler <gsf@research.att.com>                  *
18*                  David Korn <dgk@research.att.com>                   *
19*                                                                      *
20***********************************************************************/
21#pragma prototyped
22/*
23 * David Korn
24 * Glenn Fowler
25 * AT&T Bell Laboratories
26 *
27 * cmp
28 */
29
30static const char usage[] =
31"[-?\n@(#)$Id: cmp (AT&T Research) 2009-01-05 $\n]"
32USAGE_LICENSE
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.] ]"
54"\n"
55"\nfile1 file2 [skip1 [skip2]]\n"
56"\n"
57"[+EXIT STATUS?]{"
58	"[+0?The files or portions compared are identical.]"
59	"[+1?The files are different.]"
60	"[+>1?An error occurred.]"
61"}"
62"[+SEE ALSO?\bcomm\b(1), \bdiff\b(1), \bcat\b(1)]"
63;
64
65
66#include <cmd.h>
67#include <ls.h>
68#include <ctype.h>
69
70#define CMP_VERBOSE	1
71#define CMP_SILENT	2
72#define CMP_CHARS	4
73
74#define cntl(x)		(x&037)
75#define printchar(c)	((c) ^ ('A'-cntl('A')))
76
77static void outchar(Sfio_t *out, register int c, int delim)
78{
79	if(c&0200)
80	{
81		sfputc(out,'M');
82		sfputc(out,'-');
83		c &= ~0200;
84	}
85	else if(!isprint(c))
86	{
87		sfputc(out,'^');
88		c = printchar(c);
89	}
90	sfputc(out,c);
91	sfputc(out,delim);
92}
93
94/*
95 * compare two files
96 */
97
98static int
99cmp(const char* file1, Sfio_t* f1, const char* file2, Sfio_t* f2, int flags)
100{
101	register int		c1;
102	register int		c2;
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;
108	Sfoff_t			pos = 0;
109	int			ret = 0;
110	unsigned char*		last;
111
112	for (;;)
113	{
114		if ((c1 = e1 - p1) <= 0)
115		{
116			if (!(p1 = (unsigned char*)sfreserve(f1, SF_UNBOUND, 0)) || (c1 = sfvalue(f1)) <= 0)
117			{
118				if ((e2 - p2) > 0 || sfreserve(f2, SF_UNBOUND, 0) && sfvalue(f2) > 0)
119				{
120					ret = 1;
121					if (!(flags & CMP_SILENT))
122						error(ERROR_exit(1), "EOF on %s", file1);
123				}
124				return(ret);
125			}
126			e1 = p1 + c1;
127		}
128		if ((c2 = e2 - p2) <= 0)
129		{
130			if (!(p2 = (unsigned char*)sfreserve(f2, SF_UNBOUND, 0)) || (c2 = sfvalue(f2)) <= 0)
131			{
132				if (!(flags & CMP_SILENT))
133					error(ERROR_exit(1), "EOF on %s", file2);
134				return(1);
135			}
136			e2 = p2 + c2;
137		}
138		if (c1 > c2)
139			c1 = c2;
140		pos += c1;
141		if (flags & CMP_SILENT)
142		{
143			if (memcmp(p1, p2, c1))
144				return(1);
145			p1 += c1;
146			p2 += c1;
147		}
148		else
149		{
150			last = p1 + c1;
151			while (p1 < last)
152			{
153				if ((c1 = *p1++) != *p2++)
154				{
155					if (flags)
156					{
157						ret = 1;
158						if(flags&CMP_CHARS)
159						{
160							sfprintf(sfstdout, "%6I*d ", sizeof(pos), pos - (last - p1));
161							outchar(sfstdout,c1,' ');
162							outchar(sfstdout,*(p2-1),'\n');
163						}
164						else
165							sfprintf(sfstdout, "%6I*d %3o %3o\n", sizeof(pos), pos - (last - p1), c1, *(p2 - 1));
166					}
167					else
168					{
169						sfprintf(sfstdout, "%s %s differ: char %I*d, line %I*u\n", file1, file2, sizeof(pos), pos - (last - p1), sizeof(lines), lines);
170						return(1);
171					}
172				}
173				if (c1 == '\n')
174					lines++;
175			}
176		}
177	}
178}
179
180int
181b_cmp(int argc, register char** argv, void* context)
182{
183	char*		s;
184	char*		e;
185	Sfio_t*		f1 = 0;
186	Sfio_t*		f2 = 0;
187	char*		file1;
188	char*		file2;
189	int		n;
190	off_t		o1 = 0;
191	off_t		o2 = 0;
192	struct stat	s1;
193	struct stat	s2;
194
195	int		flags = 0;
196
197	NoP(argc);
198	cmdinit(argc, argv, context, ERROR_CATALOG, 0);
199	while (n = optget(argv, usage)) switch (n)
200	{
201	case 'l':
202		flags |= CMP_VERBOSE;
203		break;
204	case 's':
205		flags |= CMP_SILENT;
206		break;
207	case 'c':
208		flags |= CMP_CHARS;
209		break;
210	case 'i':
211		o1 = o2 = opt_info.num;
212		break;
213	case ':':
214		error(2, "%s", opt_info.arg);
215		break;
216	case '?':
217		error(ERROR_usage(2), "%s", opt_info.arg);
218		break;
219	}
220	argv += opt_info.index;
221	if (error_info.errors || !(file1 = *argv++) || !(file2 = *argv++))
222		error(ERROR_usage(2), "%s", optusage(NiL));
223	n = 2;
224	if (streq(file1, "-"))
225		f1 = sfstdin;
226	else if (!(f1 = sfopen(NiL, file1, "r")))
227	{
228		if (!(flags & CMP_SILENT))
229			error(ERROR_system(0), "%s: cannot open", file1);
230		goto done;
231	}
232	if (streq(file2, "-"))
233		f2 = sfstdin;
234	else if (!(f2 = sfopen(NiL, file2, "r")))
235	{
236		if (!(flags & CMP_SILENT))
237			error(ERROR_system(0), "%s: cannot open", file2);
238		goto done;
239	}
240	if (s = *argv++)
241	{
242		o1 = strtol(s, &e, 0);
243		if (*e)
244		{
245			error(ERROR_exit(0), "%s: %s: invalid skip", file1, s);
246			goto done;
247		}
248		if (s = *argv++)
249		{
250			o2 = strtol(s, &e, 0);
251			if (*e)
252			{
253				error(ERROR_exit(0), "%s: %s: invalid skip", file2, s);
254				goto done;
255			}
256		}
257		if (*argv)
258		{
259			error(ERROR_usage(0), "%s", optusage(NiL));
260			goto done;
261		}
262	}
263	if (o1 && sfseek(f1, o1, SEEK_SET) != o1)
264	{
265		if (!(flags & CMP_SILENT))
266			error(ERROR_exit(0), "EOF on %s", file1);
267		n = 1;
268		goto done;
269	}
270	if (o2 && sfseek(f2, o2, SEEK_SET) != o2)
271	{
272		if (!(flags & CMP_SILENT))
273			error(ERROR_exit(0), "EOF on %s", file2);
274		n = 1;
275		goto done;
276	}
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)
282		n = 0;
283	else
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);
285 done:
286	if (f1 && f1 != sfstdin) sfclose(f1);
287	if (f2 && f2 != sfstdin) sfclose(f2);
288	return(n);
289}
290