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