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