summaryrefslogtreecommitdiff
path: root/usr/src/tools/findunref/findunref.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/tools/findunref/findunref.c
downloadillumos-gate-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/tools/findunref/findunref.c')
-rw-r--r--usr/src/tools/findunref/findunref.c356
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);
+}