1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1985-2010 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Common Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * Glenn Fowler <gsf@research.att.com> *
18 * David Korn <dgk@research.att.com> *
19 * Phong Vo <kpv@research.att.com> *
20 * *
21 ***********************************************************************/
22 #pragma prototyped
23 /*
24 * Glenn Fowler
25 * AT&T Research
26 *
27 * xargs/tw command arg list support
28 */
29
30 #include <ast.h>
31 #include <ctype.h>
32 #include <error.h>
33 #include <proc.h>
34
35 #include "cmdarg.h"
36
37 #ifndef ARG_MAX
38 #define ARG_MAX (64*1024)
39 #endif
40 #ifndef EXIT_QUIT
41 #define EXIT_QUIT 255
42 #endif
43
44 static const char* echo[] = { "echo", 0 };
45
46 /*
47 * open a cmdarg stream
48 * initialize the command for execution
49 * argv[-1] is reserved for procrun(PROC_ARGMOD)
50 */
51
52 Cmdarg_t*
cmdopen(char ** argv,int argmax,int size,const char * argpat,int flags)53 cmdopen(char** argv, int argmax, int size, const char* argpat, int flags)
54 {
55 register Cmdarg_t* cmd;
56 register int n;
57 register char** p;
58 register char* s;
59 char* sh;
60 int c;
61 int m;
62 int argc;
63 long x;
64
65 char** post = 0;
66
67 n = sizeof(char**);
68 if (*argv)
69 {
70 for (p = argv + 1; *p; p++)
71 {
72 if ((flags & CMD_POST) && argpat && streq(*p, argpat))
73 {
74 *p = 0;
75 post = p + 1;
76 argpat = 0;
77 }
78 else
79 n += strlen(*p) + 1;
80 }
81 argc = p - argv;
82 }
83 else
84 argc = 0;
85 for (p = environ; *p; p++)
86 n += sizeof(char**) + strlen(*p) + 1;
87 if ((x = strtol(astconf("ARG_MAX", NiL, NiL), NiL, 0)) <= 0)
88 x = ARG_MAX;
89 if (size <= 0 || size > x)
90 size = x;
91 sh = pathshell();
92 m = n + (argc + 4) * sizeof(char**) + strlen(sh) + 1;
93 m = roundof(m, sizeof(char**));
94 if (size < m)
95 {
96 error(2, "size must be at least %d", m);
97 return 0;
98 }
99 if ((m = x / 10) > 2048)
100 m = 2048;
101 if (size > (x - m))
102 size = x - m;
103 n = size - n;
104 m = ((flags & CMD_INSERT) && argpat) ? (strlen(argpat) + 1) : 0;
105 if (!(cmd = newof(0, Cmdarg_t, 1, n + m)))
106 {
107 error(ERROR_SYSTEM|2, "out of space");
108 return 0;
109 }
110 c = n / sizeof(char**);
111 if (argmax <= 0 || argmax > c)
112 argmax = c;
113 s = cmd->buf;
114 if (!argv[0])
115 {
116 argv = (char**)echo;
117 cmd->echo = 1;
118 }
119 else if (streq(argv[0], echo[0]))
120 {
121 cmd->echo = 1;
122 flags &= ~CMD_NEWLINE;
123 }
124 else if (!(flags & CMD_CHECKED))
125 {
126 if (!pathpath(s, argv[0], NiL, PATH_REGULAR|PATH_EXECUTE))
127 {
128 if (!(flags & CMD_SILENT))
129 {
130 error(ERROR_SYSTEM|2, "%s: command not found", argv[0]);
131 exit(EXIT_NOTFOUND);
132 }
133 free(cmd);
134 return 0;
135 }
136 argv[0] = s;
137 }
138 s += strlen(s) + 1;
139 if (m)
140 {
141 cmd->insert = strcpy(s, argpat);
142 cmd->insertlen = m - 1;
143 s += m;
144 }
145 s += sizeof(char**) - (s - cmd->buf) % sizeof(char**);
146 p = (char**)s;
147 n -= strlen(*p++ = sh) + 1;
148 cmd->argv = p;
149 while (*p = *argv++)
150 p++;
151 if (m)
152 {
153 argmax = 1;
154 *p++ = 0;
155 cmd->insertarg = p;
156 argv = cmd->argv;
157 c = *cmd->insert;
158 while (s = *argv)
159 {
160 while ((s = strchr(s, c)) && strncmp(cmd->insert, s, cmd->insertlen))
161 s++;
162 *p++ = s ? *argv : (char*)0;
163 argv++;
164 }
165 *p++ = 0;
166 }
167 cmd->firstarg = cmd->nextarg = p;
168 cmd->laststr = cmd->nextstr = cmd->buf + n;
169 cmd->argmax = argmax;
170 cmd->flags = flags;
171 cmd->offset = ((cmd->postarg = post) ? (argc - (post - argv)) : 0) + 3;
172 return cmd;
173 }
174
175 /*
176 * flush outstanding command file args
177 */
178
179 int
cmdflush(register Cmdarg_t * cmd)180 cmdflush(register Cmdarg_t* cmd)
181 {
182 register char* s;
183 register char** p;
184 register int n;
185
186 if (cmd->flags & CMD_EMPTY)
187 cmd->flags &= ~CMD_EMPTY;
188 else if (cmd->nextarg <= cmd->firstarg)
189 return 0;
190 if ((cmd->flags & CMD_MINIMUM) && cmd->argcount < cmd->argmax)
191 {
192 if (!(cmd->flags & CMD_SILENT))
193 error(2, "%d arg command would be too long", cmd->argcount);
194 return -1;
195 }
196 cmd->total.args += cmd->argcount;
197 cmd->total.commands++;
198 cmd->argcount = 0;
199 if (p = cmd->postarg)
200 while (*cmd->nextarg++ = *p++);
201 else
202 *cmd->nextarg = 0;
203 if (s = cmd->insert)
204 {
205 char* a;
206 char* b;
207 char* e;
208 char* t;
209 char* u;
210 int c;
211 int m;
212
213 a = cmd->firstarg[0];
214 b = (char*)&cmd->nextarg[1];
215 e = cmd->nextstr;
216 c = *s;
217 m = cmd->insertlen;
218 for (n = 1; cmd->argv[n]; n++)
219 if (t = cmd->insertarg[n])
220 {
221 cmd->argv[n] = b;
222 for (;;)
223 {
224 if (!(u = strchr(t, c)))
225 {
226 b += sfsprintf(b, e - b, "%s", t);
227 break;
228 }
229 if (!strncmp(s, u, m))
230 {
231 b += sfsprintf(b, e - b, "%-.*s%s", u - t, t, a);
232 t = u + m;
233 }
234 else if (b >= e)
235 break;
236 else
237 {
238 *b++ = *u++;
239 t = u;
240 }
241 }
242 if (b < e)
243 *b++ = 0;
244 }
245 if (b >= e)
246 {
247 if (!(cmd->flags & CMD_SILENT))
248 error(2, "%s: command too large after insert", a);
249 return -1;
250 }
251 }
252 cmd->nextarg = cmd->firstarg;
253 cmd->nextstr = cmd->laststr;
254 if (cmd->flags & (CMD_QUERY|CMD_TRACE))
255 {
256 p = cmd->argv;
257 sfprintf(sfstderr, "+ %s", *p);
258 while (s = *++p)
259 sfprintf(sfstderr, " %s", s);
260 if (!(cmd->flags & CMD_QUERY))
261 sfprintf(sfstderr, "\n");
262 else if (astquery(1, "? "))
263 return 0;
264 }
265 if (cmd->echo)
266 {
267 n = (cmd->flags & CMD_NEWLINE) ? '\n' : ' ';
268 for (p = cmd->argv + 1; s = *p++;)
269 sfputr(sfstdout, s, *p ? n : '\n');
270 n = 0;
271 }
272 else if ((n = procrun(*cmd->argv, cmd->argv, PROC_ARGMOD|PROC_IGNOREPATH)) == -1)
273 {
274 if (!(cmd->flags & CMD_SILENT))
275 {
276 error(ERROR_SYSTEM|2, "%s: command exec error", *cmd->argv);
277 exit(EXIT_NOTFOUND - 1);
278 }
279 return -1;
280 }
281 else if (n >= EXIT_NOTFOUND - 1)
282 {
283 if (!(cmd->flags & CMD_SILENT))
284 exit(n);
285 }
286 else if (!(cmd->flags & CMD_IGNORE))
287 {
288 if (n == EXIT_QUIT && !(cmd->flags & CMD_SILENT))
289 exit(2);
290 if (n)
291 error_info.errors++;
292 }
293 return n;
294 }
295
296 /*
297 * add file to the command arg list
298 */
299
300 int
cmdarg(register Cmdarg_t * cmd,const char * file,register int len)301 cmdarg(register Cmdarg_t* cmd, const char* file, register int len)
302 {
303 int i;
304 int r;
305
306 r = 0;
307 if (len)
308 {
309 while ((cmd->nextstr -= len + 1) < (char*)(cmd->nextarg + cmd->offset))
310 {
311 if (cmd->nextarg == cmd->firstarg)
312 {
313 error(2, "%s: path too long for exec args", file);
314 return -1;
315 }
316 if (i = cmdflush(cmd))
317 {
318 if (r < i)
319 r = i;
320 if (!(cmd->flags & CMD_IGNORE))
321 return r;
322 }
323 }
324 *cmd->nextarg++ = cmd->nextstr;
325 memcpy(cmd->nextstr, file, len);
326 cmd->nextstr[len] = 0;
327 cmd->argcount++;
328 if (cmd->argcount >= cmd->argmax && (i = cmdflush(cmd)) > r)
329 r = i;
330 }
331 return r;
332 }
333
334 /*
335 * close a cmdarg stream
336 */
337
338 int
cmdclose(Cmdarg_t * cmd)339 cmdclose(Cmdarg_t* cmd)
340 {
341 int n;
342
343 if ((cmd->flags & CMD_EXACT) && cmd->argcount < cmd->argmax)
344 {
345 if (!(cmd->flags & CMD_SILENT))
346 error(2, "only %d arguments for last command", cmd->argcount);
347 return -1;
348 }
349 cmd->flags &= ~CMD_MINIMUM;
350 n = cmdflush(cmd);
351 free(cmd);
352 return n;
353 }
354