xlint.c revision 75710
1/* $NetBSD: xlint.c,v 1.3 1995/10/23 14:29:30 jpo Exp $ */ 2 3/* 4 * Copyright (c) 1994, 1995 Jochen Pohl 5 * All Rights Reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Jochen Pohl for 18 * The NetBSD Project. 19 * 4. The name of the author may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34#ifndef lint 35static char rcsid[] = "$FreeBSD: head/usr.bin/xlint/xlint/xlint.c 75710 2001-04-19 15:40:04Z asmodai $"; 36#endif 37 38#include <sys/param.h> 39#include <sys/wait.h> 40#include <sys/stat.h> 41#include <sys/utsname.h> 42#include <stdio.h> 43#include <stdlib.h> 44#include <signal.h> 45#include <string.h> 46#include <unistd.h> 47#include <fcntl.h> 48#include <err.h> 49#include <errno.h> 50#include <paths.h> 51 52#include "lint.h" 53#include "pathnames.h" 54 55/* directory for temporary files */ 56static const char *tmpdir; 57 58/* path name for cpp output */ 59static char *cppout; 60 61/* filedescriptor for cpp output */ 62static int cppoutfd = -1; 63 64/* files created by 1st pass */ 65static char **p1out; 66 67/* input files for 2nd pass (without libraries) */ 68static char **p2in; 69 70/* library which will be created by 2nd pass */ 71static char *p2out; 72 73/* flags always passed to cc(1) */ 74static char **cflags; 75 76/* flags for cc(1), controled by sflag/tflag */ 77static char **lcflags; 78 79/* flags for lint1 */ 80static char **l1flags; 81 82/* flags for lint2 */ 83static char **l2flags; 84 85/* libraries for lint2 */ 86static char **l2libs; 87 88/* default libraries */ 89static char **deflibs; 90 91/* additional libraries */ 92static char **libs; 93 94/* search path for libraries */ 95static char **libsrchpath; 96 97/* flags */ 98static int iflag, oflag, Cflag, sflag, tflag, Fflag; 99 100/* print the commands executed to run the stages of compilation */ 101static int Vflag; 102 103/* filename for oflag */ 104static char *outputfn; 105 106/* reset after first .c source has been processed */ 107static int first = 1; 108 109/* 110 * name of a file which is currently written by a child and should 111 * be removed after abnormal termination of the child 112 */ 113static const char *currfn; 114 115 116static void appstrg __P((char ***, char *)); 117static void appcstrg __P((char ***, const char *)); 118static void applst __P((char ***, char *const *)); 119static void freelst __P((char ***)); 120static char *concat2 __P((const char *, const char *)); 121static char *concat3 __P((const char *, const char *, const char *)); 122static void terminate __P((int)); 123static const char *basename __P((const char *, int)); 124static void appdef __P((char ***, const char *)); 125static void usage __P((void)); 126static void fname __P((const char *, int)); 127static void runchild __P((const char *, char *const *, const char *, 128 int)); 129static void findlibs __P((char *const *)); 130static int rdok __P((const char *)); 131static void lint2 __P((void)); 132static void cat __P((char *const *, const char *)); 133 134/* 135 * Some functions to deal with lists of strings. 136 * Take care that we get no surprises in case of asyncron signals. 137 */ 138static void 139appstrg(lstp, s) 140 char ***lstp, *s; 141{ 142 char **lst, **olst; 143 int i; 144 145 olst = *lstp; 146 for (i = 0; olst[i] != NULL; i++) ; 147 lst = xmalloc((i + 2) * sizeof (char *)); 148 (void)memcpy(lst, olst, i * sizeof (char *)); 149 lst[i] = s; 150 lst[i + 1] = NULL; 151 *lstp = lst; 152} 153 154static void 155appcstrg(lstp, s) 156 char ***lstp; 157 const char *s; 158{ 159 appstrg(lstp, xstrdup(s)); 160} 161 162static void 163applst(destp, src) 164 char ***destp; 165 char *const *src; 166{ 167 int i, k; 168 char **dest, **odest; 169 170 odest = *destp; 171 for (i = 0; odest[i] != NULL; i++) ; 172 for (k = 0; src[k] != NULL; k++) ; 173 dest = xmalloc((i + k + 1) * sizeof (char *)); 174 (void)memcpy(dest, odest, i * sizeof (char *)); 175 for (k = 0; src[k] != NULL; k++) 176 dest[i + k] = xstrdup(src[k]); 177 dest[i + k] = NULL; 178 *destp = dest; 179 free(odest); 180} 181 182static void 183freelst(lstp) 184 char ***lstp; 185{ 186 char *s; 187 int i; 188 189 for (i = 0; (*lstp)[i] != NULL; i++) ; 190 while (i-- > 0) { 191 s = (*lstp)[i]; 192 (*lstp)[i] = NULL; 193 free(s); 194 } 195} 196 197static char * 198concat2(s1, s2) 199 const char *s1, *s2; 200{ 201 char *s; 202 203 s = xmalloc(strlen(s1) + strlen(s2) + 1); 204 (void)strcpy(s, s1); 205 (void)strcat(s, s2); 206 207 return (s); 208} 209 210static char * 211concat3(s1, s2, s3) 212 const char *s1, *s2, *s3; 213{ 214 char *s; 215 216 s = xmalloc(strlen(s1) + strlen(s2) + strlen(s3) + 1); 217 (void)strcpy(s, s1); 218 (void)strcat(s, s2); 219 (void)strcat(s, s3); 220 221 return (s); 222} 223 224/* 225 * Clean up after a signal. 226 */ 227static void 228terminate(signo) 229 int signo; 230{ 231 int i; 232 233 if (cppoutfd != -1) 234 (void)close(cppoutfd); 235 if (cppout != NULL) 236 (void)remove(cppout); 237 238 if (p1out != NULL) { 239 for (i = 0; p1out[i] != NULL; i++) 240 (void)remove(p1out[i]); 241 } 242 243 if (p2out != NULL) 244 (void)remove(p2out); 245 246 if (currfn != NULL) 247 (void)remove(currfn); 248 249 exit(signo != 0 ? 1 : 0); 250} 251 252/* 253 * Returns a pointer to the last component of strg after delim. 254 * Returns strg if the string does not contain delim. 255 */ 256static const char * 257basename(strg, delim) 258 const char *strg; 259 int delim; 260{ 261 const char *cp, *cp1, *cp2; 262 263 cp = cp1 = cp2 = strg; 264 while (*cp != '\0') { 265 if (*cp++ == delim) { 266 cp2 = cp1; 267 cp1 = cp; 268 } 269 } 270 return (*cp1 == '\0' ? cp2 : cp1); 271} 272 273static void 274appdef(lstp, def) 275 char ***lstp; 276 const char *def; 277{ 278 appstrg(lstp, concat2("-D__", def)); 279 appstrg(lstp, concat3("-D__", def, "__")); 280} 281 282static void 283usage() 284{ 285 (void)printf("lint [-abceghprvxzHF] [-s|-t] [-i|-nu] [-Dname[=def]] [-Uname]\n"); 286 (void)printf(" [-Idirectory] [-Ldirectory] [-llibrary] [-ooutputfile] file ...\n"); 287 (void)printf("\n"); 288 (void)printf("lint [-abceghprvzHF] [-s|-t] -Clibrary [-Dname[=def]]\n"); 289 (void)printf(" [-Idirectory] [-Uname] file ...\n"); 290 terminate(-1); 291} 292 293int 294main(argc, argv) 295 int argc; 296 char *argv[]; 297{ 298 int c; 299 char flgbuf[3], *tmp, *s; 300 size_t len; 301 302 if ((tmp = getenv("TMPDIR")) == NULL || (len = strlen(tmp)) == 0) { 303 tmpdir = xstrdup(_PATH_TMP); 304 } else { 305 s = xmalloc(len + 2); 306 (void)sprintf(s, "%s%s", tmp, tmp[len - 1] == '/' ? "" : "/"); 307 tmpdir = s; 308 } 309 310 cppout = xmalloc(strlen(tmpdir) + sizeof ("lint0.XXXXXX")); 311 (void)sprintf(cppout, "%slint0.XXXXXX", tmpdir); 312 cppoutfd = mkstemp(cppout); 313 if (cppoutfd == -1) { 314 warn("can't make temp"); 315 terminate(-1); 316 } 317 318 p1out = xcalloc(1, sizeof (char *)); 319 p2in = xcalloc(1, sizeof (char *)); 320 cflags = xcalloc(1, sizeof (char *)); 321 lcflags = xcalloc(1, sizeof (char *)); 322 l1flags = xcalloc(1, sizeof (char *)); 323 l2flags = xcalloc(1, sizeof (char *)); 324 l2libs = xcalloc(1, sizeof (char *)); 325 deflibs = xcalloc(1, sizeof (char *)); 326 libs = xcalloc(1, sizeof (char *)); 327 libsrchpath = xcalloc(1, sizeof (char *)); 328 329 appcstrg(&cflags, "-E"); 330 appcstrg(&cflags, "-x"); 331 appcstrg(&cflags, "c"); 332 appcstrg(&cflags, "-D__attribute__(x)="); 333 appcstrg(&cflags, "-Wp,-$"); 334 appcstrg(&cflags, "-Wp,-Cc"); 335 appcstrg(&cflags, "-Wcomment"); 336 appcstrg(&cflags, "-Dlint"); /* XXX don't def. with -s */ 337 338 appdef(&cflags, "lint"); 339 340 appcstrg(&lcflags, "-Wtraditional"); 341 342 appcstrg(&deflibs, "c"); 343 344 if (signal(SIGHUP, terminate) == SIG_IGN) 345 (void)signal(SIGHUP, SIG_IGN); 346 (void)signal(SIGINT, terminate); 347 (void)signal(SIGQUIT, terminate); 348 (void)signal(SIGTERM, terminate); 349 350 while (argc > optind) { 351 352 argc -= optind; 353 argv += optind; 354 optind = 0; 355 356 c = getopt(argc, argv, "abceghil:no:prstuvxzC:D:FHI:L:U:V"); 357 358 switch (c) { 359 360 case 'a': 361 case 'b': 362 case 'c': 363 case 'e': 364 case 'g': 365 case 'r': 366 case 'v': 367 case 'z': 368 (void)sprintf(flgbuf, "-%c", c); 369 appcstrg(&l1flags, flgbuf); 370 break; 371 372 case 'F': 373 Fflag = 1; 374 /* FALLTHROUGH */ 375 case 'u': 376 case 'h': 377 (void)sprintf(flgbuf, "-%c", c); 378 appcstrg(&l1flags, flgbuf); 379 appcstrg(&l2flags, flgbuf); 380 break; 381 382 case 'i': 383 if (Cflag) 384 usage(); 385 iflag = 1; 386 break; 387 388 case 'n': 389 freelst(&deflibs); 390 break; 391 392 case 'p': 393 appcstrg(&l1flags, "-p"); 394 appcstrg(&l2flags, "-p"); 395 if (*deflibs != NULL) { 396 freelst(&deflibs); 397 appcstrg(&deflibs, "c"); 398 } 399 break; 400 401 case 's': 402 if (tflag) 403 usage(); 404 freelst(&lcflags); 405 appcstrg(&lcflags, "-trigraphs"); 406 appcstrg(&lcflags, "-Wtrigraphs"); 407 appcstrg(&lcflags, "-pedantic"); 408 appcstrg(&lcflags, "-D__STRICT_ANSI__"); 409 appcstrg(&l1flags, "-s"); 410 appcstrg(&l2flags, "-s"); 411 sflag = 1; 412 break; 413 414 case 't': 415 if (sflag) 416 usage(); 417 freelst(&lcflags); 418 appcstrg(&lcflags, "-traditional"); 419 appstrg(&lcflags, concat2("-D", MACHINE)); 420#ifdef MACHINE_ARCH 421 appstrg(&lcflags, concat2("-D", MACHINE_ARCH)); 422#endif 423 appcstrg(&l1flags, "-t"); 424 appcstrg(&l2flags, "-t"); 425 tflag = 1; 426 break; 427 428 case 'x': 429 appcstrg(&l2flags, "-x"); 430 break; 431 432 case 'C': 433 if (Cflag || oflag || iflag) 434 usage(); 435 Cflag = 1; 436 appstrg(&l2flags, concat2("-C", optarg)); 437 p2out = xmalloc(sizeof ("llib-l.ln") + strlen(optarg)); 438 (void)sprintf(p2out, "llib-l%s.ln", optarg); 439 freelst(&deflibs); 440 break; 441 442 case 'D': 443 case 'I': 444 case 'U': 445 (void)sprintf(flgbuf, "-%c", c); 446 appstrg(&cflags, concat2(flgbuf, optarg)); 447 break; 448 449 case 'l': 450 appcstrg(&libs, optarg); 451 break; 452 453 case 'o': 454 if (Cflag || oflag) 455 usage(); 456 oflag = 1; 457 outputfn = xstrdup(optarg); 458 break; 459 460 case 'L': 461 appcstrg(&libsrchpath, optarg); 462 break; 463 464 case 'H': 465 appcstrg(&l2flags, "-H"); 466 break; 467 468 case 'V': 469 Vflag = 1; 470 break; 471 472 case '?': 473 usage(); 474 /* NOTREACHED */ 475 476 case -1: 477 /* filename */ 478 fname(argv[0], argc == 1); 479 first = 0; 480 optind = 1; 481 } 482 483 } 484 485 if (first) 486 usage(); 487 488 if (iflag) 489 terminate(0); 490 491 if (!oflag) { 492 if ((s = getenv("LIBDIR")) == NULL || strlen(s) == 0) 493 s = PATH_LINTLIB; 494 appcstrg(&libsrchpath, s); 495 findlibs(libs); 496 findlibs(deflibs); 497 } 498 499 (void)printf("Lint pass2:\n"); 500 lint2(); 501 502 if (oflag) 503 cat(p2in, outputfn); 504 505 if (Cflag) 506 p2out = NULL; 507 508 terminate(0); 509 /* NOTREACHED */ 510 return 0; 511} 512 513/* 514 * Read a file name from the command line 515 * and pass it through lint1 if it is a C source. 516 */ 517static void 518fname(name, last) 519 const char *name; 520 int last; 521{ 522 const char *bn, *suff; 523 char **args, *ofn, *path; 524 size_t len; 525 int fd; 526 527 bn = basename(name, '/'); 528 suff = basename(bn, '.'); 529 530 if (strcmp(suff, "ln") == 0) { 531 /* only for lint2 */ 532 if (!iflag) 533 appcstrg(&p2in, name); 534 return; 535 } 536 537 if (strcmp(suff, "c") != 0 && 538 (strncmp(bn, "llib-l", 6) != 0 || bn != suff)) { 539 warnx("unknown file type: %s\n", name); 540 return; 541 } 542 543 if (!iflag || !first || !last) 544 (void)printf("%s:\n", Fflag ? name : bn); 545 546 /* build the name of the output file of lint1 */ 547 if (oflag) { 548 ofn = outputfn; 549 outputfn = NULL; 550 oflag = 0; 551 } else if (iflag) { 552 ofn = xmalloc(strlen(bn) + (bn == suff ? 4 : 2)); 553 len = bn == suff ? strlen(bn) : (suff - 1) - bn; 554 (void)sprintf(ofn, "%.*s", (int)len, bn); 555 (void)strcat(ofn, ".ln"); 556 } else { 557 ofn = xmalloc(strlen(tmpdir) + sizeof ("lint1.XXXXXX")); 558 (void)sprintf(ofn, "%slint1.XXXXXX", tmpdir); 559 fd = mkstemp(ofn); 560 if (fd == -1) { 561 warn("can't make temp"); 562 terminate(-1); 563 } 564 close(fd); 565 } 566 if (!iflag) 567 appcstrg(&p1out, ofn); 568 569 args = xcalloc(1, sizeof (char *)); 570 571 /* run cc */ 572 573 path = xmalloc(strlen(PATH_USRBIN) + sizeof ("/cc")); 574 (void)sprintf(path, "%s/cc", PATH_USRBIN); 575 576 appcstrg(&args, path); 577 applst(&args, cflags); 578 applst(&args, lcflags); 579 appcstrg(&args, name); 580 581 /* we reuse the same tmp file for cpp output, so rewind and truncate */ 582 if (lseek(cppoutfd, SEEK_SET, (off_t)0) != 0) { 583 warn("lseek"); 584 terminate(-1); 585 } 586 if (ftruncate(cppoutfd, (off_t)0) != 0) { 587 warn("ftruncate"); 588 terminate(-1); 589 } 590 591 runchild(path, args, cppout, cppoutfd); 592 free(path); 593 freelst(&args); 594 595 /* run lint1 */ 596 597 path = xmalloc(strlen(PATH_LIBEXEC) + sizeof ("/lint1")); 598 (void)sprintf(path, "%s/lint1", PATH_LIBEXEC); 599 600 appcstrg(&args, path); 601 applst(&args, l1flags); 602 appcstrg(&args, cppout); 603 appcstrg(&args, ofn); 604 605 runchild(path, args, ofn, -1); 606 free(path); 607 freelst(&args); 608 609 appcstrg(&p2in, ofn); 610 free(ofn); 611 612 free(args); 613} 614 615static void 616runchild(path, args, crfn, fdout) 617 const char *path, *crfn; 618 char *const *args; 619 int fdout; 620{ 621 int status, rv, signo, i; 622 623 if (Vflag) { 624 for (i = 0; args[i] != NULL; i++) 625 (void)printf("%s ", args[i]); 626 (void)printf("\n"); 627 } 628 629 currfn = crfn; 630 631 (void)fflush(stdout); 632 633 switch (vfork()) { 634 case -1: 635 warn("cannot fork"); 636 terminate(-1); 637 /* NOTREACHED */ 638 default: 639 /* parent */ 640 break; 641 case 0: 642 /* child */ 643 644 /* setup the standard output if necessary */ 645 if (fdout != -1) { 646 dup2(fdout, STDOUT_FILENO); 647 close(fdout); 648 } 649 (void)execv(path, args); 650 warn("cannot exec %s", path); 651 _exit(1); 652 /* NOTREACHED */ 653 } 654 655 while ((rv = wait(&status)) == -1 && errno == EINTR) ; 656 if (rv == -1) { 657 warn("wait"); 658 terminate(-1); 659 } 660 if (WIFSIGNALED(status)) { 661 signo = WTERMSIG(status); 662 warnx("%s got SIG%s", path, sys_signame[signo]); 663 terminate(-1); 664 } 665 if (WEXITSTATUS(status) != 0) 666 terminate(-1); 667 currfn = NULL; 668} 669 670static void 671findlibs(liblst) 672 char *const *liblst; 673{ 674 int i, k; 675 const char *lib, *path; 676 char *lfn; 677 size_t len; 678 679 lfn = NULL; 680 681 for (i = 0; (lib = liblst[i]) != NULL; i++) { 682 for (k = 0; (path = libsrchpath[k]) != NULL; k++) { 683 len = strlen(path) + strlen(lib); 684 lfn = xrealloc(lfn, len + sizeof ("/llib-l.ln")); 685 (void)sprintf(lfn, "%s/llib-l%s.ln", path, lib); 686 if (rdok(lfn)) 687 break; 688 lfn = xrealloc(lfn, len + sizeof ("/lint/llib-l.ln")); 689 (void)sprintf(lfn, "%s/lint/llib-l%s.ln", path, lib); 690 if (rdok(lfn)) 691 break; 692 } 693 if (path != NULL) { 694 appstrg(&l2libs, concat2("-l", lfn)); 695 } else { 696 warnx("cannot find llib-l%s.ln", lib); 697 } 698 } 699 700 free(lfn); 701} 702 703static int 704rdok(path) 705 const char *path; 706{ 707 struct stat sbuf; 708 709 if (stat(path, &sbuf) == -1) 710 return (0); 711 if ((sbuf.st_mode & S_IFMT) != S_IFREG) 712 return (0); 713 if (access(path, R_OK) == -1) 714 return (0); 715 return (1); 716} 717 718static void 719lint2() 720{ 721 char *path, **args; 722 723 args = xcalloc(1, sizeof (char *)); 724 725 path = xmalloc(strlen(PATH_LIBEXEC) + sizeof ("/lint2")); 726 (void)sprintf(path, "%s/lint2", PATH_LIBEXEC); 727 728 appcstrg(&args, path); 729 applst(&args, l2flags); 730 applst(&args, l2libs); 731 applst(&args, p2in); 732 733 runchild(path, args, p2out, -1); 734 free(path); 735 freelst(&args); 736 free(args); 737} 738 739static void 740cat(srcs, dest) 741 char *const *srcs; 742 const char *dest; 743{ 744 int ifd, ofd, i; 745 char *src, *buf; 746 ssize_t rlen; 747 748 if ((ofd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1) { 749 warn("cannot open %s", dest); 750 terminate(-1); 751 } 752 753 buf = xmalloc(MBLKSIZ); 754 755 for (i = 0; (src = srcs[i]) != NULL; i++) { 756 if ((ifd = open(src, O_RDONLY)) == -1) { 757 free(buf); 758 warn("cannot open %s", src); 759 terminate(-1); 760 } 761 do { 762 if ((rlen = read(ifd, buf, MBLKSIZ)) == -1) { 763 free(buf); 764 warn("read error on %s", src); 765 terminate(-1); 766 } 767 if (write(ofd, buf, (size_t)rlen) == -1) { 768 free(buf); 769 warn("write error on %s", dest); 770 terminate(-1); 771 } 772 } while (rlen == MBLKSIZ); 773 (void)close(ifd); 774 } 775 (void)close(ofd); 776 free(buf); 777} 778 779