home *** CD-ROM | disk | FTP | other *** search
/ ProfitPress Mega CDROM2 …eeware (MSDOS)(1992)(Eng) / ProfitPress-MegaCDROM2.B6I / MISC / GNU / SH164AS.ZIP / SHELL / SH7.C < prev    next >
Encoding:
C/C++ Source or Header  |  1992-02-28  |  37.2 KB  |  2,115 lines

  1. /* MS-DOS SHELL - Internal Command Processing
  2.  *
  3.  * MS-DOS SHELL - Copyright (c) 1990 Data Logic Limited and Charles Forsyth
  4.  *
  5.  * This code is based on (in part) the shell program written by Charles
  6.  * Forsyth and is subject to the following copyright restrictions.  The
  7.  * code for the test (dotest) command was based on code written by
  8.  * Erik Baalbergen.  The following copyright conditions apply:
  9.  *
  10.  * 1.  Redistribution and use in source and binary forms are permitted
  11.  *     provided that the above copyright notice is duplicated in the
  12.  *     source form and the copyright notice in file sh6.c is displayed
  13.  *     on entry to the program.
  14.  *
  15.  * 2.  The sources (or parts thereof) or objects generated from the sources
  16.  *     (or parts of sources) cannot be sold under any circumstances.
  17.  *
  18.  *    $Header: C:/SRC/SHELL/RCS/sh7.c 1.20 90/09/19 15:33:07 Ian_Stewartson Exp $
  19.  *
  20.  *    $Log:    sh7.c $
  21.  * Revision 1.20  90/09/19  15:33:07  Ian_Stewartson
  22.  * Allow builtin commands to selected/de-selected
  23.  * 
  24.  * Revision 1.19  90/09/11  19:42:59  Ian_Stewartson
  25.  * Implement builtin command
  26.  *
  27.  * Revision 1.18  90/08/14  23:34:55  MS_user
  28.  * Fix directory display on change directory
  29.  * Fix spelling mistake.
  30.  *
  31.  * Revision 1.17  90/05/31  09:50:05  MS_user
  32.  * Implement partial write when swapping to disk
  33.  *
  34.  * Revision 1.16  90/04/30  19:50:44  MS_user
  35.  * Stop search path if second character of name is colon
  36.  *
  37.  * Revision 1.15  90/04/25  22:35:53  MS_user
  38.  * Fix bug in doread to stop multi-line reads
  39.  *
  40.  * Revision 1.14  90/04/25  09:21:11  MS_user
  41.  * Change version message processing
  42.  *
  43.  * Revision 1.13  90/04/03  17:59:43  MS_user
  44.  * type didnot check for functions before searching PATH
  45.  *
  46.  * Revision 1.12  90/03/27  20:33:58  MS_user
  47.  * Clear extended file name on interrupt
  48.  *
  49.  * Revision 1.11  90/03/26  20:57:38  MS_user
  50.  * Change I/O restore so that "exec >filename" works
  51.  *
  52.  * Revision 1.10  90/03/14  19:32:05  MS_user
  53.  * Change buffered output to be re-entrant and add it to getopt
  54.  *
  55.  * Revision 1.9  90/03/14  16:45:52  MS_user
  56.  * New Open_buffer parameter
  57.  *
  58.  * Revision 1.8  90/03/13  21:19:50  MS_user
  59.  * Use the new Buffered Output routines in doecho
  60.  *
  61.  * Revision 1.7  90/03/12  20:43:52  MS_user
  62.  * Change bell test to check initialisation file
  63.  *
  64.  * Revision 1.6  90/03/12  17:09:38  MS_user
  65.  * Add a missing cast
  66.  *
  67.  * Revision 1.5  90/03/09  16:06:41  MS_user
  68.  * Add SH_BELL processing
  69.  *
  70.  * Revision 1.4  90/03/06  16:50:10  MS_user
  71.  * Add disable history option
  72.  *
  73.  * Revision 1.3  90/03/05  13:52:49  MS_user
  74.  * Changes to eval and dot functionality
  75.  * Fix bug in escape processing in doecho
  76.  * Add some array size checks
  77.  *
  78.  * Revision 1.2  90/01/30  14:43:34  MS_user
  79.  * Add missing author note
  80.  *
  81.  * Revision 1.1  90/01/29  17:46:25  MS_user
  82.  * Initial revision
  83.  *
  84.  *
  85.  */
  86.  
  87. #include <sys/types.h>
  88. #include <sys/stat.h>
  89. #include <stdio.h>
  90. #include <process.h>
  91. #include <dos.h>
  92. #include <signal.h>
  93. #include <errno.h>
  94. #include <setjmp.h>
  95. #include <ctype.h>
  96. #include <string.h>
  97. #include <unistd.h>
  98. #include <stdlib.h>
  99. #include <fcntl.h>
  100. #include <limits.h>
  101. #include <dirent.h>
  102. #include <stdarg.h>
  103. #include "sh.h"
  104.  
  105. #define    SECS        60L
  106. #define    MINS        3600L
  107. #define IS_OCTAL(a)    (((a) >= '0') && ((a) <= '7'))
  108.  
  109. /* Definitions for test */
  110.  
  111. #define END_OF_INPUT    0
  112. #define FILE_READABLE    1
  113. #define FILE_WRITABLE    2
  114. #define FILE_REGULAR    3
  115. #define FILE_DIRECTORY    4
  116. #define FILE_NONZERO    5
  117. #define FILE_TERMINAL    6
  118. #define STRING_ZERO    7
  119. #define STRING_NONZERO    8
  120. #define STRING_EQUAL    9
  121. #define STRING_NOTEQUAL    10
  122. #define NUMBER_EQUAL    11
  123. #define NUMBER_NOTEQUAL    12
  124. #define NUMBER_EQ_GREAT    13
  125. #define NUMBER_GREATER    14
  126. #define NUMBER_EQ_LESS    15
  127. #define NUMBER_LESS    16
  128. #define UNARY_NOT    17
  129. #define BINARY_AND    18
  130. #define BINARY_OR    19
  131. #define LPAREN        20
  132. #define RPAREN        21
  133. #define OPERAND        22
  134. #define FILE_EXECUTABLE    23
  135. #define FILE_USER    24
  136. #define FILE_GROUP    25
  137. #define FILE_TEXT    26
  138. #define FILE_BLOCK    27
  139. #define FILE_CHARACTER    28
  140. #define FILE_FIFO    29
  141.  
  142. #define UNARY_OP    1
  143. #define BINARY_OP    2
  144. #define B_UNARY_OP    3
  145. #define B_BINARY_OP    4
  146. #define PAREN        5
  147.  
  148. static struct test_op {
  149.     char    *op_text;
  150.     short     op_num;
  151.     short     op_type;
  152. } test_ops[] = {
  153.     {"-r",    FILE_READABLE,        UNARY_OP},
  154.     {"-w",    FILE_WRITABLE,        UNARY_OP},
  155.     {"-x",    FILE_EXECUTABLE,    UNARY_OP},
  156.     {"-f",    FILE_REGULAR,        UNARY_OP},
  157.     {"-d",    FILE_DIRECTORY,        UNARY_OP},
  158.     {"-s",    FILE_NONZERO,        UNARY_OP},
  159.     {"-t",    FILE_TERMINAL,        UNARY_OP},
  160.     {"-z",    STRING_ZERO,        UNARY_OP},
  161.     {"-n",    STRING_NONZERO,        UNARY_OP},
  162.     {"=",    STRING_EQUAL,        BINARY_OP},
  163.     {"!=",    STRING_NOTEQUAL,    BINARY_OP},
  164.     {"-eq",    NUMBER_EQUAL,        BINARY_OP},
  165.     {"-ne",    NUMBER_NOTEQUAL,    BINARY_OP},
  166.     {"-ge",    NUMBER_EQ_GREAT,    BINARY_OP},
  167.     {"-gt",    NUMBER_GREATER,        BINARY_OP},
  168.     {"-le",    NUMBER_EQ_LESS,        BINARY_OP},
  169.     {"-lt",    NUMBER_LESS,        BINARY_OP},
  170.     {"!",    UNARY_NOT,        B_UNARY_OP},
  171.     {"-a",    BINARY_AND,        B_BINARY_OP},
  172.     {"-o",    BINARY_OR,        B_BINARY_OP},
  173.     {"(",    LPAREN,            PAREN},
  174.     {")",    RPAREN,            PAREN},
  175. #ifdef S_IFCHR
  176.     {"-c",    FILE_CHARACTER,        UNARY_OP},
  177. #endif
  178. #ifdef S_IFBLK
  179.     {"-b",    FILE_BLOCK,        UNARY_OP},
  180. #endif
  181. #ifdef S_ISUID
  182.     {"-u",    FILE_USER,        UNARY_OP},
  183. #endif
  184. #ifdef S_ISGID
  185.     {"-g",    FILE_GROUP,        UNARY_OP},
  186. #endif
  187. #ifdef S_ISVTX
  188.     {"-k",    FILE_TEXT,        UNARY_OP},
  189. #endif
  190. #ifdef S_IFIFO
  191.     {"-p",    FILE_FIFO,        UNARY_OP},
  192. #endif
  193.     {(char *)NULL,    NULL,        NULL}
  194. };
  195.  
  196. static int        expr (int);
  197. static int        bexpr (int);
  198. static int        primary (int);
  199. static int        lex (char *);
  200. static long        num (char *);
  201. static void        syntax (void);
  202. static int        dolabel (C_Op *);
  203. static int        dochdir (C_Op *);
  204. static int        dodrive (C_Op *);
  205. static int        doshift (C_Op *);
  206. static int        doumask (C_Op *);
  207. static int        dodot (C_Op *);
  208. static int        doecho (C_Op *);
  209. static int        dogetopt (C_Op *);
  210. static int        dopwd (C_Op *);
  211. static int        doswap (C_Op *);
  212. static int        dounset (C_Op *);
  213. static int        dotype (C_Op *);
  214. static int        dotest (C_Op *);
  215. static int        dover (C_Op *);
  216. static int        doread (C_Op *);
  217. static int        doeval (C_Op *);
  218. static int        dotrap (C_Op *);
  219. static int        dobuiltin (C_Op *);
  220. static int        getsig (char *);
  221. static int        dobreak (C_Op *);
  222. static int        docontinue (C_Op *);
  223. static int        brkcontin (char *, int);
  224. static int        doexit (C_Op *);
  225. static int        doexec (C_Op *);
  226. static int        doreturn (C_Op *);
  227. static int        doexport (C_Op *);
  228. static int        domsdos (C_Op *);
  229. static int        doreadonly (C_Op *);
  230. static int        doset (C_Op *);
  231. static int        dohistory (C_Op *);
  232. static void        setsig (int, int (*)());
  233. static int        rdexp (char **, int, char *);
  234.  
  235. static char        *not_builtin = "%s: not a builtin\n";
  236. static char        **test_alist;
  237. static struct test_op    *test_op;
  238. static jmp_buf        test_jmp;
  239.  
  240. /*
  241.  * built-in commands: doX
  242.  */
  243.  
  244. static int    dolabel (t)
  245. C_Op        *t;
  246. {
  247.     return 0;
  248. }
  249.  
  250. /*
  251.  * Getopt - split arguments.  getopts pattern args
  252.  */
  253.  
  254. static int    dogetopt (t)
  255. register C_Op    *t;
  256. {
  257.     int            argc;
  258.     char        **argv = t->words;
  259.     int            c;
  260.     Out_Buf        *bp;
  261.     char        *c_s = "-c ";
  262.  
  263. /* Count arguments */
  264.  
  265.     optind = 1;                /* Reset the optind flag    */
  266.     opterr = 1;                /* Reset the error flag        */
  267.  
  268.     for (argc = 0; t->words[argc] != (char *)NULL; argc++);
  269.  
  270.     if (argc < 2)
  271.     {
  272.     S_puts ("usage: getopt legal-args $*\n");
  273.     return 2;
  274.     }
  275.  
  276. /* Get some memory for the buffer */
  277.  
  278.     if ((bp = Open_buffer (1, FALSE)) == (Out_Buf *)NULL)
  279.     {
  280.     print_error ("getopt: %s\n", strerror (ENOMEM));
  281.     return 1;
  282.     }
  283.  
  284.     argc -= 2;
  285.     argv += 2;
  286.  
  287. /* Scan each argument */
  288.  
  289.     while ((c = getopt (argc, argv, t->words[1])) != EOF)
  290.     {
  291.     if (c == '?')
  292.         return 2;
  293.  
  294.     *(c_s + 1) = (char)c;
  295.     Adds_buffer (c_s, bp);
  296.  
  297. /* Check for addition parameter */
  298.  
  299.     if (*(strchr (t->words[1], c) + 1) == ':')
  300.     {
  301.         Adds_buffer (optarg, bp);
  302.         Add_buffer (SP, bp);
  303.     }
  304.     }
  305.  
  306. /* Output the separator */
  307.  
  308.     Adds_buffer ("-- ", bp);
  309.     argv += optind;
  310.  
  311. /* Output the arguments */
  312.  
  313.     while (optind++ < argc)
  314.     {
  315.     Adds_buffer (*argv++, bp);
  316.     Add_buffer ((char)((optind == argc) ? NL : SP), bp);
  317.     }
  318.  
  319.     Close_buffer (bp);
  320.     return 0;
  321. }
  322.  
  323. /*
  324.  * Echo the parameters
  325.  */
  326.  
  327. static int    doecho (t)
  328. register C_Op    *t;
  329. {
  330.     int        n = 1;            /* Argument number        */
  331.     int        no_eol = 0;        /* No EOL            */
  332.     char    *ip;            /* Input pointer        */
  333.     int        c_val;            /* Current character        */
  334.     char    c;
  335.     bool    end_s;
  336.     Out_Buf    *bp;
  337.  
  338. /* Get some memory for the buffer */
  339.  
  340.     if ((bp = Open_buffer (1, FALSE)) == (Out_Buf *)NULL)
  341.     {
  342.     print_error ("echo: %s\n", strerror (ENOMEM));
  343.     return 1;
  344.     }
  345.  
  346. /* Process the arguments */
  347.  
  348.     while ((ip = t->words[n++]) != (char *)NULL)
  349.     {
  350.  
  351. /* Check for -n switch */
  352.  
  353.     if ((n == 2) && (strcmp (ip, "-n") == 0))
  354.     {
  355.         no_eol++;
  356.         continue;
  357.     }
  358.  
  359. /* Process the string */
  360.  
  361.     end_s = FALSE;
  362.  
  363.     do
  364.     {
  365.  
  366. /* Any special character processing ? */
  367.  
  368.         if ((c = *(ip++)) == '\\')
  369.         {
  370.         if ((c_val = Process_Escape (&ip)) == -1)
  371.         {
  372.             no_eol = 1;
  373.             continue;
  374.         }
  375.  
  376.         c = (char)c_val;
  377.         }
  378.  
  379. /* End of string - check to see if a space if required */
  380.  
  381.         else if (c == 0)
  382.         {
  383.         end_s = TRUE;
  384.  
  385.         if (t->words[n] != (char *)NULL)
  386.             c = SP;
  387.  
  388.         else
  389.             continue;
  390.         }
  391.  
  392. /* Output the character */
  393.  
  394.         Add_buffer (c, bp);
  395.  
  396.     } while (!end_s);
  397.     }
  398.  
  399. /* Is EOL required ? */
  400.  
  401.     if (!no_eol)
  402.     Add_buffer (NL, bp);
  403.  
  404. /* Flush buffer */
  405.  
  406.     Close_buffer (bp);
  407.     return 0;
  408. }
  409.  
  410. /*
  411.  * Process_Escape - Convert an escaped character to a binary value.
  412.  *
  413.  * Returns the binary value and updates the string pointer.
  414.  */
  415.  
  416. int    Process_Escape (cp)
  417. char    **cp;                    /* Pointer to character */
  418. {
  419.     int        c_val = **cp;            /* Current character    */
  420.  
  421.     if (c_val)
  422.         (*cp)++;
  423.  
  424. /* Process escaped characters */
  425.  
  426.     switch (c_val)
  427.     {
  428.         case 'b':            /* Backspace                    */
  429.             return 0x08;
  430.  
  431.         case 'f':            /* Form Feed                    */
  432.             return 0x0c;
  433.  
  434.         case 'v':            /* Vertical Tab                 */
  435.             return 0x0b;
  436.  
  437.         case 'n':            /* New Line                     */
  438.             return 0x0a;
  439.  
  440.         case 'r':            /* Carriage return              */
  441.             return 0x0d;
  442.  
  443.         case 't':            /* Forward tab                  */
  444.         return 0x09;
  445.  
  446.         case '\\':            /* Backslash                    */
  447.         return '\\';
  448.  
  449.         case 'c':            /* no eol            */
  450.         return -1;
  451.     }
  452.  
  453. /* Check for an octal string */
  454.  
  455.     if (IS_OCTAL (c_val))
  456.     {
  457.     c_val -= '0';
  458.  
  459.     while ((IS_OCTAL (**cp)))
  460.         c_val = (c_val * 8) + *((*cp)++) - '0';
  461.  
  462.     return c_val;
  463.     }
  464.  
  465.     return c_val;
  466. }
  467.  
  468. /*
  469.  * Display the current version
  470.  */
  471.  
  472. static int    dover (t)
  473. C_Op        *t;
  474. {
  475.     Print_Version (1);
  476.     return 0;
  477. }
  478.  
  479. static char    *swap_device[] = {"disk", "extend", "expand"};
  480.  
  481. /*
  482.  * Modify swapping information: swap options
  483.  */
  484.  
  485. static int    doswap (t)
  486. register C_Op    *t;
  487. {
  488.     register int    n = 1;
  489.     char        *cp;
  490.  
  491. /* Display current values ? */
  492.  
  493.     if (t->words[1] == (char *)NULL)
  494.     {
  495.     if (Swap_Mode == SWAP_OFF)
  496.         v1a_puts ("Swapping disabled");
  497.  
  498.     else
  499.     {
  500.         register int    j;
  501.  
  502.         v1_puts ("Swap devices: ");
  503.  
  504.         for (j = 0, n = 1; j < 3; ++j, n <<= 1)
  505.         {
  506.         if (Swap_Mode & n)
  507.         {
  508.             v1printf ("%s ", swap_device[j]);
  509.  
  510.             if (n == SWAP_EXTEND)
  511.             v1printf ("(0x%.6lx) ", SW_EMstart);
  512.         }
  513.         }
  514.  
  515.         v1_putc (NL);
  516.     }
  517.  
  518.     return 0;
  519.     }
  520.  
  521. /* Set up new values */
  522.  
  523.     Swap_Mode = SWAP_OFF;
  524.     Clear_Swap_File ();
  525.  
  526.     while ((cp = t->words[n++]) != (char *)NULL)
  527.     {
  528.     if (strcmp (cp, "off") == 0)
  529.         Swap_Mode = SWAP_OFF;
  530.  
  531.     else if (strcmp (cp, "on") == 0)
  532.         Swap_Mode = SWAP_DISK | SWAP_EXPAND | SWAP_EXTEND;
  533.  
  534. /* Scan for valid arguments */
  535.  
  536.     else
  537.     {
  538.         register int    j, k;
  539.  
  540.         for (j = 0, k = 1; j < 3; ++j, k <<= 1)
  541.         {
  542.         if (strcmp (cp, swap_device[j]) == 0)
  543.         {
  544.             Swap_Mode |= k;
  545.  
  546. /* If extended memory, they can specify the start address as a hex number */
  547.  
  548.             if (k == SWAP_EXTEND)
  549.             {
  550.             char    *sp;
  551.             long    start;
  552.  
  553. /* Check for not changed */
  554.  
  555.             if ((sp = t->words[n]) == (char *)NULL)
  556.                 break;
  557.  
  558. /* Convert hex number */
  559.  
  560.             start = strtol (sp, &sp, 16);
  561.  
  562. /* If not completely a hex number, ignore */
  563.  
  564.             if (*sp)
  565.                 break;
  566.  
  567. /* Set used and saved new value */
  568.  
  569.             SW_EMstart = start;
  570.             ++n;
  571.  
  572.             if ((SW_EMstart < 0x100000L) ||
  573.                 (SW_EMstart > 0xf00000L))
  574.                 SW_EMstart = 0x100000L;
  575.  
  576.             v1printf ("Extend memory start set to 0x%.6lx\n",
  577.                   SW_EMstart);
  578.             }
  579.  
  580.             break;
  581.         }
  582.         }
  583.     }
  584.     }
  585.  
  586.     return 0;
  587. }
  588.  
  589. /*
  590.  * Output the current path: pwd
  591.  */
  592.  
  593. static int    dopwd (t)
  594. register C_Op    *t;
  595. {
  596.     v1a_puts (C_dir->value);
  597.     return 0;
  598. }
  599.  
  600. /*
  601.  * Unset a variable: unset <flag..> <variable name...>
  602.  */
  603.  
  604. static int    dounset (t)
  605. register C_Op    *t;
  606. {
  607.     register int    n = 1;
  608.  
  609.     while (t->words[n] != (char *)NULL)
  610.         unset (t->words[n++], FALSE);
  611.  
  612.     return 0;
  613. }
  614.  
  615. /* Delete a variable or function.  If all is set, system variables can be
  616.  * deleted.  This is used to delete the trap functions
  617.  */
  618.  
  619. void        unset (cp, all)
  620. register char    *cp;
  621. bool        all;
  622. {
  623.     register Var_List        *vp;
  624.     register Var_List        *pvp;
  625.  
  626. /* Unset a flag */
  627.  
  628.     if (*cp == '-')
  629.     {
  630.     while (*(++cp) != 0)
  631.     {
  632.         if (islower (*cp))
  633.         FL_CLEAR (*cp);
  634.     }
  635.  
  636.     setdash ();
  637.     return;
  638.     }
  639.  
  640. /* Ok - unset a variable and not a local value */
  641.  
  642.     if (!all && !(isalpha (*cp)))
  643.     return;
  644.  
  645. /* Check in list */
  646.  
  647.     pvp = (Var_List *)NULL;
  648.  
  649.     for (vp = vlist; (vp != (Var_List *)NULL) && !eqname (vp->name, cp);
  650.      vp = vp->next)
  651.     pvp = vp;
  652.  
  653. /* If not found, delete the function if it exists */
  654.  
  655.     if (vp == (Var_List *)NULL)
  656.     {
  657.     Fun_Ops     *fp;
  658.  
  659.     if ((fp = Fun_Search (cp)) != (Fun_Ops *)NULL)
  660.         Save_Function (fp->tree, TRUE);
  661.  
  662.     return;
  663.     }
  664.  
  665. /* Error if read-only */
  666.  
  667.     if (vp->status & (RONLY | PONLY))
  668.     {
  669.     if ((cp = strchr (vp->name, '=')) != (char *)NULL)
  670.         *cp = 0;
  671.  
  672.     S_puts (vp->name);
  673.  
  674.     if (cp != (char *)NULL)
  675.         *cp = '=';
  676.  
  677.     S_puts ((vp->status & PONLY) ? ": cannot unset\n" : " is read-only\n");
  678.     return;
  679.     }
  680.  
  681. /* Delete it */
  682.  
  683.     if (vp->status & GETCELL)
  684.     DELETE (vp->name);
  685.  
  686.     if (pvp == (Var_List *)NULL)
  687.     vlist = vp->next;
  688.  
  689.     else
  690.     pvp->next = vp->next;
  691.  
  692.     DELETE (vp);
  693. }
  694.  
  695. /*
  696.  * Execute a test: test <arguments>
  697.  */
  698.  
  699. static int    dotest (t)
  700. register C_Op    *t;
  701. {
  702.     int        st = 0;
  703.  
  704.     if (*(test_alist = &t->words[1]) == (char *)NULL)
  705.     return 1;
  706.  
  707. /* If [ <arguments> ] form, check for end ] and remove it */
  708.  
  709.     if (strcmp (t->words[0], "[") == 0)
  710.     {
  711.     while (t->words[++st] != (char *)NULL)
  712.         ;
  713.  
  714.     if (strcmp (t->words[--st], "]") != 0)
  715.     {
  716.         print_error ("test: missing ']'\n");
  717.         return 1;
  718.     }
  719.  
  720.     else
  721.         t->words[st] = (char *)NULL;
  722.     }
  723.  
  724. /* Set abort address */
  725.  
  726.     if (setjmp (test_jmp))
  727.     return 1;
  728.  
  729.     st = !expr (lex (*test_alist));
  730.  
  731.     if (*(++test_alist) != (char *)NULL)
  732.     syntax ();
  733.  
  734.     return (st);
  735. }
  736.  
  737. static int    expr (n)
  738. int        n;
  739. {
  740.     int        res;
  741.  
  742.     if (n == END_OF_INPUT)
  743.     syntax ();
  744.  
  745.     res = bexpr (n);
  746.  
  747.     if (lex (*(++test_alist)) == BINARY_OR)
  748.     return expr (lex (*(++test_alist))) || res;
  749.  
  750.     test_alist--;
  751.     return res;
  752. }
  753.  
  754. static int    bexpr (n)
  755. int        n;
  756. {
  757.     int res;
  758.  
  759.     if (n == END_OF_INPUT)
  760.     syntax ();
  761.  
  762.     res = primary (n);
  763.     if (lex (*(++test_alist)) == BINARY_AND)
  764.     return bexpr (lex (*(++test_alist))) && res;
  765.  
  766.     test_alist--;
  767.     return res;
  768. }
  769.  
  770. static int    primary (n)
  771. int        n;
  772. {
  773.     register char    *opnd1, *opnd2;
  774.     struct stat        s;
  775.     int            res;
  776.  
  777.     if (n == END_OF_INPUT)
  778.     syntax ();
  779.  
  780.     if (n == UNARY_NOT)
  781.     return !expr (lex (*(++test_alist)));
  782.  
  783.     if (n == LPAREN)
  784.     {
  785.     res = expr (lex (*(++test_alist)));
  786.  
  787.     if (lex (*(++test_alist)) != RPAREN)
  788.         syntax ();
  789.  
  790.     return res;
  791.     }
  792.  
  793.     if (n == OPERAND)
  794.     {
  795.     opnd1 = *test_alist;
  796.     (void) lex (*(++test_alist));
  797.  
  798.     if ((test_op != (C_Op *)NULL) && test_op->op_type == BINARY_OP)
  799.     {
  800.         struct test_op *op = test_op;
  801.  
  802.         if ((opnd2 = *(++test_alist)) == (char *)NULL)
  803.         syntax ();
  804.  
  805.         switch (op->op_num)
  806.         {
  807.         case STRING_EQUAL:
  808.             return strcmp (opnd1, opnd2) == 0;
  809.  
  810.         case STRING_NOTEQUAL:
  811.             return strcmp (opnd1, opnd2) != 0;
  812.  
  813.         case NUMBER_EQUAL:
  814.             return num (opnd1) == num (opnd2);
  815.  
  816.         case NUMBER_NOTEQUAL:
  817.             return num (opnd1) != num (opnd2);
  818.  
  819.         case NUMBER_EQ_GREAT:
  820.             return num (opnd1) >= num (opnd2);
  821.  
  822.         case NUMBER_GREATER:
  823.             return num (opnd1) > num (opnd2);
  824.  
  825.         case NUMBER_EQ_LESS:
  826.             return num (opnd1) <= num (opnd2);
  827.  
  828.         case NUMBER_LESS:
  829.             return num (opnd1) < num (opnd2);
  830.         }
  831.     }
  832.  
  833.     test_alist--;
  834.     return strlen (opnd1) > 0;
  835.     }
  836.  
  837. /* unary expression */
  838.  
  839.     if (test_op->op_type != UNARY_OP || *++test_alist == 0)
  840.     syntax ();
  841.  
  842.     switch (n)
  843.     {
  844.     case STRING_ZERO:
  845.         return strlen (*test_alist) == 0;
  846.  
  847.     case STRING_NONZERO:
  848.         return strlen (*test_alist) != 0;
  849.  
  850.     case FILE_READABLE:
  851.         return access (*test_alist, R_OK) == 0;
  852.  
  853.     case FILE_WRITABLE:
  854.         return access (*test_alist, W_OK) == 0;
  855.  
  856.     case FILE_EXECUTABLE:
  857.         return access (*test_alist, X_OK) == 0;
  858.  
  859.     case FILE_REGULAR:
  860.         return stat (*test_alist, &s) == 0 && S_ISREG(s.st_mode);
  861.  
  862.     case FILE_DIRECTORY:
  863.         return stat (*test_alist, &s) == 0 && S_ISDIR(s.st_mode);
  864.  
  865.     case FILE_NONZERO:
  866.         return stat (*test_alist, &s) == 0 && (s.st_size > 0L);
  867.  
  868.     case FILE_TERMINAL:
  869.         return isatty ((int)num (*test_alist));
  870.  
  871. #ifdef S_ISUID
  872.     case FILE_USER:
  873.         return stat (*test_alist, &s) == 0 && (s.st_mode & S_ISUID);
  874. #endif
  875.  
  876. #ifdef S_ISGID
  877.     case FILE_GROUP:
  878.         return stat (*test_alist, &s) == 0 && (s.st_mode & S_ISGID);
  879. #endif
  880.  
  881. #ifdef S_ISVTX
  882.     case FILE_TEXT:
  883.         return stat (*test_alist, &s) == 0 && (s.st_mode & S_ISVTX);
  884. #endif
  885.  
  886. #ifdef S_IFBLK
  887.     case FILE_BLOCK:
  888.         return stat (*test_alist, &s) == 0 && S_ISBLK(s.st_mode);
  889. #endif
  890.  
  891. #ifdef S_IFCHR
  892.     case FILE_CHARACTER:
  893.         return stat (*test_alist, &s) == 0 && S_ISCHR(s.st_mode);
  894. #endif
  895.  
  896. #ifdef S_IFIFO
  897.     case FILE_FIFO:
  898.         return stat (*test_alist, &s) == 0 && S_ISFIFO(s.st_mode);
  899. #endif
  900.     }
  901. }
  902.  
  903. static int    lex (s)
  904. register char    *s;
  905. {
  906.     register struct test_op    *op = test_ops;
  907.  
  908.     if (s == (char *)NULL)
  909.     return END_OF_INPUT;
  910.  
  911.     while (op->op_text)
  912.     {
  913.     if (strcmp (s, op->op_text) == 0)
  914.     {
  915.         test_op = op;
  916.         return op->op_num;
  917.     }
  918.  
  919.     op++;
  920.     }
  921.  
  922.     test_op = (struct test_op *)NULL;
  923.     return OPERAND;
  924. }
  925.  
  926. /*
  927.  * Get a long numeric value
  928.  */
  929.  
  930. static long    num (s)
  931. register char    *s;
  932. {
  933.     char    *ep;
  934.     long    l = strtol (s, &ep, 10);
  935.  
  936.     if (!*s || *ep)
  937.     syntax ();
  938.  
  939.     return l;
  940. }
  941.  
  942. /*
  943.  * test syntax error - abort
  944.  */
  945.  
  946. static void    syntax ()
  947. {
  948.     print_error ("test: syntax error\n");
  949.     longjmp (test_jmp, 1);
  950. }
  951.  
  952. /*
  953.  * Select a new drive: x:
  954.  *
  955.  * Select the drive, get the current directory and check that we have
  956.  * actually selected the drive
  957.  */
  958.  
  959. static int    dodrive (t)
  960. register C_Op    *t;
  961. {
  962.     unsigned int    cdrive;
  963.     unsigned int    ndrive = tolower (**t->words) - 'a' + 1;
  964.  
  965.     _dos_setdrive (ndrive, &cdrive);
  966.     Getcwd ();
  967.     _dos_getdrive (&cdrive);
  968.     return (ndrive == cdrive) ? 0 : 1;
  969. }
  970.  
  971. /*
  972.  * Select a new directory: cd
  973.  */
  974.  
  975. static int    dochdir (t)
  976. register C_Op    *t;
  977. {
  978.     char        *p;        /* Original new directory    */
  979.     char        *nd;        /* New directory        */
  980.     register char    *cp;        /* In CDPATH Pointer        */
  981.     char        *directory;
  982.     int            first = 0;
  983.     unsigned int    dummy;
  984.     unsigned int    cdrive;
  985.  
  986. /* If restricted shell - illegal */
  987.  
  988.     if (check_rsh ("cd"))
  989.     return 1;
  990.  
  991. /* Use default ? */
  992.  
  993.     if (((p = t->words[1]) == (char *)NULL) &&
  994.     ((p = lookup (home, FALSE)->value) == null))
  995.     {
  996.     print_error ("cd: no home directory\n");
  997.     return 1;
  998.     }
  999.  
  1000.     if ((directory = getcell (FFNAME_MAX)) == (char *)NULL)
  1001.     {
  1002.     print_error ("cd: %s\n", strerror (ENOMEM));
  1003.     return 1;
  1004.     }
  1005.  
  1006. /* Save the current drive */
  1007.  
  1008.     _dos_getdrive (&cdrive);
  1009.  
  1010. /* Scan for the directory.  If there is not a / or : at start, use the
  1011.  * CDPATH variable
  1012.  */
  1013.  
  1014.     cp = ((*p == '/') || (*(p + 1) == ':')) ? null
  1015.                         : lookup ("CDPATH", FALSE)->value;
  1016.  
  1017.     do
  1018.     {
  1019.     cp = path_append (cp, p, directory);
  1020.  
  1021. /* Check for new disk drive */
  1022.  
  1023.     nd = directory;
  1024.  
  1025.     if (*(nd + 1) == ':')
  1026.     {
  1027.         _dos_setdrive (tolower (*nd) - 'a' + 1, &dummy);
  1028.         nd += 2;
  1029.     }
  1030.  
  1031. /* Was the change successful? */
  1032.  
  1033.     if ((!*nd) || (chdir (nd) == 0))
  1034.     {
  1035.  
  1036. /* OK - reset the current directory (in the shell) and display the new
  1037.  * path if appropriate
  1038.  */
  1039.  
  1040.         Getcwd ();
  1041.  
  1042.         if (first)
  1043.         dopwd (t);
  1044.  
  1045.         return 0;
  1046.     }
  1047.  
  1048.     first = 1;
  1049.  
  1050.     } while (cp != (char *)NULL);
  1051.  
  1052. /* Restore our original drive and restore directory info */
  1053.  
  1054.     _dos_setdrive (cdrive, &dummy);
  1055.     Getcwd ();
  1056.  
  1057.     print_error ("%s: bad directory\n", p);
  1058.     return 1;
  1059. }
  1060.  
  1061. /*
  1062.  * Extract the next path from a string and build a new path from the
  1063.  * extracted path and a file name
  1064.  */
  1065. char        *path_append (path_s, file_s, output_s)
  1066. register char    *path_s;        /* Path string            */
  1067. register char    *file_s;        /* File name string        */
  1068. char        *output_s;        /* Output path            */
  1069. {
  1070.     register char    *s = output_s;
  1071.     int            fsize = 0;
  1072.  
  1073.     while (*path_s && (*path_s != ';') && (fsize++ < FFNAME_MAX))
  1074.     *s++ = *path_s++;
  1075.  
  1076.     if ((output_s != s) && (*(s - 1) != '/') && (fsize++ < FFNAME_MAX))
  1077.     *s++ = '/';
  1078.  
  1079.     *s = '\0';
  1080.  
  1081.     if (file_s != (char *)NULL)
  1082.     strncpy (s, file_s, FFNAME_MAX - fsize);
  1083.  
  1084.     output_s[FFNAME_MAX - 1] = 0;
  1085.  
  1086.     return (*path_s ? ++path_s : (char *)NULL);
  1087. }
  1088.  
  1089. /*
  1090.  * Execute a shift command: shift <n>
  1091.  */
  1092.  
  1093. static int    doshift (t)
  1094. register C_Op    *t;
  1095. {
  1096.     register int    n;
  1097.  
  1098.     n = (t->words[1] != (char *)NULL) ? getn (t->words[1]) : 1;
  1099.  
  1100.     if (dolc < n)
  1101.     {
  1102.     print_error ("sh: nothing to shift\n");
  1103.     return 1;
  1104.     }
  1105.  
  1106.     dolv[n] = dolv[0];
  1107.     dolv += n;
  1108.     dolc -= n;
  1109.     setval (lookup ("#", TRUE), putn (dolc));
  1110.     return 0;
  1111. }
  1112.  
  1113. /*
  1114.  * Execute a umask command: umask <n>
  1115.  */
  1116.  
  1117. static int    doumask (t)
  1118. register C_Op    *t;
  1119. {
  1120.     register int    i;
  1121.     register char    *cp;
  1122.  
  1123.     if ((cp = t->words[1]) == (char *)NULL)
  1124.     {
  1125.     i = umask (0);
  1126.     umask (i);
  1127.     v1printf ("%o\n", i);
  1128.     }
  1129.  
  1130.     else
  1131.     {
  1132.     i = 0;
  1133.     while (IS_OCTAL (*cp))
  1134.         i = i * 8 + (*(cp++) - '0');
  1135.  
  1136.     umask (i);
  1137.     }
  1138.  
  1139.     return 0;
  1140. }
  1141.  
  1142. /*
  1143.  * Execute an exec command: exec <arguments>
  1144.  */
  1145.  
  1146. static int    doexec (t)
  1147. register C_Op    *t;
  1148. {
  1149.     register int    i;
  1150.     jmp_buf        ex;
  1151.     int            *ofail;
  1152.     IO_Actions        **ios = t->ioact;
  1153.  
  1154.     for (i = 0; (t->words[i] = t->words[i + 1]) != (char *)NULL; i++)
  1155.     ;
  1156.  
  1157. /* Left the I/O as it is */
  1158.  
  1159.     if (i == 0)
  1160.     return restore_std (0, FALSE);
  1161.  
  1162.     execflg = 1;
  1163.     ofail = failpt;
  1164.  
  1165. /* Set execute function recursive level to zero */
  1166.  
  1167.     Execute_stack_depth = 0;
  1168.     t->ioact = (IO_Actions **)NULL;
  1169.  
  1170.     if (setjmp (failpt = ex) == 0)
  1171.     execute (t, NOPIPE, NOPIPE, FEXEC);
  1172.  
  1173. /* Clear the extended file if an interrupt happened */
  1174.  
  1175.     Clear_Extended_File ();
  1176.     t->ioact = ios;
  1177.     failpt = ofail;
  1178.     execflg = 0;
  1179.     return 1;
  1180. }
  1181.  
  1182. /*
  1183.  * Execute a script in the current shell
  1184.  */
  1185.  
  1186. static int    dodot (t)
  1187. C_Op        *t;
  1188. {
  1189.     register int    i;
  1190.     register char    *sp;
  1191.     char        *cp;
  1192.     char        *l_path;
  1193.  
  1194.     if ((cp = t->words[1]) == (char *)NULL)
  1195.     return 0;
  1196.  
  1197. /* Get some space */
  1198.  
  1199.     if ((l_path = getcell (FFNAME_MAX)) == (char *)NULL)
  1200.     {
  1201.     print_error (".: %s\n", strerror (ENOMEM));
  1202.     return 1;
  1203.     }
  1204.  
  1205. /* Save the current drive */
  1206.  
  1207.     sp = (any ('/', cp) || (*(cp + 1) == ':')) ? null : path->value;
  1208.  
  1209.     do
  1210.     {
  1211.     sp = path_append (sp, cp, l_path);
  1212.  
  1213.     if ((i = O_for_execute (l_path, (char **)NULL, (int *)NULL)) >= 0)
  1214.         return RUN (afile, remap (i), filechar, FALSE);
  1215.  
  1216.     } while (sp != (char *)NULL);
  1217.  
  1218.     print_error ("%s: not found\n", cp);
  1219.     return 1;
  1220. }
  1221.  
  1222. /*
  1223.  * Read from standard input into a variable list
  1224.  */
  1225.  
  1226. static int    doread (t)
  1227. C_Op        *t;
  1228. {
  1229.     register char    *cp;
  1230.     register char    **wp;
  1231.     register int    nb;
  1232.     bool        nl_detected = FALSE;
  1233.     char        *buffer;
  1234.     char        *ep;
  1235.  
  1236. /* Check usage */
  1237.  
  1238.     if (t->words[1] == (char *)NULL)
  1239.     {
  1240.     print_error ("Usage: read name ...\n");
  1241.     return 1;
  1242.     }
  1243.  
  1244. /* Get some memory */
  1245.  
  1246.     if ((buffer = getcell (LINE_MAX)) == (char *)NULL)
  1247.     {
  1248.     print_error ("read: %s\n", strerror (ENOMEM));
  1249.     return 1;
  1250.     }
  1251.  
  1252.     ep = &buffer[LINE_MAX - 2];
  1253.  
  1254. /* Save the current drive */
  1255.  
  1256.     for (wp = t->words + 1; *wp != (char *)NULL; wp++)
  1257.     {
  1258.  
  1259. /* Read in until end of line, file or a field separator is detected */
  1260.  
  1261.     for (cp = buffer; !nl_detected && (cp < ep); cp++)
  1262.     {
  1263.         if (((nb = read (STDIN_FILENO, cp, 1)) != 1) || (*cp == NL) ||
  1264.         ((wp[1] != (char *)NULL) && any (*cp, ifs->value)))
  1265.         {
  1266.         if ((nb != 1) || (*cp == NL))
  1267.             nl_detected = TRUE;
  1268.  
  1269.         break;
  1270.         }
  1271.     }
  1272.  
  1273.     *cp = 0;
  1274.  
  1275.     if (nb <= 0)
  1276.         break;
  1277.  
  1278.     setval (lookup (*wp, TRUE), buffer);
  1279.     }
  1280.  
  1281.     return (nb <= 0);
  1282. }
  1283.  
  1284. /*
  1285.  * Evaluate an expression
  1286.  */
  1287.  
  1288. static int    doeval (t)
  1289. register C_Op    *t;
  1290. {
  1291.     return RUN (awordlist, t->words + 1, wdchar, TRUE);
  1292. }
  1293.  
  1294. /*
  1295.  * Execute a trap
  1296.  */
  1297.  
  1298. static int    dotrap (t)
  1299. register C_Op    *t;
  1300. {
  1301.     register int    n, i;
  1302.     register int    resetsig;
  1303.     char        tval[10];
  1304.     char        *cp;
  1305.  
  1306.     if (t->words[1] == (char *)NULL)
  1307.     {
  1308.  
  1309. /* Display trap - look up each trap and print those we find */
  1310.  
  1311.     for (i = 0; i < NSIG; i++)
  1312.     {
  1313.         sprintf (tval, "~%d", i);
  1314.  
  1315.         if ((cp = lookup (tval, FALSE)->value) != null)
  1316.         {
  1317.         v1printf ("%u: ", i);
  1318.         v1a_puts (cp);
  1319.         }
  1320.     }
  1321.  
  1322.     return 0;
  1323.     }
  1324.  
  1325.     resetsig = isdigit (*t->words[1]);        /* Reset signal?    */
  1326.  
  1327.     for (i = resetsig ? 1 : 2; t->words[i] != (char *)NULL; ++i)
  1328.     {
  1329.  
  1330. /* Generate the variable name */
  1331.  
  1332.     sprintf (tval, "~%d", (n = getsig (t->words[i])));
  1333.  
  1334.     if (n == -1)
  1335.         return 1;
  1336.  
  1337.     unset (tval, TRUE);
  1338.  
  1339. /* Re-define signal processing */
  1340.  
  1341.     if (!resetsig)
  1342.     {
  1343.         if (*t->words[1] != '\0')
  1344.         {
  1345.         setval (lookup (tval, TRUE), t->words[1]);
  1346.         setsig (n, sig);
  1347.         }
  1348.  
  1349.         else
  1350.         setsig (n, SIG_IGN);
  1351.     }
  1352.  
  1353. /* Clear signal processing */
  1354.  
  1355.     else if (talking)
  1356.     {
  1357.         if (n == SIGINT)
  1358.         setsig (n, onintr);
  1359.  
  1360.         else
  1361. #ifdef SIGQUIT
  1362.         setsig (n, n == SIGQUIT ? SIG_IGN : SIG_DFL);
  1363. #else
  1364.         setsig (n, SIG_DFL);
  1365. #endif
  1366.     }
  1367.  
  1368.     else
  1369.         setsig (n, SIG_DFL);
  1370.     }
  1371.  
  1372.     return 0;
  1373. }
  1374.  
  1375. /*
  1376.  * Get a signal number
  1377.  */
  1378.  
  1379. static int    getsig (s)
  1380. char        *s;
  1381. {
  1382.     register int    n;
  1383.  
  1384.     if (((n = getn (s)) < 0) || (n >= NSIG))
  1385.     {
  1386.     print_error ("trap: bad signal number\n");
  1387.     n = -1;
  1388.     }
  1389.  
  1390.     return n;
  1391. }
  1392.  
  1393. /*
  1394.  * Set up a signal function
  1395.  */
  1396.  
  1397. static void    setsig (n, f)
  1398. register int    n;
  1399. int        (*f)();
  1400. {
  1401.     if (n == 0)
  1402.     return;
  1403.  
  1404.     if ((signal (n, SIG_IGN) != SIG_IGN) || (ourtrap & (1L << n)))
  1405.     {
  1406.     ourtrap |= (1L << n);
  1407.     signal (n, f);
  1408.     }
  1409. }
  1410.  
  1411. /* Convert a string to a number */
  1412.  
  1413. int    getn (as)
  1414. char    *as;
  1415. {
  1416.     char    *s;
  1417.     int        n = (int)strtol (as, &s, 10);
  1418.  
  1419.     if (*s)
  1420.     print_error ("%s: bad number\n", as);
  1421.  
  1422.     return n;
  1423. }
  1424.  
  1425. /*
  1426.  * BREAK and CONTINUE processing
  1427.  */
  1428.  
  1429. static int    dobreak (t)
  1430. C_Op        *t;
  1431. {
  1432.     return brkcontin (t->words[1], BC_BREAK);
  1433. }
  1434.  
  1435. static int    docontinue (t)
  1436. C_Op        *t;
  1437. {
  1438.     return brkcontin (t->words[1], BC_CONTINUE);
  1439. }
  1440.  
  1441. static int    brkcontin (cp, val)
  1442. register char    *cp;
  1443. int        val;
  1444. {
  1445.     register Break_C    *Break_Loc;
  1446.     register int    nl;
  1447.  
  1448.     if ((nl = (cp == (char *)NULL) ? 1 : getn (cp)) <= 0)
  1449.     nl = 999;
  1450.  
  1451.     do
  1452.     {
  1453.     if ((Break_Loc = Break_List) == (Break_C *)NULL)
  1454.         break;
  1455.  
  1456.     Break_List = Break_Loc->nextlev;
  1457.  
  1458.     } while (--nl);
  1459.  
  1460.     if (nl)
  1461.     {
  1462.     print_error ("sh: bad break/continue level\n");
  1463.     return 1;
  1464.     }
  1465.  
  1466.     longjmp (Break_Loc->brkpt, val);
  1467.  
  1468. /* NOTREACHED */
  1469. }
  1470.  
  1471. /*
  1472.  * Exit function
  1473.  */
  1474.  
  1475. static int    doexit (t)
  1476. C_Op        *t;
  1477. {
  1478.     Break_C    *SShell_Loc = SShell_List;
  1479.  
  1480.     execflg = 0;
  1481.  
  1482. /* Set up error codes */
  1483.  
  1484.     if (t->words[1] != (char *)NULL)
  1485.     {
  1486.     exstat = getn (t->words[1]);
  1487.     setval (lookup ("?", TRUE), t->words[1]);
  1488.     }
  1489.  
  1490. /* Are we in a subshell.  Yes - do a longjmp instead of an exit */
  1491.  
  1492.     if (SShell_Loc != (Break_C *)NULL)
  1493.     {
  1494.     SShell_List = SShell_Loc->nextlev;
  1495.     longjmp (SShell_Loc->brkpt, 1);
  1496.     }
  1497.  
  1498.     leave ();
  1499.     return 1;
  1500. }
  1501.  
  1502. /*
  1503.  * Function return - set exit value and return via a long jmp
  1504.  */
  1505.  
  1506. static int    doreturn (t)
  1507. C_Op        *t;
  1508. {
  1509.     Break_C    *Return_Loc = Return_List;
  1510.  
  1511.     if  (t->words[1] != (char *)NULL)
  1512.     setval (lookup ("?", TRUE), t->words[1]);
  1513.  
  1514. /* If the return address is defined - return to it.  Otherwise, return
  1515.  * the value
  1516.  */
  1517.  
  1518.     if (Return_Loc != (Break_C *)NULL)
  1519.     {
  1520.     Return_List = Return_Loc->nextlev;
  1521.     longjmp (Return_Loc->brkpt, 1);
  1522.     }
  1523.  
  1524.     return getn (t->words[1]);
  1525. }
  1526.  
  1527. /*
  1528.  * MSDOS, EXPORT and READONLY functions
  1529.  */
  1530.  
  1531. static int    doexport (t)
  1532. C_Op        *t;
  1533. {
  1534.     return rdexp (t->words + 1, EXPORT, "export ");
  1535. }
  1536.  
  1537. static int    doreadonly (t)
  1538. C_Op         *t;
  1539. {
  1540.     return rdexp (t->words + 1, RONLY, "readonly ");
  1541. }
  1542.  
  1543. static int    domsdos (t)
  1544. C_Op        *t;
  1545. {
  1546.     return rdexp (t->words + 1, C_MSDOS, "msdos ");
  1547. }
  1548.  
  1549. static int    rdexp (wp, key, tstring)
  1550. register char    **wp;
  1551. int        key;
  1552. char        *tstring;
  1553. {
  1554.     char    *cp;
  1555.     bool    valid;
  1556.  
  1557.     if (*wp != (char *)NULL)
  1558.     {
  1559.     for (; *wp != (char *)NULL; wp++)
  1560.     {
  1561.         cp = *wp;
  1562.         valid = TRUE;
  1563.  
  1564. /* Check for a valid name */
  1565.  
  1566.         if (!isalpha (*(cp++)))
  1567.         valid = FALSE;
  1568.  
  1569.         else
  1570.         {
  1571.         while (*cp)
  1572.         {
  1573.             if (!isalnum (*(cp++)))
  1574.             {
  1575.             valid = FALSE;
  1576.             break;
  1577.             }
  1578.         }
  1579.         }
  1580.  
  1581. /* If valid - update, otherwise print a message */
  1582.  
  1583.         if (valid)
  1584.         s_vstatus (lookup (*wp, TRUE), key);
  1585.  
  1586.         else
  1587.         print_error ("%s: bad identifier\n", *wp);
  1588.     }
  1589.     }
  1590.  
  1591.     else
  1592.     {
  1593.     register Var_List    *vp;
  1594.  
  1595.     for (vp = vlist; vp != (Var_List *) NULL; vp = vp->next)
  1596.     {
  1597.         if ((vp->status & key) && isalpha (*vp->name))
  1598.         {
  1599.         v1_puts (tstring);
  1600.         write (STDOUT_FILENO, vp->name,
  1601.                (int)(findeq (vp->name) - vp->name));
  1602.         v1_putc (NL);
  1603.         }
  1604.     }
  1605.     }
  1606.  
  1607.     return 0;
  1608. }
  1609.  
  1610. /*
  1611.  * Sort Compare function for displaying variables
  1612.  */
  1613.  
  1614. int    sort_compare (s1, s2)
  1615. char    **s1;
  1616. char    **s2;
  1617. {
  1618.     return strcmp (*s1, *s2);
  1619. }
  1620.  
  1621. /*
  1622.  * Set function
  1623.  */
  1624.  
  1625. static int    doset (t)
  1626. register C_Op    *t;
  1627. {
  1628.     register Var_List    *vp;
  1629.     register char    *cp;
  1630.     register int    n, j;
  1631.     Fun_Ops        *fp;
  1632.     char        sign;
  1633.     char        **list;
  1634.  
  1635. /* Display ? */
  1636.  
  1637.     if ((cp = t->words[1]) == (char *)NULL)
  1638.     {
  1639.  
  1640. /* Count the number of entries to print */
  1641.  
  1642.     for (n = 0, vp = vlist; vp != (Var_List *)NULL; vp = vp->next)
  1643.     {
  1644.         if (isalnum (*vp->name))
  1645.         n++;
  1646.     }
  1647.  
  1648. /* Build a local array of name */
  1649.  
  1650.     list = (char **)space (sizeof (char *) * n);
  1651.  
  1652.     for (n = 0, vp = vlist; vp != (Var_List *)NULL; vp = vp->next)
  1653.     {
  1654.         if (isalnum (*vp->name))
  1655.         {
  1656.         if (list == (char **)NULL)
  1657.             v1a_puts (vp->name);
  1658.  
  1659.         else
  1660.             list[n++] = vp->name;
  1661.         }
  1662.     }
  1663.  
  1664. /* Sort them and then print */
  1665.  
  1666.     if (list != (char **)NULL)
  1667.     {
  1668.         qsort (list, n, sizeof (char *), sort_compare);
  1669.  
  1670.         for (j = 0; j < n; j++)
  1671.         v1a_puts (list[j]);
  1672.  
  1673.         DELETE (list);
  1674.     }
  1675.  
  1676. /* Print the list of functions */
  1677.  
  1678.     for (fp = fun_list; fp != (Fun_Ops *)NULL; fp = fp->next)
  1679.         Print_ExTree (fp->tree);
  1680.  
  1681.     return 0;
  1682.     }
  1683.  
  1684. /* Set/Unset a flag ? */
  1685.  
  1686.     if (((sign = *cp) == '-') || (*cp == '+'))
  1687.     {
  1688.     for (n = 0; (t->words[n] = t->words[n + 1]) != (char *)NULL; n++)
  1689.         ;
  1690.  
  1691.     for (; *cp; cp++)
  1692.     {
  1693.         if (*cp == 'r')
  1694.         {
  1695.         print_error ("set: -r bad option\n");
  1696.         return 1;
  1697.         }
  1698.  
  1699.         if (*cp == 'e')
  1700.         {
  1701.         if (!talking)
  1702.         {
  1703.             if (sign == '-')
  1704.             FL_SET ('e');
  1705.  
  1706.             else
  1707.             FL_CLEAR ('e');
  1708.         }
  1709.         }
  1710.  
  1711.         else if (islower (*cp))
  1712.         {
  1713.         if (sign == '-')
  1714.             FL_SET (*cp);
  1715.  
  1716.         else
  1717.             FL_CLEAR (*cp);
  1718.         }
  1719.     }
  1720.  
  1721.     setdash ();
  1722.     }
  1723.  
  1724. /* Set up parameters ? */
  1725.  
  1726.     if (t->words[1])
  1727.     {
  1728.     t->words[0] = dolv[0];
  1729.  
  1730.     for (n = 1; t->words[n] != (char *)NULL; n++)
  1731.         setarea ((char *)t->words[n], 0);
  1732.  
  1733.     dolc = n-1;
  1734.     dolv = t->words;
  1735.     setval (lookup ("#", TRUE), putn (dolc));
  1736.     setarea ((char *)(dolv - 1), 0);
  1737.     }
  1738.  
  1739.     return 0;
  1740. }
  1741.  
  1742. /*
  1743.  * History functions - display, initialise, enable, disable
  1744.  */
  1745.  
  1746. #ifndef NO_HISTORY
  1747. static int    dohistory (t)
  1748. C_Op        *t;
  1749. {
  1750.     char    *cp;
  1751.  
  1752.     if (!talking)
  1753.     return 1;
  1754.  
  1755.     if ((cp = t->words[1]) == (char *)NULL)
  1756.     Display_History ();
  1757.  
  1758.     else if (strcmp (cp, "-i") == 0)
  1759.     Clear_History ();
  1760.  
  1761.     else if (strcmp (cp, "-d") == 0)
  1762.     History_Enabled = FALSE;
  1763.  
  1764.     else if (strcmp (cp, "-e") == 0)
  1765.     History_Enabled = TRUE;
  1766.  
  1767.     return 0;
  1768. }
  1769. #endif
  1770.  
  1771. /*
  1772.  * Type function: For each name, indicate how it would be interpreted
  1773.  */
  1774.  
  1775. static char    *type_ext[] = {
  1776.     "", ".exe", ".com", ".sh", ".bat"
  1777. };
  1778.  
  1779. static int    dotype (t)
  1780. register C_Op    *t;
  1781. {
  1782.     register char    *sp;            /* Path pointers    */
  1783.     char        *cp;
  1784.     char        *ep;
  1785.     char        *xp;            /* In file name pointers */
  1786.     char        *xp1;
  1787.     int            n = 1;            /* Argument count    */
  1788.     int            i, fp;
  1789.     bool        found;            /* Found flag        */
  1790.     bool        inb;            /* Inbuilt function    */
  1791.     char        *l_path;
  1792.     Fun_Ops        *fops;
  1793.  
  1794. /* Get some memory for the buffer */
  1795.  
  1796.     if ((l_path = getcell (FFNAME_MAX + 4)) == (char *)NULL)
  1797.     {
  1798.     print_error ("type: %s\n", strerror (ENOMEM));
  1799.     return 1;
  1800.     }
  1801.  
  1802. /* Process each parameter */
  1803.  
  1804.     while ((cp = t->words[n++]) != (char *)NULL)
  1805.     {
  1806.  
  1807. /* Check for currently use inbuilt version */
  1808.  
  1809.     if (inbuilt (cp, &inb) && inb)
  1810.     {
  1811.         v1_puts (cp);
  1812.         v1a_puts (" is a shell internal command");
  1813.         continue;
  1814.     }
  1815.  
  1816. /* Check for a function */
  1817.  
  1818.     if ((fops = Fun_Search (cp)) != (Fun_Ops *)NULL)
  1819.     {
  1820.         v1_puts (cp);
  1821.         v1a_puts (" is a function");
  1822.         Print_ExTree (fops->tree);
  1823.         continue;
  1824.     }
  1825.  
  1826. /* Scan the path for an executable */
  1827.  
  1828.     sp = (any ('/', cp) || (*(cp + 1) == ':')) ? null : path->value;
  1829.     found = FALSE;
  1830.  
  1831.     do
  1832.     {
  1833.         sp = path_append (sp, cp, l_path);
  1834.         ep = &l_path[strlen (l_path)];
  1835.  
  1836. /* Get start of file name */
  1837.  
  1838.         if ((xp1 = strrchr (l_path, '/')) == (char *)NULL)
  1839.         xp1 = l_path;
  1840.  
  1841.         else
  1842.         ++xp1;
  1843.  
  1844. /* Look up all 5 types */
  1845.  
  1846.         for (i = 0; (i < 5) && !found; i++)
  1847.         {
  1848.         strcpy (ep, type_ext[i]);
  1849.  
  1850.         if (access (l_path, F_OK) == 0)
  1851.         {
  1852.  
  1853. /* If no extension or .sh extension, check for shell script */
  1854.  
  1855.             if (((xp = strchr (xp1, '.')) == (char *)NULL) ||
  1856.             (stricmp (xp, ".sh") == 0))
  1857.             {
  1858.             if ((fp = Check_Script (l_path, (char **)NULL,
  1859.                         (int *)NULL)) < 0)
  1860.                 continue;
  1861.  
  1862.             S_close (fp, TRUE);
  1863.             }
  1864.  
  1865.             else if ((stricmp (xp, ".exe") != 0) &&
  1866.                  (stricmp (xp, ".com") != 0) &&
  1867.                  (stricmp (xp, ".bat") != 0))
  1868.             continue;
  1869.  
  1870.             Convert_Backslashes (strlwr (l_path));
  1871.             print_error ("%s is %s\n", cp, l_path);
  1872.             found = TRUE;
  1873.         }
  1874.         }
  1875.     } while ((sp != (char *)NULL) && !found);
  1876.  
  1877. /* If not found, check for inbuilt version */
  1878.  
  1879.     if (!found)
  1880.     {
  1881.         if (inbuilt (cp, &inb))
  1882.         {
  1883.         v1_puts (cp);
  1884.         v1a_puts (" is a shell internal command");
  1885.         }
  1886.  
  1887.         else
  1888.         print_error ("%s not found\n", cp);
  1889.     }
  1890.     }
  1891.  
  1892.     return 0;
  1893. }
  1894.  
  1895. /* Table of internal commands */
  1896.  
  1897. static struct    builtin    builtin[] = {
  1898.     ".",        dodot,        (BLT_ALWAYS | BLT_CURRENT),
  1899.     ":",        dolabel,    (BLT_ALWAYS | BLT_CURRENT),
  1900.     "[",        dotest,        BLT_CURRENT,
  1901.     "break",    dobreak,    BLT_CURRENT,
  1902.     "builtin",    dobuiltin,    (BLT_ALWAYS | BLT_CURRENT),
  1903.     "cd",        dochdir,    BLT_CURRENT,
  1904.     "continue",    docontinue,    BLT_CURRENT,
  1905.     "echo",        doecho,        BLT_CURRENT,
  1906.     "eval",        doeval,        BLT_CURRENT,
  1907.     "exec",        doexec,        BLT_CURRENT,
  1908.     "exit",        doexit,        BLT_CURRENT,
  1909.     "export",    doexport,    BLT_CURRENT,
  1910.     "getopt",    dogetopt,    BLT_CURRENT,
  1911. #ifndef NO_HISTORY
  1912.     "history",    dohistory,    BLT_CURRENT,
  1913. #endif
  1914.     "msdos",    domsdos,    BLT_CURRENT,
  1915.     "pwd",        dopwd,        BLT_CURRENT,
  1916.     "read",        doread,        BLT_CURRENT,
  1917.     "readonly",    doreadonly,    BLT_CURRENT,
  1918.     "return",    doreturn,    BLT_CURRENT,
  1919.     "set",        doset,        BLT_CURRENT,
  1920.     "shift",    doshift,    BLT_CURRENT,
  1921.     "swap",        doswap,        BLT_CURRENT,
  1922.     "test",        dotest,        BLT_CURRENT,
  1923.     "trap",        dotrap,        BLT_CURRENT,
  1924.     "type",        dotype,        BLT_CURRENT,
  1925.     "umask",    doumask,    BLT_CURRENT,
  1926.     "unset",    dounset,    BLT_CURRENT,
  1927.     "ver",        dover,        BLT_CURRENT,
  1928.     (char *)NULL,    (int (*)())NULL,    0
  1929. };
  1930.  
  1931. /*
  1932.  * Look up a built in command
  1933.  */
  1934.  
  1935. int        (*inbuilt (s, b))()
  1936. register char    *s;
  1937. bool        *b;
  1938. {
  1939.     register struct builtin    *bp;
  1940.  
  1941. /* Check for change drive */
  1942.  
  1943.     *b = TRUE;
  1944.     if ((strlen (s) == 2) && isalpha (*s) && (*s != '_') && (*(s + 1) == ':'))
  1945.     return dodrive;
  1946.  
  1947. /* Search for command */
  1948.  
  1949.     *b = FALSE;
  1950.     for (bp = builtin; bp->command != (char *)NULL; bp++)
  1951.     {
  1952.     if (stricmp (bp->command, s) == 0)
  1953.     {
  1954.         *b = (bp->mode & BLT_CURRENT) ? TRUE : FALSE;
  1955.         return bp->fn;
  1956.     }
  1957.     }
  1958.  
  1959.     return (int (*)())NULL;
  1960. }
  1961.  
  1962. /* Write to stdout functions - printf, fputs, fputc, and a special */
  1963.  
  1964. /*
  1965.  * Equivalent of printf without using streams
  1966.  */
  1967.  
  1968. void    v1printf (fmt)
  1969. char    *fmt;
  1970. {
  1971.     va_list    ap;
  1972.     char    x[100];
  1973.  
  1974.     va_start (ap, fmt);
  1975.     vsprintf (x, fmt, ap);
  1976.     v1_puts (x);
  1977.     va_end (ap);
  1978. }
  1979.  
  1980. /*
  1981.  * Write string to STDOUT
  1982.  */
  1983.  
  1984. void        v1_puts (s)
  1985. char        *s;
  1986. {
  1987.     write (STDOUT_FILENO, s, strlen (s));
  1988. }
  1989.  
  1990. /*
  1991.  * Write string to STDOUT with a NL at end
  1992.  */
  1993.  
  1994. void        v1a_puts (s)
  1995. char        *s;
  1996. {
  1997.     char    c = NL;
  1998.  
  1999.     write (STDOUT_FILENO, s, strlen (s));
  2000.     write (STDOUT_FILENO, &c, 1);
  2001. }
  2002.  
  2003. /*
  2004.  * Write 1 character to STDOUT
  2005.  */
  2006.  
  2007. void        v1_putc (c)
  2008. char        c;
  2009. {
  2010.     if ((c != 0x07) || Ring_Bell ())
  2011.     write (STDOUT_FILENO, &c, 1);
  2012. }
  2013.  
  2014. /*
  2015.  * Builtin - either list builtins or execute it
  2016.  */
  2017.  
  2018. static int    dobuiltin (t)
  2019. register C_Op    *t;
  2020. {
  2021.     register struct builtin    *bp;
  2022.     int                (*shcom)(C_Op *) = (int (*)())NULL;
  2023.     int                rv = 0;
  2024.     bool            mode;
  2025.     int                action;
  2026.     int                i;
  2027.  
  2028.     if (t->words[1] == (char *)NULL)
  2029.     {
  2030.     for (bp = builtin; bp->command != (char *)NULL; bp++)
  2031.         v1printf ("builtin %s%s", bp->command,
  2032.               (bp->mode & BLT_CURRENT) ? " - preferred\n" : "\n");
  2033.     }
  2034.  
  2035. /* Check for changing options */
  2036.  
  2037.     else if (*t->words[1] == '-')
  2038.     {
  2039.     if (!strcmp (t->words[1], "-s"))
  2040.         action = 0;
  2041.  
  2042.     else if (!strcmp (t->words[1], "-a"))
  2043.         action = 1;
  2044.  
  2045.     else if (!strcmp (t->words[1], "-d"))
  2046.         action = 2;
  2047.  
  2048.     else
  2049.     {
  2050.         print_warn ("builtin: invalid flag %s\n", t->words[1]);
  2051.         return 1;
  2052.     }
  2053.  
  2054. /* Process the commands */
  2055.  
  2056.     for (i = 2; t->words[i] != (char *)NULL; ++i)
  2057.     {
  2058.         for (bp = builtin; bp->command != (char *)NULL; bp++)
  2059.         {
  2060.         if (stricmp (bp->command, t->words[i]) == 0)
  2061.         {
  2062.             switch (action)
  2063.             {
  2064.             case 0:
  2065.                 print_warn ("%s: %s\n", t->words[i],
  2066.                     (bp->mode |= BLT_CURRENT)
  2067.                         ? "builtin" : "external");
  2068.                 break;
  2069.  
  2070.             case 1:
  2071.                 bp->mode |= BLT_CURRENT;
  2072.                 break;
  2073.  
  2074.             case 2:
  2075.                 if (bp->mode & BLT_ALWAYS)
  2076.                 print_warn ("%s: always builtin\n",
  2077.                         t->words[i]);
  2078.                 else
  2079.                 bp->mode &= ~BLT_CURRENT;
  2080.  
  2081.                 break;
  2082.             }
  2083.  
  2084.             break;
  2085.         }
  2086.         }
  2087.  
  2088.         if (bp->command == (char *)NULL)
  2089.         {
  2090.         print_warn (not_builtin, t->words[i]);
  2091.         rv = 1;
  2092.         }
  2093.     }
  2094.     }
  2095.  
  2096. /* Check to see if we know about the builtin version */
  2097.  
  2098.     else if ((shcom = inbuilt (t->words[1], &mode)) == (int (*)())NULL)
  2099.     {
  2100.     print_warn (not_builtin, t->words[i]);
  2101.     rv = 1;
  2102.     }
  2103.  
  2104. /* Set up the word list correctly */
  2105.  
  2106.     else
  2107.     {
  2108.     t->words++;
  2109.     rv = (*shcom)(t);
  2110.     t->words--;
  2111.     }
  2112.  
  2113.     return rv;
  2114. }
  2115.