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