Home | History | Annotate | Line # | Download | only in xlint
      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