/* $NetBSD: xlint.c,v 1.50 2020/05/23 17:28:27 christos Exp $ */ /* * Copyright (c) 1996 Christopher G. Demetriou. All Rights Reserved. * Copyright (c) 1994, 1995 Jochen Pohl * All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Jochen Pohl for * The NetBSD Project. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #if HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #endif #include #if defined(__RCSID) && !defined(lint) __RCSID("$NetBSD: xlint.c,v 1.50 2020/05/23 17:28:27 christos Exp $"); #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lint.h" #include "pathnames.h" #include "findcc.h" #define DEFAULT_PATH _PATH_DEFPATH /* directory for temporary files */ static const char *tmpdir; /* path name for cpp output */ static char *cppout; /* file descriptor for cpp output */ static int cppoutfd = -1; /* files created by 1st pass */ static char **p1out; /* input files for 2nd pass (without libraries) */ static char **p2in; /* library which will be created by 2nd pass */ static char *p2out; /* flags always passed to cc(1) */ static char **cflags; /* flags for cc(1), controled by sflag/tflag */ static char **lcflags; /* flags for lint1 */ static char **l1flags; /* flags for lint2 */ static char **l2flags; /* libraries for lint2 */ static char **l2libs; /* default libraries */ static char **deflibs; /* additional libraries */ static char **libs; /* search path for libraries */ static char **libsrchpath; static char *libexec_path; /* flags */ static int iflag, oflag, Cflag, sflag, tflag, Fflag, dflag, Bflag, Sflag; /* print the commands executed to run the stages of compilation */ static int Vflag; /* filename for oflag */ static char *outputfn; /* reset after first .c source has been processed */ static int first = 1; /* * name of a file which is currently written by a child and should * be removed after abnormal termination of the child */ static const char *currfn; #if !defined(TARGET_PREFIX) #define TARGET_PREFIX "" #endif static const char target_prefix[] = TARGET_PREFIX; static void appstrg(char ***, char *); static void appcstrg(char ***, const char *); static void applst(char ***, char *const *); static void freelst(char ***); static char *concat2(const char *, const char *); static char *concat3(const char *, const char *, const char *); static void terminate(int) __attribute__((__noreturn__)); static const char *lbasename(const char *, int); static void appdef(char ***, const char *); static void usage(void); static void fname(const char *); static void runchild(const char *, char *const *, const char *, int); static void findlibs(char *const *); static int rdok(const char *); static void lint2(void); static void cat(char *const *, const char *); /* * Some functions to deal with lists of strings. * Take care that we get no surprises in case of asynchronous signals. */ static void appstrg(char ***lstp, char *s) { char **lst, **olst; int i; olst = *lstp; for (i = 0; olst[i] != NULL; i++) continue; lst = xrealloc(olst, (i + 2) * sizeof (char *)); lst[i] = s; lst[i + 1] = NULL; *lstp = lst; } static void appcstrg(char ***lstp, const char *s) { appstrg(lstp, xstrdup(s)); } static void applst(char ***destp, char *const *src) { int i, k; char **dest, **odest; odest = *destp; for (i = 0; odest[i] != NULL; i++) continue; for (k = 0; src[k] != NULL; k++) continue; dest = xrealloc(odest, (i + k + 1) * sizeof (char *)); for (k = 0; src[k] != NULL; k++) dest[i + k] = xstrdup(src[k]); dest[i + k] = NULL; *destp = dest; } static void freelst(char ***lstp) { char *s; int i; for (i = 0; (*lstp)[i] != NULL; i++) continue; while (i-- > 0) { s = (*lstp)[i]; (*lstp)[i] = NULL; free(s); } } static char * concat2(const char *s1, const char *s2) { char *s; s = xmalloc(strlen(s1) + strlen(s2) + 1); (void)strcpy(s, s1); (void)strcat(s, s2); return (s); } static char * concat3(const char *s1, const char *s2, const char *s3) { char *s; s = xmalloc(strlen(s1) + strlen(s2) + strlen(s3) + 1); (void)strcpy(s, s1); (void)strcat(s, s2); (void)strcat(s, s3); return (s); } /* * Clean up after a signal. */ static void terminate(int signo) { int i; if (cppoutfd != -1) (void)close(cppoutfd); if (cppout != NULL) (void)remove(cppout); if (p1out != NULL) { for (i = 0; p1out[i] != NULL; i++) (void)remove(p1out[i]); } if (p2out != NULL) (void)remove(p2out); if (currfn != NULL) (void)remove(currfn); if (signo != 0) (void)raise_default_signal(signo); exit(signo != 0 ? 1 : 0); } /* * Returns a pointer to the last component of strg after delim. * Returns strg if the string does not contain delim. */ static const char * lbasename(const char *strg, int delim) { const char *cp, *cp1, *cp2; cp = cp1 = cp2 = strg; while (*cp != '\0') { if (*cp++ == delim) { cp2 = cp1; cp1 = cp; } } return (*cp1 == '\0' ? cp2 : cp1); } static void appdef(char ***lstp, const char *def) { appstrg(lstp, concat2("-D__", def)); appstrg(lstp, concat3("-D__", def, "__")); } static void usage(void) { (void)fprintf(stderr, "Usage: %s [-abceghprvwxzHFS] [-s|-t] [-i|-nu] [-Dname[=def]]" " [-Uname] [-X [,]... [-Z ]\n", getprogname()); (void)fprintf(stderr, "\t[-Idirectory] [-Ldirectory] [-llibrary] [-ooutputfile]" " file...\n"); (void)fprintf(stderr, " %s [-abceghprvwzHFS] [|-s|-t] -Clibrary [-Dname[=def]]\n" " [-X [,]... [-Z ]\n", getprogname()); (void)fprintf(stderr, "\t[-Idirectory] [-Uname] [-Bpath] [-R old=new]" " file ...\n"); terminate(-1); } int main(int argc, char *argv[]) { int c; char flgbuf[3], *tmp; size_t len; const char *ks; setprogname(argv[0]); if ((tmp = getenv("TMPDIR")) == NULL || (len = strlen(tmp)) == 0) { tmpdir = xstrdup(_PATH_TMP); } else { char *p = xmalloc(len + 2); (void)sprintf(p, "%s%s", tmp, tmp[len - 1] == '/' ? "" : "/"); tmpdir = p; } cppout = xmalloc(strlen(tmpdir) + sizeof ("lint0.XXXXXX")); (void)sprintf(cppout, "%slint0.XXXXXX", tmpdir); cppoutfd = mkstemp(cppout); if (cppoutfd == -1) { warn("can't make temp"); terminate(-1); } p1out = xcalloc(1, sizeof (char *)); p2in = xcalloc(1, sizeof (char *)); cflags = xcalloc(1, sizeof (char *)); lcflags = xcalloc(1, sizeof (char *)); l1flags = xcalloc(1, sizeof (char *)); l2flags = xcalloc(1, sizeof (char *)); l2libs = xcalloc(1, sizeof (char *)); deflibs = xcalloc(1, sizeof (char *)); libs = xcalloc(1, sizeof (char *)); libsrchpath = xcalloc(1, sizeof (char *)); appcstrg(&cflags, "-E"); appcstrg(&cflags, "-x"); appcstrg(&cflags, "c"); #if 0 appcstrg(&cflags, "-D__attribute__(x)="); appcstrg(&cflags, "-D__extension__(x)=/*NOSTRICT*/0"); #else appcstrg(&cflags, "-U__GNUC__"); appcstrg(&cflags, "-U__PCC__"); appcstrg(&cflags, "-U__SSE__"); appcstrg(&cflags, "-U__SSE4_1__"); #endif #if 0 appcstrg(&cflags, "-Wp,-$"); #endif appcstrg(&cflags, "-Wp,-CC"); appcstrg(&cflags, "-Wcomment"); appcstrg(&cflags, "-D__LINT__"); appcstrg(&cflags, "-Dlint"); /* XXX don't def. with -s */ appdef(&cflags, "lint"); appcstrg(&deflibs, "c"); if (signal(SIGHUP, terminate) == SIG_IGN) (void)signal(SIGHUP, SIG_IGN); (void)signal(SIGINT, terminate); (void)signal(SIGQUIT, terminate); (void)signal(SIGTERM, terminate); while ((c = getopt(argc, argv, "abcd:eghil:no:prstuvwxzB:C:D:FHI:L:M:PR:SU:VX:Z:")) != -1) { switch (c) { case 'a': case 'b': case 'c': case 'e': case 'g': case 'r': case 'v': case 'w': case 'z': (void)sprintf(flgbuf, "-%c", c); appcstrg(&l1flags, flgbuf); break; case 'F': Fflag = 1; /* FALLTHROUGH */ case 'u': case 'h': (void)sprintf(flgbuf, "-%c", c); appcstrg(&l1flags, flgbuf); appcstrg(&l2flags, flgbuf); break; case 'X': (void)sprintf(flgbuf, "-%c", c); appcstrg(&l1flags, flgbuf); appcstrg(&l1flags, optarg); break; case 'i': if (Cflag) usage(); iflag = 1; break; case 'n': freelst(&deflibs); break; case 'p': appcstrg(&l1flags, "-p"); appcstrg(&l2flags, "-p"); if (*deflibs != NULL) { freelst(&deflibs); appcstrg(&deflibs, "c"); } break; case 'P': appcstrg(&l1flags, "-P"); break; case 'R': appcstrg(&l1flags, concat2("-R", optarg)); break; case 's': if (tflag) usage(); freelst(&lcflags); appcstrg(&lcflags, "-trigraphs"); appcstrg(&lcflags, "-Wtrigraphs"); appcstrg(&lcflags, "-pedantic"); appcstrg(&lcflags, "-D__STRICT_ANSI__"); appcstrg(&l1flags, "-s"); appcstrg(&l2flags, "-s"); sflag = 1; break; case 'S': if (tflag) usage(); appcstrg(&l1flags, "-S"); Sflag = 1; break; #if ! HAVE_NBTOOL_CONFIG_H case 't': if (sflag) usage(); freelst(&lcflags); appcstrg(&lcflags, "-traditional"); appcstrg(&lcflags, "-Wtraditional"); appstrg(&lcflags, concat2("-D", MACHINE)); appstrg(&lcflags, concat2("-D", MACHINE_ARCH)); appcstrg(&l1flags, "-t"); appcstrg(&l2flags, "-t"); tflag = 1; break; #endif case 'x': appcstrg(&l2flags, "-x"); break; case 'C': if (Cflag || oflag || iflag) usage(); Cflag = 1; appstrg(&l2flags, concat2("-C", optarg)); p2out = xmalloc(sizeof ("llib-l.ln") + strlen(optarg)); (void)sprintf(p2out, "llib-l%s.ln", optarg); freelst(&deflibs); break; case 'd': if (dflag) usage(); dflag = 1; appcstrg(&cflags, "-nostdinc"); appcstrg(&cflags, "-isystem"); appcstrg(&cflags, optarg); break; case 'D': case 'I': case 'M': case 'U': (void)sprintf(flgbuf, "-%c", c); appstrg(&cflags, concat2(flgbuf, optarg)); break; case 'l': appcstrg(&libs, optarg); break; case 'o': if (Cflag || oflag) usage(); oflag = 1; outputfn = xstrdup(optarg); break; case 'L': appcstrg(&libsrchpath, optarg); break; case 'H': appcstrg(&l2flags, "-H"); break; case 'B': Bflag = 1; libexec_path = xstrdup(optarg); break; case 'V': Vflag = 1; break; case 'Z': appcstrg(&cflags, optarg); break; default: usage(); /* NOTREACHED */ } } argc -= optind; argv += optind; /* * To avoid modifying getopt(3)'s state engine midstream, we * explicitly accept just a few options after the first source file. * * In particular, only -l and -L (and these with a space * after -l or -L) are allowed. */ while (argc > 0) { const char *arg = argv[0]; if (arg[0] == '-') { char ***list; list = NULL; /* XXXGCC -Wuninitialized */ /* option */ switch (arg[1]) { case 'l': list = &libs; break; case 'L': list = &libsrchpath; break; default: usage(); /* NOTREACHED */ } if (arg[2]) appcstrg(list, arg + 2); else if (argc > 1) { argc--; appcstrg(list, *++argv); } else usage(); } else { /* filename */ fname(arg); first = 0; } argc--; argv++; } if (first) usage(); if (iflag) terminate(0); if (!oflag) { if ((ks = getenv("LIBDIR")) == NULL || strlen(ks) == 0) ks = PATH_LINTLIB; appcstrg(&libsrchpath, ks); findlibs(libs); findlibs(deflibs); } (void)printf("Lint pass2:\n"); lint2(); if (oflag) cat(p2in, outputfn); if (Cflag) p2out = NULL; terminate(0); /* NOTREACHED */ } /* * Read a file name from the command line * and pass it through lint1 if it is a C source. */ static void fname(const char *name) { const char *bn, *suff; char **args, *ofn, *pathname; const char *CC; size_t len; int is_stdin; int fd; is_stdin = (strcmp(name, "-") == 0); bn = lbasename(name, '/'); suff = lbasename(bn, '.'); if (strcmp(suff, "ln") == 0) { /* only for lint2 */ if (!iflag) appcstrg(&p2in, name); return; } if (!is_stdin && strcmp(suff, "c") != 0 && (strncmp(bn, "llib-l", 6) != 0 || bn != suff)) { warnx("unknown file type: %s", name); return; } if (!iflag || !first) (void)printf("%s:\n", is_stdin ? "{standard input}" : Fflag ? name : bn); /* build the name of the output file of lint1 */ if (oflag) { ofn = outputfn; outputfn = NULL; oflag = 0; } else if (iflag) { if (is_stdin) { warnx("-i not supported without -o for standard input"); return; } len = bn == suff ? strlen(bn) : (size_t)((suff - 1) - bn); xasprintf(&ofn, "%.*s.ln", (int)len, bn); } else { xasprintf(&ofn, "%slint1.XXXXXX", tmpdir); fd = mkstemp(ofn); if (fd == -1) { warn("can't make temp"); terminate(-1); } close(fd); } if (!iflag) appcstrg(&p1out, ofn); args = xcalloc(1, sizeof (char *)); /* run cc */ if ((CC = getenv("CC")) == NULL) CC = DEFAULT_CC; if ((pathname = findcc(CC)) == NULL) if (!setenv("PATH", DEFAULT_PATH, 1)) pathname = findcc(CC); if (pathname == NULL) { (void)fprintf(stderr, "%s: %s: not found\n", getprogname(), CC); exit(EXIT_FAILURE); } appcstrg(&args, pathname); applst(&args, cflags); applst(&args, lcflags); appcstrg(&args, name); /* we reuse the same tmp file for cpp output, so rewind and truncate */ if (lseek(cppoutfd, (off_t)0, SEEK_SET) != 0) { warn("lseek"); terminate(-1); } if (ftruncate(cppoutfd, (off_t)0) != 0) { warn("ftruncate"); terminate(-1); } runchild(pathname, args, cppout, cppoutfd); free(pathname); freelst(&args); /* run lint1 */ if (!Bflag) { pathname = xmalloc(strlen(PATH_LIBEXEC) + sizeof ("/lint1") + strlen(target_prefix)); (void)sprintf(pathname, "%s/%slint1", PATH_LIBEXEC, target_prefix); } else { /* * XXX Unclear whether we should be using target_prefix * XXX here. --thorpej@wasabisystems.com */ pathname = xmalloc(strlen(libexec_path) + sizeof ("/lint1")); (void)sprintf(pathname, "%s/lint1", libexec_path); } appcstrg(&args, pathname); applst(&args, l1flags); appcstrg(&args, cppout); appcstrg(&args, ofn); runchild(pathname, args, ofn, -1); free(pathname); freelst(&args); appcstrg(&p2in, ofn); free(ofn); free(args); } static void runchild(const char *path, char *const *args, const char *crfn, int fdout) { int status, rv, signo, i; if (Vflag) { for (i = 0; args[i] != NULL; i++) (void)printf("%s ", args[i]); (void)printf("\n"); } currfn = crfn; (void)fflush(stdout); switch (vfork()) { case -1: warn("cannot fork"); terminate(-1); /* NOTREACHED */ default: /* parent */ break; case 0: /* child */ /* setup the standard output if necessary */ if (fdout != -1) { dup2(fdout, STDOUT_FILENO); close(fdout); } (void)execvp(path, args); warn("cannot exec %s", path); _exit(1); /* NOTREACHED */ } while ((rv = wait(&status)) == -1 && errno == EINTR) ; if (rv == -1) { warn("wait"); terminate(-1); } if (WIFSIGNALED(status)) { signo = WTERMSIG(status); #if HAVE_DECL_SYS_SIGNAME warnx("%s got SIG%s", path, sys_signame[signo]); #else warnx("%s got signal %d", path, signo); #endif terminate(-1); } if (WEXITSTATUS(status) != 0) terminate(-1); currfn = NULL; } static void findlibs(char *const *liblst) { int i, k; const char *lib, *path; char *lfn; size_t len; lfn = NULL; for (i = 0; (lib = liblst[i]) != NULL; i++) { for (k = 0; (path = libsrchpath[k]) != NULL; k++) { len = strlen(path) + strlen(lib); lfn = xrealloc(lfn, len + sizeof ("/llib-l.ln")); (void)sprintf(lfn, "%s/llib-l%s.ln", path, lib); if (rdok(lfn)) break; lfn = xrealloc(lfn, len + sizeof ("/lint/llib-l.ln")); (void)sprintf(lfn, "%s/lint/llib-l%s.ln", path, lib); if (rdok(lfn)) break; } if (path != NULL) { appstrg(&l2libs, concat2("-l", lfn)); } else { warnx("cannot find llib-l%s.ln", lib); } } free(lfn); } static int rdok(const char *path) { struct stat sbuf; if (stat(path, &sbuf) == -1) return (0); if (!S_ISREG(sbuf.st_mode)) return (0); if (access(path, R_OK) == -1) return (0); return (1); } static void lint2(void) { char *path, **args; args = xcalloc(1, sizeof (char *)); if (!Bflag) { path = xmalloc(strlen(PATH_LIBEXEC) + sizeof ("/lint2") + strlen(target_prefix)); (void)sprintf(path, "%s/%slint2", PATH_LIBEXEC, target_prefix); } else { /* * XXX Unclear whether we should be using target_prefix * XXX here. --thorpej@wasabisystems.com */ path = xmalloc(strlen(libexec_path) + sizeof ("/lint2")); (void)sprintf(path, "%s/lint2", libexec_path); } appcstrg(&args, path); applst(&args, l2flags); applst(&args, l2libs); applst(&args, p2in); runchild(path, args, p2out, -1); free(path); freelst(&args); free(args); } static void cat(char *const *srcs, const char *dest) { int ifd, ofd, i; char *src, *buf; ssize_t rlen; if ((ofd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1) { warn("cannot open %s", dest); terminate(-1); } buf = xmalloc(MBLKSIZ); for (i = 0; (src = srcs[i]) != NULL; i++) { if ((ifd = open(src, O_RDONLY)) == -1) { free(buf); warn("cannot open %s", src); terminate(-1); } do { if ((rlen = read(ifd, buf, MBLKSIZ)) == -1) { free(buf); warn("read error on %s", src); terminate(-1); } if (write(ofd, buf, (size_t)rlen) == -1) { free(buf); warn("write error on %s", dest); terminate(-1); } } while (rlen == MBLKSIZ); (void)close(ifd); } (void)close(ofd); free(buf); }