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