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 /* 569730304dSmeem * Data associated with the current Mercurial manifest. 579730304dSmeem */ 589730304dSmeem typedef struct hgdata { 599730304dSmeem pnset_t *manifest; 609730304dSmeem char hgpath[MAXPATHLEN]; 619730304dSmeem char root[MAXPATHLEN]; 629730304dSmeem unsigned int rootlen; 639730304dSmeem boolean_t rootwarn; 649730304dSmeem } hgdata_t; 659730304dSmeem 669730304dSmeem /* 679730304dSmeem * Hooks used to check if a given unreferenced file is known to an SCM 689730304dSmeem * (currently 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*1b1e41ddSRichard Lowe static checkscm_func_t check_tw, check_hg, check_git; 80*1b1e41ddSRichard 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 }, 939730304dSmeem { "hg", check_hg, chdir_hg }, 949730304dSmeem { "mercurial", check_hg, chdir_hg }, 95*1b1e41ddSRichard Lowe { "git", check_git, chdir_git }, 969730304dSmeem { NULL, NULL, NULL } 979730304dSmeem }; 989730304dSmeem 999730304dSmeem static const scm_t *scm; 1009730304dSmeem static hgdata_t hgdata; 101*1b1e41ddSRichard Lowe static pnset_t *gitmanifest = NULL; 1027c478bd9Sstevel@tonic-gate static time_t tstamp; /* timestamp to compare files to */ 1037c478bd9Sstevel@tonic-gate static pnset_t *exsetp; /* pathname globs to ignore */ 1047c478bd9Sstevel@tonic-gate static const char *progname; 1057c478bd9Sstevel@tonic-gate 1067c478bd9Sstevel@tonic-gate int 1077c478bd9Sstevel@tonic-gate main(int argc, char *argv[]) 1087c478bd9Sstevel@tonic-gate { 1097c478bd9Sstevel@tonic-gate int c; 1107c478bd9Sstevel@tonic-gate char path[MAXPATHLEN]; 1117c478bd9Sstevel@tonic-gate char subtree[MAXPATHLEN] = "./"; 1127c478bd9Sstevel@tonic-gate char *tstampfile = ".build.tstamp"; 1137c478bd9Sstevel@tonic-gate struct stat tsstat; 1147c478bd9Sstevel@tonic-gate 1157c478bd9Sstevel@tonic-gate progname = strrchr(argv[0], '/'); 1167c478bd9Sstevel@tonic-gate if (progname == NULL) 1177c478bd9Sstevel@tonic-gate progname = argv[0]; 1187c478bd9Sstevel@tonic-gate else 1197c478bd9Sstevel@tonic-gate progname++; 1207c478bd9Sstevel@tonic-gate 1219730304dSmeem while ((c = getopt(argc, argv, "as:t:S:")) != EOF) { 1227c478bd9Sstevel@tonic-gate switch (c) { 1237c478bd9Sstevel@tonic-gate case 'a': 1249730304dSmeem /* for compatibility; now the default */ 1257c478bd9Sstevel@tonic-gate break; 1267c478bd9Sstevel@tonic-gate 1277c478bd9Sstevel@tonic-gate case 's': 1287c478bd9Sstevel@tonic-gate (void) strlcat(subtree, optarg, MAXPATHLEN); 1297c478bd9Sstevel@tonic-gate break; 1307c478bd9Sstevel@tonic-gate 1317c478bd9Sstevel@tonic-gate case 't': 1327c478bd9Sstevel@tonic-gate tstampfile = optarg; 1337c478bd9Sstevel@tonic-gate break; 1347c478bd9Sstevel@tonic-gate 1359730304dSmeem case 'S': 1369730304dSmeem for (scm = scms; scm->name != NULL; scm++) { 1379730304dSmeem if (strcmp(scm->name, optarg) == 0) 1389730304dSmeem break; 1399730304dSmeem } 1409730304dSmeem if (scm->name == NULL) 1419730304dSmeem die("unsupported SCM `%s'\n", optarg); 1429730304dSmeem break; 1439730304dSmeem 1447c478bd9Sstevel@tonic-gate default: 1457c478bd9Sstevel@tonic-gate case '?': 1467c478bd9Sstevel@tonic-gate goto usage; 1477c478bd9Sstevel@tonic-gate } 1487c478bd9Sstevel@tonic-gate } 1497c478bd9Sstevel@tonic-gate 1507c478bd9Sstevel@tonic-gate argc -= optind; 1517c478bd9Sstevel@tonic-gate argv += optind; 1527c478bd9Sstevel@tonic-gate 1537c478bd9Sstevel@tonic-gate if (argc != 2) { 1549730304dSmeem usage: (void) fprintf(stderr, "usage: %s [-s <subtree>] " 155*1b1e41ddSRichard Lowe "[-t <tstampfile>] [-S hg|tw|git] <srcroot> <exceptfile>\n", 1569730304dSmeem progname); 1577c478bd9Sstevel@tonic-gate return (EXIT_FAILURE); 1587c478bd9Sstevel@tonic-gate } 1597c478bd9Sstevel@tonic-gate 1607c478bd9Sstevel@tonic-gate /* 1617c478bd9Sstevel@tonic-gate * Interpret a relative timestamp path as relative to srcroot. 1627c478bd9Sstevel@tonic-gate */ 1637c478bd9Sstevel@tonic-gate if (tstampfile[0] == '/') 1647c478bd9Sstevel@tonic-gate (void) strlcpy(path, tstampfile, MAXPATHLEN); 1657c478bd9Sstevel@tonic-gate else 1667c478bd9Sstevel@tonic-gate (void) snprintf(path, MAXPATHLEN, "%s/%s", argv[0], tstampfile); 1677c478bd9Sstevel@tonic-gate 1687c478bd9Sstevel@tonic-gate if (stat(path, &tsstat) == -1) 1697c478bd9Sstevel@tonic-gate die("cannot stat timestamp file \"%s\"", path); 1707c478bd9Sstevel@tonic-gate tstamp = tsstat.st_mtime; 1717c478bd9Sstevel@tonic-gate 1727c478bd9Sstevel@tonic-gate /* 1737c478bd9Sstevel@tonic-gate * Create the exception pathname set. 1747c478bd9Sstevel@tonic-gate */ 1757c478bd9Sstevel@tonic-gate exsetp = make_exset(argv[1]); 1767c478bd9Sstevel@tonic-gate if (exsetp == NULL) 1777c478bd9Sstevel@tonic-gate die("cannot make exception pathname set\n"); 1787c478bd9Sstevel@tonic-gate 1797c478bd9Sstevel@tonic-gate /* 1807c478bd9Sstevel@tonic-gate * Walk the specified subtree of the tree rooted at argv[0]. 1817c478bd9Sstevel@tonic-gate */ 1829730304dSmeem if (chdir(argv[0]) == -1) 1839730304dSmeem die("cannot change directory to \"%s\"", argv[0]); 1849730304dSmeem 1857c478bd9Sstevel@tonic-gate if (nftw(subtree, checkpath, 100, FTW_PHYS) != 0) 1867c478bd9Sstevel@tonic-gate die("cannot walk tree rooted at \"%s\"\n", argv[0]); 1877c478bd9Sstevel@tonic-gate 1887c478bd9Sstevel@tonic-gate pnset_empty(exsetp); 1897c478bd9Sstevel@tonic-gate return (EXIT_SUCCESS); 1907c478bd9Sstevel@tonic-gate } 1917c478bd9Sstevel@tonic-gate 1927c478bd9Sstevel@tonic-gate /* 1939730304dSmeem * Load and return a pnset for the manifest for the Mercurial repo at `hgroot'. 1949730304dSmeem */ 1959730304dSmeem static pnset_t * 1969730304dSmeem load_manifest(const char *hgroot) 1979730304dSmeem { 1989730304dSmeem FILE *fp = NULL; 1999730304dSmeem char *hgcmd = NULL; 2009730304dSmeem char *newline; 2019730304dSmeem pnset_t *pnsetp; 2029730304dSmeem char path[MAXPATHLEN]; 2039730304dSmeem 2049730304dSmeem pnsetp = calloc(sizeof (pnset_t), 1); 2059730304dSmeem if (pnsetp == NULL || 206*1b1e41ddSRichard Lowe asprintf(&hgcmd, "hg manifest -R %s", hgroot) == -1) 2079730304dSmeem goto fail; 2089730304dSmeem 2099730304dSmeem fp = popen(hgcmd, "r"); 2109730304dSmeem if (fp == NULL) 2119730304dSmeem goto fail; 2129730304dSmeem 2139730304dSmeem while (fgets(path, sizeof (path), fp) != NULL) { 2149730304dSmeem newline = strrchr(path, '\n'); 2159730304dSmeem if (newline != NULL) 2169730304dSmeem *newline = '\0'; 2179730304dSmeem 2189730304dSmeem if (pnset_add(pnsetp, path) == 0) 2199730304dSmeem goto fail; 2209730304dSmeem } 2219730304dSmeem 2229730304dSmeem (void) pclose(fp); 2239730304dSmeem free(hgcmd); 2249730304dSmeem return (pnsetp); 2259730304dSmeem fail: 2269730304dSmeem warn("cannot load hg manifest at %s", hgroot); 2279730304dSmeem if (fp != NULL) 2289730304dSmeem (void) pclose(fp); 2299730304dSmeem free(hgcmd); 2309730304dSmeem pnset_free(pnsetp); 2319730304dSmeem return (NULL); 2329730304dSmeem } 2339730304dSmeem 234*1b1e41ddSRichard Lowe static void 235*1b1e41ddSRichard Lowe chdir_git(const char *path) 236*1b1e41ddSRichard Lowe { 237*1b1e41ddSRichard Lowe FILE *fp = NULL; 238*1b1e41ddSRichard Lowe char *gitcmd = NULL; 239*1b1e41ddSRichard Lowe char *newline; 240*1b1e41ddSRichard Lowe char fn[MAXPATHLEN]; 241*1b1e41ddSRichard Lowe pnset_t *pnsetp; 242*1b1e41ddSRichard Lowe 243*1b1e41ddSRichard Lowe pnsetp = calloc(sizeof (pnset_t), 1); 244*1b1e41ddSRichard Lowe if ((pnsetp == NULL) || 245*1b1e41ddSRichard Lowe (asprintf(&gitcmd, "git ls-files %s", path) == -1)) 246*1b1e41ddSRichard Lowe goto fail; 247*1b1e41ddSRichard Lowe 248*1b1e41ddSRichard Lowe if ((fp = popen(gitcmd, "r")) == NULL) 249*1b1e41ddSRichard Lowe goto fail; 250*1b1e41ddSRichard Lowe 251*1b1e41ddSRichard Lowe while (fgets(fn, sizeof (fn), fp) != NULL) { 252*1b1e41ddSRichard Lowe if ((newline = strrchr(fn, '\n')) != NULL) 253*1b1e41ddSRichard Lowe *newline = '\0'; 254*1b1e41ddSRichard Lowe 255*1b1e41ddSRichard Lowe if (pnset_add(pnsetp, fn) == 0) 256*1b1e41ddSRichard Lowe goto fail; 257*1b1e41ddSRichard Lowe } 258*1b1e41ddSRichard Lowe 259*1b1e41ddSRichard Lowe (void) pclose(fp); 260*1b1e41ddSRichard Lowe free(gitcmd); 261*1b1e41ddSRichard Lowe gitmanifest = pnsetp; 262*1b1e41ddSRichard Lowe return; 263*1b1e41ddSRichard Lowe fail: 264*1b1e41ddSRichard Lowe warn("cannot load git manifest"); 265*1b1e41ddSRichard Lowe if (fp != NULL) 266*1b1e41ddSRichard Lowe (void) pclose(fp); 267*1b1e41ddSRichard Lowe if (pnsetp != NULL) 268*1b1e41ddSRichard Lowe free(pnsetp); 269*1b1e41ddSRichard Lowe if (gitcmd != NULL) 270*1b1e41ddSRichard Lowe free(gitcmd); 271*1b1e41ddSRichard Lowe } 272*1b1e41ddSRichard Lowe 2739730304dSmeem /* 2749730304dSmeem * If necessary, change our active manifest to be appropriate for `path'. 2759730304dSmeem */ 2769730304dSmeem static void 2779730304dSmeem chdir_hg(const char *path) 2789730304dSmeem { 2799730304dSmeem char hgpath[MAXPATHLEN]; 2809730304dSmeem char basepath[MAXPATHLEN]; 2819730304dSmeem char *slash; 2829730304dSmeem 2839730304dSmeem (void) snprintf(hgpath, MAXPATHLEN, "%s/.hg", path); 2849730304dSmeem 2859730304dSmeem /* 2869730304dSmeem * Change our active manifest if any one of the following is true: 2879730304dSmeem * 2889730304dSmeem * 1. No manifest is loaded. Find the nearest hgroot to load from. 2899730304dSmeem * 2909730304dSmeem * 2. A manifest is loaded, but we've moved into a directory with 2919730304dSmeem * its own hgroot (e.g., usr/closed). Load from its hgroot. 2929730304dSmeem * 2939730304dSmeem * 3. A manifest is loaded, but no longer applies (e.g., the manifest 2949730304dSmeem * under usr/closed is loaded, but we've moved to usr/src). 2959730304dSmeem */ 2969730304dSmeem if (hgdata.manifest == NULL || 2979730304dSmeem strcmp(hgpath, hgdata.hgpath) != 0 && access(hgpath, X_OK) == 0 || 2989730304dSmeem strncmp(path, hgdata.root, hgdata.rootlen - 1) != 0) { 2999730304dSmeem pnset_free(hgdata.manifest); 3009730304dSmeem hgdata.manifest = NULL; 3019730304dSmeem 3029730304dSmeem (void) strlcpy(basepath, path, MAXPATHLEN); 3039730304dSmeem 3049730304dSmeem /* 3059730304dSmeem * Walk up the directory tree looking for .hg subdirectories. 3069730304dSmeem */ 3079730304dSmeem while (access(hgpath, X_OK) == -1) { 3089730304dSmeem slash = strrchr(basepath, '/'); 3099730304dSmeem if (slash == NULL) { 3109730304dSmeem if (!hgdata.rootwarn) { 3119730304dSmeem warn("no hg root for \"%s\"\n", path); 3129730304dSmeem hgdata.rootwarn = B_TRUE; 3139730304dSmeem } 3149730304dSmeem return; 3159730304dSmeem } 3169730304dSmeem *slash = '\0'; 3179730304dSmeem (void) snprintf(hgpath, MAXPATHLEN, "%s/.hg", basepath); 3189730304dSmeem } 3199730304dSmeem 3209730304dSmeem /* 3219730304dSmeem * We found a directory with an .hg subdirectory; record it 3229730304dSmeem * and load its manifest. 3239730304dSmeem */ 3249730304dSmeem (void) strlcpy(hgdata.hgpath, hgpath, MAXPATHLEN); 3259730304dSmeem (void) strlcpy(hgdata.root, basepath, MAXPATHLEN); 3269730304dSmeem hgdata.manifest = load_manifest(hgdata.root); 3279730304dSmeem 3289730304dSmeem /* 3299730304dSmeem * The logic in check_hg() depends on hgdata.root having a 3309730304dSmeem * single trailing slash, so only add it if it's missing. 3319730304dSmeem */ 3329730304dSmeem if (hgdata.root[strlen(hgdata.root) - 1] != '/') 3339730304dSmeem (void) strlcat(hgdata.root, "/", MAXPATHLEN); 3349730304dSmeem hgdata.rootlen = strlen(hgdata.root); 3359730304dSmeem } 3369730304dSmeem } 3379730304dSmeem 3389730304dSmeem /* 3399730304dSmeem * Check if a file is under Mercurial control by checking against the manifest. 3409730304dSmeem */ 3419730304dSmeem /* ARGSUSED */ 3429730304dSmeem static int 3439730304dSmeem check_hg(const char *path, const struct FTW *ftwp) 3449730304dSmeem { 3459730304dSmeem /* 3469730304dSmeem * The manifest paths are relative to the manifest root; skip past it. 3479730304dSmeem */ 3489730304dSmeem path += hgdata.rootlen; 3499730304dSmeem 3509730304dSmeem return (hgdata.manifest != NULL && pnset_check(hgdata.manifest, path)); 3519730304dSmeem } 352*1b1e41ddSRichard Lowe /* ARGSUSED */ 353*1b1e41ddSRichard Lowe static int 354*1b1e41ddSRichard Lowe check_git(const char *path, const struct FTW *ftwp) 355*1b1e41ddSRichard Lowe { 356*1b1e41ddSRichard Lowe path += 2; /* Skip "./" */ 357*1b1e41ddSRichard Lowe return (gitmanifest != NULL && pnset_check(gitmanifest, path)); 358*1b1e41ddSRichard Lowe } 3599730304dSmeem 3609730304dSmeem /* 3619730304dSmeem * Check if a file is under TeamWare control by checking for its corresponding 3629730304dSmeem * SCCS "s-dot" file. 3639730304dSmeem */ 3649730304dSmeem static int 3659730304dSmeem check_tw(const char *path, const struct FTW *ftwp) 3669730304dSmeem { 3679730304dSmeem char sccspath[MAXPATHLEN]; 3689730304dSmeem 3699730304dSmeem (void) snprintf(sccspath, MAXPATHLEN, "%.*s/SCCS/s.%s", ftwp->base, 3709730304dSmeem path, path + ftwp->base); 3719730304dSmeem 3729730304dSmeem return (access(sccspath, F_OK) == 0); 3739730304dSmeem } 3749730304dSmeem 3759730304dSmeem /* 3767c478bd9Sstevel@tonic-gate * Using `exceptfile' and a built-in list of exceptions, build and return a 3777c478bd9Sstevel@tonic-gate * pnset_t consisting of all of the pathnames globs which are allowed to be 3787c478bd9Sstevel@tonic-gate * unreferenced in the source tree. 3797c478bd9Sstevel@tonic-gate */ 3807c478bd9Sstevel@tonic-gate static pnset_t * 3817c478bd9Sstevel@tonic-gate make_exset(const char *exceptfile) 3827c478bd9Sstevel@tonic-gate { 3837c478bd9Sstevel@tonic-gate FILE *fp; 3847c478bd9Sstevel@tonic-gate char line[MAXPATHLEN]; 3857c478bd9Sstevel@tonic-gate char *newline; 3867c478bd9Sstevel@tonic-gate pnset_t *pnsetp; 3877c478bd9Sstevel@tonic-gate unsigned int i; 3887c478bd9Sstevel@tonic-gate 3897c478bd9Sstevel@tonic-gate pnsetp = calloc(sizeof (pnset_t), 1); 3907c478bd9Sstevel@tonic-gate if (pnsetp == NULL) 3917c478bd9Sstevel@tonic-gate return (NULL); 3927c478bd9Sstevel@tonic-gate 3937c478bd9Sstevel@tonic-gate /* 3947c478bd9Sstevel@tonic-gate * Add any exceptions from the file. 3957c478bd9Sstevel@tonic-gate */ 3967c478bd9Sstevel@tonic-gate fp = fopen(exceptfile, "r"); 3977c478bd9Sstevel@tonic-gate if (fp == NULL) { 3987c478bd9Sstevel@tonic-gate warn("cannot open exception file \"%s\"", exceptfile); 3997c478bd9Sstevel@tonic-gate goto fail; 4007c478bd9Sstevel@tonic-gate } 4017c478bd9Sstevel@tonic-gate 4027c478bd9Sstevel@tonic-gate while (fgets(line, sizeof (line), fp) != NULL) { 4037c478bd9Sstevel@tonic-gate newline = strrchr(line, '\n'); 4047c478bd9Sstevel@tonic-gate if (newline != NULL) 4057c478bd9Sstevel@tonic-gate *newline = '\0'; 4067c478bd9Sstevel@tonic-gate 4077c478bd9Sstevel@tonic-gate for (i = 0; isspace(line[i]); i++) 4087c478bd9Sstevel@tonic-gate ; 4097c478bd9Sstevel@tonic-gate 4107c478bd9Sstevel@tonic-gate if (line[i] == '#' || line[i] == '\0') 4117c478bd9Sstevel@tonic-gate continue; 4127c478bd9Sstevel@tonic-gate 4137c478bd9Sstevel@tonic-gate if (pnset_add(pnsetp, line) == 0) { 4147c478bd9Sstevel@tonic-gate (void) fclose(fp); 4157c478bd9Sstevel@tonic-gate goto fail; 4167c478bd9Sstevel@tonic-gate } 4177c478bd9Sstevel@tonic-gate } 4187c478bd9Sstevel@tonic-gate 4197c478bd9Sstevel@tonic-gate (void) fclose(fp); 4207c478bd9Sstevel@tonic-gate return (pnsetp); 4217c478bd9Sstevel@tonic-gate fail: 4229730304dSmeem pnset_free(pnsetp); 4237c478bd9Sstevel@tonic-gate return (NULL); 4247c478bd9Sstevel@tonic-gate } 4257c478bd9Sstevel@tonic-gate 4267c478bd9Sstevel@tonic-gate /* 4277c478bd9Sstevel@tonic-gate * FTW callback: print `path' if it's older than `tstamp' and not in `exsetp'. 4287c478bd9Sstevel@tonic-gate */ 4297c478bd9Sstevel@tonic-gate static int 4307c478bd9Sstevel@tonic-gate checkpath(const char *path, const struct stat *statp, int type, 4317c478bd9Sstevel@tonic-gate struct FTW *ftwp) 4327c478bd9Sstevel@tonic-gate { 4337c478bd9Sstevel@tonic-gate switch (type) { 4347c478bd9Sstevel@tonic-gate case FTW_F: 4357c478bd9Sstevel@tonic-gate /* 4367c478bd9Sstevel@tonic-gate * Skip if the file is referenced or in the exception list. 4377c478bd9Sstevel@tonic-gate */ 4387c478bd9Sstevel@tonic-gate if (statp->st_atime >= tstamp || pnset_check(exsetp, path)) 4397c478bd9Sstevel@tonic-gate return (0); 4407c478bd9Sstevel@tonic-gate 4417c478bd9Sstevel@tonic-gate /* 4429730304dSmeem * If requested, restrict ourselves to unreferenced files 4439730304dSmeem * under SCM control. 4447c478bd9Sstevel@tonic-gate */ 4459730304dSmeem if (scm == NULL || scm->checkfunc(path, ftwp)) 4467c478bd9Sstevel@tonic-gate (void) puts(path); 4477c478bd9Sstevel@tonic-gate return (0); 4487c478bd9Sstevel@tonic-gate 4497c478bd9Sstevel@tonic-gate case FTW_D: 4507c478bd9Sstevel@tonic-gate /* 4517c478bd9Sstevel@tonic-gate * Prune any directories in the exception list. 4527c478bd9Sstevel@tonic-gate */ 4539730304dSmeem if (pnset_check(exsetp, path)) { 4547c478bd9Sstevel@tonic-gate ftwp->quit = FTW_PRUNE; 4557c478bd9Sstevel@tonic-gate return (0); 4569730304dSmeem } 4579730304dSmeem 4589730304dSmeem /* 4599730304dSmeem * If necessary, advise the SCM logic of our new directory. 4609730304dSmeem */ 4619730304dSmeem if (scm != NULL && scm->chdirfunc != NULL) 4629730304dSmeem scm->chdirfunc(path); 4639730304dSmeem 4649730304dSmeem return (0); 4657c478bd9Sstevel@tonic-gate 4667c478bd9Sstevel@tonic-gate case FTW_DNR: 4677c478bd9Sstevel@tonic-gate warn("cannot read \"%s\"", path); 4687c478bd9Sstevel@tonic-gate return (0); 4697c478bd9Sstevel@tonic-gate 4707c478bd9Sstevel@tonic-gate case FTW_NS: 4717c478bd9Sstevel@tonic-gate warn("cannot stat \"%s\"", path); 4727c478bd9Sstevel@tonic-gate return (0); 4737c478bd9Sstevel@tonic-gate 4747c478bd9Sstevel@tonic-gate default: 4757c478bd9Sstevel@tonic-gate break; 4767c478bd9Sstevel@tonic-gate } 4777c478bd9Sstevel@tonic-gate 4787c478bd9Sstevel@tonic-gate return (0); 4797c478bd9Sstevel@tonic-gate } 4807c478bd9Sstevel@tonic-gate 4817c478bd9Sstevel@tonic-gate /* 4827c478bd9Sstevel@tonic-gate * Add `path' to the pnset_t pointed to by `pnsetp'. 4837c478bd9Sstevel@tonic-gate */ 4847c478bd9Sstevel@tonic-gate static int 4857c478bd9Sstevel@tonic-gate pnset_add(pnset_t *pnsetp, const char *path) 4867c478bd9Sstevel@tonic-gate { 4877c478bd9Sstevel@tonic-gate char **newpaths; 4889730304dSmeem unsigned int maxpaths; 4897c478bd9Sstevel@tonic-gate 4907c478bd9Sstevel@tonic-gate if (pnsetp->npath == pnsetp->maxpaths) { 4919730304dSmeem maxpaths = (pnsetp->maxpaths == 0) ? 512 : pnsetp->maxpaths * 2; 4929730304dSmeem newpaths = realloc(pnsetp->paths, sizeof (char *) * maxpaths); 4937c478bd9Sstevel@tonic-gate if (newpaths == NULL) 4947c478bd9Sstevel@tonic-gate return (0); 4957c478bd9Sstevel@tonic-gate pnsetp->paths = newpaths; 4969730304dSmeem pnsetp->maxpaths = maxpaths; 4977c478bd9Sstevel@tonic-gate } 4987c478bd9Sstevel@tonic-gate 4997c478bd9Sstevel@tonic-gate pnsetp->paths[pnsetp->npath] = strdup(path); 5007c478bd9Sstevel@tonic-gate if (pnsetp->paths[pnsetp->npath] == NULL) 5017c478bd9Sstevel@tonic-gate return (0); 5027c478bd9Sstevel@tonic-gate 5037c478bd9Sstevel@tonic-gate pnsetp->npath++; 5047c478bd9Sstevel@tonic-gate return (1); 5057c478bd9Sstevel@tonic-gate } 5067c478bd9Sstevel@tonic-gate 5077c478bd9Sstevel@tonic-gate /* 5087c478bd9Sstevel@tonic-gate * Check `path' against the pnset_t pointed to by `pnsetp'. 5097c478bd9Sstevel@tonic-gate */ 5107c478bd9Sstevel@tonic-gate static int 5117c478bd9Sstevel@tonic-gate pnset_check(const pnset_t *pnsetp, const char *path) 5127c478bd9Sstevel@tonic-gate { 5137c478bd9Sstevel@tonic-gate unsigned int i; 5147c478bd9Sstevel@tonic-gate 5157c478bd9Sstevel@tonic-gate for (i = 0; i < pnsetp->npath; i++) { 5167c478bd9Sstevel@tonic-gate if (fnmatch(pnsetp->paths[i], path, 0) == 0) 5177c478bd9Sstevel@tonic-gate return (1); 5187c478bd9Sstevel@tonic-gate } 5197c478bd9Sstevel@tonic-gate return (0); 5207c478bd9Sstevel@tonic-gate } 5217c478bd9Sstevel@tonic-gate 5227c478bd9Sstevel@tonic-gate /* 5237c478bd9Sstevel@tonic-gate * Empty the pnset_t pointed to by `pnsetp'. 5247c478bd9Sstevel@tonic-gate */ 5257c478bd9Sstevel@tonic-gate static void 5267c478bd9Sstevel@tonic-gate pnset_empty(pnset_t *pnsetp) 5277c478bd9Sstevel@tonic-gate { 5287c478bd9Sstevel@tonic-gate while (pnsetp->npath-- != 0) 5297c478bd9Sstevel@tonic-gate free(pnsetp->paths[pnsetp->npath]); 5307c478bd9Sstevel@tonic-gate 5317c478bd9Sstevel@tonic-gate free(pnsetp->paths); 5327c478bd9Sstevel@tonic-gate pnsetp->maxpaths = 0; 5337c478bd9Sstevel@tonic-gate } 5347c478bd9Sstevel@tonic-gate 5359730304dSmeem /* 5369730304dSmeem * Free the pnset_t pointed to by `pnsetp'. 5379730304dSmeem */ 5389730304dSmeem static void 5399730304dSmeem pnset_free(pnset_t *pnsetp) 5409730304dSmeem { 5419730304dSmeem if (pnsetp != NULL) { 5429730304dSmeem pnset_empty(pnsetp); 5439730304dSmeem free(pnsetp); 5449730304dSmeem } 5459730304dSmeem } 5469730304dSmeem 5477c478bd9Sstevel@tonic-gate /* PRINTFLIKE1 */ 5487c478bd9Sstevel@tonic-gate static void 5497c478bd9Sstevel@tonic-gate warn(const char *format, ...) 5507c478bd9Sstevel@tonic-gate { 5517c478bd9Sstevel@tonic-gate va_list alist; 5527c478bd9Sstevel@tonic-gate char *errstr = strerror(errno); 5537c478bd9Sstevel@tonic-gate 5547c478bd9Sstevel@tonic-gate if (errstr == NULL) 5557c478bd9Sstevel@tonic-gate errstr = "<unknown error>"; 5567c478bd9Sstevel@tonic-gate 5577c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%s: ", progname); 5587c478bd9Sstevel@tonic-gate 5597c478bd9Sstevel@tonic-gate va_start(alist, format); 5607c478bd9Sstevel@tonic-gate (void) vfprintf(stderr, format, alist); 5617c478bd9Sstevel@tonic-gate va_end(alist); 5627c478bd9Sstevel@tonic-gate 5637c478bd9Sstevel@tonic-gate if (strrchr(format, '\n') == NULL) 5647c478bd9Sstevel@tonic-gate (void) fprintf(stderr, ": %s\n", errstr); 5657c478bd9Sstevel@tonic-gate } 5667c478bd9Sstevel@tonic-gate 5677c478bd9Sstevel@tonic-gate /* PRINTFLIKE1 */ 5687c478bd9Sstevel@tonic-gate static void 5697c478bd9Sstevel@tonic-gate die(const char *format, ...) 5707c478bd9Sstevel@tonic-gate { 5717c478bd9Sstevel@tonic-gate va_list alist; 5727c478bd9Sstevel@tonic-gate char *errstr = strerror(errno); 5737c478bd9Sstevel@tonic-gate 5747c478bd9Sstevel@tonic-gate if (errstr == NULL) 5757c478bd9Sstevel@tonic-gate errstr = "<unknown error>"; 5767c478bd9Sstevel@tonic-gate 5777c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%s: fatal: ", progname); 5787c478bd9Sstevel@tonic-gate 5797c478bd9Sstevel@tonic-gate va_start(alist, format); 5807c478bd9Sstevel@tonic-gate (void) vfprintf(stderr, format, alist); 5817c478bd9Sstevel@tonic-gate va_end(alist); 5827c478bd9Sstevel@tonic-gate 5837c478bd9Sstevel@tonic-gate if (strrchr(format, '\n') == NULL) 5847c478bd9Sstevel@tonic-gate (void) fprintf(stderr, ": %s\n", errstr); 5857c478bd9Sstevel@tonic-gate 5867c478bd9Sstevel@tonic-gate exit(EXIT_FAILURE); 5877c478bd9Sstevel@tonic-gate } 588