1*bf198cc6Smillert /* $OpenBSD: diffdir.c,v 1.47 2019/01/25 00:19:26 millert Exp $ */
2d0c3f575Sderaadt
3d0c3f575Sderaadt /*
4*bf198cc6Smillert * Copyright (c) 2003, 2010 Todd C. Miller <millert@openbsd.org>
5d0c3f575Sderaadt *
64ec4b3d5Smillert * Permission to use, copy, modify, and distribute this software for any
74ec4b3d5Smillert * purpose with or without fee is hereby granted, provided that the above
84ec4b3d5Smillert * copyright notice and this permission notice appear in all copies.
9d0c3f575Sderaadt *
104ec4b3d5Smillert * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
114ec4b3d5Smillert * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
124ec4b3d5Smillert * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
134ec4b3d5Smillert * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
144ec4b3d5Smillert * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
154ec4b3d5Smillert * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
164ec4b3d5Smillert * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
174ec4b3d5Smillert *
184ec4b3d5Smillert * Sponsored in part by the Defense Advanced Research Projects
194ec4b3d5Smillert * Agency (DARPA) and Air Force Research Laboratory, Air Force
204ec4b3d5Smillert * Materiel Command, USAF, under agreement number F39502-99-1-0512.
21d0c3f575Sderaadt */
22d0c3f575Sderaadt
234ec4b3d5Smillert #include <sys/stat.h>
2426da422aStedu
2549dffe13Smillert #include <dirent.h>
264ec4b3d5Smillert #include <err.h>
2749dffe13Smillert #include <errno.h>
2849dffe13Smillert #include <fcntl.h>
294ec4b3d5Smillert #include <fnmatch.h>
304ec4b3d5Smillert #include <paths.h>
315004e76eStedu #include <stdio.h>
3226da422aStedu #include <stdlib.h>
3326da422aStedu #include <string.h>
3449dffe13Smillert #include <unistd.h>
35b9fc9a72Sderaadt #include <limits.h>
36ae8d569bSderaadt
37ae8d569bSderaadt #include "diff.h"
384a034c3aSray #include "xmalloc.h"
3926da422aStedu
40a737da78Sguenther static int selectfile(const struct dirent *);
413f8e756bSray static void diffit(struct dirent *, char *, size_t, char *, size_t, int);
4226da422aStedu
43b4bca33fSmillert #define d_status d_type /* we need to store status for -l */
44b4bca33fSmillert
45ae8d569bSderaadt /*
468fa21293Sotto * Diff directory traversal. Will be called recursively if -r was specified.
47ae8d569bSderaadt */
4826da422aStedu void
diffdir(char * p1,char * p2,int flags)493f8e756bSray diffdir(char *p1, char *p2, int flags)
50ae8d569bSderaadt {
512aac1c96Smillert struct dirent *dent1, **dp1, **edp1, **dirp1 = NULL;
522aac1c96Smillert struct dirent *dent2, **dp2, **edp2, **dirp2 = NULL;
534ec4b3d5Smillert size_t dirlen1, dirlen2;
54b9fc9a72Sderaadt char path1[PATH_MAX], path2[PATH_MAX];
554ec4b3d5Smillert int pos;
56ae8d569bSderaadt
574ec4b3d5Smillert dirlen1 = strlcpy(path1, *p1 ? p1 : ".", sizeof(path1));
584ec4b3d5Smillert if (dirlen1 >= sizeof(path1) - 1) {
595ad04d35Sguenther warnc(ENAMETOOLONG, "%s", p1);
60409b9c36Smillert status |= 2;
614ec4b3d5Smillert return;
62ae8d569bSderaadt }
634ec4b3d5Smillert if (path1[dirlen1 - 1] != '/') {
644ec4b3d5Smillert path1[dirlen1++] = '/';
654ec4b3d5Smillert path1[dirlen1] = '\0';
66ae8d569bSderaadt }
674ec4b3d5Smillert dirlen2 = strlcpy(path2, *p2 ? p2 : ".", sizeof(path2));
684ec4b3d5Smillert if (dirlen2 >= sizeof(path2) - 1) {
695ad04d35Sguenther warnc(ENAMETOOLONG, "%s", p2);
70409b9c36Smillert status |= 2;
714ec4b3d5Smillert return;
72ae8d569bSderaadt }
734ec4b3d5Smillert if (path2[dirlen2 - 1] != '/') {
744ec4b3d5Smillert path2[dirlen2++] = '/';
754ec4b3d5Smillert path2[dirlen2] = '\0';
764ec4b3d5Smillert }
774ec4b3d5Smillert
782aac1c96Smillert /*
792aac1c96Smillert * Get a list of entries in each directory, skipping "excluded" files
802aac1c96Smillert * and sorting alphabetically.
812aac1c96Smillert */
822aac1c96Smillert pos = scandir(path1, &dirp1, selectfile, alphasort);
832aac1c96Smillert if (pos == -1) {
842aac1c96Smillert if (errno == ENOENT && (Nflag || Pflag)) {
852aac1c96Smillert pos = 0;
862aac1c96Smillert } else {
872aac1c96Smillert warn("%s", path1);
8889a45341Sray goto closem;
892aac1c96Smillert }
902aac1c96Smillert }
912aac1c96Smillert dp1 = dirp1;
922aac1c96Smillert edp1 = dirp1 + pos;
932aac1c96Smillert
942aac1c96Smillert pos = scandir(path2, &dirp2, selectfile, alphasort);
952aac1c96Smillert if (pos == -1) {
962aac1c96Smillert if (errno == ENOENT && Nflag) {
972aac1c96Smillert pos = 0;
982aac1c96Smillert } else {
992aac1c96Smillert warn("%s", path2);
1002aac1c96Smillert goto closem;
1012aac1c96Smillert }
1022aac1c96Smillert }
1032aac1c96Smillert dp2 = dirp2;
1042aac1c96Smillert edp2 = dirp2 + pos;
1054ec4b3d5Smillert
1064ec4b3d5Smillert /*
1074ec4b3d5Smillert * If we were given a starting point, find it.
1084ec4b3d5Smillert */
1094ec4b3d5Smillert if (start != NULL) {
1102aac1c96Smillert while (dp1 != edp1 && strcmp((*dp1)->d_name, start) < 0)
1114ec4b3d5Smillert dp1++;
1122aac1c96Smillert while (dp2 != edp2 && strcmp((*dp2)->d_name, start) < 0)
1134ec4b3d5Smillert dp2++;
1144ec4b3d5Smillert }
1154ec4b3d5Smillert
1164ec4b3d5Smillert /*
1174ec4b3d5Smillert * Iterate through the two directory lists, diffing as we go.
1184ec4b3d5Smillert */
1192aac1c96Smillert while (dp1 != edp1 || dp2 != edp2) {
1202aac1c96Smillert dent1 = dp1 != edp1 ? *dp1 : NULL;
1212aac1c96Smillert dent2 = dp2 != edp2 ? *dp2 : NULL;
1224ec4b3d5Smillert
1234ec4b3d5Smillert pos = dent1 == NULL ? 1 : dent2 == NULL ? -1 :
1244ec4b3d5Smillert strcmp(dent1->d_name, dent2->d_name);
1254ec4b3d5Smillert if (pos == 0) {
1264ec4b3d5Smillert /* file exists in both dirs, diff it */
1273f8e756bSray diffit(dent1, path1, dirlen1, path2, dirlen2, flags);
1284ec4b3d5Smillert dp1++;
1294ec4b3d5Smillert dp2++;
1304ec4b3d5Smillert } else if (pos < 0) {
1314ec4b3d5Smillert /* file only in first dir, only diff if -N */
132409b9c36Smillert if (Nflag) {
1333f8e756bSray diffit(dent1, path1, dirlen1, path2, dirlen2,
1343f8e756bSray flags);
135409b9c36Smillert } else {
1364893e147Smillert print_only(path1, dirlen1, dent1->d_name);
137409b9c36Smillert status |= 1;
138409b9c36Smillert }
1394ec4b3d5Smillert dp1++;
140ae8d569bSderaadt } else {
141aeb82612Smillert /* file only in second dir, only diff if -N or -P */
142409b9c36Smillert if (Nflag || Pflag) {
1433f8e756bSray diffit(dent2, path1, dirlen1, path2, dirlen2,
1443f8e756bSray flags);
145409b9c36Smillert } else {
1464893e147Smillert print_only(path2, dirlen2, dent2->d_name);
147409b9c36Smillert status |= 1;
148409b9c36Smillert }
1494ec4b3d5Smillert dp2++;
150ae8d569bSderaadt }
151ae8d569bSderaadt }
152ae8d569bSderaadt
15389a45341Sray closem:
1542023c35eSmillert if (dirp1 != NULL) {
1552aac1c96Smillert for (dp1 = dirp1; dp1 < edp1; dp1++)
156a79a2617Stedu free(*dp1);
157a79a2617Stedu free(dirp1);
1584ec4b3d5Smillert }
1592023c35eSmillert if (dirp2 != NULL) {
1602aac1c96Smillert for (dp2 = dirp2; dp2 < edp2; dp2++)
161a79a2617Stedu free(*dp2);
162a79a2617Stedu free(dirp2);
1634ec4b3d5Smillert }
1644ec4b3d5Smillert }
1654ec4b3d5Smillert
1664ec4b3d5Smillert /*
1674ec4b3d5Smillert * Do the actual diff by calling either diffreg() or diffdir().
1684ec4b3d5Smillert */
16926da422aStedu static void
diffit(struct dirent * dp,char * path1,size_t plen1,char * path2,size_t plen2,int flags)1703f8e756bSray diffit(struct dirent *dp, char *path1, size_t plen1, char *path2, size_t plen2,
1713f8e756bSray int flags)
172ae8d569bSderaadt {
1733f8e756bSray flags |= D_HEADER;
174b9fc9a72Sderaadt strlcpy(path1 + plen1, dp->d_name, PATH_MAX - plen1);
1754ec4b3d5Smillert if (stat(path1, &stb1) != 0) {
176aeb82612Smillert if (!(Nflag || Pflag) || errno != ENOENT) {
1774ec4b3d5Smillert warn("%s", path1);
178ae8d569bSderaadt return;
179ae8d569bSderaadt }
1804ec4b3d5Smillert flags |= D_EMPTY1;
1814ec4b3d5Smillert memset(&stb1, 0, sizeof(stb1));
1824ec4b3d5Smillert }
1834ec4b3d5Smillert
184b9fc9a72Sderaadt strlcpy(path2 + plen2, dp->d_name, PATH_MAX - plen2);
1854ec4b3d5Smillert if (stat(path2, &stb2) != 0) {
1864ec4b3d5Smillert if (!Nflag || errno != ENOENT) {
1874ec4b3d5Smillert warn("%s", path2);
188ae8d569bSderaadt return;
189ae8d569bSderaadt }
1904ec4b3d5Smillert flags |= D_EMPTY2;
1914ec4b3d5Smillert memset(&stb2, 0, sizeof(stb2));
1924ec4b3d5Smillert stb2.st_mode = stb1.st_mode;
1934ec4b3d5Smillert }
1944ec4b3d5Smillert if (stb1.st_mode == 0)
1954ec4b3d5Smillert stb1.st_mode = stb2.st_mode;
1964ec4b3d5Smillert
1974ec4b3d5Smillert if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
1984ec4b3d5Smillert if (rflag)
1993f8e756bSray diffdir(path1, path2, flags);
200016bcb7bSmillert else
201aeb82612Smillert printf("Common subdirectories: %s and %s\n",
202aeb82612Smillert path1, path2);
203ae8d569bSderaadt return;
204ae8d569bSderaadt }
2055f4c3fa8Smillert if (!S_ISREG(stb1.st_mode) && !S_ISDIR(stb1.st_mode))
2065f4c3fa8Smillert dp->d_status = D_SKIPPED1;
2075f4c3fa8Smillert else if (!S_ISREG(stb2.st_mode) && !S_ISDIR(stb2.st_mode))
2085f4c3fa8Smillert dp->d_status = D_SKIPPED2;
2095f4c3fa8Smillert else
210b4bca33fSmillert dp->d_status = diffreg(path1, path2, flags);
211d2ea36f5Sray print_status(dp->d_status, path1, path2, "");
212ae8d569bSderaadt }
213ae8d569bSderaadt
214ae8d569bSderaadt /*
2152023c35eSmillert * Returns 1 if the directory entry should be included in the
2162023c35eSmillert * diff, else 0. Checks the excludes list.
217ae8d569bSderaadt */
2184ec4b3d5Smillert static int
selectfile(const struct dirent * dp)219a737da78Sguenther selectfile(const struct dirent *dp)
220ae8d569bSderaadt {
2214ec4b3d5Smillert struct excludes *excl;
2224ec4b3d5Smillert
2232023c35eSmillert if (dp->d_fileno == 0)
2242023c35eSmillert return (0);
2252023c35eSmillert
2264ec4b3d5Smillert /* always skip "." and ".." */
2272023c35eSmillert if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
2282023c35eSmillert (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
2292023c35eSmillert return (0);
2304ec4b3d5Smillert
2314ec4b3d5Smillert /* check excludes list */
2324ec4b3d5Smillert for (excl = excludes_list; excl != NULL; excl = excl->next)
2332023c35eSmillert if (fnmatch(excl->pattern, dp->d_name, FNM_PATHNAME) == 0)
234ae8d569bSderaadt return (0);
2352023c35eSmillert
2362023c35eSmillert return (1);
237ae8d569bSderaadt }
238