1 /* $OpenBSD: diffdir.c,v 1.7 2003/06/25 03:37:32 deraadt Exp $ */ 2 3 /* 4 * Copyright (C) Caldera International Inc. 2001-2002. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code and documentation must retain the above 11 * copyright notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed or owned by Caldera 18 * International, Inc. 19 * 4. Neither the name of Caldera International, Inc. nor the names of other 20 * contributors may be used to endorse or promote products derived from 21 * this software without specific prior written permission. 22 * 23 * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA 24 * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR 25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 27 * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT, 28 * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 33 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 * POSSIBILITY OF SUCH DAMAGE. 35 */ 36 37 #include <sys/types.h> 38 #include <sys/wait.h> 39 40 #include <stdlib.h> 41 #include <fcntl.h> 42 #include <unistd.h> 43 #include <string.h> 44 45 #include "diff.h" 46 47 #if 0 48 static const char sccsid[] = "@(#)diffdir.c 4.12 (Berkeley) 4/30/89"; 49 #endif 50 51 /* 52 * diff - directory comparison 53 */ 54 #define d_flags d_ino 55 56 #define ONLY 1 /* Only in this directory */ 57 #define SAME 2 /* Both places and same */ 58 #define DIFFER 4 /* Both places and different */ 59 #define DIRECT 8 /* Directory */ 60 61 struct dir { 62 u_long d_ino; 63 short d_reclen; 64 short d_namlen; 65 char *d_entry; 66 }; 67 68 int header; 69 static int dirstatus; /* exit status from diffdir */ 70 extern int status; 71 char title[2 * BUFSIZ]; 72 char *etitle; 73 char *prargs[] = {"pr", "-h", 0, "-f", 0, 0}; 74 75 76 static struct dir *setupdir(char *); 77 static int ascii(int); 78 static void compare(struct dir *); 79 static void calldiff(char *); 80 static void setfile(char **fpp, char **epp, char *file); 81 static int useless(char *); 82 static void only(struct dir *dp, int which); 83 static void scanpr(struct dir *, int, char *, char *, char *, char *, char *); 84 85 void 86 diffdir(char **argv) 87 { 88 struct dir *d1, *d2; 89 struct dir *dir1, *dir2; 90 int i; 91 int cmp; 92 93 if (opt == D_IFDEF) { 94 fprintf(stderr, "diff: can't specify -I with directories\n"); 95 done(0); 96 } 97 if (opt == D_EDIT && (sflag || lflag)) 98 fprintf(stderr, 99 "diff: warning: shouldn't give -s or -l with -e\n"); 100 strlcpy(title, "diff ", sizeof title); 101 for (i = 1; diffargv[i + 2]; i++) { 102 if (!strcmp(diffargv[i], "-")) 103 continue; /* was -S, dont look silly */ 104 strlcat(title, diffargv[i], sizeof title); 105 strlcat(title, " ", sizeof title); 106 } 107 for (etitle = title; *etitle; etitle++); 108 setfile(&file1, &efile1, file1); 109 setfile(&file2, &efile2, file2); 110 argv[0] = file1; 111 argv[1] = file2; 112 dir1 = setupdir(file1); 113 dir2 = setupdir(file2); 114 d1 = dir1; 115 d2 = dir2; 116 while (d1->d_entry != 0 || d2->d_entry != 0) { 117 if (d1->d_entry && useless(d1->d_entry)) { 118 d1++; 119 continue; 120 } 121 if (d2->d_entry && useless(d2->d_entry)) { 122 d2++; 123 continue; 124 } 125 if (d1->d_entry == 0) 126 cmp = 1; 127 else if (d2->d_entry == 0) 128 cmp = -1; 129 else 130 cmp = strcmp(d1->d_entry, d2->d_entry); 131 if (cmp < 0) { 132 if (lflag) 133 d1->d_flags |= ONLY; 134 else if (opt == 0 || opt == 2) 135 only(d1, 1); 136 d1++; 137 dirstatus |= 1; 138 } else if (cmp == 0) { 139 compare(d1); 140 d1++; 141 d2++; 142 } else { 143 if (lflag) 144 d2->d_flags |= ONLY; 145 else if (opt == 0 || opt == 2) 146 only(d2, 2); 147 d2++; 148 dirstatus |= 1; 149 } 150 } 151 if (lflag) { 152 scanpr(dir1, ONLY, "Only in %.*s", file1, efile1, 0, 0); 153 scanpr(dir2, ONLY, "Only in %.*s", file2, efile2, 0, 0); 154 scanpr(dir1, SAME, "Common identical files in %.*s and %.*s", 155 file1, efile1, file2, efile2); 156 scanpr(dir1, DIFFER, "Binary files which differ in %.*s and %.*s", 157 file1, efile1, file2, efile2); 158 scanpr(dir1, DIRECT, "Common subdirectories of %.*s and %.*s", 159 file1, efile1, file2, efile2); 160 } 161 if (rflag) { 162 if (header && lflag) 163 printf("\f"); 164 for (d1 = dir1; d1->d_entry; d1++) { 165 if ((d1->d_flags & DIRECT) == 0) 166 continue; 167 strcpy(efile1, d1->d_entry); 168 strcpy(efile2, d1->d_entry); 169 calldiff(0); 170 } 171 } 172 status = dirstatus; 173 } 174 175 void 176 setfile(char **fpp, char **epp, char *file) 177 { 178 char *cp; 179 180 *fpp = talloc(BUFSIZ); 181 strcpy(*fpp, file); 182 for (cp = *fpp; *cp; cp++) 183 continue; 184 *cp++ = '/'; 185 *epp = cp; 186 } 187 188 static void 189 scanpr(struct dir *dp, int test, char *title, char *file1, char *efile1, 190 char *file2, char *efile2) 191 { 192 int titled = 0; 193 194 for (; dp->d_entry; dp++) { 195 if ((dp->d_flags & test) == 0) 196 continue; 197 if (titled == 0) { 198 if (header == 0) 199 header = 1; 200 else 201 printf("\n"); 202 printf(title, 203 efile1 - file1 - 1, file1, 204 efile2 - file2 - 1, file2); 205 printf(":\n"); 206 titled = 1; 207 } 208 printf("\t%s\n", dp->d_entry); 209 } 210 } 211 212 void 213 only(struct dir *dp, int which) 214 { 215 char *file = which == 1 ? file1 : file2; 216 char *efile = which == 1 ? efile1 : efile2; 217 218 printf("Only in %.*s: %s\n", (int)(efile - file - 1), file, dp->d_entry); 219 } 220 221 int entcmp(); 222 223 struct dir * 224 setupdir(char *cp) 225 { 226 struct dir *dp, *ep; 227 struct direct *rp; 228 int nitems; 229 DIR *dirp; 230 231 dirp = opendir(cp); 232 if (dirp == NULL) { 233 fprintf(stderr, "diff: "); 234 perror(cp); 235 done(0); 236 } 237 nitems = 0; 238 dp = talloc(sizeof(struct dir)); 239 while ((rp = readdir(dirp))) { 240 ep = &dp[nitems++]; 241 ep->d_reclen = rp->d_reclen; 242 ep->d_namlen = rp->d_namlen; 243 ep->d_entry = 0; 244 ep->d_flags = 0; 245 if (ep->d_namlen > 0) { 246 ep->d_entry = talloc(ep->d_namlen + 1); 247 strcpy(ep->d_entry, rp->d_name); 248 } 249 dp = ralloc(dp, (nitems + 1) * sizeof(struct dir)); 250 } 251 dp[nitems].d_entry = 0; /* delimiter */ 252 closedir(dirp); 253 qsort(dp, nitems, sizeof(struct dir), entcmp); 254 return (dp); 255 } 256 257 int 258 entcmp(struct dir *d1, struct dir *d2) 259 { 260 return (strcmp(d1->d_entry, d2->d_entry)); 261 } 262 263 static void 264 compare(struct dir *dp) 265 { 266 int i, j; 267 int f1, f2, fmt1, fmt2; 268 struct stat stb1, stb2; 269 char buf1[BUFSIZ], buf2[BUFSIZ]; 270 271 strcpy(efile1, dp->d_entry); 272 strcpy(efile2, dp->d_entry); 273 f1 = open(file1, 0); 274 if (f1 < 0) { 275 perror(file1); 276 return; 277 } 278 f2 = open(file2, 0); 279 if (f2 < 0) { 280 perror(file2); 281 close(f1); 282 return; 283 } 284 fstat(f1, &stb1); 285 fstat(f2, &stb2); 286 fmt1 = stb1.st_mode & S_IFMT; 287 fmt2 = stb2.st_mode & S_IFMT; 288 if (fmt1 != S_IFREG || fmt2 != S_IFREG) { 289 if (fmt1 == fmt2) { 290 if (fmt1 != S_IFDIR && stb1.st_rdev == stb2.st_rdev) 291 goto same; 292 if (fmt1 == S_IFDIR) { 293 dp->d_flags = DIRECT; 294 if (lflag || opt == D_EDIT) 295 goto closem; 296 printf("Common subdirectories: %s and %s\n", 297 file1, file2); 298 goto closem; 299 } 300 } 301 goto notsame; 302 } 303 if (stb1.st_size != stb2.st_size) 304 goto notsame; 305 for (;;) { 306 i = read(f1, buf1, BUFSIZ); 307 j = read(f2, buf2, BUFSIZ); 308 if (i < 0 || j < 0 || i != j) 309 goto notsame; 310 if (i == 0 && j == 0) 311 goto same; 312 for (j = 0; j < i; j++) 313 if (buf1[j] != buf2[j]) 314 goto notsame; 315 } 316 same: 317 if (sflag == 0) 318 goto closem; 319 if (lflag) 320 dp->d_flags = SAME; 321 else 322 printf("Files %s and %s are identical\n", file1, file2); 323 goto closem; 324 notsame: 325 dirstatus |= 1; 326 if (!ascii(f1) || !ascii(f2)) { 327 if (lflag) 328 dp->d_flags |= DIFFER; 329 else if (opt == D_NORMAL || opt == D_CONTEXT) 330 printf("Binary files %s and %s differ\n", 331 file1, file2); 332 goto closem; 333 } 334 close(f1); 335 close(f2); 336 anychange = 1; 337 if (lflag) 338 calldiff(title); 339 else { 340 if (opt == D_EDIT) { 341 printf("ed - %s << '-*-END-*-'\n", dp->d_entry); 342 calldiff(0); 343 } else { 344 printf("%s%s %s\n", title, file1, file2); 345 calldiff(0); 346 } 347 if (opt == D_EDIT) 348 printf("w\nq\n-*-END-*-\n"); 349 } 350 return; 351 closem: 352 close(f1); 353 close(f2); 354 } 355 356 static void 357 calldiff(char *wantpr) 358 { 359 int pid, lstatus, lstatus2, pv[2]; 360 361 prargs[2] = wantpr; 362 fflush(stdout); 363 if (wantpr) { 364 snprintf(etitle, title + sizeof title - etitle, 365 "%s %s", file1, file2); 366 pipe(pv); 367 pid = fork(); 368 if (pid == -1) { 369 fprintf(stderr, "No more processes"); 370 done(0); 371 } 372 if (pid == 0) { 373 close(0); 374 dup(pv[0]); 375 close(pv[0]); 376 close(pv[1]); 377 execv(pr + 4, prargs); 378 execv(pr, prargs); 379 perror(pr); 380 done(0); 381 } 382 } 383 pid = fork(); 384 if (pid == -1) { 385 fprintf(stderr, "diff: No more processes\n"); 386 done(0); 387 } 388 if (pid == 0) { 389 if (wantpr) { 390 close(1); 391 dup(pv[1]); 392 close(pv[0]); 393 close(pv[1]); 394 } 395 execv(diff + 4, diffargv); 396 execv(diff, diffargv); 397 perror(diff); 398 done(0); 399 } 400 if (wantpr) { 401 close(pv[0]); 402 close(pv[1]); 403 } 404 while (wait(&lstatus) != pid) 405 continue; 406 while (wait(&lstatus2) != -1) 407 continue; 408 /* 409 if ((lstatus >> 8) >= 2) 410 done(0); 411 */ 412 dirstatus |= lstatus >> 8; 413 } 414 415 int 416 ascii(int f) 417 { 418 char buf[BUFSIZ]; 419 int cnt; 420 char *cp; 421 422 lseek(f, (off_t)0, SEEK_SET); 423 cnt = read(f, buf, BUFSIZ); 424 cp = buf; 425 while (--cnt >= 0) 426 if (*cp++ & 0200) 427 return (0); 428 return (1); 429 } 430 431 /* 432 * THIS IS CRUDE. 433 */ 434 int 435 useless(char *cp) 436 { 437 if (cp[0] == '.') { 438 if (cp[1] == '\0') 439 return (1); /* directory "." */ 440 if (cp[1] == '.' && cp[2] == '\0') 441 return (1); /* directory ".." */ 442 } 443 if (start && strcmp(start, cp) > 0) 444 return (1); 445 return (0); 446 } 447