1*409b9c36Smillert /* $OpenBSD: diffdir.c,v 1.46 2017/08/28 15:33:27 millert Exp $ */ 2d0c3f575Sderaadt 3d0c3f575Sderaadt /* 42023c35eSmillert * Copyright (c) 2003, 2010 Todd C. Miller <Todd.Miller@courtesan.com> 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 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); 60*409b9c36Smillert 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); 70*409b9c36Smillert 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 */ 132*409b9c36Smillert if (Nflag) { 1333f8e756bSray diffit(dent1, path1, dirlen1, path2, dirlen2, 1343f8e756bSray flags); 135*409b9c36Smillert } else { 1364893e147Smillert print_only(path1, dirlen1, dent1->d_name); 137*409b9c36Smillert status |= 1; 138*409b9c36Smillert } 1394ec4b3d5Smillert dp1++; 140ae8d569bSderaadt } else { 141aeb82612Smillert /* file only in second dir, only diff if -N or -P */ 142*409b9c36Smillert if (Nflag || Pflag) { 1433f8e756bSray diffit(dent2, path1, dirlen1, path2, dirlen2, 1443f8e756bSray flags); 145*409b9c36Smillert } else { 1464893e147Smillert print_only(path2, dirlen2, dent2->d_name); 147*409b9c36Smillert status |= 1; 148*409b9c36Smillert } 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 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 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