xlint.c revision 162493
1/* $NetBSD: xlint.c,v 1.27 2002/01/31 19:09:33 tv Exp $ */
2
3/*
4 * Copyright (c) 1996 Christopher G. Demetriou.  All Rights Reserved.
5 * Copyright (c) 1994, 1995 Jochen Pohl
6 * All Rights Reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
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
19 *	The NetBSD Project.
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.
22 *
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.
33 */
34
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 $");
38#endif
39__FBSDID("$FreeBSD: head/usr.bin/xlint/xlint/xlint.c 162493 2006-09-21 02:05:38Z kan $");
40
41#include <sys/param.h>
42#include <sys/wait.h>
43#include <sys/stat.h>
44#include <sys/utsname.h>
45#include <err.h>
46#include <errno.h>
47#include <fcntl.h>
48#include <paths.h>
49#include <signal.h>
50#include <stdio.h>
51#include <stdlib.h>
52#include <string.h>
53#include <unistd.h>
54
55#include "lint.h"
56#include "pathnames.h"
57
58#define DEFAULT_PATH		_PATH_DEFPATH
59
60int main(int, char *[]);
61
62/* directory for temporary files */
63static	const	char *tmpdir;
64
65/* path name for cpp output */
66static	char	*cppout;
67
68/* file descriptor for cpp output */
69static	int	cppoutfd = -1;
70
71/* files created by 1st pass */
72static	char	**p1out;
73
74/* input files for 2nd pass (without libraries) */
75static	char	**p2in;
76
77/* library which will be created by 2nd pass */
78static	char	*p2out;
79
80/* flags always passed to cc(1) */
81static	char	**cflags;
82
83/* flags for cc(1), controled by sflag/tflag */
84static	char	**lcflags;
85
86/* flags for lint1 */
87static	char	**l1flags;
88
89/* flags for lint2 */
90static	char	**l2flags;
91
92/* libraries for lint2 */
93static	char	**l2libs;
94
95/* default libraries */
96static	char	**deflibs;
97
98/* additional libraries */
99static	char	**libs;
100
101/* search path for libraries */
102static	char	**libsrchpath;
103
104static  char	*libexec_path;
105
106/* flags */
107static	int	iflag, oflag, Cflag, sflag, tflag, Fflag, dflag, Bflag;
108
109/* print the commands executed to run the stages of compilation */
110static	int	Vflag;
111
112/* filename for oflag */
113static	char	*outputfn;
114
115/* reset after first .c source has been processed */
116static	int	first = 1;
117
118/*
119 * name of a file which is currently written by a child and should
120 * be removed after abnormal termination of the child
121 */
122static	const	char *currfn;
123
124#if !defined(TARGET_PREFIX)
125#define	TARGET_PREFIX	""
126#endif
127static const char target_prefix[] = TARGET_PREFIX;
128
129static	void	appstrg(char ***, char *);
130static	void	appcstrg(char ***, const char *);
131static	void	applst(char ***, char *const *);
132static	void	freelst(char ***);
133static	char	*concat2(const char *, const char *);
134static	char	*concat3(const char *, const char *, const char *);
135static	void	terminate(int) __attribute__((__noreturn__));
136static	const	char *lbasename(const char *, int);
137static	void	appdef(char ***, const char *);
138static	void	usage(void);
139static	void	fname(const char *);
140static	void	runchild(const char *, char *const *, const char *, int);
141static	void	findlibs(char *const *);
142static	int	rdok(const char *);
143static	void	lint2(void);
144static	void	cat(char *const *, const char *);
145
146/*
147 * Some functions to deal with lists of strings.
148 * Take care that we get no surprises in case of asyncron signals.
149 */
150static void
151appstrg(char ***lstp, char *s)
152{
153	char	**lst, **olst;
154	int	i;
155
156	olst = *lstp;
157	for (i = 0; olst[i] != NULL; i++)
158		continue;
159	lst = xrealloc(olst, (i + 2) * sizeof (char *));
160	lst[i] = s;
161	lst[i + 1] = NULL;
162	*lstp = lst;
163}
164
165static void
166appcstrg(char ***lstp, const char *s)
167{
168
169	appstrg(lstp, xstrdup(s));
170}
171
172static void
173applst(char ***destp, char *const *src)
174{
175	int	i, k;
176	char	**dest, **odest;
177
178	odest = *destp;
179	for (i = 0; odest[i] != NULL; i++)
180		continue;
181	for (k = 0; src[k] != NULL; k++)
182		continue;
183	dest = xrealloc(odest, (i + k + 1) * sizeof (char *));
184	for (k = 0; src[k] != NULL; k++)
185		dest[i + k] = xstrdup(src[k]);
186	dest[i + k] = NULL;
187	*destp = dest;
188}
189
190static void
191freelst(char ***lstp)
192{
193	char	*s;
194	int	i;
195
196	for (i = 0; (*lstp)[i] != NULL; i++)
197		continue;
198	while (i-- > 0) {
199		s = (*lstp)[i];
200		(*lstp)[i] = NULL;
201		free(s);
202	}
203}
204
205static char *
206concat2(const char *s1, const char *s2)
207{
208	char	*s;
209
210	s = xmalloc(strlen(s1) + strlen(s2) + 1);
211	(void)strcpy(s, s1);
212	(void)strcat(s, s2);
213
214	return (s);
215}
216
217static char *
218concat3(const char *s1, const char *s2, const char *s3)
219{
220	char	*s;
221
222	s = xmalloc(strlen(s1) + strlen(s2) + strlen(s3) + 1);
223	(void)strcpy(s, s1);
224	(void)strcat(s, s2);
225	(void)strcat(s, s3);
226
227	return (s);
228}
229
230/*
231 * Clean up after a signal.
232 */
233static void
234terminate(int signo)
235{
236	int	i;
237
238	if (cppoutfd != -1)
239		(void)close(cppoutfd);
240	if (cppout != NULL)
241		(void)remove(cppout);
242
243	if (p1out != NULL) {
244		for (i = 0; p1out[i] != NULL; i++)
245			(void)remove(p1out[i]);
246	}
247
248	if (p2out != NULL)
249		(void)remove(p2out);
250
251	if (currfn != NULL)
252		(void)remove(currfn);
253
254	exit(signo != 0 ? 1 : 0);
255}
256
257/*
258 * Returns a pointer to the last component of strg after delim.
259 * Returns strg if the string does not contain delim.
260 */
261static const char *
262lbasename(const char *strg, int delim)
263{
264	const	char *cp, *cp1, *cp2;
265
266	cp = cp1 = cp2 = strg;
267	while (*cp != '\0') {
268		if (*cp++ == delim) {
269			cp2 = cp1;
270			cp1 = cp;
271		}
272	}
273	return (*cp1 == '\0' ? cp2 : cp1);
274}
275
276static void
277appdef(char ***lstp, const char *def)
278{
279
280	appstrg(lstp, concat2("-D__", def));
281	appstrg(lstp, concat3("-D__", def, "__"));
282}
283
284static void
285usage(void)
286{
287
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]"
293	    " file...\n");
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"
298	    " ...\n");
299	terminate(-1);
300}
301
302
303int
304main(int argc, char *argv[])
305{
306	int	c;
307	char	flgbuf[3], *tmp, *s;
308	size_t	len;
309
310	if ((tmp = getenv("TMPDIR")) == NULL || (len = strlen(tmp)) == 0) {
311		tmpdir = xstrdup(_PATH_TMP);
312	} else {
313		s = xmalloc(len + 2);
314		(void)sprintf(s, "%s%s", tmp, tmp[len - 1] == '/' ? "" : "/");
315		tmpdir = s;
316	}
317
318	cppout = xmalloc(strlen(tmpdir) + sizeof ("lint0.XXXXXX"));
319	(void)sprintf(cppout, "%slint0.XXXXXX", tmpdir);
320	cppoutfd = mkstemp(cppout);
321	if (cppoutfd == -1) {
322		warn("can't make temp");
323		terminate(-1);
324	}
325
326	p1out = xcalloc(1, sizeof (char *));
327	p2in = xcalloc(1, sizeof (char *));
328	cflags = xcalloc(1, sizeof (char *));
329	lcflags = xcalloc(1, sizeof (char *));
330	l1flags = xcalloc(1, sizeof (char *));
331	l2flags = xcalloc(1, sizeof (char *));
332	l2libs = xcalloc(1, sizeof (char *));
333	deflibs = xcalloc(1, sizeof (char *));
334	libs = xcalloc(1, sizeof (char *));
335	libsrchpath = xcalloc(1, sizeof (char *));
336
337	appcstrg(&cflags, "-E");
338	appcstrg(&cflags, "-x");
339	appcstrg(&cflags, "c");
340#if 0
341	appcstrg(&cflags, "-D__attribute__(x)=");
342	appcstrg(&cflags, "-D__extension__(x)=/*NOSTRICT*/0");
343#else
344	appcstrg(&cflags, "-U__GNUC__");
345	appcstrg(&cflags, "-undef");
346#endif
347	appcstrg(&cflags, "-Wp,-C");
348	appcstrg(&cflags, "-Wcomment");
349	appcstrg(&cflags, "-D__LINT__");
350	appcstrg(&cflags, "-Dlint");		/* XXX don't def. with -s */
351
352	appdef(&cflags, "lint");
353
354	appcstrg(&deflibs, "c");
355
356	if (signal(SIGHUP, terminate) == SIG_IGN)
357		(void)signal(SIGHUP, SIG_IGN);
358	(void)signal(SIGINT, terminate);
359	(void)signal(SIGQUIT, terminate);
360	(void)signal(SIGTERM, terminate);
361
362	while ((c = getopt(argc, argv, "abcd:eghil:no:prstuvwxzB:C:D:FHI:L:U:VX:")) != -1) {
363		switch (c) {
364
365		case 'a':
366		case 'b':
367		case 'c':
368		case 'e':
369		case 'g':
370		case 'r':
371		case 'v':
372		case 'w':
373		case 'z':
374			(void)sprintf(flgbuf, "-%c", c);
375			appcstrg(&l1flags, flgbuf);
376			break;
377
378		case 'F':
379			Fflag = 1;
380			/* FALLTHROUGH */
381		case 'u':
382		case 'h':
383			(void)sprintf(flgbuf, "-%c", c);
384			appcstrg(&l1flags, flgbuf);
385			appcstrg(&l2flags, flgbuf);
386			break;
387
388		case 'X':
389			(void)sprintf(flgbuf, "-%c", c);
390			appcstrg(&l1flags, flgbuf);
391			appcstrg(&l1flags, optarg);
392			break;
393
394		case 'i':
395			if (Cflag)
396				usage();
397			iflag = 1;
398			break;
399
400		case 'n':
401			freelst(&deflibs);
402			break;
403
404		case 'p':
405			appcstrg(&lcflags, "-Wtraditional");
406			appcstrg(&lcflags, "-Wno-system-headers");
407			appcstrg(&l1flags, "-p");
408			appcstrg(&l2flags, "-p");
409			if (*deflibs != NULL) {
410				freelst(&deflibs);
411				appcstrg(&deflibs, "c");
412			}
413			break;
414
415		case 's':
416			if (tflag)
417				usage();
418			freelst(&lcflags);
419			appcstrg(&lcflags, "-trigraphs");
420			appcstrg(&lcflags, "-Wtrigraphs");
421			appcstrg(&lcflags, "-pedantic");
422			appcstrg(&lcflags, "-D__STRICT_ANSI__");
423			appcstrg(&l1flags, "-s");
424			appcstrg(&l2flags, "-s");
425			sflag = 1;
426			break;
427
428#if !HAVE_CONFIG_H
429		case 't':
430			if (sflag)
431				usage();
432			freelst(&lcflags);
433			appcstrg(&lcflags, "-traditional");
434			appstrg(&lcflags, concat2("-D", MACHINE));
435			appstrg(&lcflags, concat2("-D", MACHINE_ARCH));
436			appcstrg(&l1flags, "-t");
437			appcstrg(&l2flags, "-t");
438			tflag = 1;
439			break;
440#endif
441
442		case 'x':
443			appcstrg(&l2flags, "-x");
444			break;
445
446		case 'C':
447			if (Cflag || oflag || iflag)
448				usage();
449			Cflag = 1;
450			appstrg(&l2flags, concat2("-C", optarg));
451			p2out = xmalloc(sizeof ("llib-l.ln") + strlen(optarg));
452			(void)sprintf(p2out, "llib-l%s.ln", optarg);
453			freelst(&deflibs);
454			break;
455
456		case 'd':
457			if (dflag)
458				usage();
459			dflag = 1;
460			appcstrg(&cflags, "-nostdinc");
461			appcstrg(&cflags, "-idirafter");
462			appcstrg(&cflags, optarg);
463			break;
464
465		case 'D':
466		case 'I':
467		case 'U':
468			(void)sprintf(flgbuf, "-%c", c);
469			appstrg(&cflags, concat2(flgbuf, optarg));
470			break;
471
472		case 'l':
473			appcstrg(&libs, optarg);
474			break;
475
476		case 'o':
477			if (Cflag || oflag)
478				usage();
479			oflag = 1;
480			outputfn = xstrdup(optarg);
481			break;
482
483		case 'L':
484			appcstrg(&libsrchpath, optarg);
485			break;
486
487		case 'H':
488			appcstrg(&l2flags, "-H");
489			break;
490
491		case 'B':
492			Bflag = 1;
493			libexec_path = xstrdup(optarg);
494			break;
495
496		case 'V':
497			Vflag = 1;
498			break;
499
500		default:
501			usage();
502			/* NOTREACHED */
503		}
504	}
505	argc -= optind;
506	argv += optind;
507
508	/*
509	 * To avoid modifying getopt(3)'s state engine midstream, we
510	 * explicitly accept just a few options after the first source file.
511	 *
512	 * In particular, only -l<lib> and -L<libdir> (and these with a space
513	 * after -l or -L) are allowed.
514	 */
515	while (argc > 0) {
516		const char *arg = argv[0];
517
518		if (arg[0] == '-') {
519			char ***list;
520
521			/* option */
522			switch (arg[1]) {
523			case 'l':
524				list = &libs;
525				break;
526
527			case 'L':
528				list = &libsrchpath;
529				break;
530
531			default:
532				usage();
533				/* NOTREACHED */
534			}
535			if (arg[2])
536				appcstrg(list, arg + 2);
537			else if (argc > 1) {
538				argc--;
539				appcstrg(list, *++argv);
540			} else
541				usage();
542		} else {
543			/* filename */
544			fname(arg);
545			first = 0;
546		}
547		argc--;
548		argv++;
549	}
550
551	if (first)
552		usage();
553
554	if (iflag)
555		terminate(0);
556
557	if (!oflag) {
558		if ((s = getenv("LIBDIR")) == NULL || strlen(s) == 0)
559			s = PATH_LINTLIB;
560		appcstrg(&libsrchpath, s);
561		findlibs(libs);
562		findlibs(deflibs);
563	}
564
565	(void)printf("Lint pass2:\n");
566	lint2();
567
568	if (oflag)
569		cat(p2in, outputfn);
570
571	if (Cflag)
572		p2out = NULL;
573
574	terminate(0);
575	/* NOTREACHED */
576}
577
578/*
579 * Read a file name from the command line
580 * and pass it through lint1 if it is a C source.
581 */
582static void
583fname(const char *name)
584{
585	const	char *bn, *suff;
586	char	**args, *ofn, *p, *pathname;
587	size_t	len;
588	int is_stdin;
589	int	fd;
590
591	is_stdin = (strcmp(name, "-") == 0);
592	bn = lbasename(name, '/');
593	suff = lbasename(bn, '.');
594
595	if (strcmp(suff, "ln") == 0) {
596		/* only for lint2 */
597		if (!iflag)
598			appcstrg(&p2in, name);
599		return;
600	}
601
602	if (!is_stdin && strcmp(suff, "c") != 0 &&
603	    (strncmp(bn, "llib-l", 6) != 0 || bn != suff)) {
604		warnx("unknown file type: %s\n", name);
605		return;
606	}
607
608	if (!iflag || !first)
609		(void)printf("%s:\n",
610		    is_stdin ? "{standard input}" : Fflag ? name : bn);
611
612	/* build the name of the output file of lint1 */
613	if (oflag) {
614		ofn = outputfn;
615		outputfn = NULL;
616		oflag = 0;
617	} else if (iflag) {
618		if (is_stdin) {
619			warnx("-i not supported without -o for standard input");
620			return;
621		}
622		ofn = xmalloc(strlen(bn) + (bn == suff ? 4 : 2));
623		len = bn == suff ? strlen(bn) : (suff - 1) - bn;
624		(void)sprintf(ofn, "%.*s", (int)len, bn);
625		(void)strcat(ofn, ".ln");
626	} else {
627		ofn = xmalloc(strlen(tmpdir) + sizeof ("lint1.XXXXXX"));
628		(void)sprintf(ofn, "%slint1.XXXXXX", tmpdir);
629		fd = mkstemp(ofn);
630		if (fd == -1) {
631			warn("can't make temp");
632			terminate(-1);
633		}
634		close(fd);
635	}
636	if (!iflag)
637		appcstrg(&p1out, ofn);
638
639	args = xcalloc(1, sizeof (char *));
640
641	/* run cc */
642
643	if (getenv("CC") == NULL) {
644		pathname = xmalloc(strlen(PATH_USRBIN) + sizeof ("/cc"));
645		(void)sprintf(pathname, "%s/cc", PATH_USRBIN);
646		appcstrg(&args, pathname);
647	} else {
648		pathname = strdup(getenv("CC"));
649		for (p = strtok(pathname, " \t"); p; p = strtok(NULL, " \t"))
650			appcstrg(&args, p);
651	}
652
653	applst(&args, cflags);
654	applst(&args, lcflags);
655	appcstrg(&args, name);
656
657	/* we reuse the same tmp file for cpp output, so rewind and truncate */
658	if (lseek(cppoutfd, SEEK_SET, (off_t)0) != 0) {
659		warn("lseek");
660		terminate(-1);
661	}
662	if (ftruncate(cppoutfd, (off_t)0) != 0) {
663		warn("ftruncate");
664		terminate(-1);
665	}
666
667	runchild(pathname, args, cppout, cppoutfd);
668	free(pathname);
669	freelst(&args);
670
671	/* run lint1 */
672
673	if (!Bflag) {
674		pathname = xmalloc(strlen(PATH_LIBEXEC) + sizeof ("/lint1") +
675		    strlen(target_prefix));
676		(void)sprintf(pathname, "%s/%slint1", PATH_LIBEXEC,
677		    target_prefix);
678	} else {
679		/*
680		 * XXX Unclear whether we should be using target_prefix
681		 * XXX here.  --thorpej@wasabisystems.com
682		 */
683		pathname = xmalloc(strlen(libexec_path) + sizeof ("/lint1"));
684		(void)sprintf(pathname, "%s/lint1", libexec_path);
685	}
686
687	appcstrg(&args, pathname);
688	applst(&args, l1flags);
689	appcstrg(&args, cppout);
690	appcstrg(&args, ofn);
691
692	runchild(pathname, args, ofn, -1);
693	free(pathname);
694	freelst(&args);
695
696	appcstrg(&p2in, ofn);
697	free(ofn);
698
699	free(args);
700}
701
702static void
703runchild(const char *path, char *const *args, const char *crfn, int fdout)
704{
705	int	status, rv, signo, i;
706
707	if (Vflag) {
708		for (i = 0; args[i] != NULL; i++)
709			(void)printf("%s ", args[i]);
710		(void)printf("\n");
711	}
712
713	currfn = crfn;
714
715	(void)fflush(stdout);
716
717	switch (vfork()) {
718	case -1:
719		warn("cannot fork");
720		terminate(-1);
721		/* NOTREACHED */
722	default:
723		/* parent */
724		break;
725	case 0:
726		/* child */
727
728		/* setup the standard output if necessary */
729		if (fdout != -1) {
730			dup2(fdout, STDOUT_FILENO);
731			close(fdout);
732		}
733		(void)execvp(path, args);
734		warn("cannot exec %s", path);
735		_exit(1);
736		/* NOTREACHED */
737	}
738
739	while ((rv = wait(&status)) == -1 && errno == EINTR) ;
740	if (rv == -1) {
741		warn("wait");
742		terminate(-1);
743	}
744	if (WIFSIGNALED(status)) {
745		signo = WTERMSIG(status);
746#if HAVE_DECL_SYS_SIGNAME
747		warnx("%s got SIG%s", path, sys_signame[signo]);
748#else
749		warnx("%s got signal %d", path, signo);
750#endif
751		terminate(-1);
752	}
753	if (WEXITSTATUS(status) != 0)
754		terminate(-1);
755	currfn = NULL;
756}
757
758static void
759findlibs(char *const *liblst)
760{
761	int	i, k;
762	const	char *lib, *path;
763	char	*lfn;
764	size_t	len;
765
766	lfn = NULL;
767
768	for (i = 0; (lib = liblst[i]) != NULL; i++) {
769		for (k = 0; (path = libsrchpath[k]) != NULL; k++) {
770			len = strlen(path) + strlen(lib);
771			lfn = xrealloc(lfn, len + sizeof ("/llib-l.ln"));
772			(void)sprintf(lfn, "%s/llib-l%s.ln", path, lib);
773			if (rdok(lfn))
774				break;
775			lfn = xrealloc(lfn, len + sizeof ("/lint/llib-l.ln"));
776			(void)sprintf(lfn, "%s/lint/llib-l%s.ln", path, lib);
777			if (rdok(lfn))
778				break;
779		}
780		if (path != NULL) {
781			appstrg(&l2libs, concat2("-l", lfn));
782		} else {
783			warnx("cannot find llib-l%s.ln", lib);
784		}
785	}
786
787	free(lfn);
788}
789
790static int
791rdok(const char *path)
792{
793	struct	stat sbuf;
794
795	if (stat(path, &sbuf) == -1)
796		return (0);
797	if (!S_ISREG(sbuf.st_mode))
798		return (0);
799	if (access(path, R_OK) == -1)
800		return (0);
801	return (1);
802}
803
804static void
805lint2(void)
806{
807	char	*path, **args;
808
809	args = xcalloc(1, sizeof (char *));
810
811	if (!Bflag) {
812		path = xmalloc(strlen(PATH_LIBEXEC) + sizeof ("/lint2") +
813		    strlen(target_prefix));
814		(void)sprintf(path, "%s/%slint2", PATH_LIBEXEC,
815		    target_prefix);
816	} else {
817		/*
818		 * XXX Unclear whether we should be using target_prefix
819		 * XXX here.  --thorpej@wasabisystems.com
820		 */
821		path = xmalloc(strlen(libexec_path) + sizeof ("/lint2"));
822		(void)sprintf(path, "%s/lint2", libexec_path);
823	}
824
825	appcstrg(&args, path);
826	applst(&args, l2flags);
827	applst(&args, l2libs);
828	applst(&args, p2in);
829
830	runchild(path, args, p2out, -1);
831	free(path);
832	freelst(&args);
833	free(args);
834}
835
836static void
837cat(char *const *srcs, const char *dest)
838{
839	int	ifd, ofd, i;
840	char	*src, *buf;
841	ssize_t	rlen;
842
843	if ((ofd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1) {
844		warn("cannot open %s", dest);
845		terminate(-1);
846	}
847
848	buf = xmalloc(MBLKSIZ);
849
850	for (i = 0; (src = srcs[i]) != NULL; i++) {
851		if ((ifd = open(src, O_RDONLY)) == -1) {
852			free(buf);
853			warn("cannot open %s", src);
854			terminate(-1);
855		}
856		do {
857			if ((rlen = read(ifd, buf, MBLKSIZ)) == -1) {
858				free(buf);
859				warn("read error on %s", src);
860				terminate(-1);
861			}
862			if (write(ofd, buf, (size_t)rlen) == -1) {
863				free(buf);
864				warn("write error on %s", dest);
865				terminate(-1);
866			}
867		} while (rlen == MBLKSIZ);
868		(void)close(ifd);
869	}
870	(void)close(ofd);
871	free(buf);
872}
873