summaryrefslogtreecommitdiff
path: root/usr/src/tools/findunref/findunref.c
diff options
context:
space:
mode:
authormeem <Peter.Memishian@Sun.COM>2009-08-14 17:11:26 -0400
committermeem <Peter.Memishian@Sun.COM>2009-08-14 17:11:26 -0400
commit9730304d175c7545fecc3f6fc8731d57d3875f47 (patch)
tree47fe0dae32abea9ae49ebe9746be71fc8704b08b /usr/src/tools/findunref/findunref.c
parent79c0745d20b12387e468deefd44f647aa2451fa8 (diff)
downloadillumos-gate-9730304d175c7545fecc3f6fc8731d57d3875f47.tar.gz
6869889 findunref needs to grok mercurial
6870670 findunref should have a manpage 6870810 xref manpage is stale
Diffstat (limited to 'usr/src/tools/findunref/findunref.c')
-rw-r--r--usr/src/tools/findunref/findunref.c254
1 files changed, 224 insertions, 30 deletions
diff --git a/usr/src/tools/findunref/findunref.c b/usr/src/tools/findunref/findunref.c
index f40690fa75..11764c4963 100644
--- a/usr/src/tools/findunref/findunref.c
+++ b/usr/src/tools/findunref/findunref.c
@@ -17,13 +17,10 @@
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
- */
-
-/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Finds all unreferenced files in a source tree that do not match a list of
@@ -55,18 +52,54 @@ typedef struct {
unsigned int maxpaths;
} pnset_t;
+/*
+ * Data associated with the current Mercurial manifest.
+ */
+typedef struct hgdata {
+ pnset_t *manifest;
+ char hgpath[MAXPATHLEN];
+ char root[MAXPATHLEN];
+ unsigned int rootlen;
+ boolean_t rootwarn;
+} hgdata_t;
+
+/*
+ * Hooks used to check if a given unreferenced file is known to an SCM
+ * (currently Mercurial and TeamWare).
+ */
+typedef int checkscm_func_t(const char *, const struct FTW *);
+typedef void chdirscm_func_t(const char *);
+
+typedef struct {
+ const char *name;
+ checkscm_func_t *checkfunc;
+ chdirscm_func_t *chdirfunc;
+} scm_t;
+
+static checkscm_func_t check_tw, check_hg;
+static chdirscm_func_t chdir_hg;
static int pnset_add(pnset_t *, const char *);
static int pnset_check(const pnset_t *, const char *);
static void pnset_empty(pnset_t *);
+static void pnset_free(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 const scm_t scms[] = {
+ { "tw", check_tw, NULL },
+ { "teamware", check_tw, NULL },
+ { "hg", check_hg, chdir_hg },
+ { "mercurial", check_hg, chdir_hg },
+ { NULL, NULL, NULL }
+};
+
+static const scm_t *scm;
+static hgdata_t hgdata;
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[])
@@ -83,10 +116,10 @@ main(int argc, char *argv[])
else
progname++;
- while ((c = getopt(argc, argv, "as:t:")) != EOF) {
+ while ((c = getopt(argc, argv, "as:t:S:")) != EOF) {
switch (c) {
case 'a':
- allfiles = B_TRUE;
+ /* for compatibility; now the default */
break;
case 's':
@@ -97,6 +130,15 @@ main(int argc, char *argv[])
tstampfile = optarg;
break;
+ case 'S':
+ for (scm = scms; scm->name != NULL; scm++) {
+ if (strcmp(scm->name, optarg) == 0)
+ break;
+ }
+ if (scm->name == NULL)
+ die("unsupported SCM `%s'\n", optarg);
+ break;
+
default:
case '?':
goto usage;
@@ -107,8 +149,9 @@ main(int argc, char *argv[])
argv += optind;
if (argc != 2) {
-usage: (void) fprintf(stderr, "usage: %s [-a] [-s subtree] "
- "[-t tstampfile] srcroot exceptfile\n", progname);
+usage: (void) fprintf(stderr, "usage: %s [-s <subtree>] "
+ "[-t <tstampfile>] [-S hg|tw] <srcroot> <exceptfile>\n",
+ progname);
return (EXIT_FAILURE);
}
@@ -134,7 +177,9 @@ usage: (void) fprintf(stderr, "usage: %s [-a] [-s subtree] "
/*
* Walk the specified subtree of the tree rooted at argv[0].
*/
- (void) chdir(argv[0]);
+ if (chdir(argv[0]) == -1)
+ die("cannot change directory to \"%s\"", argv[0]);
+
if (nftw(subtree, checkpath, 100, FTW_PHYS) != 0)
die("cannot walk tree rooted at \"%s\"\n", argv[0]);
@@ -143,6 +188,143 @@ usage: (void) fprintf(stderr, "usage: %s [-a] [-s subtree] "
}
/*
+ * Load and return a pnset for the manifest for the Mercurial repo at `hgroot'.
+ */
+static pnset_t *
+load_manifest(const char *hgroot)
+{
+ FILE *fp = NULL;
+ char *hgcmd = NULL;
+ char *newline;
+ pnset_t *pnsetp;
+ char path[MAXPATHLEN];
+
+ pnsetp = calloc(sizeof (pnset_t), 1);
+ if (pnsetp == NULL ||
+ asprintf(&hgcmd, "/usr/bin/hg manifest -R %s", hgroot) == -1)
+ goto fail;
+
+ fp = popen(hgcmd, "r");
+ if (fp == NULL)
+ goto fail;
+
+ while (fgets(path, sizeof (path), fp) != NULL) {
+ newline = strrchr(path, '\n');
+ if (newline != NULL)
+ *newline = '\0';
+
+ if (pnset_add(pnsetp, path) == 0)
+ goto fail;
+ }
+
+ (void) pclose(fp);
+ free(hgcmd);
+ return (pnsetp);
+fail:
+ warn("cannot load hg manifest at %s", hgroot);
+ if (fp != NULL)
+ (void) pclose(fp);
+ free(hgcmd);
+ pnset_free(pnsetp);
+ return (NULL);
+}
+
+/*
+ * If necessary, change our active manifest to be appropriate for `path'.
+ */
+static void
+chdir_hg(const char *path)
+{
+ char hgpath[MAXPATHLEN];
+ char basepath[MAXPATHLEN];
+ char *slash;
+
+ (void) snprintf(hgpath, MAXPATHLEN, "%s/.hg", path);
+
+ /*
+ * Change our active manifest if any one of the following is true:
+ *
+ * 1. No manifest is loaded. Find the nearest hgroot to load from.
+ *
+ * 2. A manifest is loaded, but we've moved into a directory with
+ * its own hgroot (e.g., usr/closed). Load from its hgroot.
+ *
+ * 3. A manifest is loaded, but no longer applies (e.g., the manifest
+ * under usr/closed is loaded, but we've moved to usr/src).
+ */
+ if (hgdata.manifest == NULL ||
+ strcmp(hgpath, hgdata.hgpath) != 0 && access(hgpath, X_OK) == 0 ||
+ strncmp(path, hgdata.root, hgdata.rootlen - 1) != 0) {
+ pnset_free(hgdata.manifest);
+ hgdata.manifest = NULL;
+
+ (void) strlcpy(basepath, path, MAXPATHLEN);
+
+ /*
+ * Walk up the directory tree looking for .hg subdirectories.
+ */
+ while (access(hgpath, X_OK) == -1) {
+ slash = strrchr(basepath, '/');
+ if (slash == NULL) {
+ if (!hgdata.rootwarn) {
+ warn("no hg root for \"%s\"\n", path);
+ hgdata.rootwarn = B_TRUE;
+ }
+ return;
+ }
+ *slash = '\0';
+ (void) snprintf(hgpath, MAXPATHLEN, "%s/.hg", basepath);
+ }
+
+ /*
+ * We found a directory with an .hg subdirectory; record it
+ * and load its manifest.
+ */
+ (void) strlcpy(hgdata.hgpath, hgpath, MAXPATHLEN);
+ (void) strlcpy(hgdata.root, basepath, MAXPATHLEN);
+ hgdata.manifest = load_manifest(hgdata.root);
+
+ /*
+ * The logic in check_hg() depends on hgdata.root having a
+ * single trailing slash, so only add it if it's missing.
+ */
+ if (hgdata.root[strlen(hgdata.root) - 1] != '/')
+ (void) strlcat(hgdata.root, "/", MAXPATHLEN);
+ hgdata.rootlen = strlen(hgdata.root);
+ }
+}
+
+/*
+ * Check if a file is under Mercurial control by checking against the manifest.
+ */
+/* ARGSUSED */
+static int
+check_hg(const char *path, const struct FTW *ftwp)
+{
+ /*
+ * The manifest paths are relative to the manifest root; skip past it.
+ */
+ path += hgdata.rootlen;
+
+ return (hgdata.manifest != NULL && pnset_check(hgdata.manifest, path));
+}
+
+/*
+ * Check if a file is under TeamWare control by checking for its corresponding
+ * SCCS "s-dot" file.
+ */
+static int
+check_tw(const char *path, const struct FTW *ftwp)
+{
+ char sccspath[MAXPATHLEN];
+
+ (void) snprintf(sccspath, MAXPATHLEN, "%.*s/SCCS/s.%s", ftwp->base,
+ path, path + ftwp->base);
+
+ return (access(sccspath, F_OK) == 0);
+}
+
+/*
* 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.
@@ -189,8 +371,7 @@ make_exset(const char *exceptfile)
(void) fclose(fp);
return (pnsetp);
fail:
- pnset_empty(pnsetp);
- free(pnsetp);
+ pnset_free(pnsetp);
return (NULL);
}
@@ -201,8 +382,6 @@ static int
checkpath(const char *path, const struct stat *statp, int type,
struct FTW *ftwp)
{
- char sccspath[MAXPATHLEN];
-
switch (type) {
case FTW_F:
/*
@@ -212,26 +391,28 @@ checkpath(const char *path, const struct stat *statp, int type,
return (0);
/*
- * If not explicitly checking all files, restrict ourselves
- * to unreferenced files under SCCS control.
+ * If requested, restrict ourselves to unreferenced files
+ * under SCM 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);
+ if (scm == NULL || scm->checkfunc(path, ftwp))
+ (void) puts(path);
return (0);
case FTW_D:
/*
* Prune any directories in the exception list.
*/
- if (pnset_check(exsetp, path))
+ if (pnset_check(exsetp, path)) {
ftwp->quit = FTW_PRUNE;
+ return (0);
+ }
+
+ /*
+ * If necessary, advise the SCM logic of our new directory.
+ */
+ if (scm != NULL && scm->chdirfunc != NULL)
+ scm->chdirfunc(path);
+
return (0);
case FTW_DNR:
@@ -256,14 +437,15 @@ static int
pnset_add(pnset_t *pnsetp, const char *path)
{
char **newpaths;
+ unsigned int maxpaths;
if (pnsetp->npath == pnsetp->maxpaths) {
- newpaths = realloc(pnsetp->paths, sizeof (const char *) *
- (pnsetp->maxpaths + 15));
+ maxpaths = (pnsetp->maxpaths == 0) ? 512 : pnsetp->maxpaths * 2;
+ newpaths = realloc(pnsetp->paths, sizeof (char *) * maxpaths);
if (newpaths == NULL)
return (0);
pnsetp->paths = newpaths;
- pnsetp->maxpaths += 15;
+ pnsetp->maxpaths = maxpaths;
}
pnsetp->paths[pnsetp->npath] = strdup(path);
@@ -302,6 +484,18 @@ pnset_empty(pnset_t *pnsetp)
pnsetp->maxpaths = 0;
}
+/*
+ * Free the pnset_t pointed to by `pnsetp'.
+ */
+static void
+pnset_free(pnset_t *pnsetp)
+{
+ if (pnsetp != NULL) {
+ pnset_empty(pnsetp);
+ free(pnsetp);
+ }
+}
+
/* PRINTFLIKE1 */
static void
warn(const char *format, ...)