1 /**	$MirOS: src/usr.bin/xlint/xlint/xlint.c,v 1.8 2014/03/23 20:57:15 tg Exp $ */
2 /*	$OpenBSD: xlint.c,v 1.36 2011/09/21 18:08:07 jsg Exp $	*/
3 /*	$NetBSD: xlint.c,v 1.3 1995/10/23 14:29:30 jpo Exp $	*/
4 
5 /*
6  * Copyright (c) 1994, 1995 Jochen Pohl
7  * All Rights Reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *      This product includes software developed by Jochen Pohl for
20  *	The NetBSD Project.
21  * 4. The name of the author may not be used to endorse or promote products
22  *    derived from this software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
25  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
28  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 #include <sys/param.h>
37 #include <sys/wait.h>
38 #include <sys/stat.h>
39 #include <sys/utsname.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <signal.h>
43 #include <string.h>
44 #include <unistd.h>
45 #include <fcntl.h>
46 #include <err.h>
47 #include <errno.h>
48 #include <paths.h>
49 
50 #include "lint.h"
51 #include "pathnames.h"
52 
53 __RCSID("$MirOS: src/usr.bin/xlint/xlint/xlint.c,v 1.8 2014/03/23 20:57:15 tg Exp $");
54 
55 /* directory for temporary files */
56 static	const	char *tmpdir;
57 
58 /* path name for cpp output */
59 static	char	*cppout;
60 
61 /* files created by 1st pass */
62 static	char	**p1out;
63 
64 /* input files for 2nd pass (without libraries) */
65 static	char	**p2in;
66 
67 /* library which will be created by 2nd pass */
68 static	char	*p2out;
69 
70 /* flags always passed to cpp */
71 static	char	**cppflags;
72 
73 /* flags for cpp, controled by sflag/tflag */
74 static	char	**lcppflgs;
75 
76 /* flags for lint1 */
77 static	char	**l1flags;
78 
79 /* flags for lint2 */
80 static	char	**l2flags;
81 
82 /* libraries for lint2 */
83 static	char	**l2libs;
84 
85 /* default libraries */
86 static	char	**deflibs;
87 
88 /* additional libraries */
89 static	char	**libs;
90 
91 /* search path for libraries */
92 static	char	**libsrchpath;
93 
94 /* flags */
95 static	int	iflag, oflag, Cflag, sflag, tflag, Fflag = 1;
96 
97 /* print the commands executed to run the stages of compilation */
98 static	int	Vflag;
99 
100 /* filename for oflag */
101 static	char	*outputfn;
102 
103 /* reset after first .c source has been processed */
104 static	int	first = 1;
105 
106 /*
107  * name of a file which is currently written by a child and should
108  * be removed after abnormal termination of the child
109  */
110 static	const	char *currfn;
111 
112 static	void	appstrg(char ***, char *);
113 static	void	appcstrg(char ***, const char *);
114 static	void	applst(char ***, char *const *);
115 static	void	freelst(char ***);
116 static	char	*concat2(const char *, const char *);
117 static	char	*concat3(const char *, const char *, const char *);
118 static	void	terminate(int);
119 static	const	char *lbasename(const char *, int);
120 static	void	appdef(char ***, const char *);
121 static	void	usage(void);
122 static	void	fname(const char *);
123 static	int	runchild(const char *, char *const *, const char *);
124 static	void	findlibs(char *const *);
125 static	int	rdok(const char *);
126 static	void	lint2(void);
127 static	void	cat(char *const *, const char *);
128 
129 /*
130  * Some functions to deal with lists of strings.
131  * Take care that we get no surprises in case of asynchronous signals.
132  */
133 static void
appstrg(char *** lstp,char * s)134 appstrg(char ***lstp, char *s)
135 {
136 	char	**lst, **olst;
137 	int	i;
138 
139 	olst = *lstp;
140 	for (i = 0; olst[i] != NULL; i++) ;
141 	lst = xmalloc((i + 2) * sizeof (char *));
142 	(void)memcpy(lst, olst, i * sizeof (char *));
143 	lst[i] = s;
144 	lst[i + 1] = NULL;
145 	*lstp = lst;
146 }
147 
148 static void
appcstrg(char *** lstp,const char * s)149 appcstrg(char ***lstp, const char *s)
150 {
151 	appstrg(lstp, xstrdup(s));
152 }
153 
154 static void
applst(char *** destp,char * const * src)155 applst(char ***destp, char *const *src)
156 {
157 	int	i, k;
158 	char	**dest, **odest;
159 
160 	odest = *destp;
161 	for (i = 0; odest[i] != NULL; i++) ;
162 	for (k = 0; src[k] != NULL; k++) ;
163 	dest = xmalloc((i + k + 1) * sizeof (char *));
164 	(void)memcpy(dest, odest, i * sizeof (char *));
165 	for (k = 0; src[k] != NULL; k++)
166 		dest[i + k] = xstrdup(src[k]);
167 	dest[i + k] = NULL;
168 	*destp = dest;
169 	free(odest);
170 }
171 
172 static void
freelst(char *** lstp)173 freelst(char ***lstp)
174 {
175 	char	*s;
176 	int	i;
177 
178 	for (i = 0; (*lstp)[i] != NULL; i++) ;
179 	while (i-- > 0) {
180 		s = (*lstp)[i];
181 		(*lstp)[i] = NULL;
182 		free(s);
183 	}
184 }
185 
186 static char *
concat2(const char * s1,const char * s2)187 concat2(const char *s1, const char *s2)
188 {
189 	char	*s;
190 	size_t len = strlen(s1) + strlen(s2) + 1;
191 
192 	s = xmalloc(len);
193 	(void)strlcpy(s, s1, len);
194 	(void)strlcat(s, s2, len);
195 
196 	return (s);
197 }
198 
199 static char *
concat3(const char * s1,const char * s2,const char * s3)200 concat3(const char *s1, const char *s2, const char *s3)
201 {
202 	char	*s;
203 	size_t len = strlen(s1) + strlen(s2) + strlen(s3) + 1;
204 
205 	s = xmalloc(len);
206 	(void)strlcpy(s, s1, len);
207 	(void)strlcat(s, s2, len);
208 	(void)strlcat(s, s3, len);
209 
210 	return (s);
211 }
212 
213 /*
214  * Clean up after a signal.
215  */
216 static void
terminate(int signo)217 terminate(int signo)
218 {
219 	int	i;
220 
221 	if (cppout != NULL)
222 		(void)remove(cppout);
223 
224 	if (p1out != NULL) {
225 		for (i = 0; p1out[i] != NULL; i++)
226 			(void)remove(p1out[i]);
227 	}
228 
229 	if (p2out != NULL)
230 		(void)remove(p2out);
231 
232 	if (currfn != NULL)
233 		(void)remove(currfn);
234 
235 	_exit(signo != 0 ? 1 : 0);
236 }
237 
238 /*
239  * Returns a pointer to the last component of strg after delim.
240  * Returns strg if the string does not contain delim.
241  */
242 static const char *
lbasename(const char * strg,int delim)243 lbasename(const char *strg, int delim)
244 {
245 	const	char *cp, *cp1, *cp2;
246 
247 	cp = cp1 = cp2 = strg;
248 	while (*cp != '\0') {
249 		if (*cp++ == delim) {
250 			cp2 = cp1;
251 			cp1 = cp;
252 		}
253 	}
254 	return (*cp1 == '\0' ? cp2 : cp1);
255 }
256 
257 static void
appdef(char *** lstp,const char * def)258 appdef(char ***lstp, const char *def)
259 {
260 	appstrg(lstp, concat2("-D__", def));
261 	appstrg(lstp, concat3("-D__", def, "__"));
262 }
263 
264 static void
usage()265 usage()
266 {
267 	(void)printf("usage: lint [-ceFfgHhprsVvxz] [-i | -nu] [-Dname[=def]] [-Idirectory]\n");
268 	(void)printf("\t[-Ldirectory] [-llibrary] [-ooutputfile] [-MD] [-Uname] file ...\n");
269 	(void)printf("       lint [-ceFfgHhprsVvz] -Clibrary [-Dname[=def]]\n");
270 	(void)printf("\t[-Idirectory] [-MD] [-Uname] file ...\n");
271 	terminate(-1);
272 }
273 
274 int
main(int argc,char * argv[])275 main(int argc, char *argv[])
276 {
277 	int	c;
278 	int	fd;
279 	char	flgbuf[3], *tmp, *s;
280 	size_t	len;
281 	struct	utsname un;
282 
283 	if ((tmp = getenv("TMPDIR")) == NULL || (len = strlen(tmp)) == 0) {
284 		tmpdir = xstrdup(_PATH_TMP);
285 	} else {
286 		s = xmalloc(len + 2);
287 		(void)snprintf(s, len + 2, "%s%s",
288 			tmp, tmp[len - 1] == '/' ? "" : "/");
289 		tmpdir = s;
290 	}
291 
292 	if (asprintf(&cppout, "%slint0.XXXXXXXXXX", tmpdir) == -1)
293 		err(1, NULL);
294 	if ((fd = mkstemp(cppout)) == -1) {
295 		warn("can't make temp");
296 		terminate(-1);
297 	}
298 	close(fd);
299 
300 	p1out = xcalloc(1, sizeof (char *));
301 	p2in = xcalloc(1, sizeof (char *));
302 	cppflags = xcalloc(1, sizeof (char *));
303 	lcppflgs = xcalloc(1, sizeof (char *));
304 	l1flags = xcalloc(1, sizeof (char *));
305 	l2flags = xcalloc(1, sizeof (char *));
306 	l2libs = xcalloc(1, sizeof (char *));
307 	deflibs = xcalloc(1, sizeof (char *));
308 	libs = xcalloc(1, sizeof (char *));
309 	libsrchpath = xcalloc(1, sizeof (char *));
310 
311 	appcstrg(&cppflags, "-CC");
312 #ifdef	__OpenBSD__
313 	appcstrg(&cppflags, "-D__OpenBSD__");
314 #endif
315 #ifdef	__MirBSD__
316 	appcstrg(&cppflags, "-D__MirBSD__");
317 #endif
318 	appcstrg(&cppflags, "-Dlint");		/* XXX don't def. with -s */
319 	appdef(&cppflags, "lint");
320 	appdef(&cppflags, "unix");
321 
322 	if (uname(&un) == -1)
323 		err(1, "uname");
324 	appdef(&cppflags, un.machine);
325 	appstrg(&lcppflgs, concat2("-D", un.machine));
326 
327 #ifdef MACHINE_ARCH
328 #ifdef MACHINE_CPU
329 	if (strcmp(MACHINE_ARCH, MACHINE_CPU) != 0) {
330 		appdef(&cppflags, MACHINE_CPU);
331 		appstrg(&lcppflgs, concat2("-D", MACHINE_CPU));
332 	}
333 #endif
334 	if (strcmp(un.machine, MACHINE_ARCH) != 0) {
335 		appdef(&cppflags, MACHINE_ARCH);
336 		appstrg(&lcppflgs, concat2("-D", MACHINE_ARCH));
337 	}
338 #endif
339 
340 	appcstrg(&deflibs, "c");
341 
342 	if (signal(SIGHUP, terminate) == SIG_IGN)
343 		(void)signal(SIGHUP, SIG_IGN);
344 	(void)signal(SIGINT, terminate);
345 	(void)signal(SIGQUIT, terminate);
346 	(void)signal(SIGTERM, terminate);
347 
348 	while (argc > optind) {
349 		c = getopt(argc, argv, "abcefghil:no:prstuvxyzC:D:FHI:L:M:U:V");
350 
351 		switch (c) {
352 
353 		case 'a':
354 		case 'b':
355 		case 'c':
356 		case 'e':
357 		case 'f':
358 		case 'g':
359 		case 'r':
360 		case 'v':
361 		case 'y':
362 		case 'z':
363 			(void)snprintf(flgbuf, sizeof flgbuf, "-%c", c);
364 			appcstrg(&l1flags, flgbuf);
365 			break;
366 
367 		case 'F':
368 			Fflag = 1;
369 			/* FALLTHROUGH */
370 		case 'u':
371 		case 'h':
372 			(void)snprintf(flgbuf, sizeof flgbuf, "-%c", c);
373 			appcstrg(&l1flags, flgbuf);
374 			appcstrg(&l2flags, flgbuf);
375 			break;
376 
377 		case 'i':
378 			if (Cflag)
379 				usage();
380 			iflag = 1;
381 			break;
382 
383 		case 'n':
384 			freelst(&deflibs);
385 			break;
386 
387 		case 'p':
388 			appcstrg(&l1flags, "-p");
389 			appcstrg(&l2flags, "-p");
390 			if (*deflibs != NULL) {
391 				freelst(&deflibs);
392 				appcstrg(&deflibs, "c");
393 			}
394 			break;
395 
396 		case 's':
397 			if (tflag)
398 				usage();
399 			freelst(&lcppflgs);
400 			appcstrg(&lcppflgs, "-D__STRICT_ANSI__");
401 			appcstrg(&l1flags, "-s");
402 			appcstrg(&l2flags, "-s");
403 			sflag = 1;
404 			break;
405 
406 		case 't':
407 			if (sflag)
408 				usage();
409 			freelst(&lcppflgs);
410 			appcstrg(&lcppflgs, "-t");
411 			appstrg(&lcppflgs, concat2("-D", MACHINE));
412 			appstrg(&lcppflgs, concat2("-D", MACHINE_ARCH));
413 			appcstrg(&l1flags, "-t");
414 			appcstrg(&l2flags, "-t");
415 			tflag = 1;
416 			break;
417 
418 		case 'x':
419 			appcstrg(&l2flags, "-x");
420 			break;
421 
422 		case 'C':
423 			if (Cflag || oflag || iflag)
424 				usage();
425 			Cflag = 1;
426 			appstrg(&l2flags, concat2("-C", optarg));
427 			if (asprintf(&p2out, "llib-l%s.ln", optarg) == -1)
428 				err(1, NULL);
429 			freelst(&deflibs);
430 			break;
431 
432 		case 'D':
433 		case 'I':
434 		case 'U':
435 			(void)snprintf(flgbuf, sizeof flgbuf, "-%c", c);
436 			appstrg(&cppflags, concat2(flgbuf, optarg));
437 			break;
438 
439 		case 'l':
440 			appcstrg(&libs, optarg);
441 			break;
442 
443 		case 'o':
444 			if (Cflag || oflag)
445 				usage();
446 			oflag = 1;
447 			outputfn = xstrdup(optarg);
448 			break;
449 
450 		case 'L':
451 			appcstrg(&libsrchpath, optarg);
452 			break;
453 
454 		case 'M':
455 			break;
456 
457 		case 'H':
458 			appcstrg(&l2flags, "-H");
459 			break;
460 
461 		case 'V':
462 			Vflag = 1;
463 			break;
464 
465 		case '?':
466 			usage();
467 			/* NOTREACHED */
468 
469 		case -1:
470 			/* filename */
471 			if (argv[optind] == NULL)
472 				break;
473 			fname(argv[optind++]);
474 			first = 0;
475 		}
476 
477 	}
478 	argc -= optind;
479 	argv += optind;
480 
481 	if (first)
482 		usage();
483 
484 	if (iflag)
485 		terminate(0);
486 
487 	if (!oflag) {
488 		if ((s = getenv("LIBDIR")) == NULL || strlen(s) == 0)
489 			s = PATH_LINTLIB;
490 		appcstrg(&libsrchpath, s);
491 		findlibs(libs);
492 		findlibs(deflibs);
493 	}
494 
495 	(void)printf("Lint pass2:\n");
496 	lint2();
497 
498 	if (oflag)
499 		cat(p2in, outputfn);
500 
501 	if (Cflag)
502 		p2out = NULL;
503 
504 	terminate(0);
505 	/* NOTREACHED */
506 }
507 
508 /*
509  * Read a file name from the command line
510  * and pass it through lint1 if it is a C source.
511  */
512 static void
fname(const char * name)513 fname(const char *name)
514 {
515 	const	char *bn, *suff;
516 	char	**args, *ofn, *path;
517 	size_t	len;
518 	int	error;
519 	int	fd;
520 
521 	bn = lbasename(name, '/');
522 	suff = lbasename(bn, '.');
523 
524 	if (strcmp(suff, "ln") == 0) {
525 		/* only for lint2 */
526 		if (!iflag)
527 			appcstrg(&p2in, name);
528 		return;
529 	}
530 
531 	if (strcmp(suff, "c") != 0 &&
532 	    (strncmp(bn, "llib-l", 6) != 0 || bn != suff)) {
533 		warnx("unknown file type: %s", name);
534 		return;
535 	}
536 
537 	/* build the name of the output file of lint1 */
538 	if (oflag) {
539 		ofn = outputfn;
540 		outputfn = NULL;
541 		oflag = 0;
542 	} else if (iflag) {
543 		len = strlen(bn) + (bn == suff ? 4 : 2);
544 		ofn = xmalloc(len);
545 		(void)snprintf(ofn, len, "%.*s",
546 		    (int)(bn == suff ? strlen(bn) : (suff - 1) - bn), bn);
547 		(void)strlcat(ofn, ".ln", len);
548 	} else {
549 		if (asprintf(&ofn, "%slint1.XXXXXXXXXX", tmpdir) == -1)
550 			err(1, NULL);
551 		if ((fd = mkstemp(ofn)) == -1) {
552 			warn("can't make temp");
553 			terminate(-1);
554 		}
555 		close(fd);
556 	}
557 	if (!iflag)
558 		appcstrg(&p1out, ofn);
559 
560 	args = xcalloc(1, sizeof (char *));
561 
562 	/* run cpp */
563 
564 	path = strdup("env");
565 	appcstrg(&args, path);
566 
567 	if ((path = getenv("CC")) == NULL || *path == '\0') {
568 		path = NULL;
569 		appcstrg(&args, strdup("/usr/libexec/cpp"));
570 	} else {
571 		appcstrg(&args, strdup(path));
572 		appcstrg(&args, strdup("-E"));
573 		if ((path = strrchr(name, '.')) != NULL &&
574 		    strcasecmp(name, ".c") && strcmp(name, ".m") &&
575 		    strcasecmp(name, ".h") && strcmp(name, ".cxx") &&
576 		    strcasecmp(name, ".cpp") && strcmp(name, ".cc") &&
577 		    strcasecmp(name, ".fpp") && strcmp(name, ".hh") &&
578 		    strcmp(name, ".S") && strcmp(name, ".F")) {
579 			appcstrg(&args, strdup("-x"));
580 			appcstrg(&args, strdup("c"));
581 		}
582 		appcstrg(&args, strdup("-o"));
583 		appcstrg(&args, cppout);
584 	}
585 
586 	applst(&args, cppflags);
587 	applst(&args, lcppflgs);
588 	appcstrg(&args, name);
589 	if (path == NULL)
590 		appcstrg(&args, cppout);
591 
592 	error = runchild("/usr/bin/env", args, cppout);
593 	freelst(&args);
594 	if (error)
595 		return;
596 
597 	/* run lint1 */
598 
599 	if (asprintf(&path, "%s/lint1", PATH_LIBEXEC) == -1)
600 		err(1, NULL);
601 
602 	appcstrg(&args, path);
603 	applst(&args, l1flags);
604 	appcstrg(&args, cppout);
605 	appcstrg(&args, ofn);
606 
607 	error = runchild(path, args, ofn);
608 	free(path);
609 	freelst(&args);
610 	if (error)
611 		return;
612 
613 	appcstrg(&p2in, ofn);
614 	free(ofn);
615 
616 	free(args);
617 }
618 
619 static int
runchild(const char * path,char * const * args,const char * crfn)620 runchild(const char *path, char *const *args, const char *crfn)
621 {
622 	int	status, signo, i;
623 	pid_t	rv;
624 
625 	if (Vflag) {
626 		for (i = 0; args[i] != NULL; i++)
627 			(void)printf("%s ", args[i]);
628 		(void)printf("\n");
629 	}
630 
631 	currfn = crfn;
632 
633 	(void)fflush(stdout);
634 
635 	switch (fork()) {
636 	case -1:
637 		warn("cannot fork");
638 		terminate(-1);
639 		/* NOTREACHED */
640 	default:
641 		/* parent */
642 		break;
643 	case 0:
644 		/* child */
645 		(void)execv(path, args);
646 		warn("cannot exec %s", path);
647 		exit(1);
648 		/* NOTREACHED */
649 	}
650 	currfn = NULL;
651 
652 	while ((rv = wait(&status)) == -1 && errno == EINTR) ;
653 	if (rv == -1) {
654 		warn("wait");
655 		return(-1);
656 	}
657 	if (WIFSIGNALED(status)) {
658 		signo = WTERMSIG(status);
659 		warnx("%s got SIG%s", path, sys_signame[signo]);
660 		return(-1);
661 	}
662 	if (WEXITSTATUS(status) != 0)
663 		return(-1);
664 	return(0);
665 }
666 
667 static void
findlibs(char * const * liblst)668 findlibs(char *const *liblst)
669 {
670 	int	i, k;
671 	const	char *lib, *path;
672 	char	*lfn;
673 	size_t	len, l;
674 
675 	lfn = NULL;
676 
677 	for (i = 0; (lib = liblst[i]) != NULL; i++) {
678 		for (k = 0; (path = libsrchpath[k]) != NULL; k++) {
679 			len = strlen(path) + strlen(lib);
680 			l = len + sizeof ("/llib-l.ln");
681 			lfn = xrealloc(lfn, l);
682 			(void)snprintf(lfn, l, "%s/llib-l%s.ln", path, lib);
683 			if (rdok(lfn))
684 				break;
685 			l = len + sizeof ("/lint/llib-l.ln");
686 			lfn = xrealloc(lfn, l);
687 			(void)snprintf(lfn, l, "%s/lint/llib-l%s.ln", path, lib);
688 			if (rdok(lfn))
689 				break;
690 		}
691 		if (path != NULL) {
692 			appstrg(&l2libs, concat2("-l", lfn));
693 		} else {
694 			warnx("cannot find llib-l%s.ln", lib);
695 		}
696 	}
697 
698 	free(lfn);
699 }
700 
701 static int
rdok(const char * path)702 rdok(const char *path)
703 {
704 	struct	stat sbuf;
705 
706 	if (stat(path, &sbuf) == -1)
707 		return (0);
708 	if ((sbuf.st_mode & S_IFMT) != S_IFREG)
709 		return (0);
710 	if (access(path, R_OK) == -1)
711 		return (0);
712 	return (1);
713 }
714 
715 static void
lint2()716 lint2()
717 {
718 	char	*path, **args;
719 
720 	args = xcalloc(1, sizeof (char *));
721 
722 	if (asprintf(&path, "%s/lint2", PATH_LIBEXEC) == -1)
723 		err(1, NULL);
724 
725 	appcstrg(&args, path);
726 	applst(&args, l2flags);
727 	applst(&args, l2libs);
728 	applst(&args, p2in);
729 
730 	(void)runchild(path, args, p2out);
731 	free(path);
732 	freelst(&args);
733 	free(args);
734 }
735 
736 static void
cat(char * const * srcs,const char * dest)737 cat(char *const *srcs, const char *dest)
738 {
739 	int	ifd, ofd, i;
740 	char	*src, *buf;
741 	ssize_t	rlen;
742 
743 	if ((ofd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1) {
744 		warn("cannot open %s", dest);
745 		terminate(-1);
746 	}
747 
748 	buf = xmalloc(MBLKSIZ);
749 
750 	for (i = 0; (src = srcs[i]) != NULL; i++) {
751 		if ((ifd = open(src, O_RDONLY)) == -1) {
752 			free(buf);
753 			warn("cannot open %s", src);
754 			terminate(-1);
755 		}
756 		do {
757 			if ((rlen = read(ifd, buf, MBLKSIZ)) == -1) {
758 				free(buf);
759 				warn("read error on %s", src);
760 				terminate(-1);
761 			}
762 			if (write(ofd, buf, (size_t)rlen) == -1) {
763 				free(buf);
764 				warn("write error on %s", dest);
765 				terminate(-1);
766 			}
767 		} while (rlen == MBLKSIZ);
768 		(void)close(ifd);
769 	}
770 	(void)close(ofd);
771 	free(buf);
772 }
773