17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5cdf0c1d5Smjnelson * Common Development and Distribution License (the "License"). 6cdf0c1d5Smjnelson * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 209730304dSmeem * 219730304dSmeem * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 22cdf0c1d5Smjnelson * Use is subject to license terms. 23cdf0c1d5Smjnelson */ 247c478bd9Sstevel@tonic-gate 257c478bd9Sstevel@tonic-gate /* 267c478bd9Sstevel@tonic-gate * Finds all unreferenced files in a source tree that do not match a list of 277c478bd9Sstevel@tonic-gate * permitted pathnames. 287c478bd9Sstevel@tonic-gate */ 297c478bd9Sstevel@tonic-gate 307c478bd9Sstevel@tonic-gate #include <ctype.h> 317c478bd9Sstevel@tonic-gate #include <errno.h> 327c478bd9Sstevel@tonic-gate #include <fnmatch.h> 337c478bd9Sstevel@tonic-gate #include <ftw.h> 347c478bd9Sstevel@tonic-gate #include <stdarg.h> 357c478bd9Sstevel@tonic-gate #include <stdio.h> 367c478bd9Sstevel@tonic-gate #include <stdlib.h> 377c478bd9Sstevel@tonic-gate #include <string.h> 387c478bd9Sstevel@tonic-gate #include <time.h> 397c478bd9Sstevel@tonic-gate #include <unistd.h> 407c478bd9Sstevel@tonic-gate #include <sys/param.h> 417c478bd9Sstevel@tonic-gate #include <sys/stat.h> 427c478bd9Sstevel@tonic-gate #include <sys/types.h> 437c478bd9Sstevel@tonic-gate 447c478bd9Sstevel@tonic-gate /* 457c478bd9Sstevel@tonic-gate * Pathname set: a simple datatype for storing pathname pattern globs and 467c478bd9Sstevel@tonic-gate * for checking whether a given pathname is matched by a pattern glob in 477c478bd9Sstevel@tonic-gate * the set. 487c478bd9Sstevel@tonic-gate */ 497c478bd9Sstevel@tonic-gate typedef struct { 507c478bd9Sstevel@tonic-gate char **paths; 517c478bd9Sstevel@tonic-gate unsigned int npath; 527c478bd9Sstevel@tonic-gate unsigned int maxpaths; 537c478bd9Sstevel@tonic-gate } pnset_t; 547c478bd9Sstevel@tonic-gate 559730304dSmeem /* 56*b9987376SRichard Lowe * Data associated with the current SCM manifest. 579730304dSmeem */ 58*b9987376SRichard Lowe typedef struct scmdata { 599730304dSmeem pnset_t *manifest; 60*b9987376SRichard Lowe char metapath[MAXPATHLEN]; 619730304dSmeem char root[MAXPATHLEN]; 629730304dSmeem unsigned int rootlen; 639730304dSmeem boolean_t rootwarn; 64*b9987376SRichard Lowe } scmdata_t; 659730304dSmeem 669730304dSmeem /* 679730304dSmeem * Hooks used to check if a given unreferenced file is known to an SCM 68*b9987376SRichard Lowe * (currently Git, Mercurial and TeamWare). 699730304dSmeem */ 709730304dSmeem typedef int checkscm_func_t(const char *, const struct FTW *); 719730304dSmeem typedef void chdirscm_func_t(const char *); 729730304dSmeem 739730304dSmeem typedef struct { 749730304dSmeem const char *name; 759730304dSmeem checkscm_func_t *checkfunc; 769730304dSmeem chdirscm_func_t *chdirfunc; 779730304dSmeem } scm_t; 789730304dSmeem 79*b9987376SRichard Lowe static checkscm_func_t check_tw, check_scmdata; 801b1e41ddSRichard Lowe static chdirscm_func_t chdir_hg, chdir_git; 817c478bd9Sstevel@tonic-gate static int pnset_add(pnset_t *, const char *); 827c478bd9Sstevel@tonic-gate static int pnset_check(const pnset_t *, const char *); 837c478bd9Sstevel@tonic-gate static void pnset_empty(pnset_t *); 849730304dSmeem static void pnset_free(pnset_t *); 857c478bd9Sstevel@tonic-gate static int checkpath(const char *, const struct stat *, int, struct FTW *); 867c478bd9Sstevel@tonic-gate static pnset_t *make_exset(const char *); 877c478bd9Sstevel@tonic-gate static void warn(const char *, ...); 887c478bd9Sstevel@tonic-gate static void die(const char *, ...); 897c478bd9Sstevel@tonic-gate 909730304dSmeem static const scm_t scms[] = { 919730304dSmeem { "tw", check_tw, NULL }, 929730304dSmeem { "teamware", check_tw, NULL }, 93*b9987376SRichard Lowe { "hg", check_scmdata, chdir_hg }, 94*b9987376SRichard Lowe { "mercurial", check_scmdata, chdir_hg }, 95*b9987376SRichard Lowe { "git", check_scmdata, chdir_git }, 969730304dSmeem { NULL, NULL, NULL } 979730304dSmeem }; 989730304dSmeem 999730304dSmeem static const scm_t *scm; 100*b9987376SRichard Lowe static scmdata_t scmdata; 1017c478bd9Sstevel@tonic-gate static time_t tstamp; /* timestamp to compare files to */ 1027c478bd9Sstevel@tonic-gate static pnset_t *exsetp; /* pathname globs to ignore */ 1037c478bd9Sstevel@tonic-gate static const char *progname; 1047c478bd9Sstevel@tonic-gate 1057c478bd9Sstevel@tonic-gate int 1067c478bd9Sstevel@tonic-gate main(int argc, char *argv[]) 1077c478bd9Sstevel@tonic-gate { 1087c478bd9Sstevel@tonic-gate int c; 1097c478bd9Sstevel@tonic-gate char path[MAXPATHLEN]; 1107c478bd9Sstevel@tonic-gate char subtree[MAXPATHLEN] = "./"; 1117c478bd9Sstevel@tonic-gate char *tstampfile = ".build.tstamp"; 1127c478bd9Sstevel@tonic-gate struct stat tsstat; 1137c478bd9Sstevel@tonic-gate 1147c478bd9Sstevel@tonic-gate progname = strrchr(argv[0], '/'); 1157c478bd9Sstevel@tonic-gate if (progname == NULL) 1167c478bd9Sstevel@tonic-gate progname = argv[0]; 1177c478bd9Sstevel@tonic-gate else 1187c478bd9Sstevel@tonic-gate progname++; 1197c478bd9Sstevel@tonic-gate 1209730304dSmeem while ((c = getopt(argc, argv, "as:t:S:")) != EOF) { 1217c478bd9Sstevel@tonic-gate switch (c) { 1227c478bd9Sstevel@tonic-gate case 'a': 1239730304dSmeem /* for compatibility; now the default */ 1247c478bd9Sstevel@tonic-gate break; 1257c478bd9Sstevel@tonic-gate 1267c478bd9Sstevel@tonic-gate case 's': 1277c478bd9Sstevel@tonic-gate (void) strlcat(subtree, optarg, MAXPATHLEN); 1287c478bd9Sstevel@tonic-gate break; 1297c478bd9Sstevel@tonic-gate 1307c478bd9Sstevel@tonic-gate case 't': 1317c478bd9Sstevel@tonic-gate tstampfile = optarg; 1327c478bd9Sstevel@tonic-gate break; 1337c478bd9Sstevel@tonic-gate 1349730304dSmeem case 'S': 1359730304dSmeem for (scm = scms; scm->name != NULL; scm++) { 1369730304dSmeem if (strcmp(scm->name, optarg) == 0) 1379730304dSmeem break; 1389730304dSmeem } 1399730304dSmeem if (scm->name == NULL) 1409730304dSmeem die("unsupported SCM `%s'\n", optarg); 1419730304dSmeem break; 1429730304dSmeem 1437c478bd9Sstevel@tonic-gate default: 1447c478bd9Sstevel@tonic-gate case '?': 1457c478bd9Sstevel@tonic-gate goto usage; 1467c478bd9Sstevel@tonic-gate } 1477c478bd9Sstevel@tonic-gate } 1487c478bd9Sstevel@tonic-gate 1497c478bd9Sstevel@tonic-gate argc -= optind; 1507c478bd9Sstevel@tonic-gate argv += optind; 1517c478bd9Sstevel@tonic-gate 1527c478bd9Sstevel@tonic-gate if (argc != 2) { 1539730304dSmeem usage: (void) fprintf(stderr, "usage: %s [-s <subtree>] " 1541b1e41ddSRichard Lowe "[-t <tstampfile>] [-S hg|tw|git] <srcroot> <exceptfile>\n", 1559730304dSmeem progname); 1567c478bd9Sstevel@tonic-gate return (EXIT_FAILURE); 1577c478bd9Sstevel@tonic-gate } 1587c478bd9Sstevel@tonic-gate 1597c478bd9Sstevel@tonic-gate /* 1607c478bd9Sstevel@tonic-gate * Interpret a relative timestamp path as relative to srcroot. 1617c478bd9Sstevel@tonic-gate */ 1627c478bd9Sstevel@tonic-gate if (tstampfile[0] == '/') 1637c478bd9Sstevel@tonic-gate (void) strlcpy(path, tstampfile, MAXPATHLEN); 1647c478bd9Sstevel@tonic-gate else 1657c478bd9Sstevel@tonic-gate (void) snprintf(path, MAXPATHLEN, "%s/%s", argv[0], tstampfile); 1667c478bd9Sstevel@tonic-gate 1677c478bd9Sstevel@tonic-gate if (stat(path, &tsstat) == -1) 1687c478bd9Sstevel@tonic-gate die("cannot stat timestamp file \"%s\"", path); 1697c478bd9Sstevel@tonic-gate tstamp = tsstat.st_mtime; 1707c478bd9Sstevel@tonic-gate 1717c478bd9Sstevel@tonic-gate /* 1727c478bd9Sstevel@tonic-gate * Create the exception pathname set. 1737c478bd9Sstevel@tonic-gate */ 1747c478bd9Sstevel@tonic-gate exsetp = make_exset(argv[1]); 1757c478bd9Sstevel@tonic-gate if (exsetp == NULL) 1767c478bd9Sstevel@tonic-gate die("cannot make exception pathname set\n"); 1777c478bd9Sstevel@tonic-gate 1787c478bd9Sstevel@tonic-gate /* 1797c478bd9Sstevel@tonic-gate * Walk the specified subtree of the tree rooted at argv[0]. 1807c478bd9Sstevel@tonic-gate */ 1819730304dSmeem if (chdir(argv[0]) == -1) 1829730304dSmeem die("cannot change directory to \"%s\"", argv[0]); 1839730304dSmeem 1847c478bd9Sstevel@tonic-gate if (nftw(subtree, checkpath, 100, FTW_PHYS) != 0) 1857c478bd9Sstevel@tonic-gate die("cannot walk tree rooted at \"%s\"\n", argv[0]); 1867c478bd9Sstevel@tonic-gate 1877c478bd9Sstevel@tonic-gate pnset_empty(exsetp); 1887c478bd9Sstevel@tonic-gate return (EXIT_SUCCESS); 1897c478bd9Sstevel@tonic-gate } 1907c478bd9Sstevel@tonic-gate 1917c478bd9Sstevel@tonic-gate /* 1929730304dSmeem * Load and return a pnset for the manifest for the Mercurial repo at `hgroot'. 1939730304dSmeem */ 1949730304dSmeem static pnset_t * 195*b9987376SRichard Lowe hg_manifest(const char *hgroot) 1969730304dSmeem { 1979730304dSmeem FILE *fp = NULL; 1989730304dSmeem char *hgcmd = NULL; 1999730304dSmeem char *newline; 2009730304dSmeem pnset_t *pnsetp; 2019730304dSmeem char path[MAXPATHLEN]; 2029730304dSmeem 2039730304dSmeem pnsetp = calloc(sizeof (pnset_t), 1); 2049730304dSmeem if (pnsetp == NULL || 2051b1e41ddSRichard Lowe asprintf(&hgcmd, "hg manifest -R %s", hgroot) == -1) 2069730304dSmeem goto fail; 2079730304dSmeem 2089730304dSmeem fp = popen(hgcmd, "r"); 2099730304dSmeem if (fp == NULL) 2109730304dSmeem goto fail; 2119730304dSmeem 2129730304dSmeem while (fgets(path, sizeof (path), fp) != NULL) { 2139730304dSmeem newline = strrchr(path, '\n'); 2149730304dSmeem if (newline != NULL) 2159730304dSmeem *newline = '\0'; 2169730304dSmeem 2179730304dSmeem if (pnset_add(pnsetp, path) == 0) 2189730304dSmeem goto fail; 2199730304dSmeem } 2209730304dSmeem 2219730304dSmeem (void) pclose(fp); 2229730304dSmeem free(hgcmd); 2239730304dSmeem return (pnsetp); 2249730304dSmeem fail: 2259730304dSmeem warn("cannot load hg manifest at %s", hgroot); 2269730304dSmeem if (fp != NULL) 2279730304dSmeem (void) pclose(fp); 2289730304dSmeem free(hgcmd); 2299730304dSmeem pnset_free(pnsetp); 2309730304dSmeem return (NULL); 2319730304dSmeem } 2329730304dSmeem 233*b9987376SRichard Lowe /* 234*b9987376SRichard Lowe * Load and return a pnset for the manifest for the Git repo at `gitroot'. 235*b9987376SRichard Lowe */ 236*b9987376SRichard Lowe static pnset_t * 237*b9987376SRichard Lowe git_manifest(const char *gitroot) 2381b1e41ddSRichard Lowe { 2391b1e41ddSRichard Lowe FILE *fp = NULL; 2401b1e41ddSRichard Lowe char *gitcmd = NULL; 2411b1e41ddSRichard Lowe char *newline; 2421b1e41ddSRichard Lowe pnset_t *pnsetp; 243*b9987376SRichard Lowe char path[MAXPATHLEN]; 2441b1e41ddSRichard Lowe 2451b1e41ddSRichard Lowe pnsetp = calloc(sizeof (pnset_t), 1); 246*b9987376SRichard Lowe if (pnsetp == NULL || 247*b9987376SRichard Lowe asprintf(&gitcmd, "git --git-dir=%s/.git ls-files", gitroot) == -1) 2481b1e41ddSRichard Lowe goto fail; 2491b1e41ddSRichard Lowe 250*b9987376SRichard Lowe fp = popen(gitcmd, "r"); 251*b9987376SRichard Lowe if (fp == NULL) 2521b1e41ddSRichard Lowe goto fail; 2531b1e41ddSRichard Lowe 254*b9987376SRichard Lowe while (fgets(path, sizeof (path), fp) != NULL) { 255*b9987376SRichard Lowe newline = strrchr(path, '\n'); 256*b9987376SRichard Lowe if (newline != NULL) 2571b1e41ddSRichard Lowe *newline = '\0'; 2581b1e41ddSRichard Lowe 259*b9987376SRichard Lowe if (pnset_add(pnsetp, path) == 0) 2601b1e41ddSRichard Lowe goto fail; 2611b1e41ddSRichard Lowe } 2621b1e41ddSRichard Lowe 2631b1e41ddSRichard Lowe (void) pclose(fp); 2641b1e41ddSRichard Lowe free(gitcmd); 265*b9987376SRichard Lowe return (pnsetp); 2661b1e41ddSRichard Lowe fail: 267*b9987376SRichard Lowe warn("cannot load git manifest at %s", gitroot); 2681b1e41ddSRichard Lowe if (fp != NULL) 2691b1e41ddSRichard Lowe (void) pclose(fp); 2701b1e41ddSRichard Lowe free(gitcmd); 271*b9987376SRichard Lowe pnset_free(pnsetp); 272*b9987376SRichard Lowe return (NULL); 2731b1e41ddSRichard Lowe } 2741b1e41ddSRichard Lowe 2759730304dSmeem /* 2769730304dSmeem * If necessary, change our active manifest to be appropriate for `path'. 2779730304dSmeem */ 2789730304dSmeem static void 279*b9987376SRichard Lowe chdir_scmdata(const char *path, const char *meta, 280*b9987376SRichard Lowe pnset_t *(*manifest_func)(const char *path)) 2819730304dSmeem { 282*b9987376SRichard Lowe char scmpath[MAXPATHLEN]; 2839730304dSmeem char basepath[MAXPATHLEN]; 2849730304dSmeem char *slash; 2859730304dSmeem 286*b9987376SRichard Lowe (void) snprintf(scmpath, MAXPATHLEN, "%s/%s", path, meta); 2879730304dSmeem 2889730304dSmeem /* 2899730304dSmeem * Change our active manifest if any one of the following is true: 2909730304dSmeem * 291*b9987376SRichard Lowe * 1. No manifest is loaded. Find the nearest SCM root to load from. 2929730304dSmeem * 2939730304dSmeem * 2. A manifest is loaded, but we've moved into a directory with 294*b9987376SRichard Lowe * its own metadata directory (e.g., usr/closed). Load from its 295*b9987376SRichard Lowe * root. 2969730304dSmeem * 2979730304dSmeem * 3. A manifest is loaded, but no longer applies (e.g., the manifest 2989730304dSmeem * under usr/closed is loaded, but we've moved to usr/src). 2999730304dSmeem */ 300*b9987376SRichard Lowe if (scmdata.manifest == NULL || 301*b9987376SRichard Lowe (strcmp(scmpath, scmdata.metapath) != 0 && 302*b9987376SRichard Lowe access(scmpath, X_OK) == 0) || 303*b9987376SRichard Lowe strncmp(path, scmdata.root, scmdata.rootlen - 1) != 0) { 304*b9987376SRichard Lowe pnset_free(scmdata.manifest); 305*b9987376SRichard Lowe scmdata.manifest = NULL; 3069730304dSmeem 3079730304dSmeem (void) strlcpy(basepath, path, MAXPATHLEN); 3089730304dSmeem 3099730304dSmeem /* 310*b9987376SRichard Lowe * Walk up the directory tree looking for metadata 311*b9987376SRichard Lowe * subdirectories. 3129730304dSmeem */ 313*b9987376SRichard Lowe while (access(scmpath, X_OK) == -1) { 3149730304dSmeem slash = strrchr(basepath, '/'); 3159730304dSmeem if (slash == NULL) { 316*b9987376SRichard Lowe if (!scmdata.rootwarn) { 317*b9987376SRichard Lowe warn("no metadata directory " 318*b9987376SRichard Lowe "for \"%s\"\n", path); 319*b9987376SRichard Lowe scmdata.rootwarn = B_TRUE; 3209730304dSmeem } 3219730304dSmeem return; 3229730304dSmeem } 3239730304dSmeem *slash = '\0'; 324*b9987376SRichard Lowe (void) snprintf(scmpath, MAXPATHLEN, "%s/%s", basepath, 325*b9987376SRichard Lowe meta); 3269730304dSmeem } 3279730304dSmeem 3289730304dSmeem /* 329*b9987376SRichard Lowe * We found a directory with an SCM metadata directory; record 330*b9987376SRichard Lowe * it and load its manifest. 3319730304dSmeem */ 332*b9987376SRichard Lowe (void) strlcpy(scmdata.metapath, scmpath, MAXPATHLEN); 333*b9987376SRichard Lowe (void) strlcpy(scmdata.root, basepath, MAXPATHLEN); 334*b9987376SRichard Lowe scmdata.manifest = manifest_func(scmdata.root); 3359730304dSmeem 3369730304dSmeem /* 337*b9987376SRichard Lowe * The logic in check_scmdata() depends on scmdata.root having 338*b9987376SRichard Lowe * a single trailing slash, so only add it if it's missing. 3399730304dSmeem */ 340*b9987376SRichard Lowe if (scmdata.root[strlen(scmdata.root) - 1] != '/') 341*b9987376SRichard Lowe (void) strlcat(scmdata.root, "/", MAXPATHLEN); 342*b9987376SRichard Lowe scmdata.rootlen = strlen(scmdata.root); 3439730304dSmeem } 3449730304dSmeem } 3459730304dSmeem 3469730304dSmeem /* 347*b9987376SRichard Lowe * If necessary, change our active manifest to be appropriate for `path'. 3489730304dSmeem */ 349*b9987376SRichard Lowe static void 350*b9987376SRichard Lowe chdir_git(const char *path) 351*b9987376SRichard Lowe { 352*b9987376SRichard Lowe chdir_scmdata(path, ".git", git_manifest); 353*b9987376SRichard Lowe } 354*b9987376SRichard Lowe 355*b9987376SRichard Lowe static void 356*b9987376SRichard Lowe chdir_hg(const char *path) 357*b9987376SRichard Lowe { 358*b9987376SRichard Lowe chdir_scmdata(path, ".hg", hg_manifest); 359*b9987376SRichard Lowe } 360*b9987376SRichard Lowe 3619730304dSmeem /* ARGSUSED */ 3629730304dSmeem static int 363*b9987376SRichard Lowe check_scmdata(const char *path, const struct FTW *ftwp) 3649730304dSmeem { 3659730304dSmeem /* 3669730304dSmeem * The manifest paths are relative to the manifest root; skip past it. 3679730304dSmeem */ 368*b9987376SRichard Lowe path += scmdata.rootlen; 3699730304dSmeem 370*b9987376SRichard Lowe return (scmdata.manifest != NULL && pnset_check(scmdata.manifest, 371*b9987376SRichard Lowe path)); 3721b1e41ddSRichard Lowe } 3739730304dSmeem 3749730304dSmeem /* 3759730304dSmeem * Check if a file is under TeamWare control by checking for its corresponding 3769730304dSmeem * SCCS "s-dot" file. 3779730304dSmeem */ 3789730304dSmeem static int 3799730304dSmeem check_tw(const char *path, const struct FTW *ftwp) 3809730304dSmeem { 3819730304dSmeem char sccspath[MAXPATHLEN]; 3829730304dSmeem 3839730304dSmeem (void) snprintf(sccspath, MAXPATHLEN, "%.*s/SCCS/s.%s", ftwp->base, 3849730304dSmeem path, path + ftwp->base); 3859730304dSmeem 3869730304dSmeem return (access(sccspath, F_OK) == 0); 3879730304dSmeem } 3889730304dSmeem 3899730304dSmeem /* 3907c478bd9Sstevel@tonic-gate * Using `exceptfile' and a built-in list of exceptions, build and return a 3917c478bd9Sstevel@tonic-gate * pnset_t consisting of all of the pathnames globs which are allowed to be 3927c478bd9Sstevel@tonic-gate * unreferenced in the source tree. 3937c478bd9Sstevel@tonic-gate */ 3947c478bd9Sstevel@tonic-gate static pnset_t * 3957c478bd9Sstevel@tonic-gate make_exset(const char *exceptfile) 3967c478bd9Sstevel@tonic-gate { 3977c478bd9Sstevel@tonic-gate FILE *fp; 3987c478bd9Sstevel@tonic-gate char line[MAXPATHLEN]; 3997c478bd9Sstevel@tonic-gate char *newline; 4007c478bd9Sstevel@tonic-gate pnset_t *pnsetp; 4017c478bd9Sstevel@tonic-gate unsigned int i; 4027c478bd9Sstevel@tonic-gate 4037c478bd9Sstevel@tonic-gate pnsetp = calloc(sizeof (pnset_t), 1); 4047c478bd9Sstevel@tonic-gate if (pnsetp == NULL) 4057c478bd9Sstevel@tonic-gate return (NULL); 4067c478bd9Sstevel@tonic-gate 4077c478bd9Sstevel@tonic-gate /* 4087c478bd9Sstevel@tonic-gate * Add any exceptions from the file. 4097c478bd9Sstevel@tonic-gate */ 4107c478bd9Sstevel@tonic-gate fp = fopen(exceptfile, "r"); 4117c478bd9Sstevel@tonic-gate if (fp == NULL) { 4127c478bd9Sstevel@tonic-gate warn("cannot open exception file \"%s\"", exceptfile); 4137c478bd9Sstevel@tonic-gate goto fail; 4147c478bd9Sstevel@tonic-gate } 4157c478bd9Sstevel@tonic-gate 4167c478bd9Sstevel@tonic-gate while (fgets(line, sizeof (line), fp) != NULL) { 4177c478bd9Sstevel@tonic-gate newline = strrchr(line, '\n'); 4187c478bd9Sstevel@tonic-gate if (newline != NULL) 4197c478bd9Sstevel@tonic-gate *newline = '\0'; 4207c478bd9Sstevel@tonic-gate 4217c478bd9Sstevel@tonic-gate for (i = 0; isspace(line[i]); i++) 4227c478bd9Sstevel@tonic-gate ; 4237c478bd9Sstevel@tonic-gate 4247c478bd9Sstevel@tonic-gate if (line[i] == '#' || line[i] == '\0') 4257c478bd9Sstevel@tonic-gate continue; 4267c478bd9Sstevel@tonic-gate 4277c478bd9Sstevel@tonic-gate if (pnset_add(pnsetp, line) == 0) { 4287c478bd9Sstevel@tonic-gate (void) fclose(fp); 4297c478bd9Sstevel@tonic-gate goto fail; 4307c478bd9Sstevel@tonic-gate } 4317c478bd9Sstevel@tonic-gate } 4327c478bd9Sstevel@tonic-gate 4337c478bd9Sstevel@tonic-gate (void) fclose(fp); 4347c478bd9Sstevel@tonic-gate return (pnsetp); 4357c478bd9Sstevel@tonic-gate fail: 4369730304dSmeem pnset_free(pnsetp); 4377c478bd9Sstevel@tonic-gate return (NULL); 4387c478bd9Sstevel@tonic-gate } 4397c478bd9Sstevel@tonic-gate 4407c478bd9Sstevel@tonic-gate /* 4417c478bd9Sstevel@tonic-gate * FTW callback: print `path' if it's older than `tstamp' and not in `exsetp'. 4427c478bd9Sstevel@tonic-gate */ 4437c478bd9Sstevel@tonic-gate static int 4447c478bd9Sstevel@tonic-gate checkpath(const char *path, const struct stat *statp, int type, 4457c478bd9Sstevel@tonic-gate struct FTW *ftwp) 4467c478bd9Sstevel@tonic-gate { 4477c478bd9Sstevel@tonic-gate switch (type) { 4487c478bd9Sstevel@tonic-gate case FTW_F: 4497c478bd9Sstevel@tonic-gate /* 4507c478bd9Sstevel@tonic-gate * Skip if the file is referenced or in the exception list. 4517c478bd9Sstevel@tonic-gate */ 4527c478bd9Sstevel@tonic-gate if (statp->st_atime >= tstamp || pnset_check(exsetp, path)) 4537c478bd9Sstevel@tonic-gate return (0); 4547c478bd9Sstevel@tonic-gate 4557c478bd9Sstevel@tonic-gate /* 4569730304dSmeem * If requested, restrict ourselves to unreferenced files 4579730304dSmeem * under SCM control. 4587c478bd9Sstevel@tonic-gate */ 4599730304dSmeem if (scm == NULL || scm->checkfunc(path, ftwp)) 4607c478bd9Sstevel@tonic-gate (void) puts(path); 4617c478bd9Sstevel@tonic-gate return (0); 4627c478bd9Sstevel@tonic-gate 4637c478bd9Sstevel@tonic-gate case FTW_D: 4647c478bd9Sstevel@tonic-gate /* 4657c478bd9Sstevel@tonic-gate * Prune any directories in the exception list. 4667c478bd9Sstevel@tonic-gate */ 4679730304dSmeem if (pnset_check(exsetp, path)) { 4687c478bd9Sstevel@tonic-gate ftwp->quit = FTW_PRUNE; 4697c478bd9Sstevel@tonic-gate return (0); 4709730304dSmeem } 4719730304dSmeem 4729730304dSmeem /* 4739730304dSmeem * If necessary, advise the SCM logic of our new directory. 4749730304dSmeem */ 4759730304dSmeem if (scm != NULL && scm->chdirfunc != NULL) 4769730304dSmeem scm->chdirfunc(path); 4779730304dSmeem 4789730304dSmeem return (0); 4797c478bd9Sstevel@tonic-gate 4807c478bd9Sstevel@tonic-gate case FTW_DNR: 4817c478bd9Sstevel@tonic-gate warn("cannot read \"%s\"", path); 4827c478bd9Sstevel@tonic-gate return (0); 4837c478bd9Sstevel@tonic-gate 4847c478bd9Sstevel@tonic-gate case FTW_NS: 4857c478bd9Sstevel@tonic-gate warn("cannot stat \"%s\"", path); 4867c478bd9Sstevel@tonic-gate return (0); 4877c478bd9Sstevel@tonic-gate 4887c478bd9Sstevel@tonic-gate default: 4897c478bd9Sstevel@tonic-gate break; 4907c478bd9Sstevel@tonic-gate } 4917c478bd9Sstevel@tonic-gate 4927c478bd9Sstevel@tonic-gate return (0); 4937c478bd9Sstevel@tonic-gate } 4947c478bd9Sstevel@tonic-gate 4957c478bd9Sstevel@tonic-gate /* 4967c478bd9Sstevel@tonic-gate * Add `path' to the pnset_t pointed to by `pnsetp'. 4977c478bd9Sstevel@tonic-gate */ 4987c478bd9Sstevel@tonic-gate static int 4997c478bd9Sstevel@tonic-gate pnset_add(pnset_t *pnsetp, const char *path) 5007c478bd9Sstevel@tonic-gate { 5017c478bd9Sstevel@tonic-gate char **newpaths; 5029730304dSmeem unsigned int maxpaths; 5037c478bd9Sstevel@tonic-gate 5047c478bd9Sstevel@tonic-gate if (pnsetp->npath == pnsetp->maxpaths) { 5059730304dSmeem maxpaths = (pnsetp->maxpaths == 0) ? 512 : pnsetp->maxpaths * 2; 5069730304dSmeem newpaths = realloc(pnsetp->paths, sizeof (char *) * maxpaths); 5077c478bd9Sstevel@tonic-gate if (newpaths == NULL) 5087c478bd9Sstevel@tonic-gate return (0); 5097c478bd9Sstevel@tonic-gate pnsetp->paths = newpaths; 5109730304dSmeem pnsetp->maxpaths = maxpaths; 5117c478bd9Sstevel@tonic-gate } 5127c478bd9Sstevel@tonic-gate 5137c478bd9Sstevel@tonic-gate pnsetp->paths[pnsetp->npath] = strdup(path); 5147c478bd9Sstevel@tonic-gate if (pnsetp->paths[pnsetp->npath] == NULL) 5157c478bd9Sstevel@tonic-gate return (0); 5167c478bd9Sstevel@tonic-gate 5177c478bd9Sstevel@tonic-gate pnsetp->npath++; 5187c478bd9Sstevel@tonic-gate return (1); 5197c478bd9Sstevel@tonic-gate } 5207c478bd9Sstevel@tonic-gate 5217c478bd9Sstevel@tonic-gate /* 5227c478bd9Sstevel@tonic-gate * Check `path' against the pnset_t pointed to by `pnsetp'. 5237c478bd9Sstevel@tonic-gate */ 5247c478bd9Sstevel@tonic-gate static int 5257c478bd9Sstevel@tonic-gate pnset_check(const pnset_t *pnsetp, const char *path) 5267c478bd9Sstevel@tonic-gate { 5277c478bd9Sstevel@tonic-gate unsigned int i; 5287c478bd9Sstevel@tonic-gate 5297c478bd9Sstevel@tonic-gate for (i = 0; i < pnsetp->npath; i++) { 5307c478bd9Sstevel@tonic-gate if (fnmatch(pnsetp->paths[i], path, 0) == 0) 5317c478bd9Sstevel@tonic-gate return (1); 5327c478bd9Sstevel@tonic-gate } 5337c478bd9Sstevel@tonic-gate return (0); 5347c478bd9Sstevel@tonic-gate } 5357c478bd9Sstevel@tonic-gate 5367c478bd9Sstevel@tonic-gate /* 5377c478bd9Sstevel@tonic-gate * Empty the pnset_t pointed to by `pnsetp'. 5387c478bd9Sstevel@tonic-gate */ 5397c478bd9Sstevel@tonic-gate static void 5407c478bd9Sstevel@tonic-gate pnset_empty(pnset_t *pnsetp) 5417c478bd9Sstevel@tonic-gate { 5427c478bd9Sstevel@tonic-gate while (pnsetp->npath-- != 0) 5437c478bd9Sstevel@tonic-gate free(pnsetp->paths[pnsetp->npath]); 5447c478bd9Sstevel@tonic-gate 5457c478bd9Sstevel@tonic-gate free(pnsetp->paths); 5467c478bd9Sstevel@tonic-gate pnsetp->maxpaths = 0; 5477c478bd9Sstevel@tonic-gate } 5487c478bd9Sstevel@tonic-gate 5499730304dSmeem /* 5509730304dSmeem * Free the pnset_t pointed to by `pnsetp'. 5519730304dSmeem */ 5529730304dSmeem static void 5539730304dSmeem pnset_free(pnset_t *pnsetp) 5549730304dSmeem { 5559730304dSmeem if (pnsetp != NULL) { 5569730304dSmeem pnset_empty(pnsetp); 5579730304dSmeem free(pnsetp); 5589730304dSmeem } 5599730304dSmeem } 5609730304dSmeem 5617c478bd9Sstevel@tonic-gate /* PRINTFLIKE1 */ 5627c478bd9Sstevel@tonic-gate static void 5637c478bd9Sstevel@tonic-gate warn(const char *format, ...) 5647c478bd9Sstevel@tonic-gate { 5657c478bd9Sstevel@tonic-gate va_list alist; 5667c478bd9Sstevel@tonic-gate char *errstr = strerror(errno); 5677c478bd9Sstevel@tonic-gate 5687c478bd9Sstevel@tonic-gate if (errstr == NULL) 5697c478bd9Sstevel@tonic-gate errstr = "<unknown error>"; 5707c478bd9Sstevel@tonic-gate 5717c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%s: ", progname); 5727c478bd9Sstevel@tonic-gate 5737c478bd9Sstevel@tonic-gate va_start(alist, format); 5747c478bd9Sstevel@tonic-gate (void) vfprintf(stderr, format, alist); 5757c478bd9Sstevel@tonic-gate va_end(alist); 5767c478bd9Sstevel@tonic-gate 5777c478bd9Sstevel@tonic-gate if (strrchr(format, '\n') == NULL) 5787c478bd9Sstevel@tonic-gate (void) fprintf(stderr, ": %s\n", errstr); 5797c478bd9Sstevel@tonic-gate } 5807c478bd9Sstevel@tonic-gate 5817c478bd9Sstevel@tonic-gate /* PRINTFLIKE1 */ 5827c478bd9Sstevel@tonic-gate static void 5837c478bd9Sstevel@tonic-gate die(const char *format, ...) 5847c478bd9Sstevel@tonic-gate { 5857c478bd9Sstevel@tonic-gate va_list alist; 5867c478bd9Sstevel@tonic-gate char *errstr = strerror(errno); 5877c478bd9Sstevel@tonic-gate 5887c478bd9Sstevel@tonic-gate if (errstr == NULL) 5897c478bd9Sstevel@tonic-gate errstr = "<unknown error>"; 5907c478bd9Sstevel@tonic-gate 5917c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%s: fatal: ", progname); 5927c478bd9Sstevel@tonic-gate 5937c478bd9Sstevel@tonic-gate va_start(alist, format); 5947c478bd9Sstevel@tonic-gate (void) vfprintf(stderr, format, alist); 5957c478bd9Sstevel@tonic-gate va_end(alist); 5967c478bd9Sstevel@tonic-gate 5977c478bd9Sstevel@tonic-gate if (strrchr(format, '\n') == NULL) 5987c478bd9Sstevel@tonic-gate (void) fprintf(stderr, ": %s\n", errstr); 5997c478bd9Sstevel@tonic-gate 6007c478bd9Sstevel@tonic-gate exit(EXIT_FAILURE); 6017c478bd9Sstevel@tonic-gate } 602