1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1985-2012 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Eclipse Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.eclipse.org/org/documents/epl-v10.html *
11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) *
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 #define _AST_API_H 1
31
32 #include <ast.h>
33 #include <cmdlib.h>
34 #include <proc.h>
35
36 static const char lib[] = "libast:cmdarg";
37
38 static int
cmdrun(int argc,char ** argv,Cmddisc_t * disc)39 cmdrun(int argc, char** argv, Cmddisc_t* disc)
40 {
41 return procrun(argv[0], argv, PROC_ARGMOD|PROC_IGNOREPATH);
42 }
43
44 Cmdarg_t*
cmdopen(char ** argv,int argmax,int size,const char * argpat,int flags)45 cmdopen(char** argv, int argmax, int size, const char* argpat, int flags)
46 {
47 Cmddisc_t disc;
48
49 memset(&disc, 0, sizeof(disc));
50 disc.version = CMD_VERSION;
51 if (!(flags & CMD_SILENT))
52 {
53 flags |= CMD_EXIT;
54 disc.errorf = errorf;
55 }
56 disc.flags = flags;
57 return cmdopen_20120411(argv, argmax, size, argpat, &disc);
58 }
59
60 #undef _AST_API_H
61
62 #include <ast_api.h>
63
64 #include <ctype.h>
65 #include <proc.h>
66
67 #ifndef ARG_MAX
68 #define ARG_MAX (64*1024)
69 #endif
70 #ifndef EXIT_QUIT
71 #define EXIT_QUIT 255
72 #endif
73
74 static const char* echo[] = { "echo", 0 };
75
76 Cmdarg_t*
cmdopen_20110505(char ** argv,int argmax,int size,const char * argpat,int flags,Error_f errorf)77 cmdopen_20110505(char** argv, int argmax, int size, const char* argpat, int flags, Error_f errorf)
78 {
79 Cmddisc_t disc;
80
81 memset(&disc, 0, sizeof(disc));
82 disc.version = CMD_VERSION;
83 disc.flags = flags;
84 disc.errorf = errorf;
85 return cmdopen_20120411(argv, argmax, size, argpat, &disc);
86 }
87
88 /*
89 * open a cmdarg stream
90 * initialize the command for execution
91 * argv[-1] is reserved for procrun(PROC_ARGMOD)
92 */
93
94 Cmdarg_t*
cmdopen_20120411(char ** argv,int argmax,int size,const char * argpat,Cmddisc_t * disc)95 cmdopen_20120411(char** argv, int argmax, int size, const char* argpat, Cmddisc_t* disc)
96 {
97 register Cmdarg_t* cmd;
98 register int n;
99 register char** p;
100 register char* s;
101 char* sh;
102 char* exe;
103 int c;
104 int m;
105 int argc;
106 long x;
107
108 char** post = 0;
109
110 n = sizeof(char**);
111 if (*argv)
112 {
113 for (p = argv + 1; *p; p++)
114 {
115 if ((disc->flags & CMD_POST) && argpat && streq(*p, argpat))
116 {
117 *p = 0;
118 post = p + 1;
119 argpat = 0;
120 }
121 else
122 n += strlen(*p) + 1;
123 }
124 argc = p - argv;
125 }
126 else
127 argc = 0;
128 for (p = environ; *p; p++)
129 n += sizeof(char**) + strlen(*p) + 1;
130 if ((x = strtol(astconf("ARG_MAX", NiL, NiL), NiL, 0)) <= 0)
131 x = ARG_MAX;
132 if (size <= 0 || size > x)
133 size = x;
134 sh = pathshell();
135 m = n + (argc + 4) * sizeof(char**) + strlen(sh) + 1;
136 m = roundof(m, sizeof(char**));
137 if (size < m)
138 {
139 if (disc->errorf)
140 (*disc->errorf)(NiL, sh, 2, "size must be at least %d", m);
141 return 0;
142 }
143 if ((m = x / 10) > 2048)
144 m = 2048;
145 if (size > (x - m))
146 size = x - m;
147 n = size - n;
148 m = ((disc->flags & CMD_INSERT) && argpat) ? (strlen(argpat) + 1) : 0;
149 if (!(cmd = newof(0, Cmdarg_t, 1, n + m)))
150 {
151 if (disc->errorf)
152 (*disc->errorf)(NiL, sh, ERROR_SYSTEM|2, "out of space");
153 return 0;
154 }
155 cmd->id = lib;
156 cmd->disc = disc;
157 cmd->errorf = disc->errorf;
158 if (!(cmd->runf = disc->runf))
159 cmd->runf = cmdrun;
160 c = n / sizeof(char**);
161 if (argmax <= 0 || argmax > c)
162 argmax = c;
163 s = cmd->buf;
164 if (!(exe = argv[0]))
165 {
166 exe = *(argv = (char**)echo);
167 cmd->echo = 1;
168 }
169 else if (streq(exe, echo[0]))
170 {
171 cmd->echo = 1;
172 disc->flags &= ~CMD_NEWLINE;
173 }
174 else if (!(disc->flags & CMD_CHECKED))
175 {
176 if (!pathpath(exe, NiL, PATH_REGULAR|PATH_EXECUTE, s, n + m))
177 {
178 n = EXIT_NOTFOUND;
179 if (cmd->errorf)
180 (*cmd->errorf)(NiL, cmd, ERROR_SYSTEM|2, "%s: command not found", exe);
181 if (disc->flags & CMD_EXIT)
182 (*error_info.exit)(n);
183 free(cmd);
184 return 0;
185 }
186 exe = s;
187 }
188 s += strlen(s) + 1;
189 if (m)
190 {
191 cmd->insert = strcpy(s, argpat);
192 cmd->insertlen = m - 1;
193 s += m;
194 }
195 s += sizeof(char**) - (s - cmd->buf) % sizeof(char**);
196 p = (char**)s;
197 n -= strlen(*p++ = sh) + 1;
198 cmd->argv = p;
199 *p++ = exe;
200 while (*p = *++argv)
201 p++;
202 if (m)
203 {
204 argmax = 1;
205 *p++ = 0;
206 cmd->insertarg = p;
207 argv = cmd->argv;
208 c = *cmd->insert;
209 while (s = *argv)
210 {
211 while ((s = strchr(s, c)) && strncmp(cmd->insert, s, cmd->insertlen))
212 s++;
213 *p++ = s ? *argv : (char*)0;
214 argv++;
215 }
216 *p++ = 0;
217 }
218 cmd->firstarg = cmd->nextarg = p;
219 cmd->laststr = cmd->nextstr = cmd->buf + n;
220 cmd->argmax = argmax;
221 cmd->flags = disc->flags;
222 cmd->offset = ((cmd->postarg = post) ? (argc - (post - argv)) : 0) + 3;
223 return cmd;
224 }
225
226 /*
227 * flush outstanding command file args
228 */
229
230 int
cmdflush(register Cmdarg_t * cmd)231 cmdflush(register Cmdarg_t* cmd)
232 {
233 register char* s;
234 register char** p;
235 register int n;
236
237 if (cmd->flags & CMD_EMPTY)
238 cmd->flags &= ~CMD_EMPTY;
239 else if (cmd->nextarg <= cmd->firstarg)
240 return 0;
241 if ((cmd->flags & CMD_MINIMUM) && cmd->argcount < cmd->argmax)
242 {
243 if (cmd->errorf)
244 (*cmd->errorf)(NiL, cmd, 2, "%d arg command would be too long", cmd->argcount);
245 return -1;
246 }
247 cmd->total.args += cmd->argcount;
248 cmd->total.commands++;
249 cmd->argcount = 0;
250 if (p = cmd->postarg)
251 while (*cmd->nextarg++ = *p++);
252 else
253 *cmd->nextarg = 0;
254 if (s = cmd->insert)
255 {
256 char* a;
257 char* b;
258 char* e;
259 char* t;
260 char* u;
261 int c;
262 int m;
263
264 a = cmd->firstarg[0];
265 b = (char*)&cmd->nextarg[1];
266 e = cmd->nextstr;
267 c = *s;
268 m = cmd->insertlen;
269 for (n = 1; cmd->argv[n]; n++)
270 if (t = cmd->insertarg[n])
271 {
272 cmd->argv[n] = b;
273 for (;;)
274 {
275 if (!(u = strchr(t, c)))
276 {
277 b += sfsprintf(b, e - b, "%s", t);
278 break;
279 }
280 if (!strncmp(s, u, m))
281 {
282 b += sfsprintf(b, e - b, "%-.*s%s", u - t, t, a);
283 t = u + m;
284 }
285 else if (b >= e)
286 break;
287 else
288 {
289 *b++ = *u++;
290 t = u;
291 }
292 }
293 if (b < e)
294 *b++ = 0;
295 }
296 if (b >= e)
297 {
298 if (cmd->errorf)
299 (*cmd->errorf)(NiL, cmd, 2, "%s: command too large after insert", a);
300 return -1;
301 }
302 }
303 n = (int)(cmd->nextarg - cmd->argv);
304 cmd->nextarg = cmd->firstarg;
305 cmd->nextstr = cmd->laststr;
306 if (cmd->flags & (CMD_QUERY|CMD_TRACE))
307 {
308 p = cmd->argv;
309 sfprintf(sfstderr, "+ %s", *p);
310 while (s = *++p)
311 sfprintf(sfstderr, " %s", s);
312 if (!(cmd->flags & CMD_QUERY))
313 sfprintf(sfstderr, "\n");
314 else if (astquery(1, "? "))
315 {
316 return 0;
317 }
318 }
319 if (cmd->echo)
320 {
321 n = (cmd->flags & CMD_NEWLINE) ? '\n' : ' ';
322 for (p = cmd->argv + 1; s = *p++;)
323 sfputr(sfstdout, s, *p ? n : '\n');
324 n = 0;
325 }
326 else if ((n = (*cmd->runf)(n, cmd->argv, cmd->disc)) == -1)
327 {
328 n = EXIT_NOTFOUND - 1;
329 if (cmd->errorf)
330 (*cmd->errorf)(NiL, cmd, ERROR_SYSTEM|2, "%s: command exec error", *cmd->argv);
331 if (cmd->flags & CMD_EXIT)
332 (*error_info.exit)(n);
333 }
334 else if (n >= EXIT_NOTFOUND - 1)
335 {
336 if (cmd->flags & CMD_EXIT)
337 (*error_info.exit)(n);
338 }
339 else if (!(cmd->flags & CMD_IGNORE))
340 {
341 if (n == EXIT_QUIT && (cmd->flags & CMD_EXIT))
342 (*error_info.exit)(2);
343 if (n)
344 error_info.errors++;
345 }
346 return n;
347 }
348
349 /*
350 * add file to the command arg list
351 */
352
353 int
cmdarg(register Cmdarg_t * cmd,const char * file,register int len)354 cmdarg(register Cmdarg_t* cmd, const char* file, register int len)
355 {
356 int i;
357 int r;
358
359 r = 0;
360 if (len > 0)
361 {
362 while ((cmd->nextstr -= len + 1) < (char*)(cmd->nextarg + cmd->offset))
363 {
364 if (cmd->nextarg == cmd->firstarg)
365 {
366 if (cmd->errorf)
367 (*cmd->errorf)(NiL, cmd, 2, "%s: path too long for exec args", file);
368 return -1;
369 }
370 if (i = cmdflush(cmd))
371 {
372 if (r < i)
373 r = i;
374 if (!(cmd->flags & CMD_IGNORE))
375 return r;
376 }
377 }
378 *cmd->nextarg++ = cmd->nextstr;
379 memcpy(cmd->nextstr, file, len);
380 cmd->nextstr[len] = 0;
381 cmd->argcount++;
382 if (cmd->argcount >= cmd->argmax && (i = cmdflush(cmd)) > r)
383 r = i;
384 }
385 else
386 cmd->argcount += len;
387 return r;
388 }
389
390 /*
391 * close a cmdarg stream
392 */
393
394 int
cmdclose(Cmdarg_t * cmd)395 cmdclose(Cmdarg_t* cmd)
396 {
397 int n;
398
399 if ((cmd->flags & CMD_EXACT) && cmd->argcount < cmd->argmax)
400 {
401 if (cmd->errorf)
402 (*cmd->errorf)(NiL, cmd, 2, "only %d arguments for last command", cmd->argcount);
403 n = -1;
404 }
405 else
406 {
407 cmd->flags &= ~CMD_MINIMUM;
408 n = cmdflush(cmd);
409 }
410 free(cmd);
411 return n;
412 }
413