home *** CD-ROM | disk | FTP | other *** search
/ Shareware Supreme Volume 6 #1 / swsii.zip / swsii / 099 / SH164AS.ZIP / SHELL / SH1.C < prev    next >
C/C++ Source or Header  |  1992-02-28  |  41KB  |  2,262 lines

  1. /* MS-DOS SHELL - Main program, memory and variable management
  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:
  7.  *
  8.  * 1.  Redistribution and use in source and binary forms are permitted
  9.  *     provided that the above copyright notice is duplicated in the
  10.  *     source form and the copyright notice in file sh6.c is displayed
  11.  *     on entry to the program.
  12.  *
  13.  * 2.  The sources (or parts thereof) or objects generated from the sources
  14.  *     (or parts of sources) cannot be sold under any circumstances.
  15.  *
  16.  *    $Header: D:/SRC/SHELL/RCS/sh1.c 1.19 90/11/06 19:13:39 Ian_Stewartson Exp $
  17.  *
  18.  *    $Log:    sh1.c $
  19.  * Revision 1.19  90/11/06  19:13:39  Ian_Stewartson
  20.  * Add deletion of swap file on interrupt
  21.  * 
  22.  * Revision 1.18  90/08/24  21:54:05  Ian_Stewartson
  23.  * Add support for POSIX macro command {x#y} and {x%y}
  24.  * 
  25.  * Revision 1.17  90/08/14  23:32:53  MS_user
  26.  * Fix memory bugs - Add malloc checking functions for debug
  27.  * Make Convert_Backslashes public
  28.  *
  29.  * Revision 1.16  90/05/31  09:48:06  MS_user
  30.  * Implement partial write when swapping to disk
  31.  * Add some signal lockouts to prevent corruption
  32.  *
  33.  * Revision 1.15  90/05/15  21:08:59  MS_user
  34.  * Restore original directory on exit
  35.  *
  36.  * Revision 1.14  90/04/25  22:33:28  MS_user
  37.  * Fix rsh check for PATH
  38.  *
  39.  * Revision 1.13  90/04/25  09:18:12  MS_user
  40.  * Change version message processing
  41.  *
  42.  * Revision 1.12  90/04/04  11:32:12  MS_user
  43.  * Change MAILPATH to use a semi-colon and not a colon for DOS
  44.  *
  45.  * Revision 1.11  90/04/03  17:58:35  MS_user
  46.  * Stop shell exit from lowest level CLI
  47.  *
  48.  * Revision 1.10  90/03/27  20:24:49  MS_user
  49.  * Fix problem with Interrupts not restoring std??? and clearing extended file
  50.  *
  51.  * Revision 1.9  90/03/26  20:56:13  MS_user
  52.  * Change I/O restore so that "exec >filename" works
  53.  *
  54.  * Revision 1.8  90/03/26  04:30:14  MS_user
  55.  * Remove original Interrupt 24 save address
  56.  *
  57.  * Revision 1.7  90/03/12  20:16:22  MS_user
  58.  * Save program name for Initialisation file processing
  59.  *
  60.  * Revision 1.6  90/03/09  16:05:33  MS_user
  61.  * Add build file name function and change the profile check to use it
  62.  *
  63.  * Revision 1.5  90/03/06  16:49:14  MS_user
  64.  * Add disable history option
  65.  *
  66.  * Revision 1.4  90/03/06  15:09:27  MS_user
  67.  * Add Unix PATH variable conversion
  68.  *
  69.  * Revision 1.3  90/03/05  13:47:45  MS_user
  70.  * Get /etc/profile and profile order rigth
  71.  * Use $HOME/profile and not profile
  72.  * Check cursor position before outputing prompt
  73.  * Move some of processing in main to sub-routines
  74.  *
  75.  * Revision 1.2  90/02/14  04:46:20  MS_user
  76.  * Add Interrupt 24 processing
  77.  *
  78.  * Revision 1.1  90/01/25  13:40:39  MS_user
  79.  * Initial revision
  80.  *
  81.  */
  82.  
  83. #include <sys/types.h>
  84. #include <sys/stat.h>
  85. #include <stdio.h>
  86. #include <stdlib.h>
  87. #include <signal.h>
  88. #include <errno.h>
  89. #include <setjmp.h>
  90. #include <stdarg.h>
  91. #include <string.h>
  92. #include <unistd.h>
  93. #include <ctype.h>
  94. #include <fcntl.h>
  95. #include <limits.h>
  96. #include <dirent.h>
  97. #include <dos.h>
  98. #include <time.h>
  99. #include "sh.h"
  100.  
  101. /*
  102.  * Structure of Malloced space to allow release of space nolonger required
  103.  * without having to know about it.
  104.  */
  105.  
  106. #undef CHECK_MALLOC
  107.  
  108. #ifdef CHECK_MALLOC
  109. #define    MAGIC1        0xd8a5
  110. #define    MAGIC2        0xa5d8
  111. static void        sh_free (void *);
  112. static void        sh_chkmem (char *, char *, size_t, bool);
  113. #define SH_FREE        sh_free
  114. #else
  115. #define SH_FREE        free
  116. #endif
  117.  
  118. typedef struct region {
  119. #ifdef CHECK_MALLOC
  120.     unsigned int    magic1;
  121.     unsigned int    len;
  122. #endif
  123.     struct region    *next;
  124.     int            area;
  125. } s_region;
  126.  
  127. static struct region    *areastart = (s_region *)NULL;
  128.  
  129. /*
  130.  * default shell, search rules
  131.  */
  132.  
  133. static char    *shellname = "c:/bin/sh";
  134. static char    *search    = ";c:/bin;c:/usr/bin";
  135. static char    *ymail     = "You have mail\n";
  136. static char    *Path       = "PATH";
  137.                 /* Entry directory            */
  138. static char    *Start_directory = (char *)NULL;
  139.                     /* Original Interrupt 24 address */
  140. static void    (interrupt far *Orig_I24_V) (void);
  141. #ifdef SIGQUIT
  142. static void    (*qflag)(int) = SIG_IGN;
  143. #endif
  144.  
  145. /* Functions */
  146.  
  147. static char    *cclass (char *, int, bool);
  148. static char    *copy_to_equals (char *, char *);
  149. static void    nameval (Var_List *, char *, char *, bool);
  150. static bool    Initialise (char *);
  151. static void    onecommand (void);
  152. static void    Check_Mail (void);
  153. static void    Pre_Process_Argv (char **);
  154. static void    Load_G_VL (void);
  155. static void    Load_profiles (void);
  156. static void    U2D_Path (void);
  157.  
  158. /*
  159.  * The main program starts here
  160.  */
  161.  
  162. void        main (argc, argv)
  163. int        argc;
  164. register char    **argv;
  165. {
  166.     register int    f;
  167.     int            cflag = 0;
  168.     int            sc;
  169.     char        *name = *argv;
  170.     char        **ap;
  171.     int            (*iof)(IO_State *) = filechar;
  172.                     /* Load up various parts of the    */
  173.                     /* system            */
  174.     bool        l_rflag = Initialise (*argv);
  175.  
  176. /* Preprocess options to convert two character options of the form /x to
  177.  * -x.  Some programs!!
  178.  */
  179.  
  180.     Pre_Process_Argv (argv);
  181.  
  182. /* Save the start directory for when we exit */
  183.  
  184.     Start_directory = getcwd ((char *)NULL, PATH_MAX + 4);
  185.  
  186. /* Process the options */
  187.  
  188.     while ((sc = getopt (argc, argv, "abc:defghijklmnopqrtsuvwxyz0")) != EOF)
  189.     {
  190.     switch (sc)
  191.     {
  192.         case '0':                /* Level 0 flag for DOS    */
  193.         level0 = TRUE;
  194.         break;
  195.  
  196.         case 'r':                /* Restricted        */
  197.         l_rflag = TRUE;
  198.         break;
  199.  
  200.         case 'c':                /* Command on line    */
  201.         ps1->status &= ~EXPORT;
  202.         ps2->status &= ~EXPORT;
  203.         setval (ps1, null);
  204.         setval (ps2, null);
  205.         cflag = 1;
  206.  
  207.         PUSHIO (aword, optarg, iof = nlchar);
  208.         break;
  209.  
  210.         case 'q':                /* No quit ints        */
  211. #ifdef SIGQUIT
  212.         qflag = SIG_DFL;
  213. #endif
  214.         break;
  215.  
  216.         case 's':                /* standard input    */
  217.         break;
  218.  
  219.         case 't':                /* One command        */
  220.         ps1->status &= ~EXPORT;
  221.         setval (ps1, null);
  222.         iof = linechar;
  223.         break;
  224.  
  225.         case 'i':                /* Set interactive    */
  226.         talking = TRUE;
  227.  
  228.         default:
  229.         if (islower (sc))
  230.             FL_SET (sc);
  231.     }
  232.     }
  233.  
  234.     argv += optind;
  235.     argc -= optind;
  236.  
  237. /* Execute one off command - disable prompts */
  238.  
  239.     if ((iof == filechar) && (argc > 0))
  240.     {
  241.     setval (ps1, null);
  242.     setval (ps2, null);
  243.     ps1->status &= ~EXPORT;
  244.     ps2->status &= ~EXPORT;
  245.  
  246.     f = 0;
  247.  
  248. /* Open the file if necessary */
  249.  
  250.     if (strcmp ((name = *argv), "-") != 0)
  251.     {
  252.         if ((f = O_for_execute (name, (char **)NULL, (int *)NULL)) < 0)
  253.         {
  254.         print_error ("%s: cannot open\n", name);
  255.         exit (1);
  256.         }
  257.     }
  258.  
  259.     PUSHIO (afile, remap (f), filechar);     /* Load into I/O stack    */
  260.     }
  261.  
  262. /* Set up the $- variable */
  263.  
  264.     setdash ();
  265.  
  266. /* Load terminal I/O structure if necessary and load the history file */
  267.  
  268.     if (e.iop < iostack)
  269.     {
  270.     PUSHIO (afile, 0, iof);
  271.  
  272.     if (isatty (0) && isatty (1) && !cflag)
  273.     {
  274.         Print_Version (2);
  275.  
  276.         talking = TRUE;
  277. #ifndef NO_HISTORY
  278.         History_Enabled = TRUE;
  279.         Load_History ();
  280.         Configure_Keys ();
  281. #endif
  282.     }
  283.     }
  284.  
  285. #ifdef SIGQUIT
  286.     signal (SIGQUIT, qflag);
  287. #endif
  288.  
  289. /* Read profile ? */
  290.  
  291.     if (((name != (char *)NULL) && (*name == '-')) || level0)
  292.     Load_profiles ();
  293.  
  294. /* Set up signals */
  295.  
  296.     if (talking)
  297.     signal (SIGTERM, sig);
  298.  
  299.     if (signal (SIGINT, SIG_IGN) != SIG_IGN)
  300.     signal (SIGINT, onintr);
  301.  
  302. /* Load any parameters */
  303.  
  304.     dolv = argv;
  305.     dolc = argc;
  306.     dolv[0] = name;
  307.  
  308.     if (dolc > 1)
  309.     {
  310.     for (ap = ++argv; --argc > 0;)
  311.     {
  312.         if (assign (*ap = *argv++, !COPYV))
  313.         dolc--;                    /* keyword */
  314.  
  315.         else
  316.         ap++;
  317.     }
  318.     }
  319.  
  320.     setval (lookup ("#", TRUE), putn ((--dolc < 0) ? (dolc = 0) : dolc));
  321.  
  322. /* Execute the command loop */
  323.  
  324.     while (1)
  325.     {
  326.     if (talking && e.iop <= iostack)
  327.     {
  328.         In_Col_Zero ();
  329.         Check_Mail ();
  330.         put_prompt (ps1->value);
  331.         r_flag = l_rflag;
  332.         closeall ();        /* Clean up any open shell files */
  333.     }
  334.  
  335.     onecommand ();
  336.     }
  337. }
  338.  
  339. /*
  340.  * Set up the value of $-
  341.  */
  342.  
  343. void    setdash ()
  344. {
  345.     register char    *cp, c;
  346.     char        m['z' - 'a' + 1];
  347.  
  348.     for (cp = m, c = 'a'; c <= 'z'; ++c)
  349.     {
  350.     if (FL_TEST (c))
  351.         *(cp++) = c;
  352.     }
  353.  
  354.     *cp = 0;
  355.     setval (lookup ("-", TRUE), m);
  356. }
  357.  
  358. /* Execute a command */
  359.  
  360. static void    onecommand ()
  361. {
  362.     register int    i;
  363.     jmp_buf        m1;
  364.     C_Op        *outtree = (C_Op *)NULL;
  365.  
  366. /* Exit any previous environments */
  367.  
  368.     while (e.oenv)
  369.     quitenv ();
  370.  
  371. /* initialise space */
  372.  
  373.     areanum = 1;
  374.     freehere (areanum);
  375.     freearea (areanum);
  376.     wdlist = (Word_B *)NULL;
  377.     iolist = (Word_B *)NULL;
  378.     e.errpt = (int *)NULL;
  379.     e.cline = space (LINE_MAX);
  380.     e.eline = e.cline + LINE_MAX - 5;
  381.     e.linep = e.cline;
  382.     yynerrs = 0;
  383.     multiline = 0;
  384.     inparse = 1;
  385.     SW_intr = 0;
  386.     execflg = 0;
  387.  
  388. /* Get the line and process it */
  389.  
  390.     if (setjmp (failpt = m1) || ((outtree = yyparse ()) == (C_Op *)NULL) ||
  391.     SW_intr)
  392.     {
  393.  
  394. /* Failed - If parse failed - save command line as history */
  395.  
  396. #ifndef NO_HISTORY
  397.     if ((outtree == (C_Op *)NULL) && Interactive ())
  398.         Add_History (FALSE);
  399. #endif
  400.  
  401. /* If interrupt occured, remove current Input stream */
  402.  
  403.     if (SW_intr && (e.iop > e.iobase))
  404.         e.iop--;
  405.  
  406. /* Quit all environments */
  407.  
  408.     while (e.oenv)
  409.         quitenv ();
  410.  
  411.     scraphere ();
  412.  
  413.     if (!talking && SW_intr)
  414.         leave ();
  415.  
  416. /* Exit */
  417.  
  418.     inparse = 0;
  419.     SW_intr = 0;
  420.     return;
  421.     }
  422.  
  423. /* Ok - reset some variables and then execute the command tree */
  424.  
  425.     inparse = 0;
  426.     Break_List = (Break_C *)NULL;
  427.     Return_List = (Break_C *)NULL;
  428.     SShell_List = (Break_C *)NULL;
  429.     SW_intr = 0;
  430.     execflg = 0;
  431.  
  432. /* Set execute function recursive level and the SubShell count to zero */
  433.  
  434.     Execute_stack_depth = 0;
  435.  
  436. /* Set up Redirection IO (Saved) array and SubShell Environment information */
  437.  
  438.     NSave_IO_E = 0;        /* Number of entries        */
  439.     MSave_IO_E = 0;        /* Max Number of entries    */
  440.     NSubShells = 0;        /* Number of entries        */
  441.     MSubShells = 0;        /* Max Number of entries    */
  442.  
  443. /* Save the environment information */
  444.  
  445. #ifndef NO_HISTORY
  446.     if (Interactive ())
  447.     Add_History (FALSE);
  448. #endif
  449.  
  450. /* Ok - if we wail, we need to clean up the stacks */
  451.  
  452.     if ((setjmp (failpt = m1) == 0) && !FL_TEST ('n'))
  453.     execute (outtree, NOPIPE, NOPIPE, 0);
  454.  
  455. /* Make sure the I/O and environment are back at level 0 and then clear them */
  456.  
  457.     Execute_stack_depth = 0;
  458.     Clear_Extended_File ();
  459.  
  460.     if (NSubShells != 0)
  461.     Delete_G_VL ();
  462.  
  463.     if (NSave_IO_E)
  464.     restore_std (0, TRUE);
  465.  
  466.     if (MSubShells)
  467.     DELETE (SubShells);
  468.  
  469.     if (MSave_IO_E)
  470.     DELETE (SSave_IO);
  471.  
  472. /* Check for interrupts */
  473.  
  474.     if (!talking && SW_intr)
  475.     {
  476.     execflg = 0;
  477.     leave ();
  478.     }
  479.  
  480. /* Run any traps that are required */
  481.  
  482.     if ((i = trapset) != 0)
  483.     {
  484.     trapset = 0;
  485.     runtrap (i);
  486.     }
  487. }
  488.  
  489. /*
  490.  * Terminate current environment with an error
  491.  */
  492.  
  493. void    fail ()
  494. {
  495.     longjmp (failpt, 1);
  496.  
  497.     /* NOTREACHED */
  498. }
  499.  
  500. /*
  501.  * Exit the shell
  502.  */
  503.  
  504. void    leave ()
  505. {
  506.     if (execflg)
  507.     fail ();
  508.  
  509.     if (Orig_I24_V == (void (far *)())NULL)
  510.     {
  511.     S_puts ("sh: ignoring attempt to leave lowest level shell\n");
  512.     fail ();
  513.     }
  514.  
  515. /* Clean up */
  516.  
  517.     scraphere ();
  518.     freehere (1);
  519.  
  520. /* Trap zero on exit */
  521.  
  522.     runtrap (0);
  523.  
  524. /* Dump history on exit */
  525.  
  526. #ifndef NO_HISTORY
  527.     if (talking && isatty(0))
  528.     Dump_History ();
  529. #endif
  530.  
  531.     closeall ();
  532.  
  533. /* Clear swap file if necessary */
  534.  
  535.     Clear_Swap_File ();
  536.  
  537. /* If this is a command only - restore the directory because DOS doesn't
  538.  * and the user might expect it
  539.  */
  540.  
  541.     if (Start_directory != (char *)NULL)
  542.     Restore_Dir (Start_directory);
  543.  
  544. /* Exit - hurray */
  545.  
  546.     exit (exstat);
  547.  
  548. /* NOTREACHED */
  549. }
  550.  
  551. /*
  552.  * Output warning message
  553.  */
  554.  
  555. void    print_warn (fmt)
  556. char    *fmt;
  557. {
  558.     va_list    ap;
  559.  
  560.     va_start (ap, fmt);
  561.     vfprintf (stderr, fmt, ap);
  562.     exstat = -1;
  563.  
  564. /* If leave on error - exit */
  565.  
  566.     if (FL_TEST ('e'))
  567.     leave ();
  568.  
  569.     va_end (ap);
  570. }
  571.  
  572. /*
  573.  * Output error message
  574.  */
  575.  
  576. void    print_error (fmt)
  577. char    *fmt;
  578. {
  579.     va_list    ap;
  580.  
  581. /* Error message processing */
  582.  
  583.     va_start (ap, fmt);
  584.     vfprintf (stderr, fmt, ap);
  585.     exstat = -1;
  586.  
  587.     if (FL_TEST ('e'))
  588.     leave ();
  589.  
  590.     va_end (ap);
  591.  
  592. /* Error processing */
  593.  
  594.     if (FL_TEST ('n'))
  595.     return;
  596.  
  597. /* If not interactive - exit */
  598.  
  599.     if (!talking)
  600.     leave ();
  601.  
  602.     if (e.errpt)
  603.     longjmp (e.errpt, 1);
  604.  
  605. /* closeall (); Removed - caused problems.  There may be problems
  606.  * remaining with files left open?
  607.  */
  608.  
  609.     e.iop = e.iobase = iostack;
  610. }
  611.  
  612. /*
  613.  * Create or delete a new environment.  If f is set, delete the environment
  614.  */
  615.  
  616. bool    newenv (f)
  617. int    f;
  618. {
  619.     register Environ    *ep;
  620.  
  621. /* Delete environment? */
  622.  
  623.     if (f)
  624.     {
  625.     quitenv ();
  626.     return TRUE;
  627.     }
  628.  
  629. /* Create a new environment */
  630.  
  631.     if ((ep = (Environ *) space (sizeof (Environ))) == (Environ *)NULL)
  632.     {
  633.     while (e.oenv)
  634.         quitenv ();
  635.  
  636.     fail ();
  637.     }
  638.  
  639.     *ep = e;
  640.     e.eof_p = FALSE;            /* Disable EOF processing    */
  641.     e.oenv  = ep;
  642.     e.errpt = errpt;
  643.     return FALSE;
  644. }
  645.  
  646. /*
  647.  * Exit the current environment successfully
  648.  */
  649.  
  650. void    quitenv ()
  651. {
  652.     register Environ    *ep;
  653.     register int    fd;
  654.  
  655. /* Restore old environment, delete the space and close any files opened in
  656.  * this environment
  657.  */
  658.  
  659.     if ((ep = e.oenv) != (Environ *)NULL)
  660.     {
  661.  
  662. /* Close any open directories */
  663.  
  664.     if (e.cdir != (DIR *)NULL)
  665.         closedir (e.cdir);
  666.  
  667. /* Get the files used in this environment to close */
  668.  
  669.     fd = e.iofd;
  670.     e = *ep;
  671.  
  672.     DELETE (ep);
  673.  
  674.     while (--fd >= e.iofd)
  675.         S_close (fd, TRUE);
  676.     }
  677. }
  678.  
  679. /*
  680.  * Is character c in s?
  681.  */
  682.  
  683. bool        any (c, s)
  684. register char    c;
  685. register char    *s;
  686. {
  687.     while (*s)
  688.     {
  689.     if (*(s++) == c)
  690.         return TRUE;
  691.     }
  692.  
  693.     return FALSE;
  694. }
  695.  
  696. /*
  697.  * Convert binary to ascii
  698.  */
  699.  
  700. char        *putn (n)
  701. register int    n;
  702. {
  703.     static char        nt[10];
  704.  
  705.     sprintf (nt, "%u", n);
  706.     return nt;
  707. }
  708.  
  709. /*
  710.  * SIGINT interrupt processing
  711.  */
  712.  
  713. void    onintr (signo)
  714. int    signo;
  715. {
  716.  
  717. /* Restore signal processing and set SIGINT detected flag */
  718.  
  719.     signal (SIGINT, onintr);
  720.     SW_intr = 1;
  721.  
  722. /* Zap the swap file, just in case it got corrupted */
  723.  
  724.     S_close (SW_fp, TRUE);
  725.     Clear_Swap_File ();
  726.  
  727. /* Are we talking to the user?  Yes - check in parser */
  728.  
  729.     if (talking)
  730.     {
  731.     if (inparse)
  732.         S_putc (NL);
  733.  
  734. /* Abandon processing */
  735.  
  736.     fail ();
  737.     }
  738.  
  739. /* No - exit */
  740.  
  741.     else
  742.     {
  743.     execflg = 0;
  744.     leave ();
  745.     }
  746. }
  747.  
  748. /*
  749.  * Grap some space and check for an error
  750.  */
  751.  
  752. char    *space (n)
  753. int    n;
  754. {
  755.     register char *cp;
  756.  
  757.     if ((cp = getcell (n)) == (char *)NULL)
  758.     print_error ("sh: out of string space\n");
  759.  
  760.     return cp;
  761. }
  762.  
  763. /*
  764.  * Save a string in a given area
  765.  */
  766.  
  767. char        *strsave (s, a)
  768. register char    *s;
  769. {
  770.     register char    *cp;
  771.  
  772.     if ((cp = space (strlen (s) + 1)) != (char *)NULL)
  773.     {
  774.     setarea ((char *)cp, a);
  775.     return strcpy (cp, s);
  776.     }
  777.  
  778.     return null;
  779. }
  780.  
  781. /*
  782.  * trap handling - Save signal number and restore signal processing
  783.  */
  784.  
  785. void        sig (i)
  786. register int    i;
  787. {
  788.     if (i == SIGINT)        /* Need this because swapper sets it    */
  789.     {
  790.     SW_intr = 0;
  791.  
  792. /* Zap the swap file, just in case it got corrupted */
  793.  
  794.     S_close (SW_fp, TRUE);
  795.     Clear_Swap_File ();
  796.     }
  797.  
  798.     trapset = i;
  799.     signal (i, sig);
  800. }
  801.  
  802. /*
  803.  * Execute a trap command
  804.  */
  805.  
  806. void    runtrap (i)
  807. int    i;
  808. {
  809.     char    *trapstr;
  810.     char    tval[10];
  811.  
  812.     sprintf (tval, "~%d", i);
  813.  
  814.     if (((trapstr = lookup (tval, FALSE)->value)) == null)
  815.     return;
  816.  
  817. /* If signal zero, save a copy of the trap value and then delete the trap */
  818.  
  819.     if (i == 0)
  820.     {
  821.     trapstr = strsave (trapstr, areanum);
  822.     unset (tval, TRUE);
  823.     }
  824.  
  825.     RUN (aword, trapstr, nlchar, TRUE);
  826. }
  827.  
  828. /*
  829.  * Find the given name in the dictionary and return its value.  If the name was
  830.  * not previously there, enter it now and return a null value.
  831.  */
  832.  
  833. Var_List    *lookup (n, cflag)
  834. register char    *n;
  835. bool        cflag;
  836. {
  837.     register Var_List    *vp;
  838.     register char    *cp;
  839.     register int    c;
  840.     static Var_List    dummy;
  841.  
  842. /* Set up the dummy variable */
  843.  
  844.     dummy.name = n;
  845.     dummy.status = RONLY;
  846.     dummy.value = null;
  847.  
  848. /* If digit string - use the dummy to return the value */
  849.  
  850.     if (isdigit (*n))
  851.     {
  852.     for (c = 0; isdigit (*n) && (c < 1000); n++)
  853.         c = c * 10 + *n - '0';
  854.  
  855.     dummy.value = (c <= dolc) ? dolv[c] : null;
  856.     return &dummy;
  857.     }
  858.  
  859. /* Look up in list */
  860.  
  861.     for (vp = vlist; vp != (Var_List *)NULL; vp = vp->next)
  862.     {
  863.     if (eqname (vp->name, n))
  864.         return vp;
  865.     }
  866.  
  867. /* If we don't want to create it, return a dummy */
  868.  
  869.     if (!cflag)
  870.     return &dummy;
  871.  
  872. /* Create a new variable */
  873.  
  874.     cp = findeq (n);
  875.  
  876.     if (((vp = (Var_List *)space (sizeof (Var_List))) == (Var_List *)NULL)
  877.     || (vp->name = space ((int)(cp - n) + 2)) == (char *)NULL)
  878.     {
  879.     dummy.name = null;
  880.     return &dummy;
  881.     }
  882.  
  883. /* Set area for space to zero - no auto-delete */
  884.  
  885.     setarea ((char *)vp, 0);
  886.     setarea ((char *)vp->name, 0);
  887.  
  888. /* Just the name upto the equals sign, no more */
  889.  
  890.     copy_to_equals (vp->name, n);
  891.  
  892. /* Link into list */
  893.  
  894.     vp->value = null;
  895.     vp->next = vlist;
  896.     vp->status = GETCELL;
  897.     vlist = vp;
  898.     return vp;
  899. }
  900.  
  901. /*
  902.  * give variable at `vp' the value `val'.
  903.  */
  904.  
  905. void        setval (vp, val)
  906. Var_List    *vp;
  907. char        *val;
  908. {
  909.     nameval (vp, val, (char *)NULL, FALSE);
  910. }
  911.  
  912. /*
  913.  * Copy and check that it terminates in an equals sign
  914.  */
  915.  
  916. static char    *copy_to_equals (d, s)
  917. char        *d, *s;
  918. {
  919.     int        n = (int) (findeq (s) - s);
  920.  
  921.     strncpy (d, s, n);
  922.     *(d += n) = '=';
  923.     *(++d) = 0;
  924.     return d;
  925. }
  926.  
  927. /*
  928.  * Set up new value for name
  929.  *
  930.  * If name is not NULL, it must be a prefix of the space `val', and end with
  931.  * `='.  This is all so that exporting values is reasonably painless.
  932.  */
  933.  
  934. static void        nameval (vp, val, name, disable)
  935. register Var_List    *vp;
  936. char            *val;
  937. char            *name;
  938. bool            disable;
  939. {
  940.     register char    *xp;
  941.     int            fl = 0;
  942.  
  943. /* Check if variable is read only */
  944.  
  945.     if (vp->status & RONLY)
  946.     {
  947.     char    c = *(xp = findeq (vp->name));
  948.  
  949.     *xp = 0;
  950.     S_puts (xp);
  951.     *xp = c;
  952.     print_error (" is read-only\n");
  953.     return;
  954.     }
  955.  
  956. /* Check for $PATH reset in restricted shell */
  957.  
  958.     if (!disable && (strncmp (vp->name, "PATH=", 5) == 0) && check_rsh (Path))
  959.     return;
  960.  
  961. /* Get space for string ? */
  962.  
  963.     if (name == (char *)NULL)
  964.     {
  965.     if ((xp = space (strlen (vp->name) + strlen (val) + 2)) == (char *)NULL)
  966.         return;
  967.  
  968. /* make string:  name=value */
  969.  
  970.     setarea ((char *)xp, 0);
  971.     name = xp;
  972.  
  973.     xp = copy_to_equals (xp, vp->name);
  974.     strcpy (xp, val);
  975.     val = xp;
  976.     fl = GETCELL;
  977.     }
  978.  
  979.     if (vp->status & GETCELL)
  980.     DELETE (vp->name);    /* form new string `name=value' */
  981.  
  982.     vp->name = name;
  983.     vp->value = val;
  984.     vp->status |= fl;
  985.  
  986.     if (FL_TEST ('a'))
  987.     s_vstatus (vp, EXPORT);
  988.  
  989. /* Convert UNIX to DOS for PATH variable */
  990.  
  991.     if (vp == path)
  992.     U2D_Path ();
  993. }
  994.  
  995. /*
  996.  * Set the status of an environment variable
  997.  */
  998.  
  999. void        s_vstatus (vp, flag)
  1000. Var_List    *vp;
  1001. int        flag;            /* Addition status flags    */
  1002. {
  1003.     if (isalpha (*vp->name))        /* not an internal symbol ($# etc) */
  1004.     vp->status |= flag;
  1005. }
  1006.  
  1007. /*
  1008.  * Check for assignment X=Y
  1009.  */
  1010.  
  1011. bool        isassign (s)
  1012. register char    *s;
  1013. {
  1014.     if (!isalpha (*s))
  1015.     return FALSE;
  1016.  
  1017.     for (; *s != '='; s++)
  1018.     {
  1019.     if (!*s || !isalnum (*s))
  1020.         return FALSE;
  1021.     }
  1022.  
  1023.     return TRUE;
  1024. }
  1025.  
  1026. /*
  1027.  * Execute an assignment.  If a valid assignment, load it into the variable
  1028.  * list.
  1029.  */
  1030.  
  1031. bool        assign (s, cf)
  1032. register char    *s;
  1033. int        cf;
  1034. {
  1035.     register char    *cp;
  1036.     Var_List        *vp;
  1037.  
  1038.     if (!isalpha (*s))
  1039.     return FALSE;
  1040.  
  1041.     for (cp = s; *cp != '='; cp++)
  1042.     {
  1043.     if (!*cp || !isalnum (*cp))
  1044.         return FALSE;
  1045.     }
  1046.  
  1047.     nameval ((vp = lookup (s, TRUE)), ++cp, (cf == COPYV ? (char *)NULL : s),
  1048.          FALSE);
  1049.  
  1050.     if (cf != COPYV)
  1051.     vp->status &= ~GETCELL;
  1052.  
  1053.     return TRUE;
  1054. }
  1055.  
  1056. /*
  1057.  * Compare two environment strings
  1058.  */
  1059.  
  1060. bool            eqname(n1, n2)
  1061. register char        *n1, *n2;
  1062. {
  1063.     for (; *n1 != '=' && *n1 != 0; n1++)
  1064.     {
  1065.     if (*n2++ != *n1)
  1066.         return FALSE;
  1067.     }
  1068.  
  1069.     return (!*n2 || (*n2 == '=')) ? TRUE : FALSE;
  1070. }
  1071.  
  1072. /*
  1073.  * Find the equals sign in a string
  1074.  */
  1075.  
  1076. char        *findeq (cp)
  1077. register char    *cp;
  1078. {
  1079.     while (*cp && (*cp != '='))
  1080.     cp++;
  1081.  
  1082.     return cp;
  1083. }
  1084.  
  1085. /*
  1086.  * Duplicate the Variable List for a Subshell
  1087.  *
  1088.  * Create a new Var_list environment for a Sub Shell
  1089.  */
  1090.  
  1091. int    Create_NG_VL ()
  1092. {
  1093.     int            i;
  1094.     S_SubShell        *sp;
  1095.     Var_List        *vp, *vp1;
  1096.  
  1097.     for (sp = SubShells, i = 0; (i < NSubShells) &&
  1098.                    (SubShells[i].depth < Execute_stack_depth);
  1099.      i++);
  1100.  
  1101. /* If depth is greater or equal to the Execute_stack_depth - we should panic
  1102.  * as this should not happen.  However, for the moment, I'll ignore it
  1103.  */
  1104.  
  1105.     if (NSubShells == MSubShells)
  1106.     {
  1107.     sp = (S_SubShell *)space ((MSubShells + SSAVE_IO_SIZE) * sizeof (S_SubShell));
  1108.  
  1109. /* Check for error */
  1110.  
  1111.     if (sp == (S_SubShell *)NULL)
  1112.     {
  1113.         errno = ENOMEM;
  1114.         return -1;
  1115.     }
  1116.  
  1117. /* Save original data */
  1118.  
  1119.     if (MSubShells != 0)
  1120.     {
  1121.         memcpy (sp, SubShells, sizeof (S_SubShell) * MSubShells);
  1122.         DELETE (SubShells);
  1123.     }
  1124.  
  1125.     setarea ((char *)sp, 0);
  1126.     SubShells = sp;
  1127.     MSubShells += SSAVE_IO_SIZE;
  1128.     }
  1129.  
  1130. /* Save the depth and the old vlist value */
  1131.  
  1132.     sp = &SubShells[NSubShells++];
  1133.     sp->depth  = Execute_stack_depth;
  1134.     sp->header = vlist;
  1135.     vlist = (Var_List *)NULL;
  1136.  
  1137. /* Duplicate the old Variable list */
  1138.  
  1139.     for (vp = sp->header; vp != (Var_List *)NULL; vp = vp->next)
  1140.     {
  1141.     nameval ((vp1 = lookup (vp->name, TRUE)), findeq (vp->name) + 1,
  1142.          (char *)NULL, TRUE);
  1143.  
  1144.     vp1->status |= (vp->status & (C_MSDOS | PONLY | EXPORT | RONLY));
  1145.     }
  1146.  
  1147. /* Reset global values */
  1148.  
  1149.     Load_G_VL ();
  1150.     return 0;
  1151. }
  1152.  
  1153. /*
  1154.  * Delete a SubShell environment and restore the original
  1155.  */
  1156.  
  1157. void    Delete_G_VL ()
  1158. {
  1159.     int        j;
  1160.     S_SubShell    *sp;
  1161.     Var_List    *vp;
  1162.  
  1163.     for (j = NSubShells; j > 0; j--)
  1164.     {
  1165.        sp = &SubShells[j - 1];
  1166.  
  1167.        if (sp->depth < Execute_stack_depth)
  1168.        break;
  1169.  
  1170. /* Reduce number of entries */
  1171.  
  1172.     --NSubShells;
  1173.  
  1174. /* Release the space */
  1175.  
  1176.     for (vp = vlist; vp != (Var_List *)NULL; vp = vp->next)
  1177.     {
  1178.         if (vp->status & GETCELL)
  1179.         DELETE (vp->name);
  1180.  
  1181.         DELETE (vp);
  1182.     }
  1183.  
  1184. /* Restore vlist information */
  1185.  
  1186.     vlist = sp->header;
  1187.     Load_G_VL ();
  1188.     }
  1189. }
  1190.  
  1191. /*
  1192.  * Load GLobal Var List values
  1193.  */
  1194.  
  1195. static void    Load_G_VL ()
  1196. {
  1197.     path  = lookup (Path, TRUE);
  1198.     ifs   = lookup ("IFS", TRUE);
  1199.     ps1   = lookup ("PS1", TRUE);
  1200.     ps2   = lookup ("PS2", TRUE);
  1201.     C_dir = lookup ("~", TRUE);
  1202.     Restore_Dir (C_dir->value);
  1203. }
  1204.  
  1205. /*
  1206.  * Match a pattern as in sh(1).  Enhancement to handle prefix processing
  1207.  *
  1208.  * IgnoreCase - ignore case on comparisions.
  1209.  * end - end of match in 'string'.
  1210.  * mode - mode for match processing - see GM_ flags in sh.h
  1211.  */
  1212.  
  1213. bool        gmatch (string, pattern, IgnoreCase, end, mode)
  1214. register char    *string, *pattern;
  1215. bool        IgnoreCase;
  1216. char        **end;
  1217. int        mode;
  1218. {
  1219.     register int    string_c, pattern_c;
  1220.     char        *save_end;
  1221.  
  1222.     if ((string == (char *)NULL) || (pattern == (char *)NULL))
  1223.     return FALSE;
  1224.  
  1225.     while ((pattern_c = *(pattern++) & CMASK) != '\0')
  1226.     {
  1227.     string_c = *(string++) & QMASK;
  1228.  
  1229.     switch (pattern_c)
  1230.     {
  1231.         case '[':            /* Class expression        */
  1232.         if ((pattern = cclass (pattern, string_c, IgnoreCase)) == (char *)NULL)
  1233.             return FALSE;
  1234.  
  1235.         break;
  1236.  
  1237.         case '?':            /* Match any character        */
  1238.         if (string_c == 0)
  1239.             return FALSE;
  1240.  
  1241.         break;
  1242.  
  1243.         case '*':            /* Match as many as possible    */
  1244.         --string;
  1245.         save_end = (char *)NULL;
  1246.  
  1247.         do
  1248.         {
  1249.             if (!*pattern ||
  1250.             gmatch (string, pattern, IgnoreCase, end, mode))
  1251.             {
  1252.             if (mode == GM_LONGEST)
  1253.                 save_end = *end;
  1254.  
  1255.             else
  1256.                 return TRUE;
  1257.             }
  1258.  
  1259.         } while (*(string++));
  1260.  
  1261.         if (end != (char **)NULL)
  1262.             *end = save_end;
  1263.  
  1264.         return (save_end == (char *)NULL) ? FALSE : TRUE;
  1265.  
  1266.         default:
  1267.         if (IgnoreCase)
  1268.         {
  1269.             string_c = tolower (string_c);
  1270.             pattern_c = tolower ((pattern_c & ~QUOTE));
  1271.         }
  1272.  
  1273.         if (string_c != (pattern_c & ~QUOTE))
  1274.             return FALSE;
  1275.     }
  1276.     }
  1277.  
  1278.     if (end != (char **)NULL)
  1279.     {
  1280.     *end = string;
  1281.     return TRUE;
  1282.     }
  1283.  
  1284.     return (*string == 0) ? TRUE : FALSE;
  1285. }
  1286.  
  1287. /*
  1288.  * Process a class expression - []
  1289.  */
  1290.  
  1291. static char    *cclass (pattern, string_c, IgnoreCase)
  1292. register char    *pattern;
  1293. register int    string_c;
  1294. bool        IgnoreCase;
  1295. {
  1296.     register int    llimit_c, ulimit_c, not, found;
  1297.  
  1298. /* Exclusive or inclusive class */
  1299.  
  1300.     if ((not = *pattern == NOT) != 0)
  1301.     pattern++;
  1302.  
  1303.     found = not;
  1304.  
  1305.     do
  1306.     {
  1307.     if (!*pattern)
  1308.         return (char *)NULL;
  1309.  
  1310. /* Get the next character in class, converting to lower case if necessary */
  1311.  
  1312.     llimit_c = IgnoreCase ? tolower ((*pattern & CMASK))
  1313.                   : (*pattern & CMASK);
  1314.  
  1315. /* If this is a range, get the end of range character */
  1316.  
  1317.     if ((*(pattern + 1) == '-') && (*(pattern + 2) != ']'))
  1318.     {
  1319.         ulimit_c = IgnoreCase ? tolower ((*(pattern + 2) & CMASK))
  1320.                   : (*(pattern + 2) & CMASK);
  1321.         pattern++;
  1322.     }
  1323.  
  1324.     else
  1325.         ulimit_c = llimit_c;
  1326.  
  1327. /* Is the current character in the class? */
  1328.  
  1329.     if ((llimit_c <= string_c) && (string_c <= ulimit_c))
  1330.         found = !not;
  1331.  
  1332.     } while (*(++pattern) != ']');
  1333.  
  1334.     return found ? pattern + 1 : (char *)NULL;
  1335. }
  1336.  
  1337. /*
  1338.  * Suffix processing - find the longest/shortest suffix.
  1339.  */
  1340.  
  1341. bool        gmatch_suffix (string, pattern, IgnoreCase, start, mode)
  1342. register char    *string, *pattern;
  1343. bool        IgnoreCase;
  1344. char        **start;
  1345. int        mode;
  1346. {
  1347.     char    *save_start = (char *)NULL;
  1348.  
  1349. /* Scan the string, looking for a match to the end */
  1350.  
  1351.     while (*string)
  1352.     {
  1353.     if (gmatch (string, pattern, IgnoreCase, (char **)NULL, GM_ALL))
  1354.     {
  1355.  
  1356. /* If longest, stop here */
  1357.  
  1358.         if (mode == GM_LONGEST)
  1359.         {
  1360.         *start = string;
  1361.         return TRUE;
  1362.         }
  1363.  
  1364. /* Save the start of the shortest string so far and continue */
  1365.  
  1366.         save_start = string;
  1367.     }
  1368.  
  1369.     ++string;
  1370.     }
  1371.  
  1372.     return ((*start = save_start) == (char *)NULL) ? FALSE : TRUE;
  1373. }
  1374.  
  1375. /*
  1376.  * Get a string in a malloced area
  1377.  */
  1378.  
  1379. char        *getcell (nbytes)
  1380. unsigned int    nbytes;
  1381. {
  1382.     s_region        *np;
  1383.     void        (*save_signal)(int);
  1384. #ifdef CHECK_MALLOC
  1385.     char        *rp;
  1386. #endif
  1387.  
  1388.     if (nbytes == 0)
  1389.     abort ();    /* silly and defeats the algorithm */
  1390.  
  1391. /* Grab some space */
  1392.  
  1393.     np = (s_region *)calloc (nbytes + sizeof (s_region)
  1394. #ifdef CHECK_MALLOC
  1395.                  + sizeof (unsigned int)
  1396. #endif
  1397.                             , 1);
  1398.  
  1399.     if (np == (s_region *)NULL)
  1400.         return (char *)NULL;
  1401.  
  1402. #ifdef CHECK_MALLOC
  1403.     if ((((FP_SEG (np)) << 4L) + FP_OFF (np)) > 0x9fc00L)
  1404.     {
  1405.     free (np);
  1406.     print_warn ("Malloc access to bad segment\n");
  1407.     return (char *)NULL;
  1408.     }
  1409.  
  1410.     np->magic1             = MAGIC1;
  1411.     np->len               = nbytes;
  1412.     rp                          = (char *)(np + 1);
  1413.     *((int *)(rp + nbytes)) = MAGIC2;
  1414. #endif
  1415.  
  1416. /* Disable signals */
  1417.  
  1418.     save_signal = signal (SIGINT, SIG_IGN);
  1419.  
  1420. /* Link into chain */
  1421.  
  1422.     np->next = areastart;
  1423.     np->area = areanum;
  1424.     areastart = np;
  1425.  
  1426. /* Restore signals */
  1427.  
  1428.     signal (SIGINT, save_signal);
  1429.  
  1430.     return ((char *)np) + sizeof (s_region);
  1431. }
  1432.  
  1433. /*
  1434.  * Free a string in a malloced area
  1435.  */
  1436.  
  1437. void    freecell (s)
  1438. char    *s;
  1439. {
  1440.     register s_region    *cp = areastart;
  1441.     s_region        *lp = (s_region *)NULL;
  1442.     s_region        *sp = (s_region *)(s - sizeof (s_region));
  1443.     void        (*save_signal)(int);
  1444.  
  1445. /* Disable signals */
  1446.  
  1447.     save_signal = signal (SIGINT, SIG_IGN);
  1448.  
  1449. /* Find the string in the chain */
  1450.  
  1451.     if (s != (char *)NULL)
  1452.     {
  1453.     while (cp != (s_region *)NULL)
  1454.     {
  1455.         if (cp != sp)
  1456.         {
  1457.         lp = cp;
  1458.         cp = cp->next;
  1459.         continue;
  1460.         }
  1461.  
  1462. /* First in chain ? */
  1463.  
  1464.         else if (lp == (s_region *)NULL)
  1465.         areastart = cp->next;
  1466.  
  1467. /* Delete the current entry and relink */
  1468.  
  1469.         else
  1470.         lp->next = cp->next;
  1471.  
  1472.         SH_FREE (cp);
  1473.         break;
  1474.     }
  1475.     }
  1476.  
  1477. /* Restore signals */
  1478.  
  1479.     signal (SIGINT, save_signal);
  1480. }
  1481.  
  1482. /*
  1483.  * Autodelete space nolonger required.  Ie. Free all the strings in a malloced
  1484.  * area
  1485.  */
  1486.  
  1487. void        freearea (a)
  1488. register int    a;
  1489. {
  1490.     register s_region    *cp = areastart;
  1491.     s_region        *lp = (s_region *)NULL;
  1492.     void        (*save_signal)(int);
  1493.  
  1494. /* Disable signals */
  1495.  
  1496.     save_signal = signal (SIGINT, SIG_IGN);
  1497.  
  1498.     while (cp != (s_region *)NULL)
  1499.     {
  1500.  
  1501. /* Is the area number less than that specified - yes, continue */
  1502.  
  1503.     if (cp->area < a)
  1504.     {
  1505.         lp = cp;
  1506.         cp = cp->next;
  1507.     }
  1508.  
  1509. /* OK - delete the area.  Is it the first in chain ?  Yes, delete, relink
  1510.  * and update start location
  1511.  */
  1512.  
  1513.     else if (lp == (s_region *)NULL)
  1514.     {
  1515.         lp = cp;
  1516.         cp = cp->next;
  1517.         areastart = cp;
  1518.  
  1519.         SH_FREE (lp);
  1520.         lp = (char *)NULL;
  1521.     }
  1522.  
  1523. /* Not first, delete the current entry and relink */
  1524.  
  1525.     else
  1526.     {
  1527.         lp->next = cp->next;
  1528.         SH_FREE (cp);
  1529.         cp = lp->next;
  1530.     }
  1531.     }
  1532.  
  1533. /* Restore signals */
  1534.  
  1535.     signal (SIGINT, save_signal);
  1536. }
  1537.  
  1538. /*
  1539.  * Set the area number for a malloced string.  This allows autodeletion of
  1540.  * space that is nolonger required.
  1541.  */
  1542.  
  1543. void    setarea (cp,a)
  1544. char    *cp;
  1545. int    a;
  1546. {
  1547.     s_region    *sp = (s_region *)(cp - sizeof (s_region));
  1548.  
  1549.     if (cp != (char *)NULL)
  1550.     sp->area = a;
  1551. }
  1552.  
  1553. /*
  1554.  * Get the area number for a malloced string
  1555.  */
  1556.  
  1557. int    getarea (cp)
  1558. char    *cp;
  1559. {
  1560.     s_region    *sp = (s_region *)(cp - sizeof (s_region));
  1561.  
  1562.     return sp->area;
  1563. }
  1564.  
  1565. /* Output one of the Prompt.  We save the prompt for the history part of
  1566.  * the program
  1567.  */
  1568.  
  1569. void    put_prompt (s)
  1570. char    *s;
  1571. {
  1572.     struct dosdate_t     d_date;
  1573.     struct dostime_t    d_time;
  1574.     int            i;
  1575.     char        buf[PATH_MAX + 4];
  1576.  
  1577.     last_prompt = s;        /* Save the Last prompt output        */
  1578.  
  1579.     _dos_gettime (&d_time);    /* Get the date and time in case    */
  1580.     _dos_getdate (&d_date);
  1581.  
  1582.     while (*s)
  1583.     {
  1584.  
  1585. /* If a format character, process it */
  1586.  
  1587.     if (*s == '%')
  1588.     {
  1589.         s++;
  1590.         *s = tolower(*s);
  1591.  
  1592.         if (*s == '%')
  1593.         v1_putc ('%');
  1594.  
  1595.         else
  1596.         {
  1597.         *buf = 0;
  1598.  
  1599.         switch (*(s++))
  1600.         {
  1601.             case 'e':            /* Current event number */
  1602.             if (History_Enabled)
  1603.                 sprintf (buf, "%d", Current_Event + 1);
  1604.  
  1605.             break;
  1606.  
  1607.             case 't':            /* time        */
  1608.             sprintf (buf,"%.2d:%.2d", d_time.hour, d_time.minute);
  1609.             break;
  1610.  
  1611.             case 'd':            /* date        */
  1612.             sprintf (buf, "%.3s %.2d-%.2d-%.2d",
  1613.                  &"SunMonTueWedThuFriSat"[d_date.dayofweek * 3],
  1614.                  d_date.day, d_date.month, d_date.year % 100);
  1615.             break;
  1616.  
  1617.             case 'p':            /* directory    */
  1618.             case 'n':            /* default drive */
  1619.             strcpy (buf, C_dir->value);
  1620.  
  1621.             if (*(s - 1) == 'n')
  1622.                 buf[1] = 0;
  1623.  
  1624.             break;
  1625.  
  1626.             case 'v':            /* version        */
  1627.             sprintf (buf, "MS-DOS %.2d:%.2d", _osmajor, _osminor);
  1628.             break;
  1629.         }
  1630.  
  1631. /* Output the string */
  1632.  
  1633.         v1_puts (buf);
  1634.         }
  1635.     }
  1636.  
  1637. /* Escaped character ? */
  1638.  
  1639.     else if (*s == '\\')
  1640.     {
  1641.         ++s;
  1642.         if ((i = Process_Escape (&s)) == -1)
  1643.         i = 0;
  1644.  
  1645.         v1_putc ((char)i);
  1646.     }
  1647.  
  1648.     else
  1649.         v1_putc (*(s++));
  1650.     }
  1651. }
  1652.  
  1653. /*
  1654.  * Get the current path in UNIX format and save it in the environment
  1655.  * variable $~
  1656.  */
  1657.  
  1658. void    Getcwd ()
  1659. {
  1660.     char    ldir[PATH_MAX + 6];
  1661.  
  1662.     getcwd (ldir, PATH_MAX + 4);
  1663.     ldir[PATH_MAX + 5] = 0;
  1664.  
  1665. /* Convert to Unix format */
  1666.  
  1667.     Convert_Backslashes (strlwr (ldir));
  1668.  
  1669. /* Save in environment */
  1670.  
  1671.     setval ((C_dir = lookup ("~", TRUE)), ldir);
  1672. }
  1673.  
  1674. /*
  1675.  * Initialise the shell and Patch up various parts of the system for the
  1676.  * shell.  At the moment, we modify the ctype table so that _ is an upper
  1677.  * case character.
  1678.  */
  1679.  
  1680. static bool    Initialise (name)
  1681. char        *name;
  1682. {
  1683.     register char    *s, *s1;
  1684.     char        **ap;
  1685.     Var_List        *lset;
  1686.     bool        l_rflag = FALSE;
  1687.  
  1688. /* Patch the ctype table as a cheat */
  1689.  
  1690.     (_ctype+1)['_'] |= _UPPER;
  1691.  
  1692. /* Get original interrupt 24 address and set up our new interrupt 24
  1693.  * address
  1694.  */
  1695.  
  1696.     Orig_I24_V = _dos_getvect (0x24);
  1697.     _dos_setvect (0x24, SW_Int24);
  1698.  
  1699. /* Load the environment into our structures */
  1700.  
  1701.     if ((ap = environ) != (char **)NULL)
  1702.     {
  1703.     while (*ap)
  1704.         assign (*ap++, !COPYV);
  1705.  
  1706.     for (ap = environ; *ap;)
  1707.         s_vstatus (lookup (*ap++, TRUE), EXPORT);
  1708.     }
  1709.  
  1710. /* Change COMSPEC to unix format for execution */
  1711.  
  1712.     lset = lookup ("COMSPEC", FALSE);
  1713.     Convert_Backslashes (lset->value);
  1714.     s_vstatus (lset, C_MSDOS);
  1715.  
  1716. /* Zap all files */
  1717.  
  1718.     closeall ();
  1719.     areanum = 1;
  1720.  
  1721. /* Get the current directory */
  1722.  
  1723.     Getcwd ();
  1724.  
  1725. /* Set up SHELL variable.  First check for a restricted shell.  Check the
  1726.  * restricted shell
  1727.  */
  1728.  
  1729.     Program_Name = name;
  1730.     if ((s = strrchr (name, '/')) == (char *)NULL)
  1731.     s = name;
  1732.  
  1733.     if ((s1 = strchr (s, '.')) != (char *)NULL)
  1734.     *s1 = 0;
  1735.  
  1736.     if (strcmp (s, "rsh") == 0)
  1737.     l_rflag = TRUE;
  1738.  
  1739. /* Has the program name got a .exe extension - Yes probably DOS 3+.  So
  1740.  * save it as the Shell name
  1741.  */
  1742.  
  1743.     lset = lookup (shell, TRUE);
  1744.  
  1745.     if (s1 != (char *)NULL)
  1746.     {
  1747.     if ((stricmp (s1 + 1, "exe") == 0) && (lset->value == null))
  1748.         setval (lset, name);
  1749.  
  1750.     *s1 = '.';
  1751.     }
  1752.  
  1753. /* Default if necessary */
  1754.  
  1755.     if (lset->value == null)
  1756.     setval (lset, shellname);
  1757.  
  1758.     Convert_Backslashes (lset->value);
  1759.     s_vstatus (lset, EXPORT);
  1760.  
  1761. /* Check for restricted shell */
  1762.  
  1763.     if ((s = strrchr (lset->value, '/')) == (char *)NULL)
  1764.     s = lset->value;
  1765.  
  1766.     else
  1767.     s++;
  1768.  
  1769.     if (*s == 'r')
  1770.     l_rflag = TRUE;
  1771.  
  1772. /* Set up home directory */
  1773.  
  1774.     if ((lset = lookup (home, TRUE))->value == null)
  1775.     setval (lset, C_dir->value);
  1776.  
  1777.     s_vstatus (lset, EXPORT);
  1778.  
  1779. /* Set up history file location */
  1780.  
  1781.     setval (lookup ("$", TRUE), putn (getpid ()));
  1782.  
  1783.     Load_G_VL ();
  1784.     path->status |= (EXPORT | PONLY);
  1785.     ifs->status  |= (EXPORT | PONLY);
  1786.     ps1->status  |= (EXPORT | PONLY);
  1787.     ps2->status  |= (EXPORT | PONLY);
  1788.  
  1789.     if (path->value == null)
  1790.     setval (path, search);
  1791.  
  1792.     if (ifs->value == null)
  1793.     setval (ifs, " \t\n");
  1794.  
  1795.     if (ps1->value == null)
  1796.     setval (ps1, "$ ");
  1797.  
  1798.     if (ps2->value == null)
  1799.     setval (ps2, "> ");
  1800.  
  1801.     return l_rflag;
  1802. }
  1803.  
  1804. /*
  1805.  * Mail Check processing.  Every $MAILCHECK seconds, we check either $MAIL
  1806.  * or $MAILPATH to see if any file has changed its modification time since
  1807.  * we last looked.  In $MAILCHECK, the files are separated by semi-colon (;).
  1808.  * If the filename contains a %, the string following the % is the message
  1809.  * to display if the file has changed.
  1810.  */
  1811.  
  1812. static void    Check_Mail ()
  1813. {
  1814.     int            delay = atoi (lookup ("MAILCHECK", FALSE)->value);
  1815.     Var_List        *mail = lookup ("MAIL", FALSE);
  1816.     Var_List        *mailp = lookup ("MAILPATH", FALSE);
  1817.     static time_t    last = 0L;
  1818.     time_t        current = time ((time_t *)NULL);
  1819.     struct stat        st;
  1820.     char        *cp, *sp, *ap;
  1821.  
  1822. /* Have we waited long enough */
  1823.  
  1824.     if ((current - last) < delay)
  1825.     return;
  1826.  
  1827. /* Yes - Check $MAILPATH.  If it is defined, process it.  Otherwise, use
  1828.  * $MAIL
  1829.  */
  1830.  
  1831.     if (mailp->value != null)
  1832.     {
  1833.  
  1834. /* Check MAILPATH */
  1835.  
  1836.     sp = mailp->value;
  1837.  
  1838. /* Look for the next separator */
  1839.  
  1840.     while ((cp = strchr (sp, ';')) != (char *)NULL)
  1841.     {
  1842.         *cp = 0;
  1843.  
  1844. /* % in string ? */
  1845.  
  1846.         if ((ap = strchr (ap, '%')) != (char *)NULL)
  1847.         *ap = 0;
  1848.  
  1849. /* Check the file name */
  1850.  
  1851.         if ((stat (sp, &st) != -1) && (st.st_mtime > last))
  1852.         {
  1853.         if (ap != (char *)NULL)
  1854.         {
  1855.             S_puts (ap + 1);
  1856.             S_putc (NL);
  1857.         }
  1858.  
  1859.         else
  1860.             S_puts (ymail);
  1861.         }
  1862.  
  1863. /* Restore the % */
  1864.  
  1865.         if (ap != (char *)NULL)
  1866.         *ap = '%';
  1867.  
  1868. /* Restore the semi-colon and find the next one */
  1869.  
  1870.         *cp = ';';
  1871.         sp = cp + 1;
  1872.     }
  1873.     }
  1874.  
  1875. /* Just check MAIL */
  1876.  
  1877.     else if ((mail->value != null) && (stat (mail->value, &st) != -1) &&
  1878.          (st.st_mtime > last))
  1879.     S_puts (ymail);
  1880.  
  1881. /* Save the last check time */
  1882.  
  1883.     last = current;
  1884. }
  1885.  
  1886. /*
  1887.  * Preprocess Argv to get handle of options in the format /x
  1888.  *
  1889.  * Some programs invoke the shell using / instead of - to mark the options.
  1890.  * We need to convert to -.  Also /c is a special case.  The rest of the
  1891.  * command line is the command to execute.  So, we get the command line
  1892.  * from the original buffer instead of argv array.
  1893.  */
  1894.  
  1895. static void    Pre_Process_Argv (argv)
  1896. char        **argv;
  1897. {
  1898.     char    *ocl = (char far *)((((long)_psp) << 16) + 0x081L);
  1899.  
  1900.  
  1901. /* Check for these options */
  1902.  
  1903.     while ((*++argv != (char *)NULL) && (strlen (*argv) == 2) &&
  1904.        (**argv == '/'))
  1905.     {
  1906.        *strlwr (*argv) = '-';
  1907.  
  1908. /* Get the original information from the command line */
  1909.  
  1910.        if ((*argv)[1] == 'c')
  1911.        {
  1912.         while ((*ocl != '/') && (*(ocl + 1) != 'c') && (*ocl) &&
  1913.            (*ocl != '\r'))
  1914.         ++ocl;
  1915.  
  1916.         if (*ocl != '/')
  1917.         continue;
  1918.  
  1919. /* Find the start of the string */
  1920.  
  1921.         ocl += 2;
  1922.  
  1923.         while (isspace (*ocl) && (*ocl != '\r'))
  1924.         ++ocl;
  1925.  
  1926.         if (*ocl == '\r')
  1927.         continue;
  1928.  
  1929. /* Found the start.  Set up next parameter and ignore the rest */
  1930.  
  1931.         if (*(argv + 1) == (char *)NULL)
  1932.         continue;
  1933.  
  1934.         *(argv + 1) = ocl;
  1935.         *(argv + 2) = (char *)NULL;
  1936.  
  1937.         if ((ocl = strchr (ocl, '\r')) != (char *)NULL)
  1938.         *ocl = 0;
  1939.  
  1940.         return;
  1941.        }
  1942.     }
  1943. }
  1944.  
  1945. /*
  1946.  * Convert backslashes to slashes for UNIX
  1947.  */
  1948.  
  1949. void        Convert_Backslashes (sp)
  1950. char        *sp;
  1951. {
  1952.     while (*sp)
  1953.     {
  1954.     if (*sp == '\\')
  1955.         *sp = '/';
  1956.  
  1957.     ++sp;
  1958.     }
  1959. }
  1960.  
  1961. /* Load profiles onto I/O Stack */
  1962.  
  1963. static void    Load_profiles ()
  1964. {
  1965.     char    *name;
  1966.     int        f;
  1967.  
  1968. /* Set up home profile */
  1969.  
  1970.     name = Build_H_Filename ("profile");
  1971.  
  1972.     talking = TRUE;
  1973.  
  1974.     if ((f = O_for_execute (name, (char **)NULL, (int *)NULL)) >= 0)
  1975.     {
  1976.     PUSHIO (afile, remap (f), filechar);
  1977.     }
  1978.  
  1979.     DELETE (name);
  1980.  
  1981.     if ((f = O_for_execute ("/etc/profile", (char **)NULL, (int *)NULL)) >= 0)
  1982.     {
  1983.     PUSHIO (afile, remap (f), filechar);
  1984.     }
  1985. }
  1986.  
  1987. /*
  1988.  * Convert Unix PATH to MSDOS PATH
  1989.  */
  1990.  
  1991. static void    U2D_Path ()
  1992. {
  1993.     char    *cp = path->value;
  1994.     int        colon = 0;
  1995.  
  1996. /* If there is a semi-colon or a backslash, we assume this is a DOS format
  1997.  * path
  1998.  */
  1999.  
  2000.     if ((strchr (cp, ';') != (char *)NULL) ||
  2001.     (strchr (cp, '\\') != (char *)NULL))
  2002.     return;
  2003.  
  2004. /* Count the number of colons */
  2005.  
  2006.     while ((cp = strchr (cp, ':')) != (char *)NULL)
  2007.     {
  2008.     ++colon;
  2009.     ++cp;
  2010.     }
  2011.  
  2012. /* If there are no colons or there is one colon as the second character, it
  2013.  * is probably an MSDOS path
  2014.  */
  2015.  
  2016.     cp = path->value;
  2017.     if ((colon == 0) || ((colon == 1) && (*(cp + 1) == ':')))
  2018.     return;
  2019.  
  2020. /* Otherwise, convert all colons to semis */
  2021.  
  2022.     while ((cp = strchr (cp, ':')) != (char *)NULL)
  2023.     *(cp++) = ';';
  2024. }
  2025.  
  2026. /* Generate a file name from a directory and name.  Return null if an error
  2027.  * occurs or some malloced space containing the file name otherwise
  2028.  */
  2029.  
  2030. char    *Build_H_Filename (name)
  2031. char    *name;
  2032. {
  2033.     char    *dir = lookup (home, FALSE)->value;
  2034.     char    *cp;
  2035.  
  2036. /* Get some space */
  2037.  
  2038.     if ((cp = getcell (strlen (dir) + strlen (name) + 2)) == (char *)NULL)
  2039.     return null;
  2040.  
  2041. /* Addend the directory and a / if the directory does not end in one */
  2042.  
  2043.     strcpy (cp, dir);
  2044.  
  2045.     if (cp[strlen (cp) - 1] != '/')
  2046.     strcat (cp, "/");
  2047.  
  2048. /* Append the file name */
  2049.  
  2050.     return strcat (cp, name);
  2051. }
  2052.  
  2053. /* Check alloc functions */
  2054. #ifdef CHECK_MALLOC
  2055. static void    sh_free (ap)
  2056. void        *ap;
  2057. {
  2058.     s_region    *cp = (s_region *)ap;
  2059.     size_t    len = cp->len;
  2060.  
  2061.     if ((cp->magic1 != MAGIC1) ||
  2062.     (*((int *)((char *)ap + len + sizeof (s_region))) != MAGIC2))
  2063.     {
  2064.     print_warn ("ABORT: corrupt malloc\n");
  2065.     exit (1);
  2066.     }
  2067.  
  2068.     cp->magic1 = 0;
  2069.     *((int *)((char *)ap + len)) = 0;
  2070.  
  2071.     free (cp);
  2072. }
  2073.  
  2074. char        *strcat (str1, str2)
  2075. char        *str1;
  2076. const char    *str2;
  2077. {
  2078.     char    *rtn = str1;
  2079.     int        len;
  2080.  
  2081.     len = strlen (str2);
  2082.     sh_chkmem ("strcat", (char *)str2, len, FALSE);
  2083.  
  2084.     if (str2 == (const char *)NULL)
  2085.     return str1;
  2086.  
  2087.     len += strlen (str1) + 1;
  2088.     sh_chkmem ("strcat", str1, len, TRUE);
  2089.  
  2090.     while (*str1)
  2091.     str1++;
  2092.  
  2093.     while ((*str1 = *str2) != 0)
  2094.     {
  2095.     str1++;
  2096.     str2++;
  2097.     }
  2098.  
  2099.     return rtn;
  2100. }
  2101.  
  2102. char        *strcpy (str1, str2)
  2103. char        *str1;
  2104. const char    *str2;
  2105. {
  2106.     char    *rtn = str1;
  2107.     int        len = strlen (str2) + 1;
  2108.  
  2109.     sh_chkmem ("strcpy", str1, len, TRUE);
  2110.  
  2111.     if (str2 == (const char *)NULL)
  2112.     {
  2113.     *str1 = 0;
  2114.     return str1;
  2115.     }
  2116.  
  2117.     sh_chkmem ("strcpy", (char *)str2, len, FALSE);
  2118.  
  2119.     while ((*str1++ = *str2++) != '\0');
  2120.  
  2121.     return rtn;
  2122. }
  2123.  
  2124. char        *strncpy (str1, str2, len1)
  2125. char        *str1;
  2126. const char    *str2;
  2127. size_t        len1;
  2128. {
  2129.     int        i;
  2130.     char    *rtn = str1;
  2131.     int        len = (int)len1;
  2132.  
  2133.     sh_chkmem ("strncpy", str1, len, TRUE);
  2134.  
  2135.     if (str2 == (const char *)NULL)
  2136.     {
  2137.     *str1 = 0;
  2138.     return str1;
  2139.     }
  2140.  
  2141.     i = strlen (str2);
  2142.  
  2143.     if (i > len)
  2144.     i = len;
  2145.  
  2146.     sh_chkmem ("strncpy", (char *)str2, i, FALSE);
  2147.  
  2148.     while (--len >= 0)
  2149.     {
  2150.     if ((*(str1++) = *(str2++)) == 0)
  2151.     {
  2152.         while (--len >= 0)
  2153.         *(str1++) = 0;
  2154.  
  2155.         break;
  2156.     }
  2157.     }
  2158.  
  2159.     return rtn;
  2160. }
  2161.  
  2162. void        *memcpy (rtn, ptr2a, len)
  2163. void        *rtn;
  2164. const void    *ptr2a;
  2165. size_t        len;
  2166. {
  2167.     char    *ptr1 = rtn;
  2168.     char    *ptr2 = (char *)ptr2a;
  2169.  
  2170.     sh_chkmem ("memcpy", ptr1, len, TRUE);
  2171.     sh_chkmem ("memcpy", ptr2, len, FALSE);
  2172.  
  2173.     if (len > 1200)
  2174.     print_warn ("Warning: string length excessive\n");
  2175.  
  2176.     while (--len >= 0)
  2177.     *(ptr1++) = *(ptr2++);
  2178.  
  2179.     return rtn;
  2180. }
  2181.  
  2182. void    *memset (rtn, ch, len)
  2183. void    *rtn;
  2184. int    ch;
  2185. size_t    len;
  2186. {
  2187.     char    *ptr1 = rtn;
  2188.  
  2189.     sh_chkmem ("memset", ptr1, len, TRUE);
  2190.  
  2191.     if (len > 1200)
  2192.     print_warn ("Warning: string length excessive\n");
  2193.  
  2194.     while (--len >= 0)
  2195.     *(ptr1++) = (char)ch;
  2196.  
  2197.     return rtn;
  2198. }
  2199.  
  2200. static void    sh_chkmem (f, s, l, wflag)
  2201. char        *f;
  2202. char        *s;
  2203. size_t        l;
  2204. bool        wflag;
  2205. {
  2206.     register s_region    *cp = areastart;
  2207.     s_region        *lp = (s_region *)NULL;
  2208.     s_region        *sp = (s_region *)(s - sizeof (s_region));
  2209.     unsigned long    cadd;
  2210.     unsigned long    cadd1 = (unsigned long)etext << 4L;
  2211.  
  2212.     cadd = ((FP_SEG (s)) << 4L) + FP_OFF (s);
  2213.  
  2214.     if ((cadd == 0L) || (wflag && (s == null)))
  2215.     {
  2216.     print_warn ("%s: access to NULL segment\n", f);
  2217.  
  2218.     if (wflag)
  2219.         exit (1);
  2220.     }
  2221.  
  2222.     if (cadd < cadd1)
  2223.     print_warn ("warning - %s: access to text segment\n", f);
  2224.  
  2225.     if (l < 0)
  2226.     {
  2227.     print_warn ("%s: bad length %d\n", f, l);
  2228.     exit (1);
  2229.     }
  2230.  
  2231.     if (s != (char *)NULL)
  2232.     {
  2233.     while (cp != (s_region *)NULL)
  2234.     {
  2235.         if (cp != sp)
  2236.         {
  2237.         lp = cp;
  2238.         cp = cp->next;
  2239.         continue;
  2240.         }
  2241.  
  2242. /* Found it, check the length and other things */
  2243.  
  2244.         if ((cp->magic1 != MAGIC1) ||
  2245.         (*((int *)(s + cp->len)) != MAGIC2))
  2246.         {
  2247.         print_warn ("%s: access to deleted block\n", f);
  2248.         exit (1);
  2249.         }
  2250.  
  2251.         if (l > cp->len)
  2252.         {
  2253.         print_warn ("%s: bad length %d\n", f, l);
  2254.         exit (1);
  2255.         }
  2256.  
  2257.         return;
  2258.     }
  2259.     }
  2260. }
  2261. #endif
  2262.