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