builtins.c (14089B)
1 /* builtins.c: the collection of rc's builtin commands */ 2 3 /* 4 NOTE: rc's builtins do not call "rc_error" because they are 5 commands, and rc errors usually arise from syntax errors. e.g., 6 you probably don't want interpretation of a shell script to stop 7 because of a bad umask. 8 */ 9 10 #include "rc.h" 11 12 #include <sys/ioctl.h> 13 #include <sys/stat.h> 14 #include <setjmp.h> 15 #include <errno.h> 16 17 #include "addon.h" 18 #include "input.h" 19 #include "jbwrap.h" 20 #include "rlimit.h" 21 #include "sigmsgs.h" 22 23 static void b_break(char **), b_cd(char **), b_continue(char **), b_eval(char **), b_flag(char **), 24 b_exit(char **), b_newpgrp(char **), b_return(char **), b_shift(char **), b_umask(char **), 25 b_wait(char **), b_whatis(char **); 26 27 #if HAVE_SETRLIMIT 28 static void b_limit(char **); 29 #endif 30 31 #if RC_ECHO 32 static void b_echo(char **); 33 #endif 34 35 static struct { 36 builtin_t *p; 37 char *name; 38 } builtins[] = { 39 { b_break, "break" }, 40 { b_builtin, "builtin" }, 41 { b_cd, "cd" }, 42 { b_continue, "continue" }, 43 #if RC_ECHO 44 { b_echo, "echo" }, 45 #endif 46 { b_eval, "eval" }, 47 { b_exec, "exec" }, 48 { b_exit, "exit" }, 49 { b_flag, "flag" }, 50 #if HAVE_SETRLIMIT 51 { b_limit, "limit" }, 52 #endif 53 { b_newpgrp, "newpgrp" }, 54 { b_return, "return" }, 55 { b_shift, "shift" }, 56 { b_umask, "umask" }, 57 { b_wait, "wait" }, 58 { b_whatis, "whatis" }, 59 { b_dot, "." }, 60 #ifdef ADDONS 61 ADDONS 62 #endif 63 }; 64 65 extern builtin_t *isbuiltin(char *s) { 66 int i; 67 for (i = 0; i < arraysize(builtins); i++) 68 if (streq(builtins[i].name, s)) 69 return builtins[i].p; 70 return NULL; 71 } 72 73 /* funcall() is the wrapper used to invoke shell functions. pushes $*, and "return" returns here. */ 74 75 extern void funcall(char **av) { 76 Jbwrap j; 77 Estack e1, e2; 78 Edata jreturn, star; 79 if (sigsetjmp(j.j, 1)) 80 return; 81 starassign(*av, av+1, TRUE); 82 jreturn.jb = &j; 83 star.name = "*"; 84 except(eReturn, jreturn, &e1); 85 except(eVarstack, star, &e2); 86 walk(treecpy(fnlookup(*av), nalloc), TRUE); 87 varrm("*", TRUE); 88 unexcept(eVarstack); 89 unexcept(eReturn); 90 } 91 92 static void arg_count(char *name) { 93 fprint(2, RC "too many arguments to %s\n", name); 94 set(FALSE); 95 } 96 97 static void badnum(char *num) { 98 fprint(2, RC "`%s' is a bad number\n", num); 99 set(FALSE); 100 } 101 102 /* a dummy command. (exec() performs "exec" simply by not forking) */ 103 104 extern void b_exec(char **ignore) { 105 } 106 107 #if RC_ECHO 108 /* echo -n omits a newline. echo -- -n echos '-n' */ 109 110 static void b_echo(char **av) { 111 char *format = "%A\n"; 112 if (*++av != NULL) { 113 if (streq(*av, "-n")) 114 format = "%A", av++; 115 else if (streq(*av, "--")) 116 av++; 117 } 118 fprint(1, format, av); 119 set(TRUE); 120 } 121 #endif 122 123 /* cd. traverse $cdpath if the directory given is not an absolute pathname */ 124 125 static void b_cd(char **av) { 126 List *s, nil; 127 char *path = NULL; 128 size_t t, pathlen = 0; 129 if (*++av == NULL) { 130 s = varlookup("home"); 131 *av = (s == NULL) ? "/" : s->w; 132 } else if (av[1] != NULL) { 133 arg_count("cd"); 134 return; 135 } 136 if (isabsolute(*av) || streq(*av, ".") || streq(*av, "..")) { /* absolute pathname? */ 137 if (chdir(*av) < 0) { 138 set(FALSE); 139 uerror(*av); 140 } else 141 set(TRUE); 142 } else { 143 s = varlookup("cdpath"); 144 if (s == NULL) { 145 s = &nil; 146 nil.w = ""; 147 nil.n = NULL; 148 } 149 do { 150 if (s != &nil && *s->w != '\0') { 151 t = strlen(*av) + strlen(s->w) + 2; 152 if (t > pathlen) 153 path = nalloc(pathlen = t); 154 strcpy(path, s->w); 155 if (!streq(s->w, "/")) /* "//" is special to POSIX */ 156 strcat(path, "/"); 157 strcat(path, *av); 158 } else { 159 pathlen = 0; 160 path = *av; 161 } 162 if (chdir(path) >= 0) { 163 set(TRUE); 164 if (interactive && *s->w != '\0' && !streq(s->w, ".")) 165 fprint(1, "%s\n", path); 166 return; 167 } 168 s = s->n; 169 } while (s != NULL); 170 fprint(2, "couldn't cd to %s\n", *av); 171 set(FALSE); 172 } 173 } 174 175 static void b_umask(char **av) { 176 int i; 177 if (*++av == NULL) { 178 set(TRUE); 179 i = umask(0); 180 umask(i); 181 fprint(1, "0%o\n", i); 182 } else if (av[1] == NULL) { 183 i = o2u(*av); 184 if ((unsigned int) i > 0777) { 185 fprint(2, "bad umask\n"); 186 set(FALSE); 187 } else { 188 umask(i); 189 set(TRUE); 190 } 191 } else { 192 arg_count("umask"); 193 return; 194 } 195 } 196 197 static void b_exit(char **av) { 198 if (*++av != NULL) 199 ssetstatus(av); 200 rc_exit(getstatus()); 201 } 202 203 static void b_flag(char **av) { 204 bool *flagp = NULL; 205 char f; 206 int mode = 3; /* 0 = reset (-), 1 = set (+), 2 = test */ 207 const char *usage = "usage: flag f [ + | - ]\n"; 208 209 if (*++av == NULL) { 210 fprint(2, RC "not enough arguments to flag\n"); 211 set(FALSE); 212 return; 213 } 214 f = av[0][0]; 215 if (f == '\0' || av[0][1] != '\0') goto flag_usage; 216 if (*++av == NULL) { 217 mode = 2; 218 } else if (av[0][0] == '+' && av[0][1] == '\0') { 219 mode = 1; 220 } else if (av[0][0] == '-' && av[0][1] == '\0') { 221 mode = 0; 222 } 223 if (mode == 3) goto flag_usage; 224 switch (f) { 225 case 'c': 226 if (mode != 2) goto flag_immutable; 227 set(dashsee[0] != NULL); 228 return; 229 case 'd': 230 if (mode != 2) goto flag_immutable; 231 flagp = &dashdee; break; 232 case 'e': flagp = &dashee; break; 233 case 'i': flagp = &interactive; break; 234 case 'l': 235 if (mode != 2) goto flag_immutable; 236 flagp = &dashell; break; 237 case 'n': flagp = &dashen; break; 238 case 'o': 239 if (mode != 2) goto flag_immutable; 240 flagp = &dashoh; break; 241 case 'p': 242 if (mode != 2) goto flag_immutable; 243 flagp = &dashpee; break; 244 case 's': 245 if (mode != 2) goto flag_immutable; 246 flagp = &dashess; break; 247 case 'v': flagp = &dashvee; break; 248 case 'x': flagp = &dashex; break; 249 } 250 if (flagp != NULL) { 251 if (mode == 2) 252 set(*flagp); 253 else { 254 *flagp = mode; 255 set(TRUE); 256 } 257 } else { 258 fprint(2, RC "unknown flag"); 259 set(FALSE); 260 } 261 return; 262 flag_immutable: 263 fprint(2, RC "flag immutable\n"); 264 set(FALSE); 265 return; 266 flag_usage: 267 fprint(2, usage); 268 set(FALSE); 269 } 270 271 /* raise a "return" exception, i.e., return from a function. if an integer argument is present, set $status to it */ 272 273 static void b_return(char **av) { 274 if (*++av != NULL) 275 ssetstatus(av); 276 rc_raise(eReturn); 277 } 278 279 /* raise a "break" exception for breaking out of for and while loops */ 280 281 static void b_break(char **av) { 282 if (av[1] != NULL) { 283 arg_count("break"); 284 return; 285 } 286 rc_raise(eBreak); 287 } 288 289 /* raise a "continue" exception to finish early an iteration of 'for' and 'while' loops */ 290 291 static void b_continue(char **av) { 292 if (av[1] != NULL) { 293 arg_count("continue"); 294 return; 295 } 296 rc_raise(eContinue); 297 } 298 299 /* shift $* n places (default 1) */ 300 301 static void b_shift(char **av) { 302 int shift = (av[1] == NULL ? 1 : a2u(av[1])); 303 List *s, *dollarzero; 304 if (av[1] != NULL && av[2] != NULL) { 305 arg_count("shift"); 306 return; 307 } 308 if (shift < 0) { 309 badnum(av[1]); 310 return; 311 } 312 s = varlookup("*")->n; 313 dollarzero = varlookup("0"); 314 while (s != NULL && shift != 0) { 315 s = s->n; 316 --shift; 317 } 318 if (s == NULL && shift != 0) { 319 fprint(2, "rc: cannot shift\n"); 320 set(FALSE); 321 } else { 322 varassign("*", append(dollarzero, s), FALSE); 323 set(TRUE); 324 } 325 } 326 327 /* dud function */ 328 329 extern void b_builtin(char **ignore) { 330 } 331 332 /* wait for a given process, or all outstanding processes */ 333 334 static void b_wait(char **av) { 335 int status; 336 pid_t pid; 337 if (av[1] == NULL) { 338 waitforall(); 339 return; 340 } 341 if (av[2] != NULL) { 342 arg_count("wait"); 343 return; 344 } 345 if ((pid = a2u(av[1])) < 0) { 346 badnum(av[1]); 347 return; 348 } 349 if (rc_wait4(pid, &status, FALSE) > 0) 350 setstatus(pid, status); 351 else 352 set(FALSE); 353 sigchk(); 354 } 355 356 /* 357 whatis without arguments prints all variables and functions. Otherwise, check to see if a name 358 is defined as a variable, function or pathname. 359 */ 360 361 #define not(b) ((b)^TRUE) 362 #define show(b) (not(eff|vee|pee|bee|ess)|(b)) 363 364 static bool issig(char *s) { 365 int i; 366 for (i = 0; i < NUMOFSIGNALS; i++) 367 if (streq(s, signals[i].name)) 368 return TRUE; 369 return FALSE; 370 } 371 372 static void b_whatis(char **av) { 373 bool ess, eff, vee, pee, bee; 374 bool f, found; 375 int i, ac, c; 376 List *s; 377 Node *n; 378 char *e; 379 for (rc_optind = ac = 0; av[ac] != NULL; ac++) 380 ; /* count the arguments for getopt */ 381 ess = eff = vee = pee = bee = FALSE; 382 while ((c = rc_getopt(ac, av, "sfvpb")) != -1) 383 switch (c) { 384 default: set(FALSE); return; 385 case 's': ess = TRUE; break; 386 case 'f': eff = TRUE; break; 387 case 'v': vee = TRUE; break; 388 case 'p': pee = TRUE; break; 389 case 'b': bee = TRUE; break; 390 } 391 av += rc_optind; 392 if (*av == NULL) { 393 if (vee|eff) 394 whatare_all_vars(eff, vee); 395 if (ess) 396 whatare_all_signals(); 397 if (bee) 398 for (i = 0; i < arraysize(builtins); i++) 399 fprint(1, "builtin %s\n", builtins[i].name); 400 if (pee) 401 fprint(2, "whatis -p: must specify argument\n"); 402 if (show(FALSE)) /* no options? */ 403 whatare_all_vars(TRUE, TRUE); 404 set(TRUE); 405 return; 406 } 407 found = TRUE; 408 for (i = 0; av[i] != NULL; i++) { 409 f = FALSE; 410 errno = ENOENT; 411 if (show(vee) && (s = varlookup(av[i])) != NULL) { 412 f = TRUE; 413 prettyprint_var(1, av[i], s); 414 } 415 if (((show(ess)&&issig(av[i])) || show(eff)) && (n = fnlookup(av[i])) != NULL) { 416 f = TRUE; 417 prettyprint_fn(1, av[i], n); 418 } else if (show(bee) && isbuiltin(av[i]) != NULL) { 419 f = TRUE; 420 fprint(1, "builtin %s\n", av[i]); 421 } else if (show(pee) && (e = which(av[i], FALSE)) != NULL) { 422 f = TRUE; 423 fprint(1, "%S\n", e); 424 } 425 if (!f) { 426 found = FALSE; 427 if (errno != ENOENT) 428 uerror(av[i]); 429 else 430 fprint(2, "%s not found\n", av[i]); 431 } 432 } 433 set(found); 434 } 435 436 /* push a string to be eval'ed onto the input stack. evaluate it */ 437 438 static void b_eval(char **av) { 439 bool i = interactive; 440 if (av[1] == NULL) 441 return; 442 interactive = FALSE; 443 pushstring(av + 1, i); /* don't reset line numbers on noninteractive eval */ 444 doit(TRUE); 445 interactive = i; 446 } 447 448 /* 449 push a file to be interpreted onto the input stack. with "-i" treat this as an interactive 450 input source. 451 */ 452 453 extern void b_dot(char **av) { 454 int fd; 455 bool old_i = interactive, i = FALSE; 456 Estack e; 457 Edata star; 458 av++; 459 if (*av == NULL) 460 return; 461 if (streq(*av, "-i")) { 462 av++; 463 i = TRUE; 464 } 465 if (dasheye) { /* rc -i file has to do the right thing. reset the dasheye state to FALSE, though. */ 466 dasheye = FALSE; 467 i = TRUE; 468 } 469 if (*av == NULL) 470 return; 471 fd = rc_open(*av, rFrom); 472 if (fd < 0) { 473 uerror(*av); 474 set(FALSE); 475 return; 476 } 477 starassign(*av, av+1, TRUE); 478 interactive = i; 479 pushfd(fd); 480 star.name = "*"; 481 except(eVarstack, star, &e); 482 doit(TRUE); 483 varrm("*", TRUE); 484 unexcept(eVarstack); 485 interactive = old_i; 486 } 487 488 /* put rc into a new pgrp. Used on the NeXT where the Terminal program is broken (sigh) */ 489 490 static void b_newpgrp(char **av) { 491 if (av[1] != NULL) { 492 arg_count("newpgrp"); 493 return; 494 } 495 setpgid(rc_pid, rc_pid); /* XXX check return value */ 496 tcsetpgrp(2, rc_pid); /* XXX check return value */ 497 } 498 499 /* Berkeley limit support was cleaned up by Paul Haahr. */ 500 501 #if HAVE_SETRLIMIT 502 static const struct Suffix 503 kbsuf = { NULL, 1024, "k" }, 504 mbsuf = { &kbsuf, 1024*1024, "m" }, 505 gbsuf = { &mbsuf, 1024*1024*1024, "g" }, 506 stsuf = { NULL, 1, "s" }, 507 mtsuf = { &stsuf, 60, "m" }, 508 htsuf = { &mtsuf, 60*60, "h" }; 509 #define SIZESUF &gbsuf 510 #define TIMESUF &htsuf 511 #define NOSUF ((struct Suffix *) NULL) /* for RLIMIT_NOFILE on SunOS 4.1 */ 512 513 static const struct Limit limits[] = { 514 { "cputime", RLIMIT_CPU, TIMESUF }, 515 { "filesize", RLIMIT_FSIZE, SIZESUF }, 516 { "datasize", RLIMIT_DATA, SIZESUF }, 517 { "stacksize", RLIMIT_STACK, SIZESUF }, 518 { "coredumpsize", RLIMIT_CORE, SIZESUF }, 519 #ifdef RLIMIT_NOFILE /* SUSv2, but not universal */ 520 { "descriptors", RLIMIT_NOFILE, NOSUF }, 521 #endif 522 #ifdef RLIMIT_AS /* SUSv2, but not universal */ 523 { "memoryuse", RLIMIT_AS, SIZESUF }, 524 #endif 525 #if defined(RLIMIT_VMEM) && !defined(RLIMIT_AS) /* old name for AS */ 526 { "memoryuse", RLIMIT_VMEM, SIZESUF }, 527 #endif 528 #ifdef RLIMIT_RSS 529 { "memoryrss", RLIMIT_RSS, SIZESUF }, 530 #endif 531 #ifdef RLIMIT_NPROC 532 { "maxproc", RLIMIT_NPROC, NOSUF }, 533 #endif 534 #ifdef RLIMIT_MEMLOCK 535 { "memorylocked", RLIMIT_MEMLOCK, SIZESUF }, 536 #endif 537 #ifdef RLIMIT_LOCKS 538 { "filelocks", RLIMIT_LOCKS, NOSUF }, 539 #endif 540 { NULL, 0, NULL } 541 }; 542 543 static void printlimit(const struct Limit *limit, bool hard) { 544 struct rlimit rlim; 545 rlim_t lim; 546 getrlimit(limit->flag, &rlim); 547 if (hard) 548 lim = rlim.rlim_max; 549 else 550 lim = rlim.rlim_cur; 551 if (lim == RLIM_INFINITY) 552 fprint(1, "%s \tunlimited\n", limit->name); 553 else { 554 const struct Suffix *suf; 555 for (suf = limit->suffix; suf != NULL; suf = suf->next) 556 if (lim % suf->amount == 0 && (lim != 0 || suf->amount > 1)) { 557 lim /= suf->amount; 558 break; 559 } 560 fprint(1, RLIM_FMT, limit->name, (RLIM_CONV)lim, (suf == NULL || lim == 0) ? "" : suf->name); 561 } 562 } 563 564 static bool parselimit(const struct Limit *resource, rlim_t *limit, char *s) { 565 char *t; 566 int len = strlen(s); 567 const struct Suffix *suf = resource->suffix; 568 569 *limit = 1; 570 if (streq(s, "unlimited")) { 571 *limit = RLIM_INFINITY; 572 return TRUE; 573 } 574 if (suf == TIMESUF && (t = strchr(s, ':')) != NULL) { 575 int min, sec; 576 *t++ = '\0'; 577 min = a2u(s); sec = a2u(t); 578 if (min == -1 || sec == -1) return FALSE; 579 *limit = 60 * min + sec; 580 } else { 581 int n; 582 for (; suf != NULL; suf = suf->next) 583 if (streq(suf->name, s + len - strlen(suf->name))) { 584 s[len - strlen(suf->name)] = '\0'; 585 *limit *= suf->amount; 586 break; 587 } 588 n = a2u(s); 589 if (n == -1) return FALSE; 590 *limit *= n; 591 } 592 return TRUE; 593 } 594 595 static void b_limit(char **av) { 596 const struct Limit *lp = limits; 597 bool hard = FALSE; 598 if (*++av != NULL && streq(*av, "-h")) { 599 av++; 600 hard = TRUE; 601 } 602 if (*av == NULL) { 603 for (; lp->name != NULL; lp++) 604 printlimit(lp, hard); 605 return; 606 } 607 for (;; lp++) { 608 if (lp->name == NULL) { 609 fprint(2, "no such limit\n"); 610 set(FALSE); 611 return; 612 } 613 if (streq(*av, lp->name)) 614 break; 615 } 616 if (*++av == NULL) 617 printlimit(lp, hard); 618 else { 619 struct rlimit rlim; 620 rlim_t pl; 621 getrlimit(lp->flag, &rlim); 622 if (!parselimit(lp, &pl, *av)) { 623 fprint(2, "bad limit\n"); 624 set(FALSE); 625 return; 626 } 627 if (hard) 628 rlim.rlim_max = pl; 629 else 630 rlim.rlim_cur = pl; 631 if (setrlimit(lp->flag, &rlim) == -1) { 632 uerror("setrlimit"); 633 set(FALSE); 634 } else 635 set(TRUE); 636 } 637 } 638 #endif 639 640 extern char *compl_builtin(const char *text, int state) { 641 return compl_name(text, state, &builtins[0].name, arraysize(builtins), &builtins[1].name - &builtins[0].name); 642 }