1 /* $NetBSD: xlint.c,v 1.27 2002/01/31 19:09:33 tv 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.27 2002/01/31 19:09:33 tv 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;
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 asyncron 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 [-abceghprvwxzHF] [-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 [-abceghprvwzHF] [-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");
348 appcstrg(&cflags, "-Wp,-C");
349 appcstrg(&cflags, "-Wcomment");
350 appcstrg(&cflags, "-D__LINT__");
351 appcstrg(&cflags, "-Dlint"); /* XXX don't def. with -s */
353 appdef(&cflags, "lint");
355 appcstrg(&deflibs, "c");
357 if (signal(SIGHUP, terminate) == SIG_IGN)
358 (void)signal(SIGHUP, SIG_IGN);
359 (void)signal(SIGINT, terminate);
360 (void)signal(SIGQUIT, terminate);
361 (void)signal(SIGTERM, terminate);
363 while ((c = getopt(argc, argv, "abcd:eghil:no:prstuvwxzB:C:D:FHI:L:U:VX:")) != -1) {
375 (void)sprintf(flgbuf, "-%c", c);
376 appcstrg(&l1flags, flgbuf);
384 (void)sprintf(flgbuf, "-%c", c);
385 appcstrg(&l1flags, flgbuf);
386 appcstrg(&l2flags, flgbuf);
390 (void)sprintf(flgbuf, "-%c", c);
391 appcstrg(&l1flags, flgbuf);
392 appcstrg(&l1flags, optarg);
406 appcstrg(&lcflags, "-Wtraditional");
407 appcstrg(&lcflags, "-Wno-system-headers");
408 appcstrg(&l1flags, "-p");
409 appcstrg(&l2flags, "-p");
410 if (*deflibs != NULL) {
412 appcstrg(&deflibs, "c");
420 appcstrg(&lcflags, "-trigraphs");
421 appcstrg(&lcflags, "-Wtrigraphs");
422 appcstrg(&lcflags, "-pedantic");
423 appcstrg(&lcflags, "-D__STRICT_ANSI__");
424 appcstrg(&l1flags, "-s");
425 appcstrg(&l2flags, "-s");
434 appcstrg(&lcflags, "-traditional");
435 appstrg(&lcflags, concat2("-D", MACHINE));
436 appstrg(&lcflags, concat2("-D", MACHINE_ARCH));
437 appcstrg(&l1flags, "-t");
438 appcstrg(&l2flags, "-t");
444 appcstrg(&l2flags, "-x");
448 if (Cflag || oflag || iflag)
451 appstrg(&l2flags, concat2("-C", optarg));
452 p2out = xmalloc(sizeof ("llib-l.ln") + strlen(optarg));
453 (void)sprintf(p2out, "llib-l%s.ln", optarg);
461 appcstrg(&cflags, "-nostdinc");
462 appcstrg(&cflags, "-idirafter");
463 appcstrg(&cflags, optarg);
469 (void)sprintf(flgbuf, "-%c", c);
470 appstrg(&cflags, concat2(flgbuf, optarg));
474 appcstrg(&libs, optarg);
481 outputfn = xstrdup(optarg);
485 appcstrg(&libsrchpath, optarg);
489 appcstrg(&l2flags, "-H");
494 libexec_path = xstrdup(optarg);
510 * To avoid modifying getopt(3)'s state engine midstream, we
511 * explicitly accept just a few options after the first source file.
513 * In particular, only -l<lib> and -L<libdir> (and these with a space
514 * after -l or -L) are allowed.
517 const char *arg = argv[0];
537 appcstrg(list, arg + 2);
540 appcstrg(list, *++argv);
559 if ((tmp = getenv("LIBDIR")) == NULL || strlen(tmp) == 0)
561 appcstrg(&libsrchpath, tmp);
566 (void)printf("Lint pass2:\n");
580 * Read a file name from the command line
581 * and pass it through lint1 if it is a C source.
584 fname(const char *name)
586 const char *bn, *suff;
587 char **args, *ofn, *p, *pathname;
592 is_stdin = (strcmp(name, "-") == 0);
593 bn = lbasename(name, '/');
594 suff = lbasename(bn, '.');
596 if (strcmp(suff, "ln") == 0) {
599 appcstrg(&p2in, name);
603 if (!is_stdin && strcmp(suff, "c") != 0 &&
604 (strncmp(bn, "llib-l", 6) != 0 || bn != suff)) {
605 warnx("unknown file type: %s\n", name);
609 if (!iflag || !first)
610 (void)printf("%s:\n",
611 is_stdin ? "{standard input}" : Fflag ? name : bn);
613 /* build the name of the output file of lint1 */
620 warnx("-i not supported without -o for standard input");
623 ofn = xmalloc(strlen(bn) + (bn == suff ? 4 : 2));
624 len = bn == suff ? strlen(bn) : (size_t)((suff - 1) - bn);
625 (void)sprintf(ofn, "%.*s", (int)len, bn);
626 (void)strcat(ofn, ".ln");
628 ofn = xmalloc(strlen(tmpdir) + sizeof ("lint1.XXXXXX"));
629 (void)sprintf(ofn, "%slint1.XXXXXX", tmpdir);
632 warn("can't make temp");
638 appcstrg(&p1out, ofn);
640 args = xcalloc(1, sizeof (char *));
644 if (getenv("CC") == NULL) {
645 pathname = xmalloc(strlen(PATH_USRBIN) + sizeof ("/cc"));
646 (void)sprintf(pathname, "%s/cc", PATH_USRBIN);
647 appcstrg(&args, pathname);
649 pathname = strdup(getenv("CC"));
650 for (p = strtok(pathname, " \t"); p; p = strtok(NULL, " \t"))
654 applst(&args, cflags);
655 applst(&args, lcflags);
656 appcstrg(&args, name);
658 /* we reuse the same tmp file for cpp output, so rewind and truncate */
659 if (lseek(cppoutfd, (off_t)0, SEEK_SET) != 0) {
663 if (ftruncate(cppoutfd, (off_t)0) != 0) {
668 runchild(pathname, args, cppout, cppoutfd);
675 pathname = xmalloc(strlen(PATH_LIBEXEC) + sizeof ("/lint1") +
676 strlen(target_prefix));
677 (void)sprintf(pathname, "%s/%slint1", PATH_LIBEXEC,
681 * XXX Unclear whether we should be using target_prefix
682 * XXX here. --thorpej@wasabisystems.com
684 pathname = xmalloc(strlen(libexec_path) + sizeof ("/lint1"));
685 (void)sprintf(pathname, "%s/lint1", libexec_path);
688 appcstrg(&args, pathname);
689 applst(&args, l1flags);
690 appcstrg(&args, cppout);
691 appcstrg(&args, ofn);
693 runchild(pathname, args, ofn, -1);
697 appcstrg(&p2in, ofn);
704 runchild(const char *path, char *const *args, const char *crfn, int fdout)
706 int status, rv, signo, i;
709 for (i = 0; args[i] != NULL; i++)
710 (void)printf("%s ", args[i]);
716 (void)fflush(stdout);
729 /* setup the standard output if necessary */
731 dup2(fdout, STDOUT_FILENO);
734 (void)execvp(path, args);
735 warn("cannot exec %s", path);
740 while ((rv = wait(&status)) == -1 && errno == EINTR) ;
745 if (WIFSIGNALED(status)) {
746 signo = WTERMSIG(status);
747 #if HAVE_DECL_SYS_SIGNAME
748 warnx("%s got SIG%s", path, sys_signame[signo]);
750 warnx("%s got signal %d", path, signo);
754 if (WEXITSTATUS(status) != 0)
760 findlibs(char *const *liblst)
763 const char *lib, *path;
769 for (i = 0; (lib = liblst[i]) != NULL; i++) {
770 for (k = 0; (path = libsrchpath[k]) != NULL; k++) {
771 len = strlen(path) + strlen(lib);
772 lfn = xrealloc(lfn, len + sizeof ("/llib-l.ln"));
773 (void)sprintf(lfn, "%s/llib-l%s.ln", path, lib);
776 lfn = xrealloc(lfn, len + sizeof ("/lint/llib-l.ln"));
777 (void)sprintf(lfn, "%s/lint/llib-l%s.ln", path, lib);
782 appstrg(&l2libs, concat2("-l", lfn));
784 warnx("cannot find llib-l%s.ln", lib);
792 rdok(const char *path)
796 if (stat(path, &sbuf) == -1)
798 if (!S_ISREG(sbuf.st_mode))
800 if (access(path, R_OK) == -1)
810 args = xcalloc(1, sizeof (char *));
813 path = xmalloc(strlen(PATH_LIBEXEC) + sizeof ("/lint2") +
814 strlen(target_prefix));
815 (void)sprintf(path, "%s/%slint2", PATH_LIBEXEC,
819 * XXX Unclear whether we should be using target_prefix
820 * XXX here. --thorpej@wasabisystems.com
822 path = xmalloc(strlen(libexec_path) + sizeof ("/lint2"));
823 (void)sprintf(path, "%s/lint2", libexec_path);
826 appcstrg(&args, path);
827 applst(&args, l2flags);
828 applst(&args, l2libs);
831 runchild(path, args, p2out, -1);
838 cat(char *const *srcs, const char *dest)
844 if ((ofd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1) {
845 warn("cannot open %s", dest);
849 buf = xmalloc(MBLKSIZ);
851 for (i = 0; (src = srcs[i]) != NULL; i++) {
852 if ((ifd = open(src, O_RDONLY)) == -1) {
854 warn("cannot open %s", src);
858 if ((rlen = read(ifd, buf, MBLKSIZ)) == -1) {
860 warn("read error on %s", src);
863 if (write(ofd, buf, (size_t)rlen) == -1) {
865 warn("write error on %s", dest);
868 } while (rlen == MBLKSIZ);