1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1992-2007 AT&T Knowledge Ventures * 5 * and is licensed under the * 6 * Common Public License, Version 1.0 * 7 * by AT&T Knowledge Ventures * 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) 2004-12-01 $\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), "%s: EOF", 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), "%s: EOF", 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), "%s: EOF", 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), "%s: EOF", 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 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); 284 done: 285 if (f1 && f1 != sfstdin) sfclose(f1); 286 if (f2 && f2 != sfstdin) sfclose(f2); 287 return(n); 288 } 289