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