Rev 227 | Blame | Compare with Previous | Last modification | View Log | RSS feed
/** MS-DOS SHELL - Parse Tree Executor** MS-DOS SHELL - Copyright (c) 1990,4 Data Logic Limited and Ch/arles 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.** When parts of the original 2.1 shell were replaced by the Lexical* Analsyer written by Simon J. Gerraty (for his Public Domain Korn Shell,* which is also based on Charles Forsyth original idea), a number of changes* were made to reflect the changes Simon made to the Parse output tree. Some* parts of this code in this module are based on the algorithms/ideas that* he incorporated into his shell, in particular the TCASE and TTIME* functionality in ExecuteParseTree, and the PrintAList function.** $Header: /cvsroot/device/DEVL/UTILS/SH/Sh3.c,v 1.3 2004/11/04 07:10:08 ayoung Exp $** $Log: Sh3.c,v $* Revision 1.3 2004/11/04 07:10:08 ayoung* additional EXEC debug, ProcessSpaceInParamaters for generated indirect files** Revision 1.2 2004/05/10 09:30:07 ayoung* improved Path/PATH handling* Quote CreateProcess arg0 if embedded spaces are encountered* Native NT exec dont need to check command line length* Warning when '@' within redirect list* DEBUG_EXEC option, split from PRINT_EXEC option and improved** Revision 1.1 2002/08/02 06:49:34 adamy* imported (reference only)** Revision 1.3 2002/02/05 05:18:33 ayoung* removed functionality that cause drive scanning during application execution** Revision 1.2 2001/08/24 06:02:05 ayoung* WIN32 'sh' return code processing corrected, which previously always returned 0.** Revision 1.1 2001/07/20 05:55:44 ayoung* WIN32 support** Revision 1.3 2000/10/05 09:26:53 adamy* no message** Revision 1.2 2000/09/27 08:33:35 adamy* Added EXTENDED_LINE cache and use __systeml_mode during DOS builds.** Revision 1.1.1.1 1999/12/02 01:11:12 gordonh* UTIL** Revision 2.17 1994/08/25 20:49:11 istewart* MS Shell 2.3 Release** Revision 2.16 1994/02/23 09:23:38 istewart* Beta 233 updates** Revision 2.15 1994/02/01 10:25:20 istewart* Release 2.3 Beta 2, including first NT port** Revision 2.14 1994/01/20 14:51:43 istewart* Release 2.3 Beta 1** Revision 2.13 1994/01/11 17:55:25 istewart* Release 2.3 Beta 0 patches** Revision 2.12 1993/11/09 10:39:49 istewart* Beta 226 checking** Revision 2.11 1993/08/25 16:03:57 istewart* Beta 225 - see Notes file** Revision 2.10 1993/07/02 10:21:35 istewart* 224 Beta fixes** Revision 2.9 1993/06/14 11:00:12 istewart* More changes for 223 beta** Revision 2.8 1993/06/02 09:52:35 istewart* Beta 223 Updates - see Notes file** Revision 2.7 1993/02/16 16:03:15 istewart* Beta 2.22 Release** Revision 2.6 1993/01/26 18:35:09 istewart* Release 2.2 beta 0** Revision 2.5 1992/12/14 10:54:56 istewart* BETA 215 Fixes and 2.1 Release** Revision 2.4 1992/11/06 10:03:44 istewart* 214 Beta test updates** Revision 2.3 1992/09/03 18:54:45 istewart* Beta 213 Updates** Revision 2.2 1992/07/16 14:33:34 istewart* Beta 212 Baseline** Revision 2.1 1992/07/10 10:52:48 istewart* 211 Beta updates** Revision 2.0 1992/05/07 20:31:39 Ian_Stewartson* MS-Shell 2.0 Baseline release**/#include <sys/types.h>#include <sys/stat.h>#include <stdio.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 <time.h>#include "sh.h"#if (OS_TYPE == OS_DOS)#define SYSTEML_MODE#include "libdos.h"#endif/** Save struct for Parameters ($1, $2 etc)*/typedef struct SaveParameters {char **Array; /* The parameters */int Count; /* Number of them */} SaveParameters;/* static Function and string declarations */static int F_LOCAL ForkAndExecute (C_Op *, int, int, int, char **, char **,char **);static bool F_LOCAL SetUpIOHandlers (IO_Actions *, int, int);static bool F_LOCAL WriteToExtendedFile (FILE *, char *);static void F_LOCAL EchoCurrentCommand (char **);static int F_LOCAL ExecuteProgram (char *, char **, char **, int);static void F_LOCAL SaveNumericParameters (char **, SaveParameters *);static void F_LOCAL RestoreTheParameters (SaveParameters *);static bool F_LOCAL ExecuteFunction (char **, int *, bool);static void F_LOCAL PrintLoadError (char *);static void F_LOCAL TrackAllCommands (char *, char *);static int F_LOCAL ExtensionType (char *);static char * F_LOCAL FindFileAndExtension (char *, char *, char **);static bool F_LOCAL GetApplicationType (char *);static bool F_LOCAL BadApplication (char *);static bool F_LOCAL SetUpCLI (char *, Word_B **);static char * F_LOCAL ConvertErrorNumber (void);static int F_LOCAL BuildCommandLine (char *, char **, char **, int);static int F_LOCAL StartTheProcess (char *, char **, char **, int);static int F_LOCAL SetCommandReturnStatus (int);static char ** F_LOCAL FindNumberOfValues (char **, int *);static int F_LOCAL ExecuteScriptFile (char *, char **, char **, int, bool);#if (OS_TYPE != OS_UNIX)static int F_LOCAL ExecuteWindows (char *, char **, char **, int);#endifstatic int F_LOCAL ExecuteSpecialProcessor (char *, char **, char **, int,Word_B *);static int F_LOCAL EnvironExecute (char **, int);static bool F_LOCAL CheckParameterLength (char **);static int F_LOCAL LocalExecve (char **, char **, int);static unsigned int F_LOCAL CheckForCommonOptions (LineFields *, int);static char ** F_LOCAL ProcessSpaceInParameters (char **);static int F_LOCAL CountDoubleQuotes (char *);static void BuildEnvironmentEntry (const void *, VISIT, int);static char ** F_LOCAL BuildCommandEnvironment (void);#if (OS_TYPE != OS_DOS)static void F_LOCAL PrintPidStarted (void);# if (OS_TYPE == OS_OS2)static char *InsertCharacterAtStart (char *);static int F_LOCAL OS_DosExecProgram (int, char *, char **, char **);# elif (OS_TYPE == OS_NT)static int F_LOCAL OS_DosExecProgram (int, char *, char **, char **);# endif#else# define PrintPidStarted()# define InsertCharacterAtStart(a)#endifstatic void PrintProgramMode (ExeMode *PMode);#if (OS_TYPE == OS_OS2)static int F_LOCAL StartTheSession (STARTDATA *, char *, char **,char **, int);#endifstatic char *AE2big = "arg/env list too big (sh)";/* Extended Command line processing file name */static char *Extend_file = (char *)NULL;static char *DoubleQuotes = "\"";static char *WildCards = "*?[\"'";static unsigned long ApplicationType;#if (OS_TYPE == OS_DOS)static char *LIT_STARTWINP = "STARTWINP";#endif/** Global variables for TWALK to build command environment*/static Word_B *BCE_WordList;static int BCE_Length;/** List of Executable (shell script, .exe, .com) extensions and list* including functions.*/static char **ExecutableList = (char **)NULL;static char **ExecuteFunctionList = (char **)NULL;/** OS2 load error mode*/#if (OS_TYPE == OS_OS2)static char FailName[FFNAME_MAX];#endif#if (OS_TYPE != OS_DOS)static OSCALL_RET OS_DosExecPgmReturnCode;#endif/** Program started info*/#if (OS_TYPE != OS_DOS)struct PidInfo {bool Valid;int JobNo;int PidNo;} PidInfo;#endif/** execute tree recursively*/intExecuteParseTree(C_Op *t, int StandardIN, int StandardOUT, int Actions ){int Count;int LocalPipeFP; /* Pipe handlers */#if (OS_TYPE != OS_DOS)int ReadPipeFP;int WritePipeFP;# if OS_TYPE == OS_NTHANDLE ReadPipeH;HANDLE WritePipeH;# endif#endifchar *cp;char **AList; /* Argument list */Break_C BreakContinue;/* Save longjmp returns */Break_C *S_RList = Return_List;Break_C *S_BList = Break_List;Break_C *S_SList = SShell_List;GetoptsIndex GetoptsSave;int Local_depth; /* Save local values */int Local_MemoryAreaLevel;int RetVal = 0; /* Return value */char *InputBuffer; /* Select input Buffer */char *EndIB; /* End of buffer */char *LastWord = null;/* End of tree ? */if (t == (C_Op *)NULL)return SetCommandReturnStatus (0);DPRINT (1, ("ExecuteParseTree: t->type = %d, Depth = %d",t->type, Execute_stack_depth));/* Save original and Increment execute function recursive level */Local_depth = Execute_stack_depth++;/* Save original and increment area number */Local_MemoryAreaLevel = MemoryAreaLevel++;/* Switch on tree node type */switch (t->type){case TFUNC: /* name () { list; } */RetVal = SaveFunction (t) ? 0 : 1;SetCommandReturnStatus (RetVal);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 ((RetVal = CreateGlobalVariableList (FLAGS_NONE)) == -1)break;/* Save Getopts pointers */GetGetoptsValues (&GetoptsSave);if (setjmp (BreakContinue.CurrentReturnPoint) == 0){Return_List = (Break_C *)NULL;Break_List = (Break_C *)NULL;BreakContinue.NextExitLevel = SShell_List;SShell_List = &BreakContinue;RetVal = ForkAndExecute (t, StandardIN, StandardOUT, Actions,NOWORDS, NOWORDS, &LastWord);}/* Restore the original environment */elseRetVal = (int)GetVariableAsNumeric (StatusVariable);SaveGetoptsValues (GetoptsSave.Index, GetoptsSave.SubIndex);Return_List = S_RList;Break_List = S_BList;SShell_List = S_SList;RestoreEnvironment (RetVal, 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 */{ExeMode SaveValues;char **SetVlist; /* Set variable list */SetVlist = ExpandWordList (t->vars, EXPAND_TILDE, (ExeMode *)NULL);AList = ExpandWordList (t->args, EXPAND_SPLITIFS | EXPAND_GLOBBING |EXPAND_TILDE, &SaveValues);ExecProcessingMode = SaveValues;#if (OS_TYPE != OS_DOS)PidInfo.Valid = FALSE;#endifRetVal = ForkAndExecute (t, StandardIN, StandardOUT, Actions,AList, SetVlist, &LastWord);PrintPidStarted ();RestoreEnvironment (RetVal, Local_depth);/* Save last word if appropriate */if (!(DisabledVariables & DISABLE_LASTWORD)){SetVariableFromString (LastWordVariable, LastWord);SetVariableStatus (LastWordVariable, STATUS_EXPORT);}break;}case TTIME: /* Time a command process */{clock_t stime;clock_t etime;clock_t dif;stime = clock ();RetVal = ExecuteParseTree (t->left, StandardIN, StandardOUT, 0);etime = clock ();feputc (CHAR_NEW_LINE);if ((dif = (etime - stime) / (60L * (clock_t)CLOCKS_PER_SEC)) != 0)fprintf (stderr, "%ldm ", dif);dif = (etime - stime) % (60L * (clock_t)CLOCKS_PER_SEC);fprintf (stderr, "%ld.%.3lds real\n",dif / (clock_t)CLOCKS_PER_SEC,dif % (clock_t)CLOCKS_PER_SEC);break;}case TPIPE: /* Pipe processing */Actions |= EXEC_PIPE_IN;#if (OS_TYPE == OS_UNIX)fputs ("UNIX: Pipes not implemented\n", stderr);RetVal = -1;#else# if (OS_TYPE != OS_DOS)/* Do we want to use real pipes under OS2? */if (ShellGlobalFlags & FLAGS_REALPIPES){# if (OS_TYPE == OS_OS2)# if (OS_SIZE == OS_32)if (DosCreatePipe ((PHFILE) &ReadPipeFP,(PHFILE) &WritePipeFP, 4096))break;# elseif (DosMakePipe ((PHFILE) &ReadPipeFP,(PHFILE) &WritePipeFP, 0))break;# endif/* Remap the IO handler */ReadPipeFP = ReMapIOHandler (ReadPipeFP);WritePipeFP = ReMapIOHandler (WritePipeFP);DosSetFHandState (ReadPipeFP, OPEN_FLAGS_NOINHERIT);DosSetFHandState (WritePipeFP, OPEN_FLAGS_NOINHERIT);# elif (OS_TYPE == OS_NT)if (!CreatePipe (&ReadPipeH, &WritePipeH, 0, 0))break;/* Remap the IO handler */ReadPipeFP = _open_osfhandle ((long)ReadPipeH, _O_RDONLY);WritePipeFP = _open_osfhandle ((long)WritePipeH, _O_APPEND);ReadPipeFP = ReMapIOHandler (ReadPipeFP);WritePipeFP = ReMapIOHandler (WritePipeFP);# endif/* Is this a foreground thingy? */if (!(Actions & (EXEC_SPAWN_NOWAIT | EXEC_SPAWN_IGNOREWAIT))){int WaitPid;WaitPid = ExecuteParseTree (t->left, StandardIN,WritePipeFP,EXEC_SPAWN_IGNOREWAIT |(Actions & (EXEC_PIPE_IN |EXEC_PIPE_SUBS)));S_close (WritePipeFP, TRUE);RetVal = ExecuteParseTree (t->right, ReadPipeFP,StandardOUT,Actions | EXEC_PIPE_SUBS);S_close (ReadPipeFP, TRUE);cwait (&WaitPid, WaitPid, WAIT_GRANDCHILD);}/* Background processing */else{ExecuteParseTree (t->left, StandardIN, WritePipeFP,EXEC_SPAWN_IGNOREWAIT |(Actions & (EXEC_PIPE_IN |EXEC_PIPE_SUBS)));S_close (WritePipeFP, TRUE);RetVal = ExecuteParseTree (t->right, ReadPipeFP,StandardOUT,Actions | EXEC_PIPE_SUBS);S_close (ReadPipeFP, TRUE);}break;}# endif/* MSDOS or OS/2 without real pipes - use files. Safer */if ((RetVal = OpenAPipe ()) < 0)break;/* Create pipe, execute command, reset pipe, execute the other side, close* the pipe and fini*/LocalPipeFP = ReMapIOHandler (RetVal);ExecuteParseTree (t->left, StandardIN, LocalPipeFP,(Actions & (EXEC_PIPE_IN | EXEC_PIPE_SUBS)));/* Close the Input to release the file descriptor */CloseThePipe (StandardIN);lseek (LocalPipeFP, 0L, SEEK_SET);RetVal = ExecuteParseTree (t->right, LocalPipeFP, StandardOUT,Actions | EXEC_PIPE_SUBS);CloseThePipe (LocalPipeFP);#endifbreak;case TLIST: /* Entries in a for statement */while (t->type == TLIST){ExecuteParseTree (t->left, StandardIN, StandardOUT, 0);t = t->right;}RetVal = ExecuteParseTree (t, StandardIN, StandardOUT, 0);break;case TCOPROCESS: /* Co processes */if (!FL_TEST (FLAG_WARNING))PrintWarningMessage ("sh: co-processes not supported");SetCommandReturnStatus (RetVal = -1);break;case TASYNC: /* Async - not supported */#if (OS_TYPE == OS_DOS)if (!FL_TEST (FLAG_WARNING))PrintWarningMessage ("sh: Async commands not supported");SetCommandReturnStatus (RetVal = -1);#elseRetVal = ExecuteParseTree (t->left, StandardIN, StandardOUT,EXEC_SPAWN_NOWAIT);#endifbreak;case TOR: /* || and && */case TAND:RetVal = ExecuteParseTree (t->left, StandardIN, StandardOUT, 0);if ((t->right != (C_Op *)NULL) &&((RetVal == 0) == (t->type == TAND)))RetVal = ExecuteParseTree (t->right, StandardIN, StandardOUT,0);break;/* for x do...done and for x in y do...done - find the start of the variables* count the number.*/case TFOR:case TSELECT:AList = FindNumberOfValues (ExpandWordList (t->vars,EXPAND_SPLITIFS |EXPAND_GLOBBING |EXPAND_TILDE,(ExeMode *)NULL),&Count);/* 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 ((RetVal = setjmp (BreakContinue.CurrentReturnPoint)) != 0){/* Restore the current stack level and clear out any I/O */RestoreEnvironment (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 (RetVal == BC_BREAK)break;}if (RetVal == BC_BREAK)break;/* Process the next entry - Add to the break/continue chain */BreakContinue.NextExitLevel = Break_List;Break_List = &BreakContinue;/* Execute the command tree */if (t->type == TFOR){while (Count--){SetVariableFromString (t->str, *AList++);RetVal = ExecuteParseTree (t->left, StandardIN, StandardOUT,0);}}/* Select option */else if (!Count)/* SKIP */;/* Get some memory for the select input buffer */else if ((InputBuffer = AllocateMemoryCell (LINE_MAX))== (char *)NULL){ShellErrorMessage (Outofmemory1);RetVal = -1;}/* Process the select command */else{bool OutputList = TRUE;EndIB = &InputBuffer[LINE_MAX - 2];for (;;){int ReadCount; /* Local counter */int OnlyDigits; /* Only digits in string*//* Output list of words */if (OutputList){PrintAList (Count, AList);OutputList = FALSE;}/* Output prompt */OutputUserPrompt (PS3);OnlyDigits = 1;/* Read in until end of line, file or a field separator is detected */for (cp = InputBuffer; (cp < EndIB); cp++){if (((ReadCount = read (STDIN_FILENO, cp, 1)) != 1) ||(*cp == CHAR_NEW_LINE)){break;}OnlyDigits = OnlyDigits && isdigit (*cp);}*cp = 0;/* Check for end of file */if (ReadCount != 1)break;/* Check for empty line */if (!strlen (InputBuffer)){OutputList = TRUE;continue;}SetVariableFromString (LIT_REPLY, InputBuffer);/* Check that OnlyDigits is a valid number in the select range */if (OnlyDigits &&((OnlyDigits = atoi (InputBuffer)) > 0) &&(OnlyDigits <= Count))SetVariableFromString (t->str, AList[OnlyDigits - 1]);elseSetVariableFromString (t->str, null);RetVal = ExecuteParseTree (t->left, StandardIN, StandardOUT,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 ((RetVal = setjmp (BreakContinue.CurrentReturnPoint)) != 0){/* Restore the current stack level and clear out any I/O */RestoreEnvironment (0, Local_depth + 1);Return_List = S_RList;SShell_List = S_SList;/* If this is a break, terminate the while and switch statements */if (RetVal == BC_BREAK)break;}if (RetVal == BC_BREAK)break;/* Set up links */BreakContinue.NextExitLevel = Break_List;Break_List = &BreakContinue;while ((ExecuteParseTree (t->left, StandardIN, StandardOUT, 0) == 0)== (t->type == TWHILE))RetVal = ExecuteParseTree (t->right, StandardIN, StandardOUT,0);Break_List = S_BList;break;case TIF: /* IF and ELSE IF functions */case TELIF:if (t->right != (C_Op *)NULL)RetVal = ExecuteParseTree (!ExecuteParseTree (t->left,StandardIN,StandardOUT, 0)? t->right->left : t->right->right,StandardIN, StandardOUT, 0);break;case TCASE: /* CASE function */{C_Op *ts = t->left;cp = ExpandAString (t->str, 0);while ((ts != (C_Op *)NULL) && (ts->type == TPAT)){for (AList = ts->vars; *AList != NOWORD; AList++){if ((EndIB = ExpandAString (*AList, EXPAND_PATTERN)) != 0 &&GeneralPatternMatch (cp, (unsigned char *)EndIB, FALSE,(char **)NULL, GM_ALL))goto Found;}ts = ts->right;}break;Found:RetVal = ExecuteParseTree (ts->left, StandardIN, StandardOUT, 0);break;}case TBRACE: /* {} statement */if ((RetVal >= 0) && (t->left != (C_Op *)NULL))RetVal = ExecuteParseTree (t->left, StandardIN, StandardOUT,(Actions & EXEC_FUNCTION));break;}/* Processing Completed - Restore environment */Execute_stack_depth = Local_depth;/* Remove unwanted malloced space */ReleaseMemoryArea (MemoryAreaLevel);MemoryAreaLevel = Local_MemoryAreaLevel;/* Check for traps */if (t->type == TCOM){RunTrapCommand (-1); /* Debug trap */if (RetVal)RunTrapCommand (-2); /* Err trap */}/* Interrupt traps */if ((Count = InterruptTrapPending) != 0){InterruptTrapPending = 0;RunTrapCommand (Count);}/* Check for interrupts */if (InteractiveFlag && IS_TTY (0) && SW_intr){CloseAllHandlers ();TerminateCurrentEnvironment (TERMINATE_COMMAND);}return RetVal;}/** Restore the original directory*/void RestoreCurrentDirectory (char *path){SetCurrentDrive (GetDriveNumber (*path));if (!S_chdir (&path[2])){if (!FL_TEST (FLAG_WARNING))feputs ("Warning: current directory reset to /\n");S_chdir (DirectorySeparator);GetCurrentDirectoryPath ();}}/** Ok - execute the program, resetting any I/O required*/static int F_LOCALForkAndExecute (C_Op *t, int StandardIN, int StandardOUT,int ForkAction, char **AList, char **VList, char **LastWord ){int RetVal = -1; /* Return value */int (*shcom)(int, char **) = (int (*)(int, char **))NULL;char *cp;char **alp;IO_Actions **iopp = t->ioact;int builtin = 0; /* Builtin function */bool CGVLCalled = FALSE;bool InternalExec = FALSE;int Index;if (t->type == TCOM){cp = *AList;/* strip all initial assignments not correct wrt PATH=yyy command etc */if (FL_TEST (FLAG_PRINT_EXECUTE) ||((CurrentFunction != (FunctionList *)NULL) &&CurrentFunction->Traced))EchoCurrentCommand (cp != NOWORD ? AList : VList);/* Is it only an assignement? */if ((cp == NOWORD) && (t->ioact == (IO_Actions **)NULL)){while (((cp = *(VList++)) != NOWORD) &&AssignVariableFromString (cp, &Index))continue;#if (OS_TYPE != OS_DOS)ExitWithJobsActive = FALSE;#endif/* Get the status variable if expand changed it */return (int)GetVariableAsNumeric (StatusVariable);}/* Check for built in commands */else if (cp != NOWORD){shcom = IsCommandBuiltIn (cp, &builtin);InternalExec = C2bool ((strcmp (cp, LIT_exec)) == 0);/** Reset the ExitWithJobsActive flag to enable the 'jobs active' on exit* message*/#if (OS_TYPE != OS_DOS)if (strcmp (cp, LIT_exit))ExitWithJobsActive = FALSE;#endif}#if (OS_TYPE != OS_DOS)elseExitWithJobsActive = FALSE;#endif}#if (OS_TYPE != OS_DOS)elseExitWithJobsActive = FALSE;#endif/** UNIX fork simulation?*//* If there is a command to execute or we are exec'ing and this is not a* TPAREN, save the current environment*/if (t->type != TPAREN){if (((*VList != NOWORD) &&((builtin & BLT_SKIPENVIR) != BLT_SKIPENVIR)) ||(ForkAction & EXEC_WITHOUT_FORK)){if (CreateGlobalVariableList (FLAGS_FUNCTION) == -1)return -1;CGVLCalled = TRUE;}/* Set up any variables. Note there is an assumption that* AssignVariableFromString sets the equals sign to 0, hiding the value;*/if (shcom == (int (*)(int, char **))NULL){while (((cp = *(VList++)) != NOWORD) &&AssignVariableFromString (cp, &Index))SetVariableArrayStatus (cp, Index,(STATUS_EXPORT | STATUS_LOCAL));/* If the child shells from this process communicate via pipes, set the* environment so the child shell will know. This is a special case for OS/2* (and Win NT?) EMACS.*/if (ExecProcessingMode.Flags & EP_PSEUDOTTY){char temp[30];AssignVariableFromString (strcat (strcpy (temp, LIT_AllowTTY),"=true"), &Index);SetVariableArrayStatus (temp, Index,(STATUS_EXPORT | STATUS_LOCAL));}}}/* If this is the first command in a pipe, set stdin to /dev/null */if ((ForkAction & (EXEC_PIPE_IN | EXEC_PIPE_SUBS)) == EXEC_PIPE_IN){if ((Index = S_open (FALSE, "/dev/null", O_RDONLY)) != 0){S_dup2 (Index, 0);S_close (Index, TRUE);}}/* We cannot close the pipe, because once the exec/spawn has taken place* the processing of the pipe is not yet complete.*/if (StandardIN != NOPIPE){S_dup2 (StandardIN, STDIN_FILENO);/*lseek (STDIN_FILENO, 0L, SEEK_SET);*/}if (StandardOUT != NOPIPE){FlushStreams ();S_dup2 (StandardOUT, STDOUT_FILENO);lseek (STDOUT_FILENO, 0L, SEEK_END);}/* Set up any other IO required */FlushStreams ();if (iopp != (IO_Actions **)NULL){while (*iopp != (IO_Actions *)NULL){if (SetUpIOHandlers (*(iopp++), StandardIN, StandardOUT))return RetVal;}}/* 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 (t->type == TPAREN)return RestoreStandardIO (ExecuteParseTree (t->left, NOPIPE, NOPIPE, 0),TRUE);/* Are we just changing the I/O re-direction for the shell ? */if (*AList == NOWORD){if ((ForkAction & EXEC_WITHOUT_FORK) == 0)RestoreStandardIO (0, TRUE);/* Get the status variable if expand changed it */return (int)GetVariableAsNumeric (StatusVariable);}/** Find the end of the parameters and set up $_ environment variable*/alp = AList;while (*alp != NOWORD)alp++;/* Move back to last parameter, and save it */*LastWord = StringCopy (*(alp - 1));/* No - Check for a function the program. At this point, we need to put* in some processing for return.*/if (!(builtin & BLT_CURRENT) &&ExecuteFunction (AList, &RetVal, CGVLCalled))return RetVal;/* Check for another drive or directory in the restricted shell */if ((strpbrk (*AList, ":/\\") != (char *)NULL) &&CheckForRestrictedShell (*AList))return RestoreStandardIO (-1, TRUE);/* A little cheat to allow us to use the same code to start OS/2 sessions* as to load and execute a program*/#if (OS_TYPE == OS_OS2)SessionControlBlock = (STARTDATA *)NULL;#endif/* Ok - execute the program */if (!(builtin & BLT_CURRENT)){RetVal = EnvironExecute (AList, ForkAction);if (ExecProcessingMode.Flags != EP_ENVIRON)RetVal = LocalExecve (AList, BuildCommandEnvironment(), ForkAction);}/* If we didn't find it, check for internal command** Note that the exec command is a special case*/if ((builtin & BLT_CURRENT) || ((RetVal == -1) && (errno == ENOENT))){if (shcom != (int (*)(int, char **))NULL){if (InternalExec)RetVal = doexec (t);elseRetVal = (*shcom)(CountNumberArguments (AList), AList);SetCommandReturnStatus (RetVal);}}if (RetVal == -1)PrintLoadError (*AList);return RestoreStandardIO (RetVal, TRUE);}/** Restore Local Environment*/void RestoreEnvironment (int retval, int stack){Execute_stack_depth = stack;DeleteGlobalVariableList ();RestoreCurrentDirectory (CurrentDirectory->value);RestoreStandardIO (SetCommandReturnStatus (retval), TRUE);}/** Set up I/O redirection. 0< 1> are ignored as required within pipelines.*/static bool F_LOCAL SetUpIOHandlers (IO_Actions *iop,int pipein,int pipeout){int u = -1;char *cp = 0;char *msg;/* 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 & IOTYPE) == IOWRITE) ? "create" : "open";if ((iop->io_flag & IOTYPE) != IOHERE)cp = ExpandOneStringFirstComponent (iop->io_name,EXPAND_TILDE | EXPAND_GLOBBING);/** Duplicate - check for close*/if ((iop->io_flag & IOTYPE) == IODUP){if ((cp[1]) || (!isdigit (*cp) && (*cp != CHAR_CLOSE_FD))){ShellErrorMessage ("illegal >& argument (%s)", cp);return TRUE;}if (*cp == CHAR_CLOSE_FD){iop->io_flag &= ~IOTYPE;iop->io_flag |= IOCLOSE;}}/** When writing to /dev/???, we have to cheat because MSDOS appears to* have a problem with /dev/ files after find_first/find_next.*/#if (OS_TYPE != OS_UNIX)if (((iop->io_flag & IOTYPE) == IOWRITE) &&(strnicmp (cp, DeviceNameHeader, LEN_DEVICE_NAME_HEADER) == 0)){iop->io_flag &= ~IOTYPE;iop->io_flag |= IOCAT;}#endif/* Open the file in the appropriate mode */switch (iop->io_flag & IOTYPE){case IOREAD: /* < */u = S_open (FALSE, cp, O_RDONLY);break;case IOHERE: /* << */u = OpenHereFile (iop->io_name, C2bool (iop->io_flag & IOEVAL));cp = "here file";break;case IORDWR: /* <> */if (CheckForRestrictedShell (cp))return TRUE;u = S_open (FALSE, cp, O_RDWR);break;case IOCAT: /* >> */if (CheckForRestrictedShell (cp))return TRUE;if ((u = S_open (FALSE, cp, O_WRONLY | O_BINARY)) >= 0){lseek (u, 0L, SEEK_END);break;}case IOWRITE: /* > */{if (CheckForRestrictedShell (cp))return TRUE;if ((ShellGlobalFlags & FLAGS_NOCLOBER) &&(!(iop->io_flag & IOCLOBBER)) &&(S_access (cp, F_OK))){u = -1;break;}}u = S_open (FALSE, cp, O_CMASK);break;case IODUP: /* >& */if (CheckForRestrictedShell (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){PrintWarningMessage (LIT_3Strings, cp, "cannot ", 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 F_LOCAL EchoCurrentCommand (char **wp){int i;if ((CurrentFunction != (FunctionList *)NULL) && CurrentFunction->Traced)fprintf (stderr, "%s:", CurrentFunction->tree->str);feputs (GetVariableAsString (PS4, TRUE));for (i = 0; wp[i] != NOWORD; i++){if (i)feputc (CHAR_SPACE);feputs (wp[i]);}feputc (CHAR_NEW_LINE);}/** Set up the status on exit from a command*/static int F_LOCALSetCommandReturnStatus (int s){SetVariableFromNumeric (StatusVariable, (long) abs (ExitStatus = s));return s;}/** Execute a command*/intExecuteACommand(char **argv, int mode ){int RetVal;CheckProgramMode (*argv, &ExecProcessingMode);RetVal = EnvironExecute (argv, 0);if (ExecProcessingMode.Flags != EP_ENVIRON)RetVal = LocalExecve (argv, BuildCommandEnvironment (), mode);return RetVal;}/** PATH-searching interface to execve.*/static int F_LOCALLocalExecve(char **argv, char **envp, int ForkAction ){int res = -1; /* Result */char *p_name; /* Program name */int i;/* Clear the DosExec Failname field */#if (OS_TYPE == OS_OS2)*FailName = 0;#endif#if (OS_TYPE != OS_DOS)OS_DosExecPgmReturnCode = 0;#endif/* If the environment is null - It is too big - error */if (envp == NOWORDS)errno = E2BIG;else if ((p_name = AllocateMemoryCell (FFNAME_MAX)) == (char *)NULL)errno = ENOMEM;else{/* Start off on the search path for the executable file */switch (i = FindLocationOfExecutable (p_name, argv[0])){case EXTENSION_EXECUTABLE:res = ExecuteProgram (p_name, argv, envp, ForkAction);break;/* Script file */case EXTENSION_BATCH:case EXTENSION_SHELL_SCRIPT:res = ExecuteScriptFile (p_name, argv, envp, ForkAction,(bool)(i == EXTENSION_SHELL_SCRIPT));break;}if (res != -1){TrackAllCommands (p_name, *argv);return res;}}/* If no fork - exit */if (ForkAction & EXEC_WITHOUT_FORK){PrintLoadError (*argv);FinalExitCleanUp (-1);}return -1;}/** Exec or spawn the program ?*/static int F_LOCALExecuteProgram (char *path, char **parms, char **envp, int ForkAction){int res;/* Check we have access to the file */if (!S_access (path, F_OK))return SetCommandReturnStatus (-1);/* Process the command line */res = BuildCommandLine (path, parms, envp, ForkAction);SetWindowName ((char *)NULL);ClearExtendedLineFile ();return SetCommandReturnStatus (res);}/* 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*/static int F_LOCALBuildCommandLine (char *path, char **argv, char **envp, int ForkAction){char **pl = argv;FILE *fd;bool found;char *new_args[3];char cmd_line[FFNAME_MAX];/* Translate process name to MSDOS format */if ((GenerateFullExecutablePath (path) == (char *)NULL) ||(!GetApplicationType (path)))return -1;/* If this is a windows program - special */#if (OS_TYPE != OS_UNIX)if ((ApplicationType == EXETYPE_DOS_GUI) && (BaseOS != BASE_OS_NT))return ExecuteWindows (path, argv, envp, ForkAction);#endif/* Extended command line processing */Extend_file = (char *)NULL; /* Set no file */found = C2bool ((ExecProcessingMode.Flags & EP_UNIXMODE) ||(ExecProcessingMode.Flags & EP_DOSMODE));/* Set up a blank command line */cmd_line[0] = 0;cmd_line[1] = CHAR_RETURN;/* If there are no parameters, or they fit in the DOS command line* - start the process */if ((*(++pl) == (char *)NULL) || CheckParameterLength (pl))return StartTheProcess (path, argv, envp, ForkAction);/* If we can use an alternative approach - indirect files, use it */else if (found){char **pl1 = pl;/* Check parameters don't contain a re-direction parameter */while (*pl1 != NOWORD){if (**(pl1++) == CHAR_INDIRECT){if (found && !FL_TEST (FLAG_WARNING))PrintWarningMessage ("sh: cannot exec, existing redirect(s) %c located within arg list.", CHAR_INDIRECT );found = FALSE;errno = E2BIG;break;}}/* If we find it - create a temporary file and write the stuff */if ((found) &&((fd = FOpenFile (Extend_file = GenerateTemporaryFileName (),sOpenWriteMode)) != (FILE *)NULL)){if (FL_TEST(FLAG_DEBUG_EXECUTE))fprintf (stderr, "Creating temporary file (%s)\n", Extend_file);if ((Extend_file = StringSave (Extend_file)) == null)Extend_file = (char *)NULL;/* Copy to end of list */do {if (!WriteToExtendedFile (fd, *pl))return -1;} while (*(pl++) != NOWORD);/* Set up cmd_line[1] to contain the filename */memset (cmd_line, 0, FFNAME_MAX);cmd_line[1] = CHAR_SPACE;cmd_line[2] = CHAR_INDIRECT;strcpy (&cmd_line[3], Extend_file);cmd_line[0] = (char)(strlen (Extend_file) + 2);/* If the name in the file is in upper case - use \ for separators */if (ExecProcessingMode.Flags & EP_DOSMODE)PATH_TO_DOS (&cmd_line[2]);/* OK we are ready to execute */new_args[0] = *argv;new_args[1] = &cmd_line[2];new_args[2] = (char *)NULL;return StartTheProcess (path, new_args, envp, ForkAction);}}return -1;}/** Clear Extended command line file*/void ClearExtendedLineFile (void){if (Extend_file != (char *)NULL){if (!FL_TEST(FLAG_DEBUG_EXECUTE)) /* leave temporary images? */unlink (Extend_file);ReleaseMemoryCell ((void *)Extend_file);}Extend_file = (char *)NULL;}/** Convert the executable path to the full path name*/char *GenerateFullExecutablePath (char *path){char cpath[PATH_MAX + 6];char npath[FFNAME_MAX + 2];char n1path[PATH_MAX + 6];char *p;int drive;PATH_TO_UPPER_CASE (path);/* Get the current path */S_getcwd (cpath, 0);strcpy (npath, cpath);/* In current directory ? */if ((p = FindLastPathCharacter (path)) == (char *)NULL){p = path;/* Check for a:program case */if (IsDriveCharacter (*(p + 1))){p += 2;/* Get the path of the other drive */S_getcwd (npath, GetDriveNumber (*path));}}/* In root directory */else if ((p - path) == 0){++p;strcpy (npath, RootDirectory);*npath = *path;*npath = *cpath;}else if (((p - path) == 2) && IsDriveCharacter (*(path + 1))){++p;strcpy (npath, RootDirectory);*npath = *path;}/* Find the directory */else{*(p++) = 0;/* Change to the directory containing the executable */drive = IsDriveCharacter (*(path + 1))? GetDriveNumber (*path)#if (OS_TYPE == OS_OS2) && !defined (__WATCOMC__): _getdrive ();#else: 0;#endif/* Save the current directory on this drive */S_getcwd (n1path, drive);/* Find the directory we want */if (!S_chdir (path))return (char *)NULL;S_getcwd (npath, drive); /* Save its full name */S_chdir (n1path); /* Restore the original *//* Restore our original directory */if (!S_chdir (cpath))return (char *)NULL;}if (!IsPathCharacter (npath[strlen (npath) - 1]))strcat (npath, DirectorySeparator);strcat (npath, p);p = PATH_TO_DOS (strcpy (path, npath));return p;}/** Find the number of values to use for a for or select statement*/static char ** F_LOCALFindNumberOfValues (char **wp, int *Count){/* select/for x do...done - use the parameter values. Need to know how many as* it is not a NULL terminated array*/if (wp == NOWORDS){if ((*Count = ParameterCount) < 0)*Count = 0;return ParameterArray + 1;}/* select/for x in y do...done - find the start of the variables and* use them all*/*Count = CountNumberArguments (wp);return wp;}/** Count the number of entries in an array*/int CountNumberArguments (char **wp){int Count = 0;while (*(wp++) != NOWORD)Count++;return Count;}/** Write a command string to the extended file*/static bool F_LOCALWriteToExtendedFile (FILE *fd, char *string){char *sp = string;char *cp = string;bool WriteOk = TRUE;if (string == (char *)NULL){if (CloseFile (fd) != EOF)return TRUE;WriteOk = FALSE;}else if (strlen (string)){/* Write the string, converting newlines to backslash newline */while (WriteOk && (cp != (char *)NULL)){if ((cp = strchr (sp, CHAR_NEW_LINE)) != (char *)NULL)*cp = 0;if (fputs (sp, fd) == EOF)WriteOk = FALSE;else if (cp != (char *)NULL)WriteOk = C2bool (fputs ("\\\n", fd) != EOF);sp = cp + 1;}}if (WriteOk && (fputc (CHAR_NEW_LINE, fd) != EOF))return TRUE;CloseFile (fd);ClearExtendedLineFile ();errno = ENOSPC;return FALSE;}/** Execute or spawn the process*//* DOS, 16/32 bit version */#if (OS_TYPE == OS_DOS)static int F_LOCALStartTheProcess(char *path, char **argv, char **envp, int ForkAction ){int retval;__systeml_sh = 1; /* 27/09/00 */if (FL_TEST(FLAG_DEBUG_EXECUTE))__systeml_debug = 1; /* 25/09/00 */if (ForkAction & EXEC_WITHOUT_FORK){DumpHistory (); /* Exec - save history */if ((retval = long_execvpe(path, argv, envp)) != -1)exit (retval);PrintErrorMessage ("unexpected return from exec (%d)", errno);return retval;}return long_spawnvpe(path, argv, envp);}#endif/* OS/2 Version */#if (OS_TYPE == OS_OS2)static int F_LOCALStartTheProcess (char *path, char **argv, char **envp, int ForkAction){int RetVal;STARTDATA stdata;/* Is this a start session option */if (SessionControlBlock != (STARTDATA *)NULL)return StartTheSession (SessionControlBlock, path, argv, envp,ForkAction);/* Exec ? */if (ForkAction & EXEC_WITHOUT_FORK)return OS_DosExecProgram (OLD_P_OVERLAY, path, argv, envp);if (ForkAction & (EXEC_SPAWN_DETACH | EXEC_SPAWN_NOWAIT |EXEC_SPAWN_IGNOREWAIT)){int Mode = (ForkAction & EXEC_SPAWN_DETACH)? P_DETACH: ((ForkAction & EXEC_SPAWN_IGNOREWAIT)? P_NOWAITO: (FL_TEST (FLAG_SEPARATE_GROUP)? P_DETACH: P_NOWAIT));RetVal = OS_DosExecProgram (Mode, path, argv, envp);/* Remove the reference to the temporary file for background tasks */ReleaseMemoryCell ((void *)Extend_file);Extend_file = (char *)NULL;if ((RetVal != -1) && (Mode != P_NOWAITO)){if (InteractiveFlag && (source->type == STTY)){if (ForkAction & EXEC_SPAWN_DETACH)fprintf (stderr, "[0] Process %d detached\n", RetVal);else{PidInfo.Valid = TRUE;PidInfo.JobNo = AddNewJob (RetVal, 0, path);PidInfo.PidNo = RetVal;}}SetVariableFromNumeric ("!", RetVal);return 0;}return RetVal;}/* In OS/2, we need the type of the program because PM programs have to be* started in a session (or at least that was the only way I could get them* to work).*//* Do we need to start a new session for this program ? */if (((ApplicationType & EXETYPE_OS2_TYPE) == EXETYPE_OS2_GUI) ||(ApplicationType & EXETYPE_DOS)){if ((OS_VERS_N > 1) && (ApplicationType & EXETYPE_DOS))memcpy (&stdata, &DOS_SessionControlBlock, sizeof (STARTDATA));elsememcpy (&stdata, &PM_SessionControlBlock, sizeof (STARTDATA));if (ForkAction & EXEC_WINDOWS)stdata.PgmControl = 0;return StartTheSession (&stdata, path, argv, envp, ForkAction);}/* Just DosExecPgm it */return OS_DosExecProgram (P_WAIT, path, argv, envp);}#endif/* NT Version */#if (OS_TYPE == OS_NT)static int F_LOCALStartTheProcess (char *path, char **argv, char **envp, int ForkAction ){int RetVal;/* Exec ? */if (ForkAction & EXEC_WITHOUT_FORK)return OS_DosExecProgram (OLD_P_OVERLAY, path, argv, envp);if (ForkAction & (EXEC_SPAWN_DETACH | EXEC_SPAWN_NOWAIT |EXEC_SPAWN_IGNOREWAIT)){int Mode = (ForkAction & EXEC_SPAWN_DETACH)? P_DETACH: ((ForkAction & EXEC_SPAWN_IGNOREWAIT)? P_NOWAITO: (FL_TEST (FLAG_SEPARATE_GROUP)? P_DETACH: P_NOWAIT));RetVal = OS_DosExecProgram (Mode, path, argv, envp);/* Remove the reference to the temporary file for background tasks */ReleaseMemoryCell ((void *)Extend_file);Extend_file = (char *)NULL;if ((RetVal != -1) && (Mode != P_NOWAITO)){if (InteractiveFlag && (source->type == STTY)){if (ForkAction & EXEC_SPAWN_DETACH)fprintf (stderr, "[0] Process %d detached\n", RetVal);else{PidInfo.Valid = TRUE;PidInfo.JobNo = AddNewJob (RetVal, 0, path);PidInfo.PidNo = RetVal;}}SetVariableFromNumeric ("!", RetVal);return 0;}elsereturn RetVal;}/* Just DosExecPgm it */return OS_DosExecProgram (P_WAIT, path, argv, envp);}#endif/** Start a session*/#if (OS_TYPE == OS_OS2)static int F_LOCALStartTheSession(STARTDATA *SessionData, char *path,char **argv, char **envp, int ForkAction ){OSCALL_PARAM usType;OSCALL_PARAM idSession;# if (OS_SIZE == OS_32)PID pid;# elseUSHORT pid;# endif/** For OS/2 2.x, we can start DOS sessions!!*/if ((OS_VERS_N > 1) && (ApplicationType & EXETYPE_DOS)){if ((ForkAction & EXEC_WINDOWS) ||(SessionData->SessionType == SSF_TYPE_FULLSCREEN))SessionData->SessionType = SSF_TYPE_VDM;elseSessionData->SessionType = SSF_TYPE_WINDOWEDVDM;if (SessionData->Environment == (PBYTE)1)SessionData->Environment = (PBYTE)NULL;}else if ((ApplicationType & EXETYPE_OS2_TYPE) == EXETYPE_OS2_GUI)SessionData->SessionType = SSF_TYPE_PM;SessionData->PgmName = path;SessionData->TermQ = SessionEndQName; /* Queue name *//** Build the environment if the current value is 1*/if ((SessionData->Environment == (PBYTE)1) &&((SessionData->Environment = (PBYTE)BuildOS2String (envp, 0))== (PBYTE)NULL))return -1;ProcessSpaceInParameters (argv);if ((SessionData->PgmInputs = (PBYTE)BuildOS2String (&argv[1], CHAR_SPACE))== (PBYTE)NULL)return -1;/** Start the session. Do not record if it is independent*/DISABLE_HARD_ERRORS;# if (OS_SIZE == OS_16) && !defined (OS2_16_BUG)SessionData->Related = SSF_RELATED_INDEPENDENT;# endifusType = DosStartSession (SessionData, &idSession, &pid);ENABLE_HARD_ERRORS;if ((!usType) || (usType == ERROR_SMG_START_IN_BACKGROUND)){if (InteractiveFlag && (source->type == STTY)){if (SessionData->Related == SSF_RELATED_INDEPENDENT)fprintf (stderr, "[0] Independent Session %d started\n",idSession);elsefprintf (stderr, "[%d] Session %d (PID %d) started\n",AddNewJob (pid, idSession, path), idSession, pid);if (usType)fprintf (stderr, "%s\n", GetOSSystemErrorMessage (usType));}SetVariableFromNumeric ("!", pid);return 0;}else{strcpy (FailName, GetOSSystemErrorMessage (usType));errno = ENOENT;return -1;}}#endif/** Build the OS2 format <value>\0<value>\0 etc \0*/char *BuildOS2String (char **Array, char sep){int i = 0;int Length = 0;char *Output;char *sp, *cp;/* Find the total data length */while ((sp = Array[i++]) != NOWORD)Length += strlen (sp) + 1;Length += 2;if ((Output = AllocateMemoryCell (Length)) == (char *)NULL)return (char *)NULL;/* Build the new string */i = 0;sp = Output;/* Build the string */while ((cp = Array[i++]) != NOWORD){while ((*sp = *(cp++)) != 0)++sp;if (!sep || (Array[i] != NOWORD))*(sp++) = sep;}*sp = 0;return Output;}/** List of default known extensions and their type*/#define MAX_DEFAULT_EXTENSIONS ARRAY_SIZE (DefaultExtensions)static struct ExtType {char *Extension;char Type;} DefaultExtensions [] = {{ null, EXTENSION_SHELL_SCRIPT },{ SHELLExtension, EXTENSION_SHELL_SCRIPT },{ KSHELLExtension, EXTENSION_SHELL_SCRIPT },#if (OS_TYPE != OS_UNIX){ BATExtension, EXTENSION_BATCH },{ EXEExtension, EXTENSION_EXECUTABLE },{ COMExtension, EXTENSION_EXECUTABLE },#endif};/** Built the list of valid extensions*/voidBuildExtensionLists (void){Word_B *DList = (Word_B *)NULL;Word_B *FList = (Word_B *)NULL;int i;char *pe;char *sp;void (_SIGDECL *save_signal)(int);/* Disable signals */save_signal = signal (SIGINT, SIG_IGN);/* Release Old memory */if (ExecuteFunctionList != (char **)NULL){ReleaseMemoryCell (*ExecuteFunctionList);ReleaseMemoryCell (ExecuteFunctionList);}if (ExecutableList != (char **)NULL)ReleaseMemoryCell (ExecutableList);/* No extensions ? */if ((pe = GetVariableAsString (PathExtsLiteral, FALSE)) == null){for (i = 0; i < MAX_DEFAULT_EXTENSIONS; i++)DList = AddWordToBlock (DefaultExtensions [i].Extension, DList);ExecutableList = GetWordList (AddWordToBlock ((char *)NULL, DList));ExecuteFunctionList = (char **)NULL;/* Restore signals */signal (SIGINT, save_signal);return;}/* OK - scan */pe = StringSave (pe);/* Split out on semi-colons */do{if ((sp = strchr (pe, ';')) != (char *)NULL)*(sp++) = 0;FList = AddWordToBlock (pe, FList);/* Build the new order of default extensions */for (i = 0; i < MAX_DEFAULT_EXTENSIONS; i++){if (!NOCASE_COMPARE (pe, DefaultExtensions [i].Extension)){DList = AddWordToBlock (pe, DList);break;}}pe = sp;} while (pe != (char *)NULL);/* Set up the two lists */ExecutableList = GetWordList (AddWordToBlock ((char *)NULL, DList));ExecuteFunctionList = GetWordList (AddWordToBlock ((char *)NULL, FList));/* Restore signals */signal (SIGINT, save_signal);}/** Find the location of an executable and return it's full path* name*/intFindLocationOfExecutable (char *FullPath, char *name){return ExtensionType (FindFileAndExtension (FullPath, name, ExecutableList));}/** Return the extension type for an extension*/static int F_LOCALExtensionType (char *ext){int i;if (ext == (char *)NULL)return EXTENSION_NOT_FOUND;for (i = 0; i < MAX_DEFAULT_EXTENSIONS; i++){if (!NOCASE_COMPARE (ext, DefaultExtensions [i].Extension))return (int)(DefaultExtensions [i].Type);}return EXTENSION_OTHER;}/** Search the path for a file with the appropriate extension** Returns a pointer to the extension.*/static char * F_LOCALFindFileAndExtension (char *FullPath, char *name, char **Extensions ){char *sp; /* Path pointers */char *ep;char *xp; /* In file name pointers */char *xp1;int i, fp;int pathlen;/* No extensions - no file */if (Extensions == (char **)NULL){errno = ENOENT;return (char *)NULL;}if (FL_TEST(FLAG_DEBUG_EXECUTE)){fprintf (stderr, "Searching for: %s (", name);for (i = 0; Extensions[i] != (char *)NULL; i++)fprintf (stderr, "%s%s", (i ? " " : ""),(Extensions[i][0] ? Extensions[i] : "."));fprintf (stderr, ") using\n");fprintf (stderr, " PATH=%s\n", GetVariableAsString (PathLiteral, FALSE) );}/* Scan the path for an executable */sp = ((FindPathCharacter (name) != (char *)NULL) ||(IsDriveCharacter (*(name + 1))))? null : GetVariableAsString (PathLiteral, FALSE);do{sp = BuildNextFullPathName (sp, name, FullPath);ep = &FullPath[pathlen = strlen (FullPath)];if (FL_TEST(FLAG_DEBUG_EXECUTE))fprintf (stderr, " Check: %s\n", FullPath);/* Get start of file name */if ((xp1 = FindLastPathCharacter (FullPath)) == (char *)NULL)xp1 = FullPath;else++xp1;/* Look up all 5 types */for (i = 0; Extensions[i] != (char *)NULL; i++){if (pathlen + strlen (Extensions[i]) > (size_t)(FFNAME_MAX - 1))continue;strcpy (ep, Extensions[i]);if (S_access (FullPath, F_OK)){/* If no extension, .ksh or .sh extension, check for shell script */if (((xp = strrchr (xp1, CHAR_PERIOD)) == (char *)NULL) ||(NOCASE_COMPARE (xp, SHELLExtension) == 0) ||(NOCASE_COMPARE (xp, KSHELLExtension) == 0)){if ((fp = CheckForScriptFile (FullPath, (char **)NULL,(int *)NULL)) < 0) {if (FL_TEST(FLAG_DEBUG_EXECUTE))fprintf (stderr, "Unable to determine type\n" );continue;}S_close (fp, TRUE);if (FL_TEST(FLAG_DEBUG_EXECUTE) || (debug_level & DEBUG_SHOW_ARGS))fprintf (stderr, " Match: script %s\n", FullPath);return (xp == (char *)NULL) ? null : xp;}if (FL_TEST(FLAG_DEBUG_EXECUTE) || (debug_level & DEBUG_SHOW_ARGS))fprintf (stderr, " Match: binary %s\n", FullPath);return xp;}}} while (sp != (char *)NULL);/* Not found */errno = ENOENT;return (char *)NULL;}/** Execute a script file*/static int F_LOCALExecuteScriptFile (char *Fullpath, char **argv, char **envp, int ForkAction, bool ShellScript){int res; /* Result */char *params = 0; /* Script parameters */int nargc = 0; /* # script args */Word_B *wb = (Word_B *)NULL;#if (OS_TYPE == OS_DOS) && (OS_SIZE == OS_16)union REGS r;#endif/* Batfile - convert to DOS Format file name */if (!ShellScript){PATH_TO_DOS (Fullpath);nargc = 0;}else if ((res = OpenForExecution (Fullpath, ¶ms, &nargc)) >= 0)S_close (res, TRUE);else{errno = ENOENT;return -1;}/* If BAT file, use command.com else use sh */if (!ShellScript){if (!SetUpCLI (GetVariableAsString (ComspecVariable, FALSE), &wb))return -1;wb = AddWordToBlock ("/c", wb);/* Get the switch character */#if (OS_TYPE == OS_DOS) && (OS_SIZE == OS_16)r.x.REG_AX = 0x3700;DosInterrupt (&r, &r);if ((r.h.al == 0) && (_osmajor < 4))*(wb->w_words[wb->w_nword - 1]) = (char)(r.h.dl);#endif}/* Stick in the pre-fix arguments */else if (nargc)wb = SplitString (params, wb);else if (params != null)wb = AddWordToBlock (params, wb);elsewb = AddWordToBlock (GetVariableAsString (ShellVariableName, FALSE),wb);/* Add the rest of the parameters, and execute */res = ExecuteSpecialProcessor (Fullpath, argv, envp, ForkAction, wb);/* Release allocated space */if (params != null)ReleaseMemoryCell ((void *)params);return res;}/** Convert errno to error message on execute*/static char * F_LOCALConvertErrorNumber (void){switch (errno){case ENOMEM:return strerror (ENOMEM);case ENOEXEC:return "program corrupt";case E2BIG:return AE2big;case ENOENT:return NotFound;case 0:return "No Shell";}return "cannot execute";}/** Convert UNIX format lines to DOS format if appropriate.* Build Environment variable for some programs.*/static int F_LOCALEnvironExecute (char **argv, int ForkAction){int s_errno;int RetVal = 1;char *NewArgs[3];char *cp;/* If this command does not pass the command string in the environment,* no action required*/if (ExecProcessingMode.Flags != EP_ENVIRON)return 0;if ((cp = BuildOS2String (&argv[1], ExecProcessingMode.FieldSep))== (char *)NULL){ExecProcessingMode.Flags = EP_NONE;return 0;}SetVariableFromString (ExecProcessingMode.Name, cp);SetVariableStatus (ExecProcessingMode.Name, STATUS_EXPORT);/* Build and execute the environment */NewArgs[0] = argv[0];NewArgs[1] = ExecProcessingMode.Name;NewArgs[2] = (char *)NULL;RetVal = LocalExecve (NewArgs, BuildCommandEnvironment (), ForkAction);s_errno = errno;UnSetVariable (ExecProcessingMode.Name, -1, FALSE);errno = s_errno;return RetVal;}/** Check Parameter line length** Under OS2, we don't build the command line. Just check it.*/static bool F_LOCALCheckParameterLength (char **argv){int CmdLineLength;char *CommandLine;bool RetVal = FALSE;char **SavedArgs = argv;int i;/* Check for special case. If there are any special characters and we can* use UNIX mode, use it*/if (FL_TEST(FLAG_DEBUG_EXECUTE) || (debug_level & DEBUG_SHOW_ARGS)){fprintf (stderr, " Mode: ");PrintProgramMode (&ExecProcessingMode);fprintf (stderr, " args: Raw dump ...\n");for (i = 0; argv[i]; i++){fprintf (stderr, " args: [%02d] %s.\n", i, argv[i]);}}if (ExecProcessingMode.Flags & EP_UNIXMODE){for (i = 0; argv[i]; i++)if (strpbrk (argv[i], WildCards) != (char *)NULL){if (FL_TEST(FLAG_DEBUG_EXECUTE))fprintf (stderr, " args: contains wild cards @%02d.\n",i);return FALSE;}}#if (OS_TYPE == OS_NT)if (ApplicationType & EXETYPE_NT){if (FL_TEST(FLAG_DEBUG_EXECUTE))fprintf (stderr, " args: NT target.\n");return TRUE; /* APY, 05/05/04 */}#endif/** Do any parameter conversion - adding quotes or backslashes, but don't* update argv.*/CmdLineLength = CountNumberArguments (argv = SavedArgs);if ((SavedArgs = (char **)AllocateMemoryCell ((CmdLineLength + 1) * sizeof (char *)))== (char **)NULL)return FALSE;/* Save a copy of the argument addresses */memcpy (SavedArgs, argv, (CmdLineLength + 1) * sizeof (char *));/* Build the command line */if ((CommandLine = BuildOS2String (ProcessSpaceInParameters (SavedArgs),CHAR_SPACE)) == (char *)NULL){ReleaseMemoryCell (SavedArgs);return FALSE;}/** Check command line length. Under DOS, this is simple. On OS/2, we have* to remember that we default to the EMX interface, which requires* twice as much space, plus some nulls. Also remember start DOS programs* on OS/2 or NT have restricted space.** HACK: When a DOS extender assume 1/3 the command line* is needed by the stub (eg DOS4G).*/CmdLineLength = strlen (CommandLine);if ((ApplicationType & EXETYPE_DOS) &&((CmdLineLength >= CMD_LINE_MAX - 2) ||((ExecProcessingMode.Flags & EP_DOSEXT) && CmdLineLength >= (CMD_LINE_MAX/3*2))) ){errno = E2BIG;}#if (OS_TYPE == OS_OS2)else if (CmdLineLength >= (((CMD_LINE_MAX - 2) / 2) +CountNumberArguments (SavedArgs) + 3)){errno = E2BIG;}#elif (OS_TYPE == OS_NT)else if (CmdLineLength >= CMD_LINE_MAX - 2){errno = E2BIG;}#endif/* Terminate the line */else{RetVal = TRUE;}if (!RetVal && FL_TEST(FLAG_DEBUG_EXECUTE))fprintf (stderr, " args: length exceeds command line.\n");ReleaseMemoryCell (SavedArgs);ReleaseMemoryCell (CommandLine);return RetVal;}/** Convert any parameters with spaces in the to start and end with double* quotes.** Under OS2, the old string is NOT released.*/static char ** F_LOCALProcessSpaceInParameters (char **argv){char **Start = argv;char *new;char *cp;char *sp;int Count;/* If noquote set, don't even try */if (ExecProcessingMode.Flags & EP_NOQUOTE)return Start;/* Protect parameters with TABS */while (*argv != (char *)NULL){if ((strpbrk (*argv, " \t") != (char *)NULL) ||(strlen (*argv) == 0) ||((ExecProcessingMode.Flags & EP_QUOTEWILD) &&(strpbrk (*argv, WildCards) != (char *)NULL))){/* Count number of Double quotes in the parameter */Count = CountDoubleQuotes (*argv);/* Get some memory - give up update if out of memory */if ((new = GetAllocatedSpace (strlen (*argv) + (Count * 2) +3)) == (char *)NULL)return Start;SetMemoryAreaNumber ((void *)new,GetMemoryAreaNumber ((void *)*argv));*new = CHAR_DOUBLE_QUOTE;/* Escape any double quotes in the string */cp = *argv;sp = new + 1;while (*cp){if (*cp == CHAR_DOUBLE_QUOTE){*(sp++) = CHAR_META;*(sp++) = *(cp++);}else if (*cp != CHAR_META)*(sp++) = *(cp++);/* Handle escapes - count them */else{*(sp++) = *(cp++);if (*cp == CHAR_DOUBLE_QUOTE){*(sp++) = CHAR_META;*(sp++) = CHAR_META;}else if (*cp == 0){*(sp++) = CHAR_META;break;}*(sp++) = *(cp++);}}/* Append the terminating double quotes */strcpy (sp, DoubleQuotes);*argv = new;}/* Check for any double quotes */else if ((Count = CountDoubleQuotes (*argv)) != 0){/* Got them - escape them */if ((new = GetAllocatedSpace (strlen (*argv) + Count + 1))== (char *)NULL)return Start;SetMemoryAreaNumber ((void *)new,GetMemoryAreaNumber ((void *)*argv));/* Copy the string, escaping DoubleQuotes */cp = *argv;sp = new;while ((*sp = *(cp++)) != 0){if (*sp == CHAR_DOUBLE_QUOTE){*(sp++) = CHAR_META;*sp = CHAR_DOUBLE_QUOTE;}sp++;}*argv = new;}/* Next parameter */argv++;}return Start;}/** Count DoubleQuotes*/static int F_LOCALCountDoubleQuotes (char *string){int Count = 0;while ((string = strchr (string, CHAR_DOUBLE_QUOTE)) != (char *)NULL){Count++;string++;}return Count;}/** Save and Restore the Parameters array ($1, $2 etc)*/static void F_LOCALSaveNumericParameters (char **wp, SaveParameters *SaveArea){SaveArea->Array = ParameterArray;SaveArea->Count = ParameterCount;ParameterArray = wp;for (ParameterCount = 0; ParameterArray[ParameterCount] != NOWORD;++ParameterCount)continue;SetVariableFromNumeric (ParameterCountVariable, (long)--ParameterCount);}static void F_LOCALRestoreTheParameters (SaveParameters *SaveArea){ParameterArray = SaveArea->Array;ParameterCount = SaveArea->Count;SetVariableFromNumeric (ParameterCountVariable, (long)ParameterCount);}/** Special OS/2 processing for execve and spawnve*/#if (OS_TYPE == OS_OS2)static int F_LOCALOS_DosExecProgram (int Mode, char *Program, char **argv, char **envp){OSCALL_PARAM fExecFlags;void (_SIGDECL *sig_int)(); /* Interrupt signal */RESULTCODES rescResults;PID pidProcess;PID pidWait;char *OS2Environment;char *OS2Arguments;char **SavedArgs;int argc;/* Set the error module to null */*FailName = 0;OS_DosExecPgmReturnCode = 0;/* Convert spawn mode to DosExecPgm mode */switch (Mode){case P_WAIT:case P_NOWAIT:fExecFlags = EXEC_ASYNCRESULT;break;case P_NOWAITO:case OLD_P_OVERLAY:fExecFlags = EXEC_ASYNC;break;case P_DETACH:fExecFlags = EXEC_BACKGROUND;break;}/* Build OS/2 argument string** 1. Count the number of arguments.* 2. Add 2 for: 1 - NULL; 2 - argv[0]; 3 - the stringed arguments* 3. save copy of arguments at offset 2.* 4. On original argv, process white space and convert to OS2 Argument string.* 5. Set up program name at offset 2 as ~argv* 6. Convert zero length args to "~" and args beginning with ~ to ~~* 7. Build OS2 Argument string (at last).*/argc = CountNumberArguments (argv);if ((SavedArgs = (char **)AllocateMemoryCell ((argc + 3) * sizeof (char *)))== (char **)NULL)return -1;memcpy (SavedArgs + 2, argv, (argc + 1) * sizeof (char *));/* Set program name at Offset 0 */SavedArgs[0] = *argv;/* Build OS2 Argument string in Offset 1 */if ((SavedArgs[1] = BuildOS2String (ProcessSpaceInParameters (&argv[1]),CHAR_SPACE)) == (char *)NULL)return -1;/* Set up the new arg 2 - ~ + programname */if ((SavedArgs[2] = InsertCharacterAtStart (*argv)) == (char *)NULL)return -1;/* Convert zero length args and args starting with a ~ */for (argc = 3; SavedArgs[argc] != NOWORD; argc++){if (strlen (SavedArgs[argc]) == 0)SavedArgs[argc] = "~";else if ((*SavedArgs[argc] == CHAR_TILDE) &&((SavedArgs[argc] = InsertCharacterAtStart (SavedArgs[argc]))== (char *)NULL))return -1;}/* Build the full argument list */if ((OS2Arguments = BuildOS2String (SavedArgs, 0)) == (char *)NULL)return -1;/* Build OS/2 environment string */if ((OS2Environment = BuildOS2String (envp, 0)) == (char *)NULL)return -1;/* Change signal handling */if (fExecFlags == EXEC_ASYNCRESULT){char *cp;/* Also change the window title to match the foreground program */if ((cp = strrchr (Program, CHAR_DOS_PATH)) == (char *)NULL)cp = Program;elsecp++;strcpy (FailName, cp);if ((cp = strrchr (FailName, CHAR_PERIOD)) != (char *)NULL)*cp = 0;SetWindowName (FailName);*FailName = 0;IgnoreInterrupts = TRUE;sig_int = signal (SIGINT, SIG_IGN);}/* Exec it - with hard-error processing turned off */DISABLE_HARD_ERRORS;OS_DosExecPgmReturnCode = DosExecPgm (FailName, sizeof (FailName),fExecFlags, OS2Arguments,OS2Environment, &rescResults,Program);ENABLE_HARD_ERRORS;if (fExecFlags == EXEC_ASYNCRESULT){signal (SIGINT, SIG_IGN);/* If the process started OK, wait for it */if (((OS_DosExecPgmReturnCode == NO_ERROR) ||(OS_DosExecPgmReturnCode == ERROR_INTERRUPT)) &&(Mode == P_WAIT)){pidWait = rescResults.codeTerminate;/* Re-try on interrupted system calls - and kill the child. Why, because* sometimes the kill does not go to the child*/while ((OS_DosExecPgmReturnCode = DosCwait (DCWA_PROCESSTREE,DCWW_WAIT,&rescResults,&pidProcess,pidWait))== ERROR_INTERRUPT)DosKillProcess (DKP_PROCESS, pidWait);}IgnoreInterrupts = FALSE;signal (SIGINT, sig_int);}/** What happened ?. OS/2 Error - Map to UNIX errno. Why can't people* write libraries right?? Or provide access at a high level. We could* call _dosret if the interface did not require me to write more* assembler.*/if (OS_DosExecPgmReturnCode != 0){switch (OS_DosExecPgmReturnCode){#ifdef EAGAINcase ERROR_NO_PROC_SLOTS:errno = EAGAIN;return -1;#endifcase ERROR_NOT_ENOUGH_MEMORY:errno = ENOMEM;return -1;case ERROR_ACCESS_DENIED:case ERROR_DRIVE_LOCKED:case ERROR_LOCK_VIOLATION:case ERROR_SHARING_VIOLATION:errno = EACCES;return -1;case ERROR_FILE_NOT_FOUND:case ERROR_PATH_NOT_FOUND:case ERROR_PROC_NOT_FOUND:errno = ENOENT;return -1;case ERROR_BAD_ENVIRONMENT:case ERROR_INVALID_DATA:case ERROR_INVALID_FUNCTION:case ERROR_INVALID_ORDINAL:case ERROR_INVALID_SEGMENT_NUMBER:case ERROR_INVALID_STACKSEG:case ERROR_INVALID_STARTING_CODESEG:errno = EINVAL;return -1;case ERROR_TOO_MANY_OPEN_FILES:errno = EMFILE;return -1;case ERROR_INTERRUPT:case ERROR_NOT_DOS_DISK:case ERROR_SHARING_BUFFER_EXCEEDED:errno = EIO;return -1;case ERROR_BAD_ARGUMENTS:errno = E2BIG;return -1;default:errno = ENOEXEC;return -1;}}/* If exec - exit */switch (Mode){case OLD_P_OVERLAY: /* exec - exit at once */FinalExitCleanUp (0);case P_WAIT: /* Get exit code */return rescResults.codeResult;case P_NOWAIT: /* Get PID or SID */case P_NOWAITO:case P_DETACH:return rescResults.codeTerminate;}errno = EINVAL;return -1;}/** Insert character at start of string** Return NULL or new string*/static char *InsertCharacterAtStart (char *string){char *cp;if ((cp = (char *)AllocateMemoryCell (strlen (string) + 2)) == (char *)NULL)return (char *)NULL;*cp = CHAR_TILDE;strcpy (cp + 1, string);return cp;}#endif /*OS2*//** Special NT processing for execve and spawnve. Not really sure what to* do here yet!*/#if (OS_TYPE == OS_NT)static int F_LOCALOS_DosExecProgram (int Mode, char *Program, char **argv, char **envp ){#if (REMOVED)DWORD dwLogicalDrives = GetLogicalDrives();char szNewDrive[4];char *nt_ep;char *temp;unsigned int i;#endifchar **new_envp;char ldir[PATH_MAX + 6];int off = 0;STARTUPINFO StartupInfo;PROCESS_INFORMATION ProcessInformation;char *Environment;char *PgmInputs;BOOL rv;int ExitCode;OS_DosExecPgmReturnCode = 0;/** Set up NT directory variables*/if ((new_envp = (char **)GetAllocatedSpace (GetMemoryCellSize (envp) +sizeof (char *) * 26))== (char **)NULL){errno = ENOMEM;return -1;}#if (REMOVED)strcpy (szNewDrive, "x:");for (i = 0; i < 25; i++){if (dwLogicalDrives & (1L << i)){szNewDrive[0] = (char)(i + 'A');/* If the drive does not exist - give up */DISABLE_HARD_ERRORS;ExitCode = GetFullPathName (szNewDrive, PATH_MAX + 6, ldir, &temp);ENABLE_HARD_ERRORS;if (!ExitCode)continue;if ((nt_ep = GetAllocatedSpace (strlen (ldir) + 5)) == (char *)NULL){errno = ENOMEM;return -1;}sprintf (nt_ep, "=%c:=%s", i + 'A', ldir);new_envp[off++] = nt_ep;}}#endif /*REMOVED*/memcpy (&new_envp[off], envp, GetMemoryCellSize (envp));/* Setup environment block */if ((Environment = BuildOS2String (new_envp, 0)) == (char *)NULL)return -1;/* Setup parameter block */ProcessSpaceInParameters (argv);if (strchr(Program, ' ') == NULL || Program[0] == '"')argv[0] = Program; /* no spaces or quoted */else{ /* must quote .. i hate WIN */(void)sprintf( ldir, "\"%s\"", Program );argv[0] = ldir;}if ((PgmInputs = BuildOS2String (argv, CHAR_SPACE)) == (char *)NULL)return -1;/* Set up startup info */memset (&StartupInfo, 0, sizeof (StartupInfo));StartupInfo.cb = sizeof (StartupInfo);StartupInfo.lpDesktop = NULL;StartupInfo.lpTitle = NULL;StartupInfo.dwFlags = 0;/* Change Window name ? */if (Mode == P_WAIT){char *cp;char *Name;/* Also change the window title to match the foreground program */if ((cp = strrchr (Program, CHAR_DOS_PATH)) == (char *)NULL)cp = Program;elsecp++;Name = StringCopy (cp);if ((cp = strrchr (Name, CHAR_PERIOD)) != (char *)NULL)*cp = 0;SetWindowName (Name);ReleaseMemoryCell (Name);}/* Exec it - with hard-error processing turned off */if (FL_TEST(FLAG_DEBUG_EXECUTE) || (debug_level & DEBUG_CREATE_PROCESS) )fprintf (stderr, "CreateProcess(%s,%s)\n", Program, PgmInputs);DISABLE_HARD_ERRORS;rv = CreateProcess (Program, PgmInputs, NULL, NULL, TRUE,NORMAL_PRIORITY_CLASS,Environment, NULL,&StartupInfo, &ProcessInformation );ENABLE_HARD_ERRORS;if (!rv){OS_DosExecPgmReturnCode = GetLastError ();errno = ENOENT;return -1;}CloseHandle(ProcessInformation.hThread);if (Mode == P_WAIT){if (WaitForSingleObject(ProcessInformation.hProcess, INFINITE) == WAIT_OBJECT_0){DWORD exitcode = 0;GetExitCodeProcess(ProcessInformation.hProcess, &exitcode);ExitCode = (int) exitcode;return ExitCode;}else{OS_DosExecPgmReturnCode = GetLastError ();errno = EINVAL;ExitCode = -1;}CloseHandle(ProcessInformation.hProcess);return (ExitCode);}/* Otherwise, return the process ID. */return (ProcessInformation.dwProcessId);}#endif /*OS_NT*//** Execute a Function*/static bool F_LOCALExecuteFunction (char **wp, int *RetVal, bool CGVLCalled){Break_C *s_RList = Return_List;Break_C *s_BList = Break_List;Break_C *s_SList = SShell_List;Break_C BreakContinue;C_Op *New;FunctionList *s_CurrentFunction = CurrentFunction;SaveParameters s_Parameters;FunctionList *fop;GetoptsIndex GetoptsSave;bool TrapZeroExists;char *Ext;char *FullPath;TrapZeroExists = C2bool (GetVariableAsString ("~0", FALSE) != null);/* Find the extension type - if it exists */FullPath = GetAllocatedSpace (FFNAME_MAX);/* Check for a psuedo-function */switch (ExtensionType (Ext = FindFileAndExtension (FullPath,wp[0],ExecuteFunctionList))){case EXTENSION_OTHER:if ((fop = LookUpFunction (Ext, TRUE)) == (FunctionList *)NULL)return FALSE;wp[0] = FullPath;break;/* Common extension for all .sh, .ksh and "" scripts. If we find one, use* it, otherwise, check for a function of this name*/case EXTENSION_SHELL_SCRIPT:Ext = ".ksh";case EXTENSION_BATCH:if ((fop = LookUpFunction (Ext, TRUE)) != (FunctionList *)NULL){wp[0] = FullPath;break;}/* Check for a real function */case EXTENSION_NOT_FOUND:case EXTENSION_EXECUTABLE:default:if ((fop = LookUpFunction (wp[0], FALSE)) == (FunctionList *)NULL)return FALSE;}/* Ok, we really have a function to execute.* Save the current variable list*/if (!CGVLCalled && (CreateGlobalVariableList (FLAGS_FUNCTION) == -1)){*RetVal = -1;return TRUE;}/* Set up $0..$n for the function */SaveNumericParameters (wp, &s_Parameters);/* Save Getopts pointers */GetGetoptsValues (&GetoptsSave);/* Process the function */if (setjmp (BreakContinue.CurrentReturnPoint) == 0){CurrentFunction = fop;Break_List = (Break_C *)NULL;BreakContinue.NextExitLevel = Return_List;Return_List = &BreakContinue;New = CopyFunction (fop->tree->left);*RetVal = ExecuteParseTree (New, NOPIPE, NOPIPE, EXEC_FUNCTION);}/* 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*RetVal = (int)GetVariableAsNumeric (StatusVariable);if (!TrapZeroExists)RunTrapCommand (0); /* Exit trap *//* Restore the old $0, and previous return address */SaveGetoptsValues (GetoptsSave.Index, GetoptsSave.SubIndex);Break_List = s_BList;Return_List = s_RList;SShell_List = s_SList;CurrentFunction = s_CurrentFunction;RestoreTheParameters (&s_Parameters);return TRUE;}/** Print Load error message*/#if (OS_TYPE == OS_OS2)static void F_LOCAL PrintLoadError (char *path){if (*FailName)PrintWarningMessage ("%s: %s\nSYS1804: Cannot find file - %s", path,ConvertErrorNumber(), FailName);else if (OS_DosExecPgmReturnCode)PrintWarningMessage ("%s: %s\n%s", path, ConvertErrorNumber(),GetOSSystemErrorMessage (OS_DosExecPgmReturnCode));elsePrintWarningMessage ("%s: %s", path, ConvertErrorNumber());}#endif/* NT Version */#if (OS_TYPE == OS_NT)static void F_LOCAL PrintLoadError (char *path){if (OS_DosExecPgmReturnCode)PrintWarningMessage ("%s: %s\n%s", path, ConvertErrorNumber(),GetOSSystemErrorMessage (OS_DosExecPgmReturnCode));elsePrintWarningMessage ("%s: %s", path, ConvertErrorNumber());}#endif/* DOS Version */#if (OS_TYPE == OS_DOS)static void F_LOCAL PrintLoadError (char *path){PrintWarningMessage (BasicErrorMessage, path, ConvertErrorNumber ());}#endif/* DOS Version */#if (OS_TYPE == OS_UNIX)static void F_LOCAL PrintLoadError (char *path){PrintWarningMessage (BasicErrorMessage, path, ConvertErrorNumber ());}#endif/** Make the exported environment from the exported names in the dictionary.* Keyword assignments will already have been done. Convert to MSDOS* format if flag set and m enabled*/static char ** F_LOCALBuildCommandEnvironment (void){/* Update SECONDS and RANDOM */HandleSECONDandRANDOM ();/* Build the environment by walking the tree */BCE_WordList = (Word_B *)NULL;BCE_Length = 0;twalk (VariableTree, BuildEnvironmentEntry);if (BCE_Length >= 0x7f00)return (char **)NULL;return GetWordList (AddWordToBlock (NOWORD, BCE_WordList));}/** TWALK Function - Build Export VARIABLE list from VARIABLE tree*/static voidBuildEnvironmentEntry (const void *key, VISIT visit, int level){VariableList *vp = *(VariableList **)key;char *cp;char *sp;int tlen;(void) level;if ((visit != postorder) && (visit != leaf))return;if ((vp->status & STATUS_EXPORT) && (vp->index == 0)){cp = GetVariableAsString (vp->name, TRUE);tlen = strlen (vp->name) + strlen (cp) + 2;if ((BCE_Length += tlen) >= 0x7f00)return;strcpy ((sp = GetAllocatedSpace (tlen)), vp->name);SetMemoryAreaNumber ((void *)sp, MemoryAreaLevel);strcat (sp, "=");strcat (sp, cp);BCE_WordList = AddWordToBlock (sp, BCE_WordList);/* If MSDOS mode, we need to copy the variable, convert / to \ and put* the copy in the environment list instead*/if (((ShellGlobalFlags & FLAGS_MSDOS_FORMAT) ||(ExecProcessingMode.Flags & EP_EXPORT)) &&(vp->status & STATUS_CONVERT_MSDOS)){cp = StringCopy (BCE_WordList->w_words[BCE_WordList->w_nword - 1]);BCE_WordList->w_words[BCE_WordList->w_nword - 1] = PATH_TO_DOS (cp);}}}/** Parse and Execute a command in the current shell*/intRunACommand (Source *s, char **params){jmp_buf erp;int RetVal = -1;Break_C *S_RList = Return_List; /* Save loval links */Break_C *S_BList = Break_List;int LS_depth = Execute_stack_depth++;C_Op *outtree;bool s_ProcessingEXECCommand = ProcessingEXECCommand;SaveParameters s_Parameters;/* Create a new save area */MemoryAreaLevel++;/* Set up $0..$n for the command if appropriate. Note that $0 does not* change*/if (params != NOWORDS){SaveNumericParameters (params, &s_Parameters);ParameterArray[0] = s_Parameters.Array[0];}/* Execute the command */CreateNewEnvironment ();Return_List = (Break_C *)NULL;Break_List = (Break_C *)NULL;ProcessingEXECCommand = TRUE;e.ErrorReturnPoint = (ErrorPoint)NULL;if (SetErrorPoint (erp) == 0){/* Read Input until completed */for (;;){if (((outtree = BuildParseTree (s)) != (C_Op *)NULL) &&(outtree->type == TEOF))break;RetVal = ExecuteParseTree (outtree, NOPIPE, NOPIPE, 0);}}QuitCurrentEnvironment ();/* Restore the environment */ClearExtendedLineFile ();Return_List = S_RList;Break_List = S_BList;ProcessingEXECCommand = s_ProcessingEXECCommand;/* Restore $0..$n */if (params != NOWORDS)RestoreTheParameters (&s_Parameters);RestoreEnvironment (RetVal, LS_depth);ReleaseMemoryArea (MemoryAreaLevel--);return RetVal;}/** Get the OS/2 Error message*/#if (OS_TYPE == OS_OS2)char *GetOSSystemErrorMessage (OSCALL_RET code){static char Buffer [FFNAME_MAX + 9];char *Buffer1;OSCALL_PARAM MsgLength;OSCALL_RET rc;char *ip;char *op = Buffer;char *sp;int len;/* For some reason DPATH does not work with the DosGetMessage API as the* spec say it should on OS/2 2.x. Probably something we do. It usually* is! So emulate it!*/if ((len = strlen (sp = GetVariableAsString ("DPATH", FALSE))) < FFNAME_MAX)len = FFNAME_MAX;if ((Buffer1 = GetAllocatedSpace (len)) == (char *)NULL){sprintf (Buffer, "SYS%.4d: No memory to get message text", code);return Buffer;}sp = PATH_TO_UNIX (strcpy (Buffer1, sp));do{sp = BuildNextFullPathName (sp, "OSO001.MSG", Buffer);} while ((access (Buffer, F_OK) != 0) && (sp != (char *)NULL));/* If not found - use the default */if (sp == (char *)NULL){strcpy (Buffer, "c:/OS2/SYSTEM/OSO001.MSG");*Buffer = GetDriveLetter (GetRootDiskDrive ());}/* Read the message */if ((rc = DosGetMessage (NULL, 0, ip = Buffer1, len, code, Buffer,&MsgLength)))sprintf (Buffer, "SYS%.4d: No error message available (%d)",code, rc);else{if ((Buffer1[MsgLength - 1] == CHAR_NEW_LINE) &&(Buffer1[MsgLength - 2] == CHAR_RETURN))Buffer1[MsgLength - 2] = 0;elseBuffer1[MsgLength] = 0;/* Check the error number is there */if (strncmp (Buffer1, "SYS", 3) != 0){sprintf (op, "SYS%.4d: ", code);op += strlen (op);}/* Remove interior CRs & NLs */while ((*(op) = *(ip++))){if (*op == CHAR_NEW_LINE)*(op++) = CHAR_SPACE;else if (*op != CHAR_RETURN)op++;}}ReleaseMemoryCell (Buffer1);return Buffer;}# ifdef __WATCOMC__/** A cheat for WATCOM which does not have the DosGetMessage API. This* function is based an interpretation of the System message file and one or* two others. The format of the header is not know except for the flag at* offset 0x0f. It is not guaranteed to work.*/APIRET APIENTRYDosGetMessage(PCHAR *ppchVTable, ULONG usVCount, PCHAR pchBuf,ULONG cbBuf, ULONG usMsgNum, PSZ pszFileName, PULONG pcbMsg ){int fd;# pragma pack (1)union {struct {USHORT Start;USHORT End;} ShortE;struct {ULONG Start;ULONG End;} LongE;} Start, Current;# pragma pack ()char Type;int Len;unsigned long Offset;APIRET Res;if ((fd = open (pszFileName, O_RDONLY | O_BINARY)) < 0)return _doserrno;/* Get the message file format */if ((lseek (fd, 0x0fL, SEEK_SET) != 0x0fL) ||(read (fd, &Type, 1) != 1) ||(lseek (fd, 0x1fL, SEEK_SET) != 0x1fL)){Res = _doserrno;close (fd);return Res;}/* Read the start of message text location */Len = (Type) ? 4 : 8;if (read (fd, &Start, Len) != Len){Res = _doserrno;close (fd);return Res;}/* Check the offset to the message */Offset = 0x1fL + (usMsgNum * ((Type) ? 2L : 4L));if (((Type) && (Offset >= Start.ShortE.Start)) ||((!Type) && (Offset >= Start.LongE.Start))){close (fd);return ERROR_MR_MID_NOT_FOUND;}/* Get the message location */if ((lseek (fd, Offset, SEEK_SET) != Offset) ||(read (fd, &Current, Len) != Len)){Res = _doserrno;close (fd);return Res;}if (((Type) && (Offset == Start.ShortE.Start - 2)) ||((!Type) && (Offset == Start.LongE.Start - 4))){if ((Offset = lseek (fd, 0L, SEEK_END)) == -1L){Res = _doserrno;close (fd);return Res;}else if (Type)Current.ShortE.End = (USHORT)Offset;elseCurrent.LongE.End = Offset;}/* Get the message length */if (Type){*pcbMsg = Current.ShortE.End - Current.ShortE.Start;Offset = Current.ShortE.Start;}else{*pcbMsg = (USHORT)(Current.LongE.End - Current.LongE.Start);Offset = Current.LongE.Start;}/* Check the message length */*pcbMsg += 8;if (*pcbMsg >= cbBuf)return ERROR_MR_MSG_TOO_LONG;sprintf (pchBuf, "SYS%.4d: ", usMsgNum);/* Seek to the start of the message and read its type */if ((lseek (fd, Offset, SEEK_SET) != Offset) ||(read (fd, &Type, 1) != 1)){Res = _doserrno;close (fd);return Res;}if (Type == '?'){close (fd);return ERROR_MR_MID_NOT_FOUND;}/* Get the message itself */else if (read (fd, pchBuf + 9, *pcbMsg - 8) != *pcbMsg - 8){Res = _doserrno;close (fd);return Res;}close (fd);*(pchBuf + *pcbMsg) = 0;return 0;}# endif#endif/** Get the Win NT Error message*/#if (OS_TYPE == OS_NT)char *GetOSSystemErrorMessage (OSCALL_RET code){DWORD Source = 0;static char EBuffer[100];char *OSBuffer;char *Buffer1;char *ip;char *op;/* Read the message */if (!FormatMessage ((FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ALLOCATE_BUFFER), &Source, code, 0,(LPSTR)&OSBuffer, 100, NULL)){sprintf (EBuffer, "SYS%.4d: No error message available (%ld)",code, GetLastError ());OSBuffer = EBuffer;}else{op = &OSBuffer[strlen (OSBuffer) - 1];while (isspace (*op) && (op != OSBuffer))op--;}/* Allocate local space. If there isn't any give up and hope for the best */if ((Buffer1 = GetAllocatedSpace (strlen (OSBuffer) + 20)) == (char *)NULL)return OSBuffer;/* Transfer the system buffer to a local buffer.* Check the error number is there*/op = Buffer1;ip = OSBuffer;if (strncmp (OSBuffer, "SYS", 3) != 0){sprintf (op, "SYS%.4d: ", code);op += strlen (op);}/* Remove interior CRs & NLs */while ((*(op) = *(ip++)) != 0){if (*op == CHAR_NEW_LINE)*(op++) = CHAR_SPACE;else if (*op != CHAR_RETURN)op++;}if (OSBuffer != EBuffer)LocalFree (OSBuffer);return Buffer1;}#endif/** Display a started job info*/#if (OS_TYPE != OS_DOS)static void F_LOCALPrintPidStarted (void){if (PidInfo.Valid)fprintf (stderr, "[%d] %d\n", PidInfo.JobNo, PidInfo.PidNo);PidInfo.Valid = FALSE;}#endif/** Print a list of names, with or without numbers*/void PrintAList (int ArgCount, char **ArgList){char **pp = ArgList;int i, j;int ix;int MaxArgWidth = 0;int NumberWidth = 0;int ncols;int nrows;if (!ArgCount)return;/* get dimensions of the list */while (*pp != (char *)NULL){i = strlen (*(pp++)) + 1;MaxArgWidth = (i > MaxArgWidth) ? i : MaxArgWidth;}/** We print an index of the form* %d)* in front of each entry. Get the max width of this*/for (i = ArgCount, NumberWidth = 1; i >= 10; i /= 10)NumberWidth++;/* In the case of numbered lists, we go down if less than screen length */if (ArgCount < (MaximumLines - 5)){ncols = 1;nrows = ArgCount;}else{ncols = MaximumColumns / (MaxArgWidth + NumberWidth + 3);nrows = ArgCount / ncols;if (ArgCount % ncols)nrows++;if (!nrows)nrows = 1;if (ncols > nrows){nrows = ncols;ncols = 1;}}/* Display the list */for (i = 0; i < nrows; i++){for (j = 0; j < ncols; j++){if ((ix = j * nrows + i) < ArgCount){printf ("%*d) ", NumberWidth, ix + 1);if (j != (ncols - 1))printf ("%-*.*s", MaxArgWidth, MaxArgWidth, ArgList[ix]);elseprintf ("%-s", ArgList[ix]);}}fputchar (CHAR_NEW_LINE);}}/** Are we tracking all commands? Check there is no path in the command*/static void F_LOCALTrackAllCommands (char *path, char *arg){if (FL_TEST (FLAG_TRACK_ALL) &&(FindPathCharacter (arg) == (char *)NULL) &&(!IsDriveCharacter (arg[1])))SaveAlias (arg, PATH_TO_UNIX (path), TRUE);}/** Get the application type and get we can do it*/static bool F_LOCALGetApplicationType (char *path){ApplicationType = QueryApplicationType (path);if (FL_TEST(FLAG_DEBUG_EXECUTE))WhenceTypeDebug (ApplicationType);/* Some type of error */if (ApplicationType & EXETYPE_ERROR){if (ApplicationType == EXETYPE_UNKNOWN){if (!FL_TEST (FLAG_WARNING))fprintf (stderr, "sh: Cannot determine executable type <%s>\n",path);return TRUE;}else if (ApplicationType == EXETYPE_BAD_FILE)errno = ENOENT;elseerrno = ENOEXEC;return FALSE;}/** This is where it gets complicated - Sort out DOS!*/#if (OS_TYPE == OS_DOS)if (ExecProcessingMode.Flags & EP_IGNTYPE){ApplicationType = EXETYPE_DOS_CUI;return TRUE;}else if (ApplicationType == EXETYPE_DOS_GUI){if ((BaseOS == BASE_OS_WIN) &&(GetVariableAsString (LIT_STARTWINP, FALSE) == null)){if (!FL_TEST (FLAG_WARNING))feputs ("sh: Start this applications from Windows\n");errno = ENOEXEC;return FALSE;}}else if ((ApplicationType == EXETYPE_DOS_32) && (BaseOS == BASE_OS_NT))return BadApplication ("DOS 32-bit");else if (ApplicationType & EXETYPE_OS2)return BadApplication ("OS/2");else if (ApplicationType & EXETYPE_NT)return (BaseOS == BASE_OS_NT) ? TRUE : BadApplication ("Win NT");#elif (OS_TYPE == OS_OS2)if (ApplicationType & EXETYPE_NT)return BadApplication ("Win NT");# if (OS_SIZE == OS_16)if (OS_VERS_N < 2){if (ApplicationType & EXETYPE_DOS)return BadApplication (LIT_dos);else if (ApplicationType & EXETYPE_OS2_32)return BadApplication ("OS/2 32-bit");}# endifif (ApplicationType == EXETYPE_DOS_32)return BadApplication ("DOS 32-bit");#elif (OS_TYPE == OS_NT)if (ApplicationType == EXETYPE_DOS_32)return BadApplication ("DOS 32-bit");/* Yes we can ,,,,,* else if (ApplicationType & EXETYPE_OS2_32)* return BadApplication ("OS/2 32-bit");*/#endif/* In the end - execute it */return TRUE;}/** Print Bad application warning*/static bool F_LOCALBadApplication (char *mes){if (!FL_TEST (FLAG_WARNING))fprintf (stderr, "sh: Cannot execute %s applications\n", mes);errno = ENOEXEC;return FALSE;}/** Handle a Windows Program*/#if (OS_TYPE != OS_UNIX)static int F_LOCALExecuteWindows (char *Fullpath, char **argv, char **envp, int ForkAction ){int res;#if (OS_TYPE == OS_DOS)Word_B *wb = (Word_B *)NULL;if (!SetUpCLI ((BaseOS != BASE_OS_WIN)? "win": GetVariableAsString (LIT_STARTWINP, FALSE), &wb))return -1;#elif (OS_TYPE == OS_OS2)Word_B *wb = AddWordToBlock ("winos2", (Word_B *)NULL);#elif (OS_TYPE == OS_NT)Word_B *wb = (Word_B *)NULL;#endif/* Add the rest of the parameters and execute */ForkAction |= EXEC_WINDOWS;res = ExecuteSpecialProcessor (Fullpath, argv, envp, ForkAction, wb);/* A cheat to stop us exec'ing the program again. Normally,* ExecuteSpecialProcessor is call below ExecuteProgram. However, in the* case of a Windows prog, its called above, so we set this to stop* Executeprogram invoking the program again, but telling it that no* swapping was required.*/ExecProcessingMode.Flags = EP_NOSWAP;return res;}#endif /*!OS_UNIX*//** Generic processing for script and windows programs*/static int F_LOCALExecuteSpecialProcessor (char *Fullpath, char **argv, char **envp, int ForkAction, Word_B *wb ){char **nargv;char *p_name; /* Program name */char *cp;int j;/* Add the rest of the parameters */wb = AddWordToBlock (Fullpath, wb);j = 1;while (argv[j] != NOWORD)wb = AddWordToBlock (argv[j++], wb);/* Execute the program */nargv = GetWordList (AddWordToBlock (NOWORD, wb));/* Special for UNIX compatability, use ourselves */if ((strcmp (nargv[0], "/bin/sh") == 0) ||(strcmp (nargv[0], "/bin/ksh") == 0))nargv[0] = GetVariableAsString (ShellVariableName, FALSE);/* See if the program exists. If it doesn't, strip the path */else if (((cp = strrchr (nargv[0], CHAR_UNIX_DIRECTORY)) != (char *)NULL) &&((p_name = AllocateMemoryCell (FFNAME_MAX)) != (char *)NULL)){if (FindLocationOfExecutable (p_name, nargv[0]) == EXTENSION_NOT_FOUND)strcpy (nargv[0], cp + 1);ReleaseMemoryCell (p_name);}/* Get the new program mode */CheckProgramMode (*nargv, &ExecProcessingMode);j = EnvironExecute (nargv, ForkAction);if (ExecProcessingMode.Flags != EP_ENVIRON)j = LocalExecve (nargv, envp, ForkAction);/* 0 is a special case - see ConvertErrorNumber */if (j == -1)errno = 0;return j;}/** Parse a new CLI string*/static bool F_LOCALSetUpCLI (char *string, Word_B **wb){char *sp = StringCopy (string);if (sp == null)return FALSE;*wb = SplitString (sp, *wb);return TRUE;}static voidPrintProgramMode ( ExeMode *PMode ){if (PMode->Flags & EP_DOSMODE) fprintf (stderr, "DOSMODE ");if (PMode->Flags & EP_UNIXMODE) fprintf (stderr, "UNIXMODE ");if (PMode->Flags & EP_NOEXPAND) fprintf (stderr, "NOEXPAND ");if (PMode->Flags & EP_ENVIRON) fprintf (stderr, "ENVIRON ");if (PMode->Flags & EP_NOSWAP) fprintf (stderr, "NOSWAP ");if (PMode->Flags & EP_QUOTEWILD) fprintf (stderr, "QUOTEWILD ");if (PMode->Flags & EP_EXPORT) fprintf (stderr, "EXPORT ");if (PMode->Flags & EP_CONVERT) fprintf (stderr, "CONVERT ");if (PMode->Flags & EP_NOWORDS) fprintf (stderr, "NOWORDS ");if (PMode->Flags & EP_NOQUOTE) fprintf (stderr, "NOQUOTE ");if (PMode->Flags & EP_IGNTYPE) fprintf (stderr, "IGNTYPE ");if (PMode->Flags & EP_PSEUDOTTY) fprintf (stderr, "PSEUDOTTY ");if (PMode->Flags & EP_DOSEXT) fprintf (stderr, "DOSEXT ");fprintf (stderr, "\n");}#if (OS_TYPE == OS_DOS)/** Check the program type*/voidCheckProgramMode (char *Pname, ExeMode *PMode ){char *pEnv;int builtin; /* Builtin function */ExecMode_t mode;/* Check for internal no-globbed commands */if ((IsCommandBuiltIn (Pname, &builtin) != (int (*)(int, char **))NULL) &&((builtin & BLT_SKIPGLOB) == BLT_SKIPGLOB)){PMode->Flags = EP_NOEXPAND | ((builtin & BLT_NOWORDS) ? EP_NOWORDS : 0);return;}/* Set not found */mode.Flags = EM_NONE;mode.Name = (char *)NULL;/* Check not a function */if ((Pname != (char *)NULL) &&((pEnv = GetVariableAsString("EXTENDED_LINE", FALSE)) != null)){if (FL_TEST(FLAG_DEBUG_EXECUTE))__systeml_debug = 1;__systeml_mode(pEnv, Pname, &mode); /* Read extend config */}/* Copy results */PMode->Name = mode.Name;PMode->Flags = EP_NONE;if (mode.Flags & EM_DOSMODE) PMode->Flags |= EP_DOSMODE;if (mode.Flags & EM_UNIXMODE) PMode->Flags |= EP_UNIXMODE;if (mode.Flags & EM_NOEXPAND) PMode->Flags |= EP_NOEXPAND;if (mode.Flags & EM_ENVIRON) PMode->Flags |= EP_ENVIRON;if (mode.Flags & EM_NOSWAP) PMode->Flags |= EP_NOSWAP;if (mode.Flags & EM_QUOTEWILD) PMode->Flags |= EP_QUOTEWILD;if (mode.Flags & EM_EXPORT) PMode->Flags |= EP_EXPORT;if (mode.Flags & EM_CONVERT) PMode->Flags |= EP_CONVERT;if (mode.Flags & EM_NOWORDS) PMode->Flags |= EP_NOWORDS;if (mode.Flags & EM_NOQUOTE) PMode->Flags |= EP_NOQUOTE;if (mode.Flags & EM_IGNTYPE) PMode->Flags |= EP_IGNTYPE;if (mode.Flags & EM_PSEUDOTTY) PMode->Flags |= EP_PSEUDOTTY;if (mode.Flags & EM_DOSEXT) PMode->Flags |= EP_DOSEXT;PMode->FieldSep = mode.FieldSep;}#else /*!OS_DOS*//** Common fields in EXTENDED_LINE file*/#define COMMON_FIELD_COUNT ARRAY_SIZE (CommonFields)static struct CommonFields {char *Name;unsigned int Flag;} CommonFields [] = {{ "switch", EP_CONVERT },{ LIT_export, EP_EXPORT },{ "noswap", EP_NOSWAP },{ "noexpand", EP_NOEXPAND },{ "noquote", EP_NOQUOTE },{ "ignoretype", EP_IGNTYPE },{ "pipetty", EP_PSEUDOTTY },{ "quotewild", EP_QUOTEWILD },{ "dosext", EP_DOSEXT },};/** Check the program type*/void CheckProgramMode (char *Pname, ExeMode *PMode){#if defined(USE_STUPID_BROKEN_CACHE)static struct { /* Single level Cache */char *name;ExeMode mode;} cache = { NULL, 0 };#endifchar *sp, *sp1; /* Line pointers */int nFields;char *SPname;int builtin; /* Builtin function */LineFields LF;long value;/* Check for internal no-globbed commands */if ((IsCommandBuiltIn (Pname, &builtin) != (int (*)(int, char **))NULL) &&((builtin & BLT_SKIPGLOB) == BLT_SKIPGLOB)){PMode->Flags = EP_NOEXPAND | ((builtin & BLT_NOWORDS) ? EP_NOWORDS : 0);return;}/* Set not found */PMode->Flags = EP_NONE;PMode->Name = NULL;/* Check not a function */if ((Pname == (char *)NULL) ||((sp = GetVariableAsString ("EXTENDED_LINE", FALSE)) == null))return;/* Remove terminating .exe etc */sp1 = ((sp1 = FindLastPathCharacter (Pname)) == (char *)NULL)? Pname : sp1 + 1;if (IsDriveCharacter (*(sp1 + 1)))sp1 += 2;if ((SPname = StringCopy (sp1)) == null)return;if ((sp1 = strrchr (SPname, CHAR_PERIOD)) != (char *)NULL)*sp1 = 0;#if defined(USE_STUPID_BROKEN_CACHE)/* Check cache (26/09/00) */if (cache.name){if (! NOCASE_COMPARE (cache.name, SPname)){PMode->Flags |= (cache.mode.Flags & EP_CACHED);if (cache.mode.Name)PMode->Name = StringCopy(cache.mode.Name);else PMode->Name = NULL;PMode->FieldSep = cache.mode.FieldSep;ReleaseMemoryCell ((void *)SPname);return;}}#endif/* Open the file */if ((LF.Line = AllocateMemoryCell (LF.LineLength = 200)) == (char *)NULL){ReleaseMemoryCell ((void *)SPname);return;}if ((LF.FP = FOpenFile (sp, sOpenReadMode)) == (FILE *)NULL){ReleaseMemoryCell ((void *)LF.Line);ReleaseMemoryCell ((void *)SPname);return;}/* Initialise the internal buffer */LF.Fields = (Word_B *)NULL;/* Scan for the file name */while ((nFields = ExtractFieldsFromLine (&LF)) != -1){if (nFields < 2)continue;/* Remove terminating .exe etc */#if (OS_TYPE != OS_UNIX)if ((sp = strrchr (LF.Fields->w_words[0], CHAR_PERIOD)) != (char *)NULL)*sp = 0;#endifif (NOCASE_COMPARE (LF.Fields->w_words[0], SPname))continue;/* What type? */if (NOCASE_COMPARE (LF.Fields->w_words[1], "unix") == 0)PMode->Flags = (unsigned int )(EP_UNIXMODE |CheckForCommonOptions (&LF, 2));else if (NOCASE_COMPARE (LF.Fields->w_words[1], LIT_dos) == 0)PMode->Flags = (unsigned int )(EP_DOSMODE |CheckForCommonOptions (&LF, 2));/* Must have a valid name and we can get memory for it */else if ((NOCASE_COMPARE (LF.Fields->w_words[1], "environ") == 0) &&(nFields >= 3) &&(!IsValidVariableName (LF.Fields->w_words[2])) &&((PMode->Name =StringCopy (LF.Fields->w_words[2])) != null)){PMode->Flags = EP_ENVIRON;PMode->FieldSep = 0;if ((nFields >= 4) &&ConvertNumericValue (LF.Fields->w_words[3], &value, 0))PMode->FieldSep = (unsigned char)value;if (!PMode->FieldSep)PMode->FieldSep = CHAR_SPACE;}elsePMode->Flags = CheckForCommonOptions (&LF, 1);break;}CloseFile (LF.FP);ReleaseMemoryCell ((void *)LF.Line);#if defined(USE_STUPID_BROKEN_CACHE)/* Update cache (26/09/00) */if (cache.name) /* program name */ReleaseMemoryCell (cache.name);cache.name = SPname;cache.mode.Flags = PMode->Flags; /* exec mode */if (cache.mode.Name)ReleaseMemoryCell ((void *)cache.mode.Name);if ((PMode->Flags & EP_ENVIRON) && PMode->Name)cache.mode.Name = StringCopy(PMode->Name);elsecache.mode.Name = NULL;cache.mode.FieldSep = PMode->FieldSep;#endif}/** Check for common fields*/static unsigned int F_LOCALCheckForCommonOptions(LineFields *LF, int Start ){unsigned int Flags = 0;int i, j;if (LF->Fields == (Word_B *)NULL)return 0;for (i = Start; i < LF->Fields->w_nword; i++){for (j = 0; j < COMMON_FIELD_COUNT; ++j){if (!NOCASE_COMPARE (LF->Fields->w_words[i], CommonFields[j].Name)){Flags |= CommonFields[j].Flag;break;}}}return Flags;}#endif /*OS_DOS*/