xref: /netbsd/usr.bin/xlint/xlint/xlint.c (revision bf9ec67e)
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 
40 #include <sys/param.h>
41 #include <sys/wait.h>
42 #include <sys/stat.h>
43 #include <sys/utsname.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <paths.h>
47 #include <signal.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52 
53 #include "lint.h"
54 #include "pathnames.h"
55 
56 int main(int, char *[]);
57 
58 /* directory for temporary files */
59 static	const	char *tmpdir;
60 
61 /* path name for cpp output */
62 static	char	*cppout;
63 
64 /* file descriptor for cpp output */
65 static	int	cppoutfd = -1;
66 
67 /* files created by 1st pass */
68 static	char	**p1out;
69 
70 /* input files for 2nd pass (without libraries) */
71 static	char	**p2in;
72 
73 /* library which will be created by 2nd pass */
74 static	char	*p2out;
75 
76 /* flags always passed to cc(1) */
77 static	char	**cflags;
78 
79 /* flags for cc(1), controled by sflag/tflag */
80 static	char	**lcflags;
81 
82 /* flags for lint1 */
83 static	char	**l1flags;
84 
85 /* flags for lint2 */
86 static	char	**l2flags;
87 
88 /* libraries for lint2 */
89 static	char	**l2libs;
90 
91 /* default libraries */
92 static	char	**deflibs;
93 
94 /* additional libraries */
95 static	char	**libs;
96 
97 /* search path for libraries */
98 static	char	**libsrchpath;
99 
100 static  char	*libexec_path;
101 
102 /* flags */
103 static	int	iflag, oflag, Cflag, sflag, tflag, Fflag, dflag, Bflag;
104 
105 /* print the commands executed to run the stages of compilation */
106 static	int	Vflag;
107 
108 /* filename for oflag */
109 static	char	*outputfn;
110 
111 /* reset after first .c source has been processed */
112 static	int	first = 1;
113 
114 /*
115  * name of a file which is currently written by a child and should
116  * be removed after abnormal termination of the child
117  */
118 static	const	char *currfn;
119 
120 #if !defined(TARGET_PREFIX)
121 #define	TARGET_PREFIX	""
122 #endif
123 static const char target_prefix[] = TARGET_PREFIX;
124 
125 static	void	appstrg(char ***, char *);
126 static	void	appcstrg(char ***, const char *);
127 static	void	applst(char ***, char *const *);
128 static	void	freelst(char ***);
129 static	char	*concat2(const char *, const char *);
130 static	char	*concat3(const char *, const char *, const char *);
131 static	void	terminate(int) __attribute__((__noreturn__));
132 static	const	char *lbasename(const char *, int);
133 static	void	appdef(char ***, const char *);
134 static	void	usage(void);
135 static	void	fname(const char *);
136 static	void	runchild(const char *, char *const *, const char *, int);
137 static	void	findlibs(char *const *);
138 static	int	rdok(const char *);
139 static	void	lint2(void);
140 static	void	cat(char *const *, const char *);
141 
142 /*
143  * Some functions to deal with lists of strings.
144  * Take care that we get no surprises in case of asyncron signals.
145  */
146 static void
147 appstrg(char ***lstp, char *s)
148 {
149 	char	**lst, **olst;
150 	int	i;
151 
152 	olst = *lstp;
153 	for (i = 0; olst[i] != NULL; i++)
154 		continue;
155 	lst = xrealloc(olst, (i + 2) * sizeof (char *));
156 	lst[i] = s;
157 	lst[i + 1] = NULL;
158 	*lstp = lst;
159 }
160 
161 static void
162 appcstrg(char ***lstp, const char *s)
163 {
164 
165 	appstrg(lstp, xstrdup(s));
166 }
167 
168 static void
169 applst(char ***destp, char *const *src)
170 {
171 	int	i, k;
172 	char	**dest, **odest;
173 
174 	odest = *destp;
175 	for (i = 0; odest[i] != NULL; i++)
176 		continue;
177 	for (k = 0; src[k] != NULL; k++)
178 		continue;
179 	dest = xrealloc(odest, (i + k + 1) * sizeof (char *));
180 	for (k = 0; src[k] != NULL; k++)
181 		dest[i + k] = xstrdup(src[k]);
182 	dest[i + k] = NULL;
183 	*destp = dest;
184 }
185 
186 static void
187 freelst(char ***lstp)
188 {
189 	char	*s;
190 	int	i;
191 
192 	for (i = 0; (*lstp)[i] != NULL; i++)
193 		continue;
194 	while (i-- > 0) {
195 		s = (*lstp)[i];
196 		(*lstp)[i] = NULL;
197 		free(s);
198 	}
199 }
200 
201 static char *
202 concat2(const char *s1, const char *s2)
203 {
204 	char	*s;
205 
206 	s = xmalloc(strlen(s1) + strlen(s2) + 1);
207 	(void)strcpy(s, s1);
208 	(void)strcat(s, s2);
209 
210 	return (s);
211 }
212 
213 static char *
214 concat3(const char *s1, const char *s2, const char *s3)
215 {
216 	char	*s;
217 
218 	s = xmalloc(strlen(s1) + strlen(s2) + strlen(s3) + 1);
219 	(void)strcpy(s, s1);
220 	(void)strcat(s, s2);
221 	(void)strcat(s, s3);
222 
223 	return (s);
224 }
225 
226 /*
227  * Clean up after a signal.
228  */
229 static void
230 terminate(int signo)
231 {
232 	int	i;
233 
234 	if (cppoutfd != -1)
235 		(void)close(cppoutfd);
236 	if (cppout != NULL)
237 		(void)remove(cppout);
238 
239 	if (p1out != NULL) {
240 		for (i = 0; p1out[i] != NULL; i++)
241 			(void)remove(p1out[i]);
242 	}
243 
244 	if (p2out != NULL)
245 		(void)remove(p2out);
246 
247 	if (currfn != NULL)
248 		(void)remove(currfn);
249 
250 	exit(signo != 0 ? 1 : 0);
251 }
252 
253 /*
254  * Returns a pointer to the last component of strg after delim.
255  * Returns strg if the string does not contain delim.
256  */
257 static const char *
258 lbasename(const char *strg, int delim)
259 {
260 	const	char *cp, *cp1, *cp2;
261 
262 	cp = cp1 = cp2 = strg;
263 	while (*cp != '\0') {
264 		if (*cp++ == delim) {
265 			cp2 = cp1;
266 			cp1 = cp;
267 		}
268 	}
269 	return (*cp1 == '\0' ? cp2 : cp1);
270 }
271 
272 static void
273 appdef(char ***lstp, const char *def)
274 {
275 
276 	appstrg(lstp, concat2("-D__", def));
277 	appstrg(lstp, concat3("-D__", def, "__"));
278 }
279 
280 static void
281 usage(void)
282 {
283 
284 	(void)fprintf(stderr,
285 	    "Usage: %s [-abceghprvwxzHF] [-s|-t] [-i|-nu] [-Dname[=def]]"
286 	    " [-Uname] [-X <id>[,<id>]...\n", getprogname());
287 	(void)fprintf(stderr,
288 	    "\t[-Idirectory] [-Ldirectory] [-llibrary] [-ooutputfile]"
289 	    " file...\n");
290 	(void)fprintf(stderr,
291 	    "       %s [-abceghprvwzHF] [-s|-t] -Clibrary [-Dname[=def]]\n"
292 	    " [-X <id>[,<id>]...\n", getprogname());
293 	(void)fprintf(stderr, "\t[-Idirectory] [-Uname] [-Bpath] file"
294 	    " ...\n");
295 	terminate(-1);
296 }
297 
298 
299 int
300 main(int argc, char *argv[])
301 {
302 	int	c;
303 	char	flgbuf[3], *tmp, *s;
304 	size_t	len;
305 
306 	setprogname(argv[0]);
307 
308 	if ((tmp = getenv("TMPDIR")) == NULL || (len = strlen(tmp)) == 0) {
309 		tmpdir = xstrdup(_PATH_TMP);
310 	} else {
311 		s = xmalloc(len + 2);
312 		(void)sprintf(s, "%s%s", tmp, tmp[len - 1] == '/' ? "" : "/");
313 		tmpdir = s;
314 	}
315 
316 	cppout = xmalloc(strlen(tmpdir) + sizeof ("lint0.XXXXXX"));
317 	(void)sprintf(cppout, "%slint0.XXXXXX", tmpdir);
318 	cppoutfd = mkstemp(cppout);
319 	if (cppoutfd == -1) {
320 		warn("can't make temp");
321 		terminate(-1);
322 	}
323 
324 	p1out = xcalloc(1, sizeof (char *));
325 	p2in = xcalloc(1, sizeof (char *));
326 	cflags = xcalloc(1, sizeof (char *));
327 	lcflags = xcalloc(1, sizeof (char *));
328 	l1flags = xcalloc(1, sizeof (char *));
329 	l2flags = xcalloc(1, sizeof (char *));
330 	l2libs = xcalloc(1, sizeof (char *));
331 	deflibs = xcalloc(1, sizeof (char *));
332 	libs = xcalloc(1, sizeof (char *));
333 	libsrchpath = xcalloc(1, sizeof (char *));
334 
335 	appcstrg(&cflags, "-E");
336 	appcstrg(&cflags, "-x");
337 	appcstrg(&cflags, "c");
338 #if 0
339 	appcstrg(&cflags, "-D__attribute__(x)=");
340 	appcstrg(&cflags, "-D__extension__(x)=/*NOSTRICT*/0");
341 #else
342 	appcstrg(&cflags, "-U__GNUC__");
343 #endif
344 	appcstrg(&cflags, "-Wp,-$");
345 	appcstrg(&cflags, "-Wp,-CC");
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  */
580 static void
581 fname(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 
698 static void
699 runchild(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 
754 static void
755 findlibs(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 
786 static int
787 rdok(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 
800 static void
801 lint2(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 
832 static void
833 cat(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