home *** CD-ROM | disk | FTP | other *** search
- /* MS-DOS SHELL - Parse Tree Executor
- *
- * MS-DOS SHELL - Copyright (c) 1990 Data Logic Limited and Charles Forsyth
- *
- * This code is based on (in part) the shell program written by Charles
- * Forsyth and is subject to the following copyright restrictions:
- *
- * 1. Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice is duplicated in the
- * source form and the copyright notice in file sh6.c is displayed
- * on entry to the program.
- *
- * 2. The sources (or parts thereof) or objects generated from the sources
- * (or parts of sources) cannot be sold under any circumstances.
- *
- * $Header: C:/SRC/SHELL/RCS/sh3.c 1.27 90/09/19 15:30:31 Ian_Stewartson Exp $
- *
- * $Log: sh3.c $
- * Revision 1.27 90/09/19 15:30:31 Ian_Stewartson
- * Allow builtin commands to selected/de-selected
- *
- * Revision 1.26 90/09/11 20:06:11 Ian_Stewartson
- * Add support for buitlin command, including the alway builtin functions
- * Change search order to match POSIX
- *
- * Revision 1.25 90/08/24 21:55:00 Ian_Stewartson
- * Change processing order (function, external, internal) to conform to
- * POSIX. Update to gmatch for macro command changes
- *
- * Revision 1.24 90/08/16 10:28:47 Ian_Stewartson
- * Find setting of switch character for DOS4 for batch files
- *
- * Revision 1.23 90/08/14 23:34:09 MS_user
- * Fix memory bugs - Copy function tree before execution
- * Save Extended line file name correctly
- *
- * Revision 1.22 90/06/21 11:10:47 MS_user
- * Ensure Areanum is set correctly for memory areas
- *
- * Revision 1.21 90/06/08 14:53:58 MS_user
- * Finally, we've fixed Gen_Full_Path_Name
- *
- * Revision 1.20 90/05/31 17:44:11 MS_user
- * Ensure Swap file is saved at level 0
- *
- * Revision 1.19 90/05/31 09:49:12 MS_user
- * Implement partial write when swapping to disk
- * Add some signal lockouts to prevent corruption
- * Fix a bug in Gen_Full_Path for c:program
- *
- * Revision 1.18 90/05/15 21:09:39 MS_user
- * Change Restore_Dir parameter to take directory name
- *
- * Revision 1.17 90/05/11 18:47:40 MS_user
- * Get switchchar for command.com
- *
- * Revision 1.16 90/05/09 18:03:08 MS_user
- * Fix bug in Gen_Full_Path with programs in root directory
- *
- * Revision 1.15 90/04/30 19:50:11 MS_user
- * Stop search path if second character of name is colon
- *
- * Revision 1.14 90/04/26 17:27:52 MS_user
- * Fix problem with Picnix Utilities - full path name of executable required
- *
- * Revision 1.13 90/04/25 22:34:39 MS_user
- * Fix case in TELIF where then and else parts are not defined
- * Fix rsh check for execution path declared
- *
- * Revision 1.12 90/04/11 19:49:35 MS_user
- * Another problem with new command line processing
- *
- * Revision 1.11 90/04/11 12:55:49 MS_user
- * Change command line to look exactly like COMMAND.COM
- *
- * Revision 1.10 90/03/27 20:33:10 MS_user
- * Clear extended file name on interrupt
- *
- * Revision 1.9 90/03/27 20:09:21 MS_user
- * Clear_Extended_File required in SH1 for interrupt clean up
- *
- * Revision 1.8 90/03/26 20:57:14 MS_user
- * Change I/O restore so that "exec >filename" works
- *
- * Revision 1.7 90/03/22 13:47:24 MS_user
- * MSDOS does not handle /dev/ files after find_first correctly
- *
- * Revision 1.6 90/03/14 16:44:49 MS_user
- * Add quoting of arguments with white space on command line
- * Add IOTHERE processing to iosetup
- *
- * Revision 1.5 90/03/06 15:10:28 MS_user
- * Get doeval, dodot and runtrap working correctly in run so that a sub-shell
- * is not created and environment variables can be changed.
- *
- * Revision 1.4 90/03/05 13:50:04 MS_user
- * Add XMS driver
- * Fix bug with extended line file not being deleted
- * Get run to support eval and dot functionality correctly
- * Get trap processing to work
- * Add COMMAND.COM support for .bat files
- *
- * Revision 1.3 90/02/22 16:38:20 MS_user
- * Add XMS support
- *
- * Revision 1.2 90/02/14 04:47:06 MS_user
- * Clean up Interrupt 23 and 0 processing, change EMS version error message
- *
- * Revision 1.1 90/01/25 13:41:24 MS_user
- * Initial revision
- *
- */
-
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <stdio.h>
- #include <process.h>
- #include <dos.h>
- #include <signal.h>
- #include <errno.h>
- #include <setjmp.h>
- #include <ctype.h>
- #include <string.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <fcntl.h>
- #include <limits.h>
- #include <dirent.h>
- #include <ctype.h>
-
- #include "sh.h"
-
- /* static Function and string declarations */
-
- static int forkexec (C_Op *, int, int, int, char **);
- static bool iosetup (IO_Actions *, int, int);
- static C_Op **find1case (C_Op *, char *);
- static C_Op *findcase (C_Op *, char *);
- static void echo (char **);
- static int rexecve (char *, char **, char **, bool);
- static int Execute_program (char *, char **, char **, bool);
- static int S_spawnve (char *, char **, char **);
- static bool Get_EMS_Driver (void);
- static bool Get_XMS_Driver (void);
- static bool EMS_error (char *, int);
- static int EMS_Close (void);
- static bool XMS_error (char *, int);
- static int XMS_Close (void);
- static int build_command_line (char *, char **, char **);
- static int setstatus (int);
- static bool Check_for_bat_file (char *);
- static size_t white_space_len (char *, bool *);
- static char *Gen_Full_Path_Name (char *);
-
- static char *AE2big = "arg/env list too big";
- static char *EMS_emsg = "Warning: EMS Error (%x)\n";
- static char *XMS_emsg = "Warning: XMS Error (%x)\n";
- static char *EF_msg = "%s: %s\n";
- /* Extended Command line processing file name */
- static char *Extend_file = (char *)NULL;
- static char *Swap_File = (char *)NULL; /* Swap file */
-
- /*
- * execute tree recursively
- */
-
- int execute (t, pin, pout, act)
- register C_Op *t;
- int pin;
- int pout;
- int act;
- {
- register C_Op *t1;
- int i, localpipe;
- char *cp, **wp;
- char **Local_Tword;
- Var_List *vp;
- Break_C bc;
- Break_C *S_RList; /* Save link pointers */
- Break_C *S_BList;
- Break_C *S_SList;
- int Local_depth; /* Save local values */
- int Local_areanum;
- int rv = 0;
-
- /* End of tree ? */
-
- if (t == (C_Op *)NULL)
- return 0;
-
- /* Save original and Increment execute function recursive level */
-
- Local_depth = Execute_stack_depth++;
-
- /* Save original and increment area number */
-
- Local_areanum = areanum++;
-
- /* Save the exit points from SubShells, functions and for/whiles */
-
- S_RList = Return_List;
- S_BList = Break_List;
- S_SList = SShell_List;
-
- /* Expand any arguments */
-
- wp = (Local_Tword = t->words) != (char **)NULL
- ? eval (Local_Tword, (t->type == TCOM) ? DOALL : DOALL & ~DOKEY)
- : (char **)NULL;
-
- /* Switch on tree node type */
-
- switch (t->type)
- {
- case TFUNC: /* name () { list; } */
- Save_Function (t, FALSE);
- break;
-
- /* In the case of a () command string, we need to save and restore the
- * current environment, directory and traps (can't think of anything else).
- * For any other, we just restore the current directory. Also, we don't
- * want changes in the Variable list header saved for SubShells, because
- * we are effectively back at execute depth zero.
- */
- case TPAREN: /* () */
- if ((rv = Create_NG_VL ()) == -1)
- break;
-
- if (setjmp (bc.brkpt) == 0)
- {
- Return_List = (Break_C *)NULL;
- Break_List = (Break_C *)NULL;
- bc.nextlev = SShell_List;
- SShell_List = &bc;
- rv = execute (t->left, pin, pout, act);
- }
-
- /* Restore the original environment */
-
- Return_List = S_RList;
- Break_List = S_BList;
- SShell_List = S_SList;
- Restore_Environment (rv, Local_depth);
- break;
-
- /* After a normal command, we need to restore the original directory. Note
- * that a cd will have updated the variable $~, so no problem
- */
-
- case TCOM: /* A command process */
- rv = forkexec (t, pin, pout, act, wp);
- Restore_Dir (C_dir->value);
- break;
-
- case TPIPE: /* Pipe processing */
- if ((rv = openpipe ()) < 0)
- break;
-
- /* Create pipe, execute command, reset pipe, execute the other side, close
- * the pipe and fini
- */
-
- localpipe = remap (rv);
- execute (t->left, pin, localpipe, 0);
- lseek (localpipe, 0L, SEEK_SET);
- rv = execute (t->right, localpipe, pout, 0);
- closepipe (localpipe);
- break;
-
- case TLIST: /* Entries in a for statement */
- execute (t->left, pin, pout, 0);
- rv = execute (t->right, pin, pout, 0);
- break;
-
- case TASYNC: /* Async - not supported */
- rv = -1;
- S_puts ("sh: Async commands not supported\n");
- setstatus (rv);
- break;
-
- case TOR: /* || and && */
- case TAND:
- rv = execute (t->left, pin, pout, 0);
-
- if (((t1 = t->right) != (C_Op *)NULL) &&
- ((rv == 0) == (t->type == TAND)))
- rv = execute (t1, pin, pout, 0);
-
- break;
-
- case TFOR: /* First part of a for statement*/
-
- /* for x do...done - use the parameter values. Need to know how many as
- * it is not a NULL terminated array
- */
-
- if (wp == (char **)NULL)
- {
- wp = dolv + 1;
-
- if ((i = dolc) < 0)
- i = 0;
- }
-
- /* for x in y do...done - find the start of the variables and use them all */
-
- else
- {
- i = -1;
- while (*wp++ != (char *)NULL)
- ;
- }
-
- /* Create the loop variable. */
-
- vp = lookup (t->str, TRUE);
-
- /* Set up a long jump return point before executing the for function so that
- * the continue statement is executed, ie we reprocessor the for condition.
- */
-
- while (rv = setjmp (bc.brkpt))
- {
-
- /* Restore the current stack level and clear out any I/O */
-
- Restore_Environment (0, Local_depth + 1);
- Return_List = S_RList;
- SShell_List = S_SList;
-
- /* If this is a break - clear the variable and terminate the while loop and
- * switch statement
- */
-
- if (rv == BC_BREAK)
- break;
- }
-
- if (rv == BC_BREAK)
- break;
-
- /* Process the next entry - Add to the break/continue chain */
-
- bc.nextlev = Break_List;
- Break_List = &bc;
-
- /* Execute the command tree */
-
- for (t1 = t->left; i-- && *wp != NULL;)
- {
- setval (vp, *wp++);
- rv = execute (t1, pin, pout, 0);
- }
-
- /* Remove this tree from the break list */
-
- Break_List = S_BList;
- break;
-
- /* While and Until function. Similar to the For function. Set up a
- * long jump return point before executing the while function so that
- * the continue statement is executed OK.
- */
-
- case TWHILE: /* WHILE and UNTIL functions */
- case TUNTIL:
- while (rv = setjmp (bc.brkpt))
- {
-
- /* Restore the current stack level and clear out any I/O */
-
- Restore_Environment (0, Local_depth + 1);
- Return_List = S_RList;
- SShell_List = S_SList;
-
- /* If this is a break, terminate the while and switch statements */
-
- if (rv == BC_BREAK)
- break;
- }
-
- if (rv == BC_BREAK)
- break;
-
- /* Set up links */
-
- bc.nextlev = Break_List;
- Break_List = &bc;
- t1 = t->left;
-
- while ((execute(t1, pin, pout, 0) == 0) == (t->type == TWHILE))
- rv = execute (t->right, pin, pout, 0);
-
- Break_List = S_BList;
- break;
-
- case TIF: /* IF and ELSE IF functions */
- case TELIF:
- if (t->right != (C_Op *)NULL)
- rv = !execute (t->left, pin, pout, 0)
- ? execute (t->right->left, pin, pout, 0)
- : execute (t->right->right, pin, pout, 0);
- break;
-
- case TCASE: /* CASE function */
- if ((cp = evalstr (t->str, DOSUB | DOTRIM)) == (char *)NULL)
- cp = null;
-
- if ((t1 = findcase (t->left, cp)) != (C_Op *)NULL)
- rv = execute (t1, pin, pout, 0);
-
- break;
-
- case TBRACE: /* {} statement */
- if ((rv >= 0) && ((t1 = t->left) != (C_Op *)NULL))
- rv = execute (t1, pin, pout, 0);
-
- break;
- }
-
- /* Processing Completed - Restore environment */
-
- t->words = Local_Tword;
- Execute_stack_depth = Local_depth;
-
- /* Remove unwanted malloced space */
-
- freehere (areanum);
- freearea (areanum);
-
- areanum = Local_areanum;
-
- /* Check for traps */
-
- if ((i = trapset) != 0)
- {
- trapset = 0;
- runtrap (i);
- }
-
- /* Check for interrupts */
-
- if (Interactive () && SW_intr)
- {
- closeall ();
- fail ();
- }
-
- return rv;
- }
-
- /*
- * Restore the original directory
- */
-
- void Restore_Dir (path)
- char *path;
- {
- unsigned int dummy;
-
- _dos_setdrive (tolower(*path) - 'a' + 1, &dummy);
-
- if (chdir (&path[2]) != 0)
- {
- S_puts ("Warning: current directory reset to /\n");
- chdir ("/");
- Getcwd ();
- }
- }
-
- /*
- * Ok - execute the program, resetting any I/O required
- */
-
- static int forkexec (t, pin, pout, act, wp)
- register C_Op *t;
- int pin;
- int pout;
- int act;
- char **wp;
- {
- int rv = -1;
- int (*shcom)(C_Op *) = (int (*)())NULL;
- char *cp;
- IO_Actions **iopp;
- int resetsig = 0;
- void (*sig_int)();
- char **owp = wp;
- bool spawn = FALSE;
- bool builtin = FALSE;
- Fun_Ops *fop;
- int i;
-
- if (t->type == TCOM)
- {
-
- /* Malloc failed somewhere - given up */
-
- if (wp == (char **)NULL)
- return setstatus (-1);
-
- /* Skip over any assignments */
-
- while ((cp = *wp++) != (char *)NULL)
- ;
-
- cp = *wp;
-
- /* strip all initial assignments not correct wrt PATH=yyy command etc */
-
- if (FL_TEST ('x'))
- echo (cp != (char *)NULL ? wp : owp);
-
- /* Is it only an assignement? */
-
- if ((cp == (char *)NULL) && (t->ioact == (IO_Actions **)NULL))
- {
- while (((cp = *owp++) != (char *)NULL) && assign (cp, COPYV))
- ;
-
- return setstatus (0);
- }
-
- /* Check for built in commands */
-
- else if (cp != (char *)NULL)
- shcom = inbuilt (cp, &builtin);
- }
-
- /* Unix fork simulation? */
-
- t->words = wp;
- if ((act & FEXEC) == 0)
- {
- spawn = TRUE;
-
- if (Interactive ())
- {
- #ifdef SIGQUIT
- signal (SIGQUIT, SIG_IGN);
- #endif
- sig_int = signal (SIGINT, SIG_IGN);
- resetsig = 1;
- }
- }
-
- /* Set any variables */
-
- while (((cp = *owp++) != (char *)NULL) && assign (cp, COPYV))
- {
- if (shcom == (int (*)())NULL)
- s_vstatus (lookup (cp, TRUE), EXPORT);
- }
-
- /* We cannot close the pipe, because once the exec/spawn has taken place
- * the processing of the pipe is not yet complete.
- */
-
- if (pin != NOPIPE)
- {
- S_dup2 (pin, STDIN_FILENO);
- lseek (STDIN_FILENO, 0L, SEEK_SET);
- }
-
- if (pout != NOPIPE)
- {
- S_dup2 (pout, STDOUT_FILENO);
- lseek (STDOUT_FILENO, 0L, SEEK_END);
- }
-
- /* Set up any other IO required */
-
- if ((iopp = t->ioact) != (IO_Actions **)NULL)
- {
- while (*iopp != (IO_Actions *)NULL)
- {
- if (iosetup (*iopp++, pin, pout))
- return rv;
- }
- }
-
-
- /* All fids above 10 are autoclosed in the exec file because we have used
- * the O_NOINHERIT flag. Note I patched open.obj to pass this flag to the
- * open function.
- */
-
- if (resetsig)
- {
- #ifdef SIGQUIT
- signal (SIGQUIT, SIG_IGN);
- #endif
- signal (SIGINT, sig_int);
- }
-
- if (t->type == TPAREN)
- return restore_std (execute (t->left, NOPIPE, NOPIPE, FEXEC), TRUE);
-
- /* Are we just changing the I/O re-direction for the shell ? */
-
- if (wp[0] == NULL)
- {
- if (spawn)
- restore_std (0, TRUE);
-
- return 0;
- }
-
- /* No - Check for a function the program. At this point, we need to put
- * in some processing for return.
- */
-
- if (!builtin && (fop = Fun_Search (wp[0])) != (Fun_Ops *)NULL)
- {
- char **s_dolv = dolv;
- int s_dolc = dolc;
- Break_C *s_RList = Return_List;
- Break_C *s_BList = Break_List;
- Break_C *s_SList = SShell_List;
- int LS_depth = Execute_stack_depth;
- Break_C bc;
- C_Op *New;
-
- /* Set up $0..$n for the function */
-
- dolv = wp;
- for (dolc = 0; dolv[dolc] != (char *)NULL; ++dolc);
- setval (lookup ("#", TRUE), putn (dolc));
-
- if (setjmp (bc.brkpt) == 0)
- {
- Break_List = (Break_C *)NULL;
- bc.nextlev = Return_List;
- Return_List = &bc;
- New = Copy_Function (fop->tree->left);
- rv = execute (New, NOPIPE, NOPIPE, FEXEC);
- }
-
- /* A return has been executed - Unlike, while and for, we just need to
- * restore the local execute stack level and the return will restore
- * the correct I/O.
- */
-
- else
- rv = getn (lookup ("?", FALSE)->value);
-
- /* Restore the old $0, and previous return address */
-
- Break_List = s_BList;
- Return_List = s_RList;
- SShell_List = s_SList;
- dolv = s_dolv;
- dolc = s_dolc;
- Restore_Environment (rv, LS_depth);
- setval (lookup ("#", TRUE), putn (dolc));
- return rv;
- }
-
- /* Check for another drive or directory in the restricted shell */
-
- if (anys (":/\\", wp[0]) && check_rsh (wp[0]))
- return restore_std (-1, TRUE);
-
- /* Ok - execute the program */
-
- if (!builtin)
- rv = rexecve (wp[0], wp, makenv (), spawn);
-
- /* If we didn't find it, check for internal command */
-
- if (builtin || ((rv == -1) && (errno == ENOENT)))
- {
- if (shcom != (int (*)())NULL)
- rv = setstatus ((*shcom)(t));
-
- else
- print_warn (EF_msg, wp[0], "not found");
- }
-
- return restore_std (rv, TRUE);
- }
-
- /*
- * Restore Local Environment
- */
-
- void Restore_Environment (retval, stack)
- int retval;
- int stack;
- {
- Execute_stack_depth = stack;
- Delete_G_VL ();
- Restore_Dir (C_dir->value);
- restore_std (setstatus (retval), TRUE);
- }
-
- /*
- * Set up I/O redirection. 0< 1> are ignored as required within pipelines.
- */
-
- static bool iosetup (iop, pipein, pipeout)
- register IO_Actions *iop;
- int pipein;
- int pipeout;
- {
- register int u;
- char *cp, *msg;
-
- if (iop->io_unit == IODEFAULT) /* take default */
- iop->io_unit = (iop->io_flag & (IOREAD | IOHERE)) ? STDIN_FILENO
- : STDOUT_FILENO;
-
- /* Check for pipes */
-
- if ((pipein != NOPIPE) && (iop->io_unit == STDIN_FILENO))
- return FALSE;
-
- if ((pipeout != NOPIPE) && (iop->io_unit == STDOUT_FILENO))
- return FALSE;
-
- msg = (iop->io_flag & (IOREAD | IOHERE)) ? "open" : "create";
-
- if ((iop->io_flag & IOHERE) == 0)
- {
- if ((cp = evalstr (iop->io_name, DOSUB | DOTRIM)) == (char *)NULL)
- return TRUE;
- }
-
- if (iop->io_flag & IODUP)
- {
- if ((cp[1]) || !isdigit (*cp) && *cp != '-')
- {
- print_error ("%s: illegal >& argument\n", cp);
- return TRUE;
- }
-
- if (*cp == '-')
- iop->io_flag = IOCLOSE;
-
- iop->io_flag &= ~(IOREAD | IOWRITE);
- }
-
- /*
- * When writing to /dev/???, we have to cheat because MSDOS appears to
- * have a problem with /dev/ files after find_first/find_next.
- */
-
- if (((iop->io_flag & ~(IOXHERE | IOTHERE)) == IOWRITE) &&
- (strnicmp (cp, "/dev/", 5) == 0))
- iop->io_flag |= IOCAT;
-
- /* Open the file in the appropriate mode */
-
- switch (iop->io_flag & ~(IOXHERE | IOTHERE))
- {
- case IOREAD: /* < */
- u = S_open (FALSE, cp, O_RDONLY);
- break;
-
- case IOHERE: /* << */
- u = herein (iop->io_name, iop->io_flag & IOXHERE);
- cp = "here file";
- break;
-
- case IOWRITE | IOREAD: /* <> */
- if (check_rsh (cp))
- return TRUE;
-
- u = S_open (FALSE, cp, O_RDWR);
- break;
-
- case IOWRITE | IOCAT: /* >> */
- if (check_rsh (cp))
- return TRUE;
-
- if ((u = S_open (FALSE, cp, O_WRONLY | O_TEXT)) >= 0)
- {
- lseek (u, 0L, SEEK_END);
- break;
- }
-
- case IOWRITE: /* > */
- if (check_rsh (cp))
- return TRUE;
-
- u = S_open (FALSE, cp, O_CMASK, 0666);
- break;
-
- case IODUP: /* >& */
- if (check_rsh (cp))
- return TRUE;
-
- u = S_dup2 (*cp - '0', iop->io_unit);
- break;
-
- case IOCLOSE: /* >- */
- if ((iop->io_unit >= STDIN_FILENO) &&
- (iop->io_unit <= STDERR_FILENO))
- S_dup2 (-1, iop->io_unit);
-
- S_close (iop->io_unit, TRUE);
- return FALSE;
- }
-
- if (u < 0)
- {
- print_warn ("%s: cannot %s\n", cp, msg);
- return TRUE;
- }
-
- else if (u != iop->io_unit)
- {
- S_dup2 (u, iop->io_unit);
- S_close (u, TRUE);
- }
-
- return FALSE;
- }
-
- /*
- * -x flag - echo command to be executed
- */
-
- static void echo (wp)
- register char **wp;
- {
- register int i;
-
- S_putc ('+');
-
- for (i = 0; wp[i] != (char *)NULL; i++)
- {
- S_putc (SP);
- S_puts (wp[i]);
- }
-
- S_putc (NL);
- }
-
- static C_Op **find1case (t, w)
- C_Op *t;
- char *w;
- {
- register C_Op *t1;
- C_Op **tp;
- register char **wp, *cp;
-
- if (t == (C_Op *)NULL)
- return (C_Op **)NULL;
-
- if (t->type == TLIST)
- {
- if ((tp = find1case (t->left, w)) != (C_Op *)NULL)
- return tp;
-
- t1 = t->right; /* TPAT */
- }
-
- else
- t1 = t;
-
- for (wp = t1->words; *wp != (char *)NULL;)
- {
- if ((cp = evalstr (*(wp++), DOSUB)) &&
- gmatch (w, cp, FALSE, (char **)NULL, GM_ALL))
- return &t1->left;
- }
-
- return (C_Op **)NULL;
- }
-
- static C_Op *findcase (t, w)
- C_Op *t;
- char *w;
- {
- register C_Op **tp;
-
- return ((tp = find1case (t, w)) != (C_Op **)NULL) ? *tp : (C_Op *)NULL;
- }
-
- /*
- * Set up the status on exit from a command
- */
-
- static int setstatus (s)
- register int s;
- {
- exstat = s;
- setval (lookup ("?", TRUE), putn (s));
- return s;
- }
-
- /*
- * PATH-searching interface to execve.
- */
-
- static int rexecve (c, v, envp, d_flag)
- char *c;
- char **v;
- char **envp;
- bool d_flag;
- {
- register char *sp;
- int res; /* Result */
- char *em; /* Exit error message */
- bool eloop; /* Re-try flag */
- char **new_argv;
- int batfile; /* .bat file flag */
- int argc = 0; /* Original # of argcs */
- char *params; /* Script parameters */
- int nargc = 0; /* # script args */
- char *p_name; /* Program name */
- int i;
- union REGS r;
-
- /* If the environment is null - It is too big - error */
-
- if (envp == (char **)NULL)
- em = AE2big;
-
- else if ((p_name = getcell (FFNAME_MAX)) == (char *)NULL)
- em = strerror (ENOMEM);
-
- else
- {
-
- /* Count the number of arguments to the program in case of shell script or
- * bat file
- */
-
- while (v[argc++] != (char *)NULL);
-
- ++argc; /* Including the null */
-
- /* Start off on the search path for the executable file */
-
- sp = (any ('/', c) || (*(c + 1) == ':')) ? null : path->value;
-
- do
- {
- sp = path_append (sp, c, p_name);
-
- if ((res = Execute_program (p_name, v, envp, d_flag)) != -1)
- return res;
-
- eloop = TRUE;
-
- switch (errno)
- {
-
- /* No entry for the file - if the file exists, execute it as a shell
- * script. If we never find the file, return the error
- */
- case ENOENT:
- if ((res = O_for_execute (p_name, ¶ms, &nargc)) >= 0)
- {
- batfile = 1;
- S_close (res, TRUE);
- }
-
- else if (!Check_for_bat_file (p_name))
- {
- errno = ENOENT;
- eloop = FALSE;
- break;
- }
-
- else
- {
- Convert_Slashes (p_name);
- batfile = 0;
- nargc = 0;
- }
-
- /* Ok - either a shell script or a bat file (batfile = 0) */
-
- nargc = (nargc < 2) ? 0 : nargc - 1;
- if ((new_argv = (char **)getcell (sizeof (char *) *
- (argc + nargc + 2)))
- == (char **)NULL)
- {
- em = strerror (ENOMEM);
- errno = ENOMEM;
- break;
- }
-
- memcpy (&new_argv[2 + nargc], &v[0], sizeof(char *) * argc);
-
- /* If BAT file, use command.com else use sh */
-
- if (!batfile)
- {
- new_argv[0] = lookup ("COMSPEC", FALSE)->value;
- new_argv[1] = "/c";
-
- /* Get the switch character */
-
- r.x.ax = 0x3700;
- intdos (&r, &r);
-
- if ((r.h.al == 0) && (_osmajor < 4))
- *new_argv[1] = (char)(r.h.dl);
- }
-
- /* Stick in the pre-fix arguments */
-
- else if (nargc)
- {
- i = 1;
- em = params;
-
- while (*em)
- {
- while (isspace (*em))
- *(em++) = 0;
-
- if (*em)
- new_argv[i++] = em;
-
- while (!isspace (*em) && *em)
- ++em;
- }
- }
-
- else if (params != null)
- new_argv[1] = params;
-
- else
- new_argv[1] = lookup (shell, FALSE)->value;
-
- new_argv[2 + nargc] = p_name;
-
- res = rexecve (new_argv[batfile], &new_argv[batfile],
- envp, d_flag);
- /* Release allocated space */
-
- DELETE (new_argv);
-
- if (params != null)
- DELETE (params);
-
- if (res != -1)
- return res;
- /* No - shell */
-
- em = "no Shell";
- eloop = FALSE;
- errno = ENOEXEC;
- break;
-
- case ENOEXEC:
- em = "program corrupt";
- break;
-
- case ENOMEM:
- em = "program too big";
- break;
-
- case E2BIG:
- em = AE2big;
- break;
-
- default:
- em = "cannot execute";
- eloop = FALSE;
- break;
- }
- } while ((sp != (char *)NULL) && !eloop);
- }
-
- /* If we found the program - report the error */
-
- if (errno != ENOENT)
- {
- print_warn (EF_msg, c, em);
-
- if (!d_flag)
- exit (-1);
- }
-
- return -1;
- }
-
- /* Check to see if this is a bat file */
-
- static bool Check_for_bat_file (name)
- char *name;
- {
- char *local_path;
- char *cp;
- bool res;
-
- if ((local_path = getcell (strlen (name) + 5)) == (char *)NULL)
- return FALSE;
-
- /* Work on a copy of the path */
-
- if ((cp = strrchr (strcpy (local_path, name), '/')) == (char *)NULL)
- cp = local_path;
-
- else
- ++cp;
-
- if ((cp = strrchr (cp, '.')) == (char *)NULL)
- strcat (local_path, ".bat");
-
- else if (stricmp (cp, ".bat") != 0)
- {
- DELETE (local_path);
- return FALSE;
- }
-
- res = (access (local_path, X_OK) == 0) ? TRUE : FALSE;
- DELETE (local_path);
- return res;
- }
-
- /*
- * Run the command produced by generator `f' applied to stream `arg'.
- */
-
- int run (argp, f, f_loop)
- IO_Args *argp;
- int (*f)(IO_State *);
- bool f_loop;
- {
- Word_B *swdlist = wdlist;
- Word_B *siolist = iolist;
- jmp_buf ev, rt;
- int *ofail = failpt;
- int rv = -1;
- Break_C *S_RList = Return_List; /* Save loval links */
- Break_C *S_BList = Break_List;
- int LS_depth = Execute_stack_depth;
- int sjr;
- C_Op *outtree;
- int s_execflg = execflg;
-
- /* Create a new save area */
-
- areanum++;
-
- /* Execute the command */
-
- if (newenv (setjmp (errpt = ev)) == FALSE)
- {
- Return_List = (Break_C *)NULL;
- Break_List = (Break_C *)NULL;
- wdlist = (Word_B *)NULL;
- iolist = (Word_B *)NULL;
-
- pushio (argp, f);
- e.iobase = e.iop;
- e.eof_p = (bool)!f_loop; /* Set EOF processing */
- SW_intr = 0;
- multiline = 0;
- inparse = 0;
- execflg = (!f_loop) ? 1 : execflg;
-
- /* Read Input (if f_loop is not set, we are processing a . file command)
- * either for one line or until end of file.
- */
- do
- {
- yynerrs = 0;
-
- if (((sjr = setjmp (failpt = rt)) == 0) &&
- ((outtree = yyparse ()) != (C_Op *)NULL))
- rv = execute (outtree, NOPIPE, NOPIPE, 0);
-
- /* Fail or no loop - zap any files if necessary */
-
- else if (sjr || f_loop)
- {
- Clear_Extended_File ();
- break;
- }
-
- } while (!f_loop);
-
- quitenv ();
- }
-
- /* Restore the environment */
-
- Return_List = S_RList;
- Break_List = S_BList;
- execflg = s_execflg;
- wdlist = swdlist;
- iolist = siolist;
- failpt = ofail;
-
- Restore_Environment (rv, LS_depth);
-
- freearea (areanum--);
- return rv;
- }
-
- /* Exec or spawn the program ? */
-
- static int Execute_program (path, parms, envp, d_flag)
- char *path;
- char **parms;
- char **envp;
- bool d_flag;
- {
- return setstatus ((!d_flag) ? execve (path, parms, envp)
- : S_spawnve (path, parms, envp));
- }
-
- /* Set up to spawn a process */
-
- static int S_spawnve (path, parms, envp)
- char *path;
- char **parms;
- char **envp;
- {
- unsigned int c_cur = (unsigned int)(_psp - 1);
- unsigned int size = 0;
- char *ep, *ep1;
- int res, serrno;
- struct MCB_list *mp = (struct MCB_list *)((unsigned long)c_cur << 16L);
-
- /* Check to see if the file exists */
-
- strcpy (path_line, path);
-
- if ((ep = strrchr (path_line, '/')) == (char *)NULL)
- ep = path_line;
-
- /* If no dot in name - check for .exe and .com files */
-
- if ((ep1 = strchr (ep, '.')) == (char *)NULL)
- {
- ep1 = ep + strlen (ep);
- strcpy (ep1, ".exe");
-
- if ((res = access (path_line, F_OK)) != 0)
- {
- strcpy (ep1, ".com");
- res = access (path_line, F_OK);
- }
-
- if (res != 0)
- return -1;
- }
-
- else if ((stricmp (ep1, ".exe") != 0) && (stricmp (ep1, ".com") != 0))
- {
- if (access (path_line, F_OK) == 0)
- errno = ENOEXEC;
-
- return -1;
- }
-
- else if (access (path_line, F_OK) != 0)
- return -1;
-
- /* Process the command line. If no swapping, we have executed the program */
-
- res = build_command_line (path_line, parms, envp);
-
- if ((Swap_Mode == SWAP_OFF) || res)
- return res;
-
- /* Find the length of the swap area */
-
- while ((mp = (struct MCB_list *)((unsigned long)c_cur << 16L))->MCB_type
- == MCB_CON)
- {
- if ((mp->MCB_pid != _psp) && (mp->MCB_pid != 0) &&
- (mp->MCB_type != MCB_END))
- {
- Clear_Extended_File ();
- print_error ("Fatal: Memory chain corrupt\n");
- return -1;
- }
-
- c_cur += (mp->MCB_len + 1);
- size += mp->MCB_len + 1;
- }
-
- /*
- * Convert swap size from paragraphs to 16K blocks.
- */
-
- if (size == 0)
- size = mp->MCB_len + 1;
-
- SW_Blocks = (size / 0x0400) + 1;
- SW_SBlocks = ((size - etext + _psp - 1) / 0x0400) + 1;
-
- /* OK Now we've set up the FCB's, command line and opened the swap file.
- * Get some sys info for the swapper and execute my little assembler
- * function to swap us out
- */
-
- /* Save the interrupt 0 and 23 addresses */
-
- SW_I0_V = _dos_getvect (0x00);
- SW_I23_V = _dos_getvect (0x23);
-
- /* Ok - 3 methods of swapping */
-
- /* If expanded memory - try that */
-
- if ((Swap_Mode & SWAP_EXPAND) && Get_EMS_Driver ())
- {
- int cr;
-
- SW_Mode = 3; /* Set Expanded memory swap */
-
- res = SA_spawn (envp);
- cr = EMS_Close (); /* Close EMS */
-
- if ((res != -2) && cr) /* Report Close error ? */
- {
- res = -2;
- errno = cr;
- }
-
- if (res == -2)
- EMS_error ("Expanded memory swap failed (%x)\n", errno);
-
- else
- {
- Clear_Extended_File ();
- return res;
- }
-
- /* Failed - disabled */
-
- Swap_Mode &= (~SWAP_EXPAND);
- }
-
- if ((Swap_Mode & SWAP_EXTEND) && Get_XMS_Driver ())
- {
- int cr;
-
- /* Set Extended memory or XMS driver */
-
- SW_Mode = (SW_fp == -1) ? 2 : 4;
-
- res = SA_spawn (envp);
- cr = XMS_Close (); /* Close XMS */
-
- if ((res != -2) && cr) /* Report Close error ? */
- {
- res = -2;
- errno = cr;
- }
-
- if (res == -2)
- XMS_error ("Extended memory swap failed (%x)\n", errno);
-
- else
- {
- Clear_Extended_File ();
- return res;
- }
-
- /* Failed - disabled */
-
- Swap_Mode &= (~SWAP_EXTEND);
- }
-
- /* Try the disk if available */
-
- if (Swap_Mode & SWAP_DISK)
- {
- SW_Pwrite = 0;
-
- if (Swap_File == (char *)NULL)
- SW_fp = S_open (FALSE, (ep = g_tempname ()), O_SMASK, 0600);
-
- else
- {
- SW_fp = S_open (FALSE, Swap_File, O_SaMASK);
- SW_Pwrite = 1;
- }
-
- if (SW_fp < 0)
- {
- Clear_Swap_File ();
- Swap_Mode &= (~SWAP_DISK);
- print_error ("No Swap files\n");
- errno = ENOSPC;
- return -1;
- }
-
- /* Save the swap file name ? */
-
- if ((Swap_File == (char *)NULL) &&
- ((Swap_File = strsave (ep, 0)) == null))
- Swap_File = (char *)NULL;
-
- SW_Mode = 1; /* Set Disk file swap */
-
- /* Seek to correct location */
-
- if (SW_Pwrite)
- {
- long loc = (long)(etext - _psp + 1) * 16L;
-
- if (lseek (SW_fp, loc, SEEK_SET) != loc)
- {
- serrno = errno;
- S_close (SW_fp, TRUE);
- Clear_Swap_File ();
- Swap_Mode &= (~SWAP_DISK);
- print_error ("No Swap files\n");
- errno = serrno;
- return -1;
- }
- }
-
- /* Execute the program */
-
- res = SA_spawn (envp);
-
- /* Close the swap file and extended command line files */
-
- Clear_Extended_File ();
- serrno = errno;
- S_close (SW_fp, TRUE);
- errno = serrno;
-
- /* Check for out of swap space */
-
- if (res == -2)
- {
- Clear_Swap_File ();
- Swap_Mode &= (~SWAP_DISK);
- print_warn ("Swap file write failed\n");
- errno = ENOSPC;
- res = -1;
- }
-
- /* Return the result */
-
- return res;
- }
-
- /* No swapping available - give up */
-
- Clear_Extended_File ();
- print_error ("All Swapping methods failed\n");
- errno = ENOSPC;
- return -1;
- }
-
- /* Get the XMS Driver information */
-
- static bool Get_XMS_Driver ()
- {
- union REGS or;
- struct SREGS sr;
- unsigned int SW_EMsize; /* Number of extend memory blks */
-
- /* Get max Extended memory pages, and convert to 16K blocks. If Extended
- * memory swapping disabled, set to zero
- */
-
- SW_fp = -1; /* Set EMS/XMS handler not */
- /* defined */
-
- /* Is a XMS memory driver installed */
-
- or.x.ax = 0x4300;
- int86 (0x2f, &or, &or);
-
- if (or.h.al != 0x80)
- {
- or.x.ax = 0x8800;
- int86 (0x15, &or, &or);
- SW_EMsize = or.x.ax / 16;
-
- if ((SW_EMsize <= SW_Blocks) ||
- ((SW_EMstart - 0x100000L +
- ((long)(SW_Blocks - SW_EMsize) * 16L * 1024L)) < 0L))
- return XMS_error ("Not enough Extended memory for swap\n", 0);
-
- else
- return TRUE;
- }
-
- /* Get the driver interface */
-
- or.x.ax = 0x4310;
- int86x (0x2f, &or, &or, &sr);
- SW_XMS_Driver = (void (*)())((unsigned long)(sr.es) << 16L | or.x.bx);
-
- if ((SW_XMS_Gversion () & 0xff00) != 0x0200)
- return XMS_error ("Warning: XMS Version != 2\n", 0);
-
- else if ((SW_fp = SW_XMS_Allocate (SW_Blocks * 16)) == -1)
- return XMS_error (XMS_emsg, errno);
-
- return TRUE;
- }
-
- /* Get the EMS Driver information */
-
- static bool Get_EMS_Driver ()
- {
- union REGS or;
- struct SREGS sr;
- char *sp;
-
- /* Set EMS/XMS handler not defined */
-
- SW_fp = -1;
-
- or.x.ax = 0x3567;
- intdosx (&or, &or, &sr);
-
- sp = (char *)((unsigned long)(sr.es) << 16L | 10L);
-
- /* If not there - disable */
-
- if (memcmp ("EMMXXXX0", sp, 8) != 0)
- return EMS_error ("Warning: EMS not available\n", 0);
-
- or.h.ah = 0x40; /* Check status */
- int86 (0x67, &or, &or);
-
- if (or.h.ah != 0)
- return EMS_error (EMS_emsg, or.h.ah);
-
- /* Check version greater than 3.2 */
-
- or.h.ah = 0x46;
- int86 (0x67, &or, &or);
-
- if ((or.h.ah != 0) || (or.h.al < 0x32))
- return EMS_error ("Warning: EMS Version < 3.2\n", 0);
-
- /* get page frame address */
-
- or.h.ah = 0x41;
- int86 (0x67, &or, &or);
-
- if (or.h.ah != 0)
- return EMS_error (EMS_emsg, or.h.ah);
-
- SW_EMSFrame = or.x.bx; /* Save the page frame */
-
- /* Get the number of pages required */
-
- or.h.ah = 0x43;
- or.x.bx = SW_Blocks;
- int86 (0x67, &or, &or);
-
- if (or.h.ah != 0)
- return EMS_error (EMS_emsg, or.h.ah);
-
- /* Save the EMS Handler */
-
- SW_fp = or.x.dx;
-
- /* save EMS page map */
-
- or.h.ah = 0x47;
- or.x.dx = SW_fp;
- int86 (0x67, &or, &or);
-
- return (or.h.ah != 0) ? EMS_error (EMS_emsg, or.h.ah) : TRUE;
- }
-
- /* Print EMS error message */
-
- static bool EMS_error (s, v)
- char *s;
- int v;
- {
- print_warn (s, v);
- Swap_Mode &= ~(SWAP_EXPAND);
- EMS_Close ();
- return FALSE;
- }
-
- /* Print XMS error message */
-
- static bool XMS_error (s, v)
- char *s;
- int v;
- {
- print_warn (s, v);
- Swap_Mode &= ~(SWAP_EXTEND);
- XMS_Close ();
- return FALSE;
- }
-
- /* If the XMS handler is defined - close it */
-
- static int XMS_Close ()
- {
- int res = 0;
-
- /* Release XMS page */
-
- if (SW_fp != -1)
- res = SW_XMS_Free (SW_fp);
-
- SW_fp = -1;
- return res;
- }
-
- /* If the EMS handler is defined - close it */
-
- static int EMS_Close ()
- {
- union REGS or;
- int res = 0;
-
- if (SW_fp == -1)
- return 0;
-
- /* Restore EMS page */
-
- or.h.ah = 0x48;
- or.x.dx = SW_fp;
- int86 (0x67, &or, &or);
-
- if (or.h.ah != 0)
- res = or.h.al;
-
- or.h.ah = 0x45;
- or.x.dx = SW_fp;
- int86 (0x67, &or, &or);
-
- SW_fp = -1;
- return (res) ? res : or.h.ah;
- }
-
- /* Set up command line. If the EXTENDED_LINE variable is set, we create
- * a temporary file, write the argument list (one entry per line) to the
- * this file and set the command line to @<filename>. If NOSWAPPING, we
- * execute the program because I have to modify the argument line
- */
-
- int build_command_line (path, argv, envp)
- char *path;
- char **argv;
- char **envp;
- {
- char **pl = argv;
- char *fname;
- int res, fd;
- char *pname;
- FILE *fp;
- char nbuffer[NAME_MAX + 2];
- bool found;
- char *ep;
- char *new_args[3];
-
- /* Translate process name to MSDOS format */
-
- if ((argv[0] = Gen_Full_Path_Name (path)) == (char *)NULL)
- return -1;
-
- /* Find the start of the program name */
-
- pname = ((pname = strrchr (path, '\\')) == (char *)NULL) ? path : pname + 1;
-
- /* Extended command line processing */
-
- Extend_file == (char *)NULL; /* Set no file */
-
- if ((*(pl++) != (char *)NULL) &&
- ((fname = lookup ("EXTENDED_LINE", FALSE)->value) != null) &&
- ((fp = fopen (fname, "rt")) != (FILE *)NULL))
- {
-
- /* Loop through the file look for the current program */
-
- found = FALSE;
-
- while (fgets (nbuffer, NAME_MAX + 1, fp) != (char *)NULL)
- {
- if ((ep = strchr (nbuffer, '\n')) != (char *)NULL)
- *ep = 0;
-
- if (stricmp (nbuffer, pname) == 0)
- {
- found = TRUE;
- break;
- }
- }
-
- fclose (fp);
-
- /* Check parameters don't contain a re-direction parameter */
-
- if (found)
- {
- char **pl1 = pl;
-
- while (*pl1 != (char *)NULL)
- {
- if (**(pl1++) == '@')
- {
- found = FALSE;
- break;
- }
- }
- }
-
- /* If we find it - create a temporary file and write the stuff */
-
- if ((found) &&
- ((fd = S_open (FALSE, Extend_file = g_tempname (), O_CMASK,
- 0600)) >= 0))
- {
- if ((Extend_file = strsave (Extend_file, 0)) == null)
- Extend_file = (char *)NULL;
-
-
- /* Copy to end of list */
-
- while (*pl != (char *)NULL)
- {
- if (((res = strlen (*pl)) && (write (fd, *pl, res) != res)) ||
- (write (fd, "\n", 1) != 1))
- {
- close (fd);
- Clear_Extended_File ();
- errno = ENOSPC;
- return -1;
- }
-
- ++pl;
- }
-
- /* Completed write OK */
-
- close (fd);
-
- /* Set up cmd_line[1] to contain the filename */
-
- memset (cmd_line, 0, CMD_LINE_MAX);
- cmd_line[1] = ' ';
- cmd_line[2] = '@';
- strcpy (&cmd_line[3], Extend_file);
- cmd_line[0] = (char)(strlen (Extend_file) + 2);
-
- /* Correctly terminate cmd_line in no swap mode */
-
- if (Swap_Mode != SWAP_OFF)
- cmd_line[cmd_line[0] + 2] = '\r';
-
- /* If the name in the file is in upper case - use \ for separators */
-
- if (isupper (*nbuffer))
- Convert_Slashes (&cmd_line[2]);
-
- /* OK we are ready to execute */
-
- if (Swap_Mode == SWAP_OFF)
- {
- new_args[0] = *argv;
- new_args[1] = &cmd_line[1];
- new_args[2] = (char *)NULL;
- return spawnve (P_WAIT, path, new_args, envp);
- }
-
- else
- return 0;
- }
- }
-
- /* Check length of Parameter list */
-
- res = 0;
- cmd_line[0] = 0;
- cmd_line[1] = '\r';
-
- /* Skip the first parameter and get the length of the rest */
-
- if (*argv != (char *)NULL)
- {
- *(ep = cmd_line + 1) = 0;
-
- while (*pl != (char *)NULL)
- {
- res += white_space_len (*pl, &found);
-
- if (res >= CMD_LINE_MAX)
- {
- errno = E2BIG;
- return -1;
- }
-
- if (found)
- strcat (strcat (strcat (ep, " \""), *(pl++)), "\"");
-
- else
- strcat (strcat (ep, " "), *(pl++));
- }
-
- cmd_line[res + 1] = '\r';
- }
-
- /* Terminate the line and insert the line length */
-
- cmd_line[0] = (char)res;
-
- /* If swapping disabled - just execute it */
-
- return (Swap_Mode == SWAP_OFF) ? spawnve (P_WAIT, path, argv, envp) : 0;
- }
-
- /* Check string for white space */
-
- static size_t white_space_len (s, wsf)
- char *s;
- bool *wsf;
- {
- char *os = s;
-
- *wsf = FALSE;
-
- while (*s)
- {
- if (isspace (*s))
- *wsf = TRUE;
-
- ++s;
- }
-
- return (size_t)(s - os) + (*wsf ? 3 : 1);
- }
-
- /* Clear Extended command line file */
-
- void Clear_Extended_File ()
- {
- if (Extend_file != (char *)NULL)
- {
- unlink (Extend_file);
- DELETE (Extend_file);
- }
-
- Extend_file = (char *)NULL;
- }
-
- /* Clear Disk swap file file */
-
- void Clear_Swap_File ()
- {
- if (Swap_File != (char *)NULL)
- {
- unlink (Swap_File);
- DELETE (Swap_File);
- }
-
- Swap_File = (char *)NULL;
- }
-
- /* Convert the executable path to the full path name */
-
- static char *Gen_Full_Path_Name (path)
- char *path;
- {
- char cpath[PATH_MAX + 4];
- char npath[PATH_MAX + NAME_MAX + 4];
- char n1path[PATH_MAX + 4];
- char *p;
- unsigned int dummy;
-
- Convert_Slashes (path);
- strupr (path);
-
- /* Get the current path */
-
- getcwd (cpath, PATH_MAX + 3);
- strcpy (npath, cpath);
-
- /* In current directory ? */
-
- if ((p = strrchr (path, '\\')) == (char *)NULL)
- {
- p = path;
-
- /* Check for a:program case */
-
- if (*(p + 1) == ':')
- {
- p += 2;
-
- /* Switch drives and get the path of the other drive */
-
- _dos_setdrive (tolower (*path) - 'a' + 1, &dummy);
- getcwd (npath, PATH_MAX + 3);
- _dos_setdrive (tolower (*cpath) - 'a' + 1, &dummy);
- }
- }
-
- /* In root directory */
-
- else if ((p - path) == 0)
- {
- ++p;
- strcpy (npath, "x:\\");
- *npath = *cpath;
- }
-
- else if (((p - path) == 2) && (*(path + 1) == ':'))
- {
- ++p;
- strcpy (npath, "x:\\");
- *npath = *path;
- }
-
- /* Find the directory */
-
- else
- {
- *(p++) = 0;
-
- /* Change to the directory containing the executable */
-
- if (*(path + 1) == ':')
- _dos_setdrive (tolower (*path) - 'a' + 1, &dummy);
-
- /* Save the current directory on this drive */
-
- getcwd (n1path, PATH_MAX + 3);
-
- /* Find the directory we want */
-
- if (chdir (path) < 0)
- return (char *)NULL;
-
- /* Save its full name */
-
- getcwd (npath, PATH_MAX + 3);
-
- /* Restore the original */
-
- chdir (n1path);
-
- /* Restore our original directory */
-
- _dos_setdrive (tolower (*cpath) - 'a' + 1, &dummy);
-
- if (chdir (cpath) < 0)
- return (char *)NULL;
- }
-
- if (npath[strlen (npath) - 1] != '\\')
- strcat (npath, "\\");
-
- strcat (npath, p);
- return strcpy (path, npath);
- }
-