1 /* $OpenBSD: diffdir.c,v 1.13 2003/06/25 22:14:43 millert 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 <dirent.h> 41 #include <errno.h> 42 #include <fcntl.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <unistd.h> 47 48 #include "diff.h" 49 50 #if 0 51 static const char sccsid[] = "@(#)diffdir.c 4.12 (Berkeley) 4/30/89"; 52 #endif 53 54 /* 55 * diff - directory comparison 56 */ 57 #define d_flags d_ino 58 59 #define ONLY 1 /* Only in this directory */ 60 #define SAME 2 /* Both places and same */ 61 #define DIFFER 4 /* Both places and different */ 62 #define DIRECT 8 /* Directory */ 63 64 struct dir { 65 u_long d_ino; 66 short d_reclen; 67 short d_namlen; 68 char *d_entry; 69 }; 70 71 int header; 72 static int dirstatus; /* exit status from diffdir */ 73 extern int status; 74 char title[2 * BUFSIZ]; 75 char *etitle; 76 char *prargs[] = {"pr", "-h", 0, "-f", 0, 0}; 77 78 79 static struct dir *setupdir(char *); 80 static int ascii(int); 81 static void compare(struct dir *); 82 static void calldiff(char *); 83 static void setfile(char **fpp, char **epp, char *file); 84 static int useless(char *); 85 static void only(struct dir *dp, int which); 86 static void scanpr(struct dir *, int, char *, char *, char *, char *, char *); 87 static int entcmp(const void *, const void *); 88 89 void 90 diffdir(char **argv) 91 { 92 struct dir *dir1, *dir2; 93 struct dir *d1, *d2; 94 int i, cmp; 95 96 if (opt == D_IFDEF) 97 warnx("can't specify -I with directories"); 98 if (opt == D_EDIT && (sflag || lflag)) 99 warnx("warning: shouldn't give -s or -l with -e"); 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 ; 109 setfile(&file1, &efile1, file1); 110 setfile(&file2, &efile2, file2); 111 argv[0] = file1; 112 argv[1] = file2; 113 dir1 = setupdir(file1); 114 dir2 = setupdir(file2); 115 d1 = dir1; 116 d2 = dir2; 117 while (d1->d_entry != 0 || d2->d_entry != 0) { 118 if (d1->d_entry && useless(d1->d_entry)) { 119 d1++; 120 continue; 121 } 122 if (d2->d_entry && useless(d2->d_entry)) { 123 d2++; 124 continue; 125 } 126 if (d1->d_entry == 0) 127 cmp = 1; 128 else if (d2->d_entry == 0) 129 cmp = -1; 130 else 131 cmp = strcmp(d1->d_entry, d2->d_entry); 132 if (cmp < 0) { 133 if (lflag) 134 d1->d_flags |= ONLY; 135 else if (opt == 0 || opt == 2) 136 only(d1, 1); 137 d1++; 138 dirstatus |= 1; 139 } else if (cmp == 0) { 140 compare(d1); 141 d1++; 142 d2++; 143 } else { 144 if (lflag) 145 d2->d_flags |= ONLY; 146 else if (opt == 0 || opt == 2) 147 only(d2, 2); 148 d2++; 149 dirstatus |= 1; 150 } 151 } 152 if (lflag) { 153 scanpr(dir1, ONLY, "Only in %.*s", file1, efile1, 0, 0); 154 scanpr(dir2, ONLY, "Only in %.*s", file2, efile2, 0, 0); 155 scanpr(dir1, SAME, "Common identical files in %.*s and %.*s", 156 file1, efile1, file2, efile2); 157 scanpr(dir1, DIFFER, "Binary files which differ in %.*s and %.*s", 158 file1, efile1, file2, efile2); 159 scanpr(dir1, DIRECT, "Common subdirectories of %.*s and %.*s", 160 file1, efile1, file2, efile2); 161 } 162 if (rflag) { 163 if (header && lflag) 164 printf("\f"); 165 for (d1 = dir1; d1->d_entry; d1++) { 166 if ((d1->d_flags & DIRECT) == 0) 167 continue; 168 strlcpy(efile1, d1->d_entry, 169 file1 + MAXPATHLEN - efile1); 170 strlcpy(efile2, d1->d_entry, 171 file2 + MAXPATHLEN - efile2); 172 calldiff(0); 173 } 174 } 175 status = dirstatus; 176 } 177 178 void 179 setfile(char **fpp, char **epp, char *file) 180 { 181 char *cp; 182 size_t len; 183 184 if (*file == '\0') 185 file = "."; 186 *fpp = emalloc(MAXPATHLEN); 187 len = strlcpy(*fpp, file, MAXPATHLEN); 188 if (len >= MAXPATHLEN - 1) 189 errx(1, "%s: %s", file, strerror(ENAMETOOLONG)); 190 cp = *fpp + len - 1; 191 if (*cp == '/') 192 ++cp; 193 else { 194 *++cp = '/'; 195 *++cp = '\0'; 196 } 197 *epp = cp; 198 } 199 200 static void 201 scanpr(struct dir *dp, int test, char *title, char *file1, char *efile1, 202 char *file2, char *efile2) 203 { 204 int titled = 0; 205 206 for (; dp->d_entry; dp++) { 207 if ((dp->d_flags & test) == 0) 208 continue; 209 if (titled == 0) { 210 if (header == 0) 211 header = 1; 212 else 213 printf("\n"); 214 printf(title, 215 efile1 - file1 - 1, file1, 216 efile2 - file2 - 1, file2); 217 printf(":\n"); 218 titled = 1; 219 } 220 printf("\t%s\n", dp->d_entry); 221 } 222 } 223 224 void 225 only(struct dir *dp, int which) 226 { 227 char *file = which == 1 ? file1 : file2; 228 char *efile = which == 1 ? efile1 : efile2; 229 230 printf("Only in %.*s: %s\n", (int)(efile - file - 1), file, dp->d_entry); 231 } 232 233 struct dir * 234 setupdir(char *cp) 235 { 236 struct dir *dp, *ep; 237 struct dirent *rp; 238 int nitems; 239 DIR *dirp; 240 241 dirp = opendir(cp); 242 if (dirp == NULL) { 243 warn("%s", cp); 244 done(0); 245 } 246 nitems = 0; 247 dp = emalloc(sizeof(struct dir)); 248 while ((rp = readdir(dirp))) { 249 ep = &dp[nitems++]; 250 ep->d_reclen = rp->d_reclen; 251 ep->d_namlen = rp->d_namlen; 252 ep->d_entry = 0; 253 ep->d_flags = 0; 254 if (ep->d_namlen > 0) { 255 ep->d_entry = emalloc(ep->d_namlen + 1); 256 strlcpy(ep->d_entry, rp->d_name, ep->d_namlen + 1); 257 } 258 dp = erealloc(dp, (nitems + 1) * sizeof(struct dir)); 259 } 260 dp[nitems].d_entry = 0; /* delimiter */ 261 closedir(dirp); 262 qsort(dp, nitems, sizeof(struct dir), entcmp); 263 return (dp); 264 } 265 266 static int 267 entcmp(const void *v1, const void *v2) 268 { 269 const struct dir *d1, *d2; 270 271 d1 = v1; 272 d2 = v2; 273 return (strcmp(d1->d_entry, d2->d_entry)); 274 } 275 276 static void 277 compare(struct dir *dp) 278 { 279 char buf1[BUFSIZ], buf2[BUFSIZ]; 280 int i, j, f1, f2, fmt1, fmt2; 281 struct stat stb1, stb2; 282 283 strlcpy(efile1, dp->d_entry, file1 + MAXPATHLEN - efile1); 284 strlcpy(efile2, dp->d_entry, file2 + MAXPATHLEN - efile2); 285 f1 = open(file1, 0); 286 if (f1 < 0) { 287 perror(file1); 288 return; 289 } 290 f2 = open(file2, 0); 291 if (f2 < 0) { 292 perror(file2); 293 close(f1); 294 return; 295 } 296 fstat(f1, &stb1); 297 fstat(f2, &stb2); 298 fmt1 = stb1.st_mode & S_IFMT; 299 fmt2 = stb2.st_mode & S_IFMT; 300 if (fmt1 != S_IFREG || fmt2 != S_IFREG) { 301 if (fmt1 == fmt2) { 302 if (fmt1 != S_IFDIR && stb1.st_rdev == stb2.st_rdev) 303 goto same; 304 if (fmt1 == S_IFDIR) { 305 dp->d_flags = DIRECT; 306 if (lflag || opt == D_EDIT) 307 goto closem; 308 printf("Common subdirectories: %s and %s\n", 309 file1, file2); 310 goto closem; 311 } 312 } 313 goto notsame; 314 } 315 if (stb1.st_size != stb2.st_size) 316 goto notsame; 317 for (;;) { 318 i = read(f1, buf1, BUFSIZ); 319 j = read(f2, buf2, BUFSIZ); 320 if (i < 0 || j < 0 || i != j) 321 goto notsame; 322 if (i == 0 && j == 0) 323 goto same; 324 for (j = 0; j < i; j++) 325 if (buf1[j] != buf2[j]) 326 goto notsame; 327 } 328 same: 329 if (sflag == 0) 330 goto closem; 331 if (lflag) 332 dp->d_flags = SAME; 333 else 334 printf("Files %s and %s are identical\n", file1, file2); 335 goto closem; 336 notsame: 337 dirstatus |= 1; 338 if (!ascii(f1) || !ascii(f2)) { 339 if (lflag) 340 dp->d_flags |= DIFFER; 341 else if (opt == D_NORMAL || opt == D_CONTEXT || opt == D_UNIFIED) 342 printf("Binary files %s and %s differ\n", 343 file1, file2); 344 goto closem; 345 } 346 close(f1); 347 close(f2); 348 anychange = 1; 349 if (lflag) 350 calldiff(title); 351 else { 352 if (opt == D_EDIT) { 353 printf("ed - %s << '-*-END-*-'\n", dp->d_entry); 354 calldiff(0); 355 } else { 356 printf("%s%s %s\n", title, file1, file2); 357 calldiff(0); 358 } 359 if (opt == D_EDIT) 360 printf("w\nq\n-*-END-*-\n"); 361 } 362 return; 363 closem: 364 close(f1); 365 close(f2); 366 } 367 368 static void 369 calldiff(char *wantpr) 370 { 371 int lstatus, lstatus2, pv[2]; 372 pid_t pid; 373 374 prargs[2] = wantpr; 375 fflush(stdout); 376 if (wantpr) { 377 snprintf(etitle, title + sizeof title - etitle, 378 "%s %s", file1, file2); 379 pipe(pv); 380 pid = fork(); 381 if (pid == -1) { 382 warnx("No more processes"); 383 done(0); 384 } 385 if (pid == 0) { 386 close(0); 387 dup(pv[0]); 388 close(pv[0]); 389 close(pv[1]); 390 execv(pr + 4, prargs); 391 execv(pr, prargs); 392 perror(pr); 393 done(0); 394 } 395 } 396 pid = fork(); 397 if (pid == -1) { 398 warnx("No more processes"); 399 done(0); 400 } 401 if (pid == 0) { 402 if (wantpr) { 403 close(1); 404 dup(pv[1]); 405 close(pv[0]); 406 close(pv[1]); 407 } 408 execv(diff + 4, diffargv); 409 execv(diff, diffargv); 410 perror(diff); 411 done(0); 412 } 413 if (wantpr) { 414 close(pv[0]); 415 close(pv[1]); 416 } 417 while (wait(&lstatus) != pid) 418 continue; 419 while (wait(&lstatus2) != -1) 420 continue; 421 /* 422 if ((lstatus >> 8) >= 2) 423 done(0); 424 */ 425 dirstatus |= lstatus >> 8; 426 } 427 428 int 429 ascii(int f) 430 { 431 char buf[BUFSIZ], *cp; 432 int cnt; 433 434 lseek(f, (off_t)0, SEEK_SET); 435 cnt = read(f, buf, BUFSIZ); 436 cp = buf; 437 while (--cnt >= 0) 438 if (*cp++ & 0200) 439 return (0); 440 return (1); 441 } 442 443 /* 444 * THIS IS CRUDE. 445 */ 446 int 447 useless(char *cp) 448 { 449 if (cp[0] == '.') { 450 if (cp[1] == '\0') 451 return (1); /* directory "." */ 452 if (cp[1] == '.' && cp[2] == '\0') 453 return (1); /* directory ".." */ 454 } 455 if (start && strcmp(start, cp) > 0) 456 return (1); 457 return (0); 458 } 459