/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* 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>
/*
* 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;
/*
* Data associated with the current SCM manifest.
*/
typedef struct scmdata {
unsigned int rootlen;
} scmdata_t;
/*
* Hooks used to check if a given unreferenced file is known to an SCM
* (currently Git, Mercurial and TeamWare).
*/
typedef void chdirscm_func_t(const char *);
typedef struct {
const char *name;
} scm_t;
static int pnset_check(const pnset_t *, const char *);
static void pnset_empty(pnset_t *);
static void pnset_free(pnset_t *);
static pnset_t *make_exset(const char *);
static void warn(const char *, ...);
static void die(const char *, ...);
};
static const char *progname;
int
{
int c;
else
progname++;
switch (c) {
case 'a':
/* for compatibility; now the default */
break;
case 's':
break;
case 't':
tstampfile = optarg;
break;
case 'S':
break;
}
break;
default:
case '?':
goto usage;
}
}
if (argc != 2) {
"[-t <tstampfile>] [-S hg|tw|git] <srcroot> <exceptfile>\n",
progname);
return (EXIT_FAILURE);
}
/*
* Interpret a relative timestamp path as relative to srcroot.
*/
if (tstampfile[0] == '/')
else
/*
* Create the exception pathname set.
*/
die("cannot make exception pathname set\n");
/*
* Walk the specified subtree of the tree rooted at argv[0].
*/
return (EXIT_SUCCESS);
}
/*
* Load and return a pnset for the manifest for the Mercurial repo at `hgroot'.
*/
static pnset_t *
{
char *newline;
goto fail;
goto fail;
*newline = '\0';
goto fail;
}
return (pnsetp);
fail:
return (NULL);
}
/*
* Load and return a pnset for the manifest for the Git repo at `gitroot'.
*/
static pnset_t *
{
char *newline;
goto fail;
goto fail;
*newline = '\0';
goto fail;
}
return (pnsetp);
fail:
return (NULL);
}
/*
* If necessary, change our active manifest to be appropriate for `path'.
*/
static void
{
char *slash;
/*
* Change our active manifest if any one of the following is true:
*
* 1. No manifest is loaded. Find the nearest SCM root to load from.
*
* 2. A manifest is loaded, but we've moved into a directory with
* root.
*
* 3. A manifest is loaded, but no longer applies (e.g., the manifest
*/
/*
* Walk up the directory tree looking for metadata
* subdirectories.
*/
warn("no metadata directory "
"for \"%s\"\n", path);
}
return;
}
*slash = '\0';
meta);
}
/*
* We found a directory with an SCM metadata directory; record
* it and load its manifest.
*/
/*
* The logic in check_scmdata() depends on scmdata.root having
* a single trailing slash, so only add it if it's missing.
*/
}
}
/*
* If necessary, change our active manifest to be appropriate for `path'.
*/
static void
{
}
static void
{
}
/* ARGSUSED */
static int
{
/*
* The manifest paths are relative to the manifest root; skip past it.
*/
path));
}
/*
* Check if a file is under TeamWare control by checking for its corresponding
* SCCS "s-dot" file.
*/
static int
{
}
/*
* 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 *
{
char *newline;
unsigned int i;
return (NULL);
/*
* Add any exceptions from the file.
*/
goto fail;
}
*newline = '\0';
;
continue;
goto fail;
}
}
return (pnsetp);
fail:
return (NULL);
}
/*
* FTW callback: print `path' if it's older than `tstamp' and not in `exsetp'.
*/
static int
{
switch (type) {
case FTW_F:
/*
* Skip if the file is referenced or in the exception list.
*/
return (0);
/*
* If requested, restrict ourselves to unreferenced files
* under SCM control.
*/
return (0);
case FTW_D:
/*
* Prune any directories in the exception list.
*/
return (0);
}
/*
* If necessary, advise the SCM logic of our new directory.
*/
return (0);
case FTW_DNR:
return (0);
case FTW_NS:
return (0);
default:
break;
}
return (0);
}
/*
* Add `path' to the pnset_t pointed to by `pnsetp'.
*/
static int
{
char **newpaths;
unsigned int maxpaths;
return (0);
}
return (0);
return (1);
}
/*
* Check `path' against the pnset_t pointed to by `pnsetp'.
*/
static int
{
unsigned int i;
return (1);
}
return (0);
}
/*
* Empty the pnset_t pointed to by `pnsetp'.
*/
static void
{
}
/*
* Free the pnset_t pointed to by `pnsetp'.
*/
static void
{
}
}
/* PRINTFLIKE1 */
static void
{
errstr = "<unknown error>";
}
/* PRINTFLIKE1 */
static void
{
errstr = "<unknown error>";
}