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