xref: /original-bsd/usr.bin/xargs/xargs.c (revision f91c1509)
1 /*-
2  * Copyright (c) 1990 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * John B. Roll Jr.
7  *
8  * %sccs.include.redist.c%
9  */
10 
11 #ifndef lint
12 char copyright[] =
13 "@(#) Copyright (c) 1990 The Regents of the University of California.\n\
14  All rights reserved.\n";
15 #endif /* not lint */
16 
17 #ifndef lint
18 static char sccsid[] = "@(#)xargs.c	5.7 (Berkeley) 03/05/91";
19 #endif /* not lint */
20 
21 #include <sys/types.h>
22 #include <sys/wait.h>
23 #include <errno.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <limits.h>
29 #include "pathnames.h"
30 
31 #define	DEF_ARGC	255
32 
33 int tflag;
34 int fflag;
35 
36 main(argc, argv)
37 	int argc;
38 	char **argv;
39 {
40 	extern int errno, optind;
41 	extern char *optarg;
42 	register int ch;
43 	register char *p, *bp, *endbp, **bxp, **endxp, **xp;
44 	int cnt, indouble, insingle, nargs, nline;
45 	char *mark, *prog, **xargs;
46 
47 	nargs = DEF_ARGC;
48 	nline = _POSIX2_LINE_MAX;
49 
50 	while ((ch = getopt(argc, argv, "n:s:tf")) != EOF)
51 		switch(ch) {
52 		case 'n':
53 			if ((nargs = atoi(optarg)) <= 0) {
54 				(void)fprintf(stderr,
55 				    "xargs: bad argument count.\n");
56 				exit(1);
57 			}
58 			break;
59 		case 's':
60 			if ((nline = atoi(optarg)) <= 0) {
61 				(void)fprintf(stderr,
62 				    "xargs: bad command length.\n");
63 				exit(1);
64 			}
65 			break;
66 		case 't':
67 			tflag = 1;
68 			break;
69 		case 'f':
70 			fflag = 1;
71 			break;
72 		case '?':
73 		default:
74 			usage();
75 	}
76 	argc -= optind;
77 	argv += optind;
78 
79 	/* room for the command, leftover arguments and trailing NULL */
80 	if (!(xargs =
81 	    (char **)malloc((u_int)(nargs + argc + 2) * sizeof(char **))))
82 		enomem();
83 
84 	if (!(bp = malloc((u_int)nline + 1)))
85 		enomem();
86 
87 	xp = xargs + 1;
88 	if (!*argv)
89 		prog = _PATH_ECHO;
90 	else {
91 		prog = *argv;
92 		while (*++argv)
93 			*xp++ = *argv;
94 	}
95 
96 	if (xargs[0] = rindex(prog, '/'))
97 		++xargs[0];
98 	else
99 		xargs[0] = prog;
100 
101 	/* set up the pointers into the buffer and the arguments */
102 	*(endxp = (bxp = xp) + nargs) = NULL;
103 	endbp = (mark = p = bp) + nline;
104 
105 	insingle = indouble = 0;
106 	for (;;)
107 		switch(ch = getchar()) {
108 		case EOF:
109 			if (p == bp)		/* nothing to display */
110 				exit(0);
111 			if (mark == p) {	/* nothing since last arg end */
112 				*xp = NULL;
113 				run(prog, xargs);
114 				exit(0);
115 			}
116 			goto addarg;
117 		case ' ':
118 		case '\t':
119 			if (insingle || indouble)
120 				goto addch;
121 			goto addarg;
122 		case '\n':
123 			if (mark == p)			/* empty line */
124 				continue;
125 addarg:			*xp++ = mark;
126 			*p++ = '\0';
127 			if (xp == endxp || p >= endbp || ch == EOF) {
128 				if (insingle || indouble) {
129 					(void)fprintf(stderr,
130 					   "xargs: unterminated quote.\n");
131 					exit(1);
132 				}
133 				*xp = NULL;
134 				run(prog, xargs);
135 				if (ch == EOF)
136 					exit(0);
137 				p = bp;
138 				xp = bxp;
139 			}
140 			mark = p;
141 			break;
142 		case '\'':
143 			if (indouble)
144 				goto addch;
145 			insingle = !insingle;
146 			break;
147 		case '"':
148 			if (insingle)
149 				goto addch;
150 			indouble = !indouble;
151 			break;
152 		case '\\':
153 			if ((ch = getchar()) == EOF)
154 				ch = '\\';
155 			if (ch == '\n') {
156 				(void)fprintf(stderr,
157 				    "xargs: newline may not be escaped.\n");
158 				exit(1);
159 			}
160 			/* FALLTHROUGH */
161 		default:
162 addch:			if (p != endbp) {
163 				*p++ = ch;
164 				continue;
165 			}
166 			if (xp == bxp) {
167 				(void)fprintf(stderr,
168 				    "xargs: argument too large.\n");
169 				exit(1);
170 			}
171 			*xp = NULL;
172 			run(prog, xargs);
173 			cnt = endbp - mark;
174 			bcopy(mark, bp, cnt);
175 			p = (mark = bp) + cnt;
176 			*p++ = ch;
177 			xp = bxp;
178 			break;
179 		}
180 	/* NOTREACHED */
181 }
182 
183 run(prog, argv)
184 	char *prog, **argv;
185 {
186 	union wait pstat;
187 	pid_t pid;
188 	char **p;
189 
190 	if (tflag) {
191 		(void)fprintf(stderr, "%s", *argv);
192 		for (p = argv + 1; *p; ++p)
193 			(void)fprintf(stderr, " %s", *p);
194 		(void)fprintf(stderr, "\n");
195 		(void)fflush(stderr);
196 	}
197 	switch(pid = vfork()) {
198 	case -1:
199 		(void)fprintf(stderr,
200 		   "xargs: vfork: %s.\n", strerror(errno));
201 		exit(1);
202 	case 0:
203 		execvp(prog, argv);
204 		(void)fprintf(stderr,
205 		   "xargs: %s: %s.\n", prog, strerror(errno));
206 		_exit(1);
207 	}
208 	pid = waitpid(pid, (int *)&pstat, 0);
209 	if (pid == -1) {
210 		(void)fprintf(stderr,
211 		   "xargs: waitpid: %s.\n", strerror(errno));
212 		exit(1);
213 	}
214 	if (!fflag && pstat.w_status)
215 		exit(1);
216 }
217 
218 enomem()
219 {
220 	(void)fprintf(stderr, "xargs: %s.\n", strerror(ENOMEM));
221 	exit(1);
222 }
223 
224 usage()
225 {
226 	(void)fprintf(stderr,
227 	    "xargs: [-t] [-f] [-n number] [-s size] [utility [argument ...]]\n");
228 	exit(1);
229 }
230