1 /* $NetBSD: xlint.c,v 1.114 2023/07/13 08:40:38 rillig Exp $ */
2
3 /*
4 * Copyright (c) 1996 Christopher G. Demetriou. All Rights Reserved.
5 * Copyright (c) 1994, 1995 Jochen Pohl
6 * All Rights Reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by Jochen Pohl for
19 * The NetBSD Project.
20 * 4. The name of the author may not be used to endorse or promote products
21 * derived from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35 #if HAVE_NBTOOL_CONFIG_H
36 #include "nbtool_config.h"
37 #endif
38
39 #include <sys/cdefs.h>
40 #if defined(__RCSID)
41 __RCSID("$NetBSD: xlint.c,v 1.114 2023/07/13 08:40:38 rillig Exp $");
42 #endif
43
44 #include <sys/param.h>
45 #include <sys/wait.h>
46 #include <sys/stat.h>
47 #include <sys/utsname.h>
48 #include <errno.h>
49 #include <stdarg.h>
50 #include <fcntl.h>
51 #include <paths.h>
52 #include <signal.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <unistd.h>
57 #include <util.h>
58
59 #include "lint.h"
60 #include "pathnames.h"
61 #include "findcc.h"
62
63 #define DEFAULT_PATH _PATH_DEFPATH
64
65 typedef struct {
66 char **items;
67 size_t len;
68 size_t cap;
69 } list;
70
71 /* Parameters for the C preprocessor. */
72 static struct {
73 list flags; /* flags always passed */
74 list lcflags; /* flags, controlled by sflag/tflag */
75 char *outfile; /* path name for preprocessed C source */
76 int outfd; /* file descriptor for outfile */
77 } cpp = { .outfd = -1 };
78
79 /* Parameters for lint1, which checks an isolated translation unit. */
80 static struct {
81 list flags;
82 list outfiles;
83 } lint1;
84
85 /* Parameters for lint2, which performs cross-translation-unit checks. */
86 static struct {
87 list flags;
88 list infiles; /* input files (without libraries) */
89 list inlibs; /* input libraries */
90 char *outlib; /* output library that will be created */
91 } lint2;
92
93 /* directory for temporary files */
94 static const char *tmpdir;
95
96 /* default libraries */
97 static list deflibs;
98
99 /* additional libraries */
100 static list libs;
101
102 /* search path for libraries */
103 static list libsrchpath;
104
105 static const char *libexec_dir;
106
107 /* flags */
108 static bool iflag, oflag, Cflag, sflag, tflag, Fflag, dflag;
109
110 /* print the commands executed to run the stages of compilation */
111 static bool Vflag;
112
113 /* filename for oflag */
114 static char *outputfn;
115
116 /* reset after first .c source has been processed */
117 static bool first = true;
118
119 /*
120 * name of a file which is currently written by a child and should
121 * be removed after abnormal termination of the child
122 */
123 static const char *currfn;
124
125 #if !defined(TARGET_PREFIX)
126 #define TARGET_PREFIX ""
127 #endif
128 static const char target_prefix[] = TARGET_PREFIX;
129
130
131 static void
list_add_ref(list * l,char * s)132 list_add_ref(list *l, char *s)
133 {
134
135 if (l->len >= l->cap) {
136 l->cap = 2 * l->len + 16;
137 l->items = xrealloc(l->items, sizeof(*l->items) * l->cap);
138 }
139 l->items[l->len++] = s;
140 }
141
142 static void
list_add(list * l,const char * s)143 list_add(list *l, const char *s)
144 {
145
146 list_add_ref(l, xstrdup(s));
147 }
148
149 static void
list_add_flag(list * l,int c)150 list_add_flag(list *l, int c)
151 {
152
153 list_add(l, (const char[3]){ '-', (char)c, '\0' });
154 }
155
156 static void
list_add_unique(list * l,const char * s)157 list_add_unique(list *l, const char *s)
158 {
159
160 for (size_t i = 0; i < l->len; i++)
161 if (strcmp(l->items[i], s) == 0)
162 return;
163 list_add(l, s);
164 }
165
166 static void
list_add_all(list * dst,const list * src)167 list_add_all(list *dst, const list *src)
168 {
169
170 for (size_t i = 0; i < src->len; i++)
171 list_add(dst, src->items[i]);
172 }
173
174 static void
list_clear(list * l)175 list_clear(list *l)
176 {
177
178 while (l->len > 0)
179 free(l->items[--l->len]);
180 }
181
182 static char *
concat2(const char * s1,const char * s2)183 concat2(const char *s1, const char *s2)
184 {
185
186 size_t len1 = strlen(s1);
187 size_t len2 = strlen(s2);
188 char *s = xmalloc(len1 + len2 + 1);
189 memcpy(s, s1, len1);
190 memcpy(s + len1, s2, len2 + 1);
191
192 return s;
193 }
194
195 static void
set_tmpdir(void)196 set_tmpdir(void)
197 {
198 const char *tmp;
199 size_t len;
200
201 tmpdir = (tmp = getenv("TMPDIR")) != NULL && (len = strlen(tmp)) != 0
202 ? concat2(tmp, tmp[len - 1] == '/' ? "" : "/")
203 : xstrdup(_PATH_TMP);
204 }
205
206 /* Clean up after a signal or at the regular end. */
207 static void __attribute__((__noreturn__))
terminate(int signo)208 terminate(int signo)
209 {
210
211 if (cpp.outfd != -1)
212 (void)close(cpp.outfd);
213 if (cpp.outfile != NULL) {
214 const char *keep_env = getenv("LINT_KEEP_CPPOUT");
215 bool keep = keep_env != NULL && (strcmp(keep_env, "yes") == 0
216 || (strcmp(keep_env, "on-error") == 0 && signo != 0));
217 if (keep)
218 (void)printf("lint: preprocessor output kept in %s\n",
219 cpp.outfile);
220 else
221 (void)remove(cpp.outfile);
222 }
223
224 for (size_t i = 0; i < lint1.outfiles.len; i++)
225 (void)remove(lint1.outfiles.items[i]);
226
227 if (lint2.outlib != NULL)
228 (void)remove(lint2.outlib);
229
230 if (currfn != NULL && currfn != cpp.outfile)
231 (void)remove(currfn);
232
233 if (signo != 0)
234 (void)raise_default_signal(signo);
235 exit(signo != 0 ? 1 : 0);
236 }
237
238 static void __attribute__((__noreturn__, __format__(__printf__, 1, 2)))
usage(const char * fmt,...)239 usage(const char *fmt, ...)
240 {
241 va_list ap;
242
243 va_start(ap, fmt);
244 (void)vfprintf(stderr, fmt, ap);
245 va_end(ap);
246 if (fmt[0] != '\0')
247 (void)fprintf(stderr, "\n");
248
249 const char *name = getprogname();
250 int indent = (int)(strlen("usage: ") + strlen(name));
251 (void)fprintf(stderr,
252 "usage: %s [-abceghprstvwxzFHPSTV] [-Alevel] [-i|-nu]\n"
253 "%*s [-Dname[=def]] [-Uname] [-Idirectory] "
254 "[-M...] [-W...] [-Z ...]\n"
255 "%*s [-ddirectory] [-Ldirectory] [-llibrary] [-ooutputfile]\n"
256 "%*s [-Bpath] [-X id,...] [-q id,...] [-R old=new] file ...\n",
257 name, indent, "", indent, "", indent, "");
258 (void)fprintf(stderr,
259 " %s [-abceghprstvwzFHPSTV] [-Alevel] -Clibrary\n"
260 "%*s [-Bpath] [-R old=new] file ...\n",
261 name, indent, "");
262 terminate(-1);
263 }
264
265 /*
266 * Returns a pointer to the last component of path after delim.
267 * Returns path if the string does not contain delim.
268 */
269 static const char *
lbasename(const char * path,int delim)270 lbasename(const char *path, int delim)
271 {
272
273 const char *base = path;
274 for (const char *p = path; *p != '\0'; p++)
275 if (*p == delim)
276 base = p + 1;
277 return base;
278 }
279
280 static bool
is_safe_shell(char ch)281 is_safe_shell(char ch)
282 {
283
284 return ch_isalnum(ch) || ch == '%' || ch == '+' || ch == ',' ||
285 ch == '-' || ch == '.' || ch == '/' || ch == ':' ||
286 ch == '=' || ch == '@' || ch == '_';
287 }
288
289 static void
print_sh_quoted(const char * s)290 print_sh_quoted(const char *s)
291 {
292
293 if (s[0] == '\0')
294 goto needs_quoting;
295 for (const char *p = s; *p != '\0'; p++)
296 if (!is_safe_shell(*p))
297 goto needs_quoting;
298
299 (void)printf("%s", s);
300 return;
301
302 needs_quoting:
303 (void)putchar('\'');
304 for (const char *p = s; *p != '\0'; p++) {
305 if (*p == '\'')
306 (void)printf("'\\''");
307 else
308 (void)putchar(*p);
309 }
310 (void)putchar('\'');
311 }
312
313 static void
run_child(const char * path,list * args,const char * crfn,int fdout)314 run_child(const char *path, list *args, const char *crfn, int fdout)
315 {
316 int status, rv, signo;
317
318 if (Vflag) {
319 print_sh_quoted(args->items[0]);
320 for (size_t i = 1; i < args->len - 1; i++) {
321 (void)printf(" ");
322 print_sh_quoted(args->items[i]);
323 }
324 (void)printf("\n");
325 }
326
327 currfn = crfn;
328
329 (void)fflush(stdout);
330
331 switch (vfork()) {
332 case -1:
333 warn("cannot fork");
334 terminate(-1);
335 /* NOTREACHED */
336 default:
337 /* parent */
338 break;
339 case 0:
340 /* child */
341
342 /* set up the standard output if necessary */
343 if (fdout != -1) {
344 (void)dup2(fdout, STDOUT_FILENO);
345 (void)close(fdout);
346 }
347 (void)execvp(path, args->items);
348 warn("cannot exec %s", path);
349 _exit(1);
350 /* NOTREACHED */
351 }
352
353 while ((rv = wait(&status)) == -1 && errno == EINTR) ;
354 if (rv == -1) {
355 warn("wait");
356 terminate(-1);
357 }
358 if (WIFSIGNALED(status)) {
359 signo = WTERMSIG(status);
360 #if HAVE_DECL_SYS_SIGNAME
361 warnx("%s got SIG%s", path, sys_signame[signo]);
362 #else
363 warnx("%s got signal %d", path, signo);
364 #endif
365 terminate(-1);
366 }
367 if (WEXITSTATUS(status) != 0)
368 terminate(-1);
369 currfn = NULL;
370 }
371
372 static void
run_cpp(const char * name)373 run_cpp(const char *name)
374 {
375
376 const char *cc = getenv("CC");
377 if (cc == NULL)
378 cc = DEFAULT_CC;
379
380 char *abs_cc = findcc(cc);
381 if (abs_cc == NULL && setenv("PATH", DEFAULT_PATH, 1) == 0)
382 abs_cc = findcc(cc);
383 if (abs_cc == NULL) {
384 (void)fprintf(stderr, "%s: %s: not found\n", getprogname(), cc);
385 exit(EXIT_FAILURE);
386 }
387
388 list args = { NULL, 0, 0 };
389 list_add_ref(&args, abs_cc);
390 list_add_all(&args, &cpp.flags);
391 list_add_all(&args, &cpp.lcflags);
392 list_add(&args, name);
393 list_add_ref(&args, NULL);
394
395 /* we reuse the same tmp file for cpp output, so rewind and truncate */
396 if (lseek(cpp.outfd, 0, SEEK_SET) != 0) {
397 warn("lseek");
398 terminate(-1);
399 }
400 if (ftruncate(cpp.outfd, 0) != 0) {
401 warn("ftruncate");
402 terminate(-1);
403 }
404
405 run_child(abs_cc, &args, cpp.outfile, cpp.outfd);
406 list_clear(&args);
407 }
408
409 static void
run_lint1(const char * out_fname)410 run_lint1(const char *out_fname)
411 {
412
413 char *abs_lint1 = libexec_dir != NULL
414 ? concat2(libexec_dir, "/lint1")
415 : xasprintf("%s/%slint1", PATH_LIBEXEC, target_prefix);
416
417 list args = { NULL, 0, 0 };
418 list_add_ref(&args, abs_lint1);
419 list_add_all(&args, &lint1.flags);
420 list_add(&args, cpp.outfile);
421 list_add(&args, out_fname);
422 list_add_ref(&args, NULL);
423
424 run_child(abs_lint1, &args, out_fname, -1);
425 list_clear(&args);
426 }
427
428 /*
429 * Read a file name from the command line
430 * and pass it through lint1 if it is a C source.
431 */
432 static void
handle_filename(const char * name)433 handle_filename(const char *name)
434 {
435 const char *bn, *suff;
436 char *ofn;
437 size_t len;
438 int fd;
439
440 bn = lbasename(name, '/');
441 suff = lbasename(bn, '.');
442
443 if (strcmp(suff, "ln") == 0) {
444 /* only for lint2 */
445 if (!iflag)
446 list_add(&lint2.infiles, name);
447 return;
448 }
449
450 if (strcmp(suff, "c") != 0 &&
451 (strncmp(bn, "llib-l", 6) != 0 || bn != suff)) {
452 warnx("unknown file type: %s", name);
453 return;
454 }
455
456 if (!iflag || !first)
457 (void)printf("%s:\n", Fflag ? name : bn);
458
459 /* build the name of the output file of lint1 */
460 if (oflag) {
461 ofn = outputfn;
462 outputfn = NULL;
463 oflag = false;
464 } else if (iflag) {
465 len = bn == suff ? strlen(bn) : (size_t)((suff - 1) - bn);
466 ofn = xasprintf("%.*s.ln", (int)len, bn);
467 } else {
468 ofn = xasprintf("%slint1.XXXXXX", tmpdir);
469 fd = mkstemp(ofn);
470 if (fd == -1) {
471 warn("can't make temp");
472 terminate(-1);
473 }
474 (void)close(fd);
475 }
476 if (!iflag)
477 list_add(&lint1.outfiles, ofn);
478
479 run_cpp(name);
480 run_lint1(ofn);
481
482 list_add(&lint2.infiles, ofn);
483 free(ofn);
484 }
485
486 static bool
file_is_readable(const char * path)487 file_is_readable(const char *path)
488 {
489 struct stat sbuf;
490
491 if (stat(path, &sbuf) == -1)
492 return false;
493 if (!S_ISREG(sbuf.st_mode))
494 return false;
495 if (access(path, R_OK) == -1)
496 return false;
497 return true;
498 }
499
500 static void
find_lib(const char * lib)501 find_lib(const char *lib)
502 {
503 char *lfn;
504
505 for (size_t i = 0; i < libsrchpath.len; i++) {
506 const char *dir = libsrchpath.items[i];
507 lfn = xasprintf("%s/llib-l%s.ln", dir, lib);
508 if (file_is_readable(lfn))
509 goto found;
510 free(lfn);
511
512 lfn = xasprintf("%s/lint/llib-l%s.ln", dir, lib);
513 if (file_is_readable(lfn))
514 goto found;
515 free(lfn);
516 }
517
518 warnx("cannot find llib-l%s.ln", lib);
519 return;
520
521 found:
522 list_add_ref(&lint2.inlibs, concat2("-l", lfn));
523 free(lfn);
524 }
525
526 static void
find_libs(const list * l)527 find_libs(const list *l)
528 {
529
530 for (size_t i = 0; i < l->len; i++)
531 find_lib(l->items[i]);
532 }
533
534 static void
run_lint2(void)535 run_lint2(void)
536 {
537
538 char *abs_lint2 = libexec_dir != NULL
539 ? concat2(libexec_dir, "/lint2")
540 : xasprintf("%s/%slint2", PATH_LIBEXEC, target_prefix);
541
542 list args = { NULL, 0, 0 };
543 list_add_ref(&args, abs_lint2);
544 list_add_all(&args, &lint2.flags);
545 list_add_all(&args, &lint2.inlibs);
546 list_add_all(&args, &lint2.infiles);
547 list_add_ref(&args, NULL);
548
549 run_child(abs_lint2, &args, lint2.outlib, -1);
550 list_clear(&args);
551 }
552
553 static void
cat(const list * srcs,const char * dest)554 cat(const list *srcs, const char *dest)
555 {
556 int ifd, ofd;
557 ssize_t rlen;
558 char buf[0x4000];
559
560 if ((ofd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1) {
561 warn("cannot open %s", dest);
562 terminate(-1);
563 }
564
565 for (size_t i = 0; i < srcs->len; i++) {
566 const char *src = srcs->items[i];
567 if ((ifd = open(src, O_RDONLY)) == -1) {
568 warn("cannot open %s", src);
569 terminate(-1);
570 }
571 do {
572 if ((rlen = read(ifd, buf, sizeof(buf))) == -1) {
573 warn("read error on %s", src);
574 terminate(-1);
575 }
576 if (write(ofd, buf, (size_t)rlen) != rlen) {
577 warn("write error on %s", dest);
578 terminate(-1);
579 }
580 } while (rlen == sizeof(buf));
581 (void)close(ifd);
582 }
583 (void)close(ofd);
584 }
585
586 int
main(int argc,char * argv[])587 main(int argc, char *argv[])
588 {
589
590 setprogname(argv[0]);
591 set_tmpdir();
592
593 cpp.outfile = concat2(tmpdir, "lint0.XXXXXX");
594 cpp.outfd = mkstemp(cpp.outfile);
595 if (cpp.outfd == -1) {
596 warn("can't make temp");
597 terminate(-1);
598 }
599
600 list_add(&cpp.flags, "-E");
601 list_add(&cpp.flags, "-x");
602 list_add(&cpp.flags, "c");
603 list_add(&cpp.flags, "-U__GNUC__");
604 list_add(&cpp.flags, "-U__PCC__");
605 list_add(&cpp.flags, "-U__SSE__");
606 list_add(&cpp.flags, "-U__SSE4_1__");
607 list_add(&cpp.flags, "-Wp,-CC");
608 list_add(&cpp.flags, "-Wcomment");
609 list_add(&cpp.flags, "-D__LINT__");
610 list_add(&cpp.flags, "-Dlint"); /* XXX don't define with -s */
611 list_add(&cpp.flags, "-D__lint");
612 list_add(&cpp.flags, "-D__lint__");
613
614 list_add(&deflibs, "c");
615
616 if (signal(SIGHUP, terminate) == SIG_IGN)
617 (void)signal(SIGHUP, SIG_IGN);
618 (void)signal(SIGINT, terminate);
619 (void)signal(SIGQUIT, terminate);
620 (void)signal(SIGTERM, terminate);
621
622 int c;
623 while ((c = getopt(argc, argv,
624 "abcd:eghil:no:pq:rstuvwxzA:B:C:D:FHI:L:M:PR:STU:VW:X:Z:")) != -1) {
625 switch (c) {
626
627 case 'a':
628 case 'b':
629 case 'c':
630 case 'e':
631 case 'g':
632 case 'r':
633 case 'v':
634 case 'w':
635 case 'z':
636 case 'P':
637 list_add_flag(&lint1.flags, c);
638 break;
639
640 case 'A':
641 case 'q':
642 case 'R':
643 case 'X':
644 list_add_flag(&lint1.flags, c);
645 list_add(&lint1.flags, optarg);
646 break;
647
648 case 'F':
649 Fflag = true;
650 /* FALLTHROUGH */
651 case 'u':
652 case 'h':
653 list_add_flag(&lint1.flags, c);
654 list_add_flag(&lint2.flags, c);
655 break;
656
657 case 'i':
658 if (Cflag)
659 usage("%c and %s flags cannot be specified "
660 "together", 'C', "i");
661 iflag = true;
662 break;
663
664 case 'n':
665 list_clear(&deflibs);
666 break;
667
668 case 'p':
669 if (deflibs.len > 0) {
670 list_clear(&deflibs);
671 list_add(&deflibs, "c");
672 }
673 list_add_flag(&lint1.flags, c);
674 break;
675
676 case 's':
677 if (tflag)
678 usage("%c and %s flags cannot be specified "
679 "together", 's', "t");
680 list_clear(&cpp.lcflags);
681 list_add(&cpp.lcflags, "-trigraphs");
682 list_add(&cpp.lcflags, "-Wtrigraphs");
683 list_add(&cpp.lcflags, "-pedantic");
684 list_add(&cpp.lcflags, "-D__STRICT_ANSI__");
685 sflag = true;
686 list_add_flag(&lint1.flags, c);
687 list_add_flag(&lint2.flags, c);
688 break;
689
690 case 'S':
691 if (tflag)
692 usage("%c and %s flags cannot be specified "
693 "together", 'S', "t");
694 list_add_flag(&lint1.flags, c);
695 break;
696
697 case 'T':
698 list_add(&cpp.flags, "-I" PATH_STRICT_BOOL_INCLUDE);
699 list_add_flag(&lint1.flags, c);
700 break;
701
702 #if !HAVE_NBTOOL_CONFIG_H
703 case 't':
704 if (sflag)
705 usage("%c and %s flags cannot be specified "
706 "together", 's', "t");
707 tflag = true;
708 list_clear(&cpp.lcflags);
709 list_add(&cpp.lcflags, "-traditional");
710 list_add(&cpp.lcflags, "-Wtraditional");
711 list_add(&cpp.lcflags, "-D" MACHINE);
712 list_add(&cpp.lcflags, "-D" MACHINE_ARCH);
713 list_add_flag(&lint1.flags, c);
714 list_add_flag(&lint2.flags, c);
715 break;
716 #endif
717
718 case 'x':
719 case 'H':
720 list_add_flag(&lint2.flags, c);
721 break;
722
723 case 'C':
724 if (Cflag)
725 usage("%c flag already specified", 'C');
726 if (oflag || iflag)
727 usage("%c and %s flags cannot be specified "
728 "together", 'C', "o or i");
729 Cflag = true;
730 list_add_flag(&lint2.flags, c);
731 list_add(&lint2.flags, optarg);
732 lint2.outlib = xasprintf("llib-l%s.ln", optarg);
733 list_clear(&deflibs);
734 break;
735
736 case 'd':
737 if (dflag)
738 usage("%c flag already specified", 'd');
739 dflag = true;
740 list_add(&cpp.flags, "--sysroot");
741 list_add(&cpp.flags, optarg);
742 break;
743
744 case 'D':
745 case 'I':
746 case 'M':
747 case 'U':
748 case 'W':
749 list_add_ref(&cpp.flags,
750 xasprintf("-%c%s", c, optarg));
751 break;
752
753 case 'l':
754 list_add_unique(&libs, optarg);
755 break;
756
757 case 'o':
758 if (oflag)
759 usage("%c flag already specified", 'o');
760 if (Cflag)
761 usage("%c and %s flags cannot be specified "
762 "together", 'C', "o");
763 oflag = true;
764 outputfn = xstrdup(optarg);
765 break;
766
767 case 'L':
768 list_add_unique(&libsrchpath, optarg);
769 break;
770
771 case 'B':
772 libexec_dir = xstrdup(optarg);
773 break;
774
775 case 'V':
776 Vflag = true;
777 break;
778
779 case 'Z':
780 list_add(&cpp.flags, optarg);
781 break;
782
783 default:
784 usage("");
785 /* NOTREACHED */
786 }
787 }
788 argc -= optind;
789 argv += optind;
790
791 /*
792 * To avoid modifying getopt(3)'s state engine midstream, we
793 * explicitly accept just a few options after the first source file.
794 *
795 * In particular, only -l<lib> and -L<libdir> (and these with a space
796 * after -l or -L) are allowed.
797 */
798 for (; argc > 0; argc--, argv++) {
799 const char *arg = argv[0];
800
801 if (arg[0] == '-') {
802 list *lp;
803
804 if (arg[1] == 'l')
805 lp = &libs;
806 else if (arg[1] == 'L')
807 lp = &libsrchpath;
808 else
809 usage("Unknown late option '%s'", arg);
810
811 if (arg[2] != '\0')
812 list_add_unique(lp, arg + 2);
813 else if (argc > 1) {
814 argc--, argv++;
815 list_add_unique(lp, argv[0]);
816 } else
817 usage("Missing argument for l or L");
818 } else {
819 handle_filename(arg);
820 first = false;
821 }
822 }
823
824 if (first)
825 usage("Missing filename");
826
827 if (iflag)
828 terminate(0);
829
830 if (!oflag) {
831 const char *ks = getenv("LIBDIR");
832 if (ks == NULL || ks[0] == '\0')
833 ks = PATH_LINTLIB;
834 list_add(&libsrchpath, ks);
835 find_libs(&libs);
836 find_libs(&deflibs);
837 }
838
839 run_lint2();
840
841 if (oflag)
842 cat(&lint2.infiles, outputfn);
843
844 if (Cflag)
845 lint2.outlib = NULL;
846
847 terminate(0);
848 /* NOTREACHED */
849 }
850