home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume20 / rc / part01 / builtins.c next >
Encoding:
C/C++ Source or Header  |  1991-05-22  |  10.0 KB  |  512 lines

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