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