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