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