xlint.c revision 55959
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 55959 2000-01-14 09:25:31Z sheldonh $";
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, "-$");
325	appcstrg(&cppflags, "-C");
326	appcstrg(&cppflags, "-Wcomment");
327#ifdef __FreeBSD__
328	appcstrg(&cppflags, "-D__FreeBSD__=" __XSTRING(__FreeBSD__));
329#else
330#	error "This ain't NetBSD.  You lose!"
331	appcstrg(&cppflags, "-D__NetBSD__");
332#endif
333	appcstrg(&cppflags, "-Dlint");		/* XXX don't def. with -s */
334	appdef(&cppflags, "lint");
335	appdef(&cppflags, "unix");
336
337	appcstrg(&lcppflgs, "-Wtraditional");
338
339	if (uname(&un) == -1)
340		err(1, "uname");
341	appdef(&cppflags, un.machine);
342	appstrg(&lcppflgs, concat2("-D", un.machine));
343
344#ifdef MACHINE_ARCH
345	if (strcmp(un.machine, MACHINE_ARCH) != 0) {
346		appdef(&cppflags, MACHINE_ARCH);
347		appstrg(&lcppflgs, concat2("-D", MACHINE_ARCH));
348	}
349#endif
350
351	appcstrg(&deflibs, "c");
352
353	if (signal(SIGHUP, terminate) == SIG_IGN)
354		(void)signal(SIGHUP, SIG_IGN);
355	(void)signal(SIGINT, terminate);
356	(void)signal(SIGQUIT, terminate);
357	(void)signal(SIGTERM, terminate);
358
359	while (argc > optind) {
360
361		argc -= optind;
362		argv += optind;
363		optind = 0;
364
365		c = getopt(argc, argv, "abceghil:no:prstuvxzC:D:FHI:L:U:V");
366
367		switch (c) {
368
369		case 'a':
370		case 'b':
371		case 'c':
372		case 'e':
373		case 'g':
374		case 'r':
375		case 'v':
376		case 'z':
377			(void)sprintf(flgbuf, "-%c", c);
378			appcstrg(&l1flags, flgbuf);
379			break;
380
381		case 'F':
382			Fflag = 1;
383			/* FALLTHROUGH */
384		case 'u':
385		case 'h':
386			(void)sprintf(flgbuf, "-%c", c);
387			appcstrg(&l1flags, flgbuf);
388			appcstrg(&l2flags, flgbuf);
389			break;
390
391		case 'i':
392			if (Cflag)
393				usage();
394			iflag = 1;
395			break;
396
397		case 'n':
398			freelst(&deflibs);
399			break;
400
401		case 'p':
402			appcstrg(&l1flags, "-p");
403			appcstrg(&l2flags, "-p");
404			if (*deflibs != NULL) {
405				freelst(&deflibs);
406				appcstrg(&deflibs, "c");
407			}
408			break;
409
410		case 's':
411			if (tflag)
412				usage();
413			freelst(&lcppflgs);
414			appcstrg(&lcppflgs, "-trigraphs");
415			appcstrg(&lcppflgs, "-Wtrigraphs");
416			appcstrg(&lcppflgs, "-pedantic");
417			appcstrg(&lcppflgs, "-D__STRICT_ANSI__");
418			appcstrg(&l1flags, "-s");
419			appcstrg(&l2flags, "-s");
420			sflag = 1;
421			break;
422
423		case 't':
424			if (sflag)
425				usage();
426			freelst(&lcppflgs);
427			appcstrg(&lcppflgs, "-traditional");
428			appstrg(&lcppflgs, concat2("-D", MACHINE));
429#ifdef MACHINE_ARCH
430			appstrg(&lcppflgs, concat2("-D", MACHINE_ARCH));
431#endif
432			appcstrg(&l1flags, "-t");
433			appcstrg(&l2flags, "-t");
434			tflag = 1;
435			break;
436
437		case 'x':
438			appcstrg(&l2flags, "-x");
439			break;
440
441		case 'C':
442			if (Cflag || oflag || iflag)
443				usage();
444			Cflag = 1;
445			appstrg(&l2flags, concat2("-C", optarg));
446			p2out = xmalloc(sizeof ("llib-l.ln") + strlen(optarg));
447			(void)sprintf(p2out, "llib-l%s.ln", optarg);
448			freelst(&deflibs);
449			break;
450
451		case 'D':
452		case 'I':
453		case 'U':
454			(void)sprintf(flgbuf, "-%c", c);
455			appstrg(&cppflags, concat2(flgbuf, optarg));
456			break;
457
458		case 'l':
459			appcstrg(&libs, optarg);
460			break;
461
462		case 'o':
463			if (Cflag || oflag)
464				usage();
465			oflag = 1;
466			outputfn = xstrdup(optarg);
467			break;
468
469		case 'L':
470			appcstrg(&libsrchpath, optarg);
471			break;
472
473		case 'H':
474			appcstrg(&l2flags, "-H");
475			break;
476
477		case 'V':
478			Vflag = 1;
479			break;
480
481		case '?':
482			usage();
483			/* NOTREACHED */
484
485		case -1:
486			/* filename */
487			fname(argv[0], argc == 1);
488			first = 0;
489			optind = 1;
490		}
491
492	}
493
494	if (first)
495		usage();
496
497	if (iflag)
498		terminate(0);
499
500	if (!oflag) {
501		if ((s = getenv("LIBDIR")) == NULL || strlen(s) == 0)
502			s = PATH_LINTLIB;
503		appcstrg(&libsrchpath, s);
504		findlibs(libs);
505		findlibs(deflibs);
506	}
507
508	(void)printf("Lint pass2:\n");
509	lint2();
510
511	if (oflag)
512		cat(p2in, outputfn);
513
514	if (Cflag)
515		p2out = NULL;
516
517	terminate(0);
518	/* NOTREACHED */
519	return 0;
520}
521
522/*
523 * Read a file name from the command line
524 * and pass it through lint1 if it is a C source.
525 */
526static void
527fname(name, last)
528	const	char *name;
529	int	last;
530{
531	const	char *bn, *suff;
532	char	**args, *ofn, *path;
533	size_t	len;
534
535	bn = basename(name, '/');
536	suff = basename(bn, '.');
537
538	if (strcmp(suff, "ln") == 0) {
539		/* only for lint2 */
540		if (!iflag)
541			appcstrg(&p2in, name);
542		return;
543	}
544
545	if (strcmp(suff, "c") != 0 &&
546	    (strncmp(bn, "llib-l", 6) != 0 || bn != suff)) {
547		warnx("unknown file type: %s\n", name);
548		return;
549	}
550
551	if (!iflag || !first || !last)
552		(void)printf("%s:\n", Fflag ? name : bn);
553
554	/* build the name of the output file of lint1 */
555	if (oflag) {
556		ofn = outputfn;
557		outputfn = NULL;
558		oflag = 0;
559	} else if (iflag) {
560		ofn = xmalloc(strlen(bn) + (bn == suff ? 4 : 2));
561		len = bn == suff ? strlen(bn) : (suff - 1) - bn;
562		(void)sprintf(ofn, "%.*s", (int)len, bn);
563		(void)strcat(ofn, ".ln");
564	} else {
565		ofn = xmalloc(strlen(tmpdir) + sizeof ("lint1.XXXXXX"));
566		(void)sprintf(ofn, "%slint1.XXXXXX", tmpdir);
567		if (mktemp(ofn) == NULL) {
568			warn("can't make temp");
569			terminate(-1);
570		}
571	}
572	if (!iflag)
573		appcstrg(&p1out, ofn);
574
575	args = xcalloc(1, sizeof (char *));
576
577	/* run cpp */
578
579	path = xmalloc(strlen(PATH_LIBEXEC) + sizeof ("/cpp"));
580	(void)sprintf(path, "%s/cpp", PATH_LIBEXEC);
581
582	appcstrg(&args, path);
583	applst(&args, cppflags);
584	applst(&args, lcppflgs);
585	appcstrg(&args, name);
586	appcstrg(&args, cppout);
587
588	runchild(path, args, cppout);
589	free(path);
590	freelst(&args);
591
592	/* run lint1 */
593
594	path = xmalloc(strlen(PATH_LIBEXEC) + sizeof ("/lint1"));
595	(void)sprintf(path, "%s/lint1", PATH_LIBEXEC);
596
597	appcstrg(&args, path);
598	applst(&args, l1flags);
599	appcstrg(&args, cppout);
600	appcstrg(&args, ofn);
601
602	runchild(path, args, ofn);
603	free(path);
604	freelst(&args);
605
606	appcstrg(&p2in, ofn);
607	free(ofn);
608
609	free(args);
610}
611
612static void
613runchild(path, args, crfn)
614	const	char *path, *crfn;
615	char	*const *args;
616{
617	int	status, rv, signo, i;
618
619	if (Vflag) {
620		for (i = 0; args[i] != NULL; i++)
621			(void)printf("%s ", args[i]);
622		(void)printf("\n");
623	}
624
625	currfn = crfn;
626
627	(void)fflush(stdout);
628
629	switch (fork()) {
630	case -1:
631		warn("cannot fork");
632		terminate(-1);
633		/* NOTREACHED */
634	default:
635		/* parent */
636		break;
637	case 0:
638		/* child */
639		(void)execv(path, args);
640		warn("cannot exec %s", path);
641		exit(1);
642		/* NOTREACHED */
643	}
644
645	while ((rv = wait(&status)) == -1 && errno == EINTR) ;
646	if (rv == -1) {
647		warn("wait");
648		terminate(-1);
649	}
650	if (WIFSIGNALED(status)) {
651		signo = WTERMSIG(status);
652		warnx("%s got SIG%s", path, sys_signame[signo]);
653		terminate(-1);
654	}
655	if (WEXITSTATUS(status) != 0)
656		terminate(-1);
657	currfn = NULL;
658}
659
660static void
661findlibs(liblst)
662	char	*const *liblst;
663{
664	int	i, k;
665	const	char *lib, *path;
666	char	*lfn;
667	size_t	len;
668
669	lfn = NULL;
670
671	for (i = 0; (lib = liblst[i]) != NULL; i++) {
672		for (k = 0; (path = libsrchpath[k]) != NULL; k++) {
673			len = strlen(path) + strlen(lib);
674			lfn = xrealloc(lfn, len + sizeof ("/llib-l.ln"));
675			(void)sprintf(lfn, "%s/llib-l%s.ln", path, lib);
676			if (rdok(lfn))
677				break;
678			lfn = xrealloc(lfn, len + sizeof ("/lint/llib-l.ln"));
679			(void)sprintf(lfn, "%s/lint/llib-l%s.ln", path, lib);
680			if (rdok(lfn))
681				break;
682		}
683		if (path != NULL) {
684			appstrg(&l2libs, concat2("-l", lfn));
685		} else {
686			warnx("cannot find llib-l%s.ln", lib);
687		}
688	}
689
690	free(lfn);
691}
692
693static int
694rdok(path)
695	const	char *path;
696{
697	struct	stat sbuf;
698
699	if (stat(path, &sbuf) == -1)
700		return (0);
701	if ((sbuf.st_mode & S_IFMT) != S_IFREG)
702		return (0);
703	if (access(path, R_OK) == -1)
704		return (0);
705	return (1);
706}
707
708static void
709lint2()
710{
711	char	*path, **args;
712
713	args = xcalloc(1, sizeof (char *));
714
715	path = xmalloc(strlen(PATH_LIBEXEC) + sizeof ("/lint2"));
716	(void)sprintf(path, "%s/lint2", PATH_LIBEXEC);
717
718	appcstrg(&args, path);
719	applst(&args, l2flags);
720	applst(&args, l2libs);
721	applst(&args, p2in);
722
723	runchild(path, args, p2out);
724	free(path);
725	freelst(&args);
726	free(args);
727}
728
729static void
730cat(srcs, dest)
731	char	*const *srcs;
732	const	char *dest;
733{
734	int	ifd, ofd, i;
735	char	*src, *buf;
736	ssize_t	rlen;
737
738	if ((ofd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1) {
739		warn("cannot open %s", dest);
740		terminate(-1);
741	}
742
743	buf = xmalloc(MBLKSIZ);
744
745	for (i = 0; (src = srcs[i]) != NULL; i++) {
746		if ((ifd = open(src, O_RDONLY)) == -1) {
747			free(buf);
748			warn("cannot open %s", src);
749			terminate(-1);
750		}
751		do {
752			if ((rlen = read(ifd, buf, MBLKSIZ)) == -1) {
753				free(buf);
754				warn("read error on %s", src);
755				terminate(-1);
756			}
757			if (write(ofd, buf, (size_t)rlen) == -1) {
758				free(buf);
759				warn("write error on %s", dest);
760				terminate(-1);
761			}
762		} while (rlen == MBLKSIZ);
763		(void)close(ifd);
764	}
765	(void)close(ofd);
766	free(buf);
767}
768
769