xref: /titanic_51/usr/src/lib/libast/common/misc/cmdarg.c (revision 3e14f97f673e8a630f076077de35afdd43dc1587)
17c2fbfb3SApril Chin /***********************************************************************
27c2fbfb3SApril Chin *                                                                      *
37c2fbfb3SApril Chin *               This software is part of the ast package               *
4*3e14f97fSRoger A. Faulkner *          Copyright (c) 1985-2010 AT&T Intellectual Property          *
57c2fbfb3SApril Chin *                      and is licensed under the                       *
67c2fbfb3SApril Chin *                  Common Public License, Version 1.0                  *
77c2fbfb3SApril Chin *                    by AT&T Intellectual Property                     *
87c2fbfb3SApril Chin *                                                                      *
97c2fbfb3SApril Chin *                A copy of the License is available at                 *
107c2fbfb3SApril Chin *            http://www.opensource.org/licenses/cpl1.0.txt             *
117c2fbfb3SApril Chin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
127c2fbfb3SApril Chin *                                                                      *
137c2fbfb3SApril Chin *              Information and Software Systems Research               *
147c2fbfb3SApril Chin *                            AT&T Research                             *
157c2fbfb3SApril Chin *                           Florham Park NJ                            *
167c2fbfb3SApril Chin *                                                                      *
177c2fbfb3SApril Chin *                 Glenn Fowler <gsf@research.att.com>                  *
187c2fbfb3SApril Chin *                  David Korn <dgk@research.att.com>                   *
197c2fbfb3SApril Chin *                   Phong Vo <kpv@research.att.com>                    *
207c2fbfb3SApril Chin *                                                                      *
217c2fbfb3SApril Chin ***********************************************************************/
227c2fbfb3SApril Chin #pragma prototyped
237c2fbfb3SApril Chin /*
247c2fbfb3SApril Chin  * Glenn Fowler
257c2fbfb3SApril Chin  * AT&T Research
267c2fbfb3SApril Chin  *
277c2fbfb3SApril Chin  * xargs/tw command arg list support
287c2fbfb3SApril Chin  */
297c2fbfb3SApril Chin 
307c2fbfb3SApril Chin #include <ast.h>
317c2fbfb3SApril Chin #include <ctype.h>
327c2fbfb3SApril Chin #include <error.h>
337c2fbfb3SApril Chin #include <proc.h>
347c2fbfb3SApril Chin 
357c2fbfb3SApril Chin #include "cmdarg.h"
367c2fbfb3SApril Chin 
3734f9b3eeSRoland Mainz #ifndef ARG_MAX
3834f9b3eeSRoland Mainz #define ARG_MAX		(64*1024)
3934f9b3eeSRoland Mainz #endif
407c2fbfb3SApril Chin #ifndef EXIT_QUIT
417c2fbfb3SApril Chin #define EXIT_QUIT	255
427c2fbfb3SApril Chin #endif
437c2fbfb3SApril Chin 
447c2fbfb3SApril Chin static const char*	echo[] = { "echo", 0 };
457c2fbfb3SApril Chin 
467c2fbfb3SApril Chin /*
477c2fbfb3SApril Chin  * open a cmdarg stream
487c2fbfb3SApril Chin  * initialize the command for execution
497c2fbfb3SApril Chin  * argv[-1] is reserved for procrun(PROC_ARGMOD)
507c2fbfb3SApril Chin  */
517c2fbfb3SApril Chin 
527c2fbfb3SApril Chin Cmdarg_t*
cmdopen(char ** argv,int argmax,int size,const char * argpat,int flags)537c2fbfb3SApril Chin cmdopen(char** argv, int argmax, int size, const char* argpat, int flags)
547c2fbfb3SApril Chin {
557c2fbfb3SApril Chin 	register Cmdarg_t*	cmd;
567c2fbfb3SApril Chin 	register int		n;
577c2fbfb3SApril Chin 	register char**		p;
587c2fbfb3SApril Chin 	register char*		s;
597c2fbfb3SApril Chin 	char*			sh;
607c2fbfb3SApril Chin 	int			c;
617c2fbfb3SApril Chin 	int			m;
627c2fbfb3SApril Chin 	int			argc;
637c2fbfb3SApril Chin 	long			x;
647c2fbfb3SApril Chin 
657c2fbfb3SApril Chin 	char**			post = 0;
667c2fbfb3SApril Chin 
677c2fbfb3SApril Chin 	n = sizeof(char**);
687c2fbfb3SApril Chin 	if (*argv)
697c2fbfb3SApril Chin 	{
707c2fbfb3SApril Chin 		for (p = argv + 1; *p; p++)
717c2fbfb3SApril Chin 		{
727c2fbfb3SApril Chin 			if ((flags & CMD_POST) && argpat && streq(*p, argpat))
737c2fbfb3SApril Chin 			{
747c2fbfb3SApril Chin 				*p = 0;
757c2fbfb3SApril Chin 				post = p + 1;
767c2fbfb3SApril Chin 				argpat = 0;
777c2fbfb3SApril Chin 			}
787c2fbfb3SApril Chin 			else
797c2fbfb3SApril Chin 				n += strlen(*p) + 1;
807c2fbfb3SApril Chin 		}
817c2fbfb3SApril Chin 		argc = p - argv;
827c2fbfb3SApril Chin 	}
837c2fbfb3SApril Chin 	else
847c2fbfb3SApril Chin 		argc = 0;
857c2fbfb3SApril Chin 	for (p = environ; *p; p++)
867c2fbfb3SApril Chin 		n += sizeof(char**) + strlen(*p) + 1;
877c2fbfb3SApril Chin 	if ((x = strtol(astconf("ARG_MAX", NiL, NiL), NiL, 0)) <= 0)
887c2fbfb3SApril Chin 		x = ARG_MAX;
897c2fbfb3SApril Chin 	if (size <= 0 || size > x)
907c2fbfb3SApril Chin 		size = x;
917c2fbfb3SApril Chin 	sh = pathshell();
927c2fbfb3SApril Chin 	m = n + (argc + 4) * sizeof(char**) + strlen(sh) + 1;
937c2fbfb3SApril Chin 	m = roundof(m, sizeof(char**));
947c2fbfb3SApril Chin 	if (size < m)
957c2fbfb3SApril Chin 	{
967c2fbfb3SApril Chin 		error(2, "size must be at least %d", m);
977c2fbfb3SApril Chin 		return 0;
987c2fbfb3SApril Chin 	}
997c2fbfb3SApril Chin 	if ((m = x / 10) > 2048)
1007c2fbfb3SApril Chin 		m = 2048;
1017c2fbfb3SApril Chin 	if (size > (x - m))
1027c2fbfb3SApril Chin 		size = x - m;
1037c2fbfb3SApril Chin 	n = size - n;
1047c2fbfb3SApril Chin 	m = ((flags & CMD_INSERT) && argpat) ? (strlen(argpat) + 1) : 0;
1057c2fbfb3SApril Chin 	if (!(cmd = newof(0, Cmdarg_t, 1, n + m)))
1067c2fbfb3SApril Chin 	{
1077c2fbfb3SApril Chin 		error(ERROR_SYSTEM|2, "out of space");
1087c2fbfb3SApril Chin 		return 0;
1097c2fbfb3SApril Chin 	}
1107c2fbfb3SApril Chin 	c = n / sizeof(char**);
1117c2fbfb3SApril Chin 	if (argmax <= 0 || argmax > c)
1127c2fbfb3SApril Chin 		argmax = c;
1137c2fbfb3SApril Chin 	s = cmd->buf;
1147c2fbfb3SApril Chin 	if (!argv[0])
1157c2fbfb3SApril Chin 	{
1167c2fbfb3SApril Chin 		argv = (char**)echo;
1177c2fbfb3SApril Chin 		cmd->echo = 1;
1187c2fbfb3SApril Chin 	}
1197c2fbfb3SApril Chin 	else if (streq(argv[0], echo[0]))
1207c2fbfb3SApril Chin 	{
1217c2fbfb3SApril Chin 		cmd->echo = 1;
1227c2fbfb3SApril Chin 		flags &= ~CMD_NEWLINE;
1237c2fbfb3SApril Chin 	}
1247c2fbfb3SApril Chin 	else if (!(flags & CMD_CHECKED))
1257c2fbfb3SApril Chin 	{
1267c2fbfb3SApril Chin 		if (!pathpath(s, argv[0], NiL, PATH_REGULAR|PATH_EXECUTE))
1277c2fbfb3SApril Chin 		{
1287c2fbfb3SApril Chin 			if (!(flags & CMD_SILENT))
1297c2fbfb3SApril Chin 			{
1307c2fbfb3SApril Chin 				error(ERROR_SYSTEM|2, "%s: command not found", argv[0]);
1317c2fbfb3SApril Chin 				exit(EXIT_NOTFOUND);
1327c2fbfb3SApril Chin 			}
1337c2fbfb3SApril Chin 			free(cmd);
1347c2fbfb3SApril Chin 			return 0;
1357c2fbfb3SApril Chin 		}
1367c2fbfb3SApril Chin 		argv[0] = s;
1377c2fbfb3SApril Chin 	}
1387c2fbfb3SApril Chin 	s += strlen(s) + 1;
1397c2fbfb3SApril Chin 	if (m)
1407c2fbfb3SApril Chin 	{
1417c2fbfb3SApril Chin 		cmd->insert = strcpy(s, argpat);
1427c2fbfb3SApril Chin 		cmd->insertlen = m - 1;
1437c2fbfb3SApril Chin 		s += m;
1447c2fbfb3SApril Chin 	}
1457c2fbfb3SApril Chin 	s += sizeof(char**) - (s - cmd->buf) % sizeof(char**);
1467c2fbfb3SApril Chin 	p = (char**)s;
1477c2fbfb3SApril Chin 	n -= strlen(*p++ = sh) + 1;
1487c2fbfb3SApril Chin 	cmd->argv = p;
1497c2fbfb3SApril Chin 	while (*p = *argv++)
1507c2fbfb3SApril Chin 		p++;
1517c2fbfb3SApril Chin 	if (m)
1527c2fbfb3SApril Chin 	{
1537c2fbfb3SApril Chin 		argmax = 1;
1547c2fbfb3SApril Chin 		*p++ = 0;
1557c2fbfb3SApril Chin 		cmd->insertarg = p;
1567c2fbfb3SApril Chin 		argv = cmd->argv;
1577c2fbfb3SApril Chin 		c = *cmd->insert;
1587c2fbfb3SApril Chin 		while (s = *argv)
1597c2fbfb3SApril Chin 		{
1607c2fbfb3SApril Chin 			while ((s = strchr(s, c)) && strncmp(cmd->insert, s, cmd->insertlen))
1617c2fbfb3SApril Chin 				s++;
1627c2fbfb3SApril Chin 			*p++ = s ? *argv : (char*)0;
1637c2fbfb3SApril Chin 			argv++;
1647c2fbfb3SApril Chin 		}
1657c2fbfb3SApril Chin 		*p++ = 0;
1667c2fbfb3SApril Chin 	}
1677c2fbfb3SApril Chin 	cmd->firstarg = cmd->nextarg = p;
1687c2fbfb3SApril Chin 	cmd->laststr = cmd->nextstr = cmd->buf + n;
1697c2fbfb3SApril Chin 	cmd->argmax = argmax;
1707c2fbfb3SApril Chin 	cmd->flags = flags;
1717c2fbfb3SApril Chin 	cmd->offset = ((cmd->postarg = post) ? (argc - (post - argv)) : 0) + 3;
1727c2fbfb3SApril Chin 	return cmd;
1737c2fbfb3SApril Chin }
1747c2fbfb3SApril Chin 
1757c2fbfb3SApril Chin /*
1767c2fbfb3SApril Chin  * flush outstanding command file args
1777c2fbfb3SApril Chin  */
1787c2fbfb3SApril Chin 
1797c2fbfb3SApril Chin int
cmdflush(register Cmdarg_t * cmd)1807c2fbfb3SApril Chin cmdflush(register Cmdarg_t* cmd)
1817c2fbfb3SApril Chin {
1827c2fbfb3SApril Chin 	register char*	s;
1837c2fbfb3SApril Chin 	register char**	p;
1847c2fbfb3SApril Chin 	register int	n;
1857c2fbfb3SApril Chin 
1867c2fbfb3SApril Chin 	if (cmd->flags & CMD_EMPTY)
1877c2fbfb3SApril Chin 		cmd->flags &= ~CMD_EMPTY;
1887c2fbfb3SApril Chin 	else if (cmd->nextarg <= cmd->firstarg)
1897c2fbfb3SApril Chin 		return 0;
1907c2fbfb3SApril Chin 	if ((cmd->flags & CMD_MINIMUM) && cmd->argcount < cmd->argmax)
1917c2fbfb3SApril Chin 	{
1927c2fbfb3SApril Chin 		if (!(cmd->flags & CMD_SILENT))
1937c2fbfb3SApril Chin 			error(2, "%d arg command would be too long", cmd->argcount);
1947c2fbfb3SApril Chin 		return -1;
1957c2fbfb3SApril Chin 	}
1967c2fbfb3SApril Chin 	cmd->total.args += cmd->argcount;
1977c2fbfb3SApril Chin 	cmd->total.commands++;
1987c2fbfb3SApril Chin 	cmd->argcount = 0;
1997c2fbfb3SApril Chin 	if (p = cmd->postarg)
2007c2fbfb3SApril Chin 		while (*cmd->nextarg++ = *p++);
2017c2fbfb3SApril Chin 	else
2027c2fbfb3SApril Chin 		*cmd->nextarg = 0;
2037c2fbfb3SApril Chin 	if (s = cmd->insert)
2047c2fbfb3SApril Chin 	{
2057c2fbfb3SApril Chin 		char*	a;
2067c2fbfb3SApril Chin 		char*	b;
2077c2fbfb3SApril Chin 		char*	e;
2087c2fbfb3SApril Chin 		char*	t;
2097c2fbfb3SApril Chin 		char*	u;
2107c2fbfb3SApril Chin 		int	c;
2117c2fbfb3SApril Chin 		int	m;
2127c2fbfb3SApril Chin 
2137c2fbfb3SApril Chin 		a = cmd->firstarg[0];
2147c2fbfb3SApril Chin 		b = (char*)&cmd->nextarg[1];
2157c2fbfb3SApril Chin 		e = cmd->nextstr;
2167c2fbfb3SApril Chin 		c = *s;
2177c2fbfb3SApril Chin 		m = cmd->insertlen;
2187c2fbfb3SApril Chin 		for (n = 1; cmd->argv[n]; n++)
2197c2fbfb3SApril Chin 			if (t = cmd->insertarg[n])
2207c2fbfb3SApril Chin 			{
2217c2fbfb3SApril Chin 				cmd->argv[n] = b;
2227c2fbfb3SApril Chin 				for (;;)
2237c2fbfb3SApril Chin 				{
2247c2fbfb3SApril Chin 					if (!(u = strchr(t, c)))
2257c2fbfb3SApril Chin 					{
2267c2fbfb3SApril Chin 						b += sfsprintf(b, e - b, "%s", t);
2277c2fbfb3SApril Chin 						break;
2287c2fbfb3SApril Chin 					}
2297c2fbfb3SApril Chin 					if (!strncmp(s, u, m))
2307c2fbfb3SApril Chin 					{
2317c2fbfb3SApril Chin 						b += sfsprintf(b, e - b, "%-.*s%s", u - t, t, a);
2327c2fbfb3SApril Chin 						t = u + m;
2337c2fbfb3SApril Chin 					}
2347c2fbfb3SApril Chin 					else if (b >= e)
2357c2fbfb3SApril Chin 						break;
2367c2fbfb3SApril Chin 					else
2377c2fbfb3SApril Chin 					{
2387c2fbfb3SApril Chin 						*b++ = *u++;
2397c2fbfb3SApril Chin 						t = u;
2407c2fbfb3SApril Chin 					}
2417c2fbfb3SApril Chin 				}
2427c2fbfb3SApril Chin 				if (b < e)
2437c2fbfb3SApril Chin 					*b++ = 0;
2447c2fbfb3SApril Chin 			}
2457c2fbfb3SApril Chin 		if (b >= e)
2467c2fbfb3SApril Chin 		{
2477c2fbfb3SApril Chin 			if (!(cmd->flags & CMD_SILENT))
2487c2fbfb3SApril Chin 				error(2, "%s: command too large after insert", a);
2497c2fbfb3SApril Chin 			return -1;
2507c2fbfb3SApril Chin 		}
2517c2fbfb3SApril Chin 	}
2527c2fbfb3SApril Chin 	cmd->nextarg = cmd->firstarg;
2537c2fbfb3SApril Chin 	cmd->nextstr = cmd->laststr;
2547c2fbfb3SApril Chin 	if (cmd->flags & (CMD_QUERY|CMD_TRACE))
2557c2fbfb3SApril Chin 	{
2567c2fbfb3SApril Chin 		p = cmd->argv;
2577c2fbfb3SApril Chin 		sfprintf(sfstderr, "+ %s", *p);
2587c2fbfb3SApril Chin 		while (s = *++p)
2597c2fbfb3SApril Chin 			sfprintf(sfstderr, " %s", s);
2607c2fbfb3SApril Chin 		if (!(cmd->flags & CMD_QUERY))
2617c2fbfb3SApril Chin 			sfprintf(sfstderr, "\n");
2627c2fbfb3SApril Chin 		else if (astquery(1, "? "))
2637c2fbfb3SApril Chin 			return 0;
2647c2fbfb3SApril Chin 	}
2657c2fbfb3SApril Chin 	if (cmd->echo)
2667c2fbfb3SApril Chin 	{
2677c2fbfb3SApril Chin 		n = (cmd->flags & CMD_NEWLINE) ? '\n' : ' ';
2687c2fbfb3SApril Chin 		for (p = cmd->argv + 1; s = *p++;)
2697c2fbfb3SApril Chin 			sfputr(sfstdout, s, *p ? n : '\n');
2707c2fbfb3SApril Chin 		n = 0;
2717c2fbfb3SApril Chin 	}
2727c2fbfb3SApril Chin 	else if ((n = procrun(*cmd->argv, cmd->argv, PROC_ARGMOD|PROC_IGNOREPATH)) == -1)
2737c2fbfb3SApril Chin 	{
2747c2fbfb3SApril Chin 		if (!(cmd->flags & CMD_SILENT))
2757c2fbfb3SApril Chin 		{
2767c2fbfb3SApril Chin 			error(ERROR_SYSTEM|2, "%s: command exec error", *cmd->argv);
2777c2fbfb3SApril Chin 			exit(EXIT_NOTFOUND - 1);
2787c2fbfb3SApril Chin 		}
2797c2fbfb3SApril Chin 		return -1;
2807c2fbfb3SApril Chin 	}
2817c2fbfb3SApril Chin 	else if (n >= EXIT_NOTFOUND - 1)
2827c2fbfb3SApril Chin 	{
2837c2fbfb3SApril Chin 		if (!(cmd->flags & CMD_SILENT))
2847c2fbfb3SApril Chin 			exit(n);
2857c2fbfb3SApril Chin 	}
2867c2fbfb3SApril Chin 	else if (!(cmd->flags & CMD_IGNORE))
2877c2fbfb3SApril Chin 	{
2887c2fbfb3SApril Chin 		if (n == EXIT_QUIT && !(cmd->flags & CMD_SILENT))
2897c2fbfb3SApril Chin 			exit(2);
2907c2fbfb3SApril Chin 		if (n)
2917c2fbfb3SApril Chin 			error_info.errors++;
2927c2fbfb3SApril Chin 	}
2937c2fbfb3SApril Chin 	return n;
2947c2fbfb3SApril Chin }
2957c2fbfb3SApril Chin 
2967c2fbfb3SApril Chin /*
2977c2fbfb3SApril Chin  * add file to the command arg list
2987c2fbfb3SApril Chin  */
2997c2fbfb3SApril Chin 
3007c2fbfb3SApril Chin int
cmdarg(register Cmdarg_t * cmd,const char * file,register int len)3017c2fbfb3SApril Chin cmdarg(register Cmdarg_t* cmd, const char* file, register int len)
3027c2fbfb3SApril Chin {
3037c2fbfb3SApril Chin 	int	i;
3047c2fbfb3SApril Chin 	int	r;
3057c2fbfb3SApril Chin 
3067c2fbfb3SApril Chin 	r = 0;
3077c2fbfb3SApril Chin 	if (len)
3087c2fbfb3SApril Chin 	{
3097c2fbfb3SApril Chin 		while ((cmd->nextstr -= len + 1) < (char*)(cmd->nextarg + cmd->offset))
3107c2fbfb3SApril Chin 		{
3117c2fbfb3SApril Chin 			if (cmd->nextarg == cmd->firstarg)
3127c2fbfb3SApril Chin 			{
3137c2fbfb3SApril Chin 				error(2, "%s: path too long for exec args", file);
3147c2fbfb3SApril Chin 				return -1;
3157c2fbfb3SApril Chin 			}
3167c2fbfb3SApril Chin 			if (i = cmdflush(cmd))
3177c2fbfb3SApril Chin 			{
3187c2fbfb3SApril Chin 				if (r < i)
3197c2fbfb3SApril Chin 					r = i;
3207c2fbfb3SApril Chin 				if (!(cmd->flags & CMD_IGNORE))
3217c2fbfb3SApril Chin 					return r;
3227c2fbfb3SApril Chin 			}
3237c2fbfb3SApril Chin 		}
3247c2fbfb3SApril Chin 		*cmd->nextarg++ = cmd->nextstr;
3257c2fbfb3SApril Chin 		memcpy(cmd->nextstr, file, len);
3267c2fbfb3SApril Chin 		cmd->nextstr[len] = 0;
3277c2fbfb3SApril Chin 		cmd->argcount++;
3287c2fbfb3SApril Chin 		if (cmd->argcount >= cmd->argmax && (i = cmdflush(cmd)) > r)
3297c2fbfb3SApril Chin 			r = i;
3307c2fbfb3SApril Chin 	}
3317c2fbfb3SApril Chin 	return r;
3327c2fbfb3SApril Chin }
3337c2fbfb3SApril Chin 
3347c2fbfb3SApril Chin /*
3357c2fbfb3SApril Chin  * close a cmdarg stream
3367c2fbfb3SApril Chin  */
3377c2fbfb3SApril Chin 
3387c2fbfb3SApril Chin int
cmdclose(Cmdarg_t * cmd)3397c2fbfb3SApril Chin cmdclose(Cmdarg_t* cmd)
3407c2fbfb3SApril Chin {
3417c2fbfb3SApril Chin 	int	n;
3427c2fbfb3SApril Chin 
3437c2fbfb3SApril Chin 	if ((cmd->flags & CMD_EXACT) && cmd->argcount < cmd->argmax)
3447c2fbfb3SApril Chin 	{
3457c2fbfb3SApril Chin 		if (!(cmd->flags & CMD_SILENT))
3467c2fbfb3SApril Chin 			error(2, "only %d arguments for last command", cmd->argcount);
3477c2fbfb3SApril Chin 		return -1;
3487c2fbfb3SApril Chin 	}
3497c2fbfb3SApril Chin 	cmd->flags &= ~CMD_MINIMUM;
3507c2fbfb3SApril Chin 	n = cmdflush(cmd);
3517c2fbfb3SApril Chin 	free(cmd);
3527c2fbfb3SApril Chin 	return n;
3537c2fbfb3SApril Chin }
354