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