diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/tools/findunref/findunref.c | |
download | illumos-gate-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/tools/findunref/findunref.c')
-rw-r--r-- | usr/src/tools/findunref/findunref.c | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/usr/src/tools/findunref/findunref.c b/usr/src/tools/findunref/findunref.c new file mode 100644 index 0000000000..a1aab7eca8 --- /dev/null +++ b/usr/src/tools/findunref/findunref.c @@ -0,0 +1,356 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Finds all unreferenced files in a source tree that do not match a list of + * permitted pathnames. + */ + +#include <ctype.h> +#include <errno.h> +#include <fnmatch.h> +#include <ftw.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/types.h> + +/* + * Pathname set: a simple datatype for storing pathname pattern globs and + * for checking whether a given pathname is matched by a pattern glob in + * the set. + */ +typedef struct { + char **paths; + unsigned int npath; + unsigned int maxpaths; +} pnset_t; + +static int pnset_add(pnset_t *, const char *); +static int pnset_check(const pnset_t *, const char *); +static void pnset_empty(pnset_t *); +static int checkpath(const char *, const struct stat *, int, struct FTW *); +static pnset_t *make_exset(const char *); +static void warn(const char *, ...); +static void die(const char *, ...); + +static time_t tstamp; /* timestamp to compare files to */ +static pnset_t *exsetp; /* pathname globs to ignore */ +static const char *progname; +static boolean_t allfiles = B_FALSE; + +int +main(int argc, char *argv[]) +{ + int c; + char path[MAXPATHLEN]; + char subtree[MAXPATHLEN] = "./"; + char *tstampfile = ".build.tstamp"; + struct stat tsstat; + + progname = strrchr(argv[0], '/'); + if (progname == NULL) + progname = argv[0]; + else + progname++; + + while ((c = getopt(argc, argv, "as:t:")) != EOF) { + switch (c) { + case 'a': + allfiles = B_TRUE; + break; + + case 's': + (void) strlcat(subtree, optarg, MAXPATHLEN); + break; + + case 't': + tstampfile = optarg; + break; + + default: + case '?': + goto usage; + } + } + + argc -= optind; + argv += optind; + + if (argc != 2) { +usage: (void) fprintf(stderr, "usage: %s [-a] [-s subtree] " + "[-t tstampfile] srcroot exceptfile\n", progname); + return (EXIT_FAILURE); + } + + /* + * Interpret a relative timestamp path as relative to srcroot. + */ + if (tstampfile[0] == '/') + (void) strlcpy(path, tstampfile, MAXPATHLEN); + else + (void) snprintf(path, MAXPATHLEN, "%s/%s", argv[0], tstampfile); + + if (stat(path, &tsstat) == -1) + die("cannot stat timestamp file \"%s\"", path); + tstamp = tsstat.st_mtime; + + /* + * Create the exception pathname set. + */ + exsetp = make_exset(argv[1]); + if (exsetp == NULL) + die("cannot make exception pathname set\n"); + + /* + * Walk the specified subtree of the tree rooted at argv[0]. + */ + (void) chdir(argv[0]); + if (nftw(subtree, checkpath, 100, FTW_PHYS) != 0) + die("cannot walk tree rooted at \"%s\"\n", argv[0]); + + pnset_empty(exsetp); + return (EXIT_SUCCESS); +} + +/* + * Using `exceptfile' and a built-in list of exceptions, build and return a + * pnset_t consisting of all of the pathnames globs which are allowed to be + * unreferenced in the source tree. + */ +static pnset_t * +make_exset(const char *exceptfile) +{ + FILE *fp; + char line[MAXPATHLEN]; + char *newline; + pnset_t *pnsetp; + unsigned int i; + char *builtin[] = { "*/SCCS", "*/.del-*", "*/.make.*", + "*.flg", NULL }; + + pnsetp = calloc(sizeof (pnset_t), 1); + if (pnsetp == NULL) + return (NULL); + + /* + * Add the built-in exceptions. + */ + for (i = 0; builtin[i] != NULL; i++) { + if (pnset_add(pnsetp, builtin[i]) == 0) + goto fail; + } + + /* + * Add any exceptions from the file. + */ + fp = fopen(exceptfile, "r"); + if (fp == NULL) { + warn("cannot open exception file \"%s\"", exceptfile); + goto fail; + } + + while (fgets(line, sizeof (line), fp) != NULL) { + newline = strrchr(line, '\n'); + if (newline != NULL) + *newline = '\0'; + + for (i = 0; isspace(line[i]); i++) + ; + + if (line[i] == '#' || line[i] == '\0') + continue; + + if (pnset_add(pnsetp, line) == 0) { + (void) fclose(fp); + goto fail; + } + } + + (void) fclose(fp); + return (pnsetp); +fail: + pnset_empty(pnsetp); + free(pnsetp); + return (NULL); +} + +/* + * FTW callback: print `path' if it's older than `tstamp' and not in `exsetp'. + */ +static int +checkpath(const char *path, const struct stat *statp, int type, + struct FTW *ftwp) +{ + char sccspath[MAXPATHLEN]; + + switch (type) { + case FTW_F: + /* + * Skip if the file is referenced or in the exception list. + */ + if (statp->st_atime >= tstamp || pnset_check(exsetp, path)) + return (0); + + /* + * If not explicitly checking all files, restrict ourselves + * to unreferenced files under SCCS control. + */ + if (!allfiles) { + (void) snprintf(sccspath, MAXPATHLEN, "%.*s/SCCS/s.%s", + ftwp->base, path, path + ftwp->base); + + if (access(sccspath, F_OK) == -1) + return (0); + } + + (void) puts(path); + return (0); + + case FTW_D: + /* + * Prune any directories in the exception list. + */ + if (pnset_check(exsetp, path)) + ftwp->quit = FTW_PRUNE; + return (0); + + case FTW_DNR: + warn("cannot read \"%s\"", path); + return (0); + + case FTW_NS: + warn("cannot stat \"%s\"", path); + return (0); + + default: + break; + } + + return (0); +} + +/* + * Add `path' to the pnset_t pointed to by `pnsetp'. + */ +static int +pnset_add(pnset_t *pnsetp, const char *path) +{ + char **newpaths; + + if (pnsetp->npath == pnsetp->maxpaths) { + newpaths = realloc(pnsetp->paths, sizeof (const char *) * + (pnsetp->maxpaths + 15)); + if (newpaths == NULL) + return (0); + pnsetp->paths = newpaths; + pnsetp->maxpaths += 15; + } + + pnsetp->paths[pnsetp->npath] = strdup(path); + if (pnsetp->paths[pnsetp->npath] == NULL) + return (0); + + pnsetp->npath++; + return (1); +} + +/* + * Check `path' against the pnset_t pointed to by `pnsetp'. + */ +static int +pnset_check(const pnset_t *pnsetp, const char *path) +{ + unsigned int i; + + for (i = 0; i < pnsetp->npath; i++) { + if (fnmatch(pnsetp->paths[i], path, 0) == 0) + return (1); + } + return (0); +} + +/* + * Empty the pnset_t pointed to by `pnsetp'. + */ +static void +pnset_empty(pnset_t *pnsetp) +{ + while (pnsetp->npath-- != 0) + free(pnsetp->paths[pnsetp->npath]); + + free(pnsetp->paths); + pnsetp->maxpaths = 0; +} + +/* PRINTFLIKE1 */ +static void +warn(const char *format, ...) +{ + va_list alist; + char *errstr = strerror(errno); + + if (errstr == NULL) + errstr = "<unknown error>"; + + (void) fprintf(stderr, "%s: ", progname); + + va_start(alist, format); + (void) vfprintf(stderr, format, alist); + va_end(alist); + + if (strrchr(format, '\n') == NULL) + (void) fprintf(stderr, ": %s\n", errstr); +} + +/* PRINTFLIKE1 */ +static void +die(const char *format, ...) +{ + va_list alist; + char *errstr = strerror(errno); + + if (errstr == NULL) + errstr = "<unknown error>"; + + (void) fprintf(stderr, "%s: fatal: ", progname); + + va_start(alist, format); + (void) vfprintf(stderr, format, alist); + va_end(alist); + + if (strrchr(format, '\n') == NULL) + (void) fprintf(stderr, ": %s\n", errstr); + + exit(EXIT_FAILURE); +} |