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