1 /* $NetBSD: xlint.c,v 1.36 2005/02/09 21:24:48 dsl Exp $ */
4 * Copyright (c) 1996 Christopher G. Demetriou. All Rights Reserved.
5 * Copyright (c) 1994, 1995 Jochen Pohl
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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
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.
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.
35 #include <sys/cdefs.h>
36 #if defined(__RCSID) && !defined(lint)
37 __RCSID("$NetBSD: xlint.c,v 1.36 2005/02/09 21:24:48 dsl Exp $");
39 __FBSDID("$FreeBSD$");
41 #include <sys/param.h>
44 #include <sys/utsname.h>
56 #include "pathnames.h"
58 #define DEFAULT_PATH _PATH_DEFPATH
60 int main(int, char *[]);
62 /* directory for temporary files */
63 static const char *tmpdir;
65 /* path name for cpp output */
68 /* file descriptor for cpp output */
69 static int cppoutfd = -1;
71 /* files created by 1st pass */
74 /* input files for 2nd pass (without libraries) */
77 /* library which will be created by 2nd pass */
80 /* flags always passed to cc(1) */
83 /* flags for cc(1), controlled by sflag/tflag */
84 static char **lcflags;
87 static char **l1flags;
90 static char **l2flags;
92 /* libraries for lint2 */
95 /* default libraries */
96 static char **deflibs;
98 /* additional libraries */
101 /* search path for libraries */
102 static char **libsrchpath;
104 static char *libexec_path;
107 static int iflag, oflag, Cflag, sflag, tflag, Fflag, dflag, Bflag, Sflag;
109 /* print the commands executed to run the stages of compilation */
112 /* filename for oflag */
113 static char *outputfn;
115 /* reset after first .c source has been processed */
116 static int first = 1;
119 * name of a file which is currently written by a child and should
120 * be removed after abnormal termination of the child
122 static const char *currfn;
124 #if !defined(TARGET_PREFIX)
125 #define TARGET_PREFIX ""
127 static const char target_prefix[] = TARGET_PREFIX;
129 static void appstrg(char ***, char *);
130 static void appcstrg(char ***, const char *);
131 static void applst(char ***, char *const *);
132 static void freelst(char ***);
133 static char *concat2(const char *, const char *);
134 static char *concat3(const char *, const char *, const char *);
135 static void terminate(int) __attribute__((__noreturn__));
136 static const char *lbasename(const char *, int);
137 static void appdef(char ***, const char *);
138 static void usage(void) __dead2;
139 static void fname(const char *);
140 static void runchild(const char *, char *const *, const char *, int);
141 static void findlibs(char *const *);
142 static int rdok(const char *);
143 static void lint2(void);
144 static void cat(char *const *, const char *);
147 * Some functions to deal with lists of strings.
148 * Take care that we get no surprises in case of asynchronous signals.
151 appstrg(char ***lstp, char *s)
157 for (i = 0; olst[i] != NULL; i++)
159 lst = xrealloc(olst, (i + 2) * sizeof (char *));
166 appcstrg(char ***lstp, const char *s)
169 appstrg(lstp, xstrdup(s));
173 applst(char ***destp, char *const *src)
176 char **dest, **odest;
179 for (i = 0; odest[i] != NULL; i++)
181 for (k = 0; src[k] != NULL; k++)
183 dest = xrealloc(odest, (i + k + 1) * sizeof (char *));
184 for (k = 0; src[k] != NULL; k++)
185 dest[i + k] = xstrdup(src[k]);
191 freelst(char ***lstp)
196 for (i = 0; (*lstp)[i] != NULL; i++)
206 concat2(const char *s1, const char *s2)
210 s = xmalloc(strlen(s1) + strlen(s2) + 1);
218 concat3(const char *s1, const char *s2, const char *s3)
222 s = xmalloc(strlen(s1) + strlen(s2) + strlen(s3) + 1);
231 * Clean up after a signal.
239 (void)close(cppoutfd);
241 (void)remove(cppout);
244 for (i = 0; p1out[i] != NULL; i++)
245 (void)remove(p1out[i]);
252 (void)remove(currfn);
254 exit(signo != 0 ? 1 : 0);
258 * Returns a pointer to the last component of strg after delim.
259 * Returns strg if the string does not contain delim.
262 lbasename(const char *strg, int delim)
264 const char *cp, *cp1, *cp2;
266 cp = cp1 = cp2 = strg;
267 while (*cp != '\0') {
268 if (*cp++ == delim) {
273 return (*cp1 == '\0' ? cp2 : cp1);
277 appdef(char ***lstp, const char *def)
280 appstrg(lstp, concat2("-D__", def));
281 appstrg(lstp, concat3("-D__", def, "__"));
288 (void)fprintf(stderr,
289 "usage: lint [-abceghprvwxzHFS] [-s|-t] [-i|-nu] [-Dname[=def]]"
290 " [-Uname] [-X <id>[,<id>]...\n");
291 (void)fprintf(stderr,
292 "\t[-Idirectory] [-Ldirectory] [-llibrary] [-ooutputfile]"
294 (void)fprintf(stderr,
295 " lint [-abceghprvwzHFS] [-s|-t] -Clibrary [-Dname[=def]]\n"
296 " [-X <id>[,<id>]...\n");
297 (void)fprintf(stderr, "\t[-Idirectory] [-Uname] [-Bpath] file"
304 main(int argc, char *argv[])
311 if ((tmp = getenv("TMPDIR")) == NULL || (len = strlen(tmp)) == 0) {
314 s = xmalloc(len + 2);
315 (void)sprintf(s, "%s%s", tmp, tmp[len - 1] == '/' ? "" : "/");
319 cppout = xmalloc(strlen(tmpdir) + sizeof ("lint0.XXXXXX"));
320 (void)sprintf(cppout, "%slint0.XXXXXX", tmpdir);
321 cppoutfd = mkstemp(cppout);
322 if (cppoutfd == -1) {
323 warn("can't make temp");
327 p1out = xcalloc(1, sizeof (char *));
328 p2in = xcalloc(1, sizeof (char *));
329 cflags = xcalloc(1, sizeof (char *));
330 lcflags = xcalloc(1, sizeof (char *));
331 l1flags = xcalloc(1, sizeof (char *));
332 l2flags = xcalloc(1, sizeof (char *));
333 l2libs = xcalloc(1, sizeof (char *));
334 deflibs = xcalloc(1, sizeof (char *));
335 libs = xcalloc(1, sizeof (char *));
336 libsrchpath = xcalloc(1, sizeof (char *));
338 appcstrg(&cflags, "-E");
339 appcstrg(&cflags, "-x");
340 appcstrg(&cflags, "c");
342 appcstrg(&cflags, "-D__attribute__(x)=");
343 appcstrg(&cflags, "-D__extension__(x)=/*NOSTRICT*/0");
345 appcstrg(&cflags, "-U__GNUC__");
346 appcstrg(&cflags, "-undef");
349 appcstrg(&cflags, "-Wp,-$");
351 appcstrg(&cflags, "-Wp,-C");
352 appcstrg(&cflags, "-Wcomment");
353 appcstrg(&cflags, "-D__LINT__");
354 appcstrg(&cflags, "-Dlint"); /* XXX don't def. with -s */
356 appdef(&cflags, "lint");
358 appcstrg(&deflibs, "c");
360 if (signal(SIGHUP, terminate) == SIG_IGN)
361 (void)signal(SIGHUP, SIG_IGN);
362 (void)signal(SIGINT, terminate);
363 (void)signal(SIGQUIT, terminate);
364 (void)signal(SIGTERM, terminate);
365 while ((c = getopt(argc, argv, "abcd:eghil:no:prstuvwxzB:C:D:FHI:L:M:SU:VX:")) != -1) {
377 (void)sprintf(flgbuf, "-%c", c);
378 appcstrg(&l1flags, flgbuf);
386 (void)sprintf(flgbuf, "-%c", c);
387 appcstrg(&l1flags, flgbuf);
388 appcstrg(&l2flags, flgbuf);
392 (void)sprintf(flgbuf, "-%c", c);
393 appcstrg(&l1flags, flgbuf);
394 appcstrg(&l1flags, optarg);
408 appcstrg(&lcflags, "-Wtraditional");
409 appcstrg(&lcflags, "-Wno-system-headers");
410 appcstrg(&l1flags, "-p");
411 appcstrg(&l2flags, "-p");
412 if (*deflibs != NULL) {
414 appcstrg(&deflibs, "c");
422 appcstrg(&lcflags, "-trigraphs");
423 appcstrg(&lcflags, "-Wtrigraphs");
424 appcstrg(&lcflags, "-pedantic");
425 appcstrg(&lcflags, "-D__STRICT_ANSI__");
426 appcstrg(&l1flags, "-s");
427 appcstrg(&l2flags, "-s");
434 appcstrg(&l1flags, "-S");
443 appcstrg(&lcflags, "-traditional");
444 appstrg(&lcflags, concat2("-D", MACHINE));
445 appstrg(&lcflags, concat2("-D", MACHINE_ARCH));
446 appcstrg(&l1flags, "-t");
447 appcstrg(&l2flags, "-t");
453 appcstrg(&l2flags, "-x");
457 if (Cflag || oflag || iflag)
460 appstrg(&l2flags, concat2("-C", optarg));
461 p2out = xmalloc(sizeof ("llib-l.ln") + strlen(optarg));
462 (void)sprintf(p2out, "llib-l%s.ln", optarg);
470 appcstrg(&cflags, "-nostdinc");
471 appcstrg(&cflags, "-idirafter");
472 appcstrg(&cflags, optarg);
479 (void)sprintf(flgbuf, "-%c", c);
480 appstrg(&cflags, concat2(flgbuf, optarg));
484 appcstrg(&libs, optarg);
491 outputfn = xstrdup(optarg);
495 appcstrg(&libsrchpath, optarg);
499 appcstrg(&l2flags, "-H");
504 libexec_path = xstrdup(optarg);
520 * To avoid modifying getopt(3)'s state engine midstream, we
521 * explicitly accept just a few options after the first source file.
523 * In particular, only -l<lib> and -L<libdir> (and these with a space
524 * after -l or -L) are allowed.
527 const char *arg = argv[0];
547 appcstrg(list, arg + 2);
550 appcstrg(list, *++argv);
569 if ((tmp = getenv("LIBDIR")) == NULL || strlen(tmp) == 0)
571 appcstrg(&libsrchpath, tmp);
576 (void)printf("Lint pass2:\n");
590 * Read a file name from the command line
591 * and pass it through lint1 if it is a C source.
594 fname(const char *name)
596 const char *bn, *suff;
597 char **args, *ofn, *p, *pathname;
602 is_stdin = (strcmp(name, "-") == 0);
603 bn = lbasename(name, '/');
604 suff = lbasename(bn, '.');
606 if (strcmp(suff, "ln") == 0) {
609 appcstrg(&p2in, name);
613 if (!is_stdin && strcmp(suff, "c") != 0 &&
614 (strncmp(bn, "llib-l", 6) != 0 || bn != suff)) {
615 warnx("unknown file type: %s\n", name);
619 if (!iflag || !first)
620 (void)printf("%s:\n",
621 is_stdin ? "{standard input}" : Fflag ? name : bn);
623 /* build the name of the output file of lint1 */
630 warnx("-i not supported without -o for standard input");
633 ofn = xmalloc(strlen(bn) + (bn == suff ? 4 : 2));
634 len = bn == suff ? strlen(bn) : (size_t)((suff - 1) - bn);
635 (void)sprintf(ofn, "%.*s", (int)len, bn);
636 (void)strcat(ofn, ".ln");
638 ofn = xmalloc(strlen(tmpdir) + sizeof ("lint1.XXXXXX"));
639 (void)sprintf(ofn, "%slint1.XXXXXX", tmpdir);
642 warn("can't make temp");
648 appcstrg(&p1out, ofn);
650 args = xcalloc(1, sizeof (char *));
654 if (getenv("CC") == NULL) {
655 pathname = xmalloc(strlen(PATH_USRBIN) + sizeof ("/cc"));
656 (void)sprintf(pathname, "%s/cc", PATH_USRBIN);
657 appcstrg(&args, pathname);
659 pathname = strdup(getenv("CC"));
660 for (p = strtok(pathname, " \t"); p; p = strtok(NULL, " \t"))
664 applst(&args, cflags);
665 applst(&args, lcflags);
666 appcstrg(&args, name);
668 /* we reuse the same tmp file for cpp output, so rewind and truncate */
669 if (lseek(cppoutfd, (off_t)0, SEEK_SET) != 0) {
673 if (ftruncate(cppoutfd, (off_t)0) != 0) {
678 runchild(pathname, args, cppout, cppoutfd);
685 pathname = xmalloc(strlen(PATH_LIBEXEC) + sizeof ("/lint1") +
686 strlen(target_prefix));
687 (void)sprintf(pathname, "%s/%slint1", PATH_LIBEXEC,
691 * XXX Unclear whether we should be using target_prefix
692 * XXX here. --thorpej@wasabisystems.com
694 pathname = xmalloc(strlen(libexec_path) + sizeof ("/lint1"));
695 (void)sprintf(pathname, "%s/lint1", libexec_path);
698 appcstrg(&args, pathname);
699 applst(&args, l1flags);
700 appcstrg(&args, cppout);
701 appcstrg(&args, ofn);
703 runchild(pathname, args, ofn, -1);
707 appcstrg(&p2in, ofn);
714 runchild(const char *path, char *const *args, const char *crfn, int fdout)
716 int status, rv, signo, i;
719 for (i = 0; args[i] != NULL; i++)
720 (void)printf("%s ", args[i]);
726 (void)fflush(stdout);
739 /* setup the standard output if necessary */
741 dup2(fdout, STDOUT_FILENO);
744 (void)execvp(path, args);
745 warn("cannot exec %s", path);
750 while ((rv = wait(&status)) == -1 && errno == EINTR) ;
755 if (WIFSIGNALED(status)) {
756 signo = WTERMSIG(status);
757 #if HAVE_DECL_SYS_SIGNAME
758 warnx("%s got SIG%s", path, sys_signame[signo]);
760 warnx("%s got signal %d", path, signo);
764 if (WEXITSTATUS(status) != 0)
770 findlibs(char *const *liblst)
773 const char *lib, *path;
779 for (i = 0; (lib = liblst[i]) != NULL; i++) {
780 for (k = 0; (path = libsrchpath[k]) != NULL; k++) {
781 len = strlen(path) + strlen(lib);
782 lfn = xrealloc(lfn, len + sizeof ("/llib-l.ln"));
783 (void)sprintf(lfn, "%s/llib-l%s.ln", path, lib);
786 lfn = xrealloc(lfn, len + sizeof ("/lint/llib-l.ln"));
787 (void)sprintf(lfn, "%s/lint/llib-l%s.ln", path, lib);
792 appstrg(&l2libs, concat2("-l", lfn));
794 warnx("cannot find llib-l%s.ln", lib);
802 rdok(const char *path)
806 if (stat(path, &sbuf) == -1)
808 if (!S_ISREG(sbuf.st_mode))
810 if (access(path, R_OK) == -1)
820 args = xcalloc(1, sizeof (char *));
823 path = xmalloc(strlen(PATH_LIBEXEC) + sizeof ("/lint2") +
824 strlen(target_prefix));
825 (void)sprintf(path, "%s/%slint2", PATH_LIBEXEC,
829 * XXX Unclear whether we should be using target_prefix
830 * XXX here. --thorpej@wasabisystems.com
832 path = xmalloc(strlen(libexec_path) + sizeof ("/lint2"));
833 (void)sprintf(path, "%s/lint2", libexec_path);
836 appcstrg(&args, path);
837 applst(&args, l2flags);
838 applst(&args, l2libs);
841 runchild(path, args, p2out, -1);
848 cat(char *const *srcs, const char *dest)
854 if ((ofd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1) {
855 warn("cannot open %s", dest);
859 buf = xmalloc(MBLKSIZ);
861 for (i = 0; (src = srcs[i]) != NULL; i++) {
862 if ((ifd = open(src, O_RDONLY)) == -1) {
864 warn("cannot open %s", src);
868 if ((rlen = read(ifd, buf, MBLKSIZ)) == -1) {
870 warn("read error on %s", src);
873 if (write(ofd, buf, (size_t)rlen) == -1) {
875 warn("write error on %s", dest);
878 } while (rlen == MBLKSIZ);