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