1 /* $OpenBSD: diffdir.c,v 1.8 2003/06/25 03:39:23 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 <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 88 void 89 diffdir(char **argv) 90 { 91 struct dir *d1, *d2; 92 struct dir *dir1, *dir2; 93 int i; 94 int cmp; 95 96 if (opt == D_IFDEF) { 97 fprintf(stderr, "diff: can't specify -I with directories\n"); 98 done(0); 99 } 100 if (opt == D_EDIT && (sflag || lflag)) 101 fprintf(stderr, 102 "diff: warning: shouldn't give -s or -l with -e\n"); 103 strlcpy(title, "diff ", sizeof title); 104 for (i = 1; diffargv[i + 2]; i++) { 105 if (!strcmp(diffargv[i], "-")) 106 continue; /* was -S, dont look silly */ 107 strlcat(title, diffargv[i], sizeof title); 108 strlcat(title, " ", sizeof title); 109 } 110 for (etitle = title; *etitle; etitle++); 111 setfile(&file1, &efile1, file1); 112 setfile(&file2, &efile2, file2); 113 argv[0] = file1; 114 argv[1] = file2; 115 dir1 = setupdir(file1); 116 dir2 = setupdir(file2); 117 d1 = dir1; 118 d2 = dir2; 119 while (d1->d_entry != 0 || d2->d_entry != 0) { 120 if (d1->d_entry && useless(d1->d_entry)) { 121 d1++; 122 continue; 123 } 124 if (d2->d_entry && useless(d2->d_entry)) { 125 d2++; 126 continue; 127 } 128 if (d1->d_entry == 0) 129 cmp = 1; 130 else if (d2->d_entry == 0) 131 cmp = -1; 132 else 133 cmp = strcmp(d1->d_entry, d2->d_entry); 134 if (cmp < 0) { 135 if (lflag) 136 d1->d_flags |= ONLY; 137 else if (opt == 0 || opt == 2) 138 only(d1, 1); 139 d1++; 140 dirstatus |= 1; 141 } else if (cmp == 0) { 142 compare(d1); 143 d1++; 144 d2++; 145 } else { 146 if (lflag) 147 d2->d_flags |= ONLY; 148 else if (opt == 0 || opt == 2) 149 only(d2, 2); 150 d2++; 151 dirstatus |= 1; 152 } 153 } 154 if (lflag) { 155 scanpr(dir1, ONLY, "Only in %.*s", file1, efile1, 0, 0); 156 scanpr(dir2, ONLY, "Only in %.*s", file2, efile2, 0, 0); 157 scanpr(dir1, SAME, "Common identical files in %.*s and %.*s", 158 file1, efile1, file2, efile2); 159 scanpr(dir1, DIFFER, "Binary files which differ in %.*s and %.*s", 160 file1, efile1, file2, efile2); 161 scanpr(dir1, DIRECT, "Common subdirectories of %.*s and %.*s", 162 file1, efile1, file2, efile2); 163 } 164 if (rflag) { 165 if (header && lflag) 166 printf("\f"); 167 for (d1 = dir1; d1->d_entry; d1++) { 168 if ((d1->d_flags & DIRECT) == 0) 169 continue; 170 strcpy(efile1, d1->d_entry); 171 strcpy(efile2, d1->d_entry); 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 183 *fpp = talloc(BUFSIZ); 184 strcpy(*fpp, file); 185 for (cp = *fpp; *cp; cp++) 186 continue; 187 *cp++ = '/'; 188 *epp = cp; 189 } 190 191 static void 192 scanpr(struct dir *dp, int test, char *title, char *file1, char *efile1, 193 char *file2, char *efile2) 194 { 195 int titled = 0; 196 197 for (; dp->d_entry; dp++) { 198 if ((dp->d_flags & test) == 0) 199 continue; 200 if (titled == 0) { 201 if (header == 0) 202 header = 1; 203 else 204 printf("\n"); 205 printf(title, 206 efile1 - file1 - 1, file1, 207 efile2 - file2 - 1, file2); 208 printf(":\n"); 209 titled = 1; 210 } 211 printf("\t%s\n", dp->d_entry); 212 } 213 } 214 215 void 216 only(struct dir *dp, int which) 217 { 218 char *file = which == 1 ? file1 : file2; 219 char *efile = which == 1 ? efile1 : efile2; 220 221 printf("Only in %.*s: %s\n", (int)(efile - file - 1), file, dp->d_entry); 222 } 223 224 struct dir * 225 setupdir(char *cp) 226 { 227 struct dir *dp, *ep; 228 struct direct *rp; 229 int nitems; 230 DIR *dirp; 231 232 dirp = opendir(cp); 233 if (dirp == NULL) { 234 fprintf(stderr, "diff: "); 235 perror(cp); 236 done(0); 237 } 238 nitems = 0; 239 dp = talloc(sizeof(struct dir)); 240 while ((rp = readdir(dirp))) { 241 ep = &dp[nitems++]; 242 ep->d_reclen = rp->d_reclen; 243 ep->d_namlen = rp->d_namlen; 244 ep->d_entry = 0; 245 ep->d_flags = 0; 246 if (ep->d_namlen > 0) { 247 ep->d_entry = talloc(ep->d_namlen + 1); 248 strcpy(ep->d_entry, rp->d_name); 249 } 250 dp = ralloc(dp, (nitems + 1) * sizeof(struct dir)); 251 } 252 dp[nitems].d_entry = 0; /* delimiter */ 253 closedir(dirp); 254 qsort(dp, nitems, sizeof(struct dir), entcmp); 255 return (dp); 256 } 257 258 static int 259 entcmp(const void *v1, const void *v2) 260 { 261 const struct dir *d1, *d2; 262 263 d1 = v1; 264 d2 = v2; 265 return (strcmp(d1->d_entry, d2->d_entry)); 266 } 267 268 static void 269 compare(struct dir *dp) 270 { 271 int i, j; 272 int f1, f2, fmt1, fmt2; 273 struct stat stb1, stb2; 274 char buf1[BUFSIZ], buf2[BUFSIZ]; 275 276 strcpy(efile1, dp->d_entry); 277 strcpy(efile2, dp->d_entry); 278 f1 = open(file1, 0); 279 if (f1 < 0) { 280 perror(file1); 281 return; 282 } 283 f2 = open(file2, 0); 284 if (f2 < 0) { 285 perror(file2); 286 close(f1); 287 return; 288 } 289 fstat(f1, &stb1); 290 fstat(f2, &stb2); 291 fmt1 = stb1.st_mode & S_IFMT; 292 fmt2 = stb2.st_mode & S_IFMT; 293 if (fmt1 != S_IFREG || fmt2 != S_IFREG) { 294 if (fmt1 == fmt2) { 295 if (fmt1 != S_IFDIR && stb1.st_rdev == stb2.st_rdev) 296 goto same; 297 if (fmt1 == S_IFDIR) { 298 dp->d_flags = DIRECT; 299 if (lflag || opt == D_EDIT) 300 goto closem; 301 printf("Common subdirectories: %s and %s\n", 302 file1, file2); 303 goto closem; 304 } 305 } 306 goto notsame; 307 } 308 if (stb1.st_size != stb2.st_size) 309 goto notsame; 310 for (;;) { 311 i = read(f1, buf1, BUFSIZ); 312 j = read(f2, buf2, BUFSIZ); 313 if (i < 0 || j < 0 || i != j) 314 goto notsame; 315 if (i == 0 && j == 0) 316 goto same; 317 for (j = 0; j < i; j++) 318 if (buf1[j] != buf2[j]) 319 goto notsame; 320 } 321 same: 322 if (sflag == 0) 323 goto closem; 324 if (lflag) 325 dp->d_flags = SAME; 326 else 327 printf("Files %s and %s are identical\n", file1, file2); 328 goto closem; 329 notsame: 330 dirstatus |= 1; 331 if (!ascii(f1) || !ascii(f2)) { 332 if (lflag) 333 dp->d_flags |= DIFFER; 334 else if (opt == D_NORMAL || opt == D_CONTEXT) 335 printf("Binary files %s and %s differ\n", 336 file1, file2); 337 goto closem; 338 } 339 close(f1); 340 close(f2); 341 anychange = 1; 342 if (lflag) 343 calldiff(title); 344 else { 345 if (opt == D_EDIT) { 346 printf("ed - %s << '-*-END-*-'\n", dp->d_entry); 347 calldiff(0); 348 } else { 349 printf("%s%s %s\n", title, file1, file2); 350 calldiff(0); 351 } 352 if (opt == D_EDIT) 353 printf("w\nq\n-*-END-*-\n"); 354 } 355 return; 356 closem: 357 close(f1); 358 close(f2); 359 } 360 361 static void 362 calldiff(char *wantpr) 363 { 364 int pid, lstatus, lstatus2, pv[2]; 365 366 prargs[2] = wantpr; 367 fflush(stdout); 368 if (wantpr) { 369 snprintf(etitle, title + sizeof title - etitle, 370 "%s %s", file1, file2); 371 pipe(pv); 372 pid = fork(); 373 if (pid == -1) { 374 fprintf(stderr, "No more processes"); 375 done(0); 376 } 377 if (pid == 0) { 378 close(0); 379 dup(pv[0]); 380 close(pv[0]); 381 close(pv[1]); 382 execv(pr + 4, prargs); 383 execv(pr, prargs); 384 perror(pr); 385 done(0); 386 } 387 } 388 pid = fork(); 389 if (pid == -1) { 390 fprintf(stderr, "diff: No more processes\n"); 391 done(0); 392 } 393 if (pid == 0) { 394 if (wantpr) { 395 close(1); 396 dup(pv[1]); 397 close(pv[0]); 398 close(pv[1]); 399 } 400 execv(diff + 4, diffargv); 401 execv(diff, diffargv); 402 perror(diff); 403 done(0); 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]; 424 int cnt; 425 char *cp; 426 427 lseek(f, (off_t)0, SEEK_SET); 428 cnt = read(f, buf, BUFSIZ); 429 cp = buf; 430 while (--cnt >= 0) 431 if (*cp++ & 0200) 432 return (0); 433 return (1); 434 } 435 436 /* 437 * THIS IS CRUDE. 438 */ 439 int 440 useless(char *cp) 441 { 442 if (cp[0] == '.') { 443 if (cp[1] == '\0') 444 return (1); /* directory "." */ 445 if (cp[1] == '.' && cp[2] == '\0') 446 return (1); /* directory ".." */ 447 } 448 if (start && strcmp(start, cp) > 0) 449 return (1); 450 return (0); 451 } 452