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