xlint.c revision 102770
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 102770 2002-09-01 12:49:27Z iedowse $");
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,-$");
348	appcstrg(&cflags, "-Wp,-C");
349	appcstrg(&cflags, "-Wcomment");
350	appcstrg(&cflags, "-D__LINT__");
351	appcstrg(&cflags, "-Dlint");		/* XXX don't def. with -s */
352
353	appdef(&cflags, "lint");
354
355	appcstrg(&lcflags, "-Wtraditional");
356
357	appcstrg(&deflibs, "c");
358
359	if (signal(SIGHUP, terminate) == SIG_IGN)
360		(void)signal(SIGHUP, SIG_IGN);
361	(void)signal(SIGINT, terminate);
362	(void)signal(SIGQUIT, terminate);
363	(void)signal(SIGTERM, terminate);
364
365	while ((c = getopt(argc, argv, "abcd:eghil:no:prstuvwxzB:C:D:FHI:L:U:VX:")) != -1) {
366		switch (c) {
367
368		case 'a':
369		case 'b':
370		case 'c':
371		case 'e':
372		case 'g':
373		case 'r':
374		case 'v':
375		case 'w':
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 'X':
392			(void)sprintf(flgbuf, "-%c", c);
393			appcstrg(&l1flags, flgbuf);
394			appcstrg(&l1flags, optarg);
395			break;
396
397		case 'i':
398			if (Cflag)
399				usage();
400			iflag = 1;
401			break;
402
403		case 'n':
404			freelst(&deflibs);
405			break;
406
407		case 'p':
408			appcstrg(&l1flags, "-p");
409			appcstrg(&l2flags, "-p");
410			if (*deflibs != NULL) {
411				freelst(&deflibs);
412				appcstrg(&deflibs, "c");
413			}
414			break;
415
416		case 's':
417			if (tflag)
418				usage();
419			freelst(&lcflags);
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");
426			sflag = 1;
427			break;
428
429#if !HAVE_CONFIG_H
430		case 't':
431			if (sflag)
432				usage();
433			freelst(&lcflags);
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");
439			tflag = 1;
440			break;
441#endif
442
443		case 'x':
444			appcstrg(&l2flags, "-x");
445			break;
446
447		case 'C':
448			if (Cflag || oflag || iflag)
449				usage();
450			Cflag = 1;
451			appstrg(&l2flags, concat2("-C", optarg));
452			p2out = xmalloc(sizeof ("llib-l.ln") + strlen(optarg));
453			(void)sprintf(p2out, "llib-l%s.ln", optarg);
454			freelst(&deflibs);
455			break;
456
457		case 'd':
458			if (dflag)
459				usage();
460			dflag = 1;
461			appcstrg(&cflags, "-nostdinc");
462			appcstrg(&cflags, "-idirafter");
463			appcstrg(&cflags, optarg);
464			break;
465
466		case 'D':
467		case 'I':
468		case 'U':
469			(void)sprintf(flgbuf, "-%c", c);
470			appstrg(&cflags, concat2(flgbuf, optarg));
471			break;
472
473		case 'l':
474			appcstrg(&libs, optarg);
475			break;
476
477		case 'o':
478			if (Cflag || oflag)
479				usage();
480			oflag = 1;
481			outputfn = xstrdup(optarg);
482			break;
483
484		case 'L':
485			appcstrg(&libsrchpath, optarg);
486			break;
487
488		case 'H':
489			appcstrg(&l2flags, "-H");
490			break;
491
492		case 'B':
493			Bflag = 1;
494			libexec_path = xstrdup(optarg);
495			break;
496
497		case 'V':
498			Vflag = 1;
499			break;
500
501		default:
502			usage();
503			/* NOTREACHED */
504		}
505	}
506	argc -= optind;
507	argv += optind;
508
509	/*
510	 * To avoid modifying getopt(3)'s state engine midstream, we
511	 * explicitly accept just a few options after the first source file.
512	 *
513	 * In particular, only -l<lib> and -L<libdir> (and these with a space
514	 * after -l or -L) are allowed.
515	 */
516	while (argc > 0) {
517		const char *arg = argv[0];
518
519		if (arg[0] == '-') {
520			char ***list;
521
522			/* option */
523			switch (arg[1]) {
524			case 'l':
525				list = &libs;
526				break;
527
528			case 'L':
529				list = &libsrchpath;
530				break;
531
532			default:
533				usage();
534				/* NOTREACHED */
535			}
536			if (arg[2])
537				appcstrg(list, arg + 2);
538			else if (argc > 1) {
539				argc--;
540				appcstrg(list, *++argv);
541			} else
542				usage();
543		} else {
544			/* filename */
545			fname(arg);
546			first = 0;
547		}
548		argc--;
549		argv++;
550	}
551
552	if (first)
553		usage();
554
555	if (iflag)
556		terminate(0);
557
558	if (!oflag) {
559		if ((s = getenv("LIBDIR")) == NULL || strlen(s) == 0)
560			s = PATH_LINTLIB;
561		appcstrg(&libsrchpath, s);
562		findlibs(libs);
563		findlibs(deflibs);
564	}
565
566	(void)printf("Lint pass2:\n");
567	lint2();
568
569	if (oflag)
570		cat(p2in, outputfn);
571
572	if (Cflag)
573		p2out = NULL;
574
575	terminate(0);
576	/* NOTREACHED */
577}
578
579/*
580 * Read a file name from the command line
581 * and pass it through lint1 if it is a C source.
582 */
583static void
584fname(const char *name)
585{
586	const	char *bn, *suff;
587	char	**args, *ofn, *p, *pathname;
588	size_t	len;
589	int is_stdin;
590	int	fd;
591
592	is_stdin = (strcmp(name, "-") == 0);
593	bn = lbasename(name, '/');
594	suff = lbasename(bn, '.');
595
596	if (strcmp(suff, "ln") == 0) {
597		/* only for lint2 */
598		if (!iflag)
599			appcstrg(&p2in, name);
600		return;
601	}
602
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);
606		return;
607	}
608
609	if (!iflag || !first)
610		(void)printf("%s:\n",
611		    is_stdin ? "{standard input}" : Fflag ? name : bn);
612
613	/* build the name of the output file of lint1 */
614	if (oflag) {
615		ofn = outputfn;
616		outputfn = NULL;
617		oflag = 0;
618	} else if (iflag) {
619		if (is_stdin) {
620			warnx("-i not supported without -o for standard input");
621			return;
622		}
623		ofn = xmalloc(strlen(bn) + (bn == suff ? 4 : 2));
624		len = bn == suff ? strlen(bn) : (suff - 1) - bn;
625		(void)sprintf(ofn, "%.*s", (int)len, bn);
626		(void)strcat(ofn, ".ln");
627	} else {
628		ofn = xmalloc(strlen(tmpdir) + sizeof ("lint1.XXXXXX"));
629		(void)sprintf(ofn, "%slint1.XXXXXX", tmpdir);
630		fd = mkstemp(ofn);
631		if (fd == -1) {
632			warn("can't make temp");
633			terminate(-1);
634		}
635		close(fd);
636	}
637	if (!iflag)
638		appcstrg(&p1out, ofn);
639
640	args = xcalloc(1, sizeof (char *));
641
642	/* run cc */
643
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);
648	} else {
649		pathname = strdup(getenv("CC"));
650		for (p = strtok(pathname, " \t"); p; p = strtok(NULL, " \t"))
651			appcstrg(&args, p);
652	}
653
654	applst(&args, cflags);
655	applst(&args, lcflags);
656	appcstrg(&args, name);
657
658	/* we reuse the same tmp file for cpp output, so rewind and truncate */
659	if (lseek(cppoutfd, SEEK_SET, (off_t)0) != 0) {
660		warn("lseek");
661		terminate(-1);
662	}
663	if (ftruncate(cppoutfd, (off_t)0) != 0) {
664		warn("ftruncate");
665		terminate(-1);
666	}
667
668	runchild(pathname, args, cppout, cppoutfd);
669	free(pathname);
670	freelst(&args);
671
672	/* run lint1 */
673
674	if (!Bflag) {
675		pathname = xmalloc(strlen(PATH_LIBEXEC) + sizeof ("/lint1") +
676		    strlen(target_prefix));
677		(void)sprintf(pathname, "%s/%slint1", PATH_LIBEXEC,
678		    target_prefix);
679	} else {
680		/*
681		 * XXX Unclear whether we should be using target_prefix
682		 * XXX here.  --thorpej@wasabisystems.com
683		 */
684		pathname = xmalloc(strlen(libexec_path) + sizeof ("/lint1"));
685		(void)sprintf(pathname, "%s/lint1", libexec_path);
686	}
687
688	appcstrg(&args, pathname);
689	applst(&args, l1flags);
690	appcstrg(&args, cppout);
691	appcstrg(&args, ofn);
692
693	runchild(pathname, args, ofn, -1);
694	free(pathname);
695	freelst(&args);
696
697	appcstrg(&p2in, ofn);
698	free(ofn);
699
700	free(args);
701}
702
703static void
704runchild(const char *path, char *const *args, const char *crfn, int fdout)
705{
706	int	status, rv, signo, i;
707
708	if (Vflag) {
709		for (i = 0; args[i] != NULL; i++)
710			(void)printf("%s ", args[i]);
711		(void)printf("\n");
712	}
713
714	currfn = crfn;
715
716	(void)fflush(stdout);
717
718	switch (vfork()) {
719	case -1:
720		warn("cannot fork");
721		terminate(-1);
722		/* NOTREACHED */
723	default:
724		/* parent */
725		break;
726	case 0:
727		/* child */
728
729		/* setup the standard output if necessary */
730		if (fdout != -1) {
731			dup2(fdout, STDOUT_FILENO);
732			close(fdout);
733		}
734		(void)execvp(path, args);
735		warn("cannot exec %s", path);
736		_exit(1);
737		/* NOTREACHED */
738	}
739
740	while ((rv = wait(&status)) == -1 && errno == EINTR) ;
741	if (rv == -1) {
742		warn("wait");
743		terminate(-1);
744	}
745	if (WIFSIGNALED(status)) {
746		signo = WTERMSIG(status);
747#if HAVE_DECL_SYS_SIGNAME
748		warnx("%s got SIG%s", path, sys_signame[signo]);
749#else
750		warnx("%s got signal %d", path, signo);
751#endif
752		terminate(-1);
753	}
754	if (WEXITSTATUS(status) != 0)
755		terminate(-1);
756	currfn = NULL;
757}
758
759static void
760findlibs(char *const *liblst)
761{
762	int	i, k;
763	const	char *lib, *path;
764	char	*lfn;
765	size_t	len;
766
767	lfn = NULL;
768
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);
774			if (rdok(lfn))
775				break;
776			lfn = xrealloc(lfn, len + sizeof ("/lint/llib-l.ln"));
777			(void)sprintf(lfn, "%s/lint/llib-l%s.ln", path, lib);
778			if (rdok(lfn))
779				break;
780		}
781		if (path != NULL) {
782			appstrg(&l2libs, concat2("-l", lfn));
783		} else {
784			warnx("cannot find llib-l%s.ln", lib);
785		}
786	}
787
788	free(lfn);
789}
790
791static int
792rdok(const char *path)
793{
794	struct	stat sbuf;
795
796	if (stat(path, &sbuf) == -1)
797		return (0);
798	if (!S_ISREG(sbuf.st_mode))
799		return (0);
800	if (access(path, R_OK) == -1)
801		return (0);
802	return (1);
803}
804
805static void
806lint2(void)
807{
808	char	*path, **args;
809
810	args = xcalloc(1, sizeof (char *));
811
812	if (!Bflag) {
813		path = xmalloc(strlen(PATH_LIBEXEC) + sizeof ("/lint2") +
814		    strlen(target_prefix));
815		(void)sprintf(path, "%s/%slint2", PATH_LIBEXEC,
816		    target_prefix);
817	} else {
818		/*
819		 * XXX Unclear whether we should be using target_prefix
820		 * XXX here.  --thorpej@wasabisystems.com
821		 */
822		path = xmalloc(strlen(libexec_path) + sizeof ("/lint2"));
823		(void)sprintf(path, "%s/lint2", libexec_path);
824	}
825
826	appcstrg(&args, path);
827	applst(&args, l2flags);
828	applst(&args, l2libs);
829	applst(&args, p2in);
830
831	runchild(path, args, p2out, -1);
832	free(path);
833	freelst(&args);
834	free(args);
835}
836
837static void
838cat(char *const *srcs, const char *dest)
839{
840	int	ifd, ofd, i;
841	char	*src, *buf;
842	ssize_t	rlen;
843
844	if ((ofd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1) {
845		warn("cannot open %s", dest);
846		terminate(-1);
847	}
848
849	buf = xmalloc(MBLKSIZ);
850
851	for (i = 0; (src = srcs[i]) != NULL; i++) {
852		if ((ifd = open(src, O_RDONLY)) == -1) {
853			free(buf);
854			warn("cannot open %s", src);
855			terminate(-1);
856		}
857		do {
858			if ((rlen = read(ifd, buf, MBLKSIZ)) == -1) {
859				free(buf);
860				warn("read error on %s", src);
861				terminate(-1);
862			}
863			if (write(ofd, buf, (size_t)rlen) == -1) {
864				free(buf);
865				warn("write error on %s", dest);
866				terminate(-1);
867			}
868		} while (rlen == MBLKSIZ);
869		(void)close(ifd);
870	}
871	(void)close(ofd);
872	free(buf);
873}
874