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