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 20*9730304dSmeem * 21*9730304dSmeem * 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 55*9730304dSmeem /* 56*9730304dSmeem * Data associated with the current Mercurial manifest. 57*9730304dSmeem */ 58*9730304dSmeem typedef struct hgdata { 59*9730304dSmeem pnset_t *manifest; 60*9730304dSmeem char hgpath[MAXPATHLEN]; 61*9730304dSmeem char root[MAXPATHLEN]; 62*9730304dSmeem unsigned int rootlen; 63*9730304dSmeem boolean_t rootwarn; 64*9730304dSmeem } hgdata_t; 65*9730304dSmeem 66*9730304dSmeem /* 67*9730304dSmeem * Hooks used to check if a given unreferenced file is known to an SCM 68*9730304dSmeem * (currently Mercurial and TeamWare). 69*9730304dSmeem */ 70*9730304dSmeem typedef int checkscm_func_t(const char *, const struct FTW *); 71*9730304dSmeem typedef void chdirscm_func_t(const char *); 72*9730304dSmeem 73*9730304dSmeem typedef struct { 74*9730304dSmeem const char *name; 75*9730304dSmeem checkscm_func_t *checkfunc; 76*9730304dSmeem chdirscm_func_t *chdirfunc; 77*9730304dSmeem } scm_t; 78*9730304dSmeem 79*9730304dSmeem static checkscm_func_t check_tw, check_hg; 80*9730304dSmeem static chdirscm_func_t chdir_hg; 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 *); 84*9730304dSmeem 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 90*9730304dSmeem static const scm_t scms[] = { 91*9730304dSmeem { "tw", check_tw, NULL }, 92*9730304dSmeem { "teamware", check_tw, NULL }, 93*9730304dSmeem { "hg", check_hg, chdir_hg }, 94*9730304dSmeem { "mercurial", check_hg, chdir_hg }, 95*9730304dSmeem { NULL, NULL, NULL } 96*9730304dSmeem }; 97*9730304dSmeem 98*9730304dSmeem static const scm_t *scm; 99*9730304dSmeem static hgdata_t hgdata; 1007c478bd9Sstevel@tonic-gate static time_t tstamp; /* timestamp to compare files to */ 1017c478bd9Sstevel@tonic-gate static pnset_t *exsetp; /* pathname globs to ignore */ 1027c478bd9Sstevel@tonic-gate static const char *progname; 1037c478bd9Sstevel@tonic-gate 1047c478bd9Sstevel@tonic-gate int 1057c478bd9Sstevel@tonic-gate main(int argc, char *argv[]) 1067c478bd9Sstevel@tonic-gate { 1077c478bd9Sstevel@tonic-gate int c; 1087c478bd9Sstevel@tonic-gate char path[MAXPATHLEN]; 1097c478bd9Sstevel@tonic-gate char subtree[MAXPATHLEN] = "./"; 1107c478bd9Sstevel@tonic-gate char *tstampfile = ".build.tstamp"; 1117c478bd9Sstevel@tonic-gate struct stat tsstat; 1127c478bd9Sstevel@tonic-gate 1137c478bd9Sstevel@tonic-gate progname = strrchr(argv[0], '/'); 1147c478bd9Sstevel@tonic-gate if (progname == NULL) 1157c478bd9Sstevel@tonic-gate progname = argv[0]; 1167c478bd9Sstevel@tonic-gate else 1177c478bd9Sstevel@tonic-gate progname++; 1187c478bd9Sstevel@tonic-gate 119*9730304dSmeem while ((c = getopt(argc, argv, "as:t:S:")) != EOF) { 1207c478bd9Sstevel@tonic-gate switch (c) { 1217c478bd9Sstevel@tonic-gate case 'a': 122*9730304dSmeem /* for compatibility; now the default */ 1237c478bd9Sstevel@tonic-gate break; 1247c478bd9Sstevel@tonic-gate 1257c478bd9Sstevel@tonic-gate case 's': 1267c478bd9Sstevel@tonic-gate (void) strlcat(subtree, optarg, MAXPATHLEN); 1277c478bd9Sstevel@tonic-gate break; 1287c478bd9Sstevel@tonic-gate 1297c478bd9Sstevel@tonic-gate case 't': 1307c478bd9Sstevel@tonic-gate tstampfile = optarg; 1317c478bd9Sstevel@tonic-gate break; 1327c478bd9Sstevel@tonic-gate 133*9730304dSmeem case 'S': 134*9730304dSmeem for (scm = scms; scm->name != NULL; scm++) { 135*9730304dSmeem if (strcmp(scm->name, optarg) == 0) 136*9730304dSmeem break; 137*9730304dSmeem } 138*9730304dSmeem if (scm->name == NULL) 139*9730304dSmeem die("unsupported SCM `%s'\n", optarg); 140*9730304dSmeem break; 141*9730304dSmeem 1427c478bd9Sstevel@tonic-gate default: 1437c478bd9Sstevel@tonic-gate case '?': 1447c478bd9Sstevel@tonic-gate goto usage; 1457c478bd9Sstevel@tonic-gate } 1467c478bd9Sstevel@tonic-gate } 1477c478bd9Sstevel@tonic-gate 1487c478bd9Sstevel@tonic-gate argc -= optind; 1497c478bd9Sstevel@tonic-gate argv += optind; 1507c478bd9Sstevel@tonic-gate 1517c478bd9Sstevel@tonic-gate if (argc != 2) { 152*9730304dSmeem usage: (void) fprintf(stderr, "usage: %s [-s <subtree>] " 153*9730304dSmeem "[-t <tstampfile>] [-S hg|tw] <srcroot> <exceptfile>\n", 154*9730304dSmeem progname); 1557c478bd9Sstevel@tonic-gate return (EXIT_FAILURE); 1567c478bd9Sstevel@tonic-gate } 1577c478bd9Sstevel@tonic-gate 1587c478bd9Sstevel@tonic-gate /* 1597c478bd9Sstevel@tonic-gate * Interpret a relative timestamp path as relative to srcroot. 1607c478bd9Sstevel@tonic-gate */ 1617c478bd9Sstevel@tonic-gate if (tstampfile[0] == '/') 1627c478bd9Sstevel@tonic-gate (void) strlcpy(path, tstampfile, MAXPATHLEN); 1637c478bd9Sstevel@tonic-gate else 1647c478bd9Sstevel@tonic-gate (void) snprintf(path, MAXPATHLEN, "%s/%s", argv[0], tstampfile); 1657c478bd9Sstevel@tonic-gate 1667c478bd9Sstevel@tonic-gate if (stat(path, &tsstat) == -1) 1677c478bd9Sstevel@tonic-gate die("cannot stat timestamp file \"%s\"", path); 1687c478bd9Sstevel@tonic-gate tstamp = tsstat.st_mtime; 1697c478bd9Sstevel@tonic-gate 1707c478bd9Sstevel@tonic-gate /* 1717c478bd9Sstevel@tonic-gate * Create the exception pathname set. 1727c478bd9Sstevel@tonic-gate */ 1737c478bd9Sstevel@tonic-gate exsetp = make_exset(argv[1]); 1747c478bd9Sstevel@tonic-gate if (exsetp == NULL) 1757c478bd9Sstevel@tonic-gate die("cannot make exception pathname set\n"); 1767c478bd9Sstevel@tonic-gate 1777c478bd9Sstevel@tonic-gate /* 1787c478bd9Sstevel@tonic-gate * Walk the specified subtree of the tree rooted at argv[0]. 1797c478bd9Sstevel@tonic-gate */ 180*9730304dSmeem if (chdir(argv[0]) == -1) 181*9730304dSmeem die("cannot change directory to \"%s\"", argv[0]); 182*9730304dSmeem 1837c478bd9Sstevel@tonic-gate if (nftw(subtree, checkpath, 100, FTW_PHYS) != 0) 1847c478bd9Sstevel@tonic-gate die("cannot walk tree rooted at \"%s\"\n", argv[0]); 1857c478bd9Sstevel@tonic-gate 1867c478bd9Sstevel@tonic-gate pnset_empty(exsetp); 1877c478bd9Sstevel@tonic-gate return (EXIT_SUCCESS); 1887c478bd9Sstevel@tonic-gate } 1897c478bd9Sstevel@tonic-gate 1907c478bd9Sstevel@tonic-gate /* 191*9730304dSmeem * Load and return a pnset for the manifest for the Mercurial repo at `hgroot'. 192*9730304dSmeem */ 193*9730304dSmeem static pnset_t * 194*9730304dSmeem load_manifest(const char *hgroot) 195*9730304dSmeem { 196*9730304dSmeem FILE *fp = NULL; 197*9730304dSmeem char *hgcmd = NULL; 198*9730304dSmeem char *newline; 199*9730304dSmeem pnset_t *pnsetp; 200*9730304dSmeem char path[MAXPATHLEN]; 201*9730304dSmeem 202*9730304dSmeem pnsetp = calloc(sizeof (pnset_t), 1); 203*9730304dSmeem if (pnsetp == NULL || 204*9730304dSmeem asprintf(&hgcmd, "/usr/bin/hg manifest -R %s", hgroot) == -1) 205*9730304dSmeem goto fail; 206*9730304dSmeem 207*9730304dSmeem fp = popen(hgcmd, "r"); 208*9730304dSmeem if (fp == NULL) 209*9730304dSmeem goto fail; 210*9730304dSmeem 211*9730304dSmeem while (fgets(path, sizeof (path), fp) != NULL) { 212*9730304dSmeem newline = strrchr(path, '\n'); 213*9730304dSmeem if (newline != NULL) 214*9730304dSmeem *newline = '\0'; 215*9730304dSmeem 216*9730304dSmeem if (pnset_add(pnsetp, path) == 0) 217*9730304dSmeem goto fail; 218*9730304dSmeem } 219*9730304dSmeem 220*9730304dSmeem (void) pclose(fp); 221*9730304dSmeem free(hgcmd); 222*9730304dSmeem return (pnsetp); 223*9730304dSmeem fail: 224*9730304dSmeem warn("cannot load hg manifest at %s", hgroot); 225*9730304dSmeem if (fp != NULL) 226*9730304dSmeem (void) pclose(fp); 227*9730304dSmeem free(hgcmd); 228*9730304dSmeem pnset_free(pnsetp); 229*9730304dSmeem return (NULL); 230*9730304dSmeem } 231*9730304dSmeem 232*9730304dSmeem /* 233*9730304dSmeem * If necessary, change our active manifest to be appropriate for `path'. 234*9730304dSmeem */ 235*9730304dSmeem static void 236*9730304dSmeem chdir_hg(const char *path) 237*9730304dSmeem { 238*9730304dSmeem char hgpath[MAXPATHLEN]; 239*9730304dSmeem char basepath[MAXPATHLEN]; 240*9730304dSmeem char *slash; 241*9730304dSmeem 242*9730304dSmeem (void) snprintf(hgpath, MAXPATHLEN, "%s/.hg", path); 243*9730304dSmeem 244*9730304dSmeem /* 245*9730304dSmeem * Change our active manifest if any one of the following is true: 246*9730304dSmeem * 247*9730304dSmeem * 1. No manifest is loaded. Find the nearest hgroot to load from. 248*9730304dSmeem * 249*9730304dSmeem * 2. A manifest is loaded, but we've moved into a directory with 250*9730304dSmeem * its own hgroot (e.g., usr/closed). Load from its hgroot. 251*9730304dSmeem * 252*9730304dSmeem * 3. A manifest is loaded, but no longer applies (e.g., the manifest 253*9730304dSmeem * under usr/closed is loaded, but we've moved to usr/src). 254*9730304dSmeem */ 255*9730304dSmeem if (hgdata.manifest == NULL || 256*9730304dSmeem strcmp(hgpath, hgdata.hgpath) != 0 && access(hgpath, X_OK) == 0 || 257*9730304dSmeem strncmp(path, hgdata.root, hgdata.rootlen - 1) != 0) { 258*9730304dSmeem pnset_free(hgdata.manifest); 259*9730304dSmeem hgdata.manifest = NULL; 260*9730304dSmeem 261*9730304dSmeem (void) strlcpy(basepath, path, MAXPATHLEN); 262*9730304dSmeem 263*9730304dSmeem /* 264*9730304dSmeem * Walk up the directory tree looking for .hg subdirectories. 265*9730304dSmeem */ 266*9730304dSmeem while (access(hgpath, X_OK) == -1) { 267*9730304dSmeem slash = strrchr(basepath, '/'); 268*9730304dSmeem if (slash == NULL) { 269*9730304dSmeem if (!hgdata.rootwarn) { 270*9730304dSmeem warn("no hg root for \"%s\"\n", path); 271*9730304dSmeem hgdata.rootwarn = B_TRUE; 272*9730304dSmeem } 273*9730304dSmeem return; 274*9730304dSmeem } 275*9730304dSmeem *slash = '\0'; 276*9730304dSmeem (void) snprintf(hgpath, MAXPATHLEN, "%s/.hg", basepath); 277*9730304dSmeem } 278*9730304dSmeem 279*9730304dSmeem /* 280*9730304dSmeem * We found a directory with an .hg subdirectory; record it 281*9730304dSmeem * and load its manifest. 282*9730304dSmeem */ 283*9730304dSmeem (void) strlcpy(hgdata.hgpath, hgpath, MAXPATHLEN); 284*9730304dSmeem (void) strlcpy(hgdata.root, basepath, MAXPATHLEN); 285*9730304dSmeem hgdata.manifest = load_manifest(hgdata.root); 286*9730304dSmeem 287*9730304dSmeem /* 288*9730304dSmeem * The logic in check_hg() depends on hgdata.root having a 289*9730304dSmeem * single trailing slash, so only add it if it's missing. 290*9730304dSmeem */ 291*9730304dSmeem if (hgdata.root[strlen(hgdata.root) - 1] != '/') 292*9730304dSmeem (void) strlcat(hgdata.root, "/", MAXPATHLEN); 293*9730304dSmeem hgdata.rootlen = strlen(hgdata.root); 294*9730304dSmeem } 295*9730304dSmeem } 296*9730304dSmeem 297*9730304dSmeem /* 298*9730304dSmeem * Check if a file is under Mercurial control by checking against the manifest. 299*9730304dSmeem */ 300*9730304dSmeem /* ARGSUSED */ 301*9730304dSmeem static int 302*9730304dSmeem check_hg(const char *path, const struct FTW *ftwp) 303*9730304dSmeem { 304*9730304dSmeem /* 305*9730304dSmeem * The manifest paths are relative to the manifest root; skip past it. 306*9730304dSmeem */ 307*9730304dSmeem path += hgdata.rootlen; 308*9730304dSmeem 309*9730304dSmeem return (hgdata.manifest != NULL && pnset_check(hgdata.manifest, path)); 310*9730304dSmeem } 311*9730304dSmeem 312*9730304dSmeem /* 313*9730304dSmeem * Check if a file is under TeamWare control by checking for its corresponding 314*9730304dSmeem * SCCS "s-dot" file. 315*9730304dSmeem */ 316*9730304dSmeem static int 317*9730304dSmeem check_tw(const char *path, const struct FTW *ftwp) 318*9730304dSmeem { 319*9730304dSmeem char sccspath[MAXPATHLEN]; 320*9730304dSmeem 321*9730304dSmeem (void) snprintf(sccspath, MAXPATHLEN, "%.*s/SCCS/s.%s", ftwp->base, 322*9730304dSmeem path, path + ftwp->base); 323*9730304dSmeem 324*9730304dSmeem return (access(sccspath, F_OK) == 0); 325*9730304dSmeem } 326*9730304dSmeem 327*9730304dSmeem /* 3287c478bd9Sstevel@tonic-gate * Using `exceptfile' and a built-in list of exceptions, build and return a 3297c478bd9Sstevel@tonic-gate * pnset_t consisting of all of the pathnames globs which are allowed to be 3307c478bd9Sstevel@tonic-gate * unreferenced in the source tree. 3317c478bd9Sstevel@tonic-gate */ 3327c478bd9Sstevel@tonic-gate static pnset_t * 3337c478bd9Sstevel@tonic-gate make_exset(const char *exceptfile) 3347c478bd9Sstevel@tonic-gate { 3357c478bd9Sstevel@tonic-gate FILE *fp; 3367c478bd9Sstevel@tonic-gate char line[MAXPATHLEN]; 3377c478bd9Sstevel@tonic-gate char *newline; 3387c478bd9Sstevel@tonic-gate pnset_t *pnsetp; 3397c478bd9Sstevel@tonic-gate unsigned int i; 3407c478bd9Sstevel@tonic-gate 3417c478bd9Sstevel@tonic-gate pnsetp = calloc(sizeof (pnset_t), 1); 3427c478bd9Sstevel@tonic-gate if (pnsetp == NULL) 3437c478bd9Sstevel@tonic-gate return (NULL); 3447c478bd9Sstevel@tonic-gate 3457c478bd9Sstevel@tonic-gate /* 3467c478bd9Sstevel@tonic-gate * Add any exceptions from the file. 3477c478bd9Sstevel@tonic-gate */ 3487c478bd9Sstevel@tonic-gate fp = fopen(exceptfile, "r"); 3497c478bd9Sstevel@tonic-gate if (fp == NULL) { 3507c478bd9Sstevel@tonic-gate warn("cannot open exception file \"%s\"", exceptfile); 3517c478bd9Sstevel@tonic-gate goto fail; 3527c478bd9Sstevel@tonic-gate } 3537c478bd9Sstevel@tonic-gate 3547c478bd9Sstevel@tonic-gate while (fgets(line, sizeof (line), fp) != NULL) { 3557c478bd9Sstevel@tonic-gate newline = strrchr(line, '\n'); 3567c478bd9Sstevel@tonic-gate if (newline != NULL) 3577c478bd9Sstevel@tonic-gate *newline = '\0'; 3587c478bd9Sstevel@tonic-gate 3597c478bd9Sstevel@tonic-gate for (i = 0; isspace(line[i]); i++) 3607c478bd9Sstevel@tonic-gate ; 3617c478bd9Sstevel@tonic-gate 3627c478bd9Sstevel@tonic-gate if (line[i] == '#' || line[i] == '\0') 3637c478bd9Sstevel@tonic-gate continue; 3647c478bd9Sstevel@tonic-gate 3657c478bd9Sstevel@tonic-gate if (pnset_add(pnsetp, line) == 0) { 3667c478bd9Sstevel@tonic-gate (void) fclose(fp); 3677c478bd9Sstevel@tonic-gate goto fail; 3687c478bd9Sstevel@tonic-gate } 3697c478bd9Sstevel@tonic-gate } 3707c478bd9Sstevel@tonic-gate 3717c478bd9Sstevel@tonic-gate (void) fclose(fp); 3727c478bd9Sstevel@tonic-gate return (pnsetp); 3737c478bd9Sstevel@tonic-gate fail: 374*9730304dSmeem pnset_free(pnsetp); 3757c478bd9Sstevel@tonic-gate return (NULL); 3767c478bd9Sstevel@tonic-gate } 3777c478bd9Sstevel@tonic-gate 3787c478bd9Sstevel@tonic-gate /* 3797c478bd9Sstevel@tonic-gate * FTW callback: print `path' if it's older than `tstamp' and not in `exsetp'. 3807c478bd9Sstevel@tonic-gate */ 3817c478bd9Sstevel@tonic-gate static int 3827c478bd9Sstevel@tonic-gate checkpath(const char *path, const struct stat *statp, int type, 3837c478bd9Sstevel@tonic-gate struct FTW *ftwp) 3847c478bd9Sstevel@tonic-gate { 3857c478bd9Sstevel@tonic-gate switch (type) { 3867c478bd9Sstevel@tonic-gate case FTW_F: 3877c478bd9Sstevel@tonic-gate /* 3887c478bd9Sstevel@tonic-gate * Skip if the file is referenced or in the exception list. 3897c478bd9Sstevel@tonic-gate */ 3907c478bd9Sstevel@tonic-gate if (statp->st_atime >= tstamp || pnset_check(exsetp, path)) 3917c478bd9Sstevel@tonic-gate return (0); 3927c478bd9Sstevel@tonic-gate 3937c478bd9Sstevel@tonic-gate /* 394*9730304dSmeem * If requested, restrict ourselves to unreferenced files 395*9730304dSmeem * under SCM control. 3967c478bd9Sstevel@tonic-gate */ 397*9730304dSmeem if (scm == NULL || scm->checkfunc(path, ftwp)) 3987c478bd9Sstevel@tonic-gate (void) puts(path); 3997c478bd9Sstevel@tonic-gate return (0); 4007c478bd9Sstevel@tonic-gate 4017c478bd9Sstevel@tonic-gate case FTW_D: 4027c478bd9Sstevel@tonic-gate /* 4037c478bd9Sstevel@tonic-gate * Prune any directories in the exception list. 4047c478bd9Sstevel@tonic-gate */ 405*9730304dSmeem if (pnset_check(exsetp, path)) { 4067c478bd9Sstevel@tonic-gate ftwp->quit = FTW_PRUNE; 4077c478bd9Sstevel@tonic-gate return (0); 408*9730304dSmeem } 409*9730304dSmeem 410*9730304dSmeem /* 411*9730304dSmeem * If necessary, advise the SCM logic of our new directory. 412*9730304dSmeem */ 413*9730304dSmeem if (scm != NULL && scm->chdirfunc != NULL) 414*9730304dSmeem scm->chdirfunc(path); 415*9730304dSmeem 416*9730304dSmeem return (0); 4177c478bd9Sstevel@tonic-gate 4187c478bd9Sstevel@tonic-gate case FTW_DNR: 4197c478bd9Sstevel@tonic-gate warn("cannot read \"%s\"", path); 4207c478bd9Sstevel@tonic-gate return (0); 4217c478bd9Sstevel@tonic-gate 4227c478bd9Sstevel@tonic-gate case FTW_NS: 4237c478bd9Sstevel@tonic-gate warn("cannot stat \"%s\"", path); 4247c478bd9Sstevel@tonic-gate return (0); 4257c478bd9Sstevel@tonic-gate 4267c478bd9Sstevel@tonic-gate default: 4277c478bd9Sstevel@tonic-gate break; 4287c478bd9Sstevel@tonic-gate } 4297c478bd9Sstevel@tonic-gate 4307c478bd9Sstevel@tonic-gate return (0); 4317c478bd9Sstevel@tonic-gate } 4327c478bd9Sstevel@tonic-gate 4337c478bd9Sstevel@tonic-gate /* 4347c478bd9Sstevel@tonic-gate * Add `path' to the pnset_t pointed to by `pnsetp'. 4357c478bd9Sstevel@tonic-gate */ 4367c478bd9Sstevel@tonic-gate static int 4377c478bd9Sstevel@tonic-gate pnset_add(pnset_t *pnsetp, const char *path) 4387c478bd9Sstevel@tonic-gate { 4397c478bd9Sstevel@tonic-gate char **newpaths; 440*9730304dSmeem unsigned int maxpaths; 4417c478bd9Sstevel@tonic-gate 4427c478bd9Sstevel@tonic-gate if (pnsetp->npath == pnsetp->maxpaths) { 443*9730304dSmeem maxpaths = (pnsetp->maxpaths == 0) ? 512 : pnsetp->maxpaths * 2; 444*9730304dSmeem newpaths = realloc(pnsetp->paths, sizeof (char *) * maxpaths); 4457c478bd9Sstevel@tonic-gate if (newpaths == NULL) 4467c478bd9Sstevel@tonic-gate return (0); 4477c478bd9Sstevel@tonic-gate pnsetp->paths = newpaths; 448*9730304dSmeem pnsetp->maxpaths = maxpaths; 4497c478bd9Sstevel@tonic-gate } 4507c478bd9Sstevel@tonic-gate 4517c478bd9Sstevel@tonic-gate pnsetp->paths[pnsetp->npath] = strdup(path); 4527c478bd9Sstevel@tonic-gate if (pnsetp->paths[pnsetp->npath] == NULL) 4537c478bd9Sstevel@tonic-gate return (0); 4547c478bd9Sstevel@tonic-gate 4557c478bd9Sstevel@tonic-gate pnsetp->npath++; 4567c478bd9Sstevel@tonic-gate return (1); 4577c478bd9Sstevel@tonic-gate } 4587c478bd9Sstevel@tonic-gate 4597c478bd9Sstevel@tonic-gate /* 4607c478bd9Sstevel@tonic-gate * Check `path' against the pnset_t pointed to by `pnsetp'. 4617c478bd9Sstevel@tonic-gate */ 4627c478bd9Sstevel@tonic-gate static int 4637c478bd9Sstevel@tonic-gate pnset_check(const pnset_t *pnsetp, const char *path) 4647c478bd9Sstevel@tonic-gate { 4657c478bd9Sstevel@tonic-gate unsigned int i; 4667c478bd9Sstevel@tonic-gate 4677c478bd9Sstevel@tonic-gate for (i = 0; i < pnsetp->npath; i++) { 4687c478bd9Sstevel@tonic-gate if (fnmatch(pnsetp->paths[i], path, 0) == 0) 4697c478bd9Sstevel@tonic-gate return (1); 4707c478bd9Sstevel@tonic-gate } 4717c478bd9Sstevel@tonic-gate return (0); 4727c478bd9Sstevel@tonic-gate } 4737c478bd9Sstevel@tonic-gate 4747c478bd9Sstevel@tonic-gate /* 4757c478bd9Sstevel@tonic-gate * Empty the pnset_t pointed to by `pnsetp'. 4767c478bd9Sstevel@tonic-gate */ 4777c478bd9Sstevel@tonic-gate static void 4787c478bd9Sstevel@tonic-gate pnset_empty(pnset_t *pnsetp) 4797c478bd9Sstevel@tonic-gate { 4807c478bd9Sstevel@tonic-gate while (pnsetp->npath-- != 0) 4817c478bd9Sstevel@tonic-gate free(pnsetp->paths[pnsetp->npath]); 4827c478bd9Sstevel@tonic-gate 4837c478bd9Sstevel@tonic-gate free(pnsetp->paths); 4847c478bd9Sstevel@tonic-gate pnsetp->maxpaths = 0; 4857c478bd9Sstevel@tonic-gate } 4867c478bd9Sstevel@tonic-gate 487*9730304dSmeem /* 488*9730304dSmeem * Free the pnset_t pointed to by `pnsetp'. 489*9730304dSmeem */ 490*9730304dSmeem static void 491*9730304dSmeem pnset_free(pnset_t *pnsetp) 492*9730304dSmeem { 493*9730304dSmeem if (pnsetp != NULL) { 494*9730304dSmeem pnset_empty(pnsetp); 495*9730304dSmeem free(pnsetp); 496*9730304dSmeem } 497*9730304dSmeem } 498*9730304dSmeem 4997c478bd9Sstevel@tonic-gate /* PRINTFLIKE1 */ 5007c478bd9Sstevel@tonic-gate static void 5017c478bd9Sstevel@tonic-gate warn(const char *format, ...) 5027c478bd9Sstevel@tonic-gate { 5037c478bd9Sstevel@tonic-gate va_list alist; 5047c478bd9Sstevel@tonic-gate char *errstr = strerror(errno); 5057c478bd9Sstevel@tonic-gate 5067c478bd9Sstevel@tonic-gate if (errstr == NULL) 5077c478bd9Sstevel@tonic-gate errstr = "<unknown error>"; 5087c478bd9Sstevel@tonic-gate 5097c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%s: ", progname); 5107c478bd9Sstevel@tonic-gate 5117c478bd9Sstevel@tonic-gate va_start(alist, format); 5127c478bd9Sstevel@tonic-gate (void) vfprintf(stderr, format, alist); 5137c478bd9Sstevel@tonic-gate va_end(alist); 5147c478bd9Sstevel@tonic-gate 5157c478bd9Sstevel@tonic-gate if (strrchr(format, '\n') == NULL) 5167c478bd9Sstevel@tonic-gate (void) fprintf(stderr, ": %s\n", errstr); 5177c478bd9Sstevel@tonic-gate } 5187c478bd9Sstevel@tonic-gate 5197c478bd9Sstevel@tonic-gate /* PRINTFLIKE1 */ 5207c478bd9Sstevel@tonic-gate static void 5217c478bd9Sstevel@tonic-gate die(const char *format, ...) 5227c478bd9Sstevel@tonic-gate { 5237c478bd9Sstevel@tonic-gate va_list alist; 5247c478bd9Sstevel@tonic-gate char *errstr = strerror(errno); 5257c478bd9Sstevel@tonic-gate 5267c478bd9Sstevel@tonic-gate if (errstr == NULL) 5277c478bd9Sstevel@tonic-gate errstr = "<unknown error>"; 5287c478bd9Sstevel@tonic-gate 5297c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%s: fatal: ", progname); 5307c478bd9Sstevel@tonic-gate 5317c478bd9Sstevel@tonic-gate va_start(alist, format); 5327c478bd9Sstevel@tonic-gate (void) vfprintf(stderr, format, alist); 5337c478bd9Sstevel@tonic-gate va_end(alist); 5347c478bd9Sstevel@tonic-gate 5357c478bd9Sstevel@tonic-gate if (strrchr(format, '\n') == NULL) 5367c478bd9Sstevel@tonic-gate (void) fprintf(stderr, ": %s\n", errstr); 5377c478bd9Sstevel@tonic-gate 5387c478bd9Sstevel@tonic-gate exit(EXIT_FAILURE); 5397c478bd9Sstevel@tonic-gate } 540