1N/A/***********************************************************************
1N/A* *
1N/A* This software is part of the ast package *
1N/A* Copyright (c) 1985-2011 AT&T Intellectual Property *
1N/A* and is licensed under the *
1N/A* Common Public License, Version 1.0 *
1N/A* by AT&T Intellectual Property *
1N/A* *
1N/A* A copy of the License is available at *
1N/A* http://www.opensource.org/licenses/cpl1.0.txt *
1N/A* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
1N/A* *
1N/A* Information and Software Systems Research *
1N/A* AT&T Research *
1N/A* Florham Park NJ *
1N/A* *
1N/A* Glenn Fowler <gsf@research.att.com> *
1N/A* David Korn <dgk@research.att.com> *
1N/A* Phong Vo <kpv@research.att.com> *
1N/A* *
1N/A***********************************************************************/
1N/A#pragma prototyped
1N/A/*
1N/A * Glenn Fowler
1N/A * AT&T Research
1N/A *
1N/A * xargs/tw command arg list support
1N/A */
1N/A
1N/A#include <ast.h>
1N/A#include <ctype.h>
1N/A#include <error.h>
1N/A#include <proc.h>
1N/A
1N/A#include "cmdarg.h"
1N/A
1N/A#ifndef ARG_MAX
1N/A#define ARG_MAX (64*1024)
1N/A#endif
1N/A#ifndef EXIT_QUIT
1N/A#define EXIT_QUIT 255
1N/A#endif
1N/A
1N/Astatic const char* echo[] = { "echo", 0 };
1N/A
1N/A/*
1N/A * open a cmdarg stream
1N/A * initialize the command for execution
1N/A * argv[-1] is reserved for procrun(PROC_ARGMOD)
1N/A */
1N/A
1N/ACmdarg_t*
1N/Acmdopen(char** argv, int argmax, int size, const char* argpat, int flags)
1N/A{
1N/A register Cmdarg_t* cmd;
1N/A register int n;
1N/A register char** p;
1N/A register char* s;
1N/A char* sh;
1N/A int c;
1N/A int m;
1N/A int argc;
1N/A long x;
1N/A
1N/A char** post = 0;
1N/A
1N/A n = sizeof(char**);
1N/A if (*argv)
1N/A {
1N/A for (p = argv + 1; *p; p++)
1N/A {
1N/A if ((flags & CMD_POST) && argpat && streq(*p, argpat))
1N/A {
1N/A *p = 0;
1N/A post = p + 1;
1N/A argpat = 0;
1N/A }
1N/A else
1N/A n += strlen(*p) + 1;
1N/A }
1N/A argc = p - argv;
1N/A }
1N/A else
1N/A argc = 0;
1N/A for (p = environ; *p; p++)
1N/A n += sizeof(char**) + strlen(*p) + 1;
1N/A if ((x = strtol(astconf("ARG_MAX", NiL, NiL), NiL, 0)) <= 0)
1N/A x = ARG_MAX;
1N/A if (size <= 0 || size > x)
1N/A size = x;
1N/A sh = pathshell();
1N/A m = n + (argc + 4) * sizeof(char**) + strlen(sh) + 1;
1N/A m = roundof(m, sizeof(char**));
1N/A if (size < m)
1N/A {
1N/A error(2, "size must be at least %d", m);
1N/A return 0;
1N/A }
1N/A if ((m = x / 10) > 2048)
1N/A m = 2048;
1N/A if (size > (x - m))
1N/A size = x - m;
1N/A n = size - n;
1N/A m = ((flags & CMD_INSERT) && argpat) ? (strlen(argpat) + 1) : 0;
1N/A if (!(cmd = newof(0, Cmdarg_t, 1, n + m)))
1N/A {
1N/A error(ERROR_SYSTEM|2, "out of space");
1N/A return 0;
1N/A }
1N/A c = n / sizeof(char**);
1N/A if (argmax <= 0 || argmax > c)
1N/A argmax = c;
1N/A s = cmd->buf;
1N/A if (!argv[0])
1N/A {
1N/A argv = (char**)echo;
1N/A cmd->echo = 1;
1N/A }
1N/A else if (streq(argv[0], echo[0]))
1N/A {
1N/A cmd->echo = 1;
1N/A flags &= ~CMD_NEWLINE;
1N/A }
1N/A else if (!(flags & CMD_CHECKED))
1N/A {
1N/A if (!pathpath(argv[0], NiL, PATH_REGULAR|PATH_EXECUTE, s, n + m))
1N/A {
1N/A if (!(flags & CMD_SILENT))
1N/A {
1N/A error(ERROR_SYSTEM|2, "%s: command not found", argv[0]);
1N/A exit(EXIT_NOTFOUND);
1N/A }
1N/A free(cmd);
1N/A return 0;
1N/A }
1N/A argv[0] = s;
1N/A }
1N/A s += strlen(s) + 1;
1N/A if (m)
1N/A {
1N/A cmd->insert = strcpy(s, argpat);
1N/A cmd->insertlen = m - 1;
1N/A s += m;
1N/A }
1N/A s += sizeof(char**) - (s - cmd->buf) % sizeof(char**);
1N/A p = (char**)s;
1N/A n -= strlen(*p++ = sh) + 1;
1N/A cmd->argv = p;
1N/A while (*p = *argv++)
1N/A p++;
1N/A if (m)
1N/A {
1N/A argmax = 1;
1N/A *p++ = 0;
1N/A cmd->insertarg = p;
1N/A argv = cmd->argv;
1N/A c = *cmd->insert;
1N/A while (s = *argv)
1N/A {
1N/A while ((s = strchr(s, c)) && strncmp(cmd->insert, s, cmd->insertlen))
1N/A s++;
1N/A *p++ = s ? *argv : (char*)0;
1N/A argv++;
1N/A }
1N/A *p++ = 0;
1N/A }
1N/A cmd->firstarg = cmd->nextarg = p;
1N/A cmd->laststr = cmd->nextstr = cmd->buf + n;
1N/A cmd->argmax = argmax;
1N/A cmd->flags = flags;
1N/A cmd->offset = ((cmd->postarg = post) ? (argc - (post - argv)) : 0) + 3;
1N/A return cmd;
1N/A}
1N/A
1N/A/*
1N/A * flush outstanding command file args
1N/A */
1N/A
1N/Aint
1N/Acmdflush(register Cmdarg_t* cmd)
1N/A{
1N/A register char* s;
1N/A register char** p;
1N/A register int n;
1N/A
1N/A if (cmd->flags & CMD_EMPTY)
1N/A cmd->flags &= ~CMD_EMPTY;
1N/A else if (cmd->nextarg <= cmd->firstarg)
1N/A return 0;
1N/A if ((cmd->flags & CMD_MINIMUM) && cmd->argcount < cmd->argmax)
1N/A {
1N/A if (!(cmd->flags & CMD_SILENT))
1N/A error(2, "%d arg command would be too long", cmd->argcount);
1N/A return -1;
1N/A }
1N/A cmd->total.args += cmd->argcount;
1N/A cmd->total.commands++;
1N/A cmd->argcount = 0;
1N/A if (p = cmd->postarg)
1N/A while (*cmd->nextarg++ = *p++);
1N/A else
1N/A *cmd->nextarg = 0;
1N/A if (s = cmd->insert)
1N/A {
1N/A char* a;
1N/A char* b;
1N/A char* e;
1N/A char* t;
1N/A char* u;
1N/A int c;
1N/A int m;
1N/A
1N/A a = cmd->firstarg[0];
1N/A b = (char*)&cmd->nextarg[1];
1N/A e = cmd->nextstr;
1N/A c = *s;
1N/A m = cmd->insertlen;
1N/A for (n = 1; cmd->argv[n]; n++)
1N/A if (t = cmd->insertarg[n])
1N/A {
1N/A cmd->argv[n] = b;
1N/A for (;;)
1N/A {
1N/A if (!(u = strchr(t, c)))
1N/A {
1N/A b += sfsprintf(b, e - b, "%s", t);
1N/A break;
1N/A }
1N/A if (!strncmp(s, u, m))
1N/A {
1N/A b += sfsprintf(b, e - b, "%-.*s%s", u - t, t, a);
1N/A t = u + m;
1N/A }
1N/A else if (b >= e)
1N/A break;
1N/A else
1N/A {
1N/A *b++ = *u++;
1N/A t = u;
1N/A }
1N/A }
1N/A if (b < e)
1N/A *b++ = 0;
1N/A }
1N/A if (b >= e)
1N/A {
1N/A if (!(cmd->flags & CMD_SILENT))
1N/A error(2, "%s: command too large after insert", a);
1N/A return -1;
1N/A }
1N/A }
1N/A cmd->nextarg = cmd->firstarg;
1N/A cmd->nextstr = cmd->laststr;
1N/A if (cmd->flags & (CMD_QUERY|CMD_TRACE))
1N/A {
1N/A p = cmd->argv;
1N/A sfprintf(sfstderr, "+ %s", *p);
1N/A while (s = *++p)
1N/A sfprintf(sfstderr, " %s", s);
1N/A if (!(cmd->flags & CMD_QUERY))
1N/A sfprintf(sfstderr, "\n");
1N/A else if (astquery(1, "? "))
1N/A return 0;
1N/A }
1N/A if (cmd->echo)
1N/A {
1N/A n = (cmd->flags & CMD_NEWLINE) ? '\n' : ' ';
1N/A for (p = cmd->argv + 1; s = *p++;)
1N/A sfputr(sfstdout, s, *p ? n : '\n');
1N/A n = 0;
1N/A }
1N/A else if ((n = procrun(*cmd->argv, cmd->argv, PROC_ARGMOD|PROC_IGNOREPATH)) == -1)
1N/A {
1N/A if (!(cmd->flags & CMD_SILENT))
1N/A {
1N/A error(ERROR_SYSTEM|2, "%s: command exec error", *cmd->argv);
1N/A exit(EXIT_NOTFOUND - 1);
1N/A }
1N/A return -1;
1N/A }
1N/A else if (n >= EXIT_NOTFOUND - 1)
1N/A {
1N/A if (!(cmd->flags & CMD_SILENT))
1N/A exit(n);
1N/A }
1N/A else if (!(cmd->flags & CMD_IGNORE))
1N/A {
1N/A if (n == EXIT_QUIT && !(cmd->flags & CMD_SILENT))
1N/A exit(2);
1N/A if (n)
1N/A error_info.errors++;
1N/A }
1N/A return n;
1N/A}
1N/A
1N/A/*
1N/A * add file to the command arg list
1N/A */
1N/A
1N/Aint
1N/Acmdarg(register Cmdarg_t* cmd, const char* file, register int len)
1N/A{
1N/A int i;
1N/A int r;
1N/A
1N/A r = 0;
1N/A if (len)
1N/A {
1N/A while ((cmd->nextstr -= len + 1) < (char*)(cmd->nextarg + cmd->offset))
1N/A {
1N/A if (cmd->nextarg == cmd->firstarg)
1N/A {
1N/A error(2, "%s: path too long for exec args", file);
1N/A return -1;
1N/A }
1N/A if (i = cmdflush(cmd))
1N/A {
1N/A if (r < i)
1N/A r = i;
1N/A if (!(cmd->flags & CMD_IGNORE))
1N/A return r;
1N/A }
1N/A }
1N/A *cmd->nextarg++ = cmd->nextstr;
1N/A memcpy(cmd->nextstr, file, len);
1N/A cmd->nextstr[len] = 0;
1N/A cmd->argcount++;
1N/A if (cmd->argcount >= cmd->argmax && (i = cmdflush(cmd)) > r)
1N/A r = i;
1N/A }
1N/A return r;
1N/A}
1N/A
1N/A/*
1N/A * close a cmdarg stream
1N/A */
1N/A
1N/Aint
1N/Acmdclose(Cmdarg_t* cmd)
1N/A{
1N/A int n;
1N/A
1N/A if ((cmd->flags & CMD_EXACT) && cmd->argcount < cmd->argmax)
1N/A {
1N/A if (!(cmd->flags & CMD_SILENT))
1N/A error(2, "only %d arguments for last command", cmd->argcount);
1N/A return -1;
1N/A }
1N/A cmd->flags &= ~CMD_MINIMUM;
1N/A n = cmdflush(cmd);
1N/A free(cmd);
1N/A return n;
1N/A}