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