1 /* $OpenBSD: diffdir.c,v 1.15 2003/06/27 20:28:13 tedu 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 errorx("%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 error("%s", cp); 244 nitems = 0; 245 dp = emalloc(sizeof(struct dir)); 246 while ((rp = readdir(dirp))) { 247 ep = &dp[nitems++]; 248 ep->d_reclen = rp->d_reclen; 249 ep->d_namlen = rp->d_namlen; 250 ep->d_entry = 0; 251 ep->d_flags = 0; 252 if (ep->d_namlen > 0) { 253 ep->d_entry = emalloc(ep->d_namlen + 1); 254 strlcpy(ep->d_entry, rp->d_name, ep->d_namlen + 1); 255 } 256 dp = erealloc(dp, (nitems + 1) * sizeof(struct dir)); 257 } 258 dp[nitems].d_entry = 0; /* delimiter */ 259 closedir(dirp); 260 qsort(dp, nitems, sizeof(struct dir), entcmp); 261 return (dp); 262 } 263 264 static int 265 entcmp(const void *v1, const void *v2) 266 { 267 const struct dir *d1, *d2; 268 269 d1 = v1; 270 d2 = v2; 271 return (strcmp(d1->d_entry, d2->d_entry)); 272 } 273 274 static void 275 compare(struct dir *dp) 276 { 277 char buf1[BUFSIZ], buf2[BUFSIZ]; 278 int i, j, f1, f2, fmt1, fmt2; 279 struct stat stb1, stb2; 280 281 strlcpy(efile1, dp->d_entry, file1 + MAXPATHLEN - efile1); 282 strlcpy(efile2, dp->d_entry, file2 + MAXPATHLEN - efile2); 283 f1 = open(file1, 0); 284 if (f1 < 0) { 285 warn("%s", file1); 286 return; 287 } 288 f2 = open(file2, 0); 289 if (f2 < 0) { 290 warn("%s", file2); 291 close(f1); 292 return; 293 } 294 fstat(f1, &stb1); 295 fstat(f2, &stb2); 296 fmt1 = stb1.st_mode & S_IFMT; 297 fmt2 = stb2.st_mode & S_IFMT; 298 if (fmt1 != S_IFREG || fmt2 != S_IFREG) { 299 if (fmt1 == fmt2) { 300 if (fmt1 != S_IFDIR && stb1.st_rdev == stb2.st_rdev) 301 goto same; 302 if (fmt1 == S_IFDIR) { 303 dp->d_flags = DIRECT; 304 if (lflag || opt == D_EDIT) 305 goto closem; 306 printf("Common subdirectories: %s and %s\n", 307 file1, file2); 308 goto closem; 309 } 310 } 311 goto notsame; 312 } 313 if (stb1.st_size != stb2.st_size) 314 goto notsame; 315 for (;;) { 316 i = read(f1, buf1, BUFSIZ); 317 j = read(f2, buf2, BUFSIZ); 318 if (i < 0 || j < 0 || i != j) 319 goto notsame; 320 if (i == 0 && j == 0) 321 goto same; 322 for (j = 0; j < i; j++) 323 if (buf1[j] != buf2[j]) 324 goto notsame; 325 } 326 same: 327 if (sflag == 0) 328 goto closem; 329 if (lflag) 330 dp->d_flags = SAME; 331 else 332 printf("Files %s and %s are identical\n", file1, file2); 333 goto closem; 334 notsame: 335 dirstatus |= 1; 336 if (!ascii(f1) || !ascii(f2)) { 337 if (lflag) 338 dp->d_flags |= DIFFER; 339 else if (opt == D_NORMAL || opt == D_CONTEXT || opt == D_UNIFIED) 340 printf("Binary files %s and %s differ\n", 341 file1, file2); 342 goto closem; 343 } 344 close(f1); 345 close(f2); 346 anychange = 1; 347 if (lflag) 348 calldiff(title); 349 else { 350 if (opt == D_EDIT) { 351 printf("ed - %s << '-*-END-*-'\n", dp->d_entry); 352 calldiff(0); 353 } else { 354 printf("%s%s %s\n", title, file1, file2); 355 calldiff(0); 356 } 357 if (opt == D_EDIT) 358 printf("w\nq\n-*-END-*-\n"); 359 } 360 return; 361 closem: 362 close(f1); 363 close(f2); 364 } 365 366 static void 367 calldiff(char *wantpr) 368 { 369 int lstatus, lstatus2, pv[2]; 370 pid_t pid; 371 372 prargs[2] = wantpr; 373 fflush(stdout); 374 if (wantpr) { 375 snprintf(etitle, title + sizeof title - etitle, 376 "%s %s", file1, file2); 377 pipe(pv); 378 pid = fork(); 379 if (pid == -1) 380 errorx("No more processes"); 381 if (pid == 0) { 382 close(0); 383 dup(pv[0]); 384 close(pv[0]); 385 close(pv[1]); 386 execv(pr + 4, prargs); 387 execv(pr, prargs); 388 errorx("%s", pr); 389 } 390 } 391 pid = fork(); 392 if (pid == -1) 393 errorx("No more processes"); 394 if (pid == 0) { 395 if (wantpr) { 396 close(1); 397 dup(pv[1]); 398 close(pv[0]); 399 close(pv[1]); 400 } 401 execv(diff + 4, diffargv); 402 execv(diff, diffargv); 403 error("%s", diff); 404 } 405 if (wantpr) { 406 close(pv[0]); 407 close(pv[1]); 408 } 409 while (wait(&lstatus) != pid) 410 continue; 411 while (wait(&lstatus2) != -1) 412 continue; 413 /* 414 if ((lstatus >> 8) >= 2) 415 done(0); 416 */ 417 dirstatus |= lstatus >> 8; 418 } 419 420 int 421 ascii(int f) 422 { 423 char buf[BUFSIZ], *cp; 424 int cnt; 425 426 if (aflag) 427 return (1); 428 429 lseek(f, (off_t)0, SEEK_SET); 430 cnt = read(f, buf, BUFSIZ); 431 cp = buf; 432 while (--cnt >= 0) 433 if (*cp++ & 0200) 434 return (0); 435 return (1); 436 } 437 438 /* 439 * THIS IS CRUDE. 440 */ 441 int 442 useless(char *cp) 443 { 444 if (cp[0] == '.') { 445 if (cp[1] == '\0') 446 return (1); /* directory "." */ 447 if (cp[1] == '.' && cp[2] == '\0') 448 return (1); /* directory ".." */ 449 } 450 if (start && strcmp(start, cp) > 0) 451 return (1); 452 return (0); 453 } 454