home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / NEXTSTEP / UNIX / Shells / zsh-3.0.5-MIHS / src / Src / builtin.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-09-25  |  142.0 KB  |  5,744 lines

  1. /*
  2.  * $Id: builtin.c,v 2.95 1996/10/16 22:47:53 hzoli Exp $
  3.  *
  4.  * builtin.c - builtin commands
  5.  *
  6.  * This file is part of zsh, the Z shell.
  7.  *
  8.  * Copyright (c) 1992-1996 Paul Falstad
  9.  * All rights reserved.
  10.  *
  11.  * Permission is hereby granted, without written agreement and without
  12.  * license or royalty fees, to use, copy, modify, and distribute this
  13.  * software and to distribute modified versions of this software for any
  14.  * purpose, provided that the above copyright notice and the following
  15.  * two paragraphs appear in all copies of this software.
  16.  *
  17.  * In no event shall Paul Falstad or the Zsh Development Group be liable
  18.  * to any party for direct, indirect, special, incidental, or consequential
  19.  * damages arising out of the use of this software and its documentation,
  20.  * even if Paul Falstad and the Zsh Development Group have been advised of
  21.  * the possibility of such damage.
  22.  *
  23.  * Paul Falstad and the Zsh Development Group specifically disclaim any
  24.  * warranties, including, but not limited to, the implied warranties of
  25.  * merchantability and fitness for a particular purpose.  The software
  26.  * provided hereunder is on an "as is" basis, and Paul Falstad and the
  27.  * Zsh Development Group have no obligation to provide maintenance,
  28.  * support, updates, enhancements, or modifications.
  29.  *
  30.  */
  31.  
  32. #include "zsh.h"
  33.  
  34. static void printoptions _((int set));
  35.  
  36. static char *auxdata;
  37. static int auxlen;
  38.  
  39. /* execute a builtin handler function after parsing the arguments */
  40.  
  41. #define MAX_OPS 128
  42.  
  43. /**/
  44. int
  45. execbuiltin(LinkList args, Builtin bn)
  46. {
  47.     LinkNode n;
  48.     char ops[MAX_OPS], *arg, *pp, *name, **argv, **oargv, *optstr;
  49.     char *oxarg, *xarg = NULL;
  50.     int flags, sense, argc = 0, execop;
  51.  
  52.     /* initialise some static variables */
  53.     auxdata = NULL;
  54.     auxlen = 0;
  55.  
  56.     /* initialize some local variables */
  57.     memset(ops, 0, MAX_OPS);
  58.     name = (char *) ugetnode(args);
  59.  
  60.     arg = (char *) ugetnode(args);
  61.  
  62.     /* get some information about the command */
  63.     flags = bn->flags;
  64.     optstr = bn->optstr;
  65.  
  66.     /* Sort out the options. */
  67.     if ((flags & BINF_ECHOPTS) && isset(BSDECHO))
  68.     ops['E'] = 1;
  69.     if (optstr)
  70.     /* while arguments look like options ... */
  71.     while (arg &&
  72.            ((sense = (*arg == '-')) ||
  73.          ((flags & BINF_PLUSOPTS) && *arg == '+')) &&
  74.            ((flags & BINF_PLUSOPTS) || !atoi(arg))) {
  75.         /* unrecognised options to echo etc. are not really options */
  76.         if (flags & BINF_ECHOPTS) {
  77.         char *p = arg;
  78.         while (*++p && strchr(optstr, (int) *p));
  79.         if (*p)
  80.             break;
  81.         }
  82.         /* save the options in xarg, for execution tracing */
  83.         if (xarg) {
  84.         oxarg = tricat(xarg, " ", arg);
  85.         zsfree(xarg);
  86.         xarg = oxarg;
  87.         } else
  88.         xarg = ztrdup(arg);
  89.         /* handle -- or - (ops['-']), and + (ops['-'] and ops['+']) */
  90.         if (arg[1] == '-')
  91.         arg++;
  92.         if (!arg[1]) {
  93.         ops['-'] = 1;
  94.         if (!sense)
  95.             ops['+'] = 1;
  96.         }
  97.         /* save options in ops, as long as they are in bn->optstr */
  98.         execop = -1;
  99.         while (*++arg)
  100.         if (strchr(optstr, execop = (int)*arg))
  101.             ops[(int)*arg] = (sense) ? 1 : 2;
  102.         else
  103.             break;
  104.         /* "typeset" may take a numeric argument *
  105.          * at the tail of the options            */
  106.         if (idigit(*arg) && (flags & BINF_TYPEOPT) &&
  107.         (arg[-1] == 'L' || arg[-1] == 'R' ||
  108.          arg[-1] == 'Z' || arg[-1] == 'i'))
  109.         auxlen = (int)zstrtol(arg, &arg, 10);
  110.         /* The above loop may have exited on an invalid option.  (We  *
  111.          * assume that any option requiring metafication is invalid.) */
  112.         if (*arg) {
  113.         if(*arg == Meta)
  114.             *++arg ^= 32;
  115.         zerr("bad option: -%c", NULL, *arg);
  116.         zsfree(xarg);
  117.         return 1;
  118.         }
  119.         arg = (char *) ugetnode(args);
  120.         /* for the "print" builtin, the options after -R are treated as
  121.         options to "echo" */
  122.         if ((flags & BINF_PRINTOPTS) && ops['R']) {
  123.         optstr = "ne";
  124.         flags |= BINF_ECHOPTS;
  125.         }
  126.         /* the option -- indicates the end of the options */
  127.         if (ops['-'])
  128.         break;
  129.         /* for "fc", -e takes an extra argument */
  130.         if ((flags & BINF_FCOPTS) && execop == 'e') {
  131.         auxdata = arg;
  132.         arg = (char *) ugetnode(args);
  133.         }
  134.         /* for "typeset", -L, -R, -Z and -i take a numeric extra argument */
  135.         if ((flags & BINF_TYPEOPT) && (execop == 'L' || execop == 'R' ||
  136.         execop == 'Z' || execop == 'i') && arg && idigit(*arg)) {
  137.         auxlen = atoi(arg);
  138.         arg = (char *) ugetnode(args);
  139.         }
  140.     }
  141.     if (flags & BINF_R)
  142.     auxdata = "-";
  143.     /* handle built-in options, for overloaded handler functions */
  144.     if ((pp = bn->defopts))
  145.     while (*pp)
  146.         ops[(int)*pp++] = 1;
  147.  
  148.     /* Set up the argument list. */
  149.     if (arg) {
  150.     /* count the arguments */
  151.     argc = 1;
  152.     n = firstnode(args);
  153.     while (n)
  154.         argc++, incnode(n);
  155.     }
  156.     /* Get the actual arguments, into argv.  Oargv saves the *
  157.      * beginning of the array for later reference.           */
  158.     oargv = argv = (char **)ncalloc(sizeof(char **) * (argc + 1));
  159.     if ((*argv++ = arg))
  160.     while ((*argv++ = (char *)ugetnode(args)));
  161.     argv = oargv;
  162.     if (errflag) {
  163.     zsfree(xarg);
  164.     errflag = 0;
  165.     return 1;
  166.     }
  167.  
  168.     /* check that the argument count lies within the specified bounds */
  169.     if (argc < bn->minargs || (argc > bn->maxargs && bn->maxargs != -1)) {
  170.     zwarnnam(name, (argc < bn->minargs)
  171.         ? "not enough arguments" : "too many arguments", NULL, 0);
  172.     zsfree(xarg);
  173.     return 1;
  174.     }
  175.  
  176.     /* display execution trace information, if required */
  177.     if (isset(XTRACE)) {
  178.     fprintf(stderr, "%s%s", (prompt4) ? prompt4 : "", name);
  179.     if (xarg)
  180.         fprintf(stderr, " %s", xarg);
  181.     while (*oargv)
  182.         fprintf(stderr, " %s", *oargv++);
  183.     fputc('\n', stderr);
  184.     fflush(stderr);
  185.     }
  186.     zsfree(xarg);
  187.     /* call the handler function, and return its return value */
  188.     return (*(bn->handlerfunc)) (name, argv, ops, bn->funcid);
  189. }
  190.  
  191. /* Enable/disable an element in one of the internal hash tables.  *
  192.  * With no arguments, it lists all the currently enabled/disabled *
  193.  * elements in that particular hash table.                        */
  194.  
  195. /**/
  196. int
  197. bin_enable(char *name, char **argv, char *ops, int func)
  198. {
  199.     HashTable ht;
  200.     HashNode hn;
  201.     ScanFunc scanfunc;
  202.     Comp com;
  203.     int flags1 = 0, flags2 = 0;
  204.     int match = 0, returnval = 0;
  205.  
  206.     /* Find out which hash table we are working with. */
  207.     if (ops['f'])
  208.     ht = shfunctab;
  209.     else if (ops['r'])
  210.     ht = reswdtab;
  211.     else if (ops['a'])
  212.     ht = aliastab;
  213.     else
  214.     ht = builtintab;
  215.  
  216.     /* Do we want to enable or disable? */
  217.     if (func == BIN_ENABLE) {
  218.     flags2 = DISABLED;
  219.     scanfunc = ht->enablenode;
  220.     } else {
  221.     flags1 = DISABLED;
  222.     scanfunc = ht->disablenode;
  223.     }
  224.  
  225.     /* Given no arguments, print the names of the enabled/disabled elements  *
  226.      * in this hash table.  If func == BIN_ENABLE, then scanhashtable will   *
  227.      * print nodes NOT containing the DISABLED flag, else scanhashtable will *
  228.      * print nodes containing the DISABLED flag.                             */
  229.     if (!*argv) {
  230.     scanhashtable(ht, 1, flags1, flags2, ht->printnode, 0);
  231.     return 0;
  232.     }
  233.  
  234.     /* With -m option, treat arguments as glob patterns. */
  235.     if (ops['m']) {
  236.     for (; *argv; argv++) {
  237.         /* parse pattern */
  238.         tokenize(*argv);
  239.         if ((com = parsereg(*argv)))
  240.         match += scanmatchtable(ht, com, 0, 0, scanfunc, 0);
  241.         else {
  242.         untokenize(*argv);
  243.         zwarnnam(name, "bad pattern : %s", *argv, 0);
  244.         returnval = 1;
  245.         }
  246.     }
  247.     /* If we didn't match anything, we return 1. */
  248.     if (!match)
  249.         returnval = 1;
  250.     return returnval;
  251.     }
  252.  
  253.     /* Take arguments literally -- do not glob */
  254.     for (; *argv; argv++) {
  255.         if ((hn = ht->getnode2(ht, *argv))) {
  256.         scanfunc(hn, 0);
  257.         } else {
  258.         zwarnnam(name, "no such hash table element: %s", *argv, 0);
  259.         returnval = 1;
  260.         }
  261.     }
  262.     return returnval;
  263. }
  264.  
  265. /* set: either set the shell options, or set the shell arguments, *
  266.  * or declare an array, or show various things                    */
  267.  
  268. /**/
  269. int
  270. bin_set(char *nam, char **args, char *ops, int func)
  271. {
  272.     int action, optno, array = 0, hadopt = 0,
  273.     hadplus = 0, hadend = 0, sort = 0;
  274.     char **x;
  275.  
  276.     /* Obsolecent sh compatibility: set - is the same as set +xv *
  277.      * and set - args is the same as set +xv -- args             */
  278.     if (*args && **args == '-' && !args[0][1]) {
  279.     dosetopt(VERBOSE, 0, 0);
  280.     dosetopt(XTRACE, 0, 0);
  281.     if (!args[1])
  282.         return 0;
  283.     }
  284.  
  285.     /* loop through command line options (begins with "-" or "+") */
  286.     while (*args && (**args == '-' || **args == '+')) {
  287.     action = (**args == '-');
  288.     hadplus |= !action;
  289.     if(!args[0][1])
  290.         *args = "--";
  291.     while (*++*args) {
  292.         if(**args == Meta)
  293.         *++*args ^= 32;
  294.         if(**args != '-' || action)
  295.         hadopt = 1;
  296.         /* The pseudo-option `--' signifies the end of options. */
  297.         if (**args == '-') {
  298.         hadend = 1;
  299.         args++;
  300.         goto doneoptions;
  301.         } else if (**args == 'o') {
  302.         if (!*++*args)
  303.             args++;
  304.         if (!*args) {
  305.             zwarnnam(nam, "string expected after -o", NULL, 0);
  306.             inittyptab();
  307.             return 1;
  308.         }
  309.         if(!(optno = optlookup(*args)))
  310.             zwarnnam(nam, "no such option: %s", *args, 0);
  311.         else if(dosetopt(optno, action, 0))
  312.             zwarnnam(nam, "can't change option: %s", *args, 0);
  313.         break;
  314.         } else if(**args == 'A') {
  315.         if(!*++*args)
  316.             args++;
  317.         array = action ? 1 : -1;
  318.         goto doneoptions;
  319.         } else if (**args == 's')
  320.         sort = action ? 1 : -1;
  321.         else {
  322.             if (!(optno = optlookupc(**args)))
  323.             zwarnnam(nam, "bad option: -%c", NULL, **args);
  324.         else if(dosetopt(optno, action, 0))
  325.             zwarnnam(nam, "can't change option: -%c", NULL, **args);
  326.         }
  327.     }
  328.     args++;
  329.     }
  330.     doneoptions:
  331.     inittyptab();
  332.  
  333.     /* Show the parameters, possibly with values */
  334.     if (!hadopt && !*args)
  335.     scanhashtable(paramtab, 1, 0, 0, paramtab->printnode,
  336.         hadplus ? PRINT_NAMEONLY : 0);
  337.  
  338.     if (array && !*args) {
  339.     /* display arrays */
  340.     scanhashtable(paramtab, 1, PM_ARRAY, 0, paramtab->printnode,
  341.         hadplus ? PRINT_NAMEONLY : 0);
  342.     }
  343.     if (!*args && !hadend)
  344.     return 0;
  345.     if (array)
  346.     args++;
  347.     if (sort)
  348.     qsort(args, arrlen(args), sizeof(char *),
  349.           sort > 0 ? strpcmp : invstrpcmp);
  350.     if (array) {
  351.     /* create an array with the specified elements */
  352.     char **a = NULL, **y, *name = args[-1];
  353.     int len = arrlen(args);
  354.  
  355.     if (array < 0 && (a = getaparam(name))) {
  356.         int al = arrlen(a);
  357.  
  358.         if (al > len)
  359.         len = al;
  360.     }
  361.     for (x = y = zalloc((len + 1) * sizeof(char *)); len--; a++) {
  362.         if (!*args)
  363.         args = a;
  364.         *y++ = ztrdup(*args++);
  365.     }
  366.     *y++ = NULL;
  367.     setaparam(name, x);
  368.     } else {
  369.     /* set shell arguments */
  370.     freearray(pparams);
  371.     PERMALLOC {
  372.         pparams = arrdup(args);
  373.     } LASTALLOC;
  374.     }
  375.     return 0;
  376. }
  377.  
  378. /* setopt, unsetopt */
  379.  
  380. /**/
  381. int
  382. bin_setopt(char *nam, char **args, char *ops, int isun)
  383. {
  384.     int action, optno, match = 0;
  385.  
  386.     /* With no arguments or options, display options.  The output *
  387.      * format is determined by the printoptions function (below). */
  388.     if (!*args) {
  389.     printoptions(!isun);
  390.     return 0;
  391.     }
  392.  
  393.     /* loop through command line options (begins with "-" or "+") */
  394.     while (*args && (**args == '-' || **args == '+')) {
  395.     action = (**args == '-') ^ isun;
  396.     if(!args[0][1])
  397.         *args = "--";
  398.     while (*++*args) {
  399.         if(**args == Meta)
  400.         *++*args ^= 32;
  401.         /* The pseudo-option `--' signifies the end of options. */
  402.         if (**args == '-') {
  403.         args++;
  404.         goto doneoptions;
  405.         } else if (**args == 'o') {
  406.         if (!*++*args)
  407.             args++;
  408.         if (!*args) {
  409.             zwarnnam(nam, "string expected after -o", NULL, 0);
  410.             inittyptab();
  411.             return 1;
  412.         }
  413.         if(!(optno = optlookup(*args)))
  414.             zwarnnam(nam, "no such option: %s", *args, 0);
  415.         else if(dosetopt(optno, action, 0))
  416.             zwarnnam(nam, "can't change option: %s", *args, 0);
  417.         break;
  418.         } else if(**args == 'm') {
  419.         match = 1;
  420.         } else {
  421.             if (!(optno = optlookupc(**args)))
  422.             zwarnnam(nam, "bad option: -%c", NULL, **args);
  423.         else if(dosetopt(optno, action, 0))
  424.             zwarnnam(nam, "can't change option: -%c", NULL, **args);
  425.         }
  426.     }
  427.     args++;
  428.     }
  429.     doneoptions:
  430.  
  431.     if (!match) {
  432.     /* Not globbing the arguments -- arguments are simply option names. */
  433.     while (*args) {
  434.         if(!(optno = optlookup(*args++)))
  435.         zwarnnam(nam, "no such option: %s", args[-1], 0);
  436.         else if(dosetopt(optno, !isun, 0))
  437.         zwarnnam(nam, "can't change option: %s", args[-1], 0);
  438.     }
  439.     } else {
  440.     /* Globbing option (-m) set. */
  441.     while (*args) {
  442.         Comp com;
  443.  
  444.         /* Expand the current arg. */
  445.         tokenize(*args);
  446.         if (!(com = parsereg(*args))) {
  447.         untokenize(*args);
  448.         zwarnnam(nam, "bad pattern: %s", *args, 0);
  449.         continue;
  450.         }
  451.         /* Loop over expansions. */
  452.         for(optno = OPT_SIZE; --optno; )
  453.         if (optno != PRIVILEGED && domatch(optns[optno].name, com, 0))
  454.                 dosetopt(optno, !isun, 0);
  455.         args++;
  456.     }
  457.     }
  458.     inittyptab();
  459.     return 0;
  460. }
  461.  
  462. /* print options */
  463.  
  464. static void
  465. printoptions(int set)
  466. {
  467.     int optno;
  468.  
  469.     if (isset(KSHOPTIONPRINT)) {
  470.     /* ksh-style option display -- list all options, *
  471.      * with an indication of current status          */
  472.     printf("Current option settings\n");
  473.     for(optno = 1; optno < OPT_SIZE; optno++) {
  474.         if (defset(optno))
  475.         printf("no%-20s%s\n", optns[optno].name, isset(optno) ? "off" : "on");
  476.         else
  477.         printf("%-22s%s\n", optns[optno].name, isset(optno) ? "on" : "off");
  478.     }
  479.     } else {
  480.     /* list all options that are on, or all that are off */
  481.     for(optno = 1; optno < OPT_SIZE; optno++) {
  482.         if (set == (isset(optno) ^ defset(optno))) {
  483.         if (set ^ isset(optno))
  484.             fputs("no", stdout);
  485.         puts(optns[optno].name);
  486.         }
  487.     }
  488.     }
  489. }
  490.  
  491. /**** job control builtins ****/
  492.  
  493. /* Make sure we have a suitable current and previous job set. */
  494.  
  495. static void
  496. setcurjob(void)
  497. {
  498.     if (curjob == thisjob ||
  499.     (curjob != -1 && !(jobtab[curjob].stat & STAT_INUSE))) {
  500.     curjob = prevjob;
  501.     setprevjob();
  502.     if (curjob == thisjob ||
  503.         (curjob != -1 && !((jobtab[curjob].stat & STAT_INUSE) &&
  504.                    curjob != thisjob))) {
  505.         curjob = prevjob;
  506.         setprevjob();
  507.     }
  508.     }
  509. }
  510.  
  511. /* bg, disown, fg, jobs, wait: most of the job control commands are     *
  512.  * here.  They all take the same type of argument.  Exception: wait can *
  513.  * take a pid or a job specifier, whereas the others only work on jobs. */
  514.  
  515. /**/
  516. int
  517. bin_fg(char *name, char **argv, char *ops, int func)
  518. {
  519.     int job, lng, firstjob = -1, retval = 0;
  520.  
  521.     if (ops['Z']) {
  522.     if (*argv)
  523.         strcpy(hackzero, *argv);
  524.     return 0;
  525.     }
  526.  
  527.     lng = (ops['l']) ? 1 : (ops['p']) ? 2 : 0;
  528.     if ((func == BIN_FG || func == BIN_BG) && !jobbing) {
  529.     /* oops... maybe bg and fg should have been disabled? */
  530.     zwarnnam(name, "no job control in this shell.", NULL, 0);
  531.     return 1;
  532.     }
  533.  
  534.     /* If necessary, update job table. */
  535.     if (unset(NOTIFY))
  536.     scanjobs();
  537.  
  538.     setcurjob();
  539.  
  540.     if (func == BIN_JOBS)
  541.         /* If you immediately type "exit" after "jobs", this      *
  542.          * will prevent zexit from complaining about stopped jobs */
  543.     stopmsg = 2;
  544.     if (!*argv)
  545.     /* This block handles all of the default cases (no arguments).  bg,
  546.     fg and disown act on the current job, and jobs and wait act on all the
  547.     jobs. */
  548.      if (func == BIN_FG || func == BIN_BG || func == BIN_DISOWN) {
  549.         /* W.r.t. the above comment, we'd better have a current job at this
  550.         point or else. */
  551.         if (curjob == -1 || (jobtab[curjob].stat & STAT_NOPRINT)) {
  552.         zwarnnam(name, "no current job", NULL, 0);
  553.         return 1;
  554.         }
  555.         firstjob = curjob;
  556.     } else if (func == BIN_JOBS) {
  557.         /* List jobs. */
  558.         for (job = 0; job != MAXJOB; job++)
  559.         if (job != thisjob && jobtab[job].stat) {
  560.             if ((!ops['r'] && !ops['s']) ||
  561.             (ops['r'] && ops['s']) ||
  562.             (ops['r'] && !(jobtab[job].stat & STAT_STOPPED)) ||
  563.             (ops['s'] && jobtab[job].stat & STAT_STOPPED))
  564.             printjob(job + jobtab, lng, 2);
  565.         }
  566.         return 0;
  567.     } else {   /* Must be BIN_WAIT, so wait for all jobs */
  568.         for (job = 0; job != MAXJOB; job++)
  569.         if (job != thisjob && jobtab[job].stat)
  570.             waitjob(job, SIGINT);
  571.         return 0;
  572.     }
  573.  
  574.     /* Defaults have been handled.  We now have an argument or two, or three...
  575.     In the default case for bg, fg and disown, the argument will be provided by
  576.     the above routine.  We now loop over the arguments. */
  577.     for (; (firstjob != -1) || *argv; (void)(*argv && argv++)) {
  578.     int stopped, ocj = thisjob;
  579.  
  580.     if (func == BIN_WAIT && isanum(*argv)) {
  581.         /* wait can take a pid; the others can't. */
  582.         waitforpid((long)atoi(*argv));
  583.         retval = lastval2;
  584.         thisjob = ocj;
  585.         continue;
  586.     }
  587.     /* The only type of argument allowed now is a job spec.  Check it. */
  588.     job = (*argv) ? getjob(*argv, name) : firstjob;
  589.     firstjob = -1;
  590.     if (job == -1) {
  591.         retval = 1;
  592.         break;
  593.     }
  594.     if (!(jobtab[job].stat & STAT_INUSE) ||
  595.         (jobtab[job].stat & STAT_NOPRINT)) {
  596.         zwarnnam(name, "no such job: %d", 0, job);
  597.         return 1;
  598.     }
  599.     /* We have a job number.  Now decide what to do with it. */
  600.     switch (func) {
  601.     case BIN_FG:
  602.     case BIN_BG:
  603.     case BIN_WAIT:
  604.         if ((stopped = (jobtab[job].stat & STAT_STOPPED)))
  605.         makerunning(jobtab + job);
  606.         else if (func == BIN_BG) {
  607.         /* Silly to bg a job already running. */
  608.         zwarnnam(name, "job already in background", NULL, 0);
  609.         thisjob = ocj;
  610.         return 1;
  611.         }
  612.         /* It's time to shuffle the jobs around!  Reset the current job,
  613.         and pick a sensible secondary job. */
  614.         if (curjob == job) {
  615.         curjob = prevjob;
  616.         prevjob = (func == BIN_BG) ? -1 : job;
  617.         }
  618.         if (prevjob == job || prevjob == -1)
  619.         setprevjob();
  620.         if (curjob == -1) {
  621.         curjob = prevjob;
  622.         setprevjob();
  623.         }
  624.         if (func != BIN_WAIT)
  625.         /* for bg and fg -- show the job we are operating on */
  626.         printjob(jobtab + job, (stopped) ? -1 : 0, 1);
  627.         if (func == BIN_BG)
  628.         jobtab[job].stat |= STAT_NOSTTY;
  629.         else {                /* fg or wait */
  630.         if (strcmp(jobtab[job].pwd, pwd)) {
  631.             fprintf(shout, "(pwd : ");
  632.             fprintdir(jobtab[job].pwd, shout);
  633.             fprintf(shout, ")\n");
  634.         }
  635.         fflush(shout);
  636.         if (func != BIN_WAIT) {        /* fg */
  637.             thisjob = job;
  638.             attachtty(jobtab[job].gleader);
  639.         }
  640.         }
  641.         if (stopped) {
  642.         if (func != BIN_BG && jobtab[job].ty)
  643.             settyinfo(jobtab[job].ty);
  644.         killjb(jobtab + job, SIGCONT);
  645.         }
  646.         if (func == BIN_WAIT)
  647.             waitjob(job, SIGINT);
  648.         if (func != BIN_BG) {
  649.         waitjobs();
  650.         retval = lastval2;
  651.         }
  652.         break;
  653.     case BIN_JOBS:
  654.         printjob(job + jobtab, lng, 2);
  655.         break;
  656.     case BIN_DISOWN:
  657.         {
  658.         static struct job zero;
  659.  
  660.         jobtab[job] = zero;
  661.         break;
  662.         }
  663.     }
  664.     thisjob = ocj;
  665.     }
  666.     return retval;
  667. }
  668.  
  669. /* kill: send a signal to a process.  The process(es) may be specified *
  670.  * by job specifier (see above) or pid.  A signal, defaulting to       *
  671.  * SIGTERM, may be specified by name or number, preceded by a dash.    */
  672.  
  673. /**/
  674. int
  675. bin_kill(char *nam, char **argv, char *ops, int func)
  676. {
  677.     int sig = SIGTERM;
  678.     int returnval = 0;
  679.  
  680.     /* check for, and interpret, a signal specifier */
  681.     if (*argv && **argv == '-') {
  682.     if (idigit((*argv)[1]))
  683.         /* signal specified by number */
  684.         sig = atoi(*argv + 1);
  685.     else if ((*argv)[1] != '-' || (*argv)[2]) {
  686.         char *signame;
  687.  
  688.         /* with argument "-l" display the list of signal names */
  689.         if ((*argv)[1] == 'l' && (*argv)[2] == '\0') {
  690.         if (argv[1]) {
  691.             while (*++argv) {
  692.             sig = zstrtol(*argv, &signame, 10);
  693.             if (signame == *argv) {
  694.                 for (sig = 1; sig <= SIGCOUNT; sig++)
  695.                 if (!cstrpcmp(sigs + sig, &signame))
  696.                     break;
  697.                 if (sig > SIGCOUNT) {
  698.                 zwarnnam(nam, "unknown signal: SIG%s",
  699.                      signame, 0);
  700.                 returnval++;
  701.                 } else
  702.                 printf("%d\n", sig);
  703.             } else {
  704.                 if (*signame) {
  705.                 zwarnnam(nam, "unknown signal: SIG%s",
  706.                      signame, 0);
  707.                 returnval++;
  708.                 } else {
  709.                 if (WIFSIGNALED(sig))
  710.                     sig = WTERMSIG(sig);
  711.                 else if (WIFSTOPPED(sig))
  712.                     sig = WSTOPSIG(sig);
  713.                 if (1 <= sig && sig <= SIGCOUNT)
  714.                     printf("%s\n", sigs[sig]);
  715.                 else
  716.                     printf("%d\n", sig);
  717.                 }
  718.             }
  719.             }
  720.             return returnval;
  721.         }
  722.         printf("%s", sigs[1]);
  723.         for (sig = 2; sig <= SIGCOUNT; sig++)
  724.             printf(" %s", sigs[sig]);
  725.         putchar('\n');
  726.         return 0;
  727.         }
  728.         if ((*argv)[1] == 's' && (*argv)[2] == '\0')
  729.         signame = *++argv;
  730.         else
  731.         signame = *argv + 1;
  732.  
  733.         /* check for signal matching specified name */
  734.         for (sig = 1; sig <= SIGCOUNT; sig++)
  735.         if (!cstrpcmp(sigs + sig, &signame))
  736.             break;
  737.         if (*signame == '0' && !signame[1])
  738.         sig = 0;
  739.         if (sig > SIGCOUNT) {
  740.         zwarnnam(nam, "unknown signal: SIG%s", signame, 0);
  741.         zwarnnam(nam, "type kill -l for a List of signals", NULL, 0);
  742.         return 1;
  743.         }
  744.     }
  745.     argv++;
  746.     }
  747.  
  748.     setcurjob();
  749.  
  750.     /* Remaining arguments specify processes.  Loop over them, and send the
  751.     signal (number sig) to each process. */
  752.     for (; *argv; argv++) {
  753.     if (**argv == '%') {
  754.         /* job specifier introduced by '%' */
  755.         int p;
  756.  
  757.         if ((p = getjob(*argv, nam)) == -1) {
  758.         returnval++;
  759.         continue;
  760.         }
  761.         if (killjb(jobtab + p, sig) == -1) {
  762.         zwarnnam("kill", "kill %s failed: %e", *argv, errno);
  763.         returnval++;
  764.         continue;
  765.         }
  766.         /* automatically update the job table if sending a SIGCONT to a
  767.         job, and send the job a SIGCONT if sending it a non-stopping
  768.         signal. */
  769.         if (jobtab[p].stat & STAT_STOPPED) {
  770.         if (sig == SIGCONT)
  771.             jobtab[p].stat &= ~STAT_STOPPED;
  772.         if (sig != SIGKILL && sig != SIGCONT && sig != SIGTSTP
  773.             && sig != SIGTTOU && sig != SIGTTIN && sig != SIGSTOP)
  774.             killjb(jobtab + p, SIGCONT);
  775.         }
  776.     } else if (!isanum(*argv)) {
  777.         zwarnnam("kill", "illegal pid: %s", *argv, 0);
  778.         returnval++;
  779.     } else if (kill(atoi(*argv), sig) == -1) {
  780.         zwarnnam("kill", "kill %s failed: %e", *argv, errno);
  781.         returnval++;
  782.     }
  783.     }
  784.     return returnval < 126 ? returnval : 1;
  785. }
  786.  
  787. /* Suspend this shell */
  788.  
  789. /**/
  790. int
  791. bin_suspend(char *name, char **argv, char *ops, int func)
  792. {
  793.     /* won't suspend a login shell, unless forced */
  794.     if (islogin && !ops['f']) {
  795.     zwarnnam(name, "can't suspend login shell", NULL, 0);
  796.     return 1;
  797.     }
  798.     if (jobbing) {
  799.     /* stop ignoring signals */
  800.     signal_default(SIGTTIN);
  801.     signal_default(SIGTSTP);
  802.     signal_default(SIGTTOU);
  803.     }
  804.     /* suspend ourselves with a SIGTSTP */
  805.     kill(0, SIGTSTP);
  806.     if (jobbing) {
  807.     /* stay suspended */
  808.     while (gettygrp() != mypgrp) {
  809.         sleep(1);
  810.         if (gettygrp() != mypgrp)
  811.         kill(0, SIGTTIN);
  812.     }
  813.     /* restore signal handling */
  814.     signal_ignore(SIGTTOU);
  815.     signal_ignore(SIGTSTP);
  816.     signal_ignore(SIGTTIN);
  817.     }
  818.     return 0;
  819. }
  820.  
  821. /* find a job named s */
  822.  
  823. /**/
  824. int
  825. findjobnam(char *s)
  826. {
  827.     int jobnum;
  828.  
  829.     for (jobnum = MAXJOB - 1; jobnum >= 0; jobnum--)
  830.     if (!(jobtab[jobnum].stat & (STAT_SUBJOB | STAT_NOPRINT)) &&
  831.         jobtab[jobnum].stat && jobtab[jobnum].procs && jobnum != thisjob &&
  832.         jobtab[jobnum].procs->text && strpfx(s, jobtab[jobnum].procs->text))
  833.         return jobnum;
  834.     return -1;
  835. }
  836.  
  837. /* Convert a job specifier ("%%", "%1", "%foo", "%?bar?", etc.) *
  838.  * to a job number.                                             */
  839.  
  840. /**/
  841. int
  842. getjob(char *s, char *prog)
  843. {
  844.     int jobnum, returnval;
  845.  
  846.     /* if there is no %, treat as a name */
  847.     if (*s != '%')
  848.     goto jump;
  849.     s++;
  850.     /* "%%", "%+" and "%" all represent the current job */
  851.     if (*s == '%' || *s == '+' || !*s) {
  852.     if (curjob == -1) {
  853.         zwarnnam(prog, "no current job", NULL, 0);
  854.         returnval = -1;
  855.         goto done;
  856.     }
  857.     returnval = curjob;
  858.     goto done;
  859.     }
  860.     /* "%-" represents the previous job */
  861.     if (*s == '-') {
  862.     if (prevjob == -1) {
  863.         zwarnnam(prog, "no previous job", NULL, 0);
  864.         returnval = -1;
  865.         goto done;
  866.     }
  867.     returnval = prevjob;
  868.     goto done;
  869.     }
  870.     /* a digit here means we have a job number */
  871.     if (idigit(*s)) {
  872.     jobnum = atoi(s);
  873.     if (jobnum && jobnum < MAXJOB && jobtab[jobnum].stat &&
  874.         !(jobtab[jobnum].stat & STAT_SUBJOB) && jobnum != thisjob) {
  875.         returnval = jobnum;
  876.         goto done;
  877.     }
  878.     zwarnnam(prog, "%%%s: no such job", s, 0);
  879.     returnval = -1;
  880.     goto done;
  881.     }
  882.     /* "%?" introduces a search string */
  883.     if (*s == '?') {
  884.     struct process *pn;
  885.  
  886.     for (jobnum = MAXJOB - 1; jobnum >= 0; jobnum--)
  887.         if (jobtab[jobnum].stat && !(jobtab[jobnum].stat & STAT_SUBJOB) &&
  888.         jobnum != thisjob)
  889.         for (pn = jobtab[jobnum].procs; pn; pn = pn->next)
  890.             if (strstr(pn->text, s + 1)) {
  891.             returnval = jobnum;
  892.             goto done;
  893.             }
  894.     zwarnnam(prog, "job not found: %s", s, 0);
  895.     returnval = -1;
  896.     goto done;
  897.     }
  898.   jump:
  899.     /* anything else is a job name, specified as a string that begins the
  900.     job's command */
  901.     if ((jobnum = findjobnam(s)) != -1) {
  902.     returnval = jobnum;
  903.     goto done;
  904.     }
  905.     /* if we get here, it is because none of the above succeeded and went
  906.     to done */
  907.     zwarnnam(prog, "job not found: %s", s, 0);
  908.     returnval = -1;
  909.   done:
  910.     return returnval;
  911. }
  912.  
  913. /* This simple function indicates whether or not s may represent      *
  914.  * a number.  It returns true iff s consists purely of digits and     *
  915.  * minuses.  Note that minus may appear more than once, and the empty *
  916.  * string will produce a `true' response.                             */
  917.  
  918. /**/
  919. int
  920. isanum(char *s)
  921. {
  922.     while (*s == '-' || idigit(*s))
  923.     s++;
  924.     return *s == '\0';
  925. }
  926.  
  927. /**** directory-handling builtins ****/
  928.  
  929. int doprintdir = 0;        /* set in exec.c (for autocd) */
  930.  
  931. /* pwd: display the name of the current directory */
  932.  
  933. /**/
  934. int
  935. bin_pwd(char *name, char **argv, char *ops, int func)
  936. {
  937.     if (ops['r'] || isset(CHASELINKS))
  938.     printf("%s\n", zgetcwd());
  939.     else {
  940.     zputs(pwd, stdout);
  941.     putchar('\n');
  942.     }
  943.     return 0;
  944. }
  945.  
  946. /* dirs: list the directory stack, or replace it with a provided list */
  947.  
  948. /**/
  949. int
  950. bin_dirs(char *name, char **argv, char *ops, int func)
  951. {
  952.     LinkList l;
  953.  
  954.     /* with the -v option, provide a numbered list of directories, starting at
  955.     zero */
  956.     if (ops['v']) {
  957.     LinkNode node;
  958.     int pos = 1;
  959.  
  960.     printf("0\t");
  961.     fprintdir(pwd, stdout);
  962.     for (node = firstnode(dirstack); node; incnode(node)) {
  963.         printf("\n%d\t", pos++);
  964.         fprintdir(getdata(node), stdout);
  965.     }
  966.     putchar('\n');
  967.     return 0;
  968.     }
  969.     /* given no arguments, list the stack normally */
  970.     if (!*argv) {
  971.     printdirstack();
  972.     return 0;
  973.     }
  974.     /* replace the stack with the specified directories */
  975.     PERMALLOC {
  976.     l = newlinklist();
  977.     if (*argv) {
  978.         while (*argv)
  979.         addlinknode(l, ztrdup(*argv++));
  980.         freelinklist(dirstack, freestr);
  981.         dirstack = l;
  982.     }
  983.     } LASTALLOC;
  984.     return 0;
  985. }
  986.  
  987. /* cd, chdir, pushd, popd */
  988.  
  989. /* The main pwd changing function.  The real work is done by other     *
  990.  * functions.  cd_get_dest() does the initial argument processing;     *
  991.  * cd_do_chdir() actually changes directory, if possible; cd_new_pwd() *
  992.  * does the ancilliary processing associated with actually changing    *
  993.  * directory.                                                          */
  994.  
  995. /**/
  996. int
  997. bin_cd(char *nam, char **argv, char *ops, int func)
  998. {
  999.     LinkNode dir;
  1000.     struct stat st1, st2;
  1001.  
  1002.     doprintdir = (doprintdir == -1);
  1003.  
  1004.     PERMALLOC {
  1005.     pushnode(dirstack, ztrdup(pwd));
  1006.     if (!(dir = cd_get_dest(nam, argv, ops, func))) {
  1007.         zsfree(getlinknode(dirstack));
  1008.         LASTALLOC_RETURN 1;
  1009.     }
  1010.     } LASTALLOC;
  1011.     cd_new_pwd(func, dir);
  1012.  
  1013.     if (stat(unmeta(pwd), &st1) < 0) {
  1014.     zsfree(pwd);
  1015.     pwd = metafy(zgetcwd(), -1, META_REALLOC);
  1016.     } else if (stat(".", &st2) < 0)
  1017.     chdir(unmeta(pwd));
  1018.     else if (st1.st_ino != st2.st_ino || st1.st_dev != st2.st_dev) {
  1019.     if (isset(CHASELINKS)) {
  1020.         zsfree(pwd);
  1021.         pwd = metafy(zgetcwd(), -1, META_REALLOC);
  1022.     } else {
  1023.         chdir(unmeta(pwd));
  1024.     }
  1025.     }
  1026.     return 0;
  1027. }
  1028.  
  1029. /* Get directory to chdir to */
  1030.  
  1031. /**/
  1032. LinkNode
  1033. cd_get_dest(char *nam, char **argv, char *ops, int func)
  1034. {
  1035.     LinkNode dir = NULL;
  1036.     LinkNode target;
  1037.     char *dest;
  1038.  
  1039.     if (!argv[0]) {
  1040.     if (func == BIN_POPD && !nextnode(firstnode(dirstack))) {
  1041.         zwarnnam(nam, "directory stack empty", NULL, 0);
  1042.         return NULL;
  1043.     }
  1044.     if (func == BIN_PUSHD && unset(PUSHDTOHOME))
  1045.         dir = nextnode(firstnode(dirstack));
  1046.     if (dir)
  1047.         insertlinknode(dirstack, dir, getlinknode(dirstack));
  1048.     else if (func != BIN_POPD)
  1049.         pushnode(dirstack, ztrdup(home));
  1050.     } else if (!argv[1]) {
  1051.     int dd;
  1052.     char *end;
  1053.  
  1054.     doprintdir++;
  1055.     if (argv[0][1] && (argv[0][0] == '+' || argv[0][0] == '-')) {
  1056.         dd = zstrtol(argv[0] + 1, &end, 10); 
  1057.         if (*end == '\0') {
  1058.         if ((argv[0][0] == '+') ^ isset(PUSHDMINUS))
  1059.             for (dir = firstnode(dirstack); dir && dd; dd--, incnode(dir));
  1060.         else
  1061.             for (dir = lastnode(dirstack); dir != (LinkNode) dirstack && dd;
  1062.              dd--, dir = prevnode(dir)); 
  1063.         if (!dir || dir == (LinkNode) dirstack) {
  1064.             zwarnnam(nam, "no such entry in dir stack", NULL, 0);
  1065.             return NULL;
  1066.         }
  1067.         }
  1068.     }
  1069.     if (!dir)
  1070.         pushnode(dirstack, ztrdup(strcmp(argv[0], "-")
  1071.                       ? (doprintdir--, argv[0]) : oldpwd));
  1072.     } else {
  1073.     char *u, *d;
  1074.     int len1, len2, len3;
  1075.  
  1076.     if (!(u = strstr(pwd, argv[0]))) {
  1077.         zwarnnam(nam, "string not in pwd: %s", argv[0], 0);
  1078.         return NULL;
  1079.     }
  1080.     len1 = strlen(argv[0]);
  1081.     len2 = strlen(argv[1]);
  1082.     len3 = u - pwd;
  1083.     d = (char *)zalloc(len3 + len2 + strlen(u + len1) + 1);
  1084.     strncpy(d, pwd, len3);
  1085.     strcpy(d + len3, argv[1]);
  1086.     strcat(d, u + len1);
  1087.     pushnode(dirstack, d);
  1088.     doprintdir++;
  1089.     }
  1090.  
  1091.     target = dir;
  1092.     if (func == BIN_POPD) {
  1093.     if (!dir) {
  1094.         target = dir = firstnode(dirstack);
  1095.     } else if (dir != firstnode(dirstack)) {
  1096.         return dir;
  1097.     }
  1098.     dir = nextnode(dir);
  1099.     }
  1100.     if (!dir) {
  1101.     dir = firstnode(dirstack);
  1102.     }
  1103.     if (!(dest = cd_do_chdir(nam, getdata(dir)))) {
  1104.     if (!target)
  1105.         zsfree(getlinknode(dirstack));
  1106.     if (func == BIN_POPD)
  1107.         zsfree(remnode(dirstack, dir));
  1108.     return NULL;
  1109.     }
  1110.     if (dest != getdata(dir)) {
  1111.     zsfree(getdata(dir));
  1112.     setdata(dir, dest);
  1113.     }
  1114.     return target ? target : dir;
  1115. }
  1116.  
  1117. /* Change to given directory, if possible.  This function works out  *
  1118.  * exactly how the directory should be interpreted, including cdpath *
  1119.  * and CDABLEVARS.  For each possible interpretation of the given    *
  1120.  * path, this calls cd_try_chdir(), which attempts to chdir to that  *
  1121.  * particular path.                                                  */
  1122.  
  1123. /**/
  1124. char *
  1125. cd_do_chdir(char *cnam, char *dest)
  1126. {
  1127.     char **pp, *ret;
  1128.     int hasdot = 0, eno = ENOENT;
  1129.     /* nocdpath indicates that cdpath should not be used.  This is the case iff
  1130.     dest is a relative path whose first segment is . or .., but if the path is
  1131.     absolute then cdpath won't be used anyway. */
  1132.     int nocdpath = dest[0] == '.' &&
  1133.     (dest[1] == '/' || !dest[1] || (dest[1] == '.' &&
  1134.                     (dest[2] == '/' || !dest[2])));
  1135.  
  1136.     /* if we have an absolute path, use it as-is only */
  1137.     if (*dest == '/') {
  1138.     if ((ret = cd_try_chdir(NULL, dest)))
  1139.         return ret;
  1140.     zwarnnam(cnam, "%e: %s", dest, errno);
  1141.     return NULL;
  1142.     }
  1143.  
  1144.     /* if cdpath is being used, check it for . */
  1145.     if (!nocdpath)
  1146.     for (pp = cdpath; *pp; pp++)
  1147.         if (!(*pp)[0] || ((*pp)[0] == '.' && (*pp)[1] == '\0'))
  1148.         hasdot = 1;
  1149.     /* if there is no . in cdpath (or it is not being used), try the directory
  1150.     as-is (i.e. from .) */
  1151.     if (!hasdot) {
  1152.     if ((ret = cd_try_chdir(NULL, dest)))
  1153.         return ret;
  1154.     if (errno != ENOENT)
  1155.         eno = errno;
  1156.     }
  1157.     /* if cdpath is being used, try given directory relative to each element in
  1158.     cdpath in turn */
  1159.     if (!nocdpath)
  1160.     for (pp = cdpath; *pp; pp++) {
  1161.         if ((ret = cd_try_chdir(*pp, dest))) {
  1162.         if (strcmp(*pp, ".")) {
  1163.             doprintdir++;
  1164.         }
  1165.         return ret;
  1166.         }
  1167.         if (errno != ENOENT)
  1168.         eno = errno;
  1169.     }
  1170.  
  1171.     /* handle the CDABLEVARS option */
  1172.     if ((ret = cd_able_vars(dest))) {
  1173.     if ((ret = cd_try_chdir(NULL, ret))) {
  1174.         doprintdir++;
  1175.         return ret;
  1176.     }
  1177.     if (errno != ENOENT)
  1178.         eno = errno;
  1179.     }
  1180.  
  1181.     /* If we got here, it means that we couldn't chdir to any of the
  1182.     multitudinous possible paths allowed by zsh.  We've run out of options!
  1183.     Add more here! */
  1184.     zwarnnam(cnam, "%e: %s", dest, eno);
  1185.     return NULL;
  1186. }
  1187.  
  1188. /* If the CDABLEVARS option is set, return the new *
  1189.  * interpretation of the given path.               */
  1190.  
  1191. /**/
  1192. char *
  1193. cd_able_vars(char *s)
  1194. {
  1195.     char *rest, save;
  1196.  
  1197.     if (isset(CDABLEVARS)) {
  1198.     for (rest = s; *rest && *rest != '/'; rest++);
  1199.     save = *rest;
  1200.     *rest = 0;
  1201.     s = getnameddir(s);
  1202.     *rest = save;
  1203.  
  1204.     if (s && *rest)
  1205.         s = dyncat(s, rest);
  1206.  
  1207.     return s;
  1208.     }
  1209.     return NULL;
  1210. }
  1211.  
  1212. /* Attempt to change to a single given directory.  The directory,    *
  1213.  * for the convenience of the calling function, may be provided in   *
  1214.  * two parts, which must be concatenated before attempting to chdir. *
  1215.  * Returns NULL if the chdir fails.  If the directory change is      *
  1216.  * possible, it is performed, and a pointer to the new full pathname *
  1217.  * is returned.                                                      */
  1218.  
  1219. /**/
  1220. char *
  1221. cd_try_chdir(char *pfix, char *dest)
  1222. {
  1223.     char buf[PATH_MAX], buf2[PATH_MAX];
  1224.     char *s;
  1225.     int dotsct;
  1226.  
  1227.     /* handle directory prefix */
  1228.     if (pfix && *pfix) {
  1229.     if (strlen(dest) + strlen(pfix) + 1 >= PATH_MAX)
  1230.         return NULL;
  1231.     sprintf(buf, "%s/%s", (!strcmp("/", pfix)) ? "" : pfix, dest);
  1232.     } else {
  1233.     if (strlen(dest) >= PATH_MAX)
  1234.         return NULL;
  1235.     strcpy(buf, dest);
  1236.     }
  1237.     /* Normalise path.  See the definition of fixdir() for what this means. */
  1238.     dotsct = fixdir(buf2, buf);
  1239.  
  1240.     /* if the path is absolute, the test and return value are (relatively)
  1241.     simple */
  1242.     if (buf2[0] == '/')
  1243.     return (chdir(unmeta(buf)) == -1) ? NULL : ztrdup(buf2);
  1244.     /* If the path is a simple `downward' relative path, the test is again
  1245.     fairly simple.  The relative path must be added to the end of the current
  1246.     directory. */
  1247.     if (!dotsct) {
  1248.     if (chdir(unmeta(buf)) == -1)
  1249.         return NULL;
  1250.     if (*buf2) {
  1251.         if (strlen(pwd) + strlen(buf2) + 1 >= PATH_MAX)
  1252.         return NULL;
  1253.         sprintf(buf, "%s/%s", (!strcmp("/", pwd)) ? "" : pwd, buf2);
  1254.     } else
  1255.         strcpy(buf, pwd);
  1256.     return ztrdup(buf);
  1257.     }
  1258.     /* There are one or more .. segments at the beginning of the relative path.
  1259.     A corresponding number of segments must be removed from the end of the
  1260.     current directory before the downward relative path is appended. */
  1261.     strcpy(buf, pwd);
  1262.     s = buf + strlen(buf) - 1;
  1263.     while (dotsct--)
  1264.     while (s != buf)
  1265.         if (*--s == '/')
  1266.         break;
  1267.     if (s == buf || *buf2)
  1268.     s++;
  1269.     strcpy(s, buf2);
  1270.     /* For some reason, this chdir must be attempted with both the newly
  1271.     created path and the original non-normalised version. */
  1272.     if (chdir(unmeta(buf)) != -1 || chdir(unmeta(dest)) != -1)
  1273.     return ztrdup(buf);
  1274.     return NULL;
  1275. }
  1276.  
  1277. /* do the extra processing associated with changing directory */
  1278.  
  1279. /**/
  1280. void
  1281. cd_new_pwd(int func, LinkNode dir)
  1282. {
  1283.     Param pm;
  1284.     List l;
  1285.     char *new_pwd, *s;
  1286.     int dirstacksize;
  1287.  
  1288.     if (func == BIN_PUSHD)
  1289.     rolllist(dirstack, dir);
  1290.     new_pwd = remnode(dirstack, dir);
  1291.  
  1292.     if (func == BIN_POPD && firstnode(dirstack)) {
  1293.     zsfree(new_pwd);
  1294.     new_pwd = getlinknode(dirstack);
  1295.     } else if (func == BIN_CD && unset(AUTOPUSHD))
  1296.     zsfree(getlinknode(dirstack));
  1297.  
  1298.     if (isset(CHASELINKS)) {
  1299.     s = new_pwd;
  1300.     new_pwd = findpwd(s);
  1301.     zsfree(s);
  1302.     }
  1303.     if (isset(PUSHDIGNOREDUPS)) {
  1304.     LinkNode n; 
  1305.     for (n = firstnode(dirstack); n; incnode(n)) {
  1306.         if (!strcmp(new_pwd, getdata(n))) {
  1307.         zsfree(remnode(dirstack, n));
  1308.         break;
  1309.         }
  1310.     }
  1311.     }
  1312.  
  1313.     /* shift around the pwd variables, to make oldpwd and pwd relate to the
  1314.     current (i.e. new) pwd */
  1315.     zsfree(oldpwd);
  1316.     oldpwd = pwd;
  1317.     pwd = new_pwd;
  1318.     /* update the PWD and OLDPWD shell parameters */
  1319.     if ((pm = (Param) paramtab->getnode(paramtab, "PWD")) &&
  1320.     (pm->flags & PM_EXPORTED) && pm->env)
  1321.     pm->env = replenv(pm->env, pwd);
  1322.     if ((pm = (Param) paramtab->getnode(paramtab, "OLDPWD")) &&
  1323.     (pm->flags & PM_EXPORTED) && pm->env)
  1324.     pm->env = replenv(pm->env, oldpwd);
  1325.     if (unset(PUSHDSILENT) && func != BIN_CD && isset(INTERACTIVE))
  1326.     printdirstack();
  1327.     else if (doprintdir) {
  1328.     fprintdir(pwd, stdout);
  1329.         putchar('\n');
  1330.     }
  1331.  
  1332.     /* execute the chpwd function */
  1333.     if ((l = getshfunc("chpwd"))) {
  1334.     fflush(stdout);
  1335.     fflush(stderr);
  1336.     doshfunc(l, NULL, 0, 1);
  1337.     }
  1338.  
  1339.     dirstacksize = getiparam("DIRSTACKSIZE");
  1340.     /* handle directory stack sizes out of range */
  1341.     if (dirstacksize > 0) {
  1342.     int remove = countlinknodes(dirstack) -
  1343.              (dirstacksize < 2 ? 2 : dirstacksize);
  1344.     while (remove-- >= 0)
  1345.         zsfree(remnode(dirstack, lastnode(dirstack)));
  1346.     }
  1347. }
  1348.  
  1349. /* Print the directory stack */
  1350.  
  1351. /**/
  1352. void
  1353. printdirstack(void)
  1354. {
  1355.     LinkNode node;
  1356.  
  1357.     fprintdir(pwd, stdout);
  1358.     for (node = firstnode(dirstack); node; incnode(node)) {
  1359.     putchar(' ');
  1360.     fprintdir(getdata(node), stdout);
  1361.     }
  1362.     putchar('\n');
  1363. }
  1364.  
  1365. /* Normalise a path.  Segments consisting of ., and foo/.. combinations, *
  1366.  * are removed.  The number of .. segments at the beginning of the       *
  1367.  * path is returned.  The normalised path, minus leading ..s, is copied  *
  1368.  * to dest.                                                              */
  1369.  
  1370. /**/
  1371. int
  1372. fixdir(char *dest, char *src)
  1373. {
  1374.     int ct = 0;
  1375.     char *d0 = dest;
  1376.  
  1377. /*** if have RFS superroot directory ***/
  1378. #ifdef HAVE_SUPERROOT
  1379.     /* allow /.. segments to remain */
  1380.     while (*src == '/' && src[1] == '.' && src[2] == '.' &&
  1381.       (!src[3] || src[3] == '/')) {
  1382.     *dest++ = '/';
  1383.     *dest++ = '.';
  1384.     *dest++ = '.';
  1385.     src += 3;
  1386.     }
  1387. #endif
  1388.  
  1389.     for (;;) {
  1390.     /* compress multiple /es into single */
  1391.     if (*src == '/') {
  1392.         *dest++ = *src++;
  1393.         while (*src == '/')
  1394.         src++;
  1395.     }
  1396.     /* if we are at the end of the input path, remove a trailing / (if it
  1397.     exists), and return ct */
  1398.     if (!*src) {
  1399.         while (dest > d0 + 1 && dest[-1] == '/')
  1400.         dest--;
  1401.         *dest = '\0';
  1402.         return ct;
  1403.     }
  1404.     if (src[0] == '.' && src[1] == '.' &&
  1405.       (src[2] == '\0' || src[2] == '/')) {
  1406.         /* remove a foo/.. combination, or increment ct, as appropriate */
  1407.         if (dest > d0 + 1) {
  1408.         for (dest--; dest > d0 + 1 && dest[-1] != '/'; dest--);
  1409.         if (dest[-1] != '/')
  1410.             dest--;
  1411.         } else
  1412.         ct++;
  1413.         src++;
  1414.         while (*++src == '/');
  1415.     } else if (src[0] == '.' && (src[1] == '/' || src[1] == '\0')) {
  1416.         /* skip a . section */
  1417.         while (*++src == '/');
  1418.     } else {
  1419.         /* copy a normal segment into the output */
  1420.         while (*src != '/' && *src != '\0')
  1421.         *dest++ = *src++;
  1422.     }
  1423.     }
  1424. }
  1425.  
  1426. /**** compctl builtin ****/
  1427.  
  1428. #define COMP_LIST    (1<<0)    /* -L */
  1429. #define COMP_COMMAND    (1<<1)    /* -C */
  1430. #define COMP_DEFAULT    (1<<2)    /* -D */
  1431. #define COMP_FIRST    (1<<3)    /* -T */
  1432. #define COMP_REMOVE    (1<<4)
  1433.  
  1434. #define COMP_SPECIAL (COMP_COMMAND|COMP_DEFAULT|COMP_FIRST)
  1435.  
  1436. /* Flag for listing, command, default, or first completion */
  1437. static int cclist;
  1438.  
  1439. /* Mask for determining what to print */
  1440. static unsigned long showmask = 0;
  1441.  
  1442. /* Main entry point for the `compctl' builtin */
  1443.  
  1444. /**/
  1445. int
  1446. bin_compctl(char *name, char **argv, char *ops, int func)
  1447. {
  1448.     Compctl cc = NULL;
  1449.     int ret = 0;
  1450.  
  1451.     /* clear static flags */
  1452.     cclist = 0;
  1453.     showmask = 0;
  1454.  
  1455.     /* Parse all the arguments */
  1456.     if (*argv) {
  1457.     cc = (Compctl) zcalloc(sizeof(*cc));
  1458.     if (get_compctl(name, &argv, cc, 1, 0)) {
  1459.         freecompctl(cc);
  1460.         return 1;
  1461.     }
  1462.  
  1463.     /* remember flags for printing */
  1464.     showmask = cc->mask;
  1465.     if ((showmask & CC_EXCMDS) && !(showmask & CC_DISCMDS))
  1466.         showmask &= ~CC_EXCMDS;
  1467.  
  1468.     /* if no command arguments or just listing, we don't want cc */
  1469.     if (!*argv || (cclist & COMP_LIST))
  1470.         freecompctl(cc);
  1471.     }
  1472.  
  1473.     /* If no commands and no -C, -T, or -D, print all the compctl's *
  1474.      * If some flags (other than -C, -T, or -D) were given, then    *
  1475.      * only print compctl containing those flags.                   */
  1476.     if (!*argv && !(cclist & COMP_SPECIAL)) {
  1477.     scanhashtable(compctltab, 1, 0, 0, compctltab->printnode, 0);
  1478.     printcompctl((cclist & COMP_LIST) ? "" : "COMMAND", &cc_compos, 0);
  1479.     printcompctl((cclist & COMP_LIST) ? "" : "DEFAULT", &cc_default, 0);
  1480.      printcompctl((cclist & COMP_LIST) ? "" : "FIRST", &cc_first, 0);
  1481.     return ret;
  1482.     }
  1483.  
  1484.     /* If we're listing and we've made it to here, then there are arguments *
  1485.      * or a COMP_SPECIAL flag (-D, -C, -T), so print only those.            */
  1486.     if (cclist & COMP_LIST) {
  1487.     HashNode hn;
  1488.     char **ptr;
  1489.  
  1490.     showmask = 0;
  1491.     for (ptr = argv; *ptr; ptr++) {
  1492.         if ((hn = compctltab->getnode(compctltab, *ptr))) {
  1493.         compctltab->printnode(hn, 0);
  1494.         } else {
  1495.         zwarnnam(name, "no compctl defined for %s", *ptr, 0);
  1496.         ret = 1;
  1497.         }
  1498.     }
  1499.     if (cclist & COMP_COMMAND)
  1500.         printcompctl("", &cc_compos, 0);
  1501.     if (cclist & COMP_DEFAULT)
  1502.         printcompctl("", &cc_default, 0);
  1503.     if (cclist & COMP_FIRST)
  1504.         printcompctl("", &cc_first, 0);
  1505.     return ret;
  1506.     }
  1507.  
  1508.     /* Assign the compctl to the commands given */
  1509.     if (*argv) {
  1510.     if(cclist & COMP_SPECIAL)
  1511.         /* Ideally we'd handle this properly, setting both the *
  1512.          * special and normal completions.  For the moment,    *
  1513.          * this is better than silently failing.               */
  1514.         zwarnnam(name, "extraneous commands ignored", NULL, 0);
  1515.     else
  1516.         compctl_process_cc(argv, cc);
  1517.     }
  1518.  
  1519.     return ret;
  1520. }
  1521.  
  1522. /* Parse the basic flags for `compctl' */
  1523.  
  1524. /**/
  1525. int
  1526. get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
  1527. {
  1528.     /* Parse the basic flags for completion:
  1529.      * first is a flag that we are not in extended completion,
  1530.      * while hx indicates or (+) completion (need to know for
  1531.      * default and command completion as the initial compctl is special). 
  1532.      * cct is a temporary just to hold flags; it never needs freeing.
  1533.      */
  1534.     struct compctl cct;
  1535.     char **argv = *av;
  1536.     int ready = 0, hx = 0;
  1537.  
  1538.     /* Handle `compctl + foo ...' specially:  turn it into
  1539.      * a default compctl by removing it from the hash table.
  1540.      */
  1541.     if (first && argv[0][0] == '+' && !argv[0][1] &&
  1542.     !(argv[1] && argv[1][0] == '-' && argv[1][1])) {
  1543.     argv++;
  1544.     if(argv[0] && argv[0][0] == '-')
  1545.         argv++;
  1546.     *av = argv;
  1547.     freecompctl(cc);
  1548.      cclist = COMP_REMOVE;
  1549.     return 0;
  1550.     }
  1551.  
  1552.     memset((void *)&cct, 0, sizeof(cct));
  1553.  
  1554.     /* Loop through the flags until we have no more:        *
  1555.      * those with arguments are not properly allocated yet, *
  1556.      * we just hang on to the argument that was passed.     */
  1557.     for (; !ready && argv[0] && argv[0][0] == '-' && (argv[0][1] || !first);) {
  1558.     if (!argv[0][1])
  1559.         *argv = "-+";
  1560.     while (!ready && *++(*argv)) {
  1561.         if(**argv == Meta)
  1562.         *++*argv ^= 32;
  1563.         switch (**argv) {
  1564.         case 'f':
  1565.         cct.mask |= CC_FILES;
  1566.         break;
  1567.         case 'c':
  1568.         cct.mask |= CC_COMMPATH;
  1569.         break;
  1570.         case 'm':
  1571.         cct.mask |= CC_EXTCMDS;
  1572.         break;
  1573.         case 'w':
  1574.         cct.mask |= CC_RESWDS;
  1575.         break;
  1576.         case 'o':
  1577.         cct.mask |= CC_OPTIONS;
  1578.         break;
  1579.         case 'v':
  1580.         cct.mask |= CC_VARS;
  1581.         break;
  1582.         case 'b':
  1583.         cct.mask |= CC_BINDINGS;
  1584.         break;
  1585.         case 'A':
  1586.         cct.mask |= CC_ARRAYS;
  1587.         break;
  1588.         case 'I':
  1589.         cct.mask |= CC_INTVARS;
  1590.         break;
  1591.         case 'F':
  1592.         cct.mask |= CC_SHFUNCS;
  1593.         break;
  1594.         case 'p':
  1595.         cct.mask |= CC_PARAMS;
  1596.         break;
  1597.         case 'E':
  1598.         cct.mask |= CC_ENVVARS;
  1599.         break;
  1600.         case 'j':
  1601.         cct.mask |= CC_JOBS;
  1602.         break;
  1603.         case 'r':
  1604.         cct.mask |= CC_RUNNING;
  1605.         break;
  1606.         case 'z':
  1607.         cct.mask |= CC_STOPPED;
  1608.         break;
  1609.         case 'B':
  1610.         cct.mask |= CC_BUILTINS;
  1611.         break;
  1612.         case 'a':
  1613.         cct.mask |= CC_ALREG | CC_ALGLOB;
  1614.         break;
  1615.         case 'R':
  1616.         cct.mask |= CC_ALREG;
  1617.         break;
  1618.         case 'G':
  1619.         cct.mask |= CC_ALGLOB;
  1620.         break;
  1621.         case 'u':
  1622.         cct.mask |= CC_USERS;
  1623.         break;
  1624.         case 'd':
  1625.         cct.mask |= CC_DISCMDS;
  1626.         break;
  1627.         case 'e':
  1628.         cct.mask |= CC_EXCMDS;
  1629.         break;
  1630.         case 'N':
  1631.         cct.mask |= CC_SCALARS;
  1632.         break;
  1633.         case 'O':
  1634.         cct.mask |= CC_READONLYS;
  1635.         break;
  1636.         case 'Z':
  1637.         cct.mask |= CC_SPECIALS;
  1638.         break;
  1639.         case 'q':
  1640.         cct.mask |= CC_REMOVE;
  1641.         break;
  1642.         case 'U':
  1643.         cct.mask |= CC_DELETE;
  1644.         break;
  1645.         case 'n':
  1646.         cct.mask |= CC_NAMED;
  1647.         break;
  1648.         case 'Q':
  1649.         cct.mask |= CC_QUOTEFLAG;
  1650.         break;
  1651.         case 'k':
  1652.         if ((*argv)[1]) {
  1653.             cct.keyvar = (*argv) + 1;
  1654.             *argv = "" - 1;
  1655.         } else if (!argv[1]) {
  1656.             zwarnnam(name, "variable name expected after -%c", NULL,
  1657.                 **argv);
  1658.             return 1;
  1659.         } else {
  1660.             cct.keyvar = *++argv;
  1661.             *argv = "" - 1;
  1662.         }
  1663.         break;
  1664.         case 'K':
  1665.         if ((*argv)[1]) {
  1666.             cct.func = (*argv) + 1;
  1667.             *argv = "" - 1;
  1668.         } else if (!argv[1]) {
  1669.             zwarnnam(name, "function name expected after -%c", NULL,
  1670.                 **argv);
  1671.             return 1;
  1672.         } else {
  1673.             cct.func = *++argv;
  1674.             *argv = "" - 1;
  1675.         }
  1676.         break;
  1677.         case 'X':
  1678.         if ((*argv)[1]) {
  1679.             cct.explain = (*argv) + 1;
  1680.             *argv = "" - 1;
  1681.         } else if (!argv[1]) {
  1682.             zwarnnam(name, "string expected after -%c", NULL, **argv);
  1683.             return 1;
  1684.         } else {
  1685.             cct.explain = *++argv;
  1686.             *argv = "" - 1;
  1687.         }
  1688.         break;
  1689.         case 'P':
  1690.         if ((*argv)[1]) {
  1691.             cct.prefix = (*argv) + 1;
  1692.             *argv = "" - 1;
  1693.         } else if (!argv[1]) {
  1694.             zwarnnam(name, "string expected after -%c", NULL, **argv);
  1695.             return 1;
  1696.         } else {
  1697.             cct.prefix = *++argv;
  1698.             *argv = "" - 1;
  1699.         }
  1700.         break;
  1701.         case 'S':
  1702.         if ((*argv)[1]) {
  1703.             cct.suffix = (*argv) + 1;
  1704.             *argv = "" - 1;
  1705.         } else if (!argv[1]) {
  1706.             zwarnnam(name, "string expected after -%c", NULL, **argv);
  1707.             return 1;
  1708.         } else {
  1709.             cct.suffix = *++argv;
  1710.             *argv = "" - 1;
  1711.         }
  1712.         break;
  1713.         case 'g':
  1714.         if ((*argv)[1]) {
  1715.             cct.glob = (*argv) + 1;
  1716.             *argv = "" - 1;
  1717.         } else if (!argv[1]) {
  1718.             zwarnnam(name, "glob pattern expected after -%c", NULL,
  1719.                 **argv);
  1720.             return 1;
  1721.         } else {
  1722.             cct.glob = *++argv;
  1723.             *argv = "" - 1;
  1724.         }
  1725.         break;
  1726.         case 's':
  1727.         if ((*argv)[1]) {
  1728.             cct.str = (*argv) + 1;
  1729.             *argv = "" - 1;
  1730.         } else if (!argv[1]) {
  1731.             zwarnnam(name, "command string expected after -%c", NULL,
  1732.                 **argv);
  1733.             return 1;
  1734.         } else {
  1735.             cct.str = *++argv;
  1736.             *argv = "" - 1;
  1737.         }
  1738.         break;
  1739.         case 'l':
  1740.         if ((*argv)[1]) {
  1741.             cct.subcmd = (*argv) + 1;
  1742.             *argv = "" - 1;
  1743.         } else if (!argv[1]) {
  1744.             zwarnnam(name, "command name expected after -%c", NULL,
  1745.                 **argv);
  1746.             return 1;
  1747.         } else {
  1748.             cct.subcmd = *++argv;
  1749.             *argv = "" - 1;
  1750.         }
  1751.         break;
  1752.         case 'H':
  1753.         if ((*argv)[1])
  1754.             cct.hnum = atoi((*argv) + 1);
  1755.         else if (argv[1])
  1756.             cct.hnum = atoi(*++argv);
  1757.         else {
  1758.             zwarnnam(name, "number expected after -%c", NULL,
  1759.                 **argv);
  1760.             return 1;
  1761.         }
  1762.         if (!argv[1]) {
  1763.             zwarnnam(name, "missing pattern after -%c", NULL,
  1764.                 **argv);
  1765.             return 1;
  1766.         }
  1767.         cct.hpat = *++argv;
  1768.         if (cct.hnum < 1)
  1769.             cct.hnum = 0;
  1770.         if (*cct.hpat == '*' && !cct.hpat[1])
  1771.             cct.hpat = "";
  1772.         *argv = "" - 1;
  1773.         break;
  1774.         case 'C':
  1775.         if (first && !hx) {
  1776.             cclist |= COMP_COMMAND;
  1777.         } else {
  1778.             zwarnnam(name, "misplaced command completion (-C) flag",
  1779.                 NULL, 0);
  1780.             return 1;
  1781.         }
  1782.         break;
  1783.         case 'D':
  1784.         if (first && !hx) {
  1785.             isdef = 1;
  1786.             cclist |= COMP_DEFAULT;
  1787.         } else {
  1788.             zwarnnam(name, "misplaced default completion (-D) flag",
  1789.                 NULL, 0);
  1790.             return 1;
  1791.         }
  1792.         break;
  1793.          case 'T':
  1794.               if (first && !hx) {
  1795.              cclist |= COMP_FIRST;
  1796.          } else {
  1797.              zwarnnam(name, "misplaced first completion (-T) flag",
  1798.                  NULL, 0);
  1799.              return 1;
  1800.          }
  1801.          break;
  1802.         case 'L':
  1803.         if (!first || hx) {
  1804.             zwarnnam(name, "illegal use of -L flag", NULL, 0);
  1805.             return 1;
  1806.         }
  1807.         cclist |= COMP_LIST;
  1808.         break;
  1809.         case 'x':
  1810.         if (!argv[1]) {
  1811.             zwarnnam(name, "condition expected after -%c", NULL,
  1812.                 **argv);
  1813.             return 1;
  1814.         }
  1815.         if (first) {
  1816.             argv++;
  1817.             if (get_xcompctl(name, &argv, &cct, isdef)) {
  1818.             if (cct.ext)
  1819.                 freecompctl(cct.ext);
  1820.             return 1;
  1821.             }
  1822.             ready = 2;
  1823.         } else {
  1824.             zwarnnam(name, "recursive extended completion not allowed",
  1825.                 NULL, 0);
  1826.             return 1;
  1827.         }
  1828.         break;
  1829.         default:
  1830.         if (!first && (**argv == '-' || **argv == '+'))
  1831.             (*argv)--, argv--, ready = 1;
  1832.         else {
  1833.             zwarnnam(name, "bad option: -%c", NULL, **argv);
  1834.             return 1;
  1835.         }
  1836.         }
  1837.     }
  1838.  
  1839.     if (*++argv && (!ready || ready == 2) &&
  1840.         **argv == '+' && !argv[0][1]) {
  1841.         /* There's an alternative (+) completion:  assign
  1842.          * what we have so far before moving on to that.
  1843.          */
  1844.         if (cc_assign(name, &cc, &cct, first && !hx))
  1845.         return 1;
  1846.  
  1847.         hx = 1;
  1848.         ready = 0;
  1849.  
  1850.         if (!*++argv || **argv != '-' ||
  1851.         (**argv == '-' && (!argv[0][1] ||
  1852.                    (argv[0][1] == '-' && !argv[0][2])))) {
  1853.         /* No argument to +, which means do default completion */
  1854.         if (isdef)
  1855.             zwarnnam(name,
  1856.                 "recursive xor'd default completions not allowed",
  1857.                 NULL, 0);
  1858.         else
  1859.             cc->xor = &cc_default;
  1860.         } else {
  1861.         /* more flags follow:  prepare to loop again */
  1862.         cc->xor = (Compctl) zcalloc(sizeof(*cc));
  1863.         cc = cc->xor;
  1864.         memset((void *)&cct, 0, sizeof(cct));
  1865.         }
  1866.     }
  1867.     }
  1868.     if (!ready && *argv && **argv == '-')
  1869.     argv++;
  1870.  
  1871.     if (! (cct.mask & (CC_EXCMDS | CC_DISCMDS)))
  1872.     cct.mask |= CC_EXCMDS;
  1873.  
  1874.     /* assign the last set of flags we parsed */
  1875.     if (cc_assign(name, &cc, &cct, first && !hx))
  1876.     return 1;
  1877.  
  1878.     *av = argv;
  1879.  
  1880.     return 0;
  1881. }
  1882.  
  1883. /* Handle the -x ... -- part of compctl. */
  1884.  
  1885. /**/
  1886. int
  1887. get_xcompctl(char *name, char ***av, Compctl cc, int isdef)
  1888. {
  1889.     char **argv = *av, *t, *tt, sav;
  1890.     int n, l = 0, ready = 0;
  1891.     Compcond m, c, o;
  1892.     Compctl *next = &(cc->ext);
  1893.  
  1894.     while (!ready) {
  1895.     /* o keeps track of or's, m remembers the starting condition,
  1896.      * c is the current condition being parsed
  1897.      */
  1898.     o = m = c = (Compcond) zcalloc(sizeof(*c));
  1899.     /* Loop over each condition:  something like 's[...][...], p[...]' */
  1900.     for (t = *argv; *t;) {
  1901.         while (*t == ' ')
  1902.         t++;
  1903.         /* First get the condition code */
  1904.         switch (*t) {
  1905.         case 's':
  1906.         c->type = CCT_CURSUF;
  1907.         break;
  1908.         case 'S':
  1909.         c->type = CCT_CURPRE;
  1910.         break;
  1911.         case 'p':
  1912.         c->type = CCT_POS;
  1913.         break;
  1914.         case 'c':
  1915.         c->type = CCT_CURSTR;
  1916.         break;
  1917.         case 'C':
  1918.         c->type = CCT_CURPAT;
  1919.         break;
  1920.         case 'w':
  1921.         c->type = CCT_WORDSTR;
  1922.         break;
  1923.         case 'W':
  1924.         c->type = CCT_WORDPAT;
  1925.         break;
  1926.         case 'n':
  1927.         c->type = CCT_CURSUB;
  1928.         break;
  1929.         case 'N':
  1930.         c->type = CCT_CURSUBC;
  1931.         break;
  1932.         case 'm':
  1933.         c->type = CCT_NUMWORDS;
  1934.         break;
  1935.         case 'r':
  1936.         c->type = CCT_RANGESTR;
  1937.         break;
  1938.         case 'R':
  1939.         c->type = CCT_RANGEPAT;
  1940.         break;
  1941.         default:
  1942.         t[1] = '\0';
  1943.         zwarnnam(name, "unknown condition code: %s", t, 0);
  1944.         zfree(m, sizeof(struct compcond));
  1945.  
  1946.         return 1;
  1947.         }
  1948.         /* Now get the arguments in square brackets */
  1949.         if (t[1] != '[') {
  1950.         t[1] = '\0';
  1951.         zwarnnam(name, "expected condition after condition code: %s", t, 0);
  1952.         zfree(m, sizeof(struct compcond));
  1953.  
  1954.         return 1;
  1955.         }
  1956.         t++;
  1957.         /* First count how many or'd arguments there are,
  1958.          * marking the active ]'s and ,'s with unprintable characters.
  1959.          */
  1960.         for (n = 0, tt = t; *tt == '['; n++) {
  1961.         for (l = 1, tt++; *tt && l; tt++)
  1962.             if (*tt == '\\' && tt[1])
  1963.             tt++;
  1964.             else if (*tt == '[')
  1965.             l++;
  1966.             else if (*tt == ']')
  1967.             l--;
  1968.             else if (l == 1 && *tt == ',')
  1969.             *tt = '\201';
  1970.         if (tt[-1] == ']')
  1971.             tt[-1] = '\200';
  1972.         }
  1973.  
  1974.         if (l) {
  1975.         t[1] = '\0';
  1976.         zwarnnam(name, "error after condition code: %s", t, 0);
  1977.         zfree(m, sizeof(struct compcond));
  1978.  
  1979.         return 1;
  1980.         }
  1981.         c->n = n;
  1982.  
  1983.         /* Allocate space for all the arguments of the conditions */
  1984.         if (c->type == CCT_POS ||
  1985.         c->type == CCT_NUMWORDS) {
  1986.         c->u.r.a = (int *)zcalloc(n * sizeof(int));
  1987.         c->u.r.b = (int *)zcalloc(n * sizeof(int));
  1988.         } else if (c->type == CCT_CURSUF ||
  1989.                c->type == CCT_CURPRE)
  1990.         c->u.s.s = (char **)zcalloc(n * sizeof(char *));
  1991.  
  1992.         else if (c->type == CCT_RANGESTR ||
  1993.              c->type == CCT_RANGEPAT) {
  1994.         c->u.l.a = (char **)zcalloc(n * sizeof(char *));
  1995.         c->u.l.b = (char **)zcalloc(n * sizeof(char *));
  1996.         } else {
  1997.         c->u.s.p = (int *)zcalloc(n * sizeof(int));
  1998.         c->u.s.s = (char **)zcalloc(n * sizeof(char *));
  1999.         }
  2000.         /* Now loop over the actual arguments */
  2001.         for (l = 0; *t == '['; l++, t++) {
  2002.         for (t++; *t && *t == ' '; t++);
  2003.         tt = t;
  2004.         if (c->type == CCT_POS ||
  2005.             c->type == CCT_NUMWORDS) {
  2006.             /* p[...] or m[...]:  one or two numbers expected */
  2007.             for (; *t && *t != '\201' && *t != '\200'; t++);
  2008.             if (!(sav = *t)) {
  2009.             zwarnnam(name, "error in condition", NULL, 0);
  2010.             freecompcond(m);
  2011.             return 1;
  2012.             }
  2013.             *t = '\0';
  2014.             c->u.r.a[l] = atoi(tt);
  2015.             /* Second argument is optional:  see if it's there */
  2016.             if (sav == '\200')
  2017.             /* no:  copy first argument */
  2018.             c->u.r.b[l] = c->u.r.a[l];
  2019.             else {
  2020.             tt = ++t;
  2021.             for (; *t && *t != '\200'; t++);
  2022.             if (!*t) {
  2023.                 zwarnnam(name, "error in condition", NULL, 0);
  2024.                 freecompcond(m);
  2025.                 return 1;
  2026.             }
  2027.             *t = '\0';
  2028.             c->u.r.b[l] = atoi(tt);
  2029.             }
  2030.         } else if (c->type == CCT_CURSUF ||
  2031.                c->type == CCT_CURPRE) {
  2032.             /* -s[..] or -S[..]:  single string expected */
  2033.             for (; *t && *t != '\200'; t++)
  2034.             if (*t == '\201')
  2035.                 *t = ',';
  2036.             if (!*t) {
  2037.             zwarnnam(name, "error in condition", NULL, 0);
  2038.             freecompcond(m);
  2039.             return 1;
  2040.             }
  2041.             *t = '\0';
  2042.             c->u.s.s[l] = ztrdup(tt);
  2043.         } else if (c->type == CCT_RANGESTR ||
  2044.                c->type == CCT_RANGEPAT) {
  2045.             /* -r[..,..] or -R[..,..]:  two strings expected */
  2046.             for (; *t && *t != '\201'; t++);
  2047.             if (!*t) {
  2048.             zwarnnam(name, "error in condition", NULL, 0);
  2049.             freecompcond(m);
  2050.             return 1;
  2051.             }
  2052.             *t = '\0';
  2053.             c->u.l.a[l] = ztrdup(tt);
  2054.             tt = ++t;
  2055.             /* any more commas are text, not active */
  2056.             for (; *t && *t != '\200'; t++)
  2057.             if (*t == '\201')
  2058.                 *t = ',';
  2059.             if (!*t) {
  2060.             zwarnnam(name, "error in condition", NULL, 0);
  2061.             freecompcond(m);
  2062.             return 1;
  2063.             }
  2064.             *t = '\0';
  2065.             c->u.l.b[l] = ztrdup(tt);
  2066.         } else {
  2067.             /* remaining patterns are number followed by string */
  2068.             for (; *t && *t != '\200' && *t != '\201'; t++);
  2069.             if (!*t || *t == '\200') {
  2070.             zwarnnam(name, "error in condition", NULL, 0);
  2071.             freecompcond(m);
  2072.             return 1;
  2073.             }
  2074.             *t = '\0';
  2075.             c->u.s.p[l] = atoi(tt);
  2076.             tt = ++t;
  2077.             for (; *t && *t != '\200'; t++)
  2078.             if (*t == '\201')
  2079.                 *t = ',';
  2080.             if (!*t) {
  2081.             zwarnnam(name, "error in condition", NULL, 0);
  2082.             freecompcond(m);
  2083.             return 1;
  2084.             }
  2085.             *t = '\0';
  2086.             c->u.s.s[l] = ztrdup(tt);
  2087.         }
  2088.         }
  2089.         while (*t == ' ')
  2090.         t++;
  2091.         if (*t == ',') {
  2092.         /* Another condition to `or' */
  2093.         o->or = c = (Compcond) zcalloc(sizeof(*c));
  2094.         o = c;
  2095.         t++;
  2096.         } else if (*t) {
  2097.         /* Another condition to `and' */
  2098.         c->and = (Compcond) zcalloc(sizeof(*c));
  2099.         c = c->and;
  2100.         }
  2101.     }
  2102.     /* Assign condition to current compctl */
  2103.     *next = (Compctl) zcalloc(sizeof(*cc));
  2104.     (*next)->cond = m;
  2105.     argv++;
  2106.     /* End of the condition; get the flags that go with it. */
  2107.     if (get_compctl(name, &argv, *next, 0, isdef))
  2108.         return 1;
  2109.      if ((!argv || !*argv) && (cclist & COMP_SPECIAL))
  2110.          /* default, first, or command completion finished */
  2111.         ready = 1;
  2112.     else {
  2113.         /* see if we are looking for more conditions or are
  2114.          * ready to return (ready = 1)
  2115.          */
  2116.         if (!argv || !*argv || **argv != '-' ||
  2117.         ((!argv[0][1] || argv[0][1] == '+') && !argv[1])) {
  2118.         zwarnnam(name, "missing command names", NULL, 0);
  2119.         return 1;
  2120.         }
  2121.         if (!strcmp(*argv, "--"))
  2122.         ready = 1;
  2123.         else if (!strcmp(*argv, "-+") && argv[1] &&
  2124.              !strcmp(argv[1], "--")) {
  2125.         ready = 1;
  2126.         argv++;
  2127.         }
  2128.         argv++;
  2129.         /* prepare to put the next lot of conditions on the end */
  2130.         next = &((*next)->next);
  2131.     }
  2132.     }
  2133.     /* save position at end of parsing */
  2134.     *av = argv - 1;
  2135.     return 0;
  2136. }
  2137.  
  2138. /**/
  2139. int
  2140. cc_assign(char *name, Compctl *ccptr, Compctl cct, int reass)
  2141. {
  2142.     /* Copy over the details from the values in cct to those in *ccptr */
  2143.     Compctl cc;
  2144.  
  2145.     if (cct->subcmd && (cct->keyvar || cct->glob || cct->str ||
  2146.             cct->func || cct->explain || cct->prefix)) {
  2147.     zwarnnam(name, "illegal combination of options", NULL, 0);
  2148.     return 1;
  2149.     }
  2150.  
  2151.     /* Handle assignment of new default or command completion */
  2152.     if (reass && !(cclist & COMP_LIST)) {
  2153.     /* if not listing */
  2154.     if (cclist == (COMP_COMMAND|COMP_DEFAULT)
  2155.         || cclist == (COMP_COMMAND|COMP_FIRST)
  2156.         || cclist == (COMP_DEFAULT|COMP_FIRST)
  2157.         || cclist == COMP_SPECIAL) {
  2158.          zwarnnam(name, "can't set -D, -T, and -C simultaneously", NULL, 0);
  2159.         /* ... because the following code wouldn't work. */
  2160.         return 1;
  2161.     }
  2162.     if (cclist & COMP_COMMAND) {
  2163.         /* command */
  2164.         *ccptr = &cc_compos;
  2165.         cc_reassign(*ccptr);
  2166.     } else if (cclist & COMP_DEFAULT) {
  2167.         /* default */
  2168.         *ccptr = &cc_default;
  2169.         cc_reassign(*ccptr);
  2170.      } else if (cclist & COMP_FIRST) {
  2171.          /* first */
  2172.          *ccptr = &cc_first;
  2173.          cc_reassign(*ccptr);
  2174.     }
  2175.     }
  2176.  
  2177.     /* Free the old compctl */
  2178.     cc = *ccptr;
  2179.     zsfree(cc->keyvar);
  2180.     zsfree(cc->glob);
  2181.     zsfree(cc->str);
  2182.     zsfree(cc->func);
  2183.     zsfree(cc->explain);
  2184.     zsfree(cc->prefix);
  2185.     zsfree(cc->suffix);
  2186.     zsfree(cc->subcmd);
  2187.     zsfree(cc->hpat);
  2188.     
  2189.     /* and copy over the new stuff, (permanently) allocating
  2190.      * space for strings.
  2191.      */
  2192.     cc->mask = cct->mask;
  2193.     cc->keyvar = ztrdup(cct->keyvar);
  2194.     cc->glob = ztrdup(cct->glob);
  2195.     cc->str = ztrdup(cct->str);
  2196.     cc->func = ztrdup(cct->func);
  2197.     cc->explain = ztrdup(cct->explain);
  2198.     cc->prefix = ztrdup(cct->prefix);
  2199.     cc->suffix = ztrdup(cct->suffix);
  2200.     cc->subcmd = ztrdup(cct->subcmd);
  2201.     cc->hpat = ztrdup(cct->hpat);
  2202.     cc->hnum = cct->hnum;
  2203.  
  2204.     /* careful with extended completion:  it's already allocated */
  2205.     cc->ext = cct->ext;
  2206.  
  2207.     return 0;
  2208. }
  2209.  
  2210. /**/
  2211. void
  2212. cc_reassign(Compctl cc)
  2213. {
  2214.     /* Free up a new default or command completion:
  2215.      * this is a hack to free up the parts which should be deleted,
  2216.      * without removing the basic variable which is statically allocated
  2217.      */
  2218.     Compctl c2;
  2219.  
  2220.     c2 = (Compctl) zcalloc(sizeof *cc);
  2221.     c2->xor = cc->xor;
  2222.     c2->ext = cc->ext;
  2223.     c2->refc = 1;
  2224.  
  2225.     freecompctl(c2);
  2226.  
  2227.     cc->ext = cc->xor = NULL;
  2228. }
  2229.  
  2230. /**/
  2231. void
  2232. compctl_process(char **s, int mask, char *uk, char *gl, char *st, char *fu, char *ex, char *pr, char *su, char *sc, char *hp, int hn)
  2233. {
  2234.     /* Command called internally to initialise some basic compctls */
  2235.     Compctl cc;
  2236.  
  2237.     cc = (Compctl) zcalloc(sizeof *cc);
  2238.     cc->mask = mask;
  2239.     cc->keyvar = ztrdup(uk);
  2240.     cc->glob = ztrdup(gl);
  2241.     cc->str = ztrdup(st);
  2242.     cc->func = ztrdup(fu);
  2243.     cc->explain = ztrdup(ex);
  2244.     cc->prefix = ztrdup(pr);
  2245.     cc->suffix = ztrdup(su);
  2246.     cc->subcmd = ztrdup(sc);
  2247.     cc->hpat = ztrdup(hp);
  2248.     cc->hnum = hn;
  2249.  
  2250.     cclist = 0;
  2251.     compctl_process_cc(s, cc);
  2252. }
  2253.  
  2254. /**/
  2255. void
  2256. compctl_process_cc(char **s, Compctl cc)
  2257. {
  2258.     Compctlp ccp;
  2259.  
  2260.     if (cclist & COMP_REMOVE) {
  2261.     /* Delete entries for the commands listed */
  2262.     for (; *s; s++) {
  2263.         if ((ccp = (Compctlp) compctltab->removenode(compctltab, *s)))
  2264.         compctltab->freenode((HashNode) ccp);
  2265.     }
  2266.     } else {
  2267.     /* Add the compctl just read to the hash table */
  2268.  
  2269.     cc->refc = 0;
  2270.     for (; *s; s++) {
  2271.         cc->refc++;
  2272.         ccp = (Compctlp) zalloc(sizeof *ccp);
  2273.         ccp->cc = cc;
  2274.         compctltab->addnode(compctltab, ztrdup(*s), ccp);
  2275.     }
  2276.     }
  2277. }
  2278.  
  2279. /* Print a `compctl' */
  2280.  
  2281. /**/
  2282. void
  2283. printcompctl(char *s, Compctl cc, int printflags)
  2284. {
  2285.     Compctl cc2;
  2286.     char *css = "fcqovbAIFpEjrzBRGudeNOZUnQmw";
  2287.     char *mss = " pcCwWsSnNmrR";
  2288.     unsigned long t = 0x7fffffff;
  2289.     unsigned long flags = cc->mask;
  2290.     unsigned long oldshowmask;
  2291.  
  2292.     if ((flags & CC_EXCMDS) && !(flags & CC_DISCMDS))
  2293.     flags &= ~CC_EXCMDS;
  2294.  
  2295.     /* If showmask is non-zero, then print only those *
  2296.      * commands with that flag set.                   */
  2297.     if (showmask && !(flags & showmask))
  2298.     return;
  2299.  
  2300.     /* Temporarily clear showmask in case we make *
  2301.      * recursive calls to printcompctl.           */
  2302.     oldshowmask = showmask;
  2303.     showmask = 0;
  2304.  
  2305.     /* print either command name or start of compctl command itself */
  2306.     if (s) {
  2307.     if (cclist & COMP_LIST) {
  2308.         printf("compctl");
  2309.         if (cc == &cc_compos)
  2310.         printf(" -C");
  2311.         if (cc == &cc_default)
  2312.         printf(" -D");
  2313.         if (cc == &cc_first)
  2314.         printf(" -T");
  2315.     } else
  2316.         quotedzputs(s, stdout);
  2317.     }
  2318.  
  2319.     /* loop through flags w/o args that are set, printing them if so */
  2320.     if (flags & t) {
  2321.     printf(" -");
  2322.     if ((flags & (CC_ALREG | CC_ALGLOB)) == (CC_ALREG | CC_ALGLOB))
  2323.         putchar('a'), flags &= ~(CC_ALREG | CC_ALGLOB);
  2324.     while (*css) {
  2325.         if (flags & t & 1)
  2326.         putchar(*css);
  2327.         css++;
  2328.         flags >>= 1;
  2329.         t >>= 1;
  2330.     }
  2331.     }
  2332.  
  2333.     /* now flags with arguments */
  2334.     flags = cc->mask;
  2335.     printif(cc->keyvar, 'k');
  2336.     printif(cc->func, 'K');
  2337.     printif(cc->explain, 'X');
  2338.     printif(cc->prefix, 'P');
  2339.     printif(cc->suffix, 'S');
  2340.     printif(cc->glob, 'g');
  2341.     printif(cc->str, 's');
  2342.     printif(cc->subcmd, 'l');
  2343.     if (cc->hpat) {
  2344.     printf(" -H %d ", cc->hnum);
  2345.     quotedzputs(cc->hpat, stdout);
  2346.     }
  2347.  
  2348.     /* now the -x ... -- extended completion part */
  2349.     if (cc->ext) {
  2350.     Compcond c, o;
  2351.     int i;
  2352.  
  2353.     cc2 = cc->ext;
  2354.     printf(" -x");
  2355.  
  2356.     while (cc2) {
  2357.         /* loop over conditions */
  2358.         c = cc2->cond;
  2359.  
  2360.         printf(" '");
  2361.         for (c = cc2->cond; c;) {
  2362.         /* loop over or's */
  2363.         o = c->or;
  2364.         while (c) {
  2365.             /* loop over and's */
  2366.             putchar(mss[c->type]);
  2367.  
  2368.             for (i = 0; i < c->n; i++) {
  2369.             /* for all [...]'s of a given condition */
  2370.             putchar('[');
  2371.             switch (c->type) {
  2372.             case CCT_POS:
  2373.             case CCT_NUMWORDS:
  2374.                 printf("%d,%d", c->u.r.a[i], c->u.r.b[i]);
  2375.                 break;
  2376.             case CCT_CURSUF:
  2377.             case CCT_CURPRE:
  2378.                 printqt(c->u.s.s[i]);
  2379.                 break;
  2380.             case CCT_RANGESTR:
  2381.             case CCT_RANGEPAT:
  2382.                 printqt(c->u.l.a[i]);
  2383.                 putchar(',');
  2384.                 printqt(c->u.l.b[i]);
  2385.                 break;
  2386.             default:
  2387.                 printf("%d,", c->u.s.p[i]);
  2388.                 printqt(c->u.s.s[i]);
  2389.             }
  2390.             putchar(']');
  2391.             }
  2392.             if ((c = c->and))
  2393.             putchar(' ');
  2394.         }
  2395.         if ((c = o))
  2396.             printf(" , ");
  2397.         }
  2398.         putchar('\'');
  2399.         c = cc2->cond;
  2400.         cc2->cond = NULL;
  2401.         /* now print the flags for the current condition */
  2402.         printcompctl(NULL, cc2, 0);
  2403.         cc2->cond = c;
  2404.         if ((cc2 = (Compctl) (cc2->next)))
  2405.         printf(" -");
  2406.     }
  2407.     if (cclist & COMP_LIST)
  2408.         printf(" --");
  2409.     }
  2410.     if (cc && cc->xor) {
  2411.     /* print xor'd (+) completions */
  2412.     printf(" +");
  2413.     if (cc->xor != &cc_default)
  2414.         printcompctl(NULL, cc->xor, 0);
  2415.     }
  2416.     if (s) {
  2417.     if ((cclist & COMP_LIST) && (cc != &cc_compos)
  2418.         && (cc != &cc_default) && (cc != &cc_first)) {
  2419.         if(s[0] == '-' || s[0] == '+')
  2420.         printf(" -");
  2421.         putchar(' ');
  2422.         quotedzputs(s, stdout);
  2423.     }
  2424.     putchar('\n');
  2425.     }
  2426.  
  2427.     showmask = oldshowmask;
  2428. }
  2429.  
  2430. /**/
  2431. void
  2432. printcompctlp(HashNode hn, int printflags)
  2433. {
  2434.     Compctlp ccp = (Compctlp) hn;
  2435.  
  2436.     /* Function needed for use by scanhashtable() */
  2437.     printcompctl(ccp->nam, ccp->cc, printflags);
  2438. }
  2439.  
  2440. /**/
  2441. void
  2442. printqt(char *str)
  2443. {
  2444.     /* Print str, but turn any single quote into '\'' or ''. */
  2445.     for (; *str; str++)
  2446.     if (*str == '\'')
  2447.         printf(isset(RCQUOTES) ? "''" : "'\\''");
  2448.     else
  2449.         putchar(*str);
  2450. }
  2451.  
  2452. /**/
  2453. void
  2454. printif(char *str, int c)
  2455. {
  2456.     /* If flag c has an argument, print that */
  2457.     if (str) {
  2458.     printf(" -%c ", c);
  2459.     quotedzputs(str, stdout);
  2460.     }
  2461. }
  2462.  
  2463. /**** history list functions ****/
  2464.  
  2465. /* fc, history, r */
  2466.  
  2467. /**/
  2468. int
  2469. bin_fc(char *nam, char **argv, char *ops, int func)
  2470. {
  2471.     int first = -1, last = -1, retval, delayrem, minflag = 0;
  2472.     char *s;
  2473.     struct asgment *asgf = NULL, *asgl = NULL;
  2474.     Comp com = NULL;
  2475.  
  2476.     /* fc is only permitted in interactive shells */
  2477.     if (!interact) {
  2478.     zwarnnam(nam, "not interactive shell", NULL, 0);
  2479.     return 1;
  2480.     }
  2481.     /* with the -m option, the first argument is taken *
  2482.      * as a pattern that history lines have to match   */
  2483.     if (*argv && ops['m']) {
  2484.     tokenize(*argv);
  2485.     if (!(com = parsereg(*argv++))) {
  2486.         zwarnnam(nam, "invalid match pattern", NULL, 0);
  2487.         return 1;
  2488.     }
  2489.     }
  2490.     delayrem = 0;
  2491.     if (!(ops['l'] && unset(HISTNOSTORE)) &&
  2492.     !(ops['R'] || ops['W'] || ops['A']))
  2493.     delayrem = 1;
  2494.     if (ops['R']) {
  2495.     /* read history from a file */
  2496.     readhistfile(*argv ? *argv : getsparam("HISTFILE"), 1);
  2497.     return 0;
  2498.     }
  2499.     if (ops['W']) {
  2500.     /* write history to a file */
  2501.     savehistfile(*argv ? *argv : getsparam("HISTFILE"), 1,
  2502.              (ops['I'] ? 2 : 0));
  2503.     return 0;
  2504.     }
  2505.     if (ops['A']) {
  2506.     /* append history to a file */
  2507.     savehistfile(*argv ? *argv : getsparam("HISTFILE"), 1,
  2508.              (ops['I'] ? 3 : 1));
  2509.     return 0;
  2510.     }
  2511.     /* put foo=bar type arguments into the substitution list */
  2512.     while (*argv && equalsplit(*argv, &s)) {
  2513.     Asgment a = (Asgment) alloc(sizeof *a);
  2514.  
  2515.     if (!asgf)
  2516.         asgf = asgl = a;
  2517.     else {
  2518.         asgl->next = a;
  2519.         asgl = a;
  2520.     }
  2521.     a->name = *argv;
  2522.     a->value = s;
  2523.     argv++;
  2524.     }
  2525.     /* interpret and check first history line specifier */
  2526.     if (*argv) {
  2527.     minflag = **argv == '-';
  2528.     first = fcgetcomm(*argv);
  2529.     if (first == -1) {
  2530.         if (delayrem)
  2531.         remhist();
  2532.         return 1;
  2533.     }
  2534.     argv++;
  2535.     }
  2536.     /* interpret and check second history line specifier */
  2537.     if (*argv) {
  2538.     last = fcgetcomm(*argv);
  2539.     if (last == -1) {
  2540.         if (delayrem)
  2541.         remhist();
  2542.         return 1;
  2543.     }
  2544.     argv++;
  2545.     }
  2546.     /* There is a maximum of two history specifiers.  At least, there *
  2547.      * will be as long as the history list is one-dimensional.        */
  2548.     if (*argv) {
  2549.     zwarnnam("fc", "too many arguments", NULL, 0);
  2550.     if (delayrem)
  2551.         remhist();
  2552.     return 1;
  2553.     }
  2554.     /* default values of first and last, and range checking */
  2555.     if (first == -1)
  2556.     first = (ops['l']) ? curhist - 16 : curhist - 1;
  2557.     if (last == -1)
  2558.     last = (ops['l']) ? curhist - 1 : first;
  2559.     if (first < firsthist())
  2560.     first = firsthist();
  2561.     if (last == -1)
  2562.     last = (minflag) ? curhist : first;
  2563.     if (ops['l'])
  2564.     /* list the required part of the history */
  2565.     retval = fclist(stdout, !ops['n'], ops['r'], ops['D'],
  2566.             ops['d'] + ops['f'] * 2 + ops['E'] * 4 + ops['i'] * 8,
  2567.             first, last, asgf, com);
  2568.     else {
  2569.     /* edit history file, and (if successful) use the result as a new command */
  2570.     int tempfd;
  2571.     FILE *out;
  2572.     char *fil;
  2573.  
  2574.     retval = 1;
  2575.     fil = gettempname();
  2576.     if (((tempfd = open(fil, O_WRONLY | O_CREAT | O_EXCL, 0600)) == -1) ||
  2577.         ((out = fdopen(tempfd, "w")) == NULL)) {
  2578.         zwarnnam("fc", "can't open temp file: %e", NULL, errno);
  2579.     } else {
  2580.         if (!fclist(out, 0, ops['r'], 0, 0, first, last, asgf, com)) {
  2581.         char *editor;
  2582.  
  2583.         editor = auxdata ? auxdata : getsparam("FCEDIT");
  2584.         if (!editor)
  2585.             editor = DEFAULT_FCEDIT;
  2586.  
  2587.         if (fcedit(editor, fil))
  2588.             if (stuff(fil))
  2589.             zwarnnam("fc", "%e: %s", s, errno);
  2590.             else
  2591.             retval = lastval;
  2592.         }
  2593.     }
  2594.     unlink(fil);
  2595.     }
  2596.     if (delayrem)
  2597.     remhist();
  2598.     return retval;
  2599. }
  2600.  
  2601. /* History handling functions: these are called by ZLE, as well as  *
  2602.  * the actual builtins.  fcgetcomm() gets a history line, specified *
  2603.  * either by number or leading string.  fcsubs() performs a given   *
  2604.  * set of simple old=new substitutions on a given command line.     *
  2605.  * fclist() outputs a given range of history lines to a text file.  */
  2606.  
  2607. /* get the history event associated with s */
  2608.  
  2609. /**/
  2610. int
  2611. fcgetcomm(char *s)
  2612. {
  2613.     int cmd;
  2614.  
  2615.     /* First try to match a history number.  Negative *
  2616.      * numbers indicate reversed numbering.           */
  2617.     if ((cmd = atoi(s))) {
  2618.     if (cmd < 0)
  2619.         cmd = curhist + cmd;
  2620.     if (cmd >= curhist) {
  2621.         zwarnnam("fc", "bad history number: %d", 0, cmd);
  2622.         return -1;
  2623.     }
  2624.     return cmd;
  2625.     }
  2626.     /* not a number, so search by string */
  2627.     cmd = hcomsearch(s);
  2628.     if (cmd == -1)
  2629.     zwarnnam("fc", "event not found: %s", s, 0);
  2630.     return cmd;
  2631. }
  2632.  
  2633. /* Perform old=new substituions.  Uses the asgment structure from zsh.h, *
  2634.  * which is essentially a linked list of string,replacement pairs.       */
  2635.  
  2636. /**/
  2637. int
  2638. fcsubs(char **sp, struct asgment *sub)
  2639. {
  2640.     char *oldstr, *newstr, *oldpos, *newpos, *newmem, *s = *sp;
  2641.     int subbed = 0;
  2642.  
  2643.     /* loop through the linked list */
  2644.     while (sub) {
  2645.     oldstr = sub->name;
  2646.     newstr = sub->value;
  2647.     sub = sub->next;
  2648.     oldpos = s;
  2649.     /* loop over occurences of oldstr in s, replacing them with newstr */
  2650.     while ((newpos = (char *)strstr(oldpos, oldstr))) {
  2651.         newmem = (char *) alloc(1 + (newpos - s)
  2652.             + strlen(newstr) + strlen(newpos + strlen(oldstr)));
  2653.         ztrncpy(newmem, s, newpos - s);
  2654.         strcat(newmem, newstr);
  2655.         oldpos = newmem + strlen(newmem);
  2656.         strcat(newmem, newpos + strlen(oldstr));
  2657.         s = newmem;
  2658.         subbed = 1;
  2659.     }
  2660.     }
  2661.     *sp = s;
  2662.     return subbed;
  2663. }
  2664.  
  2665. /* Print a series of history events to a file.  The file pointer is     *
  2666.  * given by f, and the required range of events by first and last.      *
  2667.  * subs is an optional list of foo=bar substitutions to perform on the  *
  2668.  * history lines before output.  com is an optional comp structure      *
  2669.  * that the history lines are required to match.  n, r, D and d are     *
  2670.  * options: n indicates that each line should be numbered.  r indicates *
  2671.  * that the lines should be output in reverse order (newest first).     *
  2672.  * D indicates that the real time taken by each command should be       *
  2673.  * output.  d indicates that the time of execution of each command      *
  2674.  * should be output; d>1 means that the date should be output too; d>3  *
  2675.  * means that mm/dd/yyyy form should be used for the dates, as opposed  *
  2676.  * to dd.mm.yyyy form; d>7 means that yyyy-mm-dd form should be used.   */
  2677.  
  2678. /**/
  2679. int
  2680. fclist(FILE *f, int n, int r, int D, int d, int first, int last, struct asgment *subs, Comp com)
  2681. {
  2682.     int fclistdone = 0;
  2683.     char *s, *hs;
  2684.     Histent ent;
  2685.  
  2686.     /* reverse range if required */
  2687.     if (r) {
  2688.     r = last;
  2689.     last = first;
  2690.     first = r;
  2691.     }
  2692.     /* suppress "no substitution" warning if no substitution is requested */
  2693.     if (!subs)
  2694.     fclistdone = 1;
  2695.  
  2696.     for (;;) {
  2697.     hs = quietgetevent(first);
  2698.     if (!hs) {
  2699.         zwarnnam("fc", "no such event: %d", NULL, first);
  2700.         return 1;
  2701.     }
  2702.     s = dupstring(hs);
  2703.     /* this if does the pattern matching, if required */
  2704.     if (!com || domatch(s, com, 0)) {
  2705.         /* perform substitution */
  2706.         fclistdone |= fcsubs(&s, subs);
  2707.  
  2708.         /* do numbering */
  2709.         if (n)
  2710.         fprintf(f, "%5d  ", first);
  2711.         ent = NULL;
  2712.         /* output actual time (and possibly date) of execution of the
  2713.         command, if required */
  2714.         if (d) {
  2715.         struct tm *ltm;
  2716.         if (!ent)
  2717.             ent = gethistent(first);
  2718.         ltm = localtime(&ent->stim);
  2719.         if (d >= 2) {
  2720.             if (d >= 8) {
  2721.             fprintf(f, "%d-%02d-%02d ",
  2722.                 ltm->tm_year + 1900,
  2723.                 ltm->tm_mon + 1, ltm->tm_mday);
  2724.             } else if (d >= 4) {
  2725.             fprintf(f, "%d.%d.%d ",
  2726.                 ltm->tm_mday, ltm->tm_mon + 1,
  2727.                 ltm->tm_year + 1900);
  2728.             } else {
  2729.             fprintf(f, "%d/%d/%d ",
  2730.                 ltm->tm_mon + 1, ltm->tm_mday,
  2731.                 ltm->tm_year + 1900);
  2732.             }
  2733.         }
  2734.         fprintf(f, "%02d:%02d  ", ltm->tm_hour, ltm->tm_min);
  2735.         }
  2736.         /* display the time taken by the command, if required */
  2737.         if (D) {
  2738.         long diff;
  2739.         if (!ent)
  2740.             ent = gethistent(first);
  2741.         diff = (ent->ftim) ? ent->ftim - ent->stim : 0;
  2742.         fprintf(f, "%ld:%02ld  ", diff / 60, diff % 60);
  2743.         }
  2744.  
  2745.         /* output the command */
  2746.         if (f == stdout) {
  2747.         nicezputs(s, f);
  2748.         putc('\n', f);
  2749.         } else
  2750.         fprintf(f, "%s\n", s);
  2751.     }
  2752.     /* move on to the next history line, or quit the loop */
  2753.     if (first == last)
  2754.         break;
  2755.     else if (first > last)
  2756.         first--;
  2757.     else
  2758.         first++;
  2759.     }
  2760.  
  2761.     /* final processing */
  2762.     if (f != stdout)
  2763.     fclose(f);
  2764.     if (!fclistdone) {
  2765.     zwarnnam("fc", "no substitutions performed", NULL, 0);
  2766.     return 1;
  2767.     }
  2768.     return 0;
  2769. }
  2770.  
  2771. /* edit a history file */
  2772.  
  2773. /**/
  2774. int
  2775. fcedit(char *ename, char *fn)
  2776. {
  2777.     char *s;
  2778.  
  2779.     if (!strcmp(ename, "-"))
  2780.     return 1;
  2781.  
  2782.     s = tricat(ename, " ", fn);
  2783.     execstring(s, 1, 0);
  2784.     zsfree(s);
  2785.  
  2786.     return !lastval;
  2787. }
  2788.  
  2789. /**** parameter builtins ****/
  2790.  
  2791. /* declare, export, integer, local, readonly, typeset */
  2792.  
  2793. /**/
  2794. int
  2795. bin_typeset(char *name, char **argv, char *ops, int func)
  2796. {
  2797.     Param pm;
  2798.     Asgment asg;
  2799.     Comp com;
  2800.     char *optstr = "iLRZlurtxU";
  2801.     int on = 0, off = 0, roff, bit = PM_INTEGER;
  2802.     int initon, initoff, of, i;
  2803.     int returnval = 0, printflags = 0;
  2804.  
  2805.     /* hash -f is really the builtin `functions' */
  2806.     if (ops['f'])
  2807.     return bin_functions(name, argv, ops, func);
  2808.  
  2809.     /* Translate the options into PM_* flags.   *
  2810.      * Unfortunately, this depends on the order *
  2811.      * these flags are defined in zsh.h         */
  2812.     for (; *optstr; optstr++, bit <<= 1)
  2813.     if (ops[*optstr] == 1)
  2814.         on |= bit;
  2815.     else if (ops[*optstr] == 2)
  2816.         off |= bit;
  2817.     roff = off;
  2818.  
  2819.     /* Sanity checks on the options.  Remove conficting options. */
  2820.     if ((on | off) & PM_EXPORTED)
  2821.     func = BIN_EXPORT;
  2822.     if (on & PM_INTEGER)
  2823.     off |= PM_RIGHT_B | PM_LEFT | PM_RIGHT_Z | PM_UPPER | PM_ARRAY;
  2824.     if (on & PM_LEFT)
  2825.     off |= PM_RIGHT_B | PM_INTEGER;
  2826.     if (on & PM_RIGHT_B)
  2827.     off |= PM_LEFT | PM_INTEGER;
  2828.     if (on & PM_RIGHT_Z)
  2829.     off |= PM_INTEGER;
  2830.     if (on & PM_UPPER)
  2831.     off |= PM_LOWER;
  2832.     if (on & PM_LOWER)
  2833.     off |= PM_UPPER;
  2834.     on &= ~off;
  2835.  
  2836.     /* Given no arguments, list whatever the options specify. */
  2837.     if (!*argv) {
  2838.     if (!(on|roff))
  2839.         printflags |= PRINT_TYPE;
  2840.     if (roff || ops['+'])
  2841.         printflags |= PRINT_NAMEONLY;
  2842.     scanhashtable(paramtab, 1, on|roff, 0, paramtab->printnode, printflags);
  2843.     return 0;
  2844.     }
  2845.  
  2846.     /* With the -m option, treat arguments as glob patterns */
  2847.     if (ops['m']) {
  2848.     while ((asg = getasg(*argv++))) {
  2849.         tokenize(asg->name);   /* expand argument */
  2850.         if (!(com = parsereg(asg->name))) {
  2851.         untokenize(asg->name);
  2852.         zwarnnam(name, "bad pattern : %s", argv[-1], 0);
  2853.         returnval = 1;
  2854.         continue;
  2855.         }
  2856.         /* If no options or values are given, display all *
  2857.          * parameters matching the glob pattern.          */
  2858.         if (!(on || roff || asg->value)) {
  2859.         scanmatchtable(paramtab, com, 0, 0, paramtab->printnode, 0);
  2860.         continue;
  2861.         }
  2862.         /* Since either options or values are given, we search   *
  2863.          * through the parameter table and change all parameters *
  2864.          * matching the glob pattern to have these flags and/or  *
  2865.          * value.                                                */
  2866.         for (i = 0; i < paramtab->hsize; i++) {
  2867.         for (pm = (Param) paramtab->nodes[i]; pm; pm = (Param) pm->next) {
  2868.             if (domatch(pm->nam, com, 0)) {
  2869.             /* set up flags if we have any */
  2870.             if (on || roff) {
  2871.                 if (PM_TYPE(pm->flags) == PM_ARRAY && (on & PM_UNIQUE) &&
  2872.                 !(pm->flags & PM_READONLY & ~off))
  2873.                 uniqarray((*pm->gets.afn) (pm));
  2874.                 pm->flags = (pm->flags | on) & ~off;
  2875.                 if (PM_TYPE(pm->flags) != PM_ARRAY) {
  2876.                 if ((on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z | PM_INTEGER)) && auxlen)
  2877.                     pm->ct = auxlen;
  2878.                 /* did we just export this? */
  2879.                 if ((pm->flags & PM_EXPORTED) && !pm->env) {
  2880.                     pm->env = addenv(pm->nam, (asg->value) ? asg->value : getsparam(pm->nam));
  2881.                 } else if (!(pm->flags & PM_EXPORTED) && pm->env) {
  2882.                 /* did we just unexport this? */
  2883.                     delenv(pm->env);
  2884.                     zsfree(pm->env);
  2885.                     pm->env = NULL;
  2886.                 }
  2887.                 }
  2888.             }
  2889.             /* set up a new value if given */
  2890.             if (asg->value) {
  2891.                 setsparam(pm->nam, ztrdup(asg->value));
  2892.             }
  2893.             }
  2894.         }
  2895.         }
  2896.     }
  2897.     return returnval;
  2898.     }
  2899.  
  2900.     /* Save the values of on, off, and func */
  2901.     initon = on;
  2902.     initoff = off;
  2903.     of = func;
  2904.  
  2905.     /* Take arguments literally.  Don't glob */
  2906.     while ((asg = getasg(*argv++))) {
  2907.     /* restore the original values of on, off, and func */
  2908.     on = initon;
  2909.     off = initoff;
  2910.     func = of;
  2911.     on &= ~PM_ARRAY;
  2912.  
  2913.     /* check if argument is a valid identifier */
  2914.     if (!isident(asg->name)) {
  2915.         zerr("not an identifier: %s", asg->name, 0);
  2916.         returnval = 1;
  2917.         continue;
  2918.     }
  2919.     if ((pm = (Param) paramtab->getnode(paramtab, asg->name))) {
  2920.         if (pm->flags & PM_SPECIAL) {
  2921.         func = 0;
  2922.         on = (PM_TYPE(pm->flags) == PM_INTEGER) ?
  2923.             (on &= ~(PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z | PM_UPPER)) :
  2924.             (on & ~PM_INTEGER);
  2925.         off &= ~PM_INTEGER;
  2926.         }
  2927.         if (pm->level) {
  2928.         if ((on & PM_EXPORTED) && !(on &= ~PM_EXPORTED) && !off)
  2929.             return 1;
  2930.         }
  2931.     }
  2932.     bit = 0;    /* flag for switching int<->not-int */
  2933.     if (pm && !(pm->flags & PM_UNSET) && ((((locallevel == pm->level) || func == BIN_EXPORT)
  2934.         && !(bit = ((off & pm->flags) | (on & ~pm->flags)) & PM_INTEGER)) || (pm->flags & PM_SPECIAL))) {
  2935.         /* if no flags or values are given, just print this parameter */
  2936.         if (!on && !roff && !asg->value) {
  2937.         paramtab->printnode((HashNode) pm, 0);
  2938.         continue;
  2939.         }
  2940.         if (PM_TYPE(pm->flags) == PM_ARRAY && (on & PM_UNIQUE) &&
  2941.         !(pm->flags & PM_READONLY & ~off))
  2942.         uniqarray((*pm->gets.afn) (pm));
  2943.         pm->flags = (pm->flags | on) & ~off;
  2944.         if ((on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z | PM_INTEGER)) && auxlen)
  2945.         pm->ct = auxlen;
  2946.         if (PM_TYPE(pm->flags) != PM_ARRAY) {
  2947.         if (pm->flags & PM_EXPORTED) {
  2948.             if (!pm->env)
  2949.             pm->env = addenv(asg->name, (asg->value) ? asg->value : getsparam(asg->name));
  2950.         } else if (pm->env) {
  2951.             delenv(pm->env);
  2952.             zsfree(pm->env);
  2953.             pm->env = NULL;
  2954.         }
  2955.         if (asg->value)
  2956.             setsparam(asg->name, ztrdup(asg->value));
  2957.         }
  2958.     } else {
  2959.         if (bit) {
  2960.         if (pm->flags & PM_READONLY) {
  2961.             on |= ~off & PM_READONLY;
  2962.             pm->flags &= ~PM_READONLY;
  2963.         }
  2964.         if (!asg->value)
  2965.             asg->value = dupstring(getsparam(asg->name));
  2966.         unsetparam(asg->name);
  2967.         } else if (locallist && func != BIN_EXPORT) {
  2968.         PERMALLOC {
  2969.             addlinknode(locallist, ztrdup(asg->name));
  2970.         } LASTALLOC;
  2971.         }
  2972.         /* create a new node for a parameter with the *
  2973.          * flags in `on' minus the readonly flag      */
  2974.         pm = createparam(ztrdup(asg->name), on & ~PM_READONLY);
  2975.         DPUTS(!pm, "BUG: parameter not created");
  2976.         pm->ct = auxlen;
  2977.         if (func != BIN_EXPORT)
  2978.         pm->level = locallevel;
  2979.         if (asg->value)
  2980.         setsparam(asg->name, ztrdup(asg->value));
  2981.         pm->flags |= (on & PM_READONLY);
  2982.     }
  2983.     }
  2984.     return returnval;
  2985. }
  2986.  
  2987. /* Display or change the attributes of shell functions.   *
  2988.  * If called as autoload, it will define a new autoloaded *
  2989.  * (undefined) shell function.                            */
  2990.  
  2991. /**/
  2992. int
  2993. bin_functions(char *name, char **argv, char *ops, int func)
  2994. {
  2995.     Comp com;
  2996.     Shfunc shf;
  2997.     int i, returnval = 0;
  2998.     int on = 0, off = 0;
  2999.  
  3000.     /* Do we have any flags defined? */
  3001.     if (ops['u'] || ops['t']) {
  3002.     if (ops['u'] == 1)
  3003.         on |= PM_UNDEFINED;
  3004.     else if (ops['u'] == 2)
  3005.         off |= PM_UNDEFINED;
  3006.  
  3007.     if (ops['t'] == 1)
  3008.         on |= PM_TAGGED;
  3009.     else if (ops['t'] == 2)
  3010.         off |= PM_TAGGED;
  3011.     }
  3012.  
  3013.     if (off & PM_UNDEFINED) {
  3014.     zwarnnam(name, "invalid option(s)", NULL, 0);
  3015.     return 1;
  3016.     }
  3017.  
  3018.     /* If no arguments given, we will print functions.  If flags *
  3019.      * are given, we will print only functions containing these  *
  3020.      * flags, else we'll print them all.                         */
  3021.     if (!*argv) {
  3022.     scanhashtable(shfunctab, 1, on|off, DISABLED, shfunctab->printnode, 0);
  3023.     return 0;
  3024.     }
  3025.  
  3026.     /* With the -m option, treat arguments as glob patterns */
  3027.     if (ops['m']) {
  3028.     on &= ~PM_UNDEFINED;
  3029.     for (; *argv; argv++) {
  3030.         /* expand argument */
  3031.         tokenize(*argv);
  3032.         if ((com = parsereg(*argv))) {
  3033.         /* with no options, just print all functions matching the glob pattern */
  3034.         if (!(on|off)) {
  3035.             scanmatchtable(shfunctab, com, 0, DISABLED, shfunctab->printnode, 0);
  3036.         } else {
  3037.         /* apply the options to all functions matching the glob pattern */
  3038.             for (i = 0; i < shfunctab->hsize; i++) {
  3039.             for (shf = (Shfunc) shfunctab->nodes[i]; shf; shf = (Shfunc) shf->next)
  3040.                 if (domatch(shf->nam, com, 0) && !(shf->flags & DISABLED))
  3041.                 shf->flags = (shf->flags | on) & (~off);
  3042.             }
  3043.         }
  3044.         } else {
  3045.         untokenize(*argv);
  3046.         zwarnnam(name, "bad pattern : %s", *argv, 0);
  3047.         returnval = 1;
  3048.         }
  3049.     }
  3050.     return returnval;
  3051.     }
  3052.  
  3053.     /* Take the arguments literally -- do not glob */
  3054.     for (; *argv; argv++) {
  3055.     if ((shf = (Shfunc) shfunctab->getnode(shfunctab, *argv))) {
  3056.         /* if any flag was given */
  3057.         if (on|off)
  3058.         /* turn on/off the given flags */
  3059.         shf->flags = (shf->flags | (on & ~PM_UNDEFINED)) & ~off;
  3060.         else
  3061.         /* no flags, so just print */
  3062.         shfunctab->printnode((HashNode) shf, 0);
  3063.     } else if (on & PM_UNDEFINED) {
  3064.         /* Add a new undefined (autoloaded) function to the *
  3065.          * hash table with the corresponding flags set.     */
  3066.         shf = (Shfunc) zcalloc(sizeof *shf);
  3067.         shf->flags = on;
  3068.         shfunctab->addnode(shfunctab, ztrdup(*argv), shf);
  3069.     } else
  3070.         returnval = 1;
  3071.     }
  3072.     return returnval;
  3073. }
  3074.  
  3075. /* unset: unset parameters */
  3076.  
  3077. /**/
  3078. int
  3079. bin_unset(char *name, char **argv, char *ops, int func)
  3080. {
  3081.     Param pm, next;
  3082.     Comp com;
  3083.     char *s;
  3084.     int match = 0, returnval = 0;
  3085.     int i;
  3086.  
  3087.     /* unset -f is the same as unfunction */
  3088.     if (ops['f'])
  3089.     return bin_unhash(name, argv, ops, func);
  3090.  
  3091.     /* with -m option, treat arguments as glob patterns */
  3092.     if (ops['m']) {
  3093.     while ((s = *argv++)) {
  3094.         /* expand */
  3095.         tokenize(s);
  3096.         if ((com = parsereg(s))) {
  3097.         /* Go through the parameter table, and unset any matches */
  3098.         for (i = 0; i < paramtab->hsize; i++) {
  3099.             for (pm = (Param) paramtab->nodes[i]; pm; pm = next) {
  3100.             /* record pointer to next, since we may free this one */
  3101.             next = (Param) pm->next;
  3102.             if (domatch(pm->nam, com, 0)) {
  3103.                 unsetparam(pm->nam);
  3104.                 match++;
  3105.             }
  3106.             }
  3107.         }
  3108.         } else {
  3109.         untokenize(s);
  3110.         zwarnnam(name, "bad pattern : %s", s, 0);
  3111.         returnval = 1;
  3112.         }
  3113.     }
  3114.     /* If we didn't match anything, we return 1. */
  3115.     if (!match)
  3116.         returnval = 1;
  3117.     return returnval;
  3118.     }
  3119.  
  3120.     /* do not glob -- unset the given parameter */
  3121.     while ((s = *argv++)) {
  3122.     if (paramtab->getnode(paramtab, s)) {
  3123.         unsetparam(s);
  3124.     } else {
  3125.         returnval = 1;
  3126.     }
  3127.     }
  3128.     return returnval;
  3129. }
  3130.  
  3131. /* vared: edit (literally) a parameter value */
  3132.  
  3133. /**/
  3134. int
  3135. bin_vared(char *name, char **args, char *ops, int func)
  3136. {
  3137.     char *s;
  3138.     char *t;
  3139.     Param pm;
  3140.     int create = 0;
  3141.     char *p1 = NULL, *p2 = NULL;
  3142.  
  3143.     /* all options are handled as arguments */
  3144.     while (*args && **args == '-') {
  3145.     while (*++(*args))
  3146.         switch (**args) {
  3147.         case 'c':
  3148.         /* -c option -- allow creation of the parameter if it doesn't
  3149.         yet exist */
  3150.         create = 1;
  3151.         break;
  3152.         case 'p':
  3153.         /* -p option -- set main prompt string */
  3154.         if ((*args)[1])
  3155.             p1 = *args + 1, *args = "" - 1;
  3156.         else if (args[1])
  3157.             p1 = *(++args), *args = "" - 1;
  3158.         else {
  3159.             zwarnnam(name, "prompt string expected after -%c", NULL,
  3160.                  **args);
  3161.             return 1;
  3162.         }
  3163.         break;
  3164.         case 'r':
  3165.         /* -r option -- set right prompt string */
  3166.         if ((*args)[1])
  3167.             p2 = *args + 1, *args = "" - 1;
  3168.         else if (args[1])
  3169.             p2 = *(++args), *args = "" - 1;
  3170.         else {
  3171.             zwarnnam(name, "prompt string expected after -%c", NULL,
  3172.                  **args);
  3173.             return 1;
  3174.         }
  3175.         break;
  3176.         case 'h':
  3177.         /* -h option -- enable history */
  3178.         ops['h'] = 1;
  3179.         break;
  3180.         default:
  3181.         /* unrecognised option character */
  3182.         zwarnnam(name, "unknown option: %s", *args, 0);
  3183.         return 1;
  3184.         }
  3185.     args++;
  3186.     }
  3187.  
  3188.     /* check we have a parameter name */
  3189.     if (!*args) {
  3190.     zwarnnam(name, "missing variable", NULL, 0);
  3191.     return 1;
  3192.     }
  3193.     /* handle non-existent parameter */
  3194.     if (!(s = getsparam(args[0]))) {
  3195.     if (create)
  3196.         createparam(args[0], PM_SCALAR);
  3197.     else {
  3198.         zwarnnam(name, "no such variable: %s", args[0], 0);
  3199.         return 1;
  3200.     }
  3201.     }
  3202.     /* edit the parameter value */
  3203.     PERMALLOC {
  3204.     pushnode(bufstack, ztrdup(s));
  3205.     } LASTALLOC;
  3206.     in_vared = !ops['h'];
  3207.     t = (char *) zleread(p1, p2);
  3208.     in_vared = 0;
  3209.     if (!t || errflag) {
  3210.     /* error in editing */
  3211.     errflag = 0;
  3212.     return 1;
  3213.     }
  3214.     /* strip off trailing newline, if any */
  3215.     if (t[strlen(t) - 1] == '\n')
  3216.     t[strlen(t) - 1] = '\0';
  3217.     /* final assignment of parameter value */
  3218.     pm = (Param) paramtab->getnode(paramtab, args[0]);
  3219.     if (pm && PM_TYPE(pm->flags) == PM_ARRAY) {
  3220.     char **a;
  3221.  
  3222.     PERMALLOC {
  3223.         a = spacesplit(t, 1);
  3224.     } LASTALLOC;
  3225.     setaparam(args[0], a);
  3226.     } else
  3227.     setsparam(args[0], t);
  3228.     return 0;
  3229. }
  3230.  
  3231. /* type, whence, which */
  3232.  
  3233. /**/
  3234. int
  3235. bin_whence(char *nam, char **argv, char *ops, int func)
  3236. {
  3237.     HashNode hn;
  3238.     Comp com;
  3239.     int returnval = 0;
  3240.     int printflags = 0;
  3241.     int csh, all, v;
  3242.     int informed;
  3243.     char *cnam;
  3244.  
  3245.     /* Check some option information */
  3246.     csh = ops['c'];
  3247.     v   = ops['v'];
  3248.     all = ops['a'];
  3249.  
  3250.     if (ops['c'])
  3251.     printflags |= PRINT_WHENCE_CSH;
  3252.     else if (ops['v'])
  3253.     printflags |= PRINT_WHENCE_VERBOSE;
  3254.     else
  3255.     printflags |= PRINT_WHENCE_SIMPLE;
  3256.     if(ops['f'])
  3257.     printflags |= PRINT_WHENCE_FUNCDEF;
  3258.  
  3259.     /* With -m option -- treat arguments as a glob patterns */
  3260.     if (ops['m']) {
  3261.     for (; *argv; argv++) {
  3262.         /* parse the pattern */
  3263.         tokenize(*argv);
  3264.         if (!(com = parsereg(*argv))) {
  3265.         untokenize(*argv);
  3266.         zwarnnam(nam, "bad pattern : %s", *argv, 0);
  3267.         returnval = 1;
  3268.         continue;
  3269.         }
  3270.         if (!ops['p']) {
  3271.         /* -p option is for path search only.    *
  3272.          * We're not using it, so search for ... */
  3273.  
  3274.         /* aliases ... */
  3275.         scanmatchtable(aliastab, com, 0, DISABLED, aliastab->printnode, printflags);
  3276.  
  3277.         /* and reserved words ... */
  3278.         scanmatchtable(reswdtab, com, 0, DISABLED, reswdtab->printnode, printflags);
  3279.  
  3280.         /* and shell functions... */
  3281.         scanmatchtable(shfunctab, com, 0, DISABLED, shfunctab->printnode, printflags);
  3282.  
  3283.         /* and builtins. */
  3284.         scanmatchtable(builtintab, com, 0, DISABLED, builtintab->printnode, printflags);
  3285.         }
  3286.         /* Done search for `internal' commands, if the -p option *
  3287.          * was not used.  Now search the path.                   */
  3288.         cmdnamtab->filltable(cmdnamtab);
  3289.         scanmatchtable(cmdnamtab, com, 0, 0, cmdnamtab->printnode, printflags);
  3290.  
  3291.     }
  3292.     return returnval;
  3293.     }
  3294.  
  3295.     /* Take arguments literally -- do not glob */
  3296.     for (; *argv; argv++) {
  3297.     informed = 0;
  3298.  
  3299.     if (!ops['p']) {
  3300.         /* Look for alias */
  3301.         if ((hn = aliastab->getnode(aliastab, *argv))) {
  3302.         aliastab->printnode(hn, printflags);
  3303.         if (!all)
  3304.             continue;
  3305.         informed = 1;
  3306.         }
  3307.         /* Look for reserved word */
  3308.         if ((hn = reswdtab->getnode(reswdtab, *argv))) {
  3309.         reswdtab->printnode(hn, printflags);
  3310.         if (!all)
  3311.             continue;
  3312.         informed = 1;
  3313.         }
  3314.         /* Look for shell function */
  3315.         if ((hn = shfunctab->getnode(shfunctab, *argv))) {
  3316.         shfunctab->printnode(hn, printflags);
  3317.         if (!all)
  3318.             continue;
  3319.         informed = 1;
  3320.         }
  3321.         /* Look for builtin command */
  3322.         if ((hn = builtintab->getnode(builtintab, *argv))) {
  3323.         builtintab->printnode(hn, printflags);
  3324.         if (!all)
  3325.             continue;
  3326.         informed = 1;
  3327.         }
  3328.         /* Look for commands that have been added to the *
  3329.          * cmdnamtab with the builtin `hash foo=bar'.    */
  3330.         if ((hn = cmdnamtab->getnode(cmdnamtab, *argv)) && (hn->flags & HASHED)) {
  3331.         cmdnamtab->printnode(hn, printflags);
  3332.         if (!all)
  3333.             continue;
  3334.         informed = 1;
  3335.         }
  3336.     }
  3337.  
  3338.     /* Option -a is to search the entire path, *
  3339.      * rather than just looking for one match. */
  3340.     if (all) {
  3341.         char **pp, buf[PATH_MAX], *z;
  3342.  
  3343.         for (pp = path; *pp; pp++) {
  3344.         z = buf;
  3345.         if (**pp) {
  3346.             strucpy(&z, *pp);
  3347.             *z++ = '/';
  3348.         }
  3349.         if ((z - buf) + strlen(*argv) >= PATH_MAX)
  3350.             continue;
  3351.         strcpy(z, *argv);
  3352.         if (iscom(buf)) {
  3353.             if (v && !csh)
  3354.             printf("%s is %s\n", *argv, buf);
  3355.             else
  3356.             puts(buf);
  3357.             informed = 1;
  3358.         }
  3359.         }
  3360.         if (!informed && (v || csh)) {
  3361.         printf("%s not found\n", *argv);
  3362.         returnval = 1;
  3363.         }
  3364.     } else if ((cnam = findcmd(*argv))) {
  3365.         /* Found external command. */
  3366.         if (v && !csh)
  3367.         printf("%s is %s\n", *argv, cnam);
  3368.         else
  3369.         puts(cnam);
  3370.         zsfree(cnam);
  3371.     } else {
  3372.         /* Not found at all. */
  3373.         if (v || csh)
  3374.         printf("%s not found\n", *argv);
  3375.         returnval = 1;
  3376.     }
  3377.     }
  3378.     return returnval;
  3379. }
  3380.  
  3381. /**** command & named directory hash table builtins ****/
  3382.  
  3383. /*****************************************************************
  3384.  * hash -- explicitly hash a command.                            *
  3385.  * 1) Given no arguments, list the hash table.                   *
  3386.  * 2) The -m option prints out commands in the hash table that   *
  3387.  *    match a given glob pattern.                                *
  3388.  * 3) The -f option causes the entire path to be added to the    *
  3389.  *    hash table (cannot be combined with any arguments).        *
  3390.  * 4) The -r option causes the entire hash table to be discarded *
  3391.  *    (cannot be combined with any arguments).                   *
  3392.  * 5) Given argument of the form foo=bar, add element to command *
  3393.  *    hash table, so that when `foo' is entered, then `bar' is   *
  3394.  *    executed.                                                  *
  3395.  * 6) Given arguments not of the previous form, add it to the    *
  3396.  *    command hash table as if it were being executed.           *
  3397.  * 7) The -d option causes analogous things to be done using     *
  3398.  *    the named directory hash table.                            *
  3399.  *****************************************************************/
  3400.  
  3401. /**/
  3402. int
  3403. bin_hash(char *name, char **argv, char *ops, int func)
  3404. {
  3405.     HashTable ht;
  3406.     Comp com;
  3407.     Asgment asg;
  3408.     int returnval = 0;
  3409.  
  3410.     if (ops['d'])
  3411.     ht = nameddirtab;
  3412.     else
  3413.     ht = cmdnamtab;
  3414.  
  3415.     if (ops['r'] || ops['f']) {
  3416.     /* -f and -r can't be used with any arguments */
  3417.     if (*argv) {
  3418.         zwarnnam("hash", "too many arguments", NULL, 0);
  3419.         return 1;
  3420.     }
  3421.  
  3422.     /* empty the hash table */
  3423.     if (ops['r'])
  3424.         ht->emptytable(ht);
  3425.  
  3426.     /* fill the hash table in a standard way */
  3427.     if (ops['f'])
  3428.         ht->filltable(ht);
  3429.  
  3430.     return 0;
  3431.     }
  3432.  
  3433.     /* Given no arguments, display current hash table. */
  3434.     if (!*argv) {
  3435.     scanhashtable(ht, 1, 0, 0, ht->printnode, 0);
  3436.     return 0;
  3437.     }
  3438.  
  3439.     while ((asg = getasg(*argv))) {
  3440.     if (asg->value) {
  3441.         /* The argument is of the form foo=bar, *
  3442.          * so define an entry for the table.    */
  3443.         if(ops['d']) {
  3444.         Nameddir nd = (Nameddir) zcalloc(sizeof *nd);
  3445.         nd->flags = 0;
  3446.         nd->dir = ztrdup(asg->value);
  3447.         ht->addnode(ht, ztrdup(asg->name), nd);
  3448.         } else {
  3449.         Cmdnam cn = (Cmdnam) zcalloc(sizeof *cn);
  3450.         cn->flags = HASHED;
  3451.         cn->u.cmd = ztrdup(asg->value);
  3452.         ht->addnode(ht, ztrdup(asg->name), cn);
  3453.         }
  3454.     } else if (ops['m']) {
  3455.         /* with the -m option, treat the argument as a glob pattern */
  3456.         tokenize(*argv);  /* expand */
  3457.         if ((com = parsereg(*argv))) {
  3458.         /* display matching hash table elements */
  3459.         scanmatchtable(ht, com, 0, 0, ht->printnode, 0);
  3460.         } else {
  3461.         untokenize(*argv);
  3462.         zwarnnam(name, "bad pattern : %s", *argv, 0);
  3463.         returnval = 1;
  3464.         }
  3465.     } else if (!ht->getnode2(ht, asg->name)) {
  3466.         /* With no `=value' part to the argument, *
  3467.          * work out what it ought to be.          */
  3468.         if(ops['d'] && !getnameddir(asg->name)) {
  3469.         zwarnnam(name, "no such directory name: %s", asg->name, 0);
  3470.         returnval = 1;
  3471.         } else if (!hashcmd(asg->name, path)) {
  3472.         zwarnnam(name, "no such command: %s", asg->name, 0);
  3473.         returnval = 1;
  3474.         }
  3475.     }
  3476.     argv++;
  3477.     }
  3478.     return returnval;
  3479. }
  3480.  
  3481. /* unhash: remove specified elements from a hash table */
  3482.  
  3483. /**/
  3484. int
  3485. bin_unhash(char *name, char **argv, char *ops, int func)
  3486. {
  3487.     HashTable ht;
  3488.     HashNode hn, nhn;
  3489.     Comp com;
  3490.     int match = 0, returnval = 0;
  3491.     int i;
  3492.  
  3493.     /* Check which hash table we are working with. */
  3494.     if (ops['d'])
  3495.     ht = nameddirtab;    /* named directories */
  3496.     else if (ops['f'])
  3497.     ht = shfunctab;        /* shell functions   */
  3498.     else if (ops['a'])
  3499.     ht = aliastab;        /* aliases           */
  3500.     else
  3501.     ht = cmdnamtab;        /* external commands */
  3502.  
  3503.     /* With -m option, treat arguments as glob patterns. *
  3504.      * "unhash -m '*'" is legal, but not recommended.    */
  3505.     if (ops['m']) {
  3506.     for (; *argv; argv++) {
  3507.         /* expand argument */
  3508.         tokenize(*argv);
  3509.         if ((com = parsereg(*argv))) {
  3510.         /* remove all nodes matching glob pattern */
  3511.         for (i = 0; i < ht->hsize; i++) {
  3512.             for (hn = ht->nodes[i]; hn; hn = nhn) {
  3513.             /* record pointer to next, since we may free this one */
  3514.             nhn = hn->next;
  3515.             if (domatch(hn->nam, com, 0)) {
  3516.                 ht->freenode(ht->removenode(ht, hn->nam));
  3517.                 match++;
  3518.             }
  3519.             }
  3520.         }
  3521.         } else {
  3522.         untokenize(*argv);
  3523.         zwarnnam(name, "bad pattern : %s", *argv, 0);
  3524.         returnval = 1;
  3525.         }
  3526.     }
  3527.     /* If we didn't match anything, we return 1. */
  3528.     if (!match)
  3529.         returnval = 1;
  3530.     return returnval;
  3531.     }
  3532.  
  3533.     /* Take arguments literally -- do not glob */
  3534.     for (; *argv; argv++) {
  3535.     if ((hn = ht->removenode(ht, *argv))) {
  3536.         ht->freenode(hn);
  3537.     } else {
  3538.         zwarnnam(name, "no such hash table element: %s", *argv, 0);
  3539.         returnval = 1;
  3540.     }
  3541.     }
  3542.     return returnval;
  3543. }
  3544.  
  3545. /**** alias builtins ****/
  3546.  
  3547. /* alias: display or create aliases. */
  3548.  
  3549. /**/
  3550. int
  3551. bin_alias(char *name, char **argv, char *ops, int func)
  3552. {
  3553.     Alias a;
  3554.     Comp com;
  3555.     Asgment asg;
  3556.     int haveflags = 0, returnval = 0;
  3557.     int flags1 = 0, flags2 = DISABLED;
  3558.     int printflags = 0;
  3559.  
  3560.     /* Did we specify the type of alias? */
  3561.     if (ops['r'] || ops['g']) {
  3562.     if (ops['r'] && ops['g']) {
  3563.         zwarnnam(name, "illegal combination of options", NULL, 0);
  3564.         return 1;
  3565.     }
  3566.     haveflags = 1;
  3567.     if (ops['g'])
  3568.         flags1 |= ALIAS_GLOBAL;
  3569.     else
  3570.         flags2 |= ALIAS_GLOBAL;
  3571.     }
  3572.  
  3573.     if (ops['L'])
  3574.     printflags |= PRINT_LIST;
  3575.  
  3576.     /* In the absence of arguments, list all aliases.  If a command *
  3577.      * line flag is specified, list only those of that type.        */
  3578.     if (!*argv) {
  3579.     scanhashtable(aliastab, 1, flags1, flags2, aliastab->printnode, printflags);
  3580.     return 0;
  3581.     }
  3582.  
  3583.     /* With the -m option, treat the arguments as *
  3584.      * glob patterns of aliases to display.       */
  3585.     if (ops['m']) {
  3586.     for (; *argv; argv++) {
  3587.         tokenize(*argv);  /* expand argument */
  3588.         if ((com = parsereg(*argv))) {
  3589.         /* display the matching aliases */
  3590.         scanmatchtable(aliastab, com, flags1, flags2, aliastab->printnode, printflags);
  3591.         } else {
  3592.         untokenize(*argv);
  3593.         zwarnnam(name, "bad pattern : %s", *argv, 0);
  3594.         returnval = 1;
  3595.         }
  3596.     }
  3597.     return returnval;
  3598.     }
  3599.  
  3600.     /* Take arguments literally.  Don't glob */
  3601.     while ((asg = getasg(*argv++))) {
  3602.     if (asg->value && !ops['L']) {
  3603.         /* The argument is of the form foo=bar and we are not *
  3604.          * forcing a listing with -L, so define an alias      */
  3605.         aliastab->addnode(aliastab, ztrdup(asg->name),
  3606.         createaliasnode(ztrdup(asg->value), flags1));
  3607.     } else if ((a = (Alias) aliastab->getnode(aliastab, asg->name))) {
  3608.         /* display alias if appropriate */
  3609.         if (!haveflags ||
  3610.         (ops['r'] && !(a->flags & ALIAS_GLOBAL)) ||
  3611.         (ops['g'] &&  (a->flags & ALIAS_GLOBAL)))
  3612.         aliastab->printnode((HashNode) a, printflags);
  3613.     } else
  3614.         returnval = 1;
  3615.     }
  3616.     return returnval;
  3617. }
  3618.  
  3619.  
  3620. /**** resource limit builtins ****/
  3621.  
  3622. #ifdef HAVE_GETRLIMIT
  3623.  
  3624. #if defined(RLIM_T_IS_QUAD_T) || defined(RLIM_T_IS_UNSIGNED)
  3625. # define ZSTRTORLIMT(a, b, c)    zstrtorlimit((a), (b), (c))
  3626. #else
  3627. # define ZSTRTORLIMT(a, b, c)    zstrtol((a), (b), (c))
  3628. #endif
  3629.  
  3630. /* Generated rec array containing limits required for the limit builtin.
  3631.  * They must appear in this array in numerical order of the RLIMIT_* macros.
  3632.  */
  3633.  
  3634. #include "rlimits.h"
  3635.  
  3636. #endif /* HAVE_GETRLIMIT */
  3637.  
  3638. /* limit: set or show resource limits.  The variable hard indicates *
  3639.  * whether `hard' or `soft' resource limits are being set/shown.    */
  3640.  
  3641. /**/
  3642. int
  3643. bin_limit(char *nam, char **argv, char *ops, int func)
  3644. {
  3645. #ifndef HAVE_GETRLIMIT
  3646.     /* limit builtin not appropriate to this system */
  3647.     zwarnnam(nam, "not available on this system", NULL, 0);
  3648.     return 1;
  3649. #else
  3650.     char *s;
  3651.     int hard, limnum, lim;
  3652.     rlim_t val;
  3653.     int ret = 0;
  3654.  
  3655.     hard = ops['h'];
  3656.     if (ops['s'] && !*argv)
  3657.     return setlimits(NULL);
  3658.     /* without arguments, display limits */
  3659.     if (!*argv) {
  3660.     showlimits(hard, -1);
  3661.     return 0;
  3662.     }
  3663.     while ((s = *argv++)) {
  3664.     /* Search for the appropriate resource name.  When a name matches (i.e. *
  3665.      * starts with) the argument, the lim variable changes from -1 to the   *
  3666.      * number of the resource.  If another match is found, lim goes to -2.  */
  3667.     for (lim = -1, limnum = 0; limnum < ZSH_NLIMITS; limnum++)
  3668.         if (!strncmp(recs[limnum], s, strlen(s))) {
  3669.         if (lim != -1)
  3670.             lim = -2;
  3671.         else
  3672.             lim = limnum;
  3673.         }
  3674.     /* lim==-1 indicates that no matches were found.       *
  3675.      * lim==-2 indicates that multiple matches were found. */
  3676.     if (lim < 0) {
  3677.         zwarnnam("limit",
  3678.              (lim == -2) ? "ambiguous resource specification: %s"
  3679.              : "no such resource: %s", s, 0);
  3680.         return 1;
  3681.     }
  3682.     /* without value for limit, display the current limit */
  3683.     if (!(s = *argv++)) {
  3684.         showlimits(hard, lim);
  3685.         return 0;
  3686.     }
  3687.     if (lim==RLIMIT_CPU) {
  3688.         /* time-type resource -- may be specified as seconds, or minutes or *
  3689.          * hours with the `m' and `h' modifiers, and `:' may be used to add *
  3690.          * together more than one of these.  It's easier to understand from *
  3691.          * the code:                                                        */
  3692.         val = ZSTRTORLIMT(s, &s, 10);
  3693.         if (*s)
  3694.         if ((*s == 'h' || *s == 'H') && !s[1])
  3695.             val *= 3600L;
  3696.         else if ((*s == 'm' || *s == 'M') && !s[1])
  3697.             val *= 60L;
  3698.         else if (*s == ':')
  3699.             val = val * 60 + ZSTRTORLIMT(s + 1, &s, 10);
  3700.         else {
  3701.             zwarnnam("limit", "unknown scaling factor: %s", s, 0);
  3702.             return 1;
  3703.         }
  3704.     }
  3705. # ifdef RLIMIT_NPROC
  3706.     else if (lim == RLIMIT_NPROC)
  3707.         /* pure numeric resource -- only a straight decimal number is
  3708.         permitted. */
  3709.         val = ZSTRTORLIMT(s, &s, 10);
  3710. # endif /* RLIMIT_NPROC */
  3711. # ifdef RLIMIT_NOFILE
  3712.     else if (lim == RLIMIT_NOFILE)
  3713.         /* pure numeric resource -- only a straight decimal number is
  3714.         permitted. */
  3715.         val = ZSTRTORLIMT(s, &s, 10);
  3716. # endif /* RLIMIT_NOFILE */
  3717.     else {
  3718.         /* memory-type resource -- `k' and `M' modifiers are permitted,
  3719.         meaning (respectively) 2^10 and 2^20. */
  3720.         val = ZSTRTORLIMT(s, &s, 10);
  3721.         if (!*s || ((*s == 'k' || *s == 'K') && !s[1]))
  3722.         val *= 1024L;
  3723.         else if ((*s == 'M' || *s == 'm') && !s[1])
  3724.         val *= 1024L * 1024;
  3725.         else {
  3726.         zwarnnam("limit", "unknown scaling factor: %s", s, 0);
  3727.         return 1;
  3728.         }
  3729.     }
  3730.     /* new limit is valid and has been interpreted; apply it to the
  3731.     specified resource */
  3732.     if (hard) {
  3733.         /* can only raise hard limits if running as root */
  3734.         if (val > current_limits[lim].rlim_max && geteuid()) {
  3735.         zwarnnam("limit", "can't raise hard limits", NULL, 0);
  3736.         return 1;
  3737.         } else {
  3738.         limits[lim].rlim_max = val;
  3739.         if (val < limits[lim].rlim_cur)
  3740.             limits[lim].rlim_cur = val;
  3741.         }
  3742.     } else if (val > limits[lim].rlim_max) {
  3743.         zwarnnam("limit", "limit exceeds hard limit", NULL, 0);
  3744.         return 1;
  3745.     } else
  3746.         limits[lim].rlim_cur = val;
  3747.     if (ops['s'] && zsetlimit(lim, "limit"))
  3748.         ret++;
  3749.     }
  3750.     return ret;
  3751. #endif /* HAVE_GETRLIMIT */
  3752. }
  3753.  
  3754. /* unlimit: remove resource limits.  Much of this code is the same as *
  3755.  * that in bin_limit().                                               */
  3756.  
  3757. /**/
  3758. int
  3759. bin_unlimit(char *nam, char **argv, char *ops, int func)
  3760. {
  3761. #ifndef HAVE_GETRLIMIT
  3762.     /* unlimit builtin not appropriate to this system */
  3763.     zwarnnam(nam, "not available on this system", NULL, 0);
  3764.     return 1;
  3765. #else
  3766.     int hard, limnum, lim;
  3767.     int ret = 0;
  3768.     uid_t euid = geteuid();
  3769.  
  3770.     hard = ops['h'];
  3771.     /* Without arguments, remove all limits. */
  3772.     if (!*argv) {
  3773.     for (limnum = 0; limnum != RLIM_NLIMITS; limnum++) {
  3774.         if (hard)
  3775.         if (euid && current_limits[limnum].rlim_max != RLIM_INFINITY)
  3776.             ret++;
  3777.         else
  3778.             limits[limnum].rlim_max = RLIM_INFINITY;
  3779.         else
  3780.         limits[limnum].rlim_cur = limits[limnum].rlim_max;
  3781.     }
  3782.     if (ops['s'])
  3783.         ret += setlimits(nam);
  3784.     if (ret)
  3785.         zwarnnam(nam, "can't remove hard limits", NULL, 0);
  3786.     } else {
  3787.     for (; *argv; argv++) {
  3788.         /* Search for the appropriate resource name.  When a name     *
  3789.          * matches (i.e. starts with) the argument, the lim variable  *
  3790.          * changes from -1 to the number of the resource.  If another *
  3791.          * match is found, lim goes to -2.                            */
  3792.         for (lim = -1, limnum = 0; limnum < ZSH_NLIMITS; limnum++)
  3793.         if (!strncmp(recs[limnum], *argv, strlen(*argv))) {
  3794.             if (lim != -1)
  3795.             lim = -2;
  3796.             else
  3797.             lim = limnum;
  3798.         }
  3799.         /* lim==-1 indicates that no matches were found.       *
  3800.          * lim==-2 indicates that multiple matches were found. */
  3801.         if (lim < 0) {
  3802.         zwarnnam(nam,
  3803.              (lim == -2) ? "ambiguous resource specification: %s"
  3804.              : "no such resource: %s", *argv, 0);
  3805.         return 1;
  3806.         }
  3807.         /* remove specified limit */
  3808.         if (hard)
  3809.         if (euid && current_limits[lim].rlim_max != RLIM_INFINITY) {
  3810.             zwarnnam(nam, "can't remove hard limits", NULL, 0);
  3811.             ret++;
  3812.         } else
  3813.             limits[lim].rlim_max = RLIM_INFINITY;
  3814.         else
  3815.         limits[lim].rlim_cur = limits[lim].rlim_max;
  3816.         if (ops['s'] && zsetlimit(lim, nam))
  3817.         ret++;
  3818.     }
  3819.     }
  3820.     return ret;
  3821. #endif /* HAVE_GETRLIMIT */
  3822. }
  3823.  
  3824. /* ulimit: set or display resource limits */
  3825.  
  3826. /**/
  3827. int
  3828. bin_ulimit(char *name, char **argv, char *ops, int func)
  3829. {
  3830. #ifndef HAVE_GETRLIMIT
  3831.     /* builtin not appropriate */
  3832.     zwarnnam(name, "not available on this system", NULL, 0);
  3833.     return 1;
  3834. #else
  3835.     int res, resmask = 0, hard = 0, soft = 0, nres = 0;
  3836.     char *options;
  3837.  
  3838.     do {
  3839.     options = *argv;
  3840.     if (options && *options == '-' && !options[1]) {
  3841.         zwarnnam(name, "missing option letter", NULL, 0);
  3842.         return 1;
  3843.     }
  3844.     res = -1;
  3845.     if (options && *options == '-') {
  3846.         argv++;
  3847.         while (*++options) {
  3848.         if(*options == Meta)
  3849.             *++options ^= 32;
  3850.         res = -1;
  3851.         switch (*options) {
  3852.         case 'H':
  3853.             hard = 1;
  3854.             continue;
  3855.         case 'S':
  3856.             soft = 1;
  3857.             continue;
  3858.         case 'a':
  3859.             if (*argv || options[1] || resmask) {
  3860.             zwarnnam(name, "no arguments required after -a",
  3861.                  NULL, 0);
  3862.             return 1;
  3863.             }
  3864.             resmask = (1 << RLIM_NLIMITS) - 1;
  3865.             nres = RLIM_NLIMITS;
  3866.             continue;
  3867.         case 't':
  3868.             res = RLIMIT_CPU;
  3869.             break;
  3870.         case 'f':
  3871.             res = RLIMIT_FSIZE;
  3872.             break;
  3873.         case 'd':
  3874.             res = RLIMIT_DATA;
  3875.             break;
  3876.         case 's':
  3877.             res = RLIMIT_STACK;
  3878.             break;
  3879.         case 'c':
  3880.             res = RLIMIT_CORE;
  3881.             break;
  3882. # ifdef RLIMIT_RSS
  3883.         case 'm':
  3884.             res = RLIMIT_RSS;
  3885.             break;
  3886. # endif /* RLIMIT_RSS */
  3887. # ifdef RLIMIT_MEMLOCK
  3888.         case 'l':
  3889.             res = RLIMIT_MEMLOCK;
  3890.             break;
  3891. # endif /* RLIMIT_MEMLOCK */
  3892. # ifdef RLIMIT_NOFILE
  3893.         case 'n':
  3894.             res = RLIMIT_NOFILE;
  3895.             break;
  3896. # endif /* RLIMIT_NOFILE */
  3897. # ifdef RLIMIT_NPROC
  3898.         case 'u':
  3899.             res = RLIMIT_NPROC;
  3900.             break;
  3901. # endif /* RLIMIT_NPROC */
  3902. # ifdef RLIMIT_VMEM
  3903.         case 'v':
  3904.             res = RLIMIT_VMEM;
  3905.             break;
  3906. # endif /* RLIMIT_VMEM */
  3907.         default:
  3908.             /* unrecognised limit */
  3909.             zwarnnam(name, "bad option: -%c", NULL, *options);
  3910.             return 1;
  3911.         }
  3912.         if (options[1]) {
  3913.             resmask |= 1 << res;
  3914.             nres++;
  3915.         }
  3916.         }
  3917.     }
  3918.     if (!*argv || **argv == '-') {
  3919.         if (res < 0)
  3920.         if (*argv || nres)
  3921.             continue;
  3922.         else
  3923.             res = RLIMIT_FSIZE;
  3924.         resmask |= 1 << res;
  3925.         nres++;
  3926.         continue;
  3927.     }
  3928.     if (res < 0)
  3929.         res = RLIMIT_FSIZE;
  3930.     if (strcmp(*argv, "unlimited")) {
  3931.         /* set limit to specified value */
  3932.         rlim_t limit;
  3933.  
  3934.         limit = ZSTRTORLIMT(*argv, NULL, 10);
  3935.         /* scale appropriately */
  3936.         switch (res) {
  3937.         case RLIMIT_FSIZE:
  3938.         case RLIMIT_CORE:
  3939.         limit *= 512;
  3940.         break;
  3941.         case RLIMIT_DATA:
  3942.         case RLIMIT_STACK:
  3943. # ifdef RLIMIT_RSS
  3944.         case RLIMIT_RSS:
  3945. # endif /* RLIMIT_RSS */
  3946. # ifdef RLIMIT_MEMLOCK
  3947.         case RLIMIT_MEMLOCK:
  3948. # endif /* RLIMIT_MEMLOCK */
  3949. # ifdef RLIMIT_VMEM
  3950.         case RLIMIT_VMEM:
  3951. # endif /* RLIMIT_VMEM */
  3952.         limit *= 1024;
  3953.         break;
  3954.         }
  3955.         if (hard) {
  3956.         /* can't raise hard limit unless running as root */
  3957.         if (limit > current_limits[res].rlim_max && geteuid()) {
  3958.             zwarnnam(name, "can't raise hard limits", NULL, 0);
  3959.             return 1;
  3960.         }
  3961.         limits[res].rlim_max = limit;
  3962.         if (limit < limits[res].rlim_cur)
  3963.             limits[res].rlim_cur = limit;
  3964.         }
  3965.         if (!hard || soft) {
  3966.         /* can't raise soft limit above hard limit */
  3967.         if (limit > limits[res].rlim_max) {
  3968.             if (limit > current_limits[res].rlim_max && geteuid()) {
  3969.             zwarnnam(name, "value exceeds hard limit", NULL, 0);
  3970.             return 1;
  3971.             }
  3972.             limits[res].rlim_max = limits[res].rlim_cur = limit;
  3973.         } else
  3974.             limits[res].rlim_cur = limit;
  3975.         }
  3976.     } else {
  3977.         /* remove specified limit */
  3978.         if (hard) {
  3979.         /* can't remove hard limit unless running as root */
  3980.         if (current_limits[res].rlim_max != RLIM_INFINITY && geteuid()) {
  3981.             zwarnnam(name, "can't remove hard limits", NULL, 0);
  3982.             return 1;
  3983.         }
  3984.         limits[res].rlim_max = RLIM_INFINITY;
  3985.         }
  3986.         if (!hard || soft)
  3987.         /* `removal' of soft limit means setting it equal to the
  3988.            corresponding hard limit */
  3989.         limits[res].rlim_cur = limits[res].rlim_max;
  3990.     }
  3991.     if (zsetlimit(res, name))
  3992.         return 1;
  3993.     argv++;
  3994.     } while (*argv);
  3995.     for (res = 0; res < RLIM_NLIMITS; res++, resmask >>= 1)
  3996.     if (resmask & 1)
  3997.         printulimit(res, hard, nres > 1);
  3998.     return 0;
  3999. #endif /* HAVE_GETRLIMIT */
  4000. }
  4001.  
  4002. /* Display resource limits.  hard indicates whether `hard' or `soft'  *
  4003.  * limits should be displayed.  lim specifies the limit, or may be -1 *
  4004.  * to show all.                                                       */
  4005.  
  4006. #ifdef HAVE_GETRLIMIT
  4007. /**/
  4008. void
  4009. showlimits(int hard, int lim)
  4010. {
  4011.     int rt;
  4012.     rlim_t val;
  4013.  
  4014.     /* main loop over resource types */
  4015.     for (rt = 0; rt != ZSH_NLIMITS; rt++)
  4016.     if (rt == lim || lim == -1) {
  4017.         /* display limit for resource number rt */
  4018.         printf("%-16s", recs[rt]);
  4019.         val = (hard) ? limits[rt].rlim_max : limits[rt].rlim_cur;
  4020.         if (val == RLIM_INFINITY)
  4021.         printf("unlimited\n");
  4022.         else if (rt==RLIMIT_CPU)
  4023.         /* time-type resource -- display as hours, minutes and
  4024.         seconds. */
  4025.         printf("%d:%02d:%02d\n", (int)(val / 3600),
  4026.                (int)(val / 60) % 60, (int)(val % 60));
  4027. # ifdef RLIMIT_NPROC
  4028.         else if (rt == RLIMIT_NPROC)
  4029.         /* pure numeric resource */
  4030.         printf("%d\n", (int)val);
  4031. # endif /* RLIMIT_NPROC */
  4032. # ifdef RLIMIT_NOFILE
  4033.         else if (rt == RLIMIT_NOFILE)
  4034.         /* pure numeric resource */
  4035.         printf("%d\n", (int)val);
  4036. # endif /* RLIMIT_NOFILE */
  4037.         else if (val >= 1024L * 1024L)
  4038.         /* memory resource -- display with `K' or `M' modifier */
  4039. # ifdef RLIM_T_IS_QUAD_T
  4040.         printf("%qdMB\n", val / (1024L * 1024L));
  4041.         else
  4042.         printf("%qdkB\n", val / 1024L);
  4043. # else
  4044.         printf("%ldMB\n", val / (1024L * 1024L));
  4045.             else
  4046.         printf("%ldkB\n", val / 1024L);
  4047. # endif /* RLIM_T_IS_QUAD_T */
  4048.     }
  4049. }
  4050. #endif  /* HAVE_GETRLIMIT */
  4051.  
  4052. /* Display a resource limit, in ulimit style.  lim specifies which   *
  4053.  * limit should be displayed, and hard indicates whether the hard or *
  4054.  * soft limit should be displayed.                                   */
  4055.  
  4056. #ifdef HAVE_GETRLIMIT
  4057. /**/
  4058. void
  4059. printulimit(int lim, int hard, int head)
  4060. {
  4061.     rlim_t limit;
  4062.  
  4063.     /* get the limit in question */
  4064.     limit = (hard) ? limits[lim].rlim_max : limits[lim].rlim_cur;
  4065.     /* display the appropriate heading */
  4066.     switch (lim) {
  4067.     case RLIMIT_CPU:
  4068.     if (head)
  4069.         printf("cpu time (seconds)         ");
  4070.     break;
  4071.     case RLIMIT_FSIZE:
  4072.     if (head)
  4073.         printf("file size (blocks)         ");
  4074.     if (limit != RLIM_INFINITY)
  4075.         limit /= 512;
  4076.     break;
  4077.     case RLIMIT_DATA:
  4078.     if (head)
  4079.         printf("data seg size (kbytes)     ");
  4080.     if (limit != RLIM_INFINITY)
  4081.         limit /= 1024;
  4082.     break;
  4083.     case RLIMIT_STACK:
  4084.     if (head)
  4085.         printf("stack size (kbytes)        ");
  4086.     if (limit != RLIM_INFINITY)
  4087.         limit /= 1024;
  4088.     break;
  4089.     case RLIMIT_CORE:
  4090.     if (head)
  4091.         printf("core file size (blocks)    ");
  4092.     if (limit != RLIM_INFINITY)
  4093.         limit /= 512;
  4094.     break;
  4095. # ifdef RLIMIT_RSS
  4096.     case RLIMIT_RSS:
  4097.     if (head)
  4098.         printf("resident set size (kbytes) ");
  4099.     if (limit != RLIM_INFINITY)
  4100.         limit /= 1024;
  4101.     break;
  4102. # endif /* RLIMIT_RSS */
  4103. # ifdef RLIMIT_MEMLOCK
  4104.     case RLIMIT_MEMLOCK:
  4105.     if (head)
  4106.         printf("locked-in-memory size (kb) ");
  4107.     if (limit != RLIM_INFINITY)
  4108.         limit /= 1024;
  4109.     break;
  4110. # endif /* RLIMIT_MEMLOCK */
  4111. # ifdef RLIMIT_NPROC
  4112.     case RLIMIT_NPROC:
  4113.     if (head)
  4114.         printf("processes                  ");
  4115.     break;
  4116. # endif /* RLIMIT_NPROC */
  4117. # ifdef RLIMIT_NOFILE
  4118.     case RLIMIT_NOFILE:
  4119.     if (head)
  4120.         printf("file descriptors           ");
  4121.     break;
  4122. # endif /* RLIMIT_NOFILE */
  4123. # ifdef RLIMIT_VMEM
  4124.     case RLIMIT_VMEM:
  4125.     if (head)
  4126.         printf("virtual memory size (kb)   ");
  4127.     if (limit != RLIM_INFINITY)
  4128.         limit /= 1024;
  4129.     break;
  4130. # endif /* RLIMIT_VMEM */
  4131. # if defined RLIMIT_AS && RLIMIT_AS != RLIMIT_VMEM
  4132.     case RLIMIT_AS:
  4133.     if (head)
  4134.         printf("address space (kb)         ");
  4135.     if (limit != RLIM_INFINITY)
  4136.         limit /= 1024;
  4137.     break;
  4138. # endif /* RLIMIT_AS */
  4139. # ifdef RLIMIT_TCACHE
  4140.     case RLIMIT_TCACHE:
  4141.     if (head)
  4142.         printf("cached threads             ");
  4143.     break;
  4144. # endif /* RLIMIT_TCACHE */
  4145.     }
  4146.     /* display the limit */
  4147.     if (limit == RLIM_INFINITY)
  4148.     printf("unlimited\n");
  4149.     else
  4150.     printf("%ld\n", (long)limit);
  4151. }
  4152. #endif /* HAVE_GETRLIMIT */
  4153.  
  4154. /**** miscellaneous builtins ****/
  4155.  
  4156. /* true, : (colon) */
  4157.  
  4158. /**/
  4159. int
  4160. bin_true(char *name, char **argv, char *ops, int func)
  4161. {
  4162.     return 0;
  4163. }
  4164.  
  4165. /* false builtin */
  4166.  
  4167. /**/
  4168. int
  4169. bin_false(char *name, char **argv, char *ops, int func)
  4170. {
  4171.     return 1;
  4172. }
  4173.  
  4174. /* echo, print, pushln */
  4175.  
  4176. /**/
  4177. int
  4178. bin_print(char *name, char **args, char *ops, int func)
  4179. {
  4180.     int nnl = 0, fd, argc, n;
  4181.     int *len;
  4182.     Histent ent;
  4183.     FILE *fout = stdout;
  4184.  
  4185.     /* -m option -- treat the first argument as a pattern and remove
  4186.      * arguments not matching */
  4187.     if (ops['m']) {
  4188.     Comp com;
  4189.     char **t, **p;
  4190.  
  4191.     tokenize(*args);
  4192.     if (!(com = parsereg(*args))) {
  4193.         untokenize(*args);
  4194.         zwarnnam(name, "bad pattern : %s", *args, 0);
  4195.         return 1;
  4196.     }
  4197.     for (p = ++args; *p; p++)
  4198.         if (!domatch(*p, com, 0))
  4199.         for (t = p--; (*t = t[1]); t++);
  4200.     }
  4201.     /* compute lengths, and interpret according to -P, -D, -e, etc. */
  4202.     argc = arrlen(args);
  4203.     len = (int *)ncalloc(argc * sizeof(int));
  4204.     for(n = 0; n < argc; n++) {
  4205.     /* first \ sequences */
  4206.     if (!ops['e'] && (ops['R'] || ops['r'] || ops['E']))
  4207.         unmetafy(args[n], &len[n]);
  4208.     else
  4209.         args[n] = getkeystring(args[n], &len[n],
  4210.                     func != BIN_ECHO && !ops['e'], &nnl);
  4211.     /* -P option -- interpret as a prompt sequence */
  4212.     if(ops['P']) {
  4213.         char *arg = putprompt(metafy(args[n], len[n], META_NOALLOC),
  4214.                   &len[n], NULL, 0);
  4215.         args[n] = (char *)alloc(len[n] + 1);
  4216.         memcpy(args[n], arg, len[n]);
  4217.         args[n][len[n]] = 0;
  4218.         free(arg);
  4219.     }
  4220.     /* -D option -- interpret as a directory, and use ~ */
  4221.     if(ops['D']) {
  4222.         Nameddir d = finddir(args[n]);
  4223.         if(d) {
  4224.         char *arg = alloc(strlen(args[n]) + 1);
  4225.         sprintf(arg, "~%s%s", d->nam,
  4226.             args[n] + strlen(d->dir));
  4227.         args[n] = arg;
  4228.         len[n] = strlen(args[n]);
  4229.         }
  4230.     }
  4231.     }
  4232.  
  4233.     /* -z option -- push the arguments onto the editing buffer stack */
  4234.     if (ops['z']) {
  4235.     PERMALLOC {
  4236.         pushnode(bufstack, sepjoin(args, NULL));
  4237.     } LASTALLOC;
  4238.     return 0;
  4239.     }
  4240.     /* -s option -- add the arguments to the history list */
  4241.     if (ops['s']) {
  4242.     int nwords = 0, nlen, iwords;
  4243.     char **pargs = args;
  4244.  
  4245.     PERMALLOC {
  4246.         ent = gethistent(++curhist);
  4247.         zsfree(ent->text);
  4248.         if (ent->nwords)
  4249.         zfree(ent->words, ent->nwords*2*sizeof(short));
  4250.         while (*pargs++)
  4251.         nwords++;
  4252.         if ((ent->nwords = nwords)) {
  4253.         ent->words = (short *)zalloc(nwords*2*sizeof(short));
  4254.         nlen = iwords = 0;
  4255.         for (pargs = args; *pargs; pargs++) {
  4256.             ent->words[iwords++] = nlen;
  4257.             nlen += strlen(*pargs);
  4258.             ent->words[iwords++] = nlen;
  4259.             nlen++;
  4260.         }
  4261.         } else
  4262.         ent->words = (short *)NULL;
  4263.         ent->text = zjoin(args, ' ');
  4264.         ent->stim = ent->ftim = time(NULL);
  4265.         ent->flags = 0;
  4266.     } LASTALLOC;
  4267.     return 0;
  4268.     }
  4269.     /* -u and -p -- output to other than standard output */
  4270.     if (ops['u'] || ops['p']) {
  4271.     if (ops['u']) {
  4272.         for (fd = 0; fd < 10; fd++)
  4273.         if (ops[fd + '0'])
  4274.             break;
  4275.         if (fd == 10)
  4276.         fd = 0;
  4277.     } else
  4278.         fd = coprocout;
  4279.     if ((fd = dup(fd)) < 0) {
  4280.         zwarnnam(name, "bad file number", NULL, 0);
  4281.         return 1;
  4282.     }
  4283.     if ((fout = fdopen(fd, "w")) == 0) {
  4284.         zwarnnam(name, "bad mode on fd", NULL, 0);
  4285.         return 1;
  4286.     }
  4287.     }
  4288.  
  4289.     /* -o and -O -- sort the arguments */
  4290.     if (ops['o']) {
  4291.     if (ops['i'])
  4292.         qsort(args, arrlen(args), sizeof(char *), cstrpcmp);
  4293.  
  4294.     else
  4295.         qsort(args, arrlen(args), sizeof(char *), strpcmp);
  4296.     } else if (ops['O']) {
  4297.     if (ops['i'])
  4298.         qsort(args, arrlen(args), sizeof(char *), invcstrpcmp);
  4299.  
  4300.     else
  4301.         qsort(args, arrlen(args), sizeof(char *), invstrpcmp);
  4302.     }
  4303.     /* after sorting arguments, recalculate lengths */
  4304.     if(ops['o'] || ops['O'])
  4305.     for(n = 0; n < argc; n++)
  4306.         len[n] = strlen(args[n]);
  4307.  
  4308.     /* -c -- output in columns */
  4309.     if (ops['c']) {
  4310.     int l, nc, nr, sc, n, t, i;
  4311.     char **ap;
  4312.  
  4313.     for (n = l = 0, ap = args; *ap; ap++, n++)
  4314.         if (l < (t = strlen(*ap)))
  4315.         l = t;
  4316.  
  4317.     sc = l + 2;
  4318.     nc = (columns + 1) / sc;
  4319.     if (!nc)
  4320.         nc = 1;
  4321.     nr = (n + nc - 1) / nc;
  4322.  
  4323.     for (i = 0; i < nr; i++) {
  4324.         ap = args + i;
  4325.         do {
  4326.         l = strlen(*ap);
  4327.         fprintf(fout, "%s", *ap);
  4328.         for (t = nr; t && *ap; t--, ap++);
  4329.         if(*ap)
  4330.             for (; l < sc; l++)
  4331.             fputc(' ', fout);
  4332.         } while (*ap);
  4333.         fputc(ops['N'] ? '\0' : '\n', fout);
  4334.     }
  4335.     if (fout != stdout)
  4336.         fclose(fout);
  4337.     return 0;
  4338.     }
  4339.     /* normal output */
  4340.     for (; *args; args++, len++) {
  4341.     fwrite(*args, *len, 1, fout);
  4342.     if (args[1])
  4343.         fputc(ops['l'] ? '\n' : ops['N'] ? '\0' : ' ', fout);
  4344.     }
  4345.     if (!(ops['n'] || nnl))
  4346.     fputc(ops['N'] ? '\0' : '\n', fout);
  4347.     if (fout != stdout)
  4348.     fclose(fout);
  4349.     return 0;
  4350. }
  4351.  
  4352. /* echotc: output a termcap */
  4353.  
  4354. /**/
  4355. int
  4356. bin_echotc(char *name, char **argv, char *ops, int func)
  4357. {
  4358.     char *s, buf[2048], *t, *u;
  4359.     int num, argct;
  4360.  
  4361.     s = *argv++;
  4362.     if (termflags & TERM_BAD)
  4363.     return 1;
  4364.     if ((termflags & TERM_UNKNOWN) && (isset(INTERACTIVE) || !init_term()))
  4365.     return 1;
  4366.     /* if the specified termcap has a numeric value, display it */
  4367.     if ((num = tgetnum(s)) != -1) {
  4368.     printf("%d\n", num);
  4369.     return 0;
  4370.     }
  4371.     /* if the specified termcap is boolean, and set, say so  *
  4372.      * ncurses can tell if an existing boolean capability is *
  4373.      * off so in this case we print "no".                    */
  4374. #ifndef NCURSES_VERSION
  4375.     if (tgetflag(s) > 0) {
  4376.     puts("yes");
  4377.     return (0);
  4378.     }
  4379. #else
  4380.     switch (tgetflag(s)) {
  4381.     case -1:
  4382.     break;
  4383.     case 0:
  4384.     puts("no");
  4385.     return 0;
  4386.     default:
  4387.     puts("yes");
  4388.     return 0;
  4389.     }
  4390. #endif
  4391.     /* get a string-type capability */
  4392.     u = buf;
  4393.     t = tgetstr(s, &u);
  4394.     if (!t || !*t) {
  4395.     /* capability doesn't exist, or (if boolean) is off */
  4396.     zwarnnam(name, "no such capability: %s", s, 0);
  4397.     return 1;
  4398.     }
  4399.     /* count the number of arguments required */
  4400.     for (argct = 0, u = t; *u; u++)
  4401.     if (*u == '%') {
  4402.         if (u++, (*u == 'd' || *u == '2' || *u == '3' || *u == '.' ||
  4403.               *u == '+'))
  4404.         argct++;
  4405.     }
  4406.     /* check that the number of arguments provided is correct */
  4407.     if (arrlen(argv) != argct) {
  4408.     zwarnnam(name, (arrlen(argv) < argct) ? "not enough arguments" :
  4409.          "too many arguments", NULL, 0);
  4410.     return 1;
  4411.     }
  4412.     /* output string, through the proper termcap functions */
  4413.     if (!argct)
  4414.     tputs(t, 1, putraw);
  4415.     else {
  4416.     num = (argv[1]) ? atoi(argv[1]) : atoi(*argv);
  4417.     tputs(tgoto(t, atoi(*argv), num), num, putraw);
  4418.     }
  4419.     return 0;
  4420. }
  4421.  
  4422. /* shift builtin */
  4423.  
  4424. /**/
  4425. int
  4426. bin_shift(char *name, char **argv, char *ops, int func)
  4427. {
  4428.     int num = 1, l, ret = 0;
  4429.     char **s;
  4430.  
  4431.     /* optional argument can be either numeric or an array */
  4432.     if (*argv && !getaparam(*argv))
  4433.         num = matheval(*argv++);
  4434.  
  4435.     if (num < 0) {
  4436.         zwarnnam(name, "argument to shift must be non-negative", NULL, 0);
  4437.         return 1;
  4438.     }
  4439.  
  4440.     if (*argv) {
  4441.         for (; *argv; argv++)
  4442.             if ((s = getaparam(*argv))) {
  4443.                 if (num > arrlen(s)) {
  4444.             zwarnnam(name, "shift count must be <= $#", NULL, 0);
  4445.             ret++;
  4446.             continue;
  4447.         }
  4448.                 PERMALLOC {
  4449.             s = arrdup(s + num);
  4450.                 } LASTALLOC;
  4451.                 setaparam(*argv, s);
  4452.             }
  4453.     } else {
  4454.         if (num > (l = arrlen(pparams))) {
  4455.         zwarnnam(name, "shift count must be <= $#", NULL, 0);
  4456.         ret = 1;
  4457.     } else {
  4458.         s = zalloc((l - num + 1) * sizeof(char *));
  4459.         memcpy(s, pparams + num, (l - num + 1) * sizeof(char *));
  4460.         while (num--)
  4461.         zsfree(pparams[num]);
  4462.         zfree(pparams, (l + 1) * sizeof(char *));
  4463.         pparams = s;
  4464.     }
  4465.     }
  4466.     return ret;
  4467. }
  4468.  
  4469. /* getopts: automagical option handling for shell scripts */
  4470.  
  4471. /**/
  4472. int
  4473. bin_getopts(char *name, char **argv, char *ops, int func)
  4474. {
  4475.     int lenstr, lenoptstr, i;
  4476.     char *optstr = unmetafy(*argv++, &lenoptstr), *var = *argv++;
  4477.     char **args = (*argv) ? argv : pparams;
  4478.     static int optcind = 1, quiet;
  4479.     char *str, optbuf[2], *opch = optbuf + 1;
  4480.  
  4481.     /* zoptind keeps count of the current argument number */
  4482.     if (zoptind < 1)
  4483.     /* first call */
  4484.     zoptind = 1;
  4485.     if (zoptind == 1)
  4486.     quiet = 0;
  4487.     optbuf[0] = '+';
  4488.     zsfree(zoptarg);
  4489.     zoptarg = ztrdup("");
  4490.     setsparam(var, ztrdup(""));
  4491.     if (*optstr == ':') {
  4492.     quiet = 1;
  4493.     optstr++;
  4494.     lenoptstr--;
  4495.     }
  4496.     if (zoptind > arrlen(args))
  4497.     return 1;
  4498.     str = unmetafy(args[zoptind - 1], &lenstr);
  4499.     if ((*str != '+' && *str != '-') || optcind >= lenstr ||
  4500.     (lenstr == 2 && str[0] == '-' && str[1] == '-')) {
  4501.     /* current argument doesn't contain options, or optcind is impossibly
  4502.     large */
  4503.     if (*str == '+' || *str == '-')
  4504.         zoptind++;
  4505.     optcind = 0;
  4506.     return 1;
  4507.     }
  4508.     /* Get the option character.  optcind records the current position within
  4509.     the argument. */
  4510.     if (!optcind)
  4511.     optcind = 1;
  4512.     *opch = str[optcind++];
  4513.     if (optcind == lenstr) {
  4514.     if(args[zoptind++])
  4515.         str = unmetafy(args[zoptind - 1], &lenstr);
  4516.     optcind = 0;
  4517.     }
  4518.     /* look for option in the provided optstr */
  4519.     for (i = 0; i != lenoptstr; i++)
  4520.     if (*opch == optstr[i])
  4521.         break;
  4522.     if (i == lenoptstr || *opch == ':') {
  4523.     /* not a valid option */
  4524.     setsparam(var, ztrdup("?"));
  4525.     if (quiet) {
  4526.         zsfree(zoptarg);
  4527.         zoptarg = metafy(opch, 1, META_DUP);
  4528.         return 0;
  4529.     }
  4530.     zerr("bad option: -%c", NULL, *opch);
  4531.     errflag = 0;
  4532.     return 0;
  4533.     }
  4534.     /* copy option into specified parameter, with + if required */
  4535.     setsparam(var, metafy(opch - (*str == '+'), 1 + (*str == '+'), META_DUP));
  4536.     /* handle case of an expected extra argument */
  4537.     if (optstr[i + 1] == ':') {
  4538.     if (!args[zoptind - 1]) {
  4539.         /* no extra argument was provided */
  4540.         if (quiet) {
  4541.         zsfree(zoptarg);
  4542.         zoptarg = metafy(opch, 1, META_DUP);
  4543.         setsparam(var, ztrdup(":"));
  4544.         return 0;
  4545.         }
  4546.         setsparam(var, ztrdup("?"));
  4547.         zerr("argument expected after -%c option", NULL, *opch);
  4548.         errflag = 0;
  4549.         return 0;
  4550.     }
  4551.     /* skip over the extra argument */
  4552.     zsfree(zoptarg);
  4553.     zoptarg = metafy(str + optcind, lenstr - optcind, META_DUP);
  4554.     zoptind++;
  4555.     optcind = 0;
  4556.     }
  4557.     return 0;
  4558. }
  4559.  
  4560. /* break, bye, continue, exit, logout, return -- most of these take   *
  4561.  * one numeric argument, and the other (logout) is related to return. *
  4562.  * (return is treated as a logout when in a login shell.)             */
  4563.  
  4564. /**/
  4565. int
  4566. bin_break(char *name, char **argv, char *ops, int func)
  4567. {
  4568.     int num = lastval, nump = 0;
  4569.  
  4570.     /* handle one optional numeric argument */
  4571.     if (*argv) {
  4572.     num = matheval(*argv++);
  4573.     nump = 1;
  4574.     }
  4575.  
  4576.     switch (func) {
  4577.     case BIN_CONTINUE:
  4578.     if (!loops) {   /* continue is only permitted in loops */
  4579.         zerrnam(name, "not in while, until, select, or repeat loop", NULL, 0);
  4580.         return 1;
  4581.     }
  4582.     contflag = 1;   /* ARE WE SUPPOSED TO FALL THROUGH HERE? */
  4583.     case BIN_BREAK:
  4584.     if (!loops) {   /* break is only permitted in loops */
  4585.         zerrnam(name, "not in while, until, select, or repeat loop", NULL, 0);
  4586.         return 1;
  4587.     }
  4588.     breaks = nump ? minimum(num,loops) : 1;
  4589.     break;
  4590.     case BIN_RETURN:
  4591.     if (isset(INTERACTIVE) || locallevel || sourcelevel) {
  4592.         retflag = 1;
  4593.         breaks = loops;
  4594.         lastval = num;
  4595.         if (trapreturn == -2)
  4596.         trapreturn = lastval;
  4597.         return lastval;
  4598.     }
  4599.     zexit(num, 0);    /* else treat return as logout/exit */
  4600.     break;
  4601.     case BIN_LOGOUT:
  4602.     if (unset(LOGINSHELL)) {
  4603.         zerrnam(name, "not login shell", NULL, 0);
  4604.         return 1;
  4605.     }
  4606.     zexit(num, 0);
  4607.     break;
  4608.     case BIN_EXIT:
  4609.     zexit(num, 0);
  4610.     break;
  4611.     }
  4612.     return 0;
  4613. }
  4614.  
  4615. /* exit the shell.  val is the return value of the shell.  *
  4616.  * from_signal should be non-zero if zexit is being called *
  4617.  * because of a signal.                                    */
  4618.  
  4619. /**/
  4620. void
  4621. zexit(int val, int from_signal)
  4622. {
  4623.     static int in_exit;
  4624.  
  4625.     HEAPALLOC {
  4626.     if (isset(MONITOR) && !stopmsg && !from_signal) {
  4627.         scanjobs();    /* check if jobs need printing           */
  4628.         checkjobs();   /* check if any jobs are running/stopped */
  4629.         if (stopmsg) {
  4630.         stopmsg = 2;
  4631.         LASTALLOC_RETURN;
  4632.         }
  4633.     }
  4634.     if (in_exit++ && from_signal)
  4635.         LASTALLOC_RETURN;
  4636.     if (isset(MONITOR))
  4637.         /* send SIGHUP to any jobs left running  */
  4638.         killrunjobs(from_signal);
  4639.     if (isset(RCS) && interact) {
  4640.         if (!nohistsave)
  4641.         savehistfile(getsparam("HISTFILE"), 1, isset(APPENDHISTORY) ? 3 : 0);
  4642.         if (islogin && !subsh) {
  4643.         sourcehome(".zlogout");
  4644. #ifdef GLOBAL_ZLOGOUT
  4645.         source(GLOBAL_ZLOGOUT);
  4646. #endif
  4647.         }
  4648.     }
  4649.     if (sigtrapped[SIGEXIT])
  4650.         dotrap(SIGEXIT);
  4651.     if (mypid != getpid())
  4652.         _exit(val);
  4653.     else
  4654.         exit(val);
  4655.     } LASTALLOC;
  4656. }
  4657.  
  4658. /* . (dot), source */
  4659.  
  4660. /**/
  4661. int
  4662. bin_dot(char *name, char **argv, char *ops, int func)
  4663. {
  4664.     char **old, *old0 = NULL;
  4665.     int ret, diddot = 0, dotdot = 0;
  4666.     char buf[PATH_MAX];
  4667.     char *s, **t, *enam, *arg0;
  4668.     struct stat st;
  4669.  
  4670.     if (!*argv || strlen(*argv) >= PATH_MAX)
  4671.     return 0;
  4672.     old = pparams;
  4673.     /* get arguments for the script */
  4674.     if (argv[1]) {
  4675.     PERMALLOC {
  4676.         pparams = arrdup(argv + 1);
  4677.     } LASTALLOC;
  4678.     }
  4679.     enam = arg0 = ztrdup(*argv);
  4680.     if (isset(FUNCTIONARGZERO)) {
  4681.     old0 = argzero;
  4682.     argzero = arg0;
  4683.     }
  4684.     s = unmeta(enam);
  4685.     errno = ENOENT;
  4686.     ret = 1;
  4687.     /* for source only, check in current directory first */
  4688.     if (*name != '.' && access(s, F_OK) == 0
  4689.     && stat(s, &st) >= 0 && !S_ISDIR(st.st_mode)) {
  4690.     diddot = 1;
  4691.     ret = source(enam);
  4692.     }
  4693.     if (ret) {
  4694.     /* use a path with / in it */
  4695.     for (s = arg0; *s; s++)
  4696.         if (*s == '/') {
  4697.         if (*arg0 == '.') {
  4698.             if (arg0 + 1 == s)
  4699.             ++diddot;
  4700.             else if (arg0[1] == '.' && arg0 + 2 == s)
  4701.             ++dotdot;
  4702.         }
  4703.         ret = source(arg0);
  4704.         break;
  4705.         }
  4706.     if (!*s || (ret && isset(PATHDIRS) && diddot < 2 && dotdot == 0)) {
  4707.         /* search path for script */
  4708.         for (t = path; *t; t++) {
  4709.         if (!(*t)[0] || ((*t)[0] == '.' && !(*t)[1])) {
  4710.             if (diddot)
  4711.             continue;
  4712.             diddot = 1;
  4713.             strcpy(buf, arg0);
  4714.         } else {
  4715.             if (strlen(*t) + strlen(arg0) + 1 >= PATH_MAX)
  4716.             continue;
  4717.             sprintf(buf, "%s/%s", *t, arg0);
  4718.         }
  4719.         s = unmeta(buf);
  4720.         if (access(s, F_OK) == 0 && stat(s, &st) >= 0
  4721.             && !S_ISDIR(st.st_mode)) {
  4722.             ret = source(enam = buf);
  4723.             break;
  4724.         }
  4725.         }
  4726.     }
  4727.     }
  4728.     /* clean up and return */
  4729.     if (argv[1]) {
  4730.     freearray(pparams);
  4731.     pparams = old;
  4732.     }
  4733.     if (ret)
  4734.     zwarnnam(name, "%e: %s", enam, errno);
  4735.     zsfree(arg0);
  4736.     if (old0)
  4737.     argzero = old0;
  4738.     return ret ? ret : lastval;
  4739. }
  4740.  
  4741. /**/
  4742. int
  4743. bin_emulate(char *nam, char **argv, char *ops, int func)
  4744. {
  4745.     emulate(*argv, ops['R']);
  4746.     return 0;
  4747. }
  4748.  
  4749. /* eval: simple evaluation */
  4750.  
  4751. /**/
  4752. int
  4753. bin_eval(char *nam, char **argv, char *ops, int func)
  4754. {
  4755.     List list;
  4756.  
  4757.     inpush(zjoin(argv, ' '), 0, NULL);
  4758.     strinbeg();
  4759.     stophist = 2;
  4760.     list = parse_list();
  4761.     strinend();
  4762.     inpop();
  4763.     if (!list) {
  4764.     errflag = 0;
  4765.     return 1;
  4766.     }
  4767.     execlist(list, 1, 0);
  4768.     if (errflag) {
  4769.     lastval = errflag;
  4770.     errflag = 0;
  4771.     }
  4772.     return lastval;
  4773. }
  4774.  
  4775. static char *zbuf;
  4776. static int readfd;
  4777.  
  4778. /* Read a character from readfd, or from the buffer zbuf.  Return EOF on end of
  4779. file/buffer. */
  4780.  
  4781. extern int cs;
  4782.  
  4783. /* read: get a line of input, or (for compctl functions) return some *
  4784.  * useful data about the state of the editing line.  The -E and -e   *
  4785.  * options mean that the result should be sent to stdout.  -e means, *
  4786.  * in addition, that the result should not actually be assigned to   *
  4787.  * the specified parameters.                                         */
  4788.  
  4789. /**/
  4790. int
  4791. bin_read(char *name, char **args, char *ops, int func)
  4792. {
  4793.     char *reply, *readpmpt;
  4794.     int bsiz, c = 0, gotnl = 0, al = 0, first, nchars = 1, bslash;
  4795.     int haso = 0;    /* true if /dev/tty has been opened specially */
  4796.     int isem = !strcmp(term, "emacs");
  4797.     char *buf, *bptr, *firstarg, *zbuforig;
  4798.     LinkList readll = newlinklist();
  4799.  
  4800.     if ((ops['k'] || ops['b']) && *args && idigit(**args)) {
  4801.     if (!(nchars = atoi(*args)))
  4802.         nchars = 1;
  4803.     args++;
  4804.     }
  4805.  
  4806.     firstarg = *args;
  4807.     if (*args && **args == '?')
  4808.     args++;
  4809.     /* default result parameter */
  4810.     reply = *args ? *args++ : ops['A'] ? "reply" : "REPLY";
  4811.     if (ops['A'] && *args) {
  4812.     zwarnnam(name, "only one array argument allowed", NULL, 0);
  4813.     return 1;
  4814.     }
  4815.  
  4816.     if ((ops['k'] && !ops['u'] && !ops['p']) || ops['q']) {
  4817.     if (SHTTY == -1) {
  4818.         /* need to open /dev/tty specially */
  4819.         SHTTY = open("/dev/tty", O_RDWR);
  4820.         haso = 1;
  4821.     }
  4822.     /* We should have a SHTTY opened by now. */
  4823.     if (SHTTY == -1) {
  4824.         /* Unfortunately, we didn't. */
  4825.         fprintf(stderr, "not interactive and can't open terminal\n");
  4826.         fflush(stderr);
  4827.         return 1;
  4828.     }
  4829.     if (unset(INTERACTIVE))
  4830.         gettyinfo(&shttyinfo);
  4831.     /* attach to the tty */
  4832.     attachtty(mypgrp);
  4833.     if (!isem && ops['k'])
  4834.         setcbreak();
  4835.     readfd = SHTTY;
  4836.     } else if (ops['u'] && !ops['p']) {
  4837.     /* -u means take input from the specified file descriptor. *
  4838.      * -up means take input from the coprocess.                */
  4839.     for (readfd = 9; readfd && !ops[readfd + '0']; --readfd);
  4840.     } else if (ops['p'])
  4841.     readfd = coprocin;
  4842.     else
  4843.     readfd = 0;
  4844.  
  4845.     /* handle prompt */
  4846.     if (firstarg) {
  4847.     for (readpmpt = firstarg;
  4848.          *readpmpt && *readpmpt != '?'; readpmpt++);
  4849.     if (*readpmpt++) {
  4850.         if (isatty(0)) {
  4851.         zputs(readpmpt, stderr);
  4852.         fflush(stderr);
  4853.         }
  4854.         readpmpt[-1] = '\0';
  4855.     }
  4856.     }
  4857.  
  4858.     /* option -k means read only a given number of characters (default 1) */
  4859.     if (ops['k']) {
  4860.     int val;
  4861.     char d;
  4862.  
  4863.     /* allocate buffer space for result */
  4864.     bptr = buf = (char *)zalloc(nchars+1);
  4865.  
  4866.     do {
  4867.         /* If read returns 0, is end of file */
  4868.         if ((val = read(readfd, bptr, nchars)) <= 0)
  4869.         break;
  4870.         
  4871.         /* decrement number of characters read from number required */
  4872.         nchars -= val;
  4873.  
  4874.         /* increment pointer past read characters */
  4875.         bptr += val;
  4876.     } while (nchars > 0);
  4877.     
  4878.     if (!ops['u'] && !ops['p']) {
  4879.         /* dispose of result appropriately, etc. */
  4880.         if (isem)
  4881.         while (val > 0 && read(SHTTY, &d, 1) == 1 && d != '\n');
  4882.         else
  4883.         settyinfo(&shttyinfo);
  4884.         if (haso) {
  4885.         close(SHTTY);
  4886.         SHTTY = -1;
  4887.         }
  4888.     }
  4889.  
  4890.     if (ops['e'] || ops['E'])
  4891.         fwrite(buf, bptr - buf, 1, stdout);
  4892.     if (!ops['e'])
  4893.         setsparam(reply, metafy(buf, bptr - buf, META_REALLOC));
  4894.     else
  4895.         zfree(buf, bptr - buf + 1);
  4896.     return val <= 0;
  4897.     }
  4898.  
  4899.     /* option -l is used in compctl functions */
  4900.     if (ops['l']) {
  4901.     /* only allowed to be called by ZLE */
  4902.     if (!inzlefunc) {
  4903.         zwarnnam(name, "option valid only in functions called from zle",
  4904.              NULL, 0);
  4905.         return 1;
  4906.     }
  4907.     /* -ln gives the index of the word the cursor is currently on, which is
  4908.     available in cs (but remember that Zsh counts from one, not zero!) */
  4909.     if (ops['n']) {
  4910.         char nbuf[14];
  4911.  
  4912.         if (ops['e'] || ops['E'])
  4913.         printf("%d\n", cs + 1);
  4914.         if (!ops['e']) {
  4915.         sprintf(nbuf, "%d", cs + 1);
  4916.         setsparam(reply, ztrdup(nbuf));
  4917.         }
  4918.         return 0;
  4919.     }
  4920.     /* without -n, the current line is assigned to the given parameter as a
  4921.     scalar */
  4922.     if (ops['e'] || ops['E']) {
  4923.         zputs((char *) line, stdout);
  4924.         putchar('\n');
  4925.     }
  4926.     if (!ops['e'])
  4927.         setsparam(reply, ztrdup((char *) line));
  4928.     return 0;
  4929.     }
  4930.  
  4931.     /* option -c is used in compctl functions */
  4932.     if (ops['c']) {
  4933.     int i;
  4934.  
  4935.     /* only allowed to be called by ZLE */
  4936.     if (!inzlefunc) {
  4937.         zwarnnam(name, "option valid only in functions called from zle",
  4938.              NULL, 0);
  4939.         return 1;
  4940.     }
  4941.     /* -cn gives the current cursor position within the current word, which
  4942.     is available in clwpos (but remember that Zsh counts from one, not
  4943.     zero!) */
  4944.     if (ops['n']) {
  4945.         char nbuf[14];
  4946.  
  4947.         if (ops['e'] || ops['E'])
  4948.         printf("%d\n", clwpos + 1);
  4949.         if (!ops['e']) {
  4950.         sprintf(nbuf, "%d", clwpos + 1);
  4951.         setsparam(reply, ztrdup(nbuf));
  4952.         }
  4953.         return 0;
  4954.     }
  4955.     /* without -n, the words of the current line are assigned to the given
  4956.     parameters separately */
  4957.     if (ops['A'] && !ops['e']) {
  4958.         /* the -A option means that one array is specified, instead of
  4959.         many parameters */
  4960.         char **p, **b = (char **)zcalloc((clwnum + 1) * sizeof(char *));
  4961.  
  4962.         for (i = 0, p = b; i < clwnum; p++, i++)
  4963.         *p = ztrdup(clwords[i]);
  4964.  
  4965.         setaparam(reply, b);
  4966.         return 0;
  4967.     }
  4968.     if (ops['e'] || ops['E']) {
  4969.         for (i = 0; i < clwnum; i++) {
  4970.         zputs(clwords[i], stdout);
  4971.         putchar('\n');
  4972.         }
  4973.  
  4974.         if (ops['e'])
  4975.         return 0;
  4976.     }
  4977.  
  4978.     for (i = 0; i < clwnum && *args; reply = *args++, i++)
  4979.         setsparam(reply, ztrdup(clwords[i]));
  4980.  
  4981.     if (i < clwnum) {
  4982.         int j, len;
  4983.  
  4984.         for (j = i, len = 0; j < clwnum; len += strlen(clwords[j++]));
  4985.         bptr = buf = zalloc(len + j - i);
  4986.         while (i < clwnum) {
  4987.         strucpy(&bptr, clwords[i++]);
  4988.         *bptr++ = ' ';
  4989.         }
  4990.         bptr[-1] = '\0';
  4991.     } else
  4992.         buf = ztrdup("");
  4993.     setsparam(reply, buf);
  4994.  
  4995.     return 0;
  4996.     }
  4997.  
  4998.     /* option -q means get one character, and interpret it as a Y or N */
  4999.     if (ops['q']) {
  5000.     char readbuf[2];
  5001.  
  5002.     /* set up the buffer */
  5003.     readbuf[1] = '\0';
  5004.  
  5005.     /* get, and store, reply */
  5006.     readbuf[0] = ((char)getquery(NULL)) == 'y' ? 'y' : 'n';
  5007.  
  5008.     /* dispose of result appropriately, etc. */
  5009.     if (haso) {
  5010.         close(SHTTY);
  5011.         SHTTY = -1;
  5012.     }
  5013.  
  5014.     if (ops['e'] || ops['E'])
  5015.         printf("%s\n", readbuf);
  5016.     if (!ops['e'])
  5017.         setsparam(reply, ztrdup(readbuf));
  5018.  
  5019.     return readbuf[0] == 'n';
  5020.     }
  5021.  
  5022.     /* All possible special types of input have been exhausted.  Take one line,
  5023.     and assign words to the parameters until they run out.  Leftover words go
  5024.     onto the last parameter.  If an array is specified, all the words become
  5025.     separate elements of the array. */
  5026.  
  5027.     zbuforig = zbuf = (!ops['z']) ? NULL :
  5028.     (nonempty(bufstack)) ? (char *) getlinknode(bufstack) : ztrdup("");
  5029.     first = 1;
  5030.     bslash = 0;
  5031.     while (*args || (ops['A'] && !gotnl)) {
  5032.     buf = bptr = (char *)zalloc(bsiz = 64);
  5033.     /* get input, a character at a time */
  5034.     while (!gotnl) {
  5035.         c = zread();
  5036.         /* \ at the end of a line indicates a continuation *
  5037.          * line, except in raw mode (-r option)            */
  5038.         if (bslash && c == '\n') {
  5039.         bslash = 0;
  5040.         continue;
  5041.         }
  5042.         if (c == EOF || c == '\n')
  5043.         break;
  5044.         if (!bslash && isep(c)) {
  5045.         if (bptr != buf || (!iwsep(c) && first)) {
  5046.             first |= !iwsep(c);
  5047.             break;
  5048.         }
  5049.         first |= !iwsep(c);
  5050.         continue;
  5051.         }
  5052.         bslash = c == '\\' && !bslash && !ops['r'];
  5053.         if (bslash)
  5054.         continue;
  5055.         first = 0;
  5056.         if (imeta(c)) {
  5057.         *bptr++ = Meta;
  5058.         *bptr++ = c ^ 32;
  5059.         } else
  5060.         *bptr++ = c;
  5061.         /* increase the buffer size, if necessary */
  5062.         if (bptr >= buf + bsiz - 1) {
  5063.         int blen = bptr - buf;
  5064.  
  5065.         buf = realloc(buf, bsiz *= 2);
  5066.         bptr = buf + blen;
  5067.         }
  5068.     }
  5069.     if (c == '\n' || c == EOF)
  5070.         gotnl = 1;
  5071.     *bptr = '\0';
  5072.     /* dispose of word appropriately */
  5073.     if (ops['e'] || ops['E']) {
  5074.         zputs(buf, stdout);
  5075.         putchar('\n');
  5076.     }
  5077.     if (!ops['e']) {
  5078.         if (ops['A']) {
  5079.         addlinknode(readll, buf);
  5080.         al++;
  5081.         } else
  5082.         setsparam(reply, buf);
  5083.     } else
  5084.         free(buf);
  5085.     if (!ops['A'])
  5086.         reply = *args++;
  5087.     }
  5088.     /* handle EOF */
  5089.     if (c == EOF) {
  5090.     if (readfd == coprocin) {
  5091.         close(coprocin);
  5092.         close(coprocout);
  5093.         coprocin = coprocout = -1;
  5094.     }
  5095.     }
  5096.     /* final assignment (and display) of array parameter */
  5097.     if (ops['A']) {
  5098.     char **pp, **p = NULL;
  5099.     LinkNode n;
  5100.  
  5101.     p = (ops['e'] ? (char **)NULL
  5102.          : (char **)zalloc((al + 1) * sizeof(char *)));
  5103.  
  5104.     for (pp = p, n = firstnode(readll); n; incnode(n)) {
  5105.         if (ops['e'] || ops['E']) {
  5106.         zputs((char *) getdata(n), stdout);
  5107.         putchar('\n');
  5108.         }
  5109.         if (p)
  5110.         *pp++ = (char *)getdata(n);
  5111.         else
  5112.         zsfree(getdata(n));
  5113.     }
  5114.     if (p) {
  5115.         *pp++ = NULL;
  5116.         setaparam(reply, p);
  5117.     }
  5118.     return c == EOF;
  5119.     }
  5120.     buf = bptr = (char *)zalloc(bsiz = 64);
  5121.     /* any remaining part of the line goes into one parameter */
  5122.     bslash = 0;
  5123.     if (!gotnl)
  5124.     for (;;) {
  5125.         c = zread();
  5126.         /* \ at the end of a line introduces a continuation line, except in
  5127.         raw mode (-r option) */
  5128.         if (bslash && c == '\n') {
  5129.         bslash = 0;
  5130.         continue;
  5131.         }
  5132.         if (c == EOF || (c == '\n' && !zbuf))
  5133.         break;
  5134.         if (!bslash && isep(c) && bptr == buf)
  5135.         if (iwsep(c))
  5136.             continue;
  5137.         else if (!first) {
  5138.             first = 1;
  5139.             continue;
  5140.         }
  5141.         bslash = c == '\\' && !bslash && !ops['r'];
  5142.         if (bslash)
  5143.         continue;
  5144.         if (imeta(c)) {
  5145.         *bptr++ = Meta;
  5146.         *bptr++ = c ^ 32;
  5147.         } else
  5148.         *bptr++ = c;
  5149.         /* increase the buffer size, if necessary */
  5150.         if (bptr >= buf + bsiz - 1) {
  5151.         int blen = bptr - buf;
  5152.  
  5153.         buf = realloc(buf, bsiz *= 2);
  5154.         bptr = buf + blen;
  5155.         }
  5156.     }
  5157.     while (bptr > buf && iwsep(bptr[-1]))
  5158.     bptr--;
  5159.     *bptr = '\0';
  5160.     /* final assignment of reply, etc. */
  5161.     if (ops['e'] || ops['E']) {
  5162.     zputs(buf, stdout);
  5163.     putchar('\n');
  5164.     }
  5165.     if (!ops['e'])
  5166.     setsparam(reply, buf);
  5167.     else
  5168.     zsfree(buf);
  5169.     if (zbuforig) {
  5170.     char first = *zbuforig;
  5171.  
  5172.     zsfree(zbuforig);
  5173.     if (!first)
  5174.         return 1;
  5175.     } else if (c == EOF) {
  5176.     if (readfd == coprocin) {
  5177.         close(coprocin);
  5178.         close(coprocout);
  5179.         coprocin = coprocout = -1;
  5180.     }
  5181.     return 1;
  5182.     }
  5183.     return 0;
  5184. }
  5185.  
  5186. /**/
  5187. int
  5188. zread(void)
  5189. {
  5190.     char cc, retry = 0;
  5191.  
  5192.     /* use zbuf if possible */
  5193.     if (zbuf)
  5194.     /* If zbuf points to anything, it points to the next character in the
  5195.     buffer.  This may be a null byte to indicate EOF.  If reading from the
  5196.     buffer, move on the buffer pointer. */
  5197.     if (*zbuf == Meta)
  5198.         return zbuf++, STOUC(*zbuf++ ^ 32);
  5199.     else
  5200.         return (*zbuf) ? STOUC(*zbuf++) : EOF;
  5201.     for (;;) {
  5202.     /* read a character from readfd */
  5203.     switch (read(readfd, &cc, 1)) {
  5204.     case 1:
  5205.         /* return the character read */
  5206.         return STOUC(cc);
  5207.     case -1:
  5208.         if (!retry && errno == EWOULDBLOCK &&
  5209.         readfd == 0 && setblock_stdin()) {
  5210.         retry = 1;
  5211.         continue;
  5212.         }
  5213.         break;
  5214.     }
  5215.     return EOF;
  5216.     }
  5217. }
  5218.  
  5219. /* sched: execute commands at scheduled times */
  5220.  
  5221. /**/
  5222. int
  5223. bin_sched(char *nam, char **argv, char *ops, int func)
  5224. {
  5225.     char *s = *argv++;
  5226.     time_t t;
  5227.     long h, m;
  5228.     struct tm *tm;
  5229.     struct schedcmd *sch, *sch2, *schl;
  5230.     int sn;
  5231.  
  5232.     /* If the argument begins with a -, remove the specified item from the
  5233.     schedule. */
  5234.     if (s && *s == '-') {
  5235.     sn = atoi(s + 1);
  5236.  
  5237.     if (!sn) {
  5238.         zwarnnam("sched", "usage for delete: sched -<item#>.", NULL, 0);
  5239.         return 1;
  5240.     }
  5241.     for (schl = (struct schedcmd *)&schedcmds, sch = schedcmds, sn--;
  5242.          sch && sn; sch = (schl = sch)->next, sn--);
  5243.     if (!sch) {
  5244.         zwarnnam("sched", "not that many entries", NULL, 0);
  5245.         return 1;
  5246.     }
  5247.     schl->next = sch->next;
  5248.     zsfree(sch->cmd);
  5249.     zfree(sch, sizeof(struct schedcmd));
  5250.  
  5251.     return 0;
  5252.     }
  5253.  
  5254.     /* given no arguments, display the schedule list */
  5255.     if (!s) {
  5256.     char tbuf[40];
  5257.  
  5258.     for (sn = 1, sch = schedcmds; sch; sch = sch->next, sn++) {
  5259.         t = sch->time;
  5260.         tm = localtime(&t);
  5261.         ztrftime(tbuf, 20, "%a %b %e %k:%M:%S", tm);
  5262.         printf("%3d %s %s\n", sn, tbuf, sch->cmd);
  5263.     }
  5264.     return 0;
  5265.     } else if (!*argv) {
  5266.     /* other than the two cases above, sched *
  5267.      *requires at least two arguments        */
  5268.     zwarnnam("sched", "not enough arguments", NULL, 0);
  5269.     return 1;
  5270.     }
  5271.  
  5272.     /* The first argument specifies the time to schedule the command for.  The
  5273.     remaining arguments form the command. */
  5274.     if (*s == '+') {
  5275.     /* + introduces a relative time.  The rest of the argument is an
  5276.     hour:minute offset from the current time.  Once the hour and minute
  5277.     numbers have been extracted, and the format verified, the resulting
  5278.     offset is simply added to the current time. */
  5279.     h = zstrtol(s + 1, &s, 10);
  5280.     if (*s != ':') {
  5281.         zwarnnam("sched", "bad time specifier", NULL, 0);
  5282.         return 1;
  5283.     }
  5284.     m = zstrtol(s + 1, &s, 10);
  5285.     if (*s) {
  5286.         zwarnnam("sched", "bad time specifier", NULL, 0);
  5287.         return 1;
  5288.     }
  5289.     t = time(NULL) + h * 3600 + m * 60;
  5290.     } else {
  5291.     /* If there is no +, an absolute time of day must have been given.
  5292.     This is in hour:minute format, optionally followed by a string starting
  5293.     with `a' or `p' (for a.m. or p.m.).  Characters after the `a' or `p'
  5294.     are ignored. */
  5295.     h = zstrtol(s, &s, 10);
  5296.     if (*s != ':') {
  5297.         zwarnnam("sched", "bad time specifier", NULL, 0);
  5298.         return 1;
  5299.     }
  5300.     m = zstrtol(s + 1, &s, 10);
  5301.     if (*s && *s != 'a' && *s != 'A' && *s != 'p' && *s != 'P') {
  5302.         zwarnnam("sched", "bad time specifier", NULL, 0);
  5303.         return 1;
  5304.     }
  5305.     t = time(NULL);
  5306.     tm = localtime(&t);
  5307.     t -= tm->tm_sec + tm->tm_min * 60 + tm->tm_hour * 3600;
  5308.     if (*s == 'p' || *s == 'P')
  5309.         h += 12;
  5310.     t += h * 3600 + m * 60;
  5311.     /* If the specified time is before the current time, it must refer to
  5312.     tomorrow. */
  5313.     if (t < time(NULL))
  5314.         t += 3600 * 24;
  5315.     }
  5316.     /* The time has been calculated; now add the new entry to the linked list
  5317.     of scheduled commands. */
  5318.     sch = (struct schedcmd *) zcalloc(sizeof *sch);
  5319.     sch->time = t;
  5320.     PERMALLOC {
  5321.     sch->cmd = zjoin(argv, ' ');
  5322.     } LASTALLOC;
  5323.     sch->next = NULL;
  5324.     for (sch2 = (struct schedcmd *)&schedcmds; sch2->next; sch2 = sch2->next);
  5325.     sch2->next = sch;
  5326.     return 0;
  5327. }
  5328.  
  5329.  
  5330. /* holds lexer for par_cond():  normally yylex(), testlex() for bin_test() */
  5331. extern void (*condlex) _((void));
  5332.  
  5333. /* holds arguments for testlex() */
  5334. char **testargs;
  5335.  
  5336. /* test, [: the old-style general purpose logical expression builtin */
  5337.  
  5338. /**/
  5339. void
  5340. testlex(void)
  5341. {
  5342.     if (tok == LEXERR)
  5343.     return;
  5344.  
  5345.     tokstr = *testargs;
  5346.     if (!*testargs) {
  5347.     /* if tok is already zero, reading past the end:  error */
  5348.     tok = tok ? NULLTOK : LEXERR;
  5349.     return;
  5350.     } else if (!strcmp(*testargs, "-o"))
  5351.     tok = DBAR;
  5352.     else if (!strcmp(*testargs, "-a"))
  5353.     tok = DAMPER;
  5354.     else if (!strcmp(*testargs, "!"))
  5355.     tok = BANG;
  5356.     else if (!strcmp(*testargs, "("))
  5357.     tok = INPAR;
  5358.     else if (!strcmp(*testargs, ")"))
  5359.     tok = OUTPAR;
  5360.     else
  5361.     tok = STRING;
  5362.     testargs++;
  5363. }
  5364.  
  5365. /**/
  5366. int
  5367. bin_test(char *name, char **argv, char *ops, int func)
  5368. {
  5369.     char **s;
  5370.     Cond c;
  5371.  
  5372.     /* if "test" was invoked as "[", it needs a matching "]" *
  5373.      * which is subsequently ignored                         */
  5374.     if (func == BIN_BRACKET) {
  5375.     for (s = argv; *s; s++);
  5376.     if (s == argv || strcmp(s[-1], "]")) {
  5377.         zwarnnam(name, "']' expected", NULL, 0);
  5378.         return 1;
  5379.     }
  5380.     s[-1] = NULL;
  5381.     }
  5382.     /* an empty argument list evaluates to false (1) */
  5383.     if (!*argv)
  5384.     return 1;
  5385.  
  5386.     testargs = argv;
  5387.     tok = NULLTOK;
  5388.     condlex = testlex;
  5389.     testlex();
  5390.     c = par_cond();
  5391.     condlex = yylex;
  5392.  
  5393.     if (errflag) {
  5394.     errflag = 0;
  5395.     return 1;
  5396.     }
  5397.  
  5398.     if (!c || tok == LEXERR) {
  5399.     zwarnnam(name, tokstr ? "parse error" : "argument expected", NULL, 0);
  5400.     return 1;
  5401.     }
  5402.  
  5403.     /* syntax is OK, so evaluate */
  5404.     return !evalcond(c);
  5405. }
  5406.  
  5407. /* display a time, provided in units of 1/60s, as minutes and seconds */
  5408. #define pttime(X) printf("%ldm%ld.%02lds",((long) (X))/3600,\
  5409.              ((long) (X))/60%60,((long) (X))*100/60%100)
  5410.  
  5411. /* times: display, in a two-line format, the times provided by times(3) */
  5412.  
  5413. /**/
  5414. int
  5415. bin_times(char *name, char **argv, char *ops, int func)
  5416. {
  5417.     struct tms buf;
  5418.  
  5419.     /* get time accounting information */
  5420.     if (times(&buf) == -1)
  5421.     return 1;
  5422.     pttime(buf.tms_utime);    /* user time */
  5423.     putchar(' ');
  5424.     pttime(buf.tms_stime);    /* system time */
  5425.     putchar('\n');
  5426.     pttime(buf.tms_cutime);    /* user time, children */
  5427.     putchar(' ');
  5428.     pttime(buf.tms_cstime);    /* system time, children */
  5429.     putchar('\n');
  5430.     return 0;
  5431. }
  5432.  
  5433. /* trap: set/unset signal traps */
  5434.  
  5435. /**/
  5436. int
  5437. bin_trap(char *name, char **argv, char *ops, int func)
  5438. {
  5439.     List l;
  5440.     char *arg, *s;
  5441.     int sig;
  5442.  
  5443.     if (*argv && !strcmp(*argv, "--"))
  5444.     argv++;
  5445.  
  5446.     /* If given no arguments, list all currently-set traps */
  5447.     if (!*argv) {
  5448.     for (sig = 0; sig < VSIGCOUNT; sig++) {
  5449.         if (sigtrapped[sig] & ZSIG_FUNC) {
  5450.         char fname[20];
  5451.         HashNode hn;
  5452.  
  5453.         sprintf(fname, "TRAP%s", sigs[sig]);
  5454.         if ((hn = shfunctab->getnode(shfunctab, fname)))
  5455.             shfunctab->printnode(hn, 0);
  5456.         DPUTS(!hn, "BUG: I did not find any trap functions!");
  5457.         } else if (sigtrapped[sig]) {
  5458.         if (!sigfuncs[sig])
  5459.             printf("trap -- '' %s\n", sigs[sig]);
  5460.         else {
  5461.             s = getpermtext((void *) dupstruct((void *) sigfuncs[sig]));
  5462.             printf("trap -- ");
  5463.             quotedzputs(s, stdout);
  5464.             printf(" %s\n", sigs[sig]);
  5465.             zsfree(s);
  5466.         }
  5467.         }
  5468.     }
  5469.     return 0;
  5470.     }
  5471.  
  5472.     /* If we have a signal number, unset the specified *
  5473.      * signals.  With only -, remove all traps.        */
  5474.     if ((getsignum(*argv) != -1) || (!strcmp(*argv, "-") && argv++)) {
  5475.     if (!*argv)
  5476.         for (sig = 0; sig < VSIGCOUNT; sig++)
  5477.         unsettrap(sig);
  5478.     else
  5479.         while (*argv)
  5480.         unsettrap(getsignum(*argv++));
  5481.     return 0;
  5482.     }
  5483.  
  5484.     /* Sort out the command to execute on trap */
  5485.     arg = *argv++;
  5486.     if (!*arg)
  5487.     l = NULL;
  5488.     else if (!(l = parse_string(arg))) {
  5489.     zwarnnam(name, "couldn't parse trap command", NULL, 0);
  5490.     return 1;
  5491.     }
  5492.  
  5493.     /* set traps */
  5494.     for (; *argv; argv++) {
  5495.     List t;
  5496.  
  5497.     sig = getsignum(*argv);
  5498.     if (sig == -1) {
  5499.         zwarnnam(name, "undefined signal: %s", *argv, 0);
  5500.         break;
  5501.     }
  5502.     PERMALLOC {
  5503.         t = (List) dupstruct(l);
  5504.     } LASTALLOC;
  5505.     if (settrap(sig, t))
  5506.         freestruct(t);
  5507.     }
  5508.     return *argv != NULL;
  5509. }
  5510.  
  5511. /**/
  5512. int
  5513. bin_ttyctl(char *name, char **argv, char *ops, int func)
  5514. {
  5515.     if (ops['f'])
  5516.     ttyfrozen = 1;
  5517.     else if (ops['u'])
  5518.     ttyfrozen = 0;
  5519.     else
  5520.     printf("tty is %sfrozen\n", ttyfrozen ? "" : "not ");
  5521.     return 0;
  5522. }
  5523.  
  5524. /* let -- mathematical evaluation */
  5525.  
  5526. /**/
  5527. int
  5528. bin_let(char *name, char **argv, char *ops, int func)
  5529. {
  5530.     long val = 0;
  5531.  
  5532.     while (*argv)
  5533.     val = matheval(*argv++);
  5534.     /* Errors in math evaluation in let are non-fatal. */
  5535.     errflag = 0;
  5536.     return !val;
  5537. }
  5538.  
  5539. /* umask command.  umask may be specified as octal digits, or in the  *
  5540.  * symbolic form that chmod(1) uses.  Well, a subset of it.  Remember *
  5541.  * that only the bottom nine bits of umask are used, so there's no    *
  5542.  * point allowing the set{u,g}id and sticky bits to be specified.     */
  5543.  
  5544. /**/
  5545. int
  5546. bin_umask(char *nam, char **args, char *ops, int func)
  5547. {
  5548.     mode_t um;
  5549.     char *s = *args;
  5550.  
  5551.     /* Get the current umask. */
  5552.     um = umask(0);
  5553.     umask(um);
  5554.     /* No arguments means to display the current setting. */
  5555.     if (!s) {
  5556.     if (ops['S']) {
  5557.         char *who = "ugo";
  5558.  
  5559.         while (*who) {
  5560.         char *what = "rwx";
  5561.         printf("%c=", *who++);
  5562.         while (*what) {
  5563.             if (!(um & 0400))
  5564.             putchar(*what);
  5565.             um <<= 1;
  5566.             what++;
  5567.         }
  5568.         putchar(*who ? ',' : '\n');
  5569.         }
  5570.     } else {
  5571.         if (um & 0700)
  5572.         putchar('0');
  5573.         printf("%03o\n", (unsigned)um);
  5574.     }
  5575.     return 0;
  5576.     }
  5577.  
  5578.     if (idigit(*s)) {
  5579.     /* Simple digital umask. */
  5580.     um = zstrtol(s, &s, 8);
  5581.     if (*s) {
  5582.         zwarnnam(nam, "bad umask", NULL, 0);
  5583.         return 1;
  5584.     }
  5585.     } else {
  5586.     /* Symbolic notation -- slightly complicated. */
  5587.     int whomask, umaskop, mask;
  5588.  
  5589.     /* More than one symbolic argument may be used at once, each separated
  5590.     by commas. */
  5591.     for (;;) {
  5592.         /* First part of the argument -- who does this apply to?
  5593.         u=owner, g=group, o=other. */
  5594.         whomask = 0;
  5595.         while (*s == 'u' || *s == 'g' || *s == 'o' || *s == 'a')
  5596.         if (*s == 'u')
  5597.             s++, whomask |= 0700;
  5598.         else if (*s == 'g')
  5599.             s++, whomask |= 0070;
  5600.         else if (*s == 'o')
  5601.             s++, whomask |= 0007;
  5602.         else if (*s == 'a')
  5603.             s++, whomask |= 0777;
  5604.         /* Default whomask is everyone. */
  5605.         if (!whomask)
  5606.         whomask = 0777;
  5607.         /* Operation may be +, - or =. */
  5608.         umaskop = (int)*s;
  5609.         if (!(umaskop == '+' || umaskop == '-' || umaskop == '=')) {
  5610.         if (umaskop)
  5611.             zwarnnam(nam, "bad symbolic mode operator: %c", NULL, umaskop);
  5612.         else
  5613.             zwarnnam(nam, "bad umask", NULL, 0);
  5614.         return 1;
  5615.         }
  5616.         /* Permissions mask -- r=read, w=write, x=execute. */
  5617.         mask = 0;
  5618.         while (*++s && *s != ',')
  5619.         if (*s == 'r')
  5620.             mask |= 0444 & whomask;
  5621.         else if (*s == 'w')
  5622.             mask |= 0222 & whomask;
  5623.         else if (*s == 'x')
  5624.             mask |= 0111 & whomask;
  5625.         else {
  5626.             zwarnnam(nam, "bad symbolic mode permission: %c",
  5627.                  NULL, *s);
  5628.             return 1;
  5629.         }
  5630.         /* Apply parsed argument to um. */
  5631.         if (umaskop == '+')
  5632.         um &= ~mask;
  5633.         else if (umaskop == '-')
  5634.         um |= mask;
  5635.         else        /* umaskop == '=' */
  5636.         um = (um | (whomask)) & ~mask;
  5637.         if (*s == ',')
  5638.         s++;
  5639.         else
  5640.         break;
  5641.     }
  5642.     if (*s) {
  5643.         zwarnnam(nam, "bad character in symbolic mode: %c", NULL, *s);
  5644.         return 1;
  5645.     }
  5646.     }
  5647.  
  5648.     /* Finally, set the new umask. */
  5649.     umask(um);
  5650.     return 0;
  5651. }
  5652.  
  5653. /*** debugging functions ***/
  5654.  
  5655. #ifdef ZSH_HASH_DEBUG
  5656. /**/
  5657. int
  5658. bin_hashinfo(char *nam, char **args, char *ops, int func)
  5659. {
  5660.     printf("----------------------------------------------------\n");
  5661.     cmdnamtab->printinfo(cmdnamtab);
  5662.     printf("----------------------------------------------------\n");
  5663.     shfunctab->printinfo(shfunctab);
  5664.     printf("----------------------------------------------------\n");
  5665.     builtintab->printinfo(builtintab);
  5666.     printf("----------------------------------------------------\n");
  5667.     paramtab->printinfo(paramtab);
  5668.     printf("----------------------------------------------------\n");
  5669.     compctltab->printinfo(compctltab);
  5670.     printf("----------------------------------------------------\n");
  5671.     aliastab->printinfo(aliastab);
  5672.     printf("----------------------------------------------------\n");
  5673.     reswdtab->printinfo(reswdtab);
  5674.     printf("----------------------------------------------------\n");
  5675.     emkeybindtab->printinfo(emkeybindtab);
  5676.     printf("----------------------------------------------------\n");
  5677.     vikeybindtab->printinfo(vikeybindtab);
  5678.     printf("----------------------------------------------------\n");
  5679.     nameddirtab->printinfo(nameddirtab);
  5680.     printf("----------------------------------------------------\n");
  5681.     return 0;
  5682. }
  5683. #endif
  5684.  
  5685. /**** utility functions -- should go in utils.c ****/
  5686.  
  5687. /* Separate an argument into name=value parts, returning them in an     *
  5688.  * asgment structure.  Because the asgment structure used is global,    *
  5689.  * only one of these can be active at a time.  The string s gets placed *
  5690.  * in this global structure, so it needs to be in permanent memory.     */
  5691.  
  5692. /**/
  5693. Asgment
  5694. getasg(char *s)
  5695. {
  5696.     static struct asgment asg;
  5697.  
  5698.     /* sanity check for valid argument */
  5699.     if (!s)
  5700.     return NULL;
  5701.  
  5702.     /* check if name is empty */
  5703.     if (*s == '=') {
  5704.     zerr("bad assignment", NULL, 0);
  5705.     return NULL;
  5706.     }
  5707.     asg.name = s;
  5708.  
  5709.     /* search for `=' */
  5710.     for (; *s && *s != '='; s++);
  5711.  
  5712.     /* found `=', so return with a value */
  5713.     if (*s) {
  5714.     *s = '\0';
  5715.     asg.value = s + 1;
  5716.     } else {
  5717.     /* didn't find `=', so we only have a name */
  5718.     asg.value = NULL;
  5719.     }
  5720.     return &asg;
  5721. }
  5722.  
  5723. /* Get a signal number from a string */
  5724.  
  5725. /**/
  5726. int
  5727. getsignum(char *s)
  5728. {
  5729.     int x, i;
  5730.  
  5731.     /* check for a signal specified by number */
  5732.     x = atoi(s);
  5733.     if (idigit(*s) && x >= 0 && x < VSIGCOUNT)
  5734.     return x;
  5735.  
  5736.     /* search for signal by name */
  5737.     for (i = 0; i < VSIGCOUNT; i++)
  5738.     if (!strcmp(s, sigs[i]))
  5739.         return i;
  5740.  
  5741.     /* no matching signal */
  5742.     return -1;
  5743. }
  5744.