1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1985-2009 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 ARG_MAX 38 #define ARG_MAX (64*1024) 39 #endif 40 #ifndef EXIT_QUIT 41 #define EXIT_QUIT 255 42 #endif 43 44 static const char* echo[] = { "echo", 0 }; 45 46 /* 47 * open a cmdarg stream 48 * initialize the command for execution 49 * argv[-1] is reserved for procrun(PROC_ARGMOD) 50 */ 51 52 Cmdarg_t* 53 cmdopen(char** argv, int argmax, int size, const char* argpat, int flags) 54 { 55 register Cmdarg_t* cmd; 56 register int n; 57 register char** p; 58 register char* s; 59 char* sh; 60 int c; 61 int m; 62 int argc; 63 long x; 64 65 char** post = 0; 66 67 n = sizeof(char**); 68 if (*argv) 69 { 70 for (p = argv + 1; *p; p++) 71 { 72 if ((flags & CMD_POST) && argpat && streq(*p, argpat)) 73 { 74 *p = 0; 75 post = p + 1; 76 argpat = 0; 77 } 78 else 79 n += strlen(*p) + 1; 80 } 81 argc = p - argv; 82 } 83 else 84 argc = 0; 85 for (p = environ; *p; p++) 86 n += sizeof(char**) + strlen(*p) + 1; 87 if ((x = strtol(astconf("ARG_MAX", NiL, NiL), NiL, 0)) <= 0) 88 x = ARG_MAX; 89 if (size <= 0 || size > x) 90 size = x; 91 sh = pathshell(); 92 m = n + (argc + 4) * sizeof(char**) + strlen(sh) + 1; 93 m = roundof(m, sizeof(char**)); 94 if (size < m) 95 { 96 error(2, "size must be at least %d", m); 97 return 0; 98 } 99 if ((m = x / 10) > 2048) 100 m = 2048; 101 if (size > (x - m)) 102 size = x - m; 103 n = size - n; 104 m = ((flags & CMD_INSERT) && argpat) ? (strlen(argpat) + 1) : 0; 105 if (!(cmd = newof(0, Cmdarg_t, 1, n + m))) 106 { 107 error(ERROR_SYSTEM|2, "out of space"); 108 return 0; 109 } 110 c = n / sizeof(char**); 111 if (argmax <= 0 || argmax > c) 112 argmax = c; 113 s = cmd->buf; 114 if (!argv[0]) 115 { 116 argv = (char**)echo; 117 cmd->echo = 1; 118 } 119 else if (streq(argv[0], echo[0])) 120 { 121 cmd->echo = 1; 122 flags &= ~CMD_NEWLINE; 123 } 124 else if (!(flags & CMD_CHECKED)) 125 { 126 if (!pathpath(s, argv[0], NiL, PATH_REGULAR|PATH_EXECUTE)) 127 { 128 if (!(flags & CMD_SILENT)) 129 { 130 error(ERROR_SYSTEM|2, "%s: command not found", argv[0]); 131 exit(EXIT_NOTFOUND); 132 } 133 free(cmd); 134 return 0; 135 } 136 argv[0] = s; 137 } 138 s += strlen(s) + 1; 139 if (m) 140 { 141 cmd->insert = strcpy(s, argpat); 142 cmd->insertlen = m - 1; 143 s += m; 144 } 145 s += sizeof(char**) - (s - cmd->buf) % sizeof(char**); 146 p = (char**)s; 147 n -= strlen(*p++ = sh) + 1; 148 cmd->argv = p; 149 while (*p = *argv++) 150 p++; 151 if (m) 152 { 153 argmax = 1; 154 *p++ = 0; 155 cmd->insertarg = p; 156 argv = cmd->argv; 157 c = *cmd->insert; 158 while (s = *argv) 159 { 160 while ((s = strchr(s, c)) && strncmp(cmd->insert, s, cmd->insertlen)) 161 s++; 162 *p++ = s ? *argv : (char*)0; 163 argv++; 164 } 165 *p++ = 0; 166 } 167 cmd->firstarg = cmd->nextarg = p; 168 cmd->laststr = cmd->nextstr = cmd->buf + n; 169 cmd->argmax = argmax; 170 cmd->flags = flags; 171 cmd->offset = ((cmd->postarg = post) ? (argc - (post - argv)) : 0) + 3; 172 return cmd; 173 } 174 175 /* 176 * flush outstanding command file args 177 */ 178 179 int 180 cmdflush(register Cmdarg_t* cmd) 181 { 182 register char* s; 183 register char** p; 184 register int n; 185 186 if (cmd->flags & CMD_EMPTY) 187 cmd->flags &= ~CMD_EMPTY; 188 else if (cmd->nextarg <= cmd->firstarg) 189 return 0; 190 if ((cmd->flags & CMD_MINIMUM) && cmd->argcount < cmd->argmax) 191 { 192 if (!(cmd->flags & CMD_SILENT)) 193 error(2, "%d arg command would be too long", cmd->argcount); 194 return -1; 195 } 196 cmd->total.args += cmd->argcount; 197 cmd->total.commands++; 198 cmd->argcount = 0; 199 if (p = cmd->postarg) 200 while (*cmd->nextarg++ = *p++); 201 else 202 *cmd->nextarg = 0; 203 if (s = cmd->insert) 204 { 205 char* a; 206 char* b; 207 char* e; 208 char* t; 209 char* u; 210 int c; 211 int m; 212 213 a = cmd->firstarg[0]; 214 b = (char*)&cmd->nextarg[1]; 215 e = cmd->nextstr; 216 c = *s; 217 m = cmd->insertlen; 218 for (n = 1; cmd->argv[n]; n++) 219 if (t = cmd->insertarg[n]) 220 { 221 cmd->argv[n] = b; 222 for (;;) 223 { 224 if (!(u = strchr(t, c))) 225 { 226 b += sfsprintf(b, e - b, "%s", t); 227 break; 228 } 229 if (!strncmp(s, u, m)) 230 { 231 b += sfsprintf(b, e - b, "%-.*s%s", u - t, t, a); 232 t = u + m; 233 } 234 else if (b >= e) 235 break; 236 else 237 { 238 *b++ = *u++; 239 t = u; 240 } 241 } 242 if (b < e) 243 *b++ = 0; 244 } 245 if (b >= e) 246 { 247 if (!(cmd->flags & CMD_SILENT)) 248 error(2, "%s: command too large after insert", a); 249 return -1; 250 } 251 } 252 cmd->nextarg = cmd->firstarg; 253 cmd->nextstr = cmd->laststr; 254 if (cmd->flags & (CMD_QUERY|CMD_TRACE)) 255 { 256 p = cmd->argv; 257 sfprintf(sfstderr, "+ %s", *p); 258 while (s = *++p) 259 sfprintf(sfstderr, " %s", s); 260 if (!(cmd->flags & CMD_QUERY)) 261 sfprintf(sfstderr, "\n"); 262 else if (astquery(1, "? ")) 263 return 0; 264 } 265 if (cmd->echo) 266 { 267 n = (cmd->flags & CMD_NEWLINE) ? '\n' : ' '; 268 for (p = cmd->argv + 1; s = *p++;) 269 sfputr(sfstdout, s, *p ? n : '\n'); 270 n = 0; 271 } 272 else if ((n = procrun(*cmd->argv, cmd->argv, PROC_ARGMOD|PROC_IGNOREPATH)) == -1) 273 { 274 if (!(cmd->flags & CMD_SILENT)) 275 { 276 error(ERROR_SYSTEM|2, "%s: command exec error", *cmd->argv); 277 exit(EXIT_NOTFOUND - 1); 278 } 279 return -1; 280 } 281 else if (n >= EXIT_NOTFOUND - 1) 282 { 283 if (!(cmd->flags & CMD_SILENT)) 284 exit(n); 285 } 286 else if (!(cmd->flags & CMD_IGNORE)) 287 { 288 if (n == EXIT_QUIT && !(cmd->flags & CMD_SILENT)) 289 exit(2); 290 if (n) 291 error_info.errors++; 292 } 293 return n; 294 } 295 296 /* 297 * add file to the command arg list 298 */ 299 300 int 301 cmdarg(register Cmdarg_t* cmd, const char* file, register int len) 302 { 303 int i; 304 int r; 305 306 r = 0; 307 if (len) 308 { 309 while ((cmd->nextstr -= len + 1) < (char*)(cmd->nextarg + cmd->offset)) 310 { 311 if (cmd->nextarg == cmd->firstarg) 312 { 313 error(2, "%s: path too long for exec args", file); 314 return -1; 315 } 316 if (i = cmdflush(cmd)) 317 { 318 if (r < i) 319 r = i; 320 if (!(cmd->flags & CMD_IGNORE)) 321 return r; 322 } 323 } 324 *cmd->nextarg++ = cmd->nextstr; 325 memcpy(cmd->nextstr, file, len); 326 cmd->nextstr[len] = 0; 327 cmd->argcount++; 328 if (cmd->argcount >= cmd->argmax && (i = cmdflush(cmd)) > r) 329 r = i; 330 } 331 return r; 332 } 333 334 /* 335 * close a cmdarg stream 336 */ 337 338 int 339 cmdclose(Cmdarg_t* cmd) 340 { 341 int n; 342 343 if ((cmd->flags & CMD_EXACT) && cmd->argcount < cmd->argmax) 344 { 345 if (!(cmd->flags & CMD_SILENT)) 346 error(2, "only %d arguments for last command", cmd->argcount); 347 return -1; 348 } 349 cmd->flags &= ~CMD_MINIMUM; 350 n = cmdflush(cmd); 351 free(cmd); 352 return n; 353 } 354