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