Print this page
3272 findunref should support git


  59         pnset_t         *manifest;
  60         char            hgpath[MAXPATHLEN];
  61         char            root[MAXPATHLEN];
  62         unsigned int    rootlen;
  63         boolean_t       rootwarn;
  64 } hgdata_t;
  65 
  66 /*
  67  * Hooks used to check if a given unreferenced file is known to an SCM
  68  * (currently Mercurial and TeamWare).
  69  */
  70 typedef int checkscm_func_t(const char *, const struct FTW *);
  71 typedef void chdirscm_func_t(const char *);
  72 
  73 typedef struct {
  74         const char      *name;
  75         checkscm_func_t *checkfunc;
  76         chdirscm_func_t *chdirfunc;
  77 } scm_t;
  78 
  79 static checkscm_func_t check_tw, check_hg;
  80 static chdirscm_func_t chdir_hg;
  81 static int      pnset_add(pnset_t *, const char *);
  82 static int      pnset_check(const pnset_t *, const char *);
  83 static void     pnset_empty(pnset_t *);
  84 static void     pnset_free(pnset_t *);
  85 static int      checkpath(const char *, const struct stat *, int, struct FTW *);
  86 static pnset_t  *make_exset(const char *);
  87 static void     warn(const char *, ...);
  88 static void     die(const char *, ...);
  89 
  90 static const scm_t scms[] = {
  91         { "tw",         check_tw,       NULL            },
  92         { "teamware",   check_tw,       NULL            },
  93         { "hg",         check_hg,       chdir_hg        },
  94         { "mercurial",  check_hg,       chdir_hg        },

  95         { NULL,         NULL,           NULL            }
  96 };
  97 
  98 static const scm_t      *scm;
  99 static hgdata_t         hgdata;

 100 static time_t           tstamp;         /* timestamp to compare files to */
 101 static pnset_t          *exsetp;        /* pathname globs to ignore */
 102 static const char       *progname;
 103 
 104 int
 105 main(int argc, char *argv[])
 106 {
 107         int c;
 108         char path[MAXPATHLEN];
 109         char subtree[MAXPATHLEN] = "./";
 110         char *tstampfile = ".build.tstamp";
 111         struct stat tsstat;
 112 
 113         progname = strrchr(argv[0], '/');
 114         if (progname == NULL)
 115                 progname = argv[0];
 116         else
 117                 progname++;
 118 
 119         while ((c = getopt(argc, argv, "as:t:S:")) != EOF) {


 133                 case 'S':
 134                         for (scm = scms; scm->name != NULL; scm++) {
 135                                 if (strcmp(scm->name, optarg) == 0)
 136                                         break;
 137                         }
 138                         if (scm->name == NULL)
 139                                 die("unsupported SCM `%s'\n", optarg);
 140                         break;
 141 
 142                 default:
 143                 case '?':
 144                         goto usage;
 145                 }
 146         }
 147 
 148         argc -= optind;
 149         argv += optind;
 150 
 151         if (argc != 2) {
 152 usage:          (void) fprintf(stderr, "usage: %s [-s <subtree>] "
 153                     "[-t <tstampfile>] [-S hg|tw] <srcroot> <exceptfile>\n",
 154                     progname);
 155                 return (EXIT_FAILURE);
 156         }
 157 
 158         /*
 159          * Interpret a relative timestamp path as relative to srcroot.
 160          */
 161         if (tstampfile[0] == '/')
 162                 (void) strlcpy(path, tstampfile, MAXPATHLEN);
 163         else
 164                 (void) snprintf(path, MAXPATHLEN, "%s/%s", argv[0], tstampfile);
 165 
 166         if (stat(path, &tsstat) == -1)
 167                 die("cannot stat timestamp file \"%s\"", path);
 168         tstamp = tsstat.st_mtime;
 169 
 170         /*
 171          * Create the exception pathname set.
 172          */
 173         exsetp = make_exset(argv[1]);


 184                 die("cannot walk tree rooted at \"%s\"\n", argv[0]);
 185 
 186         pnset_empty(exsetp);
 187         return (EXIT_SUCCESS);
 188 }
 189 
 190 /*
 191  * Load and return a pnset for the manifest for the Mercurial repo at `hgroot'.
 192  */
 193 static pnset_t *
 194 load_manifest(const char *hgroot)
 195 {
 196         FILE    *fp = NULL;
 197         char    *hgcmd = NULL;
 198         char    *newline;
 199         pnset_t *pnsetp;
 200         char    path[MAXPATHLEN];
 201 
 202         pnsetp = calloc(sizeof (pnset_t), 1);
 203         if (pnsetp == NULL ||
 204             asprintf(&hgcmd, "/usr/bin/hg manifest -R %s", hgroot) == -1)
 205                 goto fail;
 206 
 207         fp = popen(hgcmd, "r");
 208         if (fp == NULL)
 209                 goto fail;
 210 
 211         while (fgets(path, sizeof (path), fp) != NULL) {
 212                 newline = strrchr(path, '\n');
 213                 if (newline != NULL)
 214                         *newline = '\0';
 215 
 216                 if (pnset_add(pnsetp, path) == 0)
 217                         goto fail;
 218         }
 219 
 220         (void) pclose(fp);
 221         free(hgcmd);
 222         return (pnsetp);
 223 fail:
 224         warn("cannot load hg manifest at %s", hgroot);
 225         if (fp != NULL)
 226                 (void) pclose(fp);
 227         free(hgcmd);
 228         pnset_free(pnsetp);
 229         return (NULL);
 230 }
 231 







































 232 /*
 233  * If necessary, change our active manifest to be appropriate for `path'.
 234  */
 235 static void
 236 chdir_hg(const char *path)
 237 {
 238         char hgpath[MAXPATHLEN];
 239         char basepath[MAXPATHLEN];
 240         char *slash;
 241 
 242         (void) snprintf(hgpath, MAXPATHLEN, "%s/.hg", path);
 243 
 244         /*
 245          * Change our active manifest if any one of the following is true:
 246          *
 247          *   1. No manifest is loaded.  Find the nearest hgroot to load from.
 248          *
 249          *   2. A manifest is loaded, but we've moved into a directory with
 250          *      its own hgroot (e.g., usr/closed).  Load from its hgroot.
 251          *


 291                 if (hgdata.root[strlen(hgdata.root) - 1] != '/')
 292                         (void) strlcat(hgdata.root, "/", MAXPATHLEN);
 293                 hgdata.rootlen = strlen(hgdata.root);
 294         }
 295 }
 296 
 297 /*
 298  * Check if a file is under Mercurial control by checking against the manifest.
 299  */
 300 /* ARGSUSED */
 301 static int
 302 check_hg(const char *path, const struct FTW *ftwp)
 303 {
 304         /*
 305          * The manifest paths are relative to the manifest root; skip past it.
 306          */
 307         path += hgdata.rootlen;
 308 
 309         return (hgdata.manifest != NULL && pnset_check(hgdata.manifest, path));
 310 }







 311 
 312 /*
 313  * Check if a file is under TeamWare control by checking for its corresponding
 314  * SCCS "s-dot" file.
 315  */
 316 static int
 317 check_tw(const char *path, const struct FTW *ftwp)
 318 {
 319         char sccspath[MAXPATHLEN];
 320 
 321         (void) snprintf(sccspath, MAXPATHLEN, "%.*s/SCCS/s.%s", ftwp->base,
 322             path, path + ftwp->base);
 323 
 324         return (access(sccspath, F_OK) == 0);
 325 }
 326 
 327 /*
 328  * Using `exceptfile' and a built-in list of exceptions, build and return a
 329  * pnset_t consisting of all of the pathnames globs which are allowed to be
 330  * unreferenced in the source tree.




  59         pnset_t         *manifest;
  60         char            hgpath[MAXPATHLEN];
  61         char            root[MAXPATHLEN];
  62         unsigned int    rootlen;
  63         boolean_t       rootwarn;
  64 } hgdata_t;
  65 
  66 /*
  67  * Hooks used to check if a given unreferenced file is known to an SCM
  68  * (currently Mercurial and TeamWare).
  69  */
  70 typedef int checkscm_func_t(const char *, const struct FTW *);
  71 typedef void chdirscm_func_t(const char *);
  72 
  73 typedef struct {
  74         const char      *name;
  75         checkscm_func_t *checkfunc;
  76         chdirscm_func_t *chdirfunc;
  77 } scm_t;
  78 
  79 static checkscm_func_t check_tw, check_hg, check_git;
  80 static chdirscm_func_t chdir_hg, chdir_git;
  81 static int      pnset_add(pnset_t *, const char *);
  82 static int      pnset_check(const pnset_t *, const char *);
  83 static void     pnset_empty(pnset_t *);
  84 static void     pnset_free(pnset_t *);
  85 static int      checkpath(const char *, const struct stat *, int, struct FTW *);
  86 static pnset_t  *make_exset(const char *);
  87 static void     warn(const char *, ...);
  88 static void     die(const char *, ...);
  89 
  90 static const scm_t scms[] = {
  91         { "tw",         check_tw,       NULL            },
  92         { "teamware",   check_tw,       NULL            },
  93         { "hg",         check_hg,       chdir_hg        },
  94         { "mercurial",  check_hg,       chdir_hg        },
  95         { "git",        check_git,      chdir_git       },
  96         { NULL,         NULL,           NULL            }
  97 };
  98 
  99 static const scm_t      *scm;
 100 static hgdata_t         hgdata;
 101 static pnset_t          *gitmanifest = NULL;
 102 static time_t           tstamp;         /* timestamp to compare files to */
 103 static pnset_t          *exsetp;        /* pathname globs to ignore */
 104 static const char       *progname;
 105 
 106 int
 107 main(int argc, char *argv[])
 108 {
 109         int c;
 110         char path[MAXPATHLEN];
 111         char subtree[MAXPATHLEN] = "./";
 112         char *tstampfile = ".build.tstamp";
 113         struct stat tsstat;
 114 
 115         progname = strrchr(argv[0], '/');
 116         if (progname == NULL)
 117                 progname = argv[0];
 118         else
 119                 progname++;
 120 
 121         while ((c = getopt(argc, argv, "as:t:S:")) != EOF) {


 135                 case 'S':
 136                         for (scm = scms; scm->name != NULL; scm++) {
 137                                 if (strcmp(scm->name, optarg) == 0)
 138                                         break;
 139                         }
 140                         if (scm->name == NULL)
 141                                 die("unsupported SCM `%s'\n", optarg);
 142                         break;
 143 
 144                 default:
 145                 case '?':
 146                         goto usage;
 147                 }
 148         }
 149 
 150         argc -= optind;
 151         argv += optind;
 152 
 153         if (argc != 2) {
 154 usage:          (void) fprintf(stderr, "usage: %s [-s <subtree>] "
 155                     "[-t <tstampfile>] [-S hg|tw|git] <srcroot> <exceptfile>\n",
 156                     progname);
 157                 return (EXIT_FAILURE);
 158         }
 159 
 160         /*
 161          * Interpret a relative timestamp path as relative to srcroot.
 162          */
 163         if (tstampfile[0] == '/')
 164                 (void) strlcpy(path, tstampfile, MAXPATHLEN);
 165         else
 166                 (void) snprintf(path, MAXPATHLEN, "%s/%s", argv[0], tstampfile);
 167 
 168         if (stat(path, &tsstat) == -1)
 169                 die("cannot stat timestamp file \"%s\"", path);
 170         tstamp = tsstat.st_mtime;
 171 
 172         /*
 173          * Create the exception pathname set.
 174          */
 175         exsetp = make_exset(argv[1]);


 186                 die("cannot walk tree rooted at \"%s\"\n", argv[0]);
 187 
 188         pnset_empty(exsetp);
 189         return (EXIT_SUCCESS);
 190 }
 191 
 192 /*
 193  * Load and return a pnset for the manifest for the Mercurial repo at `hgroot'.
 194  */
 195 static pnset_t *
 196 load_manifest(const char *hgroot)
 197 {
 198         FILE    *fp = NULL;
 199         char    *hgcmd = NULL;
 200         char    *newline;
 201         pnset_t *pnsetp;
 202         char    path[MAXPATHLEN];
 203 
 204         pnsetp = calloc(sizeof (pnset_t), 1);
 205         if (pnsetp == NULL ||
 206             asprintf(&hgcmd, "hg manifest -R %s", hgroot) == -1)
 207                 goto fail;
 208 
 209         fp = popen(hgcmd, "r");
 210         if (fp == NULL)
 211                 goto fail;
 212 
 213         while (fgets(path, sizeof (path), fp) != NULL) {
 214                 newline = strrchr(path, '\n');
 215                 if (newline != NULL)
 216                         *newline = '\0';
 217 
 218                 if (pnset_add(pnsetp, path) == 0)
 219                         goto fail;
 220         }
 221 
 222         (void) pclose(fp);
 223         free(hgcmd);
 224         return (pnsetp);
 225 fail:
 226         warn("cannot load hg manifest at %s", hgroot);
 227         if (fp != NULL)
 228                 (void) pclose(fp);
 229         free(hgcmd);
 230         pnset_free(pnsetp);
 231         return (NULL);
 232 }
 233 
 234 static void
 235 chdir_git(const char *path)
 236 {
 237         FILE *fp = NULL;
 238         char *gitcmd = NULL;
 239         char *newline;
 240         char fn[MAXPATHLEN];
 241         pnset_t *pnsetp;
 242 
 243         pnsetp = calloc(sizeof (pnset_t), 1);
 244         if ((pnsetp == NULL) ||
 245             (asprintf(&gitcmd, "git ls-files %s", path) == -1))
 246                 goto fail;
 247 
 248         if ((fp = popen(gitcmd, "r")) == NULL)
 249                 goto fail;
 250 
 251         while (fgets(fn, sizeof (fn), fp) != NULL) {
 252                 if ((newline = strrchr(fn, '\n')) != NULL)
 253                         *newline = '\0';
 254 
 255                 if (pnset_add(pnsetp, fn) == 0)
 256                         goto fail;
 257         }
 258 
 259         (void) pclose(fp);
 260         free(gitcmd);
 261         gitmanifest = pnsetp;
 262         return;
 263 fail:
 264         warn("cannot load git manifest");
 265         if (fp != NULL)
 266                 (void) pclose(fp);
 267         if (pnsetp != NULL)
 268                 free(pnsetp);
 269         if (gitcmd != NULL)
 270                 free(gitcmd);
 271 }
 272 
 273 /*
 274  * If necessary, change our active manifest to be appropriate for `path'.
 275  */
 276 static void
 277 chdir_hg(const char *path)
 278 {
 279         char hgpath[MAXPATHLEN];
 280         char basepath[MAXPATHLEN];
 281         char *slash;
 282 
 283         (void) snprintf(hgpath, MAXPATHLEN, "%s/.hg", path);
 284 
 285         /*
 286          * Change our active manifest if any one of the following is true:
 287          *
 288          *   1. No manifest is loaded.  Find the nearest hgroot to load from.
 289          *
 290          *   2. A manifest is loaded, but we've moved into a directory with
 291          *      its own hgroot (e.g., usr/closed).  Load from its hgroot.
 292          *


 332                 if (hgdata.root[strlen(hgdata.root) - 1] != '/')
 333                         (void) strlcat(hgdata.root, "/", MAXPATHLEN);
 334                 hgdata.rootlen = strlen(hgdata.root);
 335         }
 336 }
 337 
 338 /*
 339  * Check if a file is under Mercurial control by checking against the manifest.
 340  */
 341 /* ARGSUSED */
 342 static int
 343 check_hg(const char *path, const struct FTW *ftwp)
 344 {
 345         /*
 346          * The manifest paths are relative to the manifest root; skip past it.
 347          */
 348         path += hgdata.rootlen;
 349 
 350         return (hgdata.manifest != NULL && pnset_check(hgdata.manifest, path));
 351 }
 352 /* ARGSUSED */
 353 static int
 354 check_git(const char *path, const struct FTW *ftwp)
 355 {
 356         path += 2;              /* Skip "./" */
 357         return (gitmanifest != NULL && pnset_check(gitmanifest, path));
 358 }
 359 
 360 /*
 361  * Check if a file is under TeamWare control by checking for its corresponding
 362  * SCCS "s-dot" file.
 363  */
 364 static int
 365 check_tw(const char *path, const struct FTW *ftwp)
 366 {
 367         char sccspath[MAXPATHLEN];
 368 
 369         (void) snprintf(sccspath, MAXPATHLEN, "%.*s/SCCS/s.%s", ftwp->base,
 370             path, path + ftwp->base);
 371 
 372         return (access(sccspath, F_OK) == 0);
 373 }
 374 
 375 /*
 376  * Using `exceptfile' and a built-in list of exceptions, build and return a
 377  * pnset_t consisting of all of the pathnames globs which are allowed to be
 378  * unreferenced in the source tree.