xref: /netbsd-src/usr.bin/xlint/xlint/xlint.c (revision a8016b51bcf5204ff836e46c2432dc27e5b12586)
1 /* $NetBSD: xlint.c,v 1.126 2024/12/08 17:12:01 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.126 2024/12/08 17:12:01 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 typedef struct {
64 	char	**items;
65 	size_t	len;
66 	size_t	cap;
67 } list;
68 
69 /* Parameters for the C preprocessor. */
70 static struct {
71 	list	flags;		/* flags always passed */
72 	list	lcflags;	/* flags, controlled by sflag/tflag */
73 	char	*outfile;	/* path name for preprocessed C source */
74 	int	output_fd;	/* file descriptor for outfile */
75 } cpp;
76 
77 /* Parameters for lint1, which checks an isolated translation unit. */
78 static struct {
79 	list	flags;
80 	list	outfiles;
81 } lint1;
82 
83 /* Parameters for lint2, which performs cross-translation-unit checks. */
84 static struct {
85 	list	flags;
86 	list	input_files;	/* without libraries */
87 	list	input_libraries;
88 	char	*output_library;
89 } lint2;
90 
91 static const char *tmpdir;
92 static list default_libraries;
93 static list additional_libraries;
94 static list library_search_path;
95 static const char *libexec_dir;
96 static bool Cflag, dflag, Fflag, iflag, sflag, tflag, Vflag;
97 static char *output_filename;	/* filename for -o */
98 static bool seen_filename;
99 
100 /*
101  * The file that is currently written by a child process and should be removed
102  * in case the child process fails.
103  */
104 static const char *currfn;
105 
106 #if !defined(TARGET_PREFIX)
107 #define	TARGET_PREFIX	""
108 #endif
109 static const char target_prefix[] = TARGET_PREFIX;
110 
111 
112 static void
113 list_add_ref(list *l, char *s)
114 {
115 
116 	if (l->len >= l->cap) {
117 		l->cap = 2 * l->len + 16;
118 		l->items = xrealloc(l->items, sizeof(*l->items) * l->cap);
119 	}
120 	l->items[l->len++] = s;
121 }
122 
123 static void
124 list_add(list *l, const char *s)
125 {
126 
127 	list_add_ref(l, xstrdup(s));
128 }
129 
130 static void
131 list_add_flag(list *l, int c)
132 {
133 
134 	list_add(l, (const char[3]){ '-', (char)c, '\0' });
135 }
136 
137 static void
138 list_add_unique(list *l, const char *s)
139 {
140 
141 	for (size_t i = 0; i < l->len; i++)
142 		if (strcmp(l->items[i], s) == 0)
143 			return;
144 	list_add(l, s);
145 }
146 
147 static void
148 list_add_all(list *dst, const list *src)
149 {
150 
151 	for (size_t i = 0; i < src->len; i++)
152 		list_add(dst, src->items[i]);
153 }
154 
155 static void
156 list_clear(list *l)
157 {
158 
159 	while (l->len > 0)
160 		free(l->items[--l->len]);
161 }
162 
163 static char *
164 concat2(const char *s1, const char *s2)
165 {
166 
167 	size_t len1 = strlen(s1);
168 	size_t len2 = strlen(s2);
169 	char *s = xrealloc(NULL, len1 + len2 + 1);
170 	memcpy(s, s1, len1);
171 	memcpy(s + len1, s2, len2 + 1);
172 	return s;
173 }
174 
175 static void
176 set_tmpdir(void)
177 {
178 	const char *tmp;
179 	size_t len;
180 
181 	tmpdir = (tmp = getenv("TMPDIR")) != NULL && (len = strlen(tmp)) != 0
182 	    ? concat2(tmp, tmp[len - 1] == '/' ? "" : "/")
183 	    : xstrdup(_PATH_TMP);
184 }
185 
186 /* Clean up after a signal or at the regular end. */
187 __dead static void
188 terminate(int signo)
189 {
190 
191 	if (cpp.output_fd != -1)
192 		(void)close(cpp.output_fd);
193 	if (cpp.outfile != NULL) {
194 		const char *keep_env = getenv("LINT_KEEP_CPPOUT");
195 		bool keep = keep_env != NULL && (strcmp(keep_env, "yes") == 0
196 		    || (strcmp(keep_env, "on-error") == 0 && signo != 0));
197 		if (keep)
198 			(void)printf("lint: preprocessor output kept in %s\n",
199 			    cpp.outfile);
200 		else
201 			(void)remove(cpp.outfile);
202 	}
203 
204 	for (size_t i = 0; i < lint1.outfiles.len; i++)
205 		(void)remove(lint1.outfiles.items[i]);
206 
207 	if (lint2.output_library != NULL)
208 		(void)remove(lint2.output_library);
209 
210 	if (currfn != NULL && currfn != cpp.outfile)
211 		(void)remove(currfn);
212 
213 	if (signo != 0)
214 		(void)raise_default_signal(signo);
215 	exit(signo != 0 ? 1 : 0);
216 }
217 
218 __dead __printflike(1, 2) static void
219 usage(const char *fmt, ...)
220 {
221 	va_list ap;
222 
223 	va_start(ap, fmt);
224 	(void)vfprintf(stderr, fmt, ap);
225 	va_end(ap);
226 	if (fmt[0] != '\0')
227 		(void)fprintf(stderr, "\n");
228 
229 	const char *name = getprogname();
230 	int indent = (int)(strlen("usage: ") + strlen(name));
231 	(void)fprintf(stderr,
232 	    "usage: %s [-abceghprstvwxzFHPSTV] [-Alevel] [-i|-nu]\n"
233 	    "%*s [-Dname[=def]] [-Uname] [-Idirectory] "
234 	    "[-M...] [-W...] [-Z ...]\n"
235 	    "%*s [-ddirectory] [-Ldirectory] [-llibrary] [-ooutputfile]\n"
236 	    "%*s [-Bpath] [-X id,...] [-q id,...] [-R old=new] file ...\n",
237 	    name, indent, "", indent, "", indent, "");
238 	(void)fprintf(stderr,
239 	    "       %s [-abceghprstvwzFHPSTV] [-Alevel] -Clibrary\n"
240 	    "%*s [-Bpath] [-R old=new] file ...\n",
241 	    name, indent, "");
242 	terminate(-1);
243 }
244 
245 static const char *
246 skip_last(const char *path, int delim)
247 {
248 
249 	const char *base = path;
250 	for (const char *p = path; *p != '\0'; p++)
251 		if (*p == delim)
252 			base = p + 1;
253 	return base;
254 }
255 
256 static bool
257 is_safe_shell(char ch)
258 {
259 
260 	return ch_isalnum(ch)
261 	    || ch == '%' || ch == '+' || ch == ',' || ch == '-' || ch == '.'
262 	    || ch == '/' || ch == ':' || ch == '=' || ch == '@' || ch == '_';
263 }
264 
265 static void
266 print_sh_quoted(const char *s)
267 {
268 
269 	if (s[0] == '\0')
270 		goto needs_quoting;
271 	for (const char *p = s; *p != '\0'; p++)
272 		if (!is_safe_shell(*p))
273 			goto needs_quoting;
274 
275 	(void)printf("%s", s);
276 	return;
277 
278 needs_quoting:
279 	(void)putchar('\'');
280 	for (const char *p = s; *p != '\0'; p++) {
281 		if (*p == '\'')
282 			(void)printf("'\\''");
283 		else
284 			(void)putchar(*p);
285 	}
286 	(void)putchar('\'');
287 }
288 
289 static void
290 run_child(const char *path, const list *args, const char *crfn, int fdout)
291 {
292 
293 	if (Vflag) {
294 		print_sh_quoted(args->items[0]);
295 		for (size_t i = 1; i < args->len - 1; i++) {
296 			(void)printf(" ");
297 			print_sh_quoted(args->items[i]);
298 		}
299 		(void)printf("\n");
300 	}
301 
302 	currfn = crfn;
303 
304 	(void)fflush(stdout);
305 
306 	switch (vfork()) {
307 	case -1:
308 		warn("cannot fork");
309 		terminate(-1);
310 		/* NOTREACHED */
311 	default:
312 		/* parent */
313 		break;
314 	case 0:
315 		/* child */
316 
317 		/* set up the standard output if necessary */
318 		if (fdout != -1) {
319 			(void)dup2(fdout, STDOUT_FILENO);
320 			(void)close(fdout);
321 		}
322 		(void)execvp(path, args->items);
323 		warn("cannot exec %s", path);
324 		_exit(1);
325 		/* NOTREACHED */
326 	}
327 
328 	int status, rv, signo;
329 	while ((rv = wait(&status)) == -1 && errno == EINTR)
330 		continue;
331 	if (rv == -1) {
332 		warn("wait");
333 		terminate(-1);
334 	}
335 	if (WIFSIGNALED(status)) {
336 		signo = WTERMSIG(status);
337 #if HAVE_DECL_SYS_SIGNAME
338 		warnx("%s got SIG%s", path, sys_signame[signo]);
339 #else
340 		warnx("%s got signal %d", path, signo);
341 #endif
342 		terminate(-1);
343 	}
344 	if (WEXITSTATUS(status) != 0)
345 		terminate(-1);
346 	currfn = NULL;
347 }
348 
349 static void
350 run_cpp(const char *name)
351 {
352 
353 	const char *cc = getenv("CC");
354 	if (cc == NULL)
355 		cc = DEFAULT_CC;
356 
357 	char *abs_cc = findcc(cc);
358 	if (abs_cc == NULL && setenv("PATH", _PATH_DEFPATH, 1) == 0)
359 		abs_cc = findcc(cc);
360 	if (abs_cc == NULL) {
361 		(void)fprintf(stderr, "%s: %s: not found\n", getprogname(), cc);
362 		exit(EXIT_FAILURE);
363 	}
364 
365 	list args = { NULL, 0, 0 };
366 	list_add_ref(&args, abs_cc);
367 	list_add_all(&args, &cpp.flags);
368 	list_add_all(&args, &cpp.lcflags);
369 	list_add(&args, name);
370 	list_add_ref(&args, NULL);
371 
372 	/* Rewind after a possible previous run of cpp and lint1. */
373 	if (lseek(cpp.output_fd, 0, SEEK_SET) != 0) {
374 		warn("lseek");
375 		terminate(-1);
376 	}
377 	if (ftruncate(cpp.output_fd, 0) != 0) {
378 		warn("ftruncate");
379 		terminate(-1);
380 	}
381 
382 	run_child(abs_cc, &args, cpp.outfile, cpp.output_fd);
383 	list_clear(&args);
384 }
385 
386 static void
387 run_lint1(const char *out_fname)
388 {
389 
390 	char *abs_lint1 = libexec_dir != NULL
391 	    ? concat2(libexec_dir, "/lint1")
392 	    : xasprintf("%s/%slint1", PATH_LIBEXEC, target_prefix);
393 
394 	list args = { NULL, 0, 0 };
395 	list_add_ref(&args, abs_lint1);
396 	list_add_all(&args, &lint1.flags);
397 	list_add(&args, cpp.outfile);
398 	list_add(&args, out_fname);
399 	list_add_ref(&args, NULL);
400 
401 	run_child(abs_lint1, &args, out_fname, -1);
402 	list_clear(&args);
403 }
404 
405 static void
406 handle_filename(const char *name)
407 {
408 
409 	const char *base = skip_last(name, '/');
410 	const char *suff = skip_last(base, '.');
411 
412 	if (strcmp(suff, "ln") == 0) {
413 		/* only for lint2 */
414 		if (!iflag)
415 			list_add(&lint2.input_files, name);
416 		return;
417 	}
418 
419 	if (strcmp(suff, "c") == 0)
420 		goto c_file;
421 	if (strncmp(base, "llib-l", 6) == 0 && base == suff)
422 		goto c_file;
423 	warnx("unknown file type: %s", name);
424 	return;
425 
426 c_file:
427 	if (!iflag || seen_filename)
428 		(void)printf("%s:\n", Fflag ? name : base);
429 
430 	/* build the name of the output file of lint1 */
431 	char *ofn;
432 	if (output_filename != NULL) {
433 		ofn = output_filename;
434 		output_filename = NULL;
435 	} else if (iflag) {
436 		size_t len = base == suff
437 		    ? strlen(base)
438 		    : (size_t)((suff - 1) - base);
439 		ofn = xasprintf("%.*s.ln", (int)len, base);
440 	} else {
441 		ofn = xasprintf("%slint1.XXXXXX", tmpdir);
442 		int fd = mkstemp(ofn);
443 		if (fd == -1) {
444 			warn("can't make temp");
445 			terminate(-1);
446 		}
447 		(void)close(fd);
448 	}
449 	if (!iflag)
450 		list_add(&lint1.outfiles, ofn);
451 
452 	run_cpp(name);
453 	run_lint1(ofn);
454 
455 	list_add(&lint2.input_files, ofn);
456 	free(ofn);
457 }
458 
459 static bool
460 file_is_readable(const char *path)
461 {
462 	struct stat sbuf;
463 
464 	return stat(path, &sbuf) == 0
465 	    && S_ISREG(sbuf.st_mode)
466 	    && access(path, R_OK) == 0;
467 }
468 
469 static void
470 find_lib(const char *lib)
471 {
472 	char *lfn;
473 
474 	for (size_t i = 0; i < library_search_path.len; i++) {
475 		const char *dir = library_search_path.items[i];
476 		lfn = xasprintf("%s/llib-l%s.ln", dir, lib);
477 		if (file_is_readable(lfn))
478 			goto found;
479 		free(lfn);
480 
481 		lfn = xasprintf("%s/lint/llib-l%s.ln", dir, lib);
482 		if (file_is_readable(lfn))
483 			goto found;
484 		free(lfn);
485 	}
486 
487 	warnx("cannot find llib-l%s.ln", lib);
488 	return;
489 
490 found:
491 	list_add_ref(&lint2.input_libraries, concat2("-l", lfn));
492 	free(lfn);
493 }
494 
495 static void
496 find_libs(const list *l)
497 {
498 
499 	for (size_t i = 0; i < l->len; i++)
500 		find_lib(l->items[i]);
501 }
502 
503 static void
504 run_lint2(void)
505 {
506 
507 	char *abs_lint2 = libexec_dir != NULL
508 	    ? concat2(libexec_dir, "/lint2")
509 	    : xasprintf("%s/%slint2", PATH_LIBEXEC, target_prefix);
510 
511 	list args = { NULL, 0, 0 };
512 	list_add_ref(&args, abs_lint2);
513 	list_add_all(&args, &lint2.flags);
514 	list_add_all(&args, &lint2.input_libraries);
515 	list_add_all(&args, &lint2.input_files);
516 	list_add_ref(&args, NULL);
517 
518 	run_child(abs_lint2, &args, lint2.output_library, -1);
519 	list_clear(&args);
520 }
521 
522 static void
523 cat(const list *srcs, const char *dest)
524 {
525 	int ifd, ofd;
526 	ssize_t rlen;
527 	char buf[0x4000];
528 
529 	if ((ofd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1) {
530 		warn("cannot open %s", dest);
531 		terminate(-1);
532 	}
533 
534 	for (size_t i = 0; i < srcs->len; i++) {
535 		const char *src = srcs->items[i];
536 		if ((ifd = open(src, O_RDONLY)) == -1) {
537 			warn("cannot open %s", src);
538 			terminate(-1);
539 		}
540 		do {
541 			if ((rlen = read(ifd, buf, sizeof(buf))) == -1) {
542 				warn("read error on %s", src);
543 				terminate(-1);
544 			}
545 			if (write(ofd, buf, (size_t)rlen) != rlen) {
546 				warn("write error on %s", dest);
547 				terminate(-1);
548 			}
549 		} while (rlen == sizeof(buf));
550 		(void)close(ifd);
551 	}
552 	(void)close(ofd);
553 }
554 
555 int
556 main(int argc, char *argv[])
557 {
558 
559 	setprogname(argv[0]);
560 	set_tmpdir();
561 
562 	cpp.outfile = concat2(tmpdir, "lint0.XXXXXX");
563 	cpp.output_fd = mkstemp(cpp.outfile);
564 	if (cpp.output_fd == -1) {
565 		warn("can't make temp");
566 		terminate(-1);
567 	}
568 
569 	list_add(&cpp.flags, "-E");
570 	list_add(&cpp.flags, "-x");
571 	list_add(&cpp.flags, "c");
572 	list_add(&cpp.flags, "-U__GNUC__");
573 	list_add(&cpp.flags, "-U__PCC__");
574 	list_add(&cpp.flags, "-U__SSE__");
575 	list_add(&cpp.flags, "-U__SSE4_1__");
576 	list_add(&cpp.flags, "-Wp,-CC");
577 	list_add(&cpp.flags, "-Wcomment");
578 	list_add(&cpp.flags, "-D__LINT__");
579 	list_add(&cpp.flags, "-Dlint");	/* XXX don't define with -s */
580 	list_add(&cpp.flags, "-D__lint");
581 	list_add(&cpp.flags, "-D__lint__");
582 	list_add(&cpp.flags, "-dD");
583 
584 	list_add(&default_libraries, "c");
585 
586 	if (signal(SIGHUP, terminate) == SIG_IGN)
587 		(void)signal(SIGHUP, SIG_IGN);
588 	(void)signal(SIGINT, terminate);
589 	(void)signal(SIGQUIT, terminate);
590 	(void)signal(SIGTERM, terminate);
591 
592 	int c;
593 	while ((c = getopt(argc, argv,
594 	    "abcd:eghil:no:pq:rstuvwxzA:B:C:D:FHI:L:M:PR:STU:VW:X:Z:")) != -1) {
595 		switch (c) {
596 
597 		case 'a':
598 		case 'b':
599 		case 'c':
600 		case 'e':
601 		case 'g':
602 		case 'r':
603 		case 'v':
604 		case 'w':
605 		case 'z':
606 		case 'P':
607 			list_add_flag(&lint1.flags, c);
608 			break;
609 
610 		case 'A':
611 		case 'q':
612 		case 'R':
613 		case 'X':
614 			list_add_flag(&lint1.flags, c);
615 			list_add(&lint1.flags, optarg);
616 			break;
617 
618 		case 'F':
619 			Fflag = true;
620 			/* FALLTHROUGH */
621 		case 'h':
622 			list_add_flag(&lint1.flags, c);
623 			list_add_flag(&lint2.flags, c);
624 			break;
625 
626 		case 'i':
627 			if (Cflag)
628 				usage("%c and %s flags cannot be specified "
629 				    "together", 'C', "i");
630 			iflag = true;
631 			break;
632 
633 		case 'n':
634 			list_clear(&default_libraries);
635 			break;
636 
637 		case 'p':
638 			if (default_libraries.len > 0) {
639 				list_clear(&default_libraries);
640 				list_add(&default_libraries, "c");
641 			}
642 			list_add_flag(&lint1.flags, c);
643 			break;
644 
645 		case 's':
646 			if (tflag)
647 				usage("%c and %s flags cannot be specified "
648 				    "together", 's', "t");
649 			list_clear(&cpp.lcflags);
650 			list_add(&cpp.lcflags, "-trigraphs");
651 			list_add(&cpp.lcflags, "-Wtrigraphs");
652 			list_add(&cpp.lcflags, "-pedantic");
653 			list_add(&cpp.lcflags, "-D__STRICT_ANSI__");
654 			sflag = true;
655 			list_add_flag(&lint1.flags, c);
656 			list_add_flag(&lint2.flags, c);
657 			break;
658 
659 		case 'S':
660 			if (tflag)
661 				usage("%c and %s flags cannot be specified "
662 				    "together", 'S', "t");
663 			list_add_flag(&lint1.flags, c);
664 			break;
665 
666 		case 'T':
667 			list_add(&cpp.flags, "-I" PATH_STRICT_BOOL_INCLUDE);
668 			list_add_flag(&lint1.flags, c);
669 			break;
670 
671 #if !HAVE_NBTOOL_CONFIG_H
672 		case 't':
673 			if (sflag)
674 				usage("%c and %s flags cannot be specified "
675 				    "together", 's', "t");
676 			tflag = true;
677 			list_clear(&cpp.lcflags);
678 			list_add(&cpp.lcflags, "-traditional");
679 			list_add(&cpp.lcflags, "-Wtraditional");
680 			list_add(&cpp.lcflags, "-D" MACHINE);
681 			list_add(&cpp.lcflags, "-D" MACHINE_ARCH);
682 			list_add_flag(&lint1.flags, c);
683 			list_add_flag(&lint2.flags, c);
684 			break;
685 #endif
686 
687 		case 'u':
688 		case 'x':
689 		case 'H':
690 			list_add_flag(&lint2.flags, c);
691 			break;
692 
693 		case 'C':
694 			if (Cflag)
695 				usage("%c flag already specified", 'C');
696 			if (output_filename != NULL || iflag)
697 				usage("%c and %s flags cannot be specified "
698 				    "together", 'C', "o or i");
699 			Cflag = true;
700 			list_add_flag(&lint2.flags, c);
701 			list_add(&lint2.flags, optarg);
702 			lint2.output_library = xasprintf("llib-l%s.ln", optarg);
703 			list_clear(&default_libraries);
704 			break;
705 
706 		case 'd':
707 			if (dflag)
708 				usage("%c flag already specified", 'd');
709 			dflag = true;
710 			list_add(&cpp.flags, "--sysroot");
711 			list_add(&cpp.flags, optarg);
712 			break;
713 
714 		case 'D':
715 		case 'I':
716 		case 'M':
717 		case 'U':
718 		case 'W':
719 			list_add_ref(&cpp.flags,
720 			    xasprintf("-%c%s", c, optarg));
721 			break;
722 
723 		case 'l':
724 			list_add_unique(&additional_libraries, optarg);
725 			break;
726 
727 		case 'o':
728 			if (output_filename != NULL)
729 				usage("%c flag already specified", 'o');
730 			if (Cflag)
731 				usage("%c and %s flags cannot be specified "
732 				    "together", 'C', "o");
733 			output_filename = xstrdup(optarg);
734 			break;
735 
736 		case 'L':
737 			list_add_unique(&library_search_path, optarg);
738 			break;
739 
740 		case 'B':
741 			libexec_dir = xstrdup(optarg);
742 			break;
743 
744 		case 'V':
745 			Vflag = true;
746 			break;
747 
748 		case 'Z':
749 			list_add(&cpp.flags, optarg);
750 			break;
751 
752 		default:
753 			usage("");
754 			/* NOTREACHED */
755 		}
756 	}
757 	argc -= optind;
758 	argv += optind;
759 
760 	/*
761 	 * To avoid modifying getopt(3)'s state engine midstream, we explicitly
762 	 * accept just a few options after the first source file.
763 	 *
764 	 * In particular, only -l<lib> and -L<libdir> (and these with a space
765 	 * after -l or -L) are allowed.
766 	 */
767 	for (; argc > 0; argc--, argv++) {
768 		const char *arg = argv[0];
769 
770 		if (arg[0] == '-') {
771 			list *lp;
772 
773 			if (arg[1] == 'l')
774 				lp = &additional_libraries;
775 			else if (arg[1] == 'L')
776 				lp = &library_search_path;
777 			else
778 				usage("Unknown late option '%s'", arg);
779 
780 			if (arg[2] != '\0')
781 				list_add_unique(lp, arg + 2);
782 			else if (argc > 1) {
783 				argc--, argv++;
784 				list_add_unique(lp, argv[0]);
785 			} else
786 				usage("Missing argument for l or L");
787 		} else {
788 			handle_filename(arg);
789 			seen_filename = true;
790 		}
791 	}
792 
793 	if (!seen_filename)
794 		usage("Missing filename");
795 
796 	if (iflag)
797 		terminate(0);
798 
799 	if (output_filename == NULL) {
800 		const char *ks = getenv("LIBDIR");
801 		if (ks == NULL || ks[0] == '\0')
802 			ks = PATH_LINTLIB;
803 		list_add(&library_search_path, ks);
804 		find_libs(&additional_libraries);
805 		find_libs(&default_libraries);
806 	}
807 
808 	run_lint2();
809 
810 	if (output_filename != NULL)
811 		cat(&lint2.input_files, output_filename);
812 
813 	if (Cflag)
814 		lint2.output_library = NULL;
815 
816 	terminate(0);
817 	/* NOTREACHED */
818 }
819