1 /***********************************************************************
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 *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
13 * Information and Software Systems Research *
17 * Glenn Fowler <gsf@research.att.com> *
18 * David Korn <dgk@research.att.com> *
19 * Phong Vo <kpv@research.att.com> *
21 ***********************************************************************/
27 * xargs/tw command arg list support
38 #define ARG_MAX (64*1024)
44 static const char* echo
[] = { "echo", 0 };
47 * open a cmdarg stream
48 * initialize the command for execution
49 * argv[-1] is reserved for procrun(PROC_ARGMOD)
53 cmdopen(char** argv
, int argmax
, int size
, const char* argpat
, int flags
)
55 register Cmdarg_t
* cmd
;
70 for (p
= argv
+ 1; *p
; p
++)
72 if ((flags
& CMD_POST
) && argpat
&& streq(*p
, argpat
))
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)
89 if (size
<= 0 || size
> x
)
92 m
= n
+ (argc
+ 4) * sizeof(char**) + strlen(sh
) + 1;
93 m
= roundof(m
, sizeof(char**));
96 error(2, "size must be at least %d", m
);
99 if ((m
= x
/ 10) > 2048)
104 m
= ((flags
& CMD_INSERT
) && argpat
) ? (strlen(argpat
) + 1) : 0;
105 if (!(cmd
= newof(0, Cmdarg_t
, 1, n
+ m
)))
107 error(ERROR_SYSTEM
|2, "out of space");
110 c
= n
/ sizeof(char**);
111 if (argmax
<= 0 || argmax
> c
)
119 else if (streq(argv
[0], echo
[0]))
122 flags
&= ~CMD_NEWLINE
;
124 else if (!(flags
& CMD_CHECKED
))
126 if (!pathpath(s
, argv
[0], NiL
, PATH_REGULAR
|PATH_EXECUTE
))
128 if (!(flags
& CMD_SILENT
))
130 error(ERROR_SYSTEM
|2, "%s: command not found", argv
[0]);
141 cmd
->insert
= strcpy(s
, argpat
);
142 cmd
->insertlen
= m
- 1;
145 s
+= sizeof(char**) - (s
- cmd
->buf
) % sizeof(char**);
147 n
-= strlen(*p
++ = sh
) + 1;
160 while ((s
= strchr(s
, c
)) && strncmp(cmd
->insert
, s
, cmd
->insertlen
))
162 *p
++ = s
? *argv
: (char*)0;
167 cmd
->firstarg
= cmd
->nextarg
= p
;
168 cmd
->laststr
= cmd
->nextstr
= cmd
->buf
+ n
;
169 cmd
->argmax
= argmax
;
171 cmd
->offset
= ((cmd
->postarg
= post
) ? (argc
- (post
- argv
)) : 0) + 3;
176 * flush outstanding command file args
180 cmdflush(register Cmdarg_t
* cmd
)
186 if (cmd
->flags
& CMD_EMPTY
)
187 cmd
->flags
&= ~CMD_EMPTY
;
188 else if (cmd
->nextarg
<= cmd
->firstarg
)
190 if ((cmd
->flags
& CMD_MINIMUM
) && cmd
->argcount
< cmd
->argmax
)
192 if (!(cmd
->flags
& CMD_SILENT
))
193 error(2, "%d arg command would be too long", cmd
->argcount
);
196 cmd
->total
.args
+= cmd
->argcount
;
197 cmd
->total
.commands
++;
199 if (p
= cmd
->postarg
)
200 while (*cmd
->nextarg
++ = *p
++);
213 a
= cmd
->firstarg
[0];
214 b
= (char*)&cmd
->nextarg
[1];
218 for (n
= 1; cmd
->argv
[n
]; n
++)
219 if (t
= cmd
->insertarg
[n
])
224 if (!(u
= strchr(t
, c
)))
226 b
+= sfsprintf(b
, e
- b
, "%s", t
);
229 if (!strncmp(s
, u
, m
))
231 b
+= sfsprintf(b
, e
- b
, "%-.*s%s", u
- t
, t
, a
);
247 if (!(cmd
->flags
& CMD_SILENT
))
248 error(2, "%s: command too large after insert", a
);
252 cmd
->nextarg
= cmd
->firstarg
;
253 cmd
->nextstr
= cmd
->laststr
;
254 if (cmd
->flags
& (CMD_QUERY
|CMD_TRACE
))
257 sfprintf(sfstderr
, "+ %s", *p
);
259 sfprintf(sfstderr
, " %s", s
);
260 if (!(cmd
->flags
& CMD_QUERY
))
261 sfprintf(sfstderr
, "\n");
262 else if (astquery(1, "? "))
267 n
= (cmd
->flags
& CMD_NEWLINE
) ? '\n' : ' ';
268 for (p
= cmd
->argv
+ 1; s
= *p
++;)
269 sfputr(sfstdout
, s
, *p
? n
: '\n');
272 else if ((n
= procrun(*cmd
->argv
, cmd
->argv
, PROC_ARGMOD
|PROC_IGNOREPATH
)) == -1)
274 if (!(cmd
->flags
& CMD_SILENT
))
276 error(ERROR_SYSTEM
|2, "%s: command exec error", *cmd
->argv
);
277 exit(EXIT_NOTFOUND
- 1);
281 else if (n
>= EXIT_NOTFOUND
- 1)
283 if (!(cmd
->flags
& CMD_SILENT
))
286 else if (!(cmd
->flags
& CMD_IGNORE
))
288 if (n
== EXIT_QUIT
&& !(cmd
->flags
& CMD_SILENT
))
297 * add file to the command arg list
301 cmdarg(register Cmdarg_t
* cmd
, const char* file
, register int len
)
309 while ((cmd
->nextstr
-= len
+ 1) < (char*)(cmd
->nextarg
+ cmd
->offset
))
311 if (cmd
->nextarg
== cmd
->firstarg
)
313 error(2, "%s: path too long for exec args", file
);
316 if (i
= cmdflush(cmd
))
320 if (!(cmd
->flags
& CMD_IGNORE
))
324 *cmd
->nextarg
++ = cmd
->nextstr
;
325 memcpy(cmd
->nextstr
, file
, len
);
326 cmd
->nextstr
[len
] = 0;
328 if (cmd
->argcount
>= cmd
->argmax
&& (i
= cmdflush(cmd
)) > r
)
335 * close a cmdarg stream
339 cmdclose(Cmdarg_t
* cmd
)
343 if ((cmd
->flags
& CMD_EXACT
) && cmd
->argcount
< cmd
->argmax
)
345 if (!(cmd
->flags
& CMD_SILENT
))
346 error(2, "only %d arguments for last command", cmd
->argcount
);
349 cmd
->flags
&= ~CMD_MINIMUM
;