home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / SH162_2S.ZIP / SH3.C < prev    next >
C/C++ Source or Header  |  1990-08-04  |  35KB  |  1,562 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: sh3.c 1.22 90/06/21 11:10:47 MS_user Exp $
  17.  *
  18.  *    $Log:    sh3.c $
  19.  * Revision 1.22  90/06/21  11:10:47  MS_user
  20.  * Ensure Areanum is set correctly for memory areas
  21.  *
  22.  * Revision 1.21  90/06/08  14:53:58  MS_user
  23.  * Finally, we've fixed Gen_Full_Path_Name
  24.  *
  25.  * Revision 1.20  90/05/31  17:44:11  MS_user
  26.  * Ensure Swap file is saved at level 0
  27.  *
  28.  * Revision 1.19  90/05/31  09:49:12  MS_user
  29.  * Implement partial write when swapping to disk
  30.  * Add some signal lockouts to prevent corruption
  31.  * Fix a bug in Gen_Full_Path for c:program
  32.  *
  33.  * Revision 1.18  90/05/15  21:09:39  MS_user
  34.  * Change Restore_Dir parameter to take directory name
  35.  *
  36.  * Revision 1.17  90/05/11  18:47:40  MS_user
  37.  * Get switchchar for command.com
  38.  *
  39.  * Revision 1.16  90/05/09  18:03:08  MS_user
  40.  * Fix bug in Gen_Full_Path with programs in root directory
  41.  *
  42.  * Revision 1.15  90/04/30  19:50:11  MS_user
  43.  * Stop search path if second character of name is colon
  44.  *
  45.  * Revision 1.14  90/04/26  17:27:52  MS_user
  46.  * Fix problem with Picnix Utilities - full path name of executable required
  47.  *
  48.  * Revision 1.13  90/04/25  22:34:39  MS_user
  49.  * Fix case in TELIF where then and else parts are not defined
  50.  * Fix rsh check for execution path declared
  51.  *
  52.  * Revision 1.12  90/04/11  19:49:35  MS_user
  53.  * Another problem with new command line processing
  54.  *
  55.  * Revision 1.11  90/04/11  12:55:49  MS_user
  56.  * Change command line to look exactly like COMMAND.COM
  57.  *
  58.  * Revision 1.10  90/03/27  20:33:10  MS_user
  59.  * Clear extended file name on interrupt
  60.  *
  61.  * Revision 1.9  90/03/27  20:09:21  MS_user
  62.  * Clear_Extended_File required in SH1 for interrupt clean up
  63.  *
  64.  * Revision 1.8  90/03/26  20:57:14  MS_user
  65.  * Change I/O restore so that "exec >filename" works
  66.  *
  67.  * Revision 1.7  90/03/22  13:47:24  MS_user
  68.  * MSDOS does not handle /dev/ files after find_first correctly
  69.  *
  70.  * Revision 1.6  90/03/14  16:44:49  MS_user
  71.  * Add quoting of arguments with white space on command line
  72.  * Add IOTHERE processing to iosetup
  73.  *
  74.  * Revision 1.5  90/03/06  15:10:28  MS_user
  75.  * Get doeval, dodot and runtrap working correctly in run so that a sub-shell
  76.  * is not created and environment variables can be changed.
  77.  *
  78.  * Revision 1.4  90/03/05  13:50:04  MS_user
  79.  * Add XMS driver
  80.  * Fix bug with extended line file not being deleted
  81.  * Get run to support eval and dot functionality correctly
  82.  * Get trap processing to work
  83.  * Add COMMAND.COM support for .bat files
  84.  *
  85.  * Revision 1.3  90/02/22  16:38:20  MS_user
  86.  * Add XMS support
  87.  *
  88.  * Revision 1.2  90/02/14  04:47:06  MS_user
  89.  * Clean up Interrupt 23 and 0 processing, change EMS version error message
  90.  *
  91.  * Revision 1.1  90/01/25  13:41:24  MS_user
  92.  * Initial revision
  93.  *
  94.  */
  95.  
  96. #include <sys/types.h>
  97. #include <sys/stat.h>
  98. #include <stdio.h>
  99. #include <process.h>
  100. #include <signal.h>
  101. #include <errno.h>
  102. #include <setjmp.h>
  103. #include <ctype.h>
  104. #include <string.h>
  105. #include <unistd.h>
  106. #include <stdlib.h>
  107. #include <fcntl.h>
  108. #include <limits.h>
  109. #include <dir.h>
  110. #include <ctype.h>
  111.  
  112. #include "sh.h"
  113.  
  114. #define INCL_NOPM
  115. #define INCL_DOS
  116. #include <os2.h>
  117.  
  118. /* static Function and string declarations */
  119.  
  120. static int    forkexec (C_Op *, int, int, int, char **);
  121. static bool    iosetup (IO_Actions *, int, int);
  122. static C_Op    **find1case (C_Op *, char *);
  123. static C_Op    *findcase (C_Op *, char *);
  124. static void    echo (char **);
  125. static int    rexecve (char *, char **, char **, bool);
  126. static int    Execute_program (char *, char **, char **, bool);
  127. static int    S_spawnve (char *, char **, char **);
  128. static int    build_command_line (char *, char **, char **);
  129. static int    setstatus (int);
  130. static bool    Check_for_bat_file (char *);
  131. static size_t    white_space_len (char *, bool *);
  132. static char    *Gen_Full_Path_Name (char *);
  133.  
  134. static int      spawnmode = P_WAIT;
  135.  
  136. static char    *AE2big = "arg/env list too big";
  137.             /* Extended Command line processing file name    */
  138. static char        *Extend_file = (char *)NULL;
  139.  
  140. /*
  141.  * execute tree recursively
  142.  */
  143.  
  144. int        execute (t, pin, pout, act)
  145. register C_Op    *t;
  146. int        pin;
  147. int        pout;
  148. int        act;
  149. {
  150.     register C_Op    *t1;
  151.     int            i, localpipe;
  152.     char        *cp, **wp;
  153.     char        **Local_Tword;
  154.     Var_List        *vp;
  155.     Break_C        bc;
  156.     Break_C        *S_RList;        /* Save link pointers    */
  157.     Break_C        *S_BList;
  158.     Break_C        *S_SList;
  159.     int            Local_depth;        /* Save local values    */
  160.     int            Local_areanum;
  161.     int            rv = 0;
  162.     int                 readp, writep;
  163.  
  164. /* End of tree ? */
  165.  
  166.     if (t == (C_Op *)NULL)
  167.     return 0;
  168.  
  169. /* Save original and Increment execute function recursive level */
  170.  
  171.     Local_depth = Execute_stack_depth++;
  172.  
  173. /* Save original and increment area number */
  174.  
  175.     Local_areanum = areanum++;
  176.  
  177. /* Save the exit points from SubShells, functions and for/whiles */
  178.  
  179.     S_RList = Return_List;
  180.     S_BList = Break_List;
  181.     S_SList = SShell_List;
  182.  
  183. /* Expand any arguments */
  184.  
  185.     wp = (Local_Tword = t->words) != (char **)NULL
  186.      ? eval (Local_Tword, (t->type == TCOM) ? DOALL : DOALL & ~DOKEY)
  187.      : (char **)NULL;
  188.  
  189. /* Switch on tree node type */
  190.  
  191.     switch (t->type)
  192.     {
  193.     case TFUNC:            /* name () { list; }    */
  194.         Save_Function (t, FALSE);
  195.         break;
  196.  
  197. /* In the case of a () command string, we need to save and restore the
  198.  * current environment, directory and traps (can't think of anything else).
  199.  * For any other, we just restore the current directory.  Also, we don't
  200.  * want changes in the Variable list header saved for SubShells, because
  201.  * we are effectively back at execute depth zero.
  202.  */
  203.     case TPAREN:            /* ()            */
  204.         if ((rv = Create_NG_VL ()) == -1)
  205.         break;
  206.  
  207.         if (setjmp (bc.brkpt) == 0)
  208.         {
  209.         Return_List = (Break_C *)NULL;
  210.         Break_List  = (Break_C *)NULL;
  211.         bc.nextlev  = SShell_List;
  212.         SShell_List = &bc;
  213.         /* rv = forkexec (t, pin, pout, act, wp); wrong! */
  214.         rv = execute (t->left, pin, pout, act);
  215.         }
  216.  
  217. /* Restore the original environment */
  218.  
  219.         Return_List    = S_RList;
  220.         Break_List    = S_BList;
  221.         SShell_List    = S_SList;
  222.         Restore_Environment (rv, Local_depth);
  223.         break;
  224.  
  225. /* After a normal command, we need to restore the original directory.  Note
  226.  * that a cd will have updated the variable $~, so no problem
  227.  */
  228.  
  229.     case TCOM:            /* A command process    */
  230.         rv = forkexec (t, pin, pout, act, wp);
  231.         Restore_Dir (C_dir->value);
  232.         break;
  233.  
  234.     case TPIPE:            /* Pipe processing        */
  235.  
  236. /* Create pipe, execute command, reset pipe, execute the other side, close
  237.  * the pipe and fini
  238.  */
  239.      /* old DOS version:
  240.             if ((rv = openpipe ()) < 0)
  241.         break;
  242.         localpipe = remap (rv);
  243.         execute (t->left, pin, localpipe, 0);
  244.         lseek (localpipe, 0L, SEEK_SET);
  245.         rv = execute (t->right, localpipe, pout, 0);
  246.         closepipe (localpipe);
  247.          */
  248.  
  249.             if (DosMakePipe((PHFILE) &readp, (PHFILE) &writep, 0))
  250.                 break;
  251.         readp = remap (readp);
  252.         writep = remap (writep);
  253.             DosSetFHandState(readp, OPEN_FLAGS_NOINHERIT);
  254.         DosSetFHandState(writep, OPEN_FLAGS_NOINHERIT);
  255.  
  256.             if ( spawnmode == P_WAIT )
  257.             {
  258.               spawnmode = P_NOWAITO;
  259.           execute (t->left, pin, writep, 0);
  260.               close(writep);
  261.               spawnmode = P_NOWAIT;
  262.           rv = execute (t->right, readp, pout, 0);
  263.               close(readp);
  264.               spawnmode = P_WAIT;
  265.               cwait(&rv, rv, WAIT_GRANDCHILD);
  266.             }
  267.             else
  268.             {
  269.               rv = spawnmode;
  270.               spawnmode = P_NOWAITO;
  271.           execute (t->left, pin, writep, 0);
  272.               close(writep);
  273.               spawnmode = rv;
  274.           rv = execute (t->right, readp, pout, 0);
  275.               close(readp);
  276.             }
  277.  
  278.         break;
  279.  
  280.     case TLIST:            /* Entries in a for statement    */
  281.         execute (t->left, pin, pout, 0);
  282.         rv = execute (t->right, pin, pout, 0);
  283.         break;
  284.  
  285.     case TASYNC:
  286.      /* rv = -1;
  287.         S_puts ("sh: Async commands not supported\n");
  288.         setstatus (rv);
  289.          */
  290.             spawnmode = P_NOWAIT;
  291.         rv = execute (t->left, pin, pout, 0);
  292.             spawnmode = P_WAIT;
  293.             S_puts("[");
  294.             S_puts(putn(rv));
  295.             S_puts("]\n");
  296.             setval (lookup ("!", TRUE), putn (rv));
  297.         break;
  298.  
  299.     case TOR:            /* || and &&            */
  300.     case TAND:
  301.         rv = execute (t->left, pin, pout, 0);
  302.  
  303.         if (((t1 = t->right) != (C_Op *)NULL) &&
  304.         ((rv == 0) == (t->type == TAND)))
  305.         rv = execute (t1, pin, pout, 0);
  306.  
  307.         break;
  308.  
  309.     case TFOR:            /* First part of a for statement*/
  310.  
  311. /* for x do...done - use the parameter values.  Need to know how many as
  312.  * it is not a NULL terminated array
  313.  */
  314.  
  315.         if (wp == (char **)NULL)
  316.         {
  317.         wp = dolv + 1;
  318.  
  319.         if ((i = dolc) < 0)
  320.             i = 0;
  321.         }
  322.  
  323. /* for x in y do...done - find the start of the variables and use them all */
  324.  
  325.         else
  326.         {
  327.         i = -1;
  328.         while (*wp++ != (char *)NULL)
  329.             ;
  330.         }
  331.  
  332. /* Create the loop variable. */
  333.  
  334.         vp = lookup (t->str, TRUE);
  335.  
  336. /* Set up a long jump return point before executing the for function so that
  337.  * the continue statement is executed, ie we reprocessor the for condition.
  338.  */
  339.  
  340.         while (rv = setjmp (bc.brkpt))
  341.         {
  342.  
  343. /* Restore the current stack level and clear out any I/O */
  344.  
  345.         Restore_Environment (0, Local_depth + 1);
  346.         Return_List = S_RList;
  347.         SShell_List = S_SList;
  348.  
  349. /* If this is a break - clear the variable and terminate the while loop and
  350.  * switch statement
  351.  */
  352.  
  353.         if (rv == BC_BREAK)
  354.             break;
  355.         }
  356.  
  357.         if (rv == BC_BREAK)
  358.         break;
  359.  
  360. /* Process the next entry - Add to the break/continue chain */
  361.  
  362.         bc.nextlev = Break_List;
  363.         Break_List = &bc;
  364.  
  365. /* Execute the command tree */
  366.  
  367.         for (t1 = t->left; i-- && *wp != NULL;)
  368.         {
  369.         setval (vp, *wp++);
  370.         rv = execute (t1, pin, pout, 0);
  371.         }
  372.  
  373. /* Remove this tree from the break list */
  374.  
  375.         Break_List = S_BList;
  376.         break;
  377.  
  378. /* While and Until function.  Similar to the For function.  Set up a
  379.  * long jump return point before executing the while function so that
  380.  * the continue statement is executed OK.
  381.  */
  382.  
  383.     case TWHILE:            /* WHILE and UNTIL functions    */
  384.     case TUNTIL:
  385.         while (rv = setjmp (bc.brkpt))
  386.         {
  387.  
  388. /* Restore the current stack level and clear out any I/O */
  389.  
  390.         Restore_Environment (0, Local_depth + 1);
  391.         Return_List = S_RList;
  392.         SShell_List = S_SList;
  393.  
  394. /* If this is a break, terminate the while and switch statements */
  395.  
  396.         if (rv == BC_BREAK)
  397.             break;
  398.         }
  399.  
  400.         if (rv == BC_BREAK)
  401.         break;
  402.  
  403. /* Set up links */
  404.  
  405.         bc.nextlev = Break_List;
  406.         Break_List = &bc;
  407.         t1 = t->left;
  408.  
  409.         while ((execute(t1, pin, pout, 0) == 0) == (t->type == TWHILE))
  410.         rv = execute (t->right, pin, pout, 0);
  411.  
  412.         Break_List = S_BList;
  413.         break;
  414.  
  415.     case TIF:            /* IF and ELSE IF functions    */
  416.     case TELIF:
  417.         if (t->right != (C_Op *)NULL)
  418.         rv = !execute (t->left, pin, pout, 0)
  419.                 ? execute (t->right->left, pin, pout, 0)
  420.              : execute (t->right->right, pin, pout, 0);
  421.         break;
  422.  
  423.     case TCASE:            /* CASE function        */
  424.         if ((cp = evalstr (t->str, DOSUB | DOTRIM)) == (char *)NULL)
  425.         cp = null;
  426.  
  427.         if ((t1 = findcase (t->left, cp)) != (C_Op *)NULL)
  428.         rv = execute (t1, pin, pout, 0);
  429.  
  430.         break;
  431.  
  432.     case TBRACE:            /* {} statement            */
  433.         if ((rv >= 0) && ((t1 = t->left) != (C_Op *)NULL))
  434.         rv = execute (t1, pin, pout, 0);
  435.  
  436.         break;
  437.     }
  438.  
  439. /* Processing Completed - Restore environment */
  440.  
  441.     t->words        = Local_Tword;
  442.     Execute_stack_depth = Local_depth;
  443.  
  444. /* Remove unwanted malloced space */
  445.  
  446.     freehere (areanum);
  447.     freearea (areanum);
  448.  
  449.     areanum        = Local_areanum;
  450.  
  451. /* Check for traps */
  452.  
  453.     if ((i = trapset) != 0)
  454.     {
  455.     trapset = 0;
  456.     runtrap (i);
  457.     }
  458.  
  459. /* Check for interrupts */
  460.  
  461.     if (Interactive () && SW_intr)
  462.     {
  463.     closeall ();
  464.     fail ();
  465.     }
  466.  
  467.     return rv;
  468. }
  469.  
  470. /*
  471.  * Restore the original directory
  472.  */
  473.  
  474. void    Restore_Dir (path)
  475. char    *path;
  476. {
  477.     unsigned int    dummy;
  478.  
  479.     DosSelectDisk(tolower(*path) - 'a' + 1);
  480.  
  481.     if (chdir (&path[2]) != 0)
  482.     {
  483.     S_puts ("Warning: current directory reset to /\n");
  484.     chdir ("/");
  485.     Getcwd ();
  486.     }
  487. }
  488.  
  489. /*
  490.  * Ok - execute the program, resetting any I/O required
  491.  */
  492.  
  493. static int    forkexec (t, pin, pout, act, wp)
  494. register C_Op    *t;
  495. int        pin;
  496. int        pout;
  497. int        act;
  498. char        **wp;
  499. {
  500.     int        rv = -1;
  501.     int        (*shcom)(C_Op *) = (int (*)())NULL;
  502.     char    *cp;
  503.     IO_Actions    **iopp;
  504.     int        resetsig = 0;
  505.     void    (*sig_int)();
  506.     char    **owp = wp;
  507.     bool    spawn = FALSE;
  508.     Fun_Ops    *fop;
  509.     char        *cmdcom = NULL;
  510.  
  511.     if (t->type == TCOM)
  512.     {
  513.     while ((cp = *wp++) != (char *)NULL)
  514.         ;
  515.  
  516.     cp = *wp;
  517.  
  518. /* strip all initial assignments not correct wrt PATH=yyy command  etc */
  519.  
  520.     if (FL_TEST ('x'))
  521.         echo (cp != (char *)NULL ? wp : owp);
  522.  
  523. /* Is it only an assignement? */
  524.  
  525.     if ((cp == (char *)NULL) && (t->ioact == (IO_Actions **)NULL))
  526.     {
  527.         while (((cp = *owp++) != (char *)NULL) && assign (cp, COPYV))
  528.         ;
  529.  
  530.         return setstatus (0);
  531.     }
  532.  
  533. /* Check for built in commands */
  534.  
  535.     else
  536.           if (cp != (char *)NULL)
  537.         if ( (shcom = inbuilt (cp)) == NULL )
  538.               cmdcom = cmd_internal(cp);
  539.     }
  540.  
  541. /* Unix fork simulation? */
  542.  
  543.     t->words = wp;
  544.     if (shcom == NULL && (act & FEXEC) == 0)
  545.     {
  546.     spawn = TRUE;
  547.  
  548.     if (Interactive ())
  549.     {
  550. #ifdef SIGQUIT
  551.         signal (SIGQUIT, SIG_IGN);
  552. #endif
  553.         sig_int = signal (SIGINT, SIG_IGN);
  554.         resetsig = 1;
  555.     }
  556.     }
  557.  
  558. /* Set any variables */
  559.  
  560.     while (((cp = *owp++) != (char *)NULL) && assign (cp, COPYV))
  561.     {
  562.     if (shcom == NULL)
  563.         s_vstatus (lookup (cp, TRUE), EXPORT);
  564.     }
  565.  
  566. /* We cannot close the pipe, because once the exec/spawn has taken place
  567.  * the processing of the pipe is not yet complete.
  568.  */
  569.  
  570.     if (pin != NOPIPE)
  571.     {
  572.     S_dup2 (pin, STDIN_FILENO);
  573.     lseek (STDIN_FILENO, 0L, SEEK_SET);
  574.     }
  575.  
  576.     if (pout != NOPIPE)
  577.     {
  578.     S_dup2 (pout, STDOUT_FILENO);
  579.     lseek (STDOUT_FILENO, 0L, SEEK_END);
  580.     }
  581.  
  582. /* Set up any other IO required */
  583.  
  584.     if ((iopp = t->ioact) != (IO_Actions **)NULL)
  585.     {
  586.     while (*iopp != (IO_Actions *)NULL)
  587.     {
  588.         if (iosetup (*iopp++, pin, pout))
  589.         return rv;
  590.     }
  591.     }
  592.  
  593.     if (shcom)
  594.         return restore_std (setstatus ((*shcom)(t)), TRUE);
  595.  
  596. /* All fids above 10 are autoclosed in the exec file because we have used
  597.  * the O_NOINHERIT flag.  Note I patched open.obj to pass this flag to the
  598.  * open function.
  599.  */
  600.  
  601.     if (resetsig)
  602.     {
  603. #ifdef SIGQUIT
  604.     signal (SIGQUIT, SIG_IGN);
  605. #endif
  606.     signal (SIGINT, sig_int);
  607.     }
  608.  
  609.     if (t->type == TPAREN)
  610.     return restore_std (execute (t->left, NOPIPE, NOPIPE, FEXEC), TRUE);
  611.  
  612. /* Are we just changing the I/O re-direction for the shell ? */
  613.  
  614.     if (wp[0] == NULL)
  615.     {
  616.     if (spawn)
  617.         restore_std (0, TRUE);
  618.  
  619.     return 0;
  620.     }
  621.  
  622. /* No - Check for a function the program.  At this point, we need to put
  623.  * in some processing for return.
  624.  */
  625.  
  626.     if ((fop = Fun_Search (wp[0])) != (Fun_Ops *)NULL)
  627.     {
  628.     char            **s_dolv = dolv;
  629.     int            s_dolc   = dolc;
  630.     Break_C            *s_RList = Return_List;
  631.     Break_C            *s_BList = Break_List;
  632.     Break_C            *s_SList = SShell_List;
  633.     int            LS_depth = Execute_stack_depth;
  634.     Break_C            bc;
  635.  
  636. /* Set up $0..$n for the function */
  637.  
  638.     dolv = wp;
  639.     for (dolc = 0; dolv[dolc] != (char *)NULL; ++dolc);
  640.     setval (lookup ("#", TRUE), putn (dolc));
  641.  
  642.     if (setjmp (bc.brkpt) == 0)
  643.     {
  644.         Break_List  = (Break_C *)NULL;
  645.         bc.nextlev  = Return_List;
  646.         Return_List = &bc;
  647.         rv = execute (fop->tree->left, NOPIPE, NOPIPE, FEXEC);
  648.     }
  649.  
  650. /* A return has been executed - Unlike, while and for, we just need to
  651.  * restore the local execute stack level and the return will restore
  652.  * the correct I/O.
  653.  */
  654.  
  655.     else
  656.         rv = getn (lookup ("?", FALSE)->value);
  657.  
  658. /* Restore the old $0, and previous return address */
  659.  
  660.     Break_List  = s_BList;
  661.     Return_List = s_RList;
  662.     SShell_List = s_SList;
  663.     dolv        = s_dolv;
  664.     dolc        = s_dolc;
  665.     Restore_Environment (rv, LS_depth);
  666.     setval (lookup ("#", TRUE), putn (dolc));
  667.     return rv;
  668.     }
  669.  
  670. /* Check for another drive or directory in the restricted shell */
  671.  
  672.     if (anys (":/\\", wp[0]) && check_rsh (wp[0]))
  673.     return restore_std (-1, TRUE);
  674.  
  675. /* Ok - execute the program */
  676.  
  677.     if (cmdcom)
  678.       return restore_std (rexecve (NULL, wp, makenv (), spawn), TRUE);
  679.     else
  680.       return restore_std (rexecve (wp[0], wp, makenv (), spawn), 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 | IOCAT:            /* >>            */
  766.         if (check_rsh (cp))
  767.         return TRUE;
  768.  
  769.         if ((u = S_open (FALSE, cp, O_WRONLY | O_TEXT)) >= 0)
  770.         {
  771.         lseek (u, 0L, SEEK_END);
  772.         break;
  773.         }
  774.  
  775.     case IOWRITE:                /* >            */
  776.         if (check_rsh (cp))
  777.         return TRUE;
  778.  
  779.         u = S_open (FALSE, cp, O_CMASK, 0666);
  780.         break;
  781.  
  782.     case IODUP:                /* >&            */
  783.         if (check_rsh (cp))
  784.         return TRUE;
  785.  
  786.         u = S_dup2 (*cp - '0', iop->io_unit);
  787.         break;
  788.  
  789.     case IOCLOSE:                /* >-            */
  790.         if ((iop->io_unit >= STDIN_FILENO) &&
  791.         (iop->io_unit <= STDERR_FILENO))
  792.         S_dup2 (-1, iop->io_unit);
  793.  
  794.         S_close (iop->io_unit, TRUE);
  795.         return FALSE;
  796.     }
  797.  
  798.     if (u < 0)
  799.     {
  800.     print_warn ("%s: cannot %s\n", cp, msg);
  801.     return TRUE;
  802.     }
  803.  
  804.     else if (u != iop->io_unit)
  805.     {
  806.     S_dup2 (u, iop->io_unit);
  807.     S_close (u, TRUE);
  808.     }
  809.  
  810.     return FALSE;
  811. }
  812.  
  813. /*
  814.  * -x flag - echo command to be executed
  815.  */
  816.  
  817. static void    echo (wp)
  818. register char    **wp;
  819. {
  820.     register int    i;
  821.  
  822.     S_putc ('+');
  823.  
  824.     for (i = 0; wp[i] != (char *)NULL; i++)
  825.     {
  826.     S_putc (SP);
  827.     S_puts (wp[i]);
  828.     }
  829.  
  830.     S_putc (NL);
  831. }
  832.  
  833. static C_Op    **find1case (t, w)
  834. C_Op        *t;
  835. char        *w;
  836. {
  837.     register C_Op    *t1;
  838.     C_Op        **tp;
  839.     register char    **wp, *cp;
  840.  
  841.     if (t == (C_Op *)NULL)
  842.     return (C_Op **)NULL;
  843.  
  844.     if (t->type == TLIST)
  845.     {
  846.     if ((tp = find1case (t->left, w)) != (C_Op *)NULL)
  847.         return tp;
  848.  
  849.     t1 = t->right;    /* TPAT */
  850.     }
  851.  
  852.     else
  853.     t1 = t;
  854.  
  855.     for (wp = t1->words; *wp != (char *)NULL;)
  856.     {
  857.     if ((cp = evalstr (*(wp++), DOSUB)) && gmatch (w, cp, FALSE))
  858.         return &t1->left;
  859.     }
  860.  
  861.     return (C_Op **)NULL;
  862. }
  863.  
  864. static C_Op    *findcase (t, w)
  865. C_Op        *t;
  866. char        *w;
  867. {
  868.     register C_Op **tp;
  869.  
  870.     return ((tp = find1case (t, w)) != (C_Op **)NULL) ? *tp : (C_Op *)NULL;
  871. }
  872.  
  873. /*
  874.  * Set up the status on exit from a command
  875.  */
  876.  
  877. static int    setstatus (s)
  878. register int    s;
  879. {
  880.     exstat = s;
  881.     setval (lookup ("?", TRUE), putn (s));
  882.     return s;
  883. }
  884.  
  885. /*
  886.  * PATH-searching interface to execve.
  887.  */
  888.  
  889. static int    rexecve (c, v, envp, d_flag)
  890. char        *c;
  891. char        **v;
  892. char        **envp;
  893. bool        d_flag;
  894. {
  895.     register char    *sp;
  896.     int            res;            /* Result        */
  897.     char        *em;            /* Exit error message    */
  898.     bool        eloop;            /* Re-try flag        */
  899.     char        **new_argv;
  900.     int            batfile;        /* .bat file flag    */
  901.     int            argc = 0;        /* Original # of argcs    */
  902.     char        *params;        /* Script parameters    */
  903.     int            nargc = 0;        /* # script args    */
  904.     char        *p_name;        /* Program name        */
  905.     int            i;
  906.  
  907. /* Count the number of arguments to the program in case of shell script or
  908.  * bat file
  909.  */
  910.  
  911.     while (v[argc++] != (char *)NULL);
  912.  
  913.     ++argc;                /* Including the null        */
  914.  
  915. /* If the environment is null - It is too big - error */
  916.  
  917.     if (envp == (char **)NULL)
  918.     em = AE2big;
  919.  
  920.     else if ((p_name = getcell (FFNAME_MAX)) == (char *)NULL)
  921.     em = strerror (ENOMEM);
  922.  
  923.     else if ( c == NULL )
  924.     {
  925. /* cmd.exe interal command */
  926.  
  927.       if ((new_argv = (char **) getcell (sizeof(char *) * (argc + 2)))
  928.            == NULL)
  929.       em = strerror (ENOMEM);
  930.       else
  931.       {
  932.         memcpy (&new_argv[2], &v[0], sizeof(char *) * argc);
  933.         new_argv[0] = lookup ("COMSPEC", FALSE)->value;
  934.         new_argv[1] = "/c";
  935.         res = rexecve (new_argv[0], new_argv, envp, d_flag);
  936.         DELETE (new_argv);
  937.     return res;
  938.       }
  939.     }
  940.  
  941.     else
  942.     {
  943. /* Start off on the search path for the executable file */
  944.  
  945.     sp = (any ('/', c) || (*(c + 1) == ':')) ? null : path->value;
  946.  
  947.     do
  948.     {
  949.         sp = path_append (sp, c, p_name);
  950.  
  951.         if ((res = Execute_program (p_name, v, envp, d_flag)) != -1)
  952.         return res;
  953.  
  954.         eloop = TRUE;
  955.  
  956.         switch (errno)
  957.         {
  958.  
  959. /* No entry for the file - if the file exists, execute it as a shell
  960.  * script
  961.  */
  962.         case ENOENT:
  963.             if ((res = O_for_execute (p_name, ¶ms, &nargc)) >= 0)
  964.             {
  965.             batfile = 1;
  966.             S_close (res, TRUE);
  967.             }
  968.  
  969.             else if (!Check_for_bat_file (p_name))
  970.             {
  971.             em = "not found";
  972.             eloop = FALSE;
  973.             break;
  974.             }
  975.  
  976.             else
  977.             {
  978.             Convert_Slashes (p_name);
  979.             batfile = 0;
  980.             nargc = 0;
  981.             }
  982.  
  983. /* Ok - either a shell script or a bat file (batfile = 0) */
  984.  
  985.             nargc = (nargc < 2) ? 0 : nargc - 1;
  986.             if ((new_argv = (char **)getcell (sizeof (char *) *
  987.                                 (argc + nargc + 2)))
  988.                   == (char **)NULL)
  989.             {
  990.             em = strerror (ENOMEM);
  991.             break;
  992.             }
  993.  
  994.             memcpy (&new_argv[2 + nargc], &v[0], sizeof(char *) * argc);
  995.  
  996. /* If BAT file, use command.com else use sh */
  997.  
  998.             if (!batfile)
  999.             {
  1000.             new_argv[0] = lookup ("COMSPEC", FALSE)->value;
  1001.             new_argv[1] = "/c";
  1002.             }
  1003.  
  1004. /* Stick in the pre-fix arguments */
  1005.  
  1006.             else if (nargc)
  1007.             {
  1008.             i = 1;
  1009.             em = params;
  1010.  
  1011.             while (*em)
  1012.             {
  1013.                 while (isspace (*em))
  1014.                 *(em++) = 0;
  1015.  
  1016.                 if (*em)
  1017.                 new_argv[i++] = em;
  1018.  
  1019.                 while (!isspace (*em) && *em)
  1020.                 ++em;
  1021.             }
  1022.             }
  1023.  
  1024.             else if (params != null)
  1025.             new_argv[1] = params;
  1026.  
  1027.             else
  1028.             new_argv[1] = lookup (shell, FALSE)->value;
  1029.  
  1030.             new_argv[2 + nargc] = p_name;
  1031.  
  1032.             res = rexecve (new_argv[batfile], &new_argv[batfile],
  1033.                     envp, d_flag);
  1034. /* Release allocated space */
  1035.  
  1036.             DELETE (new_argv);
  1037.  
  1038.             if (params != null)
  1039.             DELETE (params);
  1040.  
  1041.             if (res != -1)
  1042.             return res;
  1043. /* No - shell */
  1044.  
  1045.             em = "no Shell";
  1046.             eloop = FALSE;
  1047.             break;
  1048.  
  1049.         case ENOEXEC:
  1050.             em = "program corrupt";
  1051.             break;
  1052.  
  1053.         case ENOMEM:
  1054.             em = "program too big";
  1055.             break;
  1056.  
  1057.         case E2BIG:
  1058.             em = AE2big;
  1059.             break;
  1060.  
  1061.         default:
  1062.             em = "cannot execute";
  1063.             eloop = FALSE;
  1064.             break;
  1065.         }
  1066.     } while ((sp != (char *)NULL) && !eloop);
  1067.     }
  1068.  
  1069.     print_warn ("%s: %s\n", c, em);
  1070.  
  1071.     if (!d_flag)
  1072.     exit (-1);
  1073.  
  1074.     return -1;
  1075. }
  1076.  
  1077. /* Check to see if this is a bat file */
  1078.  
  1079. static bool    Check_for_bat_file (name)
  1080. char        *name;
  1081. {
  1082.     char    *local_path;
  1083.     char    *cp;
  1084.     bool    res;
  1085.  
  1086.     if ((local_path = getcell (strlen (name) + 5)) == (char *)NULL)
  1087.     return FALSE;
  1088.  
  1089. /* Work on a copy of the path */
  1090.  
  1091.     if ((cp = strrchr (strcpy (local_path, name), '/')) == (char *)NULL)
  1092.     cp = local_path;
  1093.  
  1094.     else
  1095.     ++cp;
  1096.  
  1097.     if ((cp = strrchr (cp, '.')) == (char *)NULL)
  1098.     strcat (local_path, BATCHEXT);
  1099.  
  1100.     else if (stricmp (cp, BATCHEXT) != 0)
  1101.     {
  1102.     DELETE (local_path);
  1103.     return FALSE;
  1104.     }
  1105.  
  1106.     res = (access (local_path, X_OK) == 0) ? TRUE : FALSE;
  1107.     DELETE (local_path);
  1108.     return res;
  1109. }
  1110.  
  1111. /*
  1112.  * Run the command produced by generator `f' applied to stream `arg'.
  1113.  */
  1114.  
  1115. int        run (argp, f, f_loop)
  1116. IO_Args        *argp;
  1117. int        (*f)(IO_State *);
  1118. bool        f_loop;
  1119. {
  1120.     Word_B        *swdlist = wdlist;
  1121.     Word_B        *siolist = iolist;
  1122.     jmp_buf        ev, rt;
  1123.     int            *ofail = failpt;
  1124.     int            rv = -1;
  1125.     Break_C        *S_RList = Return_List;    /* Save loval links    */
  1126.     Break_C        *S_BList = Break_List;
  1127.     int            LS_depth = Execute_stack_depth;
  1128.     int            sjr;
  1129.     C_Op        *outtree;
  1130.     int            s_execflg = execflg;
  1131.  
  1132. /* Create a new save area */
  1133.  
  1134.     areanum++;
  1135.  
  1136. /* Execute the command */
  1137.  
  1138.     if (newenv (setjmp (errpt = ev)) == FALSE)
  1139.     {
  1140.     Return_List = (Break_C *)NULL;
  1141.     Break_List  = (Break_C *)NULL;
  1142.     wdlist      = (Word_B *)NULL;
  1143.     iolist      = (Word_B *)NULL;
  1144.  
  1145.     pushio (argp, f);
  1146.     e.iobase  = e.iop;
  1147.     e.eof_p   = (bool)!f_loop;    /* Set EOF processing        */
  1148.     /*+*/SW_intr   = 0;
  1149.     multiline = 0;
  1150.     inparse   = 0;
  1151.     execflg   = (!f_loop) ? 1 : execflg;
  1152.  
  1153. /* Read Input (if f_loop is not set, we are processing a . file command)
  1154.  * either for one line or until end of file.
  1155.  */
  1156.     do
  1157.     {
  1158.         yynerrs = 0;
  1159.  
  1160.         if (((sjr = setjmp (failpt = rt)) == 0) &&
  1161.         ((outtree = yyparse ()) != (C_Op *)NULL))
  1162.         rv = execute (outtree, NOPIPE, NOPIPE, 0);
  1163.  
  1164. /* Fail or no loop - zap any files if necessary */
  1165.  
  1166.         else if (sjr || f_loop)
  1167.         {
  1168.         Clear_Extended_File ();
  1169.         break;
  1170.         }
  1171.  
  1172.     } while (!f_loop);
  1173.  
  1174.     quitenv ();
  1175.     }
  1176.  
  1177. /* Restore the environment */
  1178.  
  1179.     Return_List = S_RList;
  1180.     Break_List = S_BList;
  1181.     execflg = s_execflg;
  1182.     wdlist = swdlist;
  1183.     iolist = siolist;
  1184.     failpt = ofail;
  1185.  
  1186.     Restore_Environment (rv, LS_depth);
  1187.  
  1188.     freearea (areanum--);
  1189.     return rv;
  1190. }
  1191.  
  1192. /* Exec or spawn the program ? */
  1193.  
  1194. static int    Execute_program (path, parms, envp, d_flag)
  1195. char        *path;
  1196. char        **parms;
  1197. char        **envp;
  1198. bool        d_flag;
  1199. {
  1200.     return setstatus ((!d_flag) ? execve (path, parms, envp)
  1201.                 : S_spawnve (path, parms, envp));
  1202. }
  1203.  
  1204. /* Set up to spawn a process */
  1205.  
  1206. static int    S_spawnve (path, parms, envp)
  1207. char        *path;
  1208. char        **parms;
  1209. char        **envp;
  1210. {
  1211.     char        *ep, *ep1, path_line[255];
  1212.     int            res, serrno;
  1213.  
  1214. /* Check to see if the file exists */
  1215.  
  1216.     strcpy (path_line, path);
  1217.  
  1218.     if ((ep = strrchr (path_line, '/')) == (char *)NULL)
  1219.     ep = path_line;
  1220.  
  1221. /* If no dot in name - check for .exe and .com files */
  1222.  
  1223.     if ((ep1 = strchr (ep, '.')) == (char *)NULL)
  1224.     {
  1225.     ep1 = ep + strlen (ep);
  1226.     strcpy (ep1, ".exe");
  1227.  
  1228.     if ((res = access (path_line, F_OK)) != 0)
  1229.     {
  1230.         strcpy (ep1, ".com");
  1231.         res = access (path_line, F_OK);
  1232.     }
  1233.  
  1234.     if (res != 0)
  1235.         return -1;
  1236.     }
  1237.  
  1238.     else if ((stricmp (ep1, ".exe") != 0) && (stricmp (ep1, ".com") != 0))
  1239.     {
  1240.     if (access (path_line, F_OK) == 0)
  1241.         errno = ENOEXEC;
  1242.  
  1243.     return -1;
  1244.     }
  1245.  
  1246.     else if (access (path_line, F_OK) != 0)
  1247.     return -1;
  1248.  
  1249. /* Process the command line. */
  1250.  
  1251.     return build_command_line (path_line, parms, envp);
  1252. }
  1253.  
  1254. /* Set up command line.  If the EXTENDED_LINE variable is set, we create
  1255.  * a temporary file, write the argument list (one entry per line) to the
  1256.  * this file and set the command line to @<filename>.  If NOSWAPPING, we
  1257.  * execute the program because I have to modify the argument line
  1258.  */
  1259.  
  1260. int    build_command_line (path, argv, envp)
  1261. char    *path;
  1262. char    **argv;
  1263. char    **envp;
  1264. {
  1265.     char        **pl = argv;
  1266.     char        *fname;
  1267.     int            res, fd;
  1268.     char        *pname;
  1269.     FILE        *fp;
  1270.     char        nbuffer[NAME_MAX + 2];
  1271.     bool        found;
  1272.     char        *ep;
  1273.     char        *new_args[3];
  1274.     char                cmd_line[CMD_LINE_MAX];
  1275.     void         (*save_signal)(int);
  1276.  
  1277. /* Translate process name to MSDOS format */
  1278.  
  1279.     if ((argv[0] = Gen_Full_Path_Name (path)) == (char *)NULL)
  1280.     return -1;
  1281.  
  1282. /* Find the start of the program name */
  1283.  
  1284.     pname = ((pname = strrchr (path, '\\')) == (char *)NULL) ? path : pname + 1;
  1285.  
  1286. /* Extended command line processing */
  1287.  
  1288.     Extend_file == (char *)NULL;        /* Set no file        */
  1289.  
  1290.     if ((*(pl++) != (char *)NULL) &&
  1291.     ((fname = lookup ("EXTENDED_LINE", FALSE)->value) != null) &&
  1292.     ((fp = fopen (fname, "rt")) != (FILE *)NULL))
  1293.     {
  1294.  
  1295. /* Loop through the file look for the current program */
  1296.  
  1297.     found = FALSE;
  1298.  
  1299.     while (fgets (nbuffer, NAME_MAX + 1, fp) != (char *)NULL)
  1300.     {
  1301.         if ((ep = strchr (nbuffer, '\n')) != (char *)NULL)
  1302.         *ep = 0;
  1303.  
  1304.         if (stricmp (nbuffer, pname) == 0)
  1305.         {
  1306.         found = TRUE;
  1307.         break;
  1308.         }
  1309.     }
  1310.  
  1311.     fclose (fp);
  1312.  
  1313. /* Check parameters don't contain a re-direction parameter */
  1314.  
  1315.     if (found)
  1316.     {
  1317.         char    **pl1 = pl;
  1318.  
  1319.         while (*pl1 != (char *)NULL)
  1320.         {
  1321.         if (**(pl1++) == '@')
  1322.         {
  1323.             found = FALSE;
  1324.             break;
  1325.         }
  1326.         }
  1327.     }
  1328.  
  1329. /* If we find it - create a temporary file and write the stuff */
  1330.  
  1331.     if ((found) &&
  1332.         ((fd = S_open (FALSE, Extend_file = g_tempname (), O_CMASK,
  1333.                0600)) >= 0))
  1334.     {
  1335.         Extend_file = strsave (Extend_file, 0);
  1336.  
  1337. /* Copy to end of list */
  1338.  
  1339.         while (*pl != (char *)NULL)
  1340.         {
  1341.         if (((res = strlen (*pl)) && (write (fd, *pl, res) != res)) ||
  1342.             (write (fd, "\n", 1) != 1))
  1343.         {
  1344.             close (fd);
  1345.             Clear_Extended_File ();
  1346.             errno = ENOSPC;
  1347.             return -1;
  1348.         }
  1349.  
  1350.         ++pl;
  1351.         }
  1352.  
  1353. /* Completed write OK */
  1354.  
  1355.         close (fd);
  1356.  
  1357. /* Set up cmd_line[1] to contain the filename */
  1358.  
  1359.         memset (cmd_line, 0, CMD_LINE_MAX);
  1360.         cmd_line[1] = ' ';
  1361.         cmd_line[2] = '@';
  1362.         strcpy (&cmd_line[3], Extend_file);
  1363.         cmd_line[0] = (char)(strlen (Extend_file) + 2);
  1364.  
  1365. /* Correctly terminate cmd_line in no swap mode */
  1366.  
  1367.         cmd_line[cmd_line[0] + 2] = '\r';
  1368.  
  1369. /* If the name in the file is in upper case - use \ for separators */
  1370.  
  1371.         if (isupper (*nbuffer))
  1372.         Convert_Slashes (&cmd_line[2]);
  1373.  
  1374. /* OK we are ready to execute */
  1375.  
  1376.         new_args[0] = *argv;
  1377.         new_args[1] = &cmd_line[1];
  1378.         new_args[2] = (char *)NULL;
  1379.  
  1380.             save_signal = signal (SIGINT, SIG_DFL);
  1381.         res = spawnve (spawnmode, path, new_args, envp);
  1382.             signal (SIGINT, save_signal);
  1383.             return res;
  1384.     }
  1385.     }
  1386.  
  1387. /* Check length of Parameter list */
  1388.  
  1389.     res = 0;
  1390.     cmd_line[0] = 0;
  1391.     cmd_line[1] = '\r';
  1392.  
  1393. /* Skip the first parameter and get the length of the rest */
  1394.  
  1395.     if (*argv != (char *)NULL)
  1396.     {
  1397.     *(ep = cmd_line + 1) = 0;
  1398.  
  1399.     while (*pl != (char *)NULL)
  1400.     {
  1401.         res += white_space_len (*pl, &found);
  1402.  
  1403.         if (res >= CMD_LINE_MAX)
  1404.           {
  1405.           errno = E2BIG;
  1406.           return -1;
  1407.           }
  1408.  
  1409.         if (found)
  1410.             strcat (strcat (strcat (ep, " \""), *(pl++)), "\"");
  1411.  
  1412.         else
  1413.         strcat (strcat (ep, " "), *(pl++));
  1414.       }
  1415.  
  1416.     cmd_line[res + 1] = '\r';
  1417.     }
  1418.  
  1419. /* Terminate the line and insert the line length */
  1420.  
  1421.     cmd_line[0] = (char)res;
  1422.  
  1423. /* Just execute it */
  1424.  
  1425.     save_signal = signal (SIGINT, SIG_DFL);
  1426.     res = spawnve (spawnmode, path, argv, envp);
  1427.     signal (SIGINT, save_signal);
  1428.     return res;
  1429. }
  1430.  
  1431. /* Check string for white space */
  1432.  
  1433. static size_t    white_space_len (s, wsf)
  1434. char        *s;
  1435. bool        *wsf;
  1436. {
  1437.     char    *os = s;
  1438.  
  1439.     *wsf = FALSE;
  1440.  
  1441.     while (*s)
  1442.     {
  1443.         if (isspace (*s))
  1444.         *wsf = TRUE;
  1445.  
  1446.     ++s;
  1447.     }
  1448.  
  1449.     return (size_t)(s - os) + (*wsf ? 3 : 1);
  1450. }
  1451.  
  1452. /* Clear Extended command line file */
  1453.  
  1454. void    Clear_Extended_File ()
  1455. {
  1456.     if (Extend_file != (char *)NULL)
  1457.     {
  1458.     unlink (Extend_file);
  1459.     DELETE (Extend_file);
  1460.     }
  1461.  
  1462.     Extend_file = (char *)NULL;
  1463. }
  1464.  
  1465. /* Convert the executable path to the full path name */
  1466.  
  1467. static char    *Gen_Full_Path_Name (path)
  1468. char        *path;
  1469. {
  1470.     char        cpath[PATH_MAX + 4];
  1471.     char        npath[PATH_MAX + NAME_MAX + 4];
  1472.     char        n1path[PATH_MAX + 4];
  1473.     char        *p;
  1474.     unsigned int    dummy;
  1475.  
  1476.     Convert_Slashes (path);
  1477.     strupr (path);
  1478.  
  1479. /* Get the current path */
  1480.  
  1481.     getcwd (cpath, PATH_MAX + 3);
  1482.     strcpy (npath, cpath);
  1483.  
  1484. /* In current directory ? */
  1485.  
  1486.     if ((p = strrchr (path, '\\')) == (char *)NULL)
  1487.     {
  1488.      p = path;
  1489.  
  1490. /* Check for a:program case */
  1491.  
  1492.      if (*(p + 1) == ':')
  1493.      {
  1494.         p += 2;
  1495.  
  1496. /* Switch drives and get the path of the other drive */
  1497.  
  1498.         DosSelectDisk(tolower (*path) - 'a' + 1);
  1499.         getcwd (npath, PATH_MAX + 3);
  1500.         DosSelectDisk(tolower (*cpath) - 'a' + 1);
  1501.      }
  1502.     }
  1503.  
  1504. /* In root directory */
  1505.  
  1506.     else if ((p - path) == 0)
  1507.     {
  1508.     ++p;
  1509.     strcpy (npath, "x:\\");
  1510.     *npath = *cpath;
  1511.     }
  1512.  
  1513.     else if (((p - path) == 2) && (*(path + 1) == ':'))
  1514.     {
  1515.     ++p;
  1516.     strcpy (npath, "x:\\");
  1517.     *npath = *path;
  1518.     }
  1519.  
  1520. /* Find the directory */
  1521.  
  1522.     else
  1523.     {
  1524.     *(p++) = 0;
  1525.  
  1526. /* Change to the directory containing the executable */
  1527.  
  1528.     if (*(path + 1) == ':')
  1529.         DosSelectDisk(tolower (*path) - 'a' + 1);
  1530.  
  1531. /* Save the current directory on this drive */
  1532.  
  1533.     getcwd (n1path, PATH_MAX + 3);
  1534.  
  1535. /* Find the directory we want */
  1536.  
  1537.     if (chdir (path) < 0)
  1538.         return (char *)NULL;
  1539.  
  1540. /* Save its full name */
  1541.  
  1542.     getcwd (npath, PATH_MAX + 3);
  1543.  
  1544. /* Restore the original */
  1545.  
  1546.     chdir (n1path);
  1547.  
  1548. /* Restore our original directory */
  1549.  
  1550.     DosSelectDisk(tolower (*cpath) - 'a' + 1);
  1551.  
  1552.     if (chdir (cpath) < 0)
  1553.         return (char *)NULL;
  1554.     }
  1555.  
  1556.     if (npath[strlen (npath) - 1] != '\\')
  1557.     strcat (npath, "\\");
  1558.  
  1559.     strcat (npath, p);
  1560.     return strcpy (path, npath);
  1561. }
  1562.