1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright (c) 2001 by Sun Microsystems, Inc. 24 * All rights reserved. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Finds all unreferenced files in a source tree that do not match a list of 31 * permitted pathnames. 32 */ 33 34 #include <ctype.h> 35 #include <errno.h> 36 #include <fnmatch.h> 37 #include <ftw.h> 38 #include <stdarg.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <time.h> 43 #include <unistd.h> 44 #include <sys/param.h> 45 #include <sys/stat.h> 46 #include <sys/types.h> 47 48 /* 49 * Pathname set: a simple datatype for storing pathname pattern globs and 50 * for checking whether a given pathname is matched by a pattern glob in 51 * the set. 52 */ 53 typedef struct { 54 char **paths; 55 unsigned int npath; 56 unsigned int maxpaths; 57 } pnset_t; 58 59 static int pnset_add(pnset_t *, const char *); 60 static int pnset_check(const pnset_t *, const char *); 61 static void pnset_empty(pnset_t *); 62 static int checkpath(const char *, const struct stat *, int, struct FTW *); 63 static pnset_t *make_exset(const char *); 64 static void warn(const char *, ...); 65 static void die(const char *, ...); 66 67 static time_t tstamp; /* timestamp to compare files to */ 68 static pnset_t *exsetp; /* pathname globs to ignore */ 69 static const char *progname; 70 static boolean_t allfiles = B_FALSE; 71 72 int 73 main(int argc, char *argv[]) 74 { 75 int c; 76 char path[MAXPATHLEN]; 77 char subtree[MAXPATHLEN] = "./"; 78 char *tstampfile = ".build.tstamp"; 79 struct stat tsstat; 80 81 progname = strrchr(argv[0], '/'); 82 if (progname == NULL) 83 progname = argv[0]; 84 else 85 progname++; 86 87 while ((c = getopt(argc, argv, "as:t:")) != EOF) { 88 switch (c) { 89 case 'a': 90 allfiles = B_TRUE; 91 break; 92 93 case 's': 94 (void) strlcat(subtree, optarg, MAXPATHLEN); 95 break; 96 97 case 't': 98 tstampfile = optarg; 99 break; 100 101 default: 102 case '?': 103 goto usage; 104 } 105 } 106 107 argc -= optind; 108 argv += optind; 109 110 if (argc != 2) { 111 usage: (void) fprintf(stderr, "usage: %s [-a] [-s subtree] " 112 "[-t tstampfile] srcroot exceptfile\n", progname); 113 return (EXIT_FAILURE); 114 } 115 116 /* 117 * Interpret a relative timestamp path as relative to srcroot. 118 */ 119 if (tstampfile[0] == '/') 120 (void) strlcpy(path, tstampfile, MAXPATHLEN); 121 else 122 (void) snprintf(path, MAXPATHLEN, "%s/%s", argv[0], tstampfile); 123 124 if (stat(path, &tsstat) == -1) 125 die("cannot stat timestamp file \"%s\"", path); 126 tstamp = tsstat.st_mtime; 127 128 /* 129 * Create the exception pathname set. 130 */ 131 exsetp = make_exset(argv[1]); 132 if (exsetp == NULL) 133 die("cannot make exception pathname set\n"); 134 135 /* 136 * Walk the specified subtree of the tree rooted at argv[0]. 137 */ 138 (void) chdir(argv[0]); 139 if (nftw(subtree, checkpath, 100, FTW_PHYS) != 0) 140 die("cannot walk tree rooted at \"%s\"\n", argv[0]); 141 142 pnset_empty(exsetp); 143 return (EXIT_SUCCESS); 144 } 145 146 /* 147 * Using `exceptfile' and a built-in list of exceptions, build and return a 148 * pnset_t consisting of all of the pathnames globs which are allowed to be 149 * unreferenced in the source tree. 150 */ 151 static pnset_t * 152 make_exset(const char *exceptfile) 153 { 154 FILE *fp; 155 char line[MAXPATHLEN]; 156 char *newline; 157 pnset_t *pnsetp; 158 unsigned int i; 159 char *builtin[] = { "*/SCCS", "*/.del-*", "*/.make.*", 160 "*.flg", NULL }; 161 162 pnsetp = calloc(sizeof (pnset_t), 1); 163 if (pnsetp == NULL) 164 return (NULL); 165 166 /* 167 * Add the built-in exceptions. 168 */ 169 for (i = 0; builtin[i] != NULL; i++) { 170 if (pnset_add(pnsetp, builtin[i]) == 0) 171 goto fail; 172 } 173 174 /* 175 * Add any exceptions from the file. 176 */ 177 fp = fopen(exceptfile, "r"); 178 if (fp == NULL) { 179 warn("cannot open exception file \"%s\"", exceptfile); 180 goto fail; 181 } 182 183 while (fgets(line, sizeof (line), fp) != NULL) { 184 newline = strrchr(line, '\n'); 185 if (newline != NULL) 186 *newline = '\0'; 187 188 for (i = 0; isspace(line[i]); i++) 189 ; 190 191 if (line[i] == '#' || line[i] == '\0') 192 continue; 193 194 if (pnset_add(pnsetp, line) == 0) { 195 (void) fclose(fp); 196 goto fail; 197 } 198 } 199 200 (void) fclose(fp); 201 return (pnsetp); 202 fail: 203 pnset_empty(pnsetp); 204 free(pnsetp); 205 return (NULL); 206 } 207 208 /* 209 * FTW callback: print `path' if it's older than `tstamp' and not in `exsetp'. 210 */ 211 static int 212 checkpath(const char *path, const struct stat *statp, int type, 213 struct FTW *ftwp) 214 { 215 char sccspath[MAXPATHLEN]; 216 217 switch (type) { 218 case FTW_F: 219 /* 220 * Skip if the file is referenced or in the exception list. 221 */ 222 if (statp->st_atime >= tstamp || pnset_check(exsetp, path)) 223 return (0); 224 225 /* 226 * If not explicitly checking all files, restrict ourselves 227 * to unreferenced files under SCCS control. 228 */ 229 if (!allfiles) { 230 (void) snprintf(sccspath, MAXPATHLEN, "%.*s/SCCS/s.%s", 231 ftwp->base, path, path + ftwp->base); 232 233 if (access(sccspath, F_OK) == -1) 234 return (0); 235 } 236 237 (void) puts(path); 238 return (0); 239 240 case FTW_D: 241 /* 242 * Prune any directories in the exception list. 243 */ 244 if (pnset_check(exsetp, path)) 245 ftwp->quit = FTW_PRUNE; 246 return (0); 247 248 case FTW_DNR: 249 warn("cannot read \"%s\"", path); 250 return (0); 251 252 case FTW_NS: 253 warn("cannot stat \"%s\"", path); 254 return (0); 255 256 default: 257 break; 258 } 259 260 return (0); 261 } 262 263 /* 264 * Add `path' to the pnset_t pointed to by `pnsetp'. 265 */ 266 static int 267 pnset_add(pnset_t *pnsetp, const char *path) 268 { 269 char **newpaths; 270 271 if (pnsetp->npath == pnsetp->maxpaths) { 272 newpaths = realloc(pnsetp->paths, sizeof (const char *) * 273 (pnsetp->maxpaths + 15)); 274 if (newpaths == NULL) 275 return (0); 276 pnsetp->paths = newpaths; 277 pnsetp->maxpaths += 15; 278 } 279 280 pnsetp->paths[pnsetp->npath] = strdup(path); 281 if (pnsetp->paths[pnsetp->npath] == NULL) 282 return (0); 283 284 pnsetp->npath++; 285 return (1); 286 } 287 288 /* 289 * Check `path' against the pnset_t pointed to by `pnsetp'. 290 */ 291 static int 292 pnset_check(const pnset_t *pnsetp, const char *path) 293 { 294 unsigned int i; 295 296 for (i = 0; i < pnsetp->npath; i++) { 297 if (fnmatch(pnsetp->paths[i], path, 0) == 0) 298 return (1); 299 } 300 return (0); 301 } 302 303 /* 304 * Empty the pnset_t pointed to by `pnsetp'. 305 */ 306 static void 307 pnset_empty(pnset_t *pnsetp) 308 { 309 while (pnsetp->npath-- != 0) 310 free(pnsetp->paths[pnsetp->npath]); 311 312 free(pnsetp->paths); 313 pnsetp->maxpaths = 0; 314 } 315 316 /* PRINTFLIKE1 */ 317 static void 318 warn(const char *format, ...) 319 { 320 va_list alist; 321 char *errstr = strerror(errno); 322 323 if (errstr == NULL) 324 errstr = "<unknown error>"; 325 326 (void) fprintf(stderr, "%s: ", progname); 327 328 va_start(alist, format); 329 (void) vfprintf(stderr, format, alist); 330 va_end(alist); 331 332 if (strrchr(format, '\n') == NULL) 333 (void) fprintf(stderr, ": %s\n", errstr); 334 } 335 336 /* PRINTFLIKE1 */ 337 static void 338 die(const char *format, ...) 339 { 340 va_list alist; 341 char *errstr = strerror(errno); 342 343 if (errstr == NULL) 344 errstr = "<unknown error>"; 345 346 (void) fprintf(stderr, "%s: fatal: ", progname); 347 348 va_start(alist, format); 349 (void) vfprintf(stderr, format, alist); 350 va_end(alist); 351 352 if (strrchr(format, '\n') == NULL) 353 (void) fprintf(stderr, ": %s\n", errstr); 354 355 exit(EXIT_FAILURE); 356 } 357