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