=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/diff/diffdir.c,v retrieving revision 1.18 retrieving revision 1.19 diff -c -r1.18 -r1.19 *** src/usr.bin/diff/diffdir.c 2003/07/06 02:11:12 1.18 --- src/usr.bin/diff/diffdir.c 2003/07/06 20:48:59 1.19 *************** *** 1,365 **** ! /* $OpenBSD: diffdir.c,v 1.18 2003/07/06 02:11:12 millert Exp $ */ /* ! * Copyright (C) Caldera International Inc. 2001-2002. ! * All rights reserved. * ! * Redistribution and use in source and binary forms, with or without ! * modification, are permitted provided that the following conditions ! * are met: ! * 1. Redistributions of source code and documentation must retain the above ! * copyright notice, this list of conditions and the following disclaimer. ! * 2. Redistributions in binary form must reproduce the above copyright ! * notice, this list of conditions and the following disclaimer in the ! * documentation and/or other materials provided with the distribution. ! * 3. All advertising materials mentioning features or use of this software ! * must display the following acknowledgement: ! * This product includes software developed or owned by Caldera ! * International, Inc. ! * 4. Neither the name of Caldera International, Inc. nor the names of other ! * contributors may be used to endorse or promote products derived from ! * this software without specific prior written permission. * ! * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA ! * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR ! * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ! * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ! * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT, ! * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ! * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ! * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ! * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, ! * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING ! * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ! * POSSIBILITY OF SUCH DAMAGE. */ ! #include ! #include #include #include #include #include #include #include #include #include "diff.h" - #include "pathnames.h" ! #if 0 ! static const char sccsid[] = "@(#)diffdir.c 4.12 (Berkeley) 4/30/89"; ! #endif /* ! * diff - directory comparison */ ! #define d_flags d_ino ! #define DIRECT 1 /* Directory */ ! struct dir { ! u_long d_ino; ! short d_reclen; ! short d_namlen; ! char *d_entry; ! }; ! static int dirstatus; /* exit status from diffdir */ ! static char title[2 * BUFSIZ]; ! static struct dir *setupdir(char *); ! static int ascii(int); ! static void compare(struct dir *); ! static void calldiff(void); ! static void setfile(char **fpp, char **epp, char *file); ! static int useless(char *); ! static void only(struct dir *dp, int which); ! static int entcmp(const void *, const void *); ! ! void ! diffdir(char **argv) ! { ! struct dir *dir1, *dir2; ! struct dir *d1, *d2; ! int i, cmp; ! ! if (opt == D_IFDEF) ! warnx("can't specify -I with directories"); ! if (opt == D_EDIT && sflag) ! warnx("warning: shouldn't give -s with -e"); ! strlcpy(title, "diff ", sizeof title); ! for (i = 1; diffargv[i + 2]; i++) { ! if (!strcmp(diffargv[i], "-")) ! continue; /* was -S, dont look silly */ ! strlcat(title, diffargv[i], sizeof title); ! strlcat(title, " ", sizeof title); ! } ! setfile(&file1, &efile1, file1); ! setfile(&file2, &efile2, file2); ! argv[0] = file1; ! argv[1] = file2; ! dir1 = setupdir(file1); ! dir2 = setupdir(file2); ! d1 = dir1; ! d2 = dir2; ! while (d1->d_entry != 0 || d2->d_entry != 0) { ! if (d1->d_entry && useless(d1->d_entry)) { ! d1++; ! continue; ! } ! if (d2->d_entry && useless(d2->d_entry)) { ! d2++; ! continue; ! } ! if (d1->d_entry == 0) ! cmp = 1; ! else if (d2->d_entry == 0) ! cmp = -1; ! else ! cmp = strcmp(d1->d_entry, d2->d_entry); ! if (cmp < 0) { ! if (opt == D_NORMAL || opt == D_CONTEXT || ! opt == D_UNIFIED) ! only(d1, 1); ! d1++; ! dirstatus |= 1; ! } else if (cmp == 0) { ! compare(d1); ! d1++; ! d2++; } else { ! if (opt == D_NORMAL || opt == D_CONTEXT || ! opt == D_UNIFIED) ! only(d2, 2); ! d2++; ! dirstatus |= 1; } } ! if (rflag) { ! for (d1 = dir1; d1->d_entry; d1++) { ! if ((d1->d_flags & DIRECT) == 0) ! continue; ! strlcpy(efile1, d1->d_entry, ! file1 + MAXPATHLEN - efile1); ! strlcpy(efile2, d1->d_entry, ! file2 + MAXPATHLEN - efile2); ! calldiff(); ! } } ! status = dirstatus; } ! void ! setfile(char **fpp, char **epp, char *file) { ! char *cp; ! size_t len; ! if (*file == '\0') ! file = "."; ! *fpp = emalloc(MAXPATHLEN); ! len = strlcpy(*fpp, file, MAXPATHLEN); ! if (len >= MAXPATHLEN - 1) ! errorx("%s: %s", file, strerror(ENAMETOOLONG)); ! cp = *fpp + len - 1; ! if (*cp == '/') ! ++cp; ! else { ! *++cp = '/'; ! *++cp = '\0'; } ! *epp = cp; ! } ! void ! only(struct dir *dp, int which) ! { ! char *file = which == 1 ? file1 : file2; ! char *efile = which == 1 ? efile1 : efile2; ! printf("Only in %.*s: %s\n", (int)(efile - file - 1), file, dp->d_entry); ! } ! struct dir * ! setupdir(char *cp) ! { ! struct dir *dp, *ep; ! struct dirent *rp; ! int nitems; ! DIR *dirp; ! ! dirp = opendir(cp); ! if (dirp == NULL) ! error("%s", cp); ! nitems = 0; ! dp = emalloc(sizeof(struct dir)); ! while ((rp = readdir(dirp))) { ! ep = &dp[nitems++]; ! ep->d_reclen = rp->d_reclen; ! ep->d_namlen = rp->d_namlen; ! ep->d_entry = 0; ! ep->d_flags = 0; ! if (ep->d_namlen > 0) { ! ep->d_entry = emalloc(ep->d_namlen + 1); ! strlcpy(ep->d_entry, rp->d_name, ep->d_namlen + 1); ! } ! dp = erealloc(dp, (nitems + 1) * sizeof(struct dir)); } ! dp[nitems].d_entry = 0; /* delimiter */ ! closedir(dirp); ! qsort(dp, nitems, sizeof(struct dir), entcmp); ! return (dp); } static int ! entcmp(const void *v1, const void *v2) { ! const struct dir *d1, *d2; ! d1 = v1; ! d2 = v2; ! return (strcmp(d1->d_entry, d2->d_entry)); } static void ! compare(struct dir *dp) { ! char buf1[BUFSIZ], buf2[BUFSIZ]; ! int i, j, f1, f2, fmt1, fmt2; ! struct stat stb1, stb2; ! strlcpy(efile1, dp->d_entry, file1 + MAXPATHLEN - efile1); ! strlcpy(efile2, dp->d_entry, file2 + MAXPATHLEN - efile2); ! f1 = open(file1, 0); ! if (f1 < 0) { ! warn("%s", file1); ! return; } ! f2 = open(file2, 0); ! if (f2 < 0) { ! warn("%s", file2); ! close(f1); ! return; ! } ! fstat(f1, &stb1); ! fstat(f2, &stb2); ! fmt1 = stb1.st_mode & S_IFMT; ! fmt2 = stb2.st_mode & S_IFMT; ! if (fmt1 != S_IFREG || fmt2 != S_IFREG) { ! if (fmt1 == fmt2) { ! if (fmt1 != S_IFDIR && stb1.st_rdev == stb2.st_rdev) ! goto same; ! if (fmt1 == S_IFDIR) { ! dp->d_flags = DIRECT; ! if (opt == D_EDIT) ! goto closem; ! printf("Common subdirectories: %s and %s\n", ! file1, file2); ! goto closem; ! } } ! goto notsame; } ! if (stb1.st_size != stb2.st_size) ! goto notsame; ! for (;;) { ! i = read(f1, buf1, BUFSIZ); ! j = read(f2, buf2, BUFSIZ); ! if (i < 0 || j < 0 || i != j) ! goto notsame; ! if (i == 0 && j == 0) ! goto same; ! for (j = 0; j < i; j++) ! if (buf1[j] != buf2[j]) ! goto notsame; ! } ! same: ! if (sflag != 0) ! printf("Files %s and %s are identical\n", file1, file2); ! goto closem; ! notsame: ! dirstatus |= 1; ! if (!ascii(f1) || !ascii(f2)) { ! if (opt == D_NORMAL || opt == D_CONTEXT || opt == D_UNIFIED) ! printf("Binary files %s and %s differ\n", ! file1, file2); ! goto closem; ! } ! close(f1); ! close(f2); ! anychange = 1; ! if (opt == D_EDIT) { ! printf("ed - %s << '-*-END-*-'\n", dp->d_entry); ! calldiff(); ! } else { ! printf("%s%s %s\n", title, file1, file2); ! calldiff(); ! } ! if (opt == D_EDIT) ! printf("w\nq\n-*-END-*-\n"); ! return; ! closem: ! close(f1); ! close(f2); ! } ! static void ! calldiff(void) ! { ! int lstatus; ! pid_t pid; ! ! fflush(stdout); ! pid = fork(); ! if (pid == -1) ! errorx("No more processes"); ! if (pid == 0) { ! execv(_PATH_DIFF, diffargv); ! error("%s", _PATH_DIFF); } ! while (wait(&lstatus) != pid) ! continue; ! /* ! if ((lstatus >> 8) >= 2) ! done(0); ! */ ! dirstatus |= lstatus >> 8; } ! int ! ascii(int f) { ! char buf[BUFSIZ], *cp; ! int cnt; ! if (aflag) return (1); ! lseek(f, (off_t)0, SEEK_SET); ! cnt = read(f, buf, BUFSIZ); ! cp = buf; ! while (--cnt >= 0) ! if (*cp++ & 0200) ! return (0); ! return (1); ! } - /* - * THIS IS CRUDE. - */ - int - useless(char *cp) - { - if (cp[0] == '.') { - if (cp[1] == '\0') - return (1); /* directory "." */ - if (cp[1] == '.' && cp[2] == '\0') - return (1); /* directory ".." */ - } - if (start && strcmp(start, cp) > 0) - return (1); return (0); } --- 1,292 ---- ! /* $OpenBSD: diffdir.c,v 1.19 2003/07/06 20:48:59 millert Exp $ */ /* ! * Copyright (c) 2003 Todd C. Miller * ! * Permission to use, copy, modify, and distribute this software for any ! * purpose with or without fee is hereby granted, provided that the above ! * copyright notice and this permission notice appear in all copies. * ! * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ! * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ! * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ! * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ! * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ! * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ! * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ! * ! * Sponsored in part by the Defense Advanced Research Projects ! * Agency (DARPA) and Air Force Research Laboratory, Air Force ! * Materiel Command, USAF, under agreement number F39502-99-1-0512. */ ! #ifndef lint ! static const char rcsid[] = "$OpenBSD: diffdir.c,v 1.19 2003/07/06 20:48:59 millert Exp $"; ! #endif /* not lint */ + #include + #include + #include + #include #include #include + #include + #include #include #include #include #include #include "diff.h" ! static int dircompare(const void *, const void *); ! static int excluded(const char *); ! static struct dirent **slurpdir(char *, char **); ! static void diffit(struct dirent *, char *, size_t, char *, size_t); /* ! * Diff directory traveral. Will be called recursively if -r was specified. */ ! void ! diffdir(char *p1, char *p2) ! { ! struct dirent **dirp1, **dirp2, **dp1, **dp2; ! struct dirent *dent1, *dent2; ! size_t dirlen1, dirlen2; ! char path1[MAXPATHLEN], path2[MAXPATHLEN]; ! char *dirbuf1, *dirbuf2; ! int pos; ! dirlen1 = strlcpy(path1, *p1 ? p1 : ".", sizeof(path1)); ! if (dirlen1 >= sizeof(path1) - 1) { ! warnx("%s: %s", p1, strerror(ENAMETOOLONG)); ! status = 2; ! return; ! } ! if (path1[dirlen1 - 1] != '/') { ! path1[dirlen1++] = '/'; ! path1[dirlen1] = '\0'; ! } ! dirlen2 = strlcpy(path2, *p2 ? p2 : ".", sizeof(path2)); ! if (dirlen2 >= sizeof(path2) - 1) { ! warnx("%s: %s", p2, strerror(ENAMETOOLONG)); ! status = 2; ! return; ! } ! if (path2[dirlen2 - 1] != '/') { ! path2[dirlen2++] = '/'; ! path2[dirlen2] = '\0'; ! } ! /* get a list of the entries in each directory */ ! dp1 = dirp1 = slurpdir(path1, &dirbuf1); ! dp2 = dirp2 = slurpdir(path2, &dirbuf2); ! if (dirp1 == NULL || dirp2 == NULL) ! return; ! /* ! * If we were given a starting point, find it. ! */ ! if (start != NULL) { ! while (*dp1 != NULL && strcmp((*dp1)->d_name, start) < 0) ! dp1++; ! while (*dp2 != NULL && strcmp((*dp2)->d_name, start) < 0) ! dp2++; ! } + /* + * Iterate through the two directory lists, diffing as we go. + */ + while (*dp1 != NULL || *dp2 != NULL) { + dent1 = *dp1; + dent2 = *dp2; ! pos = dent1 == NULL ? 1 : dent2 == NULL ? -1 : ! strcmp(dent1->d_name, dent2->d_name); ! if (pos == 0) { ! /* file exists in both dirs, diff it */ ! diffit(dent1, path1, dirlen1, path2, dirlen2); ! dp1++; ! dp2++; ! } else if (pos < 0) { ! /* file only in first dir, only diff if -N */ ! if (Nflag) ! diffit(dent1, path1, dirlen1, path2, dirlen2); ! else if (format == D_NORMAL || format == D_CONTEXT || ! format == D_UNIFIED) ! /* XXX GNU diff always prints this XXX */ ! printf("Only in %.*s: %s\n", (int)(dirlen1 - 1), ! path1, dent1->d_name); ! dp1++; } else { ! /* file only in second dir, only diff if -N */ ! if (Nflag) ! diffit(dent2, path1, dirlen1, path2, dirlen2); ! else if (format == D_NORMAL || format == D_CONTEXT || ! format == D_UNIFIED) ! /* XXX GNU diff always prints this XXX */ ! printf("Only in %.*s: %s\n", (int)(dirlen2 - 1), ! path2, dent2->d_name); ! dp2++; } } ! ! if (dirbuf1 != NULL) { ! free(dirp1); ! free(dirbuf1); } ! if (dirbuf2 != NULL) { ! free(dirp2); ! free(dirbuf2); ! } } ! /* ! * Read in a whole directory's worth of struct dirents, culling ! * out the "excluded" ones. ! * Returns an array of struct dirent *'s that point into the buffer ! * returned via bufp. Caller is responsible for free()ing both of these. ! */ ! static struct dirent ** ! slurpdir(char *path, char **bufp) { ! char *buf, *ebuf, *cp; ! size_t bufsize; ! long base; ! int fd, nbytes, entries; ! struct stat sb; ! struct dirent **dirlist, *dp; ! *bufp = NULL; ! if ((fd = open(path, O_RDONLY, 0644)) == -1) { ! static struct dirent *dummy; ! ! if (!Nflag) { ! warn("%s", path); ! return (NULL); ! } ! return (&dummy); } ! fstat(fd, &sb); ! bufsize = sb.st_size; ! if (bufsize < sb.st_blksize) ! bufsize = sb.st_blksize; ! buf = emalloc(bufsize); ! nbytes = getdirentries(fd, buf, bufsize, &base); ! if (nbytes <= 0) { ! free(buf); ! warn("%s", path); ! return (NULL); ! } ! ebuf = buf + nbytes; ! close(fd); ! /* ! * We now have all the directory entries in our buffer. ! * However, in order to easily sort them we need to convert ! * the buffer into an array. ! */ ! for (entries = 0, cp = buf; cp < ebuf; ) { ! dp = (struct dirent *)cp; ! if (dp->d_fileno != 0 && dp->d_type != DT_WHT) ! entries++; ! if (dp->d_reclen <= 0) ! break; ! cp += dp->d_reclen; } ! dirlist = emalloc(sizeof(struct dirent *) * (entries + 1)); ! for (entries = 0, cp = buf; cp < ebuf; ) { ! dp = (struct dirent *)cp; ! if (dp->d_fileno != 0 && dp->d_type != DT_WHT && ! !excluded(dp->d_name)) ! dirlist[entries++] = dp; ! if (dp->d_reclen <= 0) ! break; ! cp += dp->d_reclen; ! } ! dirlist[entries] = NULL; ! ! qsort(dirlist, entries, sizeof(struct dir *), dircompare); ! ! *bufp = buf; ! return (dirlist); } + /* + * Compare d_name in two dirent structures; for qsort(3). + */ static int ! dircompare(const void *vp1, const void *vp2) { ! struct dirent *dp1 = *((struct dirent **) vp1); ! struct dirent *dp2 = *((struct dirent **) vp2); ! return (strcmp(dp1->d_name, dp2->d_name)); } + /* + * Do the actual diff by calling either diffreg() or diffdir(). + */ static void ! diffit(struct dirent *dp, char *path1, size_t plen1, char *path2, size_t plen2) { ! int flags = D_HEADER; ! strlcpy(path1 + plen1, dp->d_name, MAXPATHLEN - plen1); ! if (stat(path1, &stb1) != 0) { ! if (!Nflag || errno != ENOENT) { ! warn("%s", path1); ! return; ! } ! flags |= D_EMPTY1; ! memset(&stb1, 0, sizeof(stb1)); } ! ! strlcpy(path2 + plen2, dp->d_name, MAXPATHLEN - plen2); ! if (stat(path2, &stb2) != 0) { ! if (!Nflag || errno != ENOENT) { ! warn("%s", path2); ! return; } ! flags |= D_EMPTY2; ! memset(&stb2, 0, sizeof(stb2)); ! stb2.st_mode = stb1.st_mode; } ! if (stb1.st_mode == 0) ! stb1.st_mode = stb2.st_mode; ! if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) { ! /* XXX GNU diff always prints this for dirs XXX */ ! if (format != D_EDIT) ! printf("Common subdirectories: %s and %s\n", ! path1, path2); ! if (rflag) ! diffdir(path1, path2); ! return; } ! diffreg(path1, path2, flags); } ! /* ! * Exclude the given directory entry? ! */ ! static int ! excluded(const char *entry) { ! struct excludes *excl; ! /* always skip "." and ".." */ ! if (entry[0] == '.' && ! (entry[1] == '\0' || (entry[1] == '.' && entry[2] == '\0'))) return (1); ! /* check excludes list */ ! for (excl = excludes_list; excl != NULL; excl = excl->next) ! if (fnmatch(excl->pattern, entry, FNM_PATHNAME) == 0) ! return (1); return (0); }