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 36static const char lib[] = "libast:cmdarg"; 37 38static int 39cmdrun(int argc, char** argv, Cmddisc_t* disc) 40{ 41 return procrun(argv[0], argv, PROC_ARGMOD|PROC_IGNOREPATH); 42} 43 44Cmdarg_t* 45cmdopen(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 74static const char* echo[] = { "echo", 0 }; 75 76Cmdarg_t* 77cmdopen_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 94Cmdarg_t* 95cmdopen_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 230int 231cmdflush(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 353int 354cmdarg(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 394int 395cmdclose(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