1/*********************************************************************** 2* * 3* This software is part of the ast package * 4* Copyright (c) 1992-2012 AT&T Intellectual Property * 5* and is licensed under the * 6* Eclipse Public License, Version 1.0 * 7* by AT&T Intellectual Property * 8* * 9* A copy of the License is available at * 10* http://www.eclipse.org/org/documents/epl-v10.html * 11* (with md5 checksum b35adb5213ca9657e911e9befb180842) * 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) 2010-04-11 $\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, if the " 36 "files differ, the byte and line number at which the first difference " 37 "occurred are written to standard output. Bytes and lines are numbered " 38 "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 before " 41 "beginning the compare. The skip values are in bytes or can have a " 42 "suffix of \bk\b for kilobytes or \bm\b for megabytes.]" 43"[+?If either \afile1\a or \afiles2\a is \b-\b, \bcmp\b uses standard " 44 "input starting at the current location.]" 45"[b:print-bytes?Print differing bytes as 3 digit octal values.]" 46"[c:print-chars?Print differing bytes as follows: non-space printable " 47 "characters as themselves; space and control characters as \b^\b " 48 "followed by a letter of the alphabet; and characters with the high bit " 49 "set as the lower 7 bit character prefixed by \bM^\b for 7 bit space and " 50 "non-printable characters and \bM-\b for all other characters. If the 7 " 51 "bit character encoding is not ASCII then the characters are converted " 52 "to ASCII to determine \ahigh bit set\a, and if set it is cleared and " 53 "converted back to the native encoding. Multibyte characters in the " 54 "current locale are treated as printable characters.]" 55"[d:differences?Print at most \adifferences\a differences using " 56 "\b--verbose\b output format. \b--differences=0\b is equivalent to " 57 "\b--silent\b.]#[differences]" 58"[i:ignore-initial|skip?Skip the the first \askip1\a bytes in \afile1\a " 59 "and the first \askip2\a bytes in \afile2\a. If \askip2\a is omitted " 60 "then \askip1\a is used.]:[skip1[::skip2]]:=0::0]" 61"[l:verbose?Write the decimal byte number and the differing bytes (in " 62 "octal) for each difference.]" 63"[n:count|bytes?Compare at most \acount\a bytes.]#[count]" 64"[s:quiet|silent?Write nothing for differing files; return non-zero exit " 65 "status only.]" 66"\n" 67"\nfile1 file2 [skip1 [skip2]]\n" 68"\n" 69"[+EXIT STATUS?]" 70 "{" 71 "[+0?The files or portions compared are identical.]" 72 "[+1?The files are different.]" 73 "[+>1?An error occurred.]" 74 "}" 75"[+SEE ALSO?\bcomm\b(1), \bdiff\b(1), \bcat\b(1)]" 76; 77 78#include <cmd.h> 79#include <ls.h> 80#include <ctype.h> 81#include <ccode.h> 82 83#define CMP_VERBOSE 0x01 84#define CMP_SILENT 0x02 85#define CMP_CHARS 0x04 86#define CMP_BYTES 0x08 87 88static void 89pretty(Sfio_t *out, int o, int delim, int flags) 90{ 91 int c; 92 int m; 93 char* s; 94 char buf[10]; 95 96 s = buf; 97 if ((flags & CMP_BYTES) || !(flags & CMP_CHARS)) 98 { 99 *s++ = ' '; 100 if ((flags & CMP_CHARS) && delim != -1) 101 *s++ = ' '; 102 *s++ = '0' + ((o >> 6) & 07); 103 *s++ = '0' + ((o >> 3) & 07); 104 *s++ = '0' + (o & 07); 105 } 106 if (flags & CMP_CHARS) 107 { 108 *s++ = ' '; 109 c = ccmapc(o, CC_NATIVE, CC_ASCII); 110 if (c & 0x80) 111 { 112 m = 1; 113 *s++ = 'M'; 114 c &= 0x7f; 115 o = ccmapc(c, CC_ASCII, CC_NATIVE); 116 } 117 else 118 m = 0; 119 if (isspace(o) || !isprint(o)) 120 { 121 if (!m) 122 *s++ = ' '; 123 *s++ = '^'; 124 c ^= 0x40; 125 o = ccmapc(c, CC_ASCII, CC_NATIVE); 126 } 127 else if (m) 128 *s++ = '-'; 129 else 130 { 131 *s++ = ' '; 132 *s++ = ' '; 133 } 134 *s++ = o; 135 } 136 *s = 0; 137 sfputr(out, buf, delim); 138} 139 140/* 141 * compare two files 142 */ 143 144static int 145cmp(const char* file1, Sfio_t* f1, const char* file2, Sfio_t* f2, int flags, Sfoff_t count, Sfoff_t differences) 146{ 147 register int c1; 148 register int c2; 149 register unsigned char* p1 = 0; 150 register unsigned char* p2 = 0; 151 register Sfoff_t lines = 1; 152 register unsigned char* e1 = 0; 153 register unsigned char* e2 = 0; 154 Sfoff_t pos = 0; 155 int n1 = 0; 156 int ret = 0; 157 unsigned char* last; 158 159 for (;;) 160 { 161 if ((c1 = e1 - p1) <= 0) 162 { 163 if (count > 0 && !(count -= n1)) 164 return ret; 165 if (!(p1 = (unsigned char*)sfreserve(f1, SF_UNBOUND, 0)) || (c1 = sfvalue(f1)) <= 0) 166 { 167 if ((e2 - p2) > 0 || sfreserve(f2, SF_UNBOUND, 0) && sfvalue(f2) > 0) 168 { 169 ret = 1; 170 if (!(flags & CMP_SILENT)) 171 error(ERROR_exit(1), "EOF on %s", file1); 172 } 173 return ret; 174 } 175 if (count > 0 && c1 > count) 176 c1 = (int)count; 177 e1 = p1 + c1; 178 n1 = c1; 179 } 180 if ((c2 = e2 - p2) <= 0) 181 { 182 if (!(p2 = (unsigned char*)sfreserve(f2, SF_UNBOUND, 0)) || (c2 = sfvalue(f2)) <= 0) 183 { 184 if (!(flags & CMP_SILENT)) 185 error(ERROR_exit(1), "EOF on %s", file2); 186 return 1; 187 } 188 e2 = p2 + c2; 189 } 190 if (c1 > c2) 191 c1 = c2; 192 pos += c1; 193 if (flags & CMP_SILENT) 194 { 195 if (memcmp(p1, p2, c1)) 196 return 1; 197 p1 += c1; 198 p2 += c1; 199 } 200 else 201 { 202 last = p1 + c1; 203 while (p1 < last) 204 { 205 if ((c1 = *p1++) != *p2++) 206 { 207 if (differences >= 0) 208 { 209 if (!differences) 210 return 1; 211 differences--; 212 } 213#if 0 214 if (!flags) 215 sfprintf(sfstdout, "%s %s differ: char %I*d, line %I*u\n", file1, file2, sizeof(pos), pos - (last - p1), sizeof(lines), lines); 216 else 217 { 218 sfprintf(sfstdout, "%6I*d", sizeof(pos), pos - (last - p1)); 219 pretty(sfstdout, c1, -1, flags); 220 pretty(sfstdout, *(p2-1), '\n', flags); 221 } 222#else 223 if (flags & CMP_VERBOSE) 224 sfprintf(sfstdout, "%6I*d", sizeof(pos), pos - (last - p1)); 225 else 226 sfprintf(sfstdout, "%s %s differ: char %I*d, line %I*u", file1, file2, sizeof(pos), pos - (last - p1), sizeof(lines), lines); 227 if (flags & (CMP_BYTES|CMP_CHARS|CMP_VERBOSE)) 228 { 229 sfputc(sfstdout, (flags & CMP_VERBOSE) ? ' ' : ','); 230 pretty(sfstdout, c1, -1, flags); 231 pretty(sfstdout, *(p2-1), '\n', flags); 232 } 233 else 234 sfputc(sfstdout, '\n'); 235#endif 236 if (!differences || differences < 0 && !(flags & CMP_VERBOSE)) 237 return 1; 238 ret = 1; 239 } 240 if (c1 == '\n') 241 lines++; 242 } 243 } 244 } 245} 246 247int 248b_cmp(int argc, register char** argv, Shbltin_t* context) 249{ 250 char* s; 251 char* e; 252 char* file1; 253 char* file2; 254 int n; 255 struct stat s1; 256 struct stat s2; 257 258 Sfio_t* f1 = 0; 259 Sfio_t* f2 = 0; 260 Sfoff_t o1 = 0; 261 Sfoff_t o2 = 0; 262 Sfoff_t count = -1; 263 Sfoff_t differences = -1; 264 int flags = 0; 265 266 NoP(argc); 267 cmdinit(argc, argv, context, ERROR_CATALOG, 0); 268 for (;;) 269 { 270 switch (optget(argv, usage)) 271 { 272 case 'b': 273 flags |= CMP_BYTES; 274 continue; 275 case 'c': 276 flags |= CMP_CHARS; 277 continue; 278 case 'd': 279 flags |= CMP_VERBOSE; 280 differences = opt_info.number; 281 continue; 282 case 'i': 283 o1 = strtoll(opt_info.arg, &e, 0); 284 if (*e == ':') 285 o2 = strtoll(e + 1, &e, 0); 286 else 287 o2 = o1; 288 if (*e) 289 { 290 error(2, "%s: skip1:skip2 expected", opt_info.arg); 291 break; 292 } 293 continue; 294 case 'l': 295 flags |= CMP_VERBOSE; 296 continue; 297 case 'n': 298 count = opt_info.number; 299 continue; 300 case 's': 301 flags |= CMP_SILENT; 302 continue; 303 case ':': 304 error(2, "%s", opt_info.arg); 305 break; 306 case '?': 307 error(ERROR_usage(2), "%s", opt_info.arg); 308 break; 309 } 310 break; 311 } 312 argv += opt_info.index; 313 if (error_info.errors || !(file1 = *argv++) || !(file2 = *argv++)) 314 error(ERROR_usage(2), "%s", optusage(NiL)); 315 n = 2; 316 if (streq(file1, "-")) 317 f1 = sfstdin; 318 else if (!(f1 = sfopen(NiL, file1, "r"))) 319 { 320 if (!(flags & CMP_SILENT)) 321 error(ERROR_system(0), "%s: cannot open", file1); 322 goto done; 323 } 324 if (streq(file2, "-")) 325 f2 = sfstdin; 326 else if (!(f2 = sfopen(NiL, file2, "r"))) 327 { 328 if (!(flags & CMP_SILENT)) 329 error(ERROR_system(0), "%s: cannot open", file2); 330 goto done; 331 } 332 if (s = *argv++) 333 { 334 o1 = strtoll(s, &e, 0); 335 if (*e) 336 { 337 error(ERROR_exit(0), "%s: %s: invalid skip", file1, s); 338 goto done; 339 } 340 if (s = *argv++) 341 { 342 o2 = strtoll(s, &e, 0); 343 if (*e) 344 { 345 error(ERROR_exit(0), "%s: %s: invalid skip", file2, s); 346 goto done; 347 } 348 } 349 if (*argv) 350 { 351 error(ERROR_usage(0), "%s", optusage(NiL)); 352 goto done; 353 } 354 } 355 if (o1 && sfseek(f1, o1, SEEK_SET) != o1) 356 { 357 if (!(flags & CMP_SILENT)) 358 error(ERROR_exit(0), "EOF on %s", file1); 359 n = 1; 360 goto done; 361 } 362 if (o2 && sfseek(f2, o2, SEEK_SET) != o2) 363 { 364 if (!(flags & CMP_SILENT)) 365 error(ERROR_exit(0), "EOF on %s", file2); 366 n = 1; 367 goto done; 368 } 369 if (fstat(sffileno(f1), &s1)) 370 error(ERROR_system(0), "%s: cannot stat", file1); 371 else if (fstat(sffileno(f2), &s2)) 372 error(ERROR_system(0), "%s: cannot stat", file1); 373 else if (s1.st_ino == s2.st_ino && s1.st_dev == s2.st_dev && o1 == o2) 374 n = 0; 375 else 376 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, count, differences); 377 done: 378 if (f1 && f1 != sfstdin) 379 sfclose(f1); 380 if (f2 && f2 != sfstdin) 381 sfclose(f2); 382 return n; 383} 384