xref: /titanic_51/usr/src/lib/libcmd/common/cmp.c (revision 68c47f65208790c466e5e484f2293d3baed71c6a)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1992-2009 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 
30 static const char usage[] =
31 "[-?\n@(#)$Id: cmp (AT&T Research) 2009-01-05 $\n]"
32 USAGE_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 
77 static 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 
98 static int
99 cmp(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 
180 int
181 b_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