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