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
133 #ifdef __linux__
134 // adjust for linux, perhaps page alignment is going on
135 // causes problems to tw when selecting many files
136 if (x > getpagesize () * 50)
137 x -= getpagesize ();
138 #endif
139
140 if (size <= 0 || size > x)
141 size = x;
142 sh = pathshell();
143 m = n + (argc + 4) * sizeof(char**) + strlen(sh) + 1;
144 m = roundof(m, sizeof(char**));
145 if (size < m)
146 {
147 if (disc->errorf)
148 (*disc->errorf)(NiL, sh, 2, "size must be at least %d", m);
149 return 0;
150 }
151 if ((m = x / 10) > 2048)
152 m = 2048;
153 if (size > (x - m))
154 size = x - m;
155 n = size - n;
156 m = ((disc->flags & CMD_INSERT) && argpat) ? (strlen(argpat) + 1) : 0;
157 if (!(cmd = newof(0, Cmdarg_t, 1, n + m)))
158 {
159 if (disc->errorf)
160 (*disc->errorf)(NiL, sh, ERROR_SYSTEM|2, "out of space");
161 return 0;
162 }
163 cmd->id = lib;
164 cmd->disc = disc;
165 cmd->errorf = disc->errorf;
166 if (!(cmd->runf = disc->runf))
167 cmd->runf = cmdrun;
168 c = n / sizeof(char**);
169 if (argmax <= 0 || argmax > c)
170 argmax = c;
171 s = cmd->buf;
172 if (!(exe = argv[0]))
173 {
174 exe = *(argv = (char**)echo);
175 cmd->echo = 1;
176 }
177 else if (streq(exe, echo[0]))
178 {
179 cmd->echo = 1;
180 disc->flags &= ~CMD_NEWLINE;
181 }
182 else if (!(disc->flags & CMD_CHECKED))
183 {
184 if (!pathpath(exe, NiL, PATH_REGULAR|PATH_EXECUTE, s, n + m))
185 {
186 n = EXIT_NOTFOUND;
187 if (cmd->errorf)
188 (*cmd->errorf)(NiL, cmd, ERROR_SYSTEM|2, "%s: command not found", exe);
189 if (disc->flags & CMD_EXIT)
190 (*error_info.exit)(n);
191 free(cmd);
192 return 0;
193 }
194 exe = s;
195 }
196 s += strlen(s) + 1;
197 if (m)
198 {
199 cmd->insert = strcpy(s, argpat);
200 cmd->insertlen = m - 1;
201 s += m;
202 }
203 s += sizeof(char**) - (s - cmd->buf) % sizeof(char**);
204 p = (char**)s;
205 n -= strlen(*p++ = sh) + 1;
206 cmd->argv = p;
207 *p++ = exe;
208 while (*p = *++argv)
209 p++;
210 if (m)
211 {
212 argmax = 1;
213 *p++ = 0;
214 cmd->insertarg = p;
215 argv = cmd->argv;
216 c = *cmd->insert;
217 while (s = *argv)
218 {
219 while ((s = strchr(s, c)) && strncmp(cmd->insert, s, cmd->insertlen))
220 s++;
221 *p++ = s ? *argv : (char*)0;
222 argv++;
223 }
224 *p++ = 0;
225 }
226 cmd->firstarg = cmd->nextarg = p;
227 cmd->laststr = cmd->nextstr = cmd->buf + n;
228 cmd->argmax = argmax;
229 cmd->flags = disc->flags;
230 cmd->offset = ((cmd->postarg = post) ? (argc - (post - argv)) : 0) + 3;
231 return cmd;
232 }
233
234 /*
235 * flush outstanding command file args
236 */
237
238 int
cmdflush(register Cmdarg_t * cmd)239 cmdflush(register Cmdarg_t* cmd)
240 {
241 register char* s;
242 register char** p;
243 register int n;
244
245 if (cmd->flags & CMD_EMPTY)
246 cmd->flags &= ~CMD_EMPTY;
247 else if (cmd->nextarg <= cmd->firstarg)
248 return 0;
249 if ((cmd->flags & CMD_MINIMUM) && cmd->argcount < cmd->argmax)
250 {
251 if (cmd->errorf)
252 (*cmd->errorf)(NiL, cmd, 2, "%d arg command would be too long", cmd->argcount);
253 return -1;
254 }
255 cmd->total.args += cmd->argcount;
256 cmd->total.commands++;
257 cmd->argcount = 0;
258 if (p = cmd->postarg)
259 while (*cmd->nextarg++ = *p++);
260 else
261 *cmd->nextarg = 0;
262 if (s = cmd->insert)
263 {
264 char* a;
265 char* b;
266 char* e;
267 char* t;
268 char* u;
269 int c;
270 int m;
271
272 a = cmd->firstarg[0];
273 b = (char*)&cmd->nextarg[1];
274 e = cmd->nextstr;
275 c = *s;
276 m = cmd->insertlen;
277 for (n = 1; cmd->argv[n]; n++)
278 if (t = cmd->insertarg[n])
279 {
280 cmd->argv[n] = b;
281 for (;;)
282 {
283 if (!(u = strchr(t, c)))
284 {
285 b += sfsprintf(b, e - b, "%s", t);
286 break;
287 }
288 if (!strncmp(s, u, m))
289 {
290 b += sfsprintf(b, e - b, "%-.*s%s", u - t, t, a);
291 t = u + m;
292 }
293 else if (b >= e)
294 break;
295 else
296 {
297 *b++ = *u++;
298 t = u;
299 }
300 }
301 if (b < e)
302 *b++ = 0;
303 }
304 if (b >= e)
305 {
306 if (cmd->errorf)
307 (*cmd->errorf)(NiL, cmd, 2, "%s: command too large after insert", a);
308 return -1;
309 }
310 }
311 n = (int)(cmd->nextarg - cmd->argv);
312 cmd->nextarg = cmd->firstarg;
313 cmd->nextstr = cmd->laststr;
314 if (cmd->flags & (CMD_QUERY|CMD_TRACE))
315 {
316 p = cmd->argv;
317 sfprintf(sfstderr, "+ %s", *p);
318 while (s = *++p)
319 sfprintf(sfstderr, " %s", s);
320 if (!(cmd->flags & CMD_QUERY))
321 sfprintf(sfstderr, "\n");
322 else if (astquery(1, "? "))
323 {
324 return 0;
325 }
326 }
327 if (cmd->echo)
328 {
329 n = (cmd->flags & CMD_NEWLINE) ? '\n' : ' ';
330 for (p = cmd->argv + 1; s = *p++;)
331 sfputr(sfstdout, s, *p ? n : '\n');
332 n = 0;
333 }
334 else if ((n = (*cmd->runf)(n, cmd->argv, cmd->disc)) == -1)
335 {
336 n = EXIT_NOTFOUND - 1;
337 if (cmd->errorf)
338 (*cmd->errorf)(NiL, cmd, ERROR_SYSTEM|2, "%s: command exec error", *cmd->argv);
339 if (cmd->flags & CMD_EXIT)
340 (*error_info.exit)(n);
341 }
342 else if (n >= EXIT_NOTFOUND - 1)
343 {
344 if (cmd->flags & CMD_EXIT)
345 (*error_info.exit)(n);
346 }
347 else if (!(cmd->flags & CMD_IGNORE))
348 {
349 if (n == EXIT_QUIT && (cmd->flags & CMD_EXIT))
350 (*error_info.exit)(2);
351 if (n)
352 error_info.errors++;
353 }
354 return n;
355 }
356
357 /*
358 * add file to the command arg list
359 */
360
361 int
cmdarg(register Cmdarg_t * cmd,const char * file,register int len)362 cmdarg(register Cmdarg_t* cmd, const char* file, register int len)
363 {
364 int i;
365 int r;
366
367 r = 0;
368 if (len > 0)
369 {
370 while ((cmd->nextstr -= len + 1) < (char*)(cmd->nextarg + cmd->offset))
371 {
372 if (cmd->nextarg == cmd->firstarg)
373 {
374 if (cmd->errorf)
375 (*cmd->errorf)(NiL, cmd, 2, "%s: path too long for exec args", file);
376 return -1;
377 }
378 if (i = cmdflush(cmd))
379 {
380 if (r < i)
381 r = i;
382 if (!(cmd->flags & CMD_IGNORE))
383 return r;
384 }
385 }
386 *cmd->nextarg++ = cmd->nextstr;
387 memcpy(cmd->nextstr, file, len);
388 cmd->nextstr[len] = 0;
389 cmd->argcount++;
390 if (cmd->argcount >= cmd->argmax && (i = cmdflush(cmd)) > r)
391 r = i;
392 }
393 else
394 cmd->argcount += len;
395 return r;
396 }
397
398 /*
399 * close a cmdarg stream
400 */
401
402 int
cmdclose(Cmdarg_t * cmd)403 cmdclose(Cmdarg_t* cmd)
404 {
405 int n;
406
407 if ((cmd->flags & CMD_EXACT) && cmd->argcount < cmd->argmax)
408 {
409 if (cmd->errorf)
410 (*cmd->errorf)(NiL, cmd, 2, "only %d arguments for last command", cmd->argcount);
411 n = -1;
412 }
413 else
414 {
415 cmd->flags &= ~CMD_MINIMUM;
416 n = cmdflush(cmd);
417 }
418 free(cmd);
419 return n;
420 }
421