home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume23 / rc / part01 / builtins.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-10-18  |  10.9 KB  |  576 lines

  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 "jbwrap.h"
  11. #include <errno.h>
  12. #include "rc.h"
  13. #ifndef NOLIMITS
  14. #include <sys/time.h>
  15. #include <sys/resource.h>
  16. #endif
  17. #include "utils.h"
  18. #include "walk.h"
  19. #include "input.h"
  20. #include "builtins.h"
  21. #include "hash.h"
  22. #include "nalloc.h"
  23. #include "status.h"
  24. #include "footobar.h"
  25. #include "lex.h"
  26. #include "open.h"
  27. #include "except.h"
  28. #include "redir.h"
  29. #include "glom.h"
  30. #include "tree.h"
  31. #include "sigmsgs.h"
  32. #include "getopt.h"
  33. #include "wait.h"
  34. #include "addon.h"
  35.  
  36. extern int umask(int);
  37.  
  38. static void b_break(char **), b_cd(char **),
  39.     b_echo(char **), b_eval(char **), b_exit(char **), b_limit(char **),
  40.     b_return(char **), b_shift(char **), b_umask(char **), b_wait(char **),
  41.     b_whatis(char **);
  42.  
  43. static builtin_t *const builtins[] = {
  44.     b_break, b_builtin, b_cd, b_echo, b_eval, b_exec, b_exit,
  45.     b_limit, b_return, b_shift, b_umask, b_wait, b_whatis, b_dot,
  46.     ADDON_FUN
  47. };
  48.  
  49. static char *const builtins_str[] = {
  50.     "break", "builtin", "cd", "echo", "eval", "exec", "exit",
  51.     "limit", "return", "shift", "umask", "wait", "whatis", ".",
  52.     ADDON_STR
  53. };
  54.  
  55. builtin_t *isbuiltin(char *s) {
  56.     int i;
  57.  
  58.     for (i = 0; i < arraysize(builtins_str); i++)
  59.         if (streq(builtins_str[i], s))
  60.             return builtins[i];
  61.     return NULL;
  62. }
  63.  
  64. /* funcall() is the wrapper used to invoke shell functions. pushes $*, and "return" returns here. */
  65.  
  66. void funcall(char **av) {
  67.     jbwrap j;
  68.     Estack e1, e2;
  69.  
  70.     if (setjmp(j.j))
  71.         return;
  72.  
  73.     starassign(*av, av+1, TRUE);
  74.     except(RETURN, &j, &e1);
  75.     except(VARSTACK, "*", &e2);
  76.     walk(treecpy(fnlookup(*av),nalloc), TRUE);
  77.     varrm("*", TRUE);
  78.     unexcept(); /* VARSTACK */
  79.     unexcept(); /* RETURN */
  80. }
  81.  
  82. static void arg_count(char *name) {
  83.     fprint(2, "too many arguments to %s\n", name);
  84.     set(FALSE);
  85. }
  86.  
  87. static void badnum(char *num) {
  88.     fprint(2, "%s is a bad number", num);
  89.     set(FALSE);
  90. }
  91.  
  92. /* a dummy command. (exec() performs "exec" simply by not forking) */
  93.  
  94. void b_exec(char **av) {
  95. }
  96.  
  97. /* echo -n omits a newline. echo -- -n echos '-n' */
  98.  
  99. static void b_echo(char **av) {
  100.     SIZE_T i;
  101.     char *format = "%a\n";
  102.  
  103.     if (*++av != NULL) {
  104.         if (streq(*av, "-n")) {
  105.             format = "%a";
  106.             av++;
  107.         } else if (streq(*av, "--")) {
  108.             av++;
  109.         }
  110.     }
  111.  
  112.     i = strarraylen(av) + 1; /* one for the null terminator */
  113.  
  114.     if (i < FPRINT_SIZE)
  115.         fprint(1, format, av);
  116.     else
  117.         writeall(1, sprint(nalloc(i), format, av), i-1);
  118.     set(TRUE);
  119. }
  120.  
  121. /* cd. traverse $cdpath if the directory given is not an absolute pathname */
  122.  
  123. static void b_cd(char **av) {
  124.     List *s, nil;
  125.     char *path = NULL;
  126.     SIZE_T t, pathlen = 0;
  127.  
  128.     if (*++av == NULL) {
  129.         s = varlookup("home");
  130.         *av = (s == NULL) ? "/" : s->w;
  131.     } else if (av[1] != NULL) {
  132.         arg_count("cd");
  133.         return;
  134.     }
  135.  
  136.     if (isabsolute(*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.                 strcat(path, "/");
  156.                 strcat(path, *av);
  157.             } else {
  158.                 pathlen = 0;
  159.                 path = *av;
  160.             }
  161.             if (chdir(path) >= 0) {
  162.                 set(TRUE);
  163.                 if (interactive && *s->w != '\0' && !streq(s->w,"."))
  164.                     fprint(1,"%s\n",path);
  165.                 return;
  166.             }
  167.             s = s->n;
  168.         } while (s != NULL);
  169.         fprint(2,"couldn't cd to %s\n", *av);
  170.         set(FALSE);
  171.     }
  172. }
  173.  
  174. static void b_umask(char **av) {
  175.     int i;
  176.  
  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.     int s;
  199.  
  200.     if (av[1] == NULL)
  201.         rc_exit(getstatus());
  202.     if (av[2] != NULL) {
  203.         fprint(2, "exit: too many arguments\n");
  204.         rc_exit(1);
  205.     }
  206.     if ((s = a2u(av[1])) >= 0)
  207.         rc_exit(s);
  208.     badnum(av[1]);
  209.     rc_exit(1);
  210. }
  211.  
  212. /* raise a "return" exception, i.e., return from a function. if an integer argument is present, set $status to it */
  213.  
  214. static void b_return(char **av) {
  215.     if (av[1] != NULL)
  216.         ssetstatus(av + 1);
  217.     rc_raise(RETURN);
  218. }
  219.  
  220. /* raise a "break" exception for breaking out of for and while loops */
  221.  
  222. static void b_break(char **av) {
  223.     if (av[1] != NULL) {
  224.         arg_count("break");
  225.         return;
  226.     }
  227.     rc_raise(BREAK);
  228. }
  229.  
  230. /* shift $* n places (default 1) */
  231.  
  232. static void b_shift(char **av) {
  233.     int shift;
  234.     List *s, *dollarzero;
  235.  
  236.     shift = (av[1] == NULL ? 1 : a2u(av[1]));
  237.  
  238.     if (av[1] != NULL && av[2] != NULL) {
  239.         arg_count("shift");
  240.         return;
  241.     }
  242.  
  243.     if (shift < 0) {
  244.         badnum(av[1]);
  245.         return;
  246.     }
  247.  
  248.     s = varlookup("*")->n;
  249.     dollarzero = varlookup("0");
  250.  
  251.     while (s != NULL && shift != 0) {
  252.         s = s->n;
  253.         --shift;
  254.     }
  255.  
  256.     if (s == NULL && shift != 0) {
  257.         fprint(2,"cannot shift\n");
  258.         set(FALSE);
  259.     } else {
  260.         varassign("*", append(dollarzero, s), FALSE);
  261.         set(TRUE);
  262.     }
  263. }
  264.  
  265. /* dud function */
  266.  
  267. void b_builtin(char **av) {
  268. }
  269.  
  270. /* wait for a given process, or all outstanding processes */
  271.  
  272. static void b_wait(char **av) {
  273.     int stat, pid;
  274.  
  275.     if (av[1] == NULL) {
  276.         waitforall(&stat);
  277.         setstatus(stat);
  278.         return;
  279.     }
  280.  
  281.     if (av[2] != NULL) {
  282.         arg_count("wait");
  283.         return;
  284.     }
  285.  
  286.     if ((pid = a2u(av[1])) < 0) {
  287.         badnum(av[1]);
  288.         return;
  289.     }
  290.  
  291.     if (rc_wait4(pid, &stat) > 0)
  292.         setstatus(stat);
  293.     else
  294.         set(FALSE);
  295. }
  296.  
  297. /*
  298.    whatis without arguments prints all variables and functions. Otherwise, check to see if a name
  299.    is defined as a variable, function or pathname.
  300. */
  301.  
  302. static void b_whatis(char **av) {
  303.     enum bool f,found;
  304.     int i,j,ac,c;
  305.     List *s;
  306.     Node *n;
  307.     char *e;
  308.     boolean ess = FALSE;
  309.  
  310.     optind = 0;
  311.     for (ac = 0; av[ac] != NULL; ac++)
  312.         ; /* count the arguments for getopt */
  313.  
  314.     while ((c = getopt(ac, av, "s")) != -1)
  315.         switch (c) {
  316.         case 's':
  317.             ess = TRUE;
  318.             break;
  319.         case '?':
  320.             set(FALSE);
  321.             return;
  322.         }
  323.  
  324.     av += optind;
  325.  
  326.     if (*av == NULL && !ess) {
  327.         whatare_all_vars();
  328.         set(TRUE);
  329.         return;
  330.     }
  331.  
  332.     if (ess)
  333.         whatare_all_signals();
  334.  
  335.     found = TRUE;
  336.  
  337.     for (i = 0; av[i] != NULL; i++) {
  338.         f = FALSE;
  339.         errno = ENOENT;
  340.         if ((s = varlookup(av[i])) != NULL) {
  341.             f = TRUE;
  342.             prettyprint_var(1, av[i], s);
  343.         }
  344.         if ((n = fnlookup(av[i])) != NULL) {
  345.             f = TRUE;
  346.             prettyprint_fn(1, av[i], n);
  347.         } else if (isbuiltin(av[i]) != NULL) {
  348.             f = TRUE;
  349.             for (j = 0; j < arraysize(builtins_str); j++)
  350.                 if (streq(av[i], builtins_str[j]))
  351.                     break;
  352.             fprint(1, "builtin %s\n", builtins_str[j]);
  353.         } else if ((e = which(av[i], FALSE)) != NULL) {
  354.             f = TRUE;
  355.             fprint(1, "%s\n", e);
  356.         }
  357.         if (!f) {
  358.             found = FALSE;
  359.             if (errno != ENOENT)
  360.                 uerror(av[i]);
  361.             else
  362.                 fprint(2, "%s not found\n", av[i]);
  363.         }
  364.     }
  365.  
  366.     set(found);
  367. }
  368.  
  369. /* push a string to be eval'ed onto the input stack. evaluate it */
  370.  
  371. static void b_eval(char **av) {
  372.     boolean i = interactive;
  373.  
  374.     if (av[1] == NULL)
  375.         return;
  376.  
  377.     interactive = FALSE;
  378.     pushinput(STRING, av + 1, i); /* don't reset line numbers on noninteractive eval */
  379.     doit(TRUE);
  380.     interactive = i;
  381. }
  382.  
  383. /*
  384.    push a file to be interpreted onto the input stack. with "-i" treat this as an interactive
  385.    input source.
  386. */
  387.  
  388. void b_dot(char **av) {
  389.     int fd;
  390.     boolean old_i = interactive, i = FALSE;
  391.     Estack e;
  392.  
  393.     av++;
  394.  
  395.     if (*av == NULL)
  396.         return;
  397.  
  398.     if (streq(*av,"-i")) {
  399.         av++;
  400.         i = TRUE;
  401.     }
  402.  
  403.     if (dasheye) { /* rc -i file has to do the right thing. reset the dasheye state to FALSE, though. */
  404.         dasheye = FALSE;
  405.         i = TRUE;
  406.     }
  407.  
  408.     if (*av == NULL)
  409.         return;
  410.  
  411.     fd = rc_open(*av, FROM);
  412.  
  413.     if (fd < 0) {
  414.         if (rcrc) /* on rc -l, don't flag nonexistence of .rcrc */
  415.             rcrc = FALSE;
  416.         else
  417.             uerror(*av);
  418.         set(FALSE);
  419.         return;
  420.     }
  421.     rcrc = FALSE;
  422.  
  423.     starassign(*av, av+1, TRUE);
  424.     pushinput(FD, fd);
  425.     interactive = i;
  426.     except(VARSTACK, "*", &e);
  427.     doit(TRUE);
  428.     varrm("*", TRUE);
  429.     unexcept(); /* VARSTACK */
  430.     interactive = old_i;
  431. }
  432.  
  433. /* Berkeley limit support was cleaned up by Paul Haahr. */
  434.  
  435. #ifdef NOLIMITS
  436. static void b_limit(char **av) {
  437.     rc_error("rc was compiled without berkeley limits");
  438. }
  439. #else
  440.  
  441. typedef struct Suffix Suffix;
  442. struct Suffix {
  443.     const Suffix *next;
  444.     long amount;
  445.     char *name;
  446. };
  447.  
  448. static const Suffix
  449.     kbsuf = { NULL, 1024, "k" },
  450.     mbsuf = { &kbsuf, 1024*1024, "m" },
  451.     gbsuf = { &mbsuf, 1024*1024*1024, "g" },
  452.     stsuf = { NULL, 1, "s" },
  453.     mtsuf = { &stsuf, 60, "m" },
  454.     htsuf = { &mtsuf, 60*60, "h" };
  455. #define    SIZESUF &gbsuf
  456. #define    TIMESUF &htsuf
  457. #define    NOSUF ((Suffix *) NULL)  /* for RLIMIT_NOFILE on SunOS 4.1 */
  458.  
  459. typedef struct {
  460.     char *name;
  461.     int flag;
  462.     const Suffix *suffix;
  463. } Limit;
  464. static const Limit limits[] = {
  465.     { "cputime",        RLIMIT_CPU,    TIMESUF },
  466.     { "filesize",        RLIMIT_FSIZE,    SIZESUF },
  467.     { "datasize",        RLIMIT_DATA,    SIZESUF },
  468.     { "stacksize",        RLIMIT_STACK,    SIZESUF },
  469.     { "coredumpsize",    RLIMIT_CORE,    SIZESUF },
  470. #ifdef RLIMIT_RSS /* SysVr4 does not have this */
  471.     { "memoryuse",        RLIMIT_RSS,    SIZESUF },
  472. #endif
  473. #ifdef RLIMIT_VMEM /* instead, they have this! */
  474.     { "vmemory",        RLIMIT_VMEM,    SIZESUF },
  475. #endif
  476. #ifdef RLIMIT_NOFILE  /* SunOS 4.1 adds a limit on file descriptors */
  477.     { "descriptors",    RLIMIT_NOFILE,    NOSUF },
  478. #endif
  479.     { NULL, 0, NULL }
  480. };
  481.  
  482. extern int getrlimit(int, struct rlimit *);
  483. extern int setrlimit(int, struct rlimit *);
  484.  
  485. static void printlimit(const Limit *limit, boolean hard) {
  486.     struct rlimit rlim;
  487.     long lim;
  488.     getrlimit(limit->flag, &rlim);
  489.     if (hard)
  490.         lim = rlim.rlim_max;
  491.     else
  492.         lim = rlim.rlim_cur;
  493.     if (lim == RLIM_INFINITY)
  494.         fprint(1, "%s \tunlimited\n", limit->name);
  495.     else {
  496.         const Suffix *suf;
  497.         for (suf = limit->suffix; suf != NULL; suf = suf->next)
  498.             if (lim % suf->amount == 0) {
  499.                 lim /= suf->amount;
  500.                 break;
  501.             }
  502.         fprint(1, "%s \t%d%s\n", limit->name, lim, suf == NULL ? "" : suf->name);
  503.     }
  504. }
  505.  
  506. static long parselimit(const Limit *limit, char *s) {
  507.     int len = strlen(s);
  508.     long lim = 1;
  509.     const Suffix *suf = limit->suffix;
  510.     if (streq(s, "unlimited"))
  511.         return RLIM_INFINITY;
  512.     if (suf == TIMESUF && strchr(s, ':') != NULL) {
  513.         char *t = strchr(s, ':');
  514.         *t++ = '\0';
  515.         lim = 60 * a2u(s) + a2u(t);
  516.     } else {
  517.         for (; suf != NULL; suf = suf->next)
  518.             if (streq(suf->name, s + len - strlen(suf->name))) {
  519.                 s[len - strlen(suf->name)] = '\0';
  520.                 lim *= suf->amount;
  521.                 break;
  522.             }
  523.         lim *= a2u(s);
  524.     }
  525.     return lim;
  526. }
  527.  
  528. static void b_limit(char **av) {
  529.     const Limit *lp = limits;
  530.     boolean hard = FALSE;
  531.  
  532.     if (*++av != NULL && streq(*av, "-h")) {
  533.         av++;
  534.         hard = TRUE;
  535.     }
  536.  
  537.     if (*av == NULL) {
  538.         for (; lp->name != NULL; lp++)
  539.             printlimit(lp, hard);
  540.         return;
  541.     }
  542.  
  543.     for (;; lp++) {
  544.         if (lp->name == NULL) {
  545.             fprint(2,"no such limit\n");
  546.             set(FALSE);
  547.             return;
  548.         }
  549.         if (streq(*av, lp->name))
  550.             break;
  551.     }
  552.  
  553.     if (*++av == NULL)
  554.         printlimit(lp, hard);
  555.     else {
  556.         struct rlimit rlim;
  557.         long pl;
  558.         getrlimit(lp->flag, &rlim);
  559.         if ((pl = parselimit(lp, *av)) < 0) {
  560.             fprint(2,"bad limit\n");
  561.             set(FALSE);
  562.             return;
  563.         }
  564.         if (hard)
  565.             rlim.rlim_max = pl;
  566.         else
  567.             rlim.rlim_cur = pl;
  568.         if (setrlimit(lp->flag, &rlim) == -1) {
  569.             uerror("setrlimit");
  570.             set(FALSE);
  571.         } else
  572.             set(TRUE);
  573.     }
  574. }
  575. #endif
  576.