Annotation of src/usr.bin/diff/diffdir.c, Revision 1.23
1.23 ! millert 1: /* $OpenBSD: diffdir.c,v 1.22 2003/07/09 00:07:44 millert Exp $ */
1.2 deraadt 2:
3: /*
1.19 millert 4: * Copyright (c) 2003 Todd C. Miller <Todd.Miller@courtesan.com>
1.2 deraadt 5: *
1.19 millert 6: * Permission to use, copy, modify, and distribute this software for any
7: * purpose with or without fee is hereby granted, provided that the above
8: * copyright notice and this permission notice appear in all copies.
1.2 deraadt 9: *
1.19 millert 10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17: *
18: * Sponsored in part by the Defense Advanced Research Projects
19: * Agency (DARPA) and Air Force Research Laboratory, Air Force
20: * Materiel Command, USAF, under agreement number F39502-99-1-0512.
1.2 deraadt 21: */
22:
1.19 millert 23: #ifndef lint
1.23 ! millert 24: static const char rcsid[] = "$OpenBSD: diffdir.c,v 1.22 2003/07/09 00:07:44 millert Exp $";
1.19 millert 25: #endif /* not lint */
26:
27: #include <sys/param.h>
28: #include <sys/stat.h>
1.3 tedu 29:
1.12 millert 30: #include <dirent.h>
1.19 millert 31: #include <err.h>
1.12 millert 32: #include <errno.h>
33: #include <fcntl.h>
1.19 millert 34: #include <fnmatch.h>
35: #include <paths.h>
1.8 tedu 36: #include <stdio.h>
1.3 tedu 37: #include <stdlib.h>
1.12 millert 38: #include <string.h>
1.3 tedu 39: #include <unistd.h>
1.1 deraadt 40:
41: #include "diff.h"
1.3 tedu 42:
1.19 millert 43: static int dircompare(const void *, const void *);
44: static int excluded(const char *);
1.20 millert 45: static struct dirent **slurpdir(char *, char **, int);
1.19 millert 46: static void diffit(struct dirent *, char *, size_t, char *, size_t);
1.3 tedu 47:
1.22 millert 48: #define d_status d_type /* we need to store status for -l */
49:
1.1 deraadt 50: /*
1.19 millert 51: * Diff directory traveral. Will be called recursively if -r was specified.
1.1 deraadt 52: */
1.19 millert 53: void
54: diffdir(char *p1, char *p2)
55: {
56: struct dirent **dirp1, **dirp2, **dp1, **dp2;
57: struct dirent *dent1, *dent2;
58: size_t dirlen1, dirlen2;
59: char path1[MAXPATHLEN], path2[MAXPATHLEN];
60: char *dirbuf1, *dirbuf2;
61: int pos;
62:
63: dirlen1 = strlcpy(path1, *p1 ? p1 : ".", sizeof(path1));
64: if (dirlen1 >= sizeof(path1) - 1) {
65: warnx("%s: %s", p1, strerror(ENAMETOOLONG));
66: status = 2;
67: return;
68: }
69: if (path1[dirlen1 - 1] != '/') {
70: path1[dirlen1++] = '/';
71: path1[dirlen1] = '\0';
72: }
73: dirlen2 = strlcpy(path2, *p2 ? p2 : ".", sizeof(path2));
74: if (dirlen2 >= sizeof(path2) - 1) {
75: warnx("%s: %s", p2, strerror(ENAMETOOLONG));
76: status = 2;
77: return;
78: }
79: if (path2[dirlen2 - 1] != '/') {
80: path2[dirlen2++] = '/';
81: path2[dirlen2] = '\0';
82: }
1.1 deraadt 83:
1.19 millert 84: /* get a list of the entries in each directory */
1.20 millert 85: dp1 = dirp1 = slurpdir(path1, &dirbuf1, Nflag + Pflag);
86: dp2 = dirp2 = slurpdir(path2, &dirbuf2, Nflag);
1.19 millert 87: if (dirp1 == NULL || dirp2 == NULL)
88: return;
1.1 deraadt 89:
1.19 millert 90: /*
91: * If we were given a starting point, find it.
92: */
93: if (start != NULL) {
94: while (*dp1 != NULL && strcmp((*dp1)->d_name, start) < 0)
95: dp1++;
96: while (*dp2 != NULL && strcmp((*dp2)->d_name, start) < 0)
97: dp2++;
98: }
1.8 tedu 99:
1.19 millert 100: /*
101: * Iterate through the two directory lists, diffing as we go.
102: */
103: while (*dp1 != NULL || *dp2 != NULL) {
104: dent1 = *dp1;
105: dent2 = *dp2;
106:
107: pos = dent1 == NULL ? 1 : dent2 == NULL ? -1 :
108: strcmp(dent1->d_name, dent2->d_name);
109: if (pos == 0) {
110: /* file exists in both dirs, diff it */
111: diffit(dent1, path1, dirlen1, path2, dirlen2);
112: dp1++;
113: dp2++;
114: } else if (pos < 0) {
115: /* file only in first dir, only diff if -N */
116: if (Nflag)
117: diffit(dent1, path1, dirlen1, path2, dirlen2);
1.22 millert 118: else if (lflag)
119: dent1->d_status |= D_ONLY;
1.19 millert 120: else if (format == D_NORMAL || format == D_CONTEXT ||
1.21 millert 121: format == D_UNIFIED || format == D_BRIEF)
1.19 millert 122: /* XXX GNU diff always prints this XXX */
123: printf("Only in %.*s: %s\n", (int)(dirlen1 - 1),
124: path1, dent1->d_name);
125: dp1++;
1.1 deraadt 126: } else {
1.20 millert 127: /* file only in second dir, only diff if -N or -P */
128: if (Nflag || Pflag)
1.19 millert 129: diffit(dent2, path1, dirlen1, path2, dirlen2);
1.22 millert 130: else if (lflag)
131: dent2->d_status |= D_ONLY;
1.19 millert 132: else if (format == D_NORMAL || format == D_CONTEXT ||
1.21 millert 133: format == D_UNIFIED || format == D_BRIEF)
1.19 millert 134: /* XXX GNU diff always prints this XXX */
135: printf("Only in %.*s: %s\n", (int)(dirlen2 - 1),
136: path2, dent2->d_name);
137: dp2++;
1.1 deraadt 138: }
139: }
1.22 millert 140: if (lflag) {
1.23 ! millert 141: path1[dirlen1] = '\0';
! 142: path2[dirlen2] = '\0';
1.22 millert 143: for (dp1 = dirp1; (dent1 = *dp1) != NULL; dp1++) {
144: print_status(dent1->d_status, path1, path2,
145: dent1->d_name);
146: }
147: for (dp2 = dirp2; (dent2 = *dp2) != NULL; dp2++) {
148: if (dent2->d_status == D_ONLY)
149: print_status(dent2->d_status, path2, NULL,
150: dent2->d_name);
151: }
152: }
1.19 millert 153:
154: if (dirbuf1 != NULL) {
155: free(dirp1);
156: free(dirbuf1);
157: }
158: if (dirbuf2 != NULL) {
159: free(dirp2);
160: free(dirbuf2);
1.1 deraadt 161: }
162: }
163:
1.19 millert 164: /*
165: * Read in a whole directory's worth of struct dirents, culling
166: * out the "excluded" ones.
167: * Returns an array of struct dirent *'s that point into the buffer
168: * returned via bufp. Caller is responsible for free()ing both of these.
169: */
170: static struct dirent **
1.20 millert 171: slurpdir(char *path, char **bufp, int enoentok)
1.1 deraadt 172: {
1.19 millert 173: char *buf, *ebuf, *cp;
174: size_t bufsize;
175: long base;
176: int fd, nbytes, entries;
177: struct stat sb;
178: struct dirent **dirlist, *dp;
179:
180: *bufp = NULL;
181: if ((fd = open(path, O_RDONLY, 0644)) == -1) {
182: static struct dirent *dummy;
183:
1.20 millert 184: if (!enoentok || errno != ENOENT) {
1.19 millert 185: warn("%s", path);
186: return (NULL);
187: }
188: return (&dummy);
189: }
190: fstat(fd, &sb);
1.1 deraadt 191:
1.19 millert 192: bufsize = sb.st_size;
193: if (bufsize < sb.st_blksize)
194: bufsize = sb.st_blksize;
195: buf = emalloc(bufsize);
196:
197: nbytes = getdirentries(fd, buf, bufsize, &base);
198: if (nbytes <= 0) {
199: free(buf);
200: warn("%s", path);
201: return (NULL);
1.12 millert 202: }
1.19 millert 203: ebuf = buf + nbytes;
204: close(fd);
1.1 deraadt 205:
1.19 millert 206: /*
207: * We now have all the directory entries in our buffer.
208: * However, in order to easily sort them we need to convert
209: * the buffer into an array.
210: */
211: for (entries = 0, cp = buf; cp < ebuf; ) {
212: dp = (struct dirent *)cp;
213: if (dp->d_fileno != 0 && dp->d_type != DT_WHT)
214: entries++;
215: if (dp->d_reclen <= 0)
216: break;
217: cp += dp->d_reclen;
218: }
219: dirlist = emalloc(sizeof(struct dirent *) * (entries + 1));
220: for (entries = 0, cp = buf; cp < ebuf; ) {
221: dp = (struct dirent *)cp;
222: if (dp->d_fileno != 0 && dp->d_type != DT_WHT &&
1.22 millert 223: !excluded(dp->d_name)) {
224: dp->d_status = 0;
1.19 millert 225: dirlist[entries++] = dp;
1.22 millert 226: }
1.19 millert 227: if (dp->d_reclen <= 0)
228: break;
229: cp += dp->d_reclen;
230: }
231: dirlist[entries] = NULL;
1.1 deraadt 232:
1.19 millert 233: qsort(dirlist, entries, sizeof(struct dir *), dircompare);
1.1 deraadt 234:
1.19 millert 235: *bufp = buf;
236: return (dirlist);
1.1 deraadt 237: }
238:
1.19 millert 239: /*
240: * Compare d_name in two dirent structures; for qsort(3).
241: */
1.8 tedu 242: static int
1.19 millert 243: dircompare(const void *vp1, const void *vp2)
1.1 deraadt 244: {
1.19 millert 245: struct dirent *dp1 = *((struct dirent **) vp1);
246: struct dirent *dp2 = *((struct dirent **) vp2);
1.8 tedu 247:
1.19 millert 248: return (strcmp(dp1->d_name, dp2->d_name));
1.1 deraadt 249: }
250:
1.19 millert 251: /*
252: * Do the actual diff by calling either diffreg() or diffdir().
253: */
1.3 tedu 254: static void
1.19 millert 255: diffit(struct dirent *dp, char *path1, size_t plen1, char *path2, size_t plen2)
1.1 deraadt 256: {
1.19 millert 257: int flags = D_HEADER;
258:
259: strlcpy(path1 + plen1, dp->d_name, MAXPATHLEN - plen1);
260: if (stat(path1, &stb1) != 0) {
1.20 millert 261: if (!(Nflag || Pflag) || errno != ENOENT) {
1.19 millert 262: warn("%s", path1);
263: return;
1.1 deraadt 264: }
1.19 millert 265: flags |= D_EMPTY1;
266: memset(&stb1, 0, sizeof(stb1));
1.1 deraadt 267: }
268:
1.19 millert 269: strlcpy(path2 + plen2, dp->d_name, MAXPATHLEN - plen2);
270: if (stat(path2, &stb2) != 0) {
271: if (!Nflag || errno != ENOENT) {
272: warn("%s", path2);
273: return;
274: }
275: flags |= D_EMPTY2;
276: memset(&stb2, 0, sizeof(stb2));
277: stb2.st_mode = stb1.st_mode;
278: }
279: if (stb1.st_mode == 0)
280: stb1.st_mode = stb2.st_mode;
281:
282: if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
1.20 millert 283: if (rflag)
284: diffdir(path1, path2);
1.22 millert 285: else if (lflag)
286: dp->d_status |= D_COMMON;
1.20 millert 287: else if (format != D_EDIT)
288: /* XXX GNU diff always prints this for dirs XXX */
1.19 millert 289: printf("Common subdirectories: %s and %s\n",
290: path1, path2);
291: return;
1.1 deraadt 292: }
1.22 millert 293: dp->d_status = diffreg(path1, path2, flags);
1.23 ! millert 294: if (!lflag)
! 295: print_status(dp->d_status, path1, path2, NULL);
1.1 deraadt 296: }
297:
1.19 millert 298: /*
299: * Exclude the given directory entry?
300: */
301: static int
302: excluded(const char *entry)
1.1 deraadt 303: {
1.19 millert 304: struct excludes *excl;
1.15 tedu 305:
1.19 millert 306: /* always skip "." and ".." */
307: if (entry[0] == '.' &&
308: (entry[1] == '\0' || (entry[1] == '.' && entry[2] == '\0')))
1.15 tedu 309: return (1);
1.1 deraadt 310:
1.19 millert 311: /* check excludes list */
312: for (excl = excludes_list; excl != NULL; excl = excl->next)
313: if (fnmatch(excl->pattern, entry, FNM_PATHNAME) == 0)
314: return (1);
1.1 deraadt 315:
316: return (0);
317: }