home *** CD-ROM | disk | FTP | other *** search
/ ProfitPress Mega CDROM2 …eeware (MSDOS)(1992)(Eng) / ProfitPress-MegaCDROM2.B6I / MISC / GNU / SH164AS.ZIP / SHELL / SH3.C < prev    next >
Encoding:
C/C++ Source or Header  |  1992-02-28  |  39.9 KB  |  1,957 lines

  1. /* MS-DOS SHELL - Parse Tree Executor
  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: C:/SRC/SHELL/RCS/sh3.c 1.27 90/09/19 15:30:31 Ian_Stewartson Exp $
  17.  *
  18.  *    $Log:    sh3.c $
  19.  * Revision 1.27  90/09/19  15:30:31  Ian_Stewartson
  20.  * Allow builtin commands to selected/de-selected
  21.  * 
  22.  * Revision 1.26  90/09/11  20:06:11  Ian_Stewartson
  23.  * Add support for buitlin command, including the alway builtin functions
  24.  * Change search order to match POSIX
  25.  * 
  26.  * Revision 1.25  90/08/24  21:55:00  Ian_Stewartson
  27.  * Change processing order (function, external, internal) to conform to
  28.  * POSIX.  Update to gmatch for macro command changes
  29.  * 
  30.  * Revision 1.24  90/08/16  10:28:47  Ian_Stewartson
  31.  * Find setting of switch character for DOS4 for batch files
  32.  * 
  33.  * Revision 1.23  90/08/14  23:34:09  MS_user
  34.  * Fix memory bugs - Copy function tree before execution
  35.  * Save Extended line file name correctly
  36.  * 
  37.  * Revision 1.22  90/06/21  11:10:47  MS_user
  38.  * Ensure Areanum is set correctly for memory areas
  39.  * 
  40.  * Revision 1.21  90/06/08  14:53:58  MS_user
  41.  * Finally, we've fixed Gen_Full_Path_Name
  42.  * 
  43.  * Revision 1.20  90/05/31  17:44:11  MS_user
  44.  * Ensure Swap file is saved at level 0
  45.  * 
  46.  * Revision 1.19  90/05/31  09:49:12  MS_user
  47.  * Implement partial write when swapping to disk
  48.  * Add some signal lockouts to prevent corruption
  49.  * Fix a bug in Gen_Full_Path for c:program
  50.  * 
  51.  * Revision 1.18  90/05/15  21:09:39  MS_user
  52.  * Change Restore_Dir parameter to take directory name
  53.  * 
  54.  * Revision 1.17  90/05/11  18:47:40  MS_user
  55.  * Get switchchar for command.com
  56.  * 
  57.  * Revision 1.16  90/05/09  18:03:08  MS_user
  58.  * Fix bug in Gen_Full_Path with programs in root directory
  59.  * 
  60.  * Revision 1.15  90/04/30  19:50:11  MS_user
  61.  * Stop search path if second character of name is colon
  62.  * 
  63.  * Revision 1.14  90/04/26  17:27:52  MS_user
  64.  * Fix problem with Picnix Utilities - full path name of executable required
  65.  * 
  66.  * Revision 1.13  90/04/25  22:34:39  MS_user
  67.  * Fix case in TELIF where then and else parts are not defined
  68.  * Fix rsh check for execution path declared
  69.  * 
  70.  * Revision 1.12  90/04/11  19:49:35  MS_user
  71.  * Another problem with new command line processing
  72.  * 
  73.  * Revision 1.11  90/04/11  12:55:49  MS_user
  74.  * Change command line to look exactly like COMMAND.COM
  75.  * 
  76.  * Revision 1.10  90/03/27  20:33:10  MS_user
  77.  * Clear extended file name on interrupt
  78.  * 
  79.  * Revision 1.9  90/03/27  20:09:21  MS_user
  80.  * Clear_Extended_File required in SH1 for interrupt clean up
  81.  * 
  82.  * Revision 1.8  90/03/26  20:57:14  MS_user
  83.  * Change I/O restore so that "exec >filename" works
  84.  * 
  85.  * Revision 1.7  90/03/22  13:47:24  MS_user
  86.  * MSDOS does not handle /dev/ files after find_first correctly
  87.  * 
  88.  * Revision 1.6  90/03/14  16:44:49  MS_user
  89.  * Add quoting of arguments with white space on command line
  90.  * Add IOTHERE processing to iosetup
  91.  * 
  92.  * Revision 1.5  90/03/06  15:10:28  MS_user
  93.  * Get doeval, dodot and runtrap working correctly in run so that a sub-shell
  94.  * is not created and environment variables can be changed.
  95.  * 
  96.  * Revision 1.4  90/03/05  13:50:04  MS_user
  97.  * Add XMS driver
  98.  * Fix bug with extended line file not being deleted
  99.  * Get run to support eval and dot functionality correctly
  100.  * Get trap processing to work
  101.  * Add COMMAND.COM support for .bat files
  102.  * 
  103.  * Revision 1.3  90/02/22  16:38:20  MS_user
  104.  * Add XMS support
  105.  * 
  106.  * Revision 1.2  90/02/14  04:47:06  MS_user
  107.  * Clean up Interrupt 23 and 0 processing, change EMS version error message
  108.  * 
  109.  * Revision 1.1  90/01/25  13:41:24  MS_user
  110.  * Initial revision
  111.  * 
  112.  */
  113.  
  114. #include <sys/types.h>
  115. #include <sys/stat.h>
  116. #include <stdio.h>
  117. #include <process.h>
  118. #include <dos.h>
  119. #include <signal.h>
  120. #include <errno.h>
  121. #include <setjmp.h>
  122. #include <ctype.h>
  123. #include <string.h>
  124. #include <unistd.h>
  125. #include <stdlib.h>
  126. #include <fcntl.h>
  127. #include <limits.h>
  128. #include <dirent.h>
  129. #include <ctype.h>
  130.  
  131. #include "sh.h"
  132.  
  133. /* static Function and string declarations */
  134.  
  135. static int    forkexec (C_Op *, int, int, int, char **);
  136. static bool    iosetup (IO_Actions *, int, int);
  137. static C_Op    **find1case (C_Op *, char *);
  138. static C_Op    *findcase (C_Op *, char *);
  139. static void    echo (char **);
  140. static int    rexecve (char *, char **, char **, bool);
  141. static int    Execute_program (char *, char **, char **, bool);
  142. static int    S_spawnve (char *, char **, char **);
  143. static bool    Get_EMS_Driver (void);
  144. static bool    Get_XMS_Driver (void);
  145. static bool    EMS_error (char *, int);
  146. static int    EMS_Close (void);
  147. static bool    XMS_error (char *, int);
  148. static int    XMS_Close (void);
  149. static int    build_command_line (char *, char **, char **);
  150. static int    setstatus (int);
  151. static bool    Check_for_bat_file (char *);
  152. static size_t    white_space_len (char *, bool *);
  153. static char    *Gen_Full_Path_Name (char *);
  154.  
  155. static char    *AE2big = "arg/env list too big";
  156. static char    *EMS_emsg = "Warning: EMS Error (%x)\n";
  157. static char    *XMS_emsg = "Warning: XMS Error (%x)\n";
  158. static char    *EF_msg = "%s: %s\n";
  159.             /* Extended Command line processing file name    */
  160. static char        *Extend_file = (char *)NULL;
  161. static char        *Swap_File = (char *)NULL;    /* Swap file    */
  162.  
  163. /*
  164.  * execute tree recursively
  165.  */
  166.  
  167. int        execute (t, pin, pout, act)
  168. register C_Op    *t;
  169. int        pin;
  170. int        pout;
  171. int        act;
  172. {
  173.     register C_Op    *t1;
  174.     int            i, localpipe;
  175.     char        *cp, **wp;
  176.     char        **Local_Tword;
  177.     Var_List        *vp;
  178.     Break_C        bc;
  179.     Break_C        *S_RList;        /* Save link pointers    */
  180.     Break_C        *S_BList;
  181.     Break_C        *S_SList;
  182.     int            Local_depth;        /* Save local values    */
  183.     int            Local_areanum;
  184.     int            rv = 0;
  185.  
  186. /* End of tree ? */
  187.  
  188.     if (t == (C_Op *)NULL)
  189.     return 0;
  190.  
  191. /* Save original and Increment execute function recursive level */
  192.  
  193.     Local_depth = Execute_stack_depth++;
  194.  
  195. /* Save original and increment area number */
  196.  
  197.     Local_areanum = areanum++;
  198.  
  199. /* Save the exit points from SubShells, functions and for/whiles */
  200.  
  201.     S_RList = Return_List;
  202.     S_BList = Break_List;
  203.     S_SList = SShell_List;
  204.  
  205. /* Expand any arguments */
  206.  
  207.     wp = (Local_Tword = t->words) != (char **)NULL
  208.      ? eval (Local_Tword, (t->type == TCOM) ? DOALL : DOALL & ~DOKEY)
  209.      : (char **)NULL;
  210.  
  211. /* Switch on tree node type */
  212.  
  213.     switch (t->type)
  214.     {
  215.     case TFUNC:            /* name () { list; }    */
  216.         Save_Function (t, FALSE);
  217.         break;
  218.  
  219. /* In the case of a () command string, we need to save and restore the
  220.  * current environment, directory and traps (can't think of anything else).
  221.  * For any other, we just restore the current directory.  Also, we don't
  222.  * want changes in the Variable list header saved for SubShells, because
  223.  * we are effectively back at execute depth zero.
  224.  */
  225.     case TPAREN:            /* ()            */
  226.         if ((rv = Create_NG_VL ()) == -1)
  227.         break;
  228.  
  229.         if (setjmp (bc.brkpt) == 0)
  230.         {
  231.         Return_List = (Break_C *)NULL;
  232.         Break_List  = (Break_C *)NULL;
  233.         bc.nextlev  = SShell_List;
  234.         SShell_List = &bc;
  235.         rv = execute (t->left, pin, pout, act);
  236.         }
  237.  
  238. /* Restore the original environment */
  239.  
  240.         Return_List    = S_RList;
  241.         Break_List    = S_BList;
  242.         SShell_List    = S_SList;
  243.         Restore_Environment (rv, Local_depth);
  244.         break;
  245.  
  246. /* After a normal command, we need to restore the original directory.  Note
  247.  * that a cd will have updated the variable $~, so no problem
  248.  */
  249.  
  250.     case TCOM:            /* A command process    */
  251.         rv = forkexec (t, pin, pout, act, wp);
  252.         Restore_Dir (C_dir->value);
  253.         break;
  254.  
  255.     case TPIPE:            /* Pipe processing        */
  256.         if ((rv = openpipe ()) < 0)
  257.         break;
  258.  
  259. /* Create pipe, execute command, reset pipe, execute the other side, close
  260.  * the pipe and fini
  261.  */
  262.  
  263.         localpipe = remap (rv);
  264.         execute (t->left, pin, localpipe, 0);
  265.         lseek (localpipe, 0L, SEEK_SET);
  266.         rv = execute (t->right, localpipe, pout, 0);
  267.         closepipe (localpipe);
  268.         break;
  269.  
  270.     case TLIST:            /* Entries in a for statement    */
  271.         execute (t->left, pin, pout, 0);
  272.         rv = execute (t->right, pin, pout, 0);
  273.         break;
  274.  
  275.     case TASYNC:            /* Async - not supported    */
  276.         rv = -1;
  277.         S_puts ("sh: Async commands not supported\n");
  278.         setstatus (rv);
  279.         break;
  280.  
  281.     case TOR:            /* || and &&            */
  282.     case TAND:
  283.         rv = execute (t->left, pin, pout, 0);
  284.  
  285.         if (((t1 = t->right) != (C_Op *)NULL) &&
  286.         ((rv == 0) == (t->type == TAND)))
  287.         rv = execute (t1, pin, pout, 0);
  288.  
  289.         break;
  290.  
  291.     case TFOR:            /* First part of a for statement*/
  292.  
  293. /* for x do...done - use the parameter values.  Need to know how many as
  294.  * it is not a NULL terminated array
  295.  */
  296.  
  297.         if (wp == (char **)NULL)
  298.         {
  299.         wp = dolv + 1;
  300.  
  301.         if ((i = dolc) < 0)
  302.             i = 0;
  303.         }
  304.  
  305. /* for x in y do...done - find the start of the variables and use them all */
  306.  
  307.         else
  308.         {
  309.         i = -1;
  310.         while (*wp++ != (char *)NULL)
  311.             ;
  312.         }
  313.  
  314. /* Create the loop variable. */
  315.  
  316.         vp = lookup (t->str, TRUE);
  317.  
  318. /* Set up a long jump return point before executing the for function so that
  319.  * the continue statement is executed, ie we reprocessor the for condition.
  320.  */
  321.  
  322.         while (rv = setjmp (bc.brkpt))
  323.         {
  324.  
  325. /* Restore the current stack level and clear out any I/O */
  326.  
  327.         Restore_Environment (0, Local_depth + 1);
  328.         Return_List = S_RList;
  329.         SShell_List = S_SList;
  330.  
  331. /* If this is a break - clear the variable and terminate the while loop and
  332.  * switch statement
  333.  */
  334.  
  335.         if (rv == BC_BREAK)
  336.             break;
  337.         }
  338.  
  339.         if (rv == BC_BREAK)
  340.         break;
  341.  
  342. /* Process the next entry - Add to the break/continue chain */
  343.  
  344.         bc.nextlev = Break_List;
  345.         Break_List = &bc;
  346.  
  347. /* Execute the command tree */
  348.  
  349.         for (t1 = t->left; i-- && *wp != NULL;)
  350.         {
  351.         setval (vp, *wp++);
  352.         rv = execute (t1, pin, pout, 0);
  353.         }
  354.  
  355. /* Remove this tree from the break list */
  356.  
  357.         Break_List = S_BList;
  358.         break;
  359.  
  360. /* While and Until function.  Similar to the For function.  Set up a
  361.  * long jump return point before executing the while function so that
  362.  * the continue statement is executed OK.
  363.  */
  364.  
  365.     case TWHILE:            /* WHILE and UNTIL functions    */
  366.     case TUNTIL:
  367.         while (rv = setjmp (bc.brkpt))
  368.         {
  369.  
  370. /* Restore the current stack level and clear out any I/O */
  371.  
  372.         Restore_Environment (0, Local_depth + 1);
  373.         Return_List = S_RList;
  374.         SShell_List = S_SList;
  375.  
  376. /* If this is a break, terminate the while and switch statements */
  377.  
  378.         if (rv == BC_BREAK)
  379.             break;
  380.         }
  381.  
  382.         if (rv == BC_BREAK)
  383.         break;
  384.  
  385. /* Set up links */
  386.  
  387.         bc.nextlev = Break_List;
  388.         Break_List = &bc;
  389.         t1 = t->left;
  390.  
  391.         while ((execute(t1, pin, pout, 0) == 0) == (t->type == TWHILE))
  392.         rv = execute (t->right, pin, pout, 0);
  393.  
  394.         Break_List = S_BList;
  395.         break;
  396.  
  397.     case TIF:            /* IF and ELSE IF functions    */
  398.     case TELIF:
  399.         if (t->right != (C_Op *)NULL)
  400.         rv = !execute (t->left, pin, pout, 0)
  401.                 ? execute (t->right->left, pin, pout, 0)
  402.              : execute (t->right->right, pin, pout, 0);
  403.         break;
  404.  
  405.     case TCASE:            /* CASE function        */
  406.         if ((cp = evalstr (t->str, DOSUB | DOTRIM)) == (char *)NULL)
  407.         cp = null;
  408.  
  409.         if ((t1 = findcase (t->left, cp)) != (C_Op *)NULL)
  410.         rv = execute (t1, pin, pout, 0);
  411.  
  412.         break;
  413.  
  414.     case TBRACE:            /* {} statement            */
  415.         if ((rv >= 0) && ((t1 = t->left) != (C_Op *)NULL))
  416.         rv = execute (t1, pin, pout, 0);
  417.  
  418.         break;
  419.     }
  420.  
  421. /* Processing Completed - Restore environment */
  422.  
  423.     t->words        = Local_Tword;
  424.     Execute_stack_depth = Local_depth;
  425.  
  426. /* Remove unwanted malloced space */
  427.  
  428.     freehere (areanum);
  429.     freearea (areanum);
  430.  
  431.     areanum        = Local_areanum;
  432.  
  433. /* Check for traps */
  434.  
  435.     if ((i = trapset) != 0)
  436.     {
  437.     trapset = 0;
  438.     runtrap (i);
  439.     }
  440.  
  441. /* Check for interrupts */
  442.  
  443.     if (Interactive () && SW_intr)
  444.     {
  445.     closeall ();
  446.     fail ();
  447.     }
  448.  
  449.     return rv;
  450. }
  451.  
  452. /*
  453.  * Restore the original directory
  454.  */
  455.  
  456. void    Restore_Dir (path)
  457. char    *path;
  458. {
  459.     unsigned int    dummy;
  460.  
  461.     _dos_setdrive (tolower(*path) - 'a' + 1, &dummy);
  462.  
  463.     if (chdir (&path[2]) != 0)
  464.     {
  465.     S_puts ("Warning: current directory reset to /\n");
  466.     chdir ("/");
  467.     Getcwd ();
  468.     }
  469. }
  470.  
  471. /*
  472.  * Ok - execute the program, resetting any I/O required
  473.  */
  474.  
  475. static int    forkexec (t, pin, pout, act, wp)
  476. register C_Op    *t;
  477. int        pin;
  478. int        pout;
  479. int        act;
  480. char        **wp;
  481. {
  482.     int        rv = -1;
  483.     int        (*shcom)(C_Op *) = (int (*)())NULL;
  484.     char    *cp;
  485.     IO_Actions    **iopp;
  486.     int        resetsig = 0;
  487.     void    (*sig_int)();
  488.     char    **owp = wp;
  489.     bool    spawn = FALSE;
  490.     bool    builtin = FALSE;
  491.     Fun_Ops    *fop;
  492.     int        i;
  493.  
  494.     if (t->type == TCOM)
  495.     {
  496.  
  497. /* Malloc failed somewhere - given up */
  498.  
  499.     if (wp == (char **)NULL)
  500.         return setstatus (-1);
  501.  
  502. /* Skip over any assignments */
  503.  
  504.     while ((cp = *wp++) != (char *)NULL)
  505.         ;
  506.  
  507.     cp = *wp;
  508.  
  509. /* strip all initial assignments not correct wrt PATH=yyy command  etc */
  510.  
  511.     if (FL_TEST ('x'))
  512.         echo (cp != (char *)NULL ? wp : owp);
  513.  
  514. /* Is it only an assignement? */
  515.  
  516.     if ((cp == (char *)NULL) && (t->ioact == (IO_Actions **)NULL))
  517.     {
  518.         while (((cp = *owp++) != (char *)NULL) && assign (cp, COPYV))
  519.         ;
  520.  
  521.         return setstatus (0);
  522.     }
  523.  
  524. /* Check for built in commands */
  525.  
  526.     else if (cp != (char *)NULL)
  527.         shcom = inbuilt (cp, &builtin);
  528.     }
  529.  
  530. /* Unix fork simulation? */
  531.  
  532.     t->words = wp;
  533.     if ((act & FEXEC) == 0)
  534.     {
  535.     spawn = TRUE;
  536.  
  537.     if (Interactive ())
  538.     {
  539. #ifdef SIGQUIT
  540.         signal (SIGQUIT, SIG_IGN);
  541. #endif
  542.         sig_int = signal (SIGINT, SIG_IGN);
  543.         resetsig = 1;
  544.     }
  545.     }
  546.  
  547. /* Set any variables */
  548.  
  549.     while (((cp = *owp++) != (char *)NULL) && assign (cp, COPYV))
  550.     {
  551.     if (shcom == (int (*)())NULL) 
  552.         s_vstatus (lookup (cp, TRUE), EXPORT);
  553.     }
  554.  
  555. /* We cannot close the pipe, because once the exec/spawn has taken place
  556.  * the processing of the pipe is not yet complete.
  557.  */
  558.  
  559.     if (pin != NOPIPE)
  560.     {
  561.     S_dup2 (pin, STDIN_FILENO);
  562.     lseek (STDIN_FILENO, 0L, SEEK_SET);
  563.     }
  564.  
  565.     if (pout != NOPIPE)
  566.     {
  567.     S_dup2 (pout, STDOUT_FILENO);
  568.     lseek (STDOUT_FILENO, 0L, SEEK_END);
  569.     }
  570.  
  571. /* Set up any other IO required */
  572.  
  573.     if ((iopp = t->ioact) != (IO_Actions **)NULL)
  574.     {
  575.     while (*iopp != (IO_Actions *)NULL)
  576.     {
  577.         if (iosetup (*iopp++, pin, pout))
  578.         return rv;
  579.     }
  580.     }
  581.  
  582.  
  583. /* All fids above 10 are autoclosed in the exec file because we have used
  584.  * the O_NOINHERIT flag.  Note I patched open.obj to pass this flag to the
  585.  * open function.
  586.  */
  587.  
  588.     if (resetsig)
  589.     {
  590. #ifdef SIGQUIT
  591.     signal (SIGQUIT, SIG_IGN);
  592. #endif
  593.     signal (SIGINT, sig_int);
  594.     }
  595.  
  596.     if (t->type == TPAREN)
  597.     return restore_std (execute (t->left, NOPIPE, NOPIPE, FEXEC), TRUE);
  598.  
  599. /* Are we just changing the I/O re-direction for the shell ? */
  600.  
  601.     if (wp[0] == NULL)
  602.     {
  603.     if (spawn)
  604.         restore_std (0, TRUE);
  605.  
  606.     return 0;
  607.     }
  608.  
  609. /* No - Check for a function the program.  At this point, we need to put
  610.  * in some processing for return.
  611.  */
  612.  
  613.     if (!builtin && (fop = Fun_Search (wp[0])) != (Fun_Ops *)NULL)
  614.     {
  615.     char            **s_dolv = dolv;
  616.     int            s_dolc   = dolc;
  617.     Break_C            *s_RList = Return_List;
  618.     Break_C            *s_BList = Break_List;
  619.     Break_C            *s_SList = SShell_List;
  620.     int            LS_depth = Execute_stack_depth;
  621.     Break_C            bc;
  622.     C_Op            *New;
  623.  
  624. /* Set up $0..$n for the function */
  625.  
  626.     dolv = wp;
  627.     for (dolc = 0; dolv[dolc] != (char *)NULL; ++dolc);
  628.     setval (lookup ("#", TRUE), putn (dolc));
  629.  
  630.     if (setjmp (bc.brkpt) == 0)
  631.     {
  632.         Break_List  = (Break_C *)NULL;
  633.         bc.nextlev  = Return_List;
  634.         Return_List = &bc;
  635.         New = Copy_Function (fop->tree->left);
  636.         rv = execute (New, NOPIPE, NOPIPE, FEXEC);
  637.     }
  638.  
  639. /* A return has been executed - Unlike, while and for, we just need to
  640.  * restore the local execute stack level and the return will restore
  641.  * the correct I/O.
  642.  */
  643.  
  644.     else
  645.         rv = getn (lookup ("?", FALSE)->value);
  646.  
  647. /* Restore the old $0, and previous return address */
  648.  
  649.     Break_List  = s_BList;
  650.     Return_List = s_RList;
  651.     SShell_List = s_SList;
  652.     dolv        = s_dolv;
  653.     dolc        = s_dolc;
  654.     Restore_Environment (rv, LS_depth);
  655.     setval (lookup ("#", TRUE), putn (dolc));
  656.     return rv;
  657.     }
  658.  
  659. /* Check for another drive or directory in the restricted shell */
  660.  
  661.     if (anys (":/\\", wp[0]) && check_rsh (wp[0]))
  662.     return restore_std (-1, TRUE);
  663.  
  664. /* Ok - execute the program */
  665.  
  666.     if (!builtin)
  667.     rv = rexecve (wp[0], wp, makenv (), spawn);
  668.  
  669. /* If we didn't find it, check for internal command */
  670.  
  671.     if (builtin || ((rv == -1) && (errno == ENOENT)))
  672.     {
  673.     if (shcom != (int (*)())NULL) 
  674.         rv =  setstatus ((*shcom)(t));
  675.  
  676.     else
  677.         print_warn (EF_msg, wp[0], "not found");
  678.     }
  679.  
  680.     return restore_std (rv, TRUE);
  681. }
  682.  
  683. /*
  684.  * Restore Local Environment
  685.  */
  686.  
  687. void    Restore_Environment (retval, stack)
  688. int    retval;
  689. int    stack;
  690. {
  691.     Execute_stack_depth = stack;
  692.     Delete_G_VL ();
  693.     Restore_Dir (C_dir->value);
  694.     restore_std (setstatus (retval), TRUE);
  695. }
  696.  
  697. /*
  698.  * Set up I/O redirection.  0< 1> are ignored as required within pipelines.
  699.  */
  700.  
  701. static bool        iosetup (iop, pipein, pipeout)
  702. register IO_Actions    *iop;
  703. int            pipein;
  704. int            pipeout;
  705. {
  706.     register int    u;
  707.     char        *cp, *msg;
  708.  
  709.     if (iop->io_unit == IODEFAULT)    /* take default */
  710.     iop->io_unit = (iop->io_flag & (IOREAD | IOHERE)) ? STDIN_FILENO
  711.                               : STDOUT_FILENO;
  712.  
  713. /* Check for pipes */
  714.  
  715.     if ((pipein != NOPIPE) && (iop->io_unit == STDIN_FILENO))
  716.     return FALSE;
  717.  
  718.     if ((pipeout != NOPIPE) && (iop->io_unit == STDOUT_FILENO))
  719.     return FALSE;
  720.  
  721.     msg = (iop->io_flag & (IOREAD | IOHERE)) ? "open" : "create";
  722.  
  723.     if ((iop->io_flag & IOHERE) == 0)
  724.     {
  725.     if ((cp = evalstr (iop->io_name, DOSUB | DOTRIM)) == (char *)NULL)
  726.         return TRUE;
  727.     }
  728.  
  729.     if (iop->io_flag & IODUP)
  730.     {
  731.     if ((cp[1]) || !isdigit (*cp) && *cp != '-')
  732.     {
  733.         print_error ("%s: illegal >& argument\n", cp);
  734.         return TRUE;
  735.     }
  736.  
  737.     if (*cp == '-')
  738.         iop->io_flag = IOCLOSE;
  739.  
  740.     iop->io_flag &= ~(IOREAD | IOWRITE);
  741.     }
  742.  
  743. /*
  744.  * When writing to /dev/???, we have to cheat because MSDOS appears to
  745.  * have a problem with /dev/ files after find_first/find_next.
  746.  */
  747.  
  748.     if (((iop->io_flag & ~(IOXHERE | IOTHERE)) == IOWRITE) &&
  749.     (strnicmp (cp, "/dev/", 5) == 0))
  750.     iop->io_flag |= IOCAT;
  751.  
  752. /* Open the file in the appropriate mode */
  753.  
  754.     switch (iop->io_flag & ~(IOXHERE | IOTHERE))
  755.     {
  756.     case IOREAD:                /* <            */
  757.         u = S_open (FALSE, cp, O_RDONLY);
  758.         break;
  759.  
  760.     case IOHERE:                /* <<            */
  761.         u = herein (iop->io_name, iop->io_flag & IOXHERE);
  762.         cp = "here file";
  763.         break;
  764.  
  765.     case IOWRITE | IOREAD:            /* <>            */
  766.         if (check_rsh (cp))
  767.         return TRUE;
  768.  
  769.         u = S_open (FALSE, cp, O_RDWR);
  770.         break;
  771.  
  772.     case IOWRITE | IOCAT:            /* >>            */
  773.         if (check_rsh (cp))
  774.         return TRUE;
  775.  
  776.         if ((u = S_open (FALSE, cp, O_WRONLY | O_TEXT)) >= 0)
  777.         {
  778.         lseek (u, 0L, SEEK_END);
  779.         break;
  780.         }
  781.  
  782.     case IOWRITE:                /* >            */
  783.         if (check_rsh (cp))
  784.         return TRUE;
  785.  
  786.         u = S_open (FALSE, cp, O_CMASK, 0666);
  787.         break;
  788.  
  789.     case IODUP:                /* >&            */
  790.         if (check_rsh (cp))
  791.         return TRUE;
  792.  
  793.         u = S_dup2 (*cp - '0', iop->io_unit);
  794.         break;
  795.  
  796.     case IOCLOSE:                /* >-            */
  797.         if ((iop->io_unit >= STDIN_FILENO) &&
  798.         (iop->io_unit <= STDERR_FILENO))
  799.         S_dup2 (-1, iop->io_unit);
  800.  
  801.         S_close (iop->io_unit, TRUE);
  802.         return FALSE;
  803.     }
  804.  
  805.     if (u < 0)
  806.     {
  807.     print_warn ("%s: cannot %s\n", cp, msg);
  808.     return TRUE;
  809.     }
  810.  
  811.     else if (u != iop->io_unit)
  812.     {
  813.     S_dup2 (u, iop->io_unit);
  814.     S_close (u, TRUE);
  815.     }
  816.  
  817.     return FALSE;
  818. }
  819.  
  820. /*
  821.  * -x flag - echo command to be executed
  822.  */
  823.  
  824. static void    echo (wp)
  825. register char    **wp;
  826. {
  827.     register int    i;
  828.  
  829.     S_putc ('+');
  830.  
  831.     for (i = 0; wp[i] != (char *)NULL; i++)
  832.     {
  833.     S_putc (SP);
  834.     S_puts (wp[i]);
  835.     }
  836.  
  837.     S_putc (NL);
  838. }
  839.  
  840. static C_Op    **find1case (t, w)
  841. C_Op        *t;
  842. char        *w;
  843. {
  844.     register C_Op    *t1;
  845.     C_Op        **tp;
  846.     register char    **wp, *cp;
  847.  
  848.     if (t == (C_Op *)NULL)
  849.     return (C_Op **)NULL;
  850.  
  851.     if (t->type == TLIST)
  852.     {
  853.     if ((tp = find1case (t->left, w)) != (C_Op *)NULL)
  854.         return tp;
  855.  
  856.     t1 = t->right;    /* TPAT */
  857.     }
  858.  
  859.     else
  860.     t1 = t;
  861.  
  862.     for (wp = t1->words; *wp != (char *)NULL;)
  863.     {
  864.     if ((cp = evalstr (*(wp++), DOSUB)) &&
  865.         gmatch (w, cp, FALSE, (char **)NULL, GM_ALL))
  866.         return &t1->left;
  867.     }
  868.  
  869.     return (C_Op **)NULL;
  870. }
  871.  
  872. static C_Op    *findcase (t, w)
  873. C_Op        *t;
  874. char        *w;
  875. {
  876.     register C_Op **tp;
  877.  
  878.     return ((tp = find1case (t, w)) != (C_Op **)NULL) ? *tp : (C_Op *)NULL;
  879. }
  880.  
  881. /*
  882.  * Set up the status on exit from a command
  883.  */
  884.  
  885. static int    setstatus (s)
  886. register int    s;
  887. {
  888.     exstat = s;
  889.     setval (lookup ("?", TRUE), putn (s));
  890.     return s;
  891. }
  892.  
  893. /*
  894.  * PATH-searching interface to execve.
  895.  */
  896.  
  897. static int    rexecve (c, v, envp, d_flag)
  898. char        *c;
  899. char        **v;
  900. char        **envp;
  901. bool        d_flag;
  902. {
  903.     register char    *sp;
  904.     int            res;            /* Result        */
  905.     char        *em;            /* Exit error message    */
  906.     bool        eloop;            /* Re-try flag        */
  907.     char        **new_argv;
  908.     int            batfile;        /* .bat file flag    */
  909.     int            argc = 0;        /* Original # of argcs    */
  910.     char        *params;        /* Script parameters    */
  911.     int            nargc = 0;        /* # script args    */
  912.     char        *p_name;        /* Program name        */
  913.     int            i;
  914.     union REGS        r;
  915.  
  916. /* If the environment is null - It is too big - error */
  917.  
  918.     if (envp == (char **)NULL)
  919.     em = AE2big;
  920.  
  921.     else if ((p_name = getcell (FFNAME_MAX)) == (char *)NULL)
  922.     em = strerror (ENOMEM);
  923.     
  924.     else
  925.     {
  926.  
  927. /* Count the number of arguments to the program in case of shell script or
  928.  * bat file
  929.  */
  930.  
  931.     while (v[argc++] != (char *)NULL);
  932.  
  933.     ++argc;                /* Including the null        */
  934.  
  935. /* Start off on the search path for the executable file */
  936.  
  937.     sp = (any ('/', c) || (*(c + 1) == ':')) ? null : path->value;
  938.  
  939.     do
  940.     {
  941.         sp = path_append (sp, c, p_name);
  942.  
  943.         if ((res = Execute_program (p_name, v, envp, d_flag)) != -1)
  944.         return res;
  945.  
  946.         eloop = TRUE;
  947.  
  948.         switch (errno)
  949.         {
  950.  
  951. /* No entry for the file - if the file exists, execute it as a shell
  952.  * script.  If we never find the file, return the error
  953.  */
  954.         case ENOENT:
  955.             if ((res = O_for_execute (p_name, ¶ms, &nargc)) >= 0)
  956.             {
  957.             batfile = 1;
  958.             S_close (res, TRUE);
  959.             }
  960.  
  961.             else if (!Check_for_bat_file (p_name))
  962.             {
  963.             errno = ENOENT;
  964.             eloop = FALSE;
  965.             break;
  966.             }
  967.  
  968.             else
  969.             {
  970.             Convert_Slashes (p_name);
  971.             batfile = 0;
  972.             nargc = 0;
  973.             }
  974.  
  975. /* Ok - either a shell script or a bat file (batfile = 0) */
  976.  
  977.             nargc = (nargc < 2) ? 0 : nargc - 1;
  978.             if ((new_argv = (char **)getcell (sizeof (char *) *
  979.                                 (argc + nargc + 2)))
  980.                   == (char **)NULL)
  981.             {
  982.             em = strerror (ENOMEM);
  983.             errno = ENOMEM;
  984.             break;
  985.             }
  986.  
  987.             memcpy (&new_argv[2 + nargc], &v[0], sizeof(char *) * argc);
  988.  
  989. /* If BAT file, use command.com else use sh */
  990.  
  991.             if (!batfile)
  992.             {
  993.             new_argv[0] = lookup ("COMSPEC", FALSE)->value;
  994.             new_argv[1] = "/c";
  995.  
  996. /* Get the switch character */
  997.  
  998.             r.x.ax = 0x3700;
  999.             intdos (&r, &r);
  1000.  
  1001.             if ((r.h.al == 0) && (_osmajor < 4))
  1002.                 *new_argv[1] = (char)(r.h.dl);
  1003.             }
  1004.  
  1005. /* Stick in the pre-fix arguments */
  1006.  
  1007.             else if (nargc)
  1008.             {
  1009.             i = 1;
  1010.             em = params;
  1011.  
  1012.             while (*em)
  1013.             {
  1014.                 while (isspace (*em))
  1015.                 *(em++) = 0;
  1016.                 
  1017.                 if (*em)
  1018.                 new_argv[i++] = em;
  1019.  
  1020.                 while (!isspace (*em) && *em)
  1021.                 ++em;
  1022.             }
  1023.             }
  1024.  
  1025.             else if (params != null)
  1026.             new_argv[1] = params;
  1027.  
  1028.             else
  1029.             new_argv[1] = lookup (shell, FALSE)->value;
  1030.  
  1031.             new_argv[2 + nargc] = p_name;
  1032.  
  1033.             res = rexecve (new_argv[batfile], &new_argv[batfile],
  1034.                     envp, d_flag);
  1035. /* Release allocated space */
  1036.  
  1037.             DELETE (new_argv);
  1038.  
  1039.             if (params != null)
  1040.             DELETE (params);
  1041.  
  1042.             if (res != -1)
  1043.             return res;
  1044. /* No - shell */
  1045.  
  1046.             em = "no Shell";
  1047.             eloop = FALSE;
  1048.             errno = ENOEXEC;
  1049.             break;
  1050.  
  1051.         case ENOEXEC:
  1052.             em = "program corrupt";
  1053.             break;
  1054.  
  1055.         case ENOMEM:
  1056.             em = "program too big";
  1057.             break;
  1058.  
  1059.         case E2BIG:
  1060.             em = AE2big;
  1061.             break;
  1062.  
  1063.         default:
  1064.             em = "cannot execute";
  1065.             eloop = FALSE;
  1066.             break;
  1067.         }
  1068.     } while ((sp != (char *)NULL) && !eloop);
  1069.     }
  1070.  
  1071. /* If we found the program - report the error */
  1072.  
  1073.     if (errno != ENOENT)
  1074.     {
  1075.     print_warn (EF_msg, c, em);
  1076.  
  1077.     if (!d_flag)
  1078.         exit (-1);
  1079.     }
  1080.  
  1081.     return -1;
  1082. }
  1083.  
  1084. /* Check to see if this is a bat file */
  1085.  
  1086. static bool    Check_for_bat_file (name)
  1087. char        *name;
  1088. {
  1089.     char    *local_path;
  1090.     char    *cp;
  1091.     bool    res;
  1092.  
  1093.     if ((local_path = getcell (strlen (name) + 5)) == (char *)NULL)
  1094.     return FALSE;
  1095.  
  1096. /* Work on a copy of the path */
  1097.  
  1098.     if ((cp = strrchr (strcpy (local_path, name), '/')) == (char *)NULL)
  1099.     cp = local_path;
  1100.     
  1101.     else
  1102.     ++cp;
  1103.  
  1104.     if ((cp = strrchr (cp, '.')) == (char *)NULL)
  1105.     strcat (local_path, ".bat");
  1106.     
  1107.     else if (stricmp (cp, ".bat") != 0)
  1108.     {
  1109.     DELETE (local_path);
  1110.     return FALSE;
  1111.     }
  1112.     
  1113.     res = (access (local_path, X_OK) == 0) ? TRUE : FALSE;
  1114.     DELETE (local_path);
  1115.     return res;
  1116. }
  1117.  
  1118. /*
  1119.  * Run the command produced by generator `f' applied to stream `arg'.
  1120.  */
  1121.  
  1122. int        run (argp, f, f_loop)
  1123. IO_Args        *argp;
  1124. int        (*f)(IO_State *);
  1125. bool        f_loop;
  1126. {
  1127.     Word_B        *swdlist = wdlist;
  1128.     Word_B        *siolist = iolist;
  1129.     jmp_buf        ev, rt;
  1130.     int            *ofail = failpt;
  1131.     int            rv = -1;
  1132.     Break_C        *S_RList = Return_List;    /* Save loval links    */
  1133.     Break_C        *S_BList = Break_List;
  1134.     int            LS_depth = Execute_stack_depth;
  1135.     int            sjr;
  1136.     C_Op        *outtree;
  1137.     int            s_execflg = execflg;
  1138.  
  1139. /* Create a new save area */
  1140.  
  1141.     areanum++;
  1142.  
  1143. /* Execute the command */
  1144.  
  1145.     if (newenv (setjmp (errpt = ev)) == FALSE)
  1146.     {
  1147.     Return_List = (Break_C *)NULL;
  1148.     Break_List  = (Break_C *)NULL;
  1149.     wdlist      = (Word_B *)NULL;
  1150.     iolist      = (Word_B *)NULL;
  1151.  
  1152.     pushio (argp, f);
  1153.     e.iobase  = e.iop;
  1154.     e.eof_p   = (bool)!f_loop;    /* Set EOF processing        */
  1155.     SW_intr   = 0;
  1156.     multiline = 0;
  1157.     inparse   = 0;
  1158.     execflg   = (!f_loop) ? 1 : execflg;
  1159.  
  1160. /* Read Input (if f_loop is not set, we are processing a . file command)
  1161.  * either for one line or until end of file.
  1162.  */
  1163.     do
  1164.     {
  1165.         yynerrs = 0;
  1166.  
  1167.         if (((sjr = setjmp (failpt = rt)) == 0) &&
  1168.         ((outtree = yyparse ()) != (C_Op *)NULL))
  1169.         rv = execute (outtree, NOPIPE, NOPIPE, 0);
  1170.  
  1171. /* Fail or no loop - zap any files if necessary */
  1172.  
  1173.         else if (sjr || f_loop)
  1174.         {
  1175.         Clear_Extended_File ();
  1176.         break;
  1177.         }
  1178.  
  1179.     } while (!f_loop);
  1180.  
  1181.     quitenv ();
  1182.     }
  1183.  
  1184. /* Restore the environment */
  1185.  
  1186.     Return_List = S_RList;
  1187.     Break_List = S_BList;
  1188.     execflg = s_execflg;
  1189.     wdlist = swdlist;
  1190.     iolist = siolist;
  1191.     failpt = ofail;
  1192.  
  1193.     Restore_Environment (rv, LS_depth);
  1194.  
  1195.     freearea (areanum--);
  1196.     return rv;
  1197. }
  1198.  
  1199. /* Exec or spawn the program ? */
  1200.  
  1201. static int    Execute_program (path, parms, envp, d_flag)
  1202. char        *path;
  1203. char        **parms;
  1204. char        **envp;
  1205. bool        d_flag;
  1206. {
  1207.     return setstatus ((!d_flag) ? execve (path, parms, envp)
  1208.                 : S_spawnve (path, parms, envp));
  1209. }
  1210.  
  1211. /* Set up to spawn a process */
  1212.  
  1213. static int    S_spawnve (path, parms, envp)
  1214. char        *path;
  1215. char        **parms;
  1216. char        **envp;
  1217. {
  1218.     unsigned int    c_cur = (unsigned int)(_psp - 1);
  1219.     unsigned int    size = 0;
  1220.     char        *ep, *ep1;
  1221.     int            res, serrno;
  1222.     struct MCB_list    *mp = (struct MCB_list *)((unsigned long)c_cur << 16L);
  1223.  
  1224. /* Check to see if the file exists */
  1225.  
  1226.     strcpy (path_line, path);
  1227.  
  1228.     if ((ep = strrchr (path_line, '/')) == (char *)NULL)
  1229.     ep = path_line;
  1230.  
  1231. /* If no dot in name - check for .exe and .com files */
  1232.  
  1233.     if ((ep1 = strchr (ep, '.')) == (char *)NULL)
  1234.     {
  1235.     ep1 = ep + strlen (ep);
  1236.     strcpy (ep1, ".exe");
  1237.  
  1238.     if ((res = access (path_line, F_OK)) != 0)
  1239.     {
  1240.         strcpy (ep1, ".com");
  1241.         res = access (path_line, F_OK);
  1242.     }
  1243.  
  1244.     if (res != 0)
  1245.         return -1;
  1246.     }
  1247.  
  1248.     else if ((stricmp (ep1, ".exe") != 0) && (stricmp (ep1, ".com") != 0))
  1249.     {
  1250.     if (access (path_line, F_OK) == 0)
  1251.         errno = ENOEXEC;
  1252.  
  1253.     return -1;
  1254.     }
  1255.  
  1256.     else if (access (path_line, F_OK) != 0)
  1257.     return -1;
  1258.  
  1259. /* Process the command line.  If no swapping, we have executed the program */
  1260.  
  1261.     res = build_command_line (path_line, parms, envp);
  1262.  
  1263.     if ((Swap_Mode == SWAP_OFF) || res)
  1264.     return res;
  1265.  
  1266. /* Find the length of the swap area */
  1267.  
  1268.     while ((mp = (struct MCB_list *)((unsigned long)c_cur << 16L))->MCB_type
  1269.         == MCB_CON)
  1270.     {
  1271.     if ((mp->MCB_pid != _psp) && (mp->MCB_pid != 0) &&
  1272.         (mp->MCB_type != MCB_END))
  1273.     {
  1274.         Clear_Extended_File ();
  1275.         print_error ("Fatal: Memory chain corrupt\n");
  1276.         return -1;
  1277.     }
  1278.  
  1279.     c_cur += (mp->MCB_len + 1);
  1280.     size += mp->MCB_len + 1;
  1281.     }
  1282.  
  1283. /*
  1284.  * Convert swap size from paragraphs to 16K blocks.
  1285.  */
  1286.  
  1287.     if (size == 0)
  1288.     size = mp->MCB_len + 1;
  1289.  
  1290.     SW_Blocks = (size / 0x0400) + 1;
  1291.     SW_SBlocks = ((size - etext + _psp - 1) / 0x0400) + 1;
  1292.  
  1293. /* OK Now we've set up the FCB's, command line and opened the swap file.
  1294.  * Get some sys info for the swapper and execute my little assembler
  1295.  * function to swap us out
  1296.  */
  1297.  
  1298. /* Save the interrupt 0 and 23 addresses */
  1299.  
  1300.     SW_I0_V  = _dos_getvect (0x00);
  1301.     SW_I23_V = _dos_getvect (0x23);
  1302.  
  1303. /* Ok - 3 methods of swapping */
  1304.  
  1305. /* If expanded memory - try that */
  1306.  
  1307.     if ((Swap_Mode & SWAP_EXPAND) && Get_EMS_Driver ())
  1308.     {
  1309.     int    cr;
  1310.  
  1311.     SW_Mode = 3;            /* Set Expanded memory swap    */
  1312.  
  1313.     res = SA_spawn (envp);
  1314.     cr = EMS_Close ();        /* Close EMS            */
  1315.  
  1316.     if ((res != -2) && cr)        /* Report Close error ?        */
  1317.     {
  1318.         res = -2;
  1319.         errno = cr;
  1320.     }
  1321.  
  1322.     if (res == -2)
  1323.         EMS_error ("Expanded memory swap failed (%x)\n", errno);
  1324.  
  1325.     else
  1326.     {
  1327.         Clear_Extended_File ();
  1328.         return res;
  1329.     }
  1330.  
  1331. /* Failed - disabled */
  1332.  
  1333.     Swap_Mode &= (~SWAP_EXPAND);
  1334.     }
  1335.  
  1336.     if ((Swap_Mode & SWAP_EXTEND) && Get_XMS_Driver ())
  1337.     {
  1338.     int    cr;
  1339.  
  1340. /* Set Extended memory or XMS driver */
  1341.  
  1342.     SW_Mode = (SW_fp == -1) ? 2 : 4;
  1343.  
  1344.     res = SA_spawn (envp);
  1345.     cr = XMS_Close ();        /* Close XMS            */
  1346.  
  1347.     if ((res != -2) && cr)        /* Report Close error ?        */
  1348.     {
  1349.         res = -2;
  1350.         errno = cr;
  1351.     }
  1352.  
  1353.     if (res == -2)
  1354.         XMS_error ("Extended memory swap failed (%x)\n", errno);
  1355.  
  1356.     else
  1357.     {
  1358.         Clear_Extended_File ();
  1359.         return res;
  1360.     }
  1361.  
  1362. /* Failed - disabled */
  1363.  
  1364.     Swap_Mode &= (~SWAP_EXTEND);
  1365.     }
  1366.  
  1367. /* Try the disk if available */
  1368.  
  1369.     if (Swap_Mode & SWAP_DISK)
  1370.     {
  1371.     SW_Pwrite = 0;
  1372.  
  1373.     if (Swap_File == (char *)NULL)
  1374.         SW_fp = S_open (FALSE, (ep = g_tempname ()), O_SMASK, 0600);
  1375.     
  1376.     else
  1377.     {
  1378.         SW_fp = S_open (FALSE, Swap_File, O_SaMASK);
  1379.         SW_Pwrite = 1;
  1380.     }
  1381.  
  1382.     if (SW_fp < 0)
  1383.     {
  1384.         Clear_Swap_File ();
  1385.         Swap_Mode &= (~SWAP_DISK);
  1386.         print_error ("No Swap files\n");
  1387.         errno = ENOSPC;
  1388.         return -1;
  1389.     }
  1390.  
  1391. /* Save the swap file name ? */
  1392.  
  1393.     if ((Swap_File == (char *)NULL) &&
  1394.         ((Swap_File = strsave (ep, 0)) == null))
  1395.         Swap_File = (char *)NULL;
  1396.  
  1397.     SW_Mode = 1;            /* Set Disk file swap        */
  1398.     
  1399. /* Seek to correct location */
  1400.  
  1401.     if (SW_Pwrite)
  1402.     {
  1403.             long    loc = (long)(etext - _psp + 1) * 16L;
  1404.  
  1405.         if (lseek (SW_fp, loc, SEEK_SET) != loc)
  1406.         {
  1407.         serrno = errno;
  1408.         S_close (SW_fp, TRUE);
  1409.         Clear_Swap_File ();
  1410.         Swap_Mode &= (~SWAP_DISK);
  1411.         print_error ("No Swap files\n");
  1412.         errno = serrno;
  1413.         return -1;
  1414.         }
  1415.     }
  1416.  
  1417. /* Execute the program */
  1418.  
  1419.     res = SA_spawn (envp);
  1420.  
  1421. /* Close the swap file and extended command line files */
  1422.  
  1423.     Clear_Extended_File ();
  1424.     serrno = errno;
  1425.     S_close (SW_fp, TRUE);
  1426.     errno = serrno;
  1427.  
  1428. /* Check for out of swap space */
  1429.  
  1430.     if (res == -2)
  1431.     {
  1432.         Clear_Swap_File ();
  1433.         Swap_Mode &= (~SWAP_DISK);
  1434.         print_warn ("Swap file write failed\n");
  1435.         errno = ENOSPC;
  1436.         res = -1;
  1437.     }
  1438.  
  1439. /* Return the result */
  1440.  
  1441.     return res;
  1442.     }
  1443.  
  1444. /* No swapping available - give up */
  1445.  
  1446.     Clear_Extended_File ();
  1447.     print_error ("All Swapping methods failed\n");
  1448.     errno = ENOSPC;
  1449.     return -1;
  1450. }
  1451.  
  1452. /* Get the XMS Driver information */
  1453.  
  1454. static bool    Get_XMS_Driver ()
  1455. {
  1456.     union REGS        or;
  1457.     struct SREGS    sr;
  1458.     unsigned int    SW_EMsize;    /* Number of extend memory blks    */
  1459.  
  1460. /* Get max Extended memory pages, and convert to 16K blocks.  If Extended
  1461.  * memory swapping disabled, set to zero
  1462.  */
  1463.  
  1464.     SW_fp = -1;                /* Set EMS/XMS handler not    */
  1465.                     /* defined            */
  1466.  
  1467. /* Is a XMS memory driver installed */
  1468.  
  1469.     or.x.ax = 0x4300;
  1470.     int86 (0x2f, &or, &or);
  1471.  
  1472.     if (or.h.al != 0x80)
  1473.     {
  1474.     or.x.ax = 0x8800;
  1475.     int86 (0x15, &or, &or);
  1476.     SW_EMsize = or.x.ax / 16;
  1477.  
  1478.     if ((SW_EMsize <= SW_Blocks) ||
  1479.          ((SW_EMstart - 0x100000L +
  1480.           ((long)(SW_Blocks - SW_EMsize) * 16L * 1024L)) < 0L))
  1481.         return XMS_error ("Not enough Extended memory for swap\n", 0);
  1482.  
  1483.     else
  1484.         return TRUE;
  1485.     }
  1486.  
  1487. /* Get the driver interface */
  1488.  
  1489.     or.x.ax = 0x4310;
  1490.     int86x (0x2f, &or, &or, &sr);
  1491.     SW_XMS_Driver = (void (*)())((unsigned long)(sr.es) << 16L | or.x.bx);
  1492.  
  1493.     if ((SW_XMS_Gversion () & 0xff00) != 0x0200)
  1494.     return XMS_error ("Warning: XMS Version != 2\n", 0);
  1495.  
  1496.     else if ((SW_fp = SW_XMS_Allocate (SW_Blocks * 16)) == -1)
  1497.     return XMS_error (XMS_emsg, errno);
  1498.  
  1499.     return TRUE;
  1500. }
  1501.  
  1502. /* Get the EMS Driver information */
  1503.  
  1504. static bool    Get_EMS_Driver ()
  1505. {
  1506.     union REGS        or;
  1507.     struct SREGS    sr;
  1508.     char        *sp;
  1509.  
  1510. /* Set EMS/XMS handler not defined */
  1511.  
  1512.     SW_fp = -1;
  1513.  
  1514.     or.x.ax = 0x3567;
  1515.     intdosx (&or, &or, &sr);
  1516.  
  1517.     sp = (char *)((unsigned long)(sr.es) << 16L | 10L);
  1518.  
  1519. /* If not there - disable */
  1520.  
  1521.     if (memcmp ("EMMXXXX0", sp, 8) != 0)
  1522.     return EMS_error ("Warning: EMS not available\n", 0);
  1523.  
  1524.     or.h.ah = 0x40;            /* Check status            */
  1525.     int86 (0x67, &or, &or);
  1526.  
  1527.     if (or.h.ah != 0)
  1528.     return EMS_error (EMS_emsg, or.h.ah);
  1529.  
  1530. /* Check version greater than 3.2 */
  1531.  
  1532.     or.h.ah = 0x46;
  1533.     int86 (0x67, &or, &or);
  1534.  
  1535.     if ((or.h.ah != 0) || (or.h.al < 0x32))
  1536.     return EMS_error ("Warning: EMS Version < 3.2\n", 0);
  1537.  
  1538. /*  get page frame address */
  1539.  
  1540.     or.h.ah = 0x41;
  1541.     int86 (0x67, &or, &or);
  1542.  
  1543.     if (or.h.ah != 0)
  1544.     return EMS_error (EMS_emsg, or.h.ah);
  1545.  
  1546.     SW_EMSFrame = or.x.bx;        /* Save the page frame        */
  1547.  
  1548. /* Get the number of pages required */
  1549.  
  1550.     or.h.ah = 0x43;
  1551.     or.x.bx = SW_Blocks;
  1552.     int86 (0x67, &or, &or);
  1553.  
  1554.     if (or.h.ah != 0)
  1555.     return EMS_error (EMS_emsg, or.h.ah);
  1556.  
  1557. /* Save the EMS Handler */
  1558.  
  1559.     SW_fp = or.x.dx;
  1560.  
  1561. /* save EMS page map */
  1562.  
  1563.     or.h.ah = 0x47;
  1564.     or.x.dx = SW_fp;
  1565.     int86 (0x67, &or, &or);
  1566.  
  1567.     return (or.h.ah != 0) ? EMS_error (EMS_emsg, or.h.ah) : TRUE;
  1568. }
  1569.  
  1570. /* Print EMS error message */
  1571.  
  1572. static bool    EMS_error (s, v)
  1573. char        *s;
  1574. int        v;
  1575. {
  1576.     print_warn (s, v);
  1577.     Swap_Mode &= ~(SWAP_EXPAND);
  1578.     EMS_Close ();
  1579.     return FALSE;
  1580. }
  1581.  
  1582. /* Print XMS error message */
  1583.  
  1584. static bool    XMS_error (s, v)
  1585. char        *s;
  1586. int        v;
  1587. {
  1588.     print_warn (s, v);
  1589.     Swap_Mode &= ~(SWAP_EXTEND);
  1590.     XMS_Close ();
  1591.     return FALSE;
  1592. }
  1593.  
  1594. /* If the XMS handler is defined - close it */
  1595.  
  1596. static int    XMS_Close ()
  1597. {
  1598.     int        res = 0;
  1599.  
  1600. /* Release XMS page */
  1601.  
  1602.     if (SW_fp != -1)
  1603.     res = SW_XMS_Free (SW_fp);
  1604.  
  1605.     SW_fp = -1;
  1606.     return res;
  1607. }
  1608.  
  1609. /* If the EMS handler is defined - close it */
  1610.  
  1611. static int    EMS_Close ()
  1612. {
  1613.     union REGS        or;
  1614.     int            res = 0;
  1615.  
  1616.     if (SW_fp == -1)
  1617.     return 0;
  1618.  
  1619. /* Restore EMS page */
  1620.  
  1621.     or.h.ah = 0x48;
  1622.     or.x.dx = SW_fp;
  1623.     int86 (0x67, &or, &or);
  1624.  
  1625.     if (or.h.ah != 0)
  1626.     res = or.h.al;
  1627.  
  1628.     or.h.ah = 0x45;
  1629.     or.x.dx = SW_fp;
  1630.     int86 (0x67, &or, &or);
  1631.  
  1632.     SW_fp = -1;
  1633.     return (res) ? res : or.h.ah;
  1634. }
  1635.  
  1636. /* Set up command line.  If the EXTENDED_LINE variable is set, we create
  1637.  * a temporary file, write the argument list (one entry per line) to the
  1638.  * this file and set the command line to @<filename>.  If NOSWAPPING, we
  1639.  * execute the program because I have to modify the argument line
  1640.  */
  1641.  
  1642. int    build_command_line (path, argv, envp)
  1643. char    *path;
  1644. char    **argv;
  1645. char    **envp;
  1646. {
  1647.     char        **pl = argv;
  1648.     char        *fname;
  1649.     int            res, fd;
  1650.     char        *pname;
  1651.     FILE        *fp;
  1652.     char        nbuffer[NAME_MAX + 2];
  1653.     bool        found;
  1654.     char        *ep;
  1655.     char        *new_args[3];
  1656.  
  1657. /* Translate process name to MSDOS format */
  1658.  
  1659.     if ((argv[0] = Gen_Full_Path_Name (path)) == (char *)NULL)
  1660.     return -1;
  1661.  
  1662. /* Find the start of the program name */
  1663.  
  1664.     pname = ((pname = strrchr (path, '\\')) == (char *)NULL) ? path : pname + 1;
  1665.  
  1666. /* Extended command line processing */
  1667.  
  1668.     Extend_file == (char *)NULL;        /* Set no file        */
  1669.  
  1670.     if ((*(pl++) != (char *)NULL) &&
  1671.     ((fname = lookup ("EXTENDED_LINE", FALSE)->value) != null) &&
  1672.     ((fp = fopen (fname, "rt")) != (FILE *)NULL))
  1673.     {
  1674.  
  1675. /* Loop through the file look for the current program */
  1676.  
  1677.     found = FALSE;
  1678.  
  1679.     while (fgets (nbuffer, NAME_MAX + 1, fp) != (char *)NULL)
  1680.     {
  1681.         if ((ep = strchr (nbuffer, '\n')) != (char *)NULL)
  1682.         *ep = 0;
  1683.  
  1684.         if (stricmp (nbuffer, pname) == 0)
  1685.         {
  1686.         found = TRUE;
  1687.         break;
  1688.         }
  1689.     }
  1690.  
  1691.     fclose (fp);
  1692.  
  1693. /* Check parameters don't contain a re-direction parameter */
  1694.  
  1695.     if (found)
  1696.     {
  1697.         char    **pl1 = pl;
  1698.  
  1699.         while (*pl1 != (char *)NULL)
  1700.         {
  1701.         if (**(pl1++) == '@')
  1702.         {
  1703.             found = FALSE;
  1704.             break;
  1705.         }
  1706.         }
  1707.     }
  1708.  
  1709. /* If we find it - create a temporary file and write the stuff */
  1710.  
  1711.     if ((found) &&
  1712.         ((fd = S_open (FALSE, Extend_file = g_tempname (), O_CMASK,
  1713.                0600)) >= 0))
  1714.     {
  1715.         if ((Extend_file = strsave (Extend_file, 0)) == null)
  1716.         Extend_file = (char *)NULL;
  1717.  
  1718.  
  1719. /* Copy to end of list */
  1720.  
  1721.         while (*pl != (char *)NULL)
  1722.         {
  1723.         if (((res = strlen (*pl)) && (write (fd, *pl, res) != res)) ||
  1724.             (write (fd, "\n", 1) != 1))
  1725.         {
  1726.             close (fd);
  1727.             Clear_Extended_File ();
  1728.             errno = ENOSPC;
  1729.             return -1;
  1730.         }
  1731.  
  1732.         ++pl;
  1733.         }
  1734.  
  1735. /* Completed write OK */
  1736.  
  1737.         close (fd);
  1738.  
  1739. /* Set up cmd_line[1] to contain the filename */
  1740.  
  1741.         memset (cmd_line, 0, CMD_LINE_MAX);
  1742.         cmd_line[1] = ' ';
  1743.         cmd_line[2] = '@';
  1744.         strcpy (&cmd_line[3], Extend_file);
  1745.         cmd_line[0] = (char)(strlen (Extend_file) + 2);
  1746.  
  1747. /* Correctly terminate cmd_line in no swap mode */
  1748.  
  1749.         if (Swap_Mode != SWAP_OFF)
  1750.         cmd_line[cmd_line[0] + 2] = '\r';
  1751.  
  1752. /* If the name in the file is in upper case - use \ for separators */
  1753.  
  1754.         if (isupper (*nbuffer))
  1755.         Convert_Slashes (&cmd_line[2]);
  1756.  
  1757. /* OK we are ready to execute */
  1758.  
  1759.         if (Swap_Mode == SWAP_OFF)
  1760.         {
  1761.         new_args[0] = *argv;
  1762.         new_args[1] = &cmd_line[1];
  1763.         new_args[2] = (char *)NULL;
  1764.         return spawnve (P_WAIT, path, new_args, envp);
  1765.         }
  1766.  
  1767.         else
  1768.         return 0;
  1769.     }
  1770.     }
  1771.  
  1772. /* Check length of Parameter list */
  1773.  
  1774.     res = 0;
  1775.     cmd_line[0] = 0;
  1776.     cmd_line[1] = '\r';
  1777.  
  1778. /* Skip the first parameter and get the length of the rest */
  1779.  
  1780.     if (*argv != (char *)NULL)
  1781.     {
  1782.     *(ep = cmd_line + 1) = 0;
  1783.  
  1784.     while (*pl != (char *)NULL)
  1785.     {
  1786.         res += white_space_len (*pl, &found);
  1787.  
  1788.         if (res >= CMD_LINE_MAX)
  1789.           {
  1790.           errno = E2BIG;
  1791.           return -1;
  1792.           }
  1793.   
  1794.         if (found)
  1795.             strcat (strcat (strcat (ep, " \""), *(pl++)), "\"");
  1796.  
  1797.         else
  1798.         strcat (strcat (ep, " "), *(pl++));
  1799.       }
  1800.  
  1801.     cmd_line[res + 1] = '\r';
  1802.     }
  1803.  
  1804. /* Terminate the line and insert the line length */
  1805.  
  1806.     cmd_line[0] = (char)res;
  1807.  
  1808. /* If swapping disabled - just execute it */
  1809.  
  1810.     return (Swap_Mode == SWAP_OFF) ? spawnve (P_WAIT, path, argv, envp) : 0;
  1811. }
  1812.  
  1813. /* Check string for white space */
  1814.  
  1815. static size_t    white_space_len (s, wsf)
  1816. char        *s;
  1817. bool        *wsf;
  1818. {
  1819.     char    *os = s;
  1820.  
  1821.     *wsf = FALSE;
  1822.  
  1823.     while (*s)
  1824.     {
  1825.         if (isspace (*s))
  1826.         *wsf = TRUE;
  1827.  
  1828.     ++s;
  1829.     }
  1830.  
  1831.     return (size_t)(s - os) + (*wsf ? 3 : 1);
  1832. }
  1833.  
  1834. /* Clear Extended command line file */
  1835.  
  1836. void    Clear_Extended_File ()
  1837. {
  1838.     if (Extend_file != (char *)NULL)
  1839.     {
  1840.     unlink (Extend_file);
  1841.     DELETE (Extend_file);
  1842.     }
  1843.  
  1844.     Extend_file = (char *)NULL;
  1845. }
  1846.  
  1847. /* Clear Disk swap file file */
  1848.  
  1849. void    Clear_Swap_File ()
  1850. {
  1851.     if (Swap_File != (char *)NULL)
  1852.     {
  1853.     unlink (Swap_File);
  1854.     DELETE (Swap_File);
  1855.     }
  1856.  
  1857.     Swap_File = (char *)NULL;
  1858. }
  1859.  
  1860. /* Convert the executable path to the full path name */
  1861.  
  1862. static char    *Gen_Full_Path_Name (path)
  1863. char        *path;
  1864. {
  1865.     char        cpath[PATH_MAX + 4];
  1866.     char        npath[PATH_MAX + NAME_MAX + 4];
  1867.     char        n1path[PATH_MAX + 4];
  1868.     char        *p;
  1869.     unsigned int    dummy;
  1870.  
  1871.     Convert_Slashes (path);
  1872.     strupr (path);
  1873.  
  1874. /* Get the current path */
  1875.  
  1876.     getcwd (cpath, PATH_MAX + 3);
  1877.     strcpy (npath, cpath);
  1878.  
  1879. /* In current directory ? */
  1880.  
  1881.     if ((p = strrchr (path, '\\')) == (char *)NULL)
  1882.     {
  1883.      p = path;
  1884.  
  1885. /* Check for a:program case */
  1886.  
  1887.      if (*(p + 1) == ':')
  1888.      {
  1889.         p += 2;
  1890.  
  1891. /* Switch drives and get the path of the other drive */
  1892.  
  1893.         _dos_setdrive (tolower (*path) - 'a' + 1, &dummy);
  1894.         getcwd (npath, PATH_MAX + 3);
  1895.         _dos_setdrive (tolower (*cpath) - 'a' + 1, &dummy);
  1896.      }
  1897.     }
  1898.  
  1899. /* In root directory */
  1900.  
  1901.     else if ((p - path) == 0)
  1902.     {
  1903.     ++p;
  1904.     strcpy (npath, "x:\\");
  1905.     *npath = *cpath;
  1906.     }
  1907.  
  1908.     else if (((p - path) == 2) && (*(path + 1) == ':'))
  1909.     {
  1910.     ++p;
  1911.     strcpy (npath, "x:\\");
  1912.     *npath = *path;
  1913.     }
  1914.  
  1915. /* Find the directory */
  1916.  
  1917.     else
  1918.     {
  1919.     *(p++) = 0;
  1920.  
  1921. /* Change to the directory containing the executable */
  1922.  
  1923.     if (*(path + 1) == ':')
  1924.         _dos_setdrive (tolower (*path) - 'a' + 1, &dummy);
  1925.  
  1926. /* Save the current directory on this drive */
  1927.  
  1928.     getcwd (n1path, PATH_MAX + 3);
  1929.  
  1930. /* Find the directory we want */
  1931.  
  1932.     if (chdir (path) < 0)
  1933.         return (char *)NULL;
  1934.  
  1935. /* Save its full name */
  1936.  
  1937.     getcwd (npath, PATH_MAX + 3);
  1938.  
  1939. /* Restore the original */
  1940.  
  1941.     chdir (n1path);
  1942.  
  1943. /* Restore our original directory */
  1944.  
  1945.     _dos_setdrive (tolower (*cpath) - 'a' + 1, &dummy);
  1946.  
  1947.     if (chdir (cpath) < 0)
  1948.         return (char *)NULL;
  1949.     }
  1950.  
  1951.     if (npath[strlen (npath) - 1] != '\\')
  1952.     strcat (npath, "\\");
  1953.  
  1954.     strcat (npath, p);
  1955.     return strcpy (path, npath);
  1956. }
  1957.