Print this page
10075 make usr/src/tools smatch clean
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/tools/findunref/findunref.c
+++ new/usr/src/tools/findunref/findunref.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
↓ open down ↓ |
15 lines elided |
↑ open up ↑ |
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 *
21 21 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
22 22 * Use is subject to license terms.
23 23 */
24 24
25 25 /*
26 + * Copyright (c) 2018, Joyent, Inc.
27 + */
28 +
29 +/*
26 30 * Finds all unreferenced files in a source tree that do not match a list of
27 31 * permitted pathnames.
28 32 */
29 33
30 34 #include <ctype.h>
31 35 #include <errno.h>
32 36 #include <fnmatch.h>
33 37 #include <ftw.h>
34 38 #include <stdarg.h>
35 39 #include <stdio.h>
36 40 #include <stdlib.h>
37 41 #include <string.h>
38 42 #include <time.h>
39 43 #include <unistd.h>
40 44 #include <sys/param.h>
41 45 #include <sys/stat.h>
42 46 #include <sys/types.h>
43 47
44 48 /*
45 49 * Pathname set: a simple datatype for storing pathname pattern globs and
46 50 * for checking whether a given pathname is matched by a pattern glob in
47 51 * the set.
48 52 */
49 53 typedef struct {
50 54 char **paths;
51 55 unsigned int npath;
52 56 unsigned int maxpaths;
53 57 } pnset_t;
54 58
55 59 /*
56 60 * Data associated with the current SCM manifest.
57 61 */
58 62 typedef struct scmdata {
59 63 pnset_t *manifest;
60 64 char metapath[MAXPATHLEN];
61 65 char root[MAXPATHLEN];
62 66 unsigned int rootlen;
63 67 boolean_t rootwarn;
64 68 } scmdata_t;
65 69
66 70 /*
67 71 * Hooks used to check if a given unreferenced file is known to an SCM
68 72 * (currently Git, Mercurial and TeamWare).
69 73 */
70 74 typedef int checkscm_func_t(const char *, const struct FTW *);
71 75 typedef void chdirscm_func_t(const char *);
72 76
73 77 typedef struct {
74 78 const char *name;
75 79 checkscm_func_t *checkfunc;
76 80 chdirscm_func_t *chdirfunc;
77 81 } scm_t;
78 82
79 83 static checkscm_func_t check_tw, check_scmdata;
80 84 static chdirscm_func_t chdir_hg, chdir_git;
81 85 static int pnset_add(pnset_t *, const char *);
82 86 static int pnset_check(const pnset_t *, const char *);
83 87 static void pnset_empty(pnset_t *);
84 88 static void pnset_free(pnset_t *);
85 89 static int checkpath(const char *, const struct stat *, int, struct FTW *);
86 90 static pnset_t *make_exset(const char *);
87 91 static void warn(const char *, ...);
88 92 static void die(const char *, ...);
89 93
90 94 static const scm_t scms[] = {
91 95 { "tw", check_tw, NULL },
92 96 { "teamware", check_tw, NULL },
93 97 { "hg", check_scmdata, chdir_hg },
94 98 { "mercurial", check_scmdata, chdir_hg },
95 99 { "git", check_scmdata, chdir_git },
96 100 { NULL, NULL, NULL }
97 101 };
98 102
99 103 static const scm_t *scm;
100 104 static scmdata_t scmdata;
101 105 static time_t tstamp; /* timestamp to compare files to */
102 106 static pnset_t *exsetp; /* pathname globs to ignore */
103 107 static const char *progname;
104 108
105 109 int
106 110 main(int argc, char *argv[])
107 111 {
108 112 int c;
109 113 char path[MAXPATHLEN];
110 114 char subtree[MAXPATHLEN] = "./";
111 115 char *tstampfile = ".build.tstamp";
112 116 struct stat tsstat;
113 117
114 118 progname = strrchr(argv[0], '/');
115 119 if (progname == NULL)
116 120 progname = argv[0];
117 121 else
118 122 progname++;
119 123
120 124 while ((c = getopt(argc, argv, "as:t:S:")) != EOF) {
121 125 switch (c) {
122 126 case 'a':
123 127 /* for compatibility; now the default */
124 128 break;
125 129
126 130 case 's':
127 131 (void) strlcat(subtree, optarg, MAXPATHLEN);
128 132 break;
129 133
130 134 case 't':
131 135 tstampfile = optarg;
132 136 break;
133 137
134 138 case 'S':
135 139 for (scm = scms; scm->name != NULL; scm++) {
136 140 if (strcmp(scm->name, optarg) == 0)
137 141 break;
138 142 }
139 143 if (scm->name == NULL)
140 144 die("unsupported SCM `%s'\n", optarg);
141 145 break;
142 146
143 147 default:
144 148 case '?':
145 149 goto usage;
146 150 }
147 151 }
148 152
149 153 argc -= optind;
150 154 argv += optind;
151 155
152 156 if (argc != 2) {
153 157 usage: (void) fprintf(stderr, "usage: %s [-s <subtree>] "
154 158 "[-t <tstampfile>] [-S hg|tw|git] <srcroot> <exceptfile>\n",
155 159 progname);
156 160 return (EXIT_FAILURE);
157 161 }
158 162
159 163 /*
160 164 * Interpret a relative timestamp path as relative to srcroot.
161 165 */
162 166 if (tstampfile[0] == '/')
163 167 (void) strlcpy(path, tstampfile, MAXPATHLEN);
164 168 else
165 169 (void) snprintf(path, MAXPATHLEN, "%s/%s", argv[0], tstampfile);
166 170
167 171 if (stat(path, &tsstat) == -1)
168 172 die("cannot stat timestamp file \"%s\"", path);
169 173 tstamp = tsstat.st_mtime;
170 174
171 175 /*
172 176 * Create the exception pathname set.
173 177 */
174 178 exsetp = make_exset(argv[1]);
175 179 if (exsetp == NULL)
176 180 die("cannot make exception pathname set\n");
177 181
178 182 /*
179 183 * Walk the specified subtree of the tree rooted at argv[0].
180 184 */
181 185 if (chdir(argv[0]) == -1)
182 186 die("cannot change directory to \"%s\"", argv[0]);
183 187
184 188 if (nftw(subtree, checkpath, 100, FTW_PHYS) != 0)
185 189 die("cannot walk tree rooted at \"%s\"\n", argv[0]);
186 190
187 191 pnset_empty(exsetp);
188 192 return (EXIT_SUCCESS);
189 193 }
190 194
191 195 /*
192 196 * Load and return a pnset for the manifest for the Mercurial repo at `hgroot'.
↓ open down ↓ |
157 lines elided |
↑ open up ↑ |
193 197 */
194 198 static pnset_t *
195 199 hg_manifest(const char *hgroot)
196 200 {
197 201 FILE *fp = NULL;
198 202 char *hgcmd = NULL;
199 203 char *newline;
200 204 pnset_t *pnsetp;
201 205 char path[MAXPATHLEN];
202 206
203 - pnsetp = calloc(sizeof (pnset_t), 1);
207 + pnsetp = calloc(1, sizeof (pnset_t));
204 208 if (pnsetp == NULL ||
205 209 asprintf(&hgcmd, "hg manifest -R %s", hgroot) == -1)
206 210 goto fail;
207 211
208 212 fp = popen(hgcmd, "r");
209 213 if (fp == NULL)
210 214 goto fail;
211 215
212 216 while (fgets(path, sizeof (path), fp) != NULL) {
213 217 newline = strrchr(path, '\n');
214 218 if (newline != NULL)
215 219 *newline = '\0';
216 220
217 221 if (pnset_add(pnsetp, path) == 0)
218 222 goto fail;
219 223 }
220 224
221 225 (void) pclose(fp);
222 226 free(hgcmd);
223 227 return (pnsetp);
224 228 fail:
225 229 warn("cannot load hg manifest at %s", hgroot);
226 230 if (fp != NULL)
227 231 (void) pclose(fp);
228 232 free(hgcmd);
229 233 pnset_free(pnsetp);
230 234 return (NULL);
231 235 }
232 236
233 237 /*
234 238 * Load and return a pnset for the manifest for the Git repo at `gitroot'.
↓ open down ↓ |
21 lines elided |
↑ open up ↑ |
235 239 */
236 240 static pnset_t *
237 241 git_manifest(const char *gitroot)
238 242 {
239 243 FILE *fp = NULL;
240 244 char *gitcmd = NULL;
241 245 char *newline;
242 246 pnset_t *pnsetp;
243 247 char path[MAXPATHLEN];
244 248
245 - pnsetp = calloc(sizeof (pnset_t), 1);
249 + pnsetp = calloc(1, sizeof (pnset_t));
246 250 if (pnsetp == NULL ||
247 251 asprintf(&gitcmd, "git --git-dir=%s/.git ls-files", gitroot) == -1)
248 252 goto fail;
249 253
250 254 fp = popen(gitcmd, "r");
251 255 if (fp == NULL)
252 256 goto fail;
253 257
254 258 while (fgets(path, sizeof (path), fp) != NULL) {
255 259 newline = strrchr(path, '\n');
256 260 if (newline != NULL)
257 261 *newline = '\0';
258 262
259 263 if (pnset_add(pnsetp, path) == 0)
260 264 goto fail;
261 265 }
262 266
263 267 (void) pclose(fp);
264 268 free(gitcmd);
265 269 return (pnsetp);
266 270 fail:
267 271 warn("cannot load git manifest at %s", gitroot);
268 272 if (fp != NULL)
269 273 (void) pclose(fp);
270 274 free(gitcmd);
271 275 pnset_free(pnsetp);
272 276 return (NULL);
273 277 }
274 278
275 279 /*
276 280 * If necessary, change our active manifest to be appropriate for `path'.
277 281 */
278 282 static void
279 283 chdir_scmdata(const char *path, const char *meta,
280 284 pnset_t *(*manifest_func)(const char *path))
281 285 {
282 286 char scmpath[MAXPATHLEN];
283 287 char basepath[MAXPATHLEN];
284 288 char *slash;
285 289
286 290 (void) snprintf(scmpath, MAXPATHLEN, "%s/%s", path, meta);
287 291
288 292 /*
289 293 * Change our active manifest if any one of the following is true:
290 294 *
291 295 * 1. No manifest is loaded. Find the nearest SCM root to load from.
292 296 *
293 297 * 2. A manifest is loaded, but we've moved into a directory with
294 298 * its own metadata directory (e.g., usr/closed). Load from its
295 299 * root.
296 300 *
297 301 * 3. A manifest is loaded, but no longer applies (e.g., the manifest
298 302 * under usr/closed is loaded, but we've moved to usr/src).
299 303 */
300 304 if (scmdata.manifest == NULL ||
301 305 (strcmp(scmpath, scmdata.metapath) != 0 &&
302 306 access(scmpath, X_OK) == 0) ||
303 307 strncmp(path, scmdata.root, scmdata.rootlen - 1) != 0) {
304 308 pnset_free(scmdata.manifest);
305 309 scmdata.manifest = NULL;
306 310
307 311 (void) strlcpy(basepath, path, MAXPATHLEN);
308 312
309 313 /*
310 314 * Walk up the directory tree looking for metadata
311 315 * subdirectories.
312 316 */
313 317 while (access(scmpath, X_OK) == -1) {
314 318 slash = strrchr(basepath, '/');
315 319 if (slash == NULL) {
316 320 if (!scmdata.rootwarn) {
317 321 warn("no metadata directory "
318 322 "for \"%s\"\n", path);
319 323 scmdata.rootwarn = B_TRUE;
320 324 }
321 325 return;
322 326 }
323 327 *slash = '\0';
324 328 (void) snprintf(scmpath, MAXPATHLEN, "%s/%s", basepath,
325 329 meta);
326 330 }
327 331
328 332 /*
329 333 * We found a directory with an SCM metadata directory; record
330 334 * it and load its manifest.
331 335 */
332 336 (void) strlcpy(scmdata.metapath, scmpath, MAXPATHLEN);
333 337 (void) strlcpy(scmdata.root, basepath, MAXPATHLEN);
334 338 scmdata.manifest = manifest_func(scmdata.root);
335 339
336 340 /*
337 341 * The logic in check_scmdata() depends on scmdata.root having
338 342 * a single trailing slash, so only add it if it's missing.
339 343 */
340 344 if (scmdata.root[strlen(scmdata.root) - 1] != '/')
341 345 (void) strlcat(scmdata.root, "/", MAXPATHLEN);
342 346 scmdata.rootlen = strlen(scmdata.root);
343 347 }
344 348 }
345 349
346 350 /*
347 351 * If necessary, change our active manifest to be appropriate for `path'.
348 352 */
349 353 static void
350 354 chdir_git(const char *path)
351 355 {
352 356 chdir_scmdata(path, ".git", git_manifest);
353 357 }
354 358
355 359 static void
356 360 chdir_hg(const char *path)
357 361 {
358 362 chdir_scmdata(path, ".hg", hg_manifest);
359 363 }
360 364
361 365 /* ARGSUSED */
362 366 static int
363 367 check_scmdata(const char *path, const struct FTW *ftwp)
364 368 {
365 369 /*
366 370 * The manifest paths are relative to the manifest root; skip past it.
367 371 */
368 372 path += scmdata.rootlen;
369 373
370 374 return (scmdata.manifest != NULL && pnset_check(scmdata.manifest,
371 375 path));
372 376 }
373 377
374 378 /*
375 379 * Check if a file is under TeamWare control by checking for its corresponding
376 380 * SCCS "s-dot" file.
377 381 */
378 382 static int
379 383 check_tw(const char *path, const struct FTW *ftwp)
380 384 {
381 385 char sccspath[MAXPATHLEN];
382 386
383 387 (void) snprintf(sccspath, MAXPATHLEN, "%.*s/SCCS/s.%s", ftwp->base,
384 388 path, path + ftwp->base);
385 389
386 390 return (access(sccspath, F_OK) == 0);
387 391 }
388 392
389 393 /*
390 394 * Using `exceptfile' and a built-in list of exceptions, build and return a
391 395 * pnset_t consisting of all of the pathnames globs which are allowed to be
392 396 * unreferenced in the source tree.
↓ open down ↓ |
137 lines elided |
↑ open up ↑ |
393 397 */
394 398 static pnset_t *
395 399 make_exset(const char *exceptfile)
396 400 {
397 401 FILE *fp;
398 402 char line[MAXPATHLEN];
399 403 char *newline;
400 404 pnset_t *pnsetp;
401 405 unsigned int i;
402 406
403 - pnsetp = calloc(sizeof (pnset_t), 1);
407 + pnsetp = calloc(1, sizeof (pnset_t));
404 408 if (pnsetp == NULL)
405 409 return (NULL);
406 410
407 411 /*
408 412 * Add any exceptions from the file.
409 413 */
410 414 fp = fopen(exceptfile, "r");
411 415 if (fp == NULL) {
412 416 warn("cannot open exception file \"%s\"", exceptfile);
413 417 goto fail;
414 418 }
415 419
416 420 while (fgets(line, sizeof (line), fp) != NULL) {
417 421 newline = strrchr(line, '\n');
418 422 if (newline != NULL)
419 423 *newline = '\0';
420 424
421 425 for (i = 0; isspace(line[i]); i++)
422 426 ;
423 427
424 428 if (line[i] == '#' || line[i] == '\0')
425 429 continue;
426 430
427 431 if (pnset_add(pnsetp, line) == 0) {
428 432 (void) fclose(fp);
429 433 goto fail;
430 434 }
431 435 }
432 436
433 437 (void) fclose(fp);
434 438 return (pnsetp);
435 439 fail:
436 440 pnset_free(pnsetp);
437 441 return (NULL);
438 442 }
439 443
440 444 /*
441 445 * FTW callback: print `path' if it's older than `tstamp' and not in `exsetp'.
442 446 */
443 447 static int
444 448 checkpath(const char *path, const struct stat *statp, int type,
445 449 struct FTW *ftwp)
446 450 {
447 451 switch (type) {
448 452 case FTW_F:
449 453 /*
450 454 * Skip if the file is referenced or in the exception list.
451 455 */
452 456 if (statp->st_atime >= tstamp || pnset_check(exsetp, path))
453 457 return (0);
454 458
455 459 /*
456 460 * If requested, restrict ourselves to unreferenced files
457 461 * under SCM control.
458 462 */
459 463 if (scm == NULL || scm->checkfunc(path, ftwp))
460 464 (void) puts(path);
461 465 return (0);
462 466
463 467 case FTW_D:
464 468 /*
465 469 * Prune any directories in the exception list.
466 470 */
467 471 if (pnset_check(exsetp, path)) {
468 472 ftwp->quit = FTW_PRUNE;
469 473 return (0);
470 474 }
471 475
472 476 /*
473 477 * If necessary, advise the SCM logic of our new directory.
474 478 */
475 479 if (scm != NULL && scm->chdirfunc != NULL)
476 480 scm->chdirfunc(path);
477 481
478 482 return (0);
479 483
480 484 case FTW_DNR:
481 485 warn("cannot read \"%s\"", path);
482 486 return (0);
483 487
484 488 case FTW_NS:
485 489 warn("cannot stat \"%s\"", path);
486 490 return (0);
487 491
488 492 default:
489 493 break;
490 494 }
491 495
492 496 return (0);
493 497 }
494 498
495 499 /*
496 500 * Add `path' to the pnset_t pointed to by `pnsetp'.
497 501 */
498 502 static int
499 503 pnset_add(pnset_t *pnsetp, const char *path)
500 504 {
501 505 char **newpaths;
502 506 unsigned int maxpaths;
503 507
504 508 if (pnsetp->npath == pnsetp->maxpaths) {
505 509 maxpaths = (pnsetp->maxpaths == 0) ? 512 : pnsetp->maxpaths * 2;
506 510 newpaths = realloc(pnsetp->paths, sizeof (char *) * maxpaths);
507 511 if (newpaths == NULL)
508 512 return (0);
509 513 pnsetp->paths = newpaths;
510 514 pnsetp->maxpaths = maxpaths;
511 515 }
512 516
513 517 pnsetp->paths[pnsetp->npath] = strdup(path);
514 518 if (pnsetp->paths[pnsetp->npath] == NULL)
515 519 return (0);
516 520
517 521 pnsetp->npath++;
518 522 return (1);
519 523 }
520 524
521 525 /*
522 526 * Check `path' against the pnset_t pointed to by `pnsetp'.
523 527 */
524 528 static int
525 529 pnset_check(const pnset_t *pnsetp, const char *path)
526 530 {
527 531 unsigned int i;
528 532
529 533 for (i = 0; i < pnsetp->npath; i++) {
530 534 if (fnmatch(pnsetp->paths[i], path, 0) == 0)
531 535 return (1);
532 536 }
533 537 return (0);
534 538 }
535 539
536 540 /*
537 541 * Empty the pnset_t pointed to by `pnsetp'.
538 542 */
539 543 static void
540 544 pnset_empty(pnset_t *pnsetp)
541 545 {
542 546 while (pnsetp->npath-- != 0)
543 547 free(pnsetp->paths[pnsetp->npath]);
544 548
545 549 free(pnsetp->paths);
546 550 pnsetp->maxpaths = 0;
547 551 }
548 552
549 553 /*
550 554 * Free the pnset_t pointed to by `pnsetp'.
551 555 */
552 556 static void
553 557 pnset_free(pnset_t *pnsetp)
554 558 {
555 559 if (pnsetp != NULL) {
556 560 pnset_empty(pnsetp);
557 561 free(pnsetp);
558 562 }
559 563 }
560 564
561 565 /* PRINTFLIKE1 */
562 566 static void
563 567 warn(const char *format, ...)
564 568 {
565 569 va_list alist;
566 570 char *errstr = strerror(errno);
567 571
568 572 if (errstr == NULL)
569 573 errstr = "<unknown error>";
570 574
571 575 (void) fprintf(stderr, "%s: ", progname);
572 576
573 577 va_start(alist, format);
574 578 (void) vfprintf(stderr, format, alist);
575 579 va_end(alist);
576 580
577 581 if (strrchr(format, '\n') == NULL)
578 582 (void) fprintf(stderr, ": %s\n", errstr);
579 583 }
580 584
581 585 /* PRINTFLIKE1 */
582 586 static void
583 587 die(const char *format, ...)
584 588 {
585 589 va_list alist;
586 590 char *errstr = strerror(errno);
587 591
588 592 if (errstr == NULL)
589 593 errstr = "<unknown error>";
590 594
591 595 (void) fprintf(stderr, "%s: fatal: ", progname);
592 596
593 597 va_start(alist, format);
594 598 (void) vfprintf(stderr, format, alist);
595 599 va_end(alist);
596 600
597 601 if (strrchr(format, '\n') == NULL)
598 602 (void) fprintf(stderr, ": %s\n", errstr);
599 603
600 604 exit(EXIT_FAILURE);
601 605 }
↓ open down ↓ |
188 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX