14887Schin /*********************************************************************** 24887Schin * * 34887Schin * This software is part of the ast package * 4*8462SApril.Chin@Sun.COM * Copyright (c) 1992-2008 AT&T Intellectual Property * 54887Schin * and is licensed under the * 64887Schin * Common Public License, Version 1.0 * 7*8462SApril.Chin@Sun.COM * by AT&T Intellectual Property * 84887Schin * * 94887Schin * A copy of the License is available at * 104887Schin * http://www.opensource.org/licenses/cpl1.0.txt * 114887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 124887Schin * * 134887Schin * Information and Software Systems Research * 144887Schin * AT&T Research * 154887Schin * Florham Park NJ * 164887Schin * * 174887Schin * Glenn Fowler <gsf@research.att.com> * 184887Schin * David Korn <dgk@research.att.com> * 194887Schin * * 204887Schin ***********************************************************************/ 214887Schin #pragma prototyped 224887Schin /* 234887Schin * David Korn 244887Schin * Glenn Fowler 254887Schin * AT&T Bell Laboratories 264887Schin * 274887Schin * cmp 284887Schin */ 294887Schin 304887Schin static const char usage[] = 314887Schin "[-?\n@(#)$Id: cmp (AT&T Research) 2004-12-01 $\n]" 324887Schin USAGE_LICENSE 334887Schin "[+NAME?cmp - compare two files]" 344887Schin "[+DESCRIPTION?\bcmp\b compares two files \afile1\a and \afile2\a. " 354887Schin "\bcmp\b writes no output if the files are the same. By default, " 364887Schin "if the files differ, the byte and line number at which the " 374887Schin "first difference occurred are written to standard output. Bytes " 384887Schin "and lines are numbered beginning with 1.]" 394887Schin "[+?If \askip1\a or \askip2\a are specified, or the \b-i\b option is " 404887Schin "specified, initial bytes of the corresponding file are skipped " 414887Schin "before beginning the compare. The skip values are in bytes or " 424887Schin "can have a suffix of \bk\b for kilobytes or \bm\b for megabytes.]" 434887Schin "[+?If either \afile1\a or \afiles2\a is \b-\b, \bcmp\b " 444887Schin "uses standard input starting at the current location.]" 454887Schin "[c:print-chars?Writes control characters as a \b^\b followed by a letter of " 464887Schin "the alphabet and precede characters that have the high bit set with " 474887Schin "\bM-\b as with \bcat\b(1).]" 484887Schin "[i:ignore-initial]#[skip:=0?Sets default skip values for the operands " 494887Schin "\askip1\a and \askip2\a to \askip\a.]" 504887Schin "[l:verbose?Write the decimal byte number and the differing bytes (in octal) " 514887Schin "for each difference.]" 524887Schin "[s:quiet|silent?Write nothing for differing files; return non-zero " 534887Schin "exit status only.] ]" 544887Schin "\n" 554887Schin "\nfile1 file2 [skip1 [skip2]]\n" 564887Schin "\n" 574887Schin "[+EXIT STATUS?]{" 584887Schin "[+0?The files or portions compared are identical.]" 594887Schin "[+1?The files are different.]" 604887Schin "[+>1?An error occurred.]" 614887Schin "}" 624887Schin "[+SEE ALSO?\bcomm\b(1), \bdiff\b(1), \bcat\b(1)]" 634887Schin ; 644887Schin 654887Schin 664887Schin #include <cmd.h> 674887Schin #include <ls.h> 684887Schin #include <ctype.h> 694887Schin 704887Schin #define CMP_VERBOSE 1 714887Schin #define CMP_SILENT 2 724887Schin #define CMP_CHARS 4 734887Schin 744887Schin #define cntl(x) (x&037) 754887Schin #define printchar(c) ((c) ^ ('A'-cntl('A'))) 764887Schin 774887Schin static void outchar(Sfio_t *out, register int c, int delim) 784887Schin { 794887Schin if(c&0200) 804887Schin { 814887Schin sfputc(out,'M'); 824887Schin sfputc(out,'-'); 834887Schin c &= ~0200; 844887Schin } 854887Schin else if(!isprint(c)) 864887Schin { 874887Schin sfputc(out,'^'); 884887Schin c = printchar(c); 894887Schin } 904887Schin sfputc(out,c); 914887Schin sfputc(out,delim); 924887Schin } 934887Schin 944887Schin /* 954887Schin * compare two files 964887Schin */ 974887Schin 984887Schin static int 994887Schin cmp(const char* file1, Sfio_t* f1, const char* file2, Sfio_t* f2, int flags) 1004887Schin { 1014887Schin register int c1; 1024887Schin register int c2; 1034887Schin register unsigned char* p1 = 0; 1044887Schin register unsigned char* p2 = 0; 1054887Schin register Sfoff_t lines = 1; 1064887Schin register unsigned char* e1 = 0; 1074887Schin register unsigned char* e2 = 0; 1084887Schin Sfoff_t pos = 0; 1094887Schin int ret = 0; 1104887Schin unsigned char* last; 1114887Schin 1124887Schin for (;;) 1134887Schin { 1144887Schin if ((c1 = e1 - p1) <= 0) 1154887Schin { 1164887Schin if (!(p1 = (unsigned char*)sfreserve(f1, SF_UNBOUND, 0)) || (c1 = sfvalue(f1)) <= 0) 1174887Schin { 1184887Schin if ((e2 - p2) > 0 || sfreserve(f2, SF_UNBOUND, 0) && sfvalue(f2) > 0) 1194887Schin { 1204887Schin ret = 1; 1214887Schin if (!(flags & CMP_SILENT)) 1224887Schin error(ERROR_exit(1), "%s: EOF", file1); 1234887Schin } 1244887Schin return(ret); 1254887Schin } 1264887Schin e1 = p1 + c1; 1274887Schin } 1284887Schin if ((c2 = e2 - p2) <= 0) 1294887Schin { 1304887Schin if (!(p2 = (unsigned char*)sfreserve(f2, SF_UNBOUND, 0)) || (c2 = sfvalue(f2)) <= 0) 1314887Schin { 1324887Schin if (!(flags & CMP_SILENT)) 1334887Schin error(ERROR_exit(1), "%s: EOF", file2); 1344887Schin return(1); 1354887Schin } 1364887Schin e2 = p2 + c2; 1374887Schin } 1384887Schin if (c1 > c2) 1394887Schin c1 = c2; 1404887Schin pos += c1; 1414887Schin if (flags & CMP_SILENT) 1424887Schin { 1434887Schin if (memcmp(p1, p2, c1)) 1444887Schin return(1); 1454887Schin p1 += c1; 1464887Schin p2 += c1; 1474887Schin } 1484887Schin else 1494887Schin { 1504887Schin last = p1 + c1; 1514887Schin while (p1 < last) 1524887Schin { 1534887Schin if ((c1 = *p1++) != *p2++) 1544887Schin { 1554887Schin if (flags) 1564887Schin { 1574887Schin ret = 1; 1584887Schin if(flags&CMP_CHARS) 1594887Schin { 1604887Schin sfprintf(sfstdout, "%6I*d ", sizeof(pos), pos - (last - p1)); 1614887Schin outchar(sfstdout,c1,' '); 1624887Schin outchar(sfstdout,*(p2-1),'\n'); 1634887Schin } 1644887Schin else 1654887Schin sfprintf(sfstdout, "%6I*d %3o %3o\n", sizeof(pos), pos - (last - p1), c1, *(p2 - 1)); 1664887Schin } 1674887Schin else 1684887Schin { 1694887Schin sfprintf(sfstdout, "%s %s differ: char %I*d, line %I*u\n", file1, file2, sizeof(pos), pos - (last - p1), sizeof(lines), lines); 1704887Schin return(1); 1714887Schin } 1724887Schin } 1734887Schin if (c1 == '\n') 1744887Schin lines++; 1754887Schin } 1764887Schin } 1774887Schin } 1784887Schin } 1794887Schin 1804887Schin int 1814887Schin b_cmp(int argc, register char** argv, void* context) 1824887Schin { 1834887Schin char* s; 1844887Schin char* e; 1854887Schin Sfio_t* f1 = 0; 1864887Schin Sfio_t* f2 = 0; 1874887Schin char* file1; 1884887Schin char* file2; 1894887Schin int n; 1904887Schin off_t o1 = 0; 1914887Schin off_t o2 = 0; 1924887Schin struct stat s1; 1934887Schin struct stat s2; 1944887Schin 1954887Schin int flags = 0; 1964887Schin 1974887Schin NoP(argc); 1984887Schin cmdinit(argc, argv, context, ERROR_CATALOG, 0); 1994887Schin while (n = optget(argv, usage)) switch (n) 2004887Schin { 2014887Schin case 'l': 2024887Schin flags |= CMP_VERBOSE; 2034887Schin break; 2044887Schin case 's': 2054887Schin flags |= CMP_SILENT; 2064887Schin break; 2074887Schin case 'c': 2084887Schin flags |= CMP_CHARS; 2094887Schin break; 2104887Schin case 'i': 2114887Schin o1 = o2 = opt_info.num; 2124887Schin break; 2134887Schin case ':': 2144887Schin error(2, "%s", opt_info.arg); 2154887Schin break; 2164887Schin case '?': 2174887Schin error(ERROR_usage(2), "%s", opt_info.arg); 2184887Schin break; 2194887Schin } 2204887Schin argv += opt_info.index; 2214887Schin if (error_info.errors || !(file1 = *argv++) || !(file2 = *argv++)) 2224887Schin error(ERROR_usage(2), "%s", optusage(NiL)); 2234887Schin n = 2; 2244887Schin if (streq(file1, "-")) 2254887Schin f1 = sfstdin; 2264887Schin else if (!(f1 = sfopen(NiL, file1, "r"))) 2274887Schin { 2284887Schin if (!(flags & CMP_SILENT)) 2294887Schin error(ERROR_system(0), "%s: cannot open", file1); 2304887Schin goto done; 2314887Schin } 2324887Schin if (streq(file2, "-")) 2334887Schin f2 = sfstdin; 2344887Schin else if (!(f2 = sfopen(NiL, file2, "r"))) 2354887Schin { 2364887Schin if (!(flags & CMP_SILENT)) 2374887Schin error(ERROR_system(0), "%s: cannot open", file2); 2384887Schin goto done; 2394887Schin } 2404887Schin if (s = *argv++) 2414887Schin { 2424887Schin o1 = strtol(s, &e, 0); 2434887Schin if (*e) 2444887Schin { 2454887Schin error(ERROR_exit(0), "%s: %s: invalid skip", file1, s); 2464887Schin goto done; 2474887Schin } 2484887Schin if (s = *argv++) 2494887Schin { 2504887Schin o2 = strtol(s, &e, 0); 2514887Schin if (*e) 2524887Schin { 2534887Schin error(ERROR_exit(0), "%s: %s: invalid skip", file2, s); 2544887Schin goto done; 2554887Schin } 2564887Schin } 2574887Schin if (*argv) 2584887Schin { 2594887Schin error(ERROR_usage(0), "%s", optusage(NiL)); 2604887Schin goto done; 2614887Schin } 2624887Schin } 2634887Schin if (o1 && sfseek(f1, o1, SEEK_SET) != o1) 2644887Schin { 2654887Schin if (!(flags & CMP_SILENT)) 2664887Schin error(ERROR_exit(0), "%s: EOF", file1); 2674887Schin n = 1; 2684887Schin goto done; 2694887Schin } 2704887Schin if (o2 && sfseek(f2, o2, SEEK_SET) != o2) 2714887Schin { 2724887Schin if (!(flags & CMP_SILENT)) 2734887Schin error(ERROR_exit(0), "%s: EOF", file2); 2744887Schin n = 1; 2754887Schin goto done; 2764887Schin } 2774887Schin if (fstat(sffileno(f1), &s1)) 2784887Schin error(ERROR_system(0), "%s: cannot stat", file1); 2794887Schin else if (fstat(sffileno(f2), &s2)) 2804887Schin error(ERROR_system(0), "%s: cannot stat", file1); 2814887Schin else if (s1.st_ino == s2.st_ino && s1.st_dev == s2.st_dev && o1 == o2) 2824887Schin n = 0; 2834887Schin else 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); 2844887Schin done: 2854887Schin if (f1 && f1 != sfstdin) sfclose(f1); 2864887Schin if (f2 && f2 != sfstdin) sfclose(f2); 2874887Schin return(n); 2884887Schin } 289