Rev 227 | Rev 303 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
/** MS-DOS SHELL - Main program, memory and variable management** MS-DOS SHELL - Copyright (c) 1990,4 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.** 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 interfaces to the new Lexical* Scanner.** $Header: /cvsroot/device/DEVL/UTILS/SH/Sh1.c,v 1.3 2004/11/04 02:47:59 ayoung Exp $** $Log: Sh1.c,v $* Revision 1.3 2004/11/04 02:47:59 ayoung* handle upto 8 PATH definitions** Revision 1.2 2004/05/10 09:30:06 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:33 adamy* imported (reference only)** Revision 1.1 2001/07/20 05:55:43 ayoung* WIN32 support** Revision 1.1.1.1 1999/12/02 01:11:12 gordonh* UTIL** Revision 2.19 1994/08/25 20:49:11 istewart* MS Shell 2.3 Release** Revision 2.18 1994/02/23 09:23:38 istewart* Beta 233 updates** Revision 2.17 1994/02/01 10:25:20 istewart* Release 2.3 Beta 2, including first NT port** Revision 2.16 1994/01/20 14:51:43 istewart* Release 2.3 Beta 1** Revision 2.15 1994/01/11 17:55:25 istewart* Release 2.3 Beta 0 patches** Revision 2.14 1993/12/01 11:58:34 istewart* Release 226 beta** Revision 2.13 1993/11/09 10:39:49 istewart* Beta 226 checking** Revision 2.12 1993/08/25 16:03:57 istewart* Beta 225 - see Notes file** Revision 2.11 1993/07/02 10:21:35 istewart* 224 Beta fixes** Revision 2.10 1993/06/16 12:55:49 istewart* Fix the -s and -t options so they work** 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/04/13 17:39:09 Ian_Stewartson* MS-Shell 2.0 Baseline release*/#include <sys/types.h>#include <sys/stat.h>#include <stdio.h>#include <stdlib.h>#include <signal.h>#include <errno.h>#include <setjmp.h>#include <stdarg.h>#include <string.h>#include <unistd.h>#include <ctype.h>#include <fcntl.h>#include <limits.h>#include <dirent.h>#include <time.h>#include "sh.h"/* OS2 or DOS, DEBUG MEMORY or normal malloc */#ifdef OS2_DOSALLOC# ifdef DEBUG_MEMORY# define RELEASE_MEMORY(x) if (DosFreeSeg (SELECTOROF ((x)))) \fprintf(stderr, "Memory Release error\n");# else# define RELEASE_MEMORY(x) DosFreeSeg (SELECTOROF ((x)))# endif#else# define RELEASE_MEMORY(x) free ((x))#endif/** Structure of Malloced space to allow release of space nolonger required* without having to know about it.*/typedef struct region {struct region *next;size_t nbytes;int area;} s_region;static struct region *MemoryAreaHeader = (s_region *)NULL;/** default shell, search rules*/#if (OS_TYPE == OS_UNIX)static char *shellname = "/bin/pksh";static char search[] = ":/bin:/usr/bin";extern char **environ;#elsestatic char *shellname = "c:/bin/sh.exe";static char search[] = ";c:/bin;c:/usr/bin";#endifstatic char *ymail = "You have mail";static char *ShellNameLiteral = "sh";static char *ShellOptions = "D:MPRX:abc:defghijklmnopqrtsuvwxyz0";#if (OS_TYPE != OS_DOS)static char DefaultWinTitle[] = "MS Shell";#endif#if (OS_TYPE == OS_OS2)# if (OS_SIZE == OS_32)static HEV SessionQueueSema = 0;static HQUEUE SessionQueueHandler = 0;# elsestatic HSEM SessionQueueSema = 0;static HQUEUE SessionQueueHandler = 0;# endif#endifstatic char *tilde = "~";static char LIT_OSmode[] = "OSMODE";static char LIT_SHmode[] = "SHMODE";static char LIT_Dollar[] = "$";#if (OS_TYPE == OS_DOS)static char *NOExit = "sh: ignoring attempt to leave lowest level shell";#endifstatic bool ProcessingDEBUGTrap = FALSE;static bool ProcessingERRORTrap = FALSE;static unsigned int ATOE_GFlags; /* Twalk GLOBAL flags */static unsigned int ATNE_Function; /* Twalk GLOBAL flags *//** Count value for counting the number entries in an array*/static int Count_Array; /* Array size */static char *Count_Name; /* Array name *//* Integer Variables */static struct ShellVariablesInit {char *Name; /* Name of variable */int Status; /* Initial status */char *CValue;} InitialiseShellVariables[] = {{ LIT_COLUMNS, STATUS_INTEGER, "80" },{ HistorySizeVariable, STATUS_INTEGER, "100" },{ LineCountVariable, STATUS_INTEGER, "1" },{ LIT_LINES, STATUS_INTEGER, "25" },{ OptIndVariable, STATUS_INTEGER, "1" },{ StatusVariable, STATUS_INTEGER, "0" },{ RandomVariable, (STATUS_EXPORT | STATUS_INTEGER),null },{ SecondsVariable, (STATUS_EXPORT | STATUS_INTEGER),null },{ LIT_OSmode, (STATUS_EXPORT | STATUS_CANNOT_UNSET |STATUS_INTEGER), null },{ LIT_SHmode, (STATUS_EXPORT | STATUS_CANNOT_UNSET |STATUS_INTEGER), null },{ LIT_Dollar, (STATUS_CANNOT_UNSET | STATUS_INTEGER),null },{ LastWordVariable, STATUS_EXPORT, null },{ PathLiteral, (STATUS_EXPORT | STATUS_CANNOT_UNSET),search },{ IFS, (STATUS_EXPORT | STATUS_CANNOT_UNSET)," \t\n" },{ PS1, (STATUS_EXPORT | STATUS_CANNOT_UNSET),"%e$ " },{ PS2, (STATUS_EXPORT | STATUS_CANNOT_UNSET),"> " },{ PS3, STATUS_EXPORT, "#? " },{ PS4, STATUS_EXPORT, "+ " },{ HomeVariableName, STATUS_EXPORT, null },{ ShellVariableName, STATUS_EXPORT, null },#if (OS_TYPE != OS_DOS){ WinTitleVariable, 0, DefaultWinTitle },#endif{ (char *)NULL, 0 }};/** List of Editor variables*/#if defined (FLAGS_EMACS) || defined (FLAGS_VI) || defined (FLAGS_GMACS)static char *EditorVariables[] = {FCEditVariable, EditorVariable, VisualVariable, (char *)NULL};#endif/* Entry directory */static char Start_directory[PATH_MAX + 6] = { 0 };static time_t ShellStartTime = 0; /* Load time of shell *//* Original Interrupt 24 address */#if (OS_TYPE == OS_DOS) && !defined (__EMX__)#if defined (__WATCOMC__)# define INTERRUPT_TYPE __interrupt __far# else# define INTERRUPT_TYPE interrupt far# endifstatic void (INTERRUPT_TYPE *Orig_I24_V) (void);#endif#ifdef SIGQUITstatic void (_SIGDECL *qflag)(int) = SIG_IGN;#endif/* Functions */static int F_LOCAL RunCommands (Source *);static unsigned char * F_LOCAL CheckClassExpression (unsigned char *, int, bool);static bool F_LOCAL Initialise (int, char **);static void F_LOCAL CheckForMailArriving (void);static void F_LOCAL LoadGlobalVariableList (void);static void F_LOCAL LoadTheProfileFiles (void);static void F_LOCAL ConvertUnixPathToMSDOS (char *);static void F_LOCAL ClearUserPrompts (void);static void F_LOCAL SecondAndRandomEV (char *, long);static void F_LOCAL SetUpParameterEV (int, char **, char *);static bool F_LOCAL AllowedToSetVariable (VariableList *);static void F_LOCAL SetUpANumericValue (VariableList *, long, int);static void F_LOCAL ProcessErrorExit (char *, va_list);static char * F_LOCAL SuppressSpacesZeros (VariableList *, char *);static void AddToNewEnvironment (const void *, VISIT, int);static void AddToOldEnvironment (const void *, VISIT, int);static void DeleteEnvironment (const void *, VISIT, int);static void F_LOCAL CheckOPTIND (char *, long);static void F_LOCAL CreateIntegerVariables (void);static bool F_LOCAL ExecuteShellScript (char *);static void CountEnvironment (const void *, VISIT, int);/** Process termination*/#if (OS_TYPE != OS_DOS)static void F_LOCAL CheckForTerminatedProcess (void);# if (OS_TYPE == OS_NT)static void LookUpJobs (const void *, VISIT, int);# endif#else# define CheckForTerminatedProcess()#endif/** No real argv interface*/#if (OS_TYPE == OS_UNIX)#define Pre_Process_Argv(a,b)#elsestatic void F_LOCAL Pre_Process_Argv (char **, int *);#endif/** OS/2 Session Queues*/#if (OS_TYPE == OS_OS2)static void F_LOCAL CheckForSessionEnd (void);static void F_LOCAL CreateTerminationQueues (void);#else# define CheckForSessionEnd()# define CreateTerminationQueues()#endif#if defined (FLAGS_EMACS) || defined (FLAGS_VI) || defined (FLAGS_GMACS)static void F_LOCAL SetEditorMode (char *);#endif/** The Program Name and Command Line, set up by stdargv.c*/#if (OS_TYPE != OS_UNIX)extern char *_APgmName;extern char *_ACmdLine;#endif/*** Debug level*/int debug_level = 0;/** The main program starts here*/#if defined(WIN32)void __cdecl main(int argc, char **argv, char **envp)#elsevoid main(int argc, char **argv)#endif{int cflag = 0;int sc;char *name;/* Load up various parts of the *//* system */bool OptionsRflag;bool OptionsXflag = FALSE; /* -x option from *//* command line */bool Level0Shell = FALSE; /* Level 0 (read profile)*/jmp_buf ReturnPoint;char *cp;Source *s;int fid;bool TTYInput;bool PIPEInput;bool RootShell = FALSE;(void) envp;name = *argv;OptionsRflag = Initialise(argc, argv);SetWindowName ((char *)NULL);/* Create Parse input */s = pushs (SFILE);s->u.file = stdin;/* Set up start time */ShellStartTime = time ((time_t *)NULL);/* Preprocess options to convert two character options of the form /x to* -x. Some programs!!*/if (argc > 1)Pre_Process_Argv (argv, &argc);/* Save the start directory for when we exit */S_getcwd (Start_directory, 0);/* Process the options */while ((sc = GetOptions (argc, argv, ShellOptions, GETOPT_MESSAGE)) != EOF){switch (sc){case '?':FinalExitCleanUp (1);case '0': /* Level 0 flag for DOS */Level0Shell = TRUE;break;case 'r': /* Restricted */OptionsRflag = TRUE;break;case 'c': /* Command on line */ClearUserPrompts ();cflag = 1;s->type = SSTRING;s->str = OptionArgument;SetVariableFromString ("_cString", OptionArgument);break;case 'q': /* No quit ints */#ifdef SIGQUITqflag = SIG_DFL;#endifbreak;case 'X':if (!GotoDirectory (OptionArgument, GetCurrentDrive ())){PrintErrorMessage ("%s: bad directory", OptionArgument);FinalExitCleanUp (1);}break;case 'x':OptionsXflag = TRUE;break;case 'M':ShellGlobalFlags |= FLAGS_MSDOS_FORMAT;break;case 'D':AssignVariableFromString (OptionArgument, (int *)NULL);break;case 'R':RootShell = TRUE;ChangeInitLoad = TRUE; /* Change load .ini pt. */break;#if (OS_TYPE != OS_DOS)case 'P': /* Use real pipes */ShellGlobalFlags |= FLAGS_REALPIPES;break;#endifcase 's': /* standard input */if (cflag)PrintErrorMessage ("cannot use -s and -c together");case 'i': /* Set interactive */InteractiveFlag = TRUE;default:if (islower (sc))FL_SET (sc);}/* If -s, set the argv to point to -s so that the rest of the parameters* get used as parameters ($digit) values.*/if (FL_TEST (FLAG_POSITION)){OptionIndex--;break;}}/* Under UNIX, check for login shell */#if (OS_TYPE == OS_UNIX)if (*argv[0] == '-')Level0Shell = TRUE;#endifargv += OptionIndex;argc -= OptionIndex;/* Get configuration info */Configure_Keys ();/** Check for terminal input. A special case for OS/2. If pipe input and* output and no arguments, set interactive flag.** Unset the variable after!*/TTYInput = C2bool ((IS_TTY (0) && IS_TTY (1)));PIPEInput = C2bool ((IS_Pipe (0) && IS_Pipe (1) &&(GetVariableAsString (LIT_AllowTTY, FALSE) != null)));UnSetVariable (LIT_AllowTTY, -1, TRUE);if ((s->type == SFILE) &&((FL_TEST (FLAG_INTERACTIVE)) || TTYInput || PIPEInput) &&!cflag &&((argc == 0) || FL_TEST (FLAG_POSITION))){s->type = STTY;FL_SET (FLAG_POSITION);FL_SET (FLAG_INTERACTIVE);InteractiveFlag = TRUE;if (TTYInput)PrintVersionNumber (stderr);}/* Root shell - check only tty devices */if (RootShell){if ((s->type != STTY) || !TTYInput)PrintErrorMessage ("-R not valid on non-interactive shells");#if (OS_TYPE == OS_DOS) && !defined (__EMX__)Orig_I24_V = (void (INTERRUPT_TYPE *)())NULL;#endif}/** Execute commands from a file? - disable prompts*/if ((s->type == SFILE) && (argc > 0) && !InteractiveFlag){ClearUserPrompts ();name = *argv;if (((fid = S_open (FALSE, s->file = name, O_RMASK)) < 0) ||((s->u.file = ReOpenFile (ReMapIOHandler (fid), sOpenReadMode))== (FILE *)NULL)){PrintErrorMessage (LIT_Emsg, "cannot open script", name,strerror (errno));FinalExitCleanUp (1);}/* Un-map this file descriptor from the current environment so it does not* get closed at the wrong times*/ChangeFileDescriptorStatus (fileno (s->u.file), FALSE);#if (OS_TYPE != OS_DOS)SetVariableFromString (WinTitleVariable, name);#endif}/* Setup stderr, stdout buffering */setvbuf (stderr, (char *)NULL, _IONBF, 0);if (InteractiveFlag && !IS_TTY (1))setvbuf (stdout, (char *)NULL, _IOLBF, BUFSIZ);/* Set up the $- variable */SetShellSwitches ();#ifdef SIGQUITsignal (SIGQUIT, qflag);#endif/* Set up signals */if (InteractiveFlag)signal (SIGTERM, TerminateSignalled);/* Load any parameters */SetUpParameterEV (argc, argv, name);/* Return point */if (SetErrorPoint (ReturnPoint))ExitTheShell (FALSE);signal (SIGINT, InterruptSignalled);/* Read profile ?. Init EMAC first for binding keys */if (((name != (char *)NULL) && (*name == CHAR_HYPHEN)) || Level0Shell){#if defined (FLAGS_EMACS) || defined (FLAGS_GMACS)EMACS_Initialisation ();#endifLoadTheProfileFiles ();}/** Load history and configure*/if (s->type == STTY){HistoryEnabled = TRUE;LoadHistory ();}/** If the x or r flag was set on the command line, set it now after the* profiles have been executed.*/if ( debug_level & DEBUG_EXEC_ALL ){FL_SET (FLAG_DEBUG_EXECUTE);FL_CLEAR (FLAG_WARNING);}if (OptionsXflag){FL_SET (FLAG_PRINT_EXECUTE);}if (OptionsRflag){FL_SET (FLAG_READONLY_SHELL);RestrictedShellFlag = TRUE;}/** Execute $ENV** TOCHECK - substitute (cp, DOTILDE);*/if ((cp = GetVariableAsString (ENVVariable, FALSE)) != null)ExecuteShellScript (cp);/* If interactive, set up Editor modes */#if defined (FLAGS_EMACS) || defined (FLAGS_VI) || defined (FLAGS_GMACS)if (InteractiveFlag){char **rep = EditorVariables;while (*rep != (char *)NULL){if ((cp = GetVariableAsString (*(rep++), FALSE)) != null){SetEditorMode (cp);break;}}}#endif/** Execute what ever we have to do!!*/// while (TRUE)for (;;){switch (SetErrorPoint (ReturnPoint)){case TERMINATE_POINT_SET:RunCommands (s); /* Drop to exit */case TERMINATE_COMMAND:default:ExitTheShell (FALSE);/* Re-set TTY input. If we reach this point, the shell is a root shell and* the no exit message has been displayed. Reset the shell for input from the* TTY.*/case TERMINATE_SHELL:s->type = STTY;break;}}}/** Process a script file*/static bool F_LOCAL ExecuteShellScript (char *name){FILE *f = stdin;int fp;Source *s;if (strcmp (name, "-") != 0){if ((fp = OpenForExecution (name, (char **)NULL, (int *)NULL)) < 0)return FALSE;if ((f = ReOpenFile (fp = ReMapIOHandler (fp),sOpenReadMode)) == (FILE *)NULL)return FALSE;}(s = pushs (SFILE))->u.file = f;s->file = name;RunCommands (s);if (f != stdin)CloseFile (f);return TRUE;}/** run the commands from the input source, returning status.*/static int F_LOCAL RunCommands (Source *src){int i;jmp_buf ReturnPoint;C_Op *t = (C_Op *)NULL;bool wastty;int EntryMemoryLevel = MemoryAreaLevel + 1;CreateNewEnvironment ();e.ErrorReturnPoint = (ErrorPoint)NULL;e.line = GetAllocatedSpace (LINE_MAX);/** Run until the end*/// while (TRUE)for (;;){/* Initialise space */MemoryAreaLevel = EntryMemoryLevel;ReleaseMemoryArea (MemoryAreaLevel);SW_intr = 0;ProcessingEXECCommand = FALSE;if (src->next == NULL)src->echo = C2bool (FL_TEST (FLAG_ECHO_INPUT));/** Set up a few things for console input - cursor, mail prompt etc*/if ((wastty = C2bool (src->type == STTY)) != FALSE){PositionCursorInColumnZero ();CheckForMailArriving ();CheckForTerminatedProcess ();CloseAllHandlers (); /* Clean up any open shell files */}LastUserPrompt = PS1;FlushStreams (); /* Clear output *//* Set execute function recursive level and the SubShell count to zero */Execute_stack_depth = 0;/* Set up Redirection IO (Saved) array and SubShell Environment information */NSave_IO_E = 0; /* Number of entries */MSave_IO_E = 0; /* Max Number of entries */NSubShells = 0; /* Number of entries */MSubShells = 0; /* Max Number of entries */CurrentFunction = (FunctionList *)NULL;CurrentFunction = (FunctionList *)NULL;ProcessingDEBUGTrap = FALSE;ProcessingERRORTrap = FALSE;Break_List = (Break_C *)NULL;Return_List = (Break_C *)NULL;SShell_List = (Break_C *)NULL;ProcessingEXECCommand = FALSE;/* Get the line and process it */if (SetErrorPoint (ReturnPoint) ||((t = BuildParseTree (src)) == (C_Op *)NULL) || SW_intr){ScrapHereList ();if ((!InteractiveFlag && SW_intr) || FL_TEST (FLAG_ONE_COMMAND))ExitTheShell (FALSE);/* Go round again */src->str = null;SW_intr = 0;continue;}/* Ok - reset some variables and then execute the command tree */SW_intr = 0;ProcessingEXECCommand = FALSE;FlushHistoryBuffer (); /* Save history *//* Check for exit */if ((t != NULL) && (t->type == TEOF)){if (wastty && (ShellGlobalFlags & FLAGS_IGNOREEOF)){PrintWarningMessage ("Use `exit'");src->type = STTY;continue;}elsebreak;}/* Execute the parse tree */if ((SetErrorPoint (ReturnPoint) == 0) &&((!FL_TEST (FLAG_NO_EXECUTE)) || (src->type == STTY)))ExecuteParseTree (t, NOPIPE, NOPIPE, 0);/* Make sure the I/O and environment are back at level 0 and then clear them */e.ErrorReturnPoint = (ErrorPoint)NULL;Execute_stack_depth = 0;ClearExtendedLineFile ();if (NSubShells != 0)DeleteGlobalVariableList ();if (NSave_IO_E)RestoreStandardIO (0, TRUE);if (MSubShells)ReleaseMemoryCell ((void *)SubShells);if (MSave_IO_E)ReleaseMemoryCell ((void *)SSave_IO);/* Check for interrupts */if ((!InteractiveFlag && SW_intr) || FL_TEST (FLAG_ONE_COMMAND)){ProcessingEXECCommand = FALSE;ExitTheShell (FALSE);}/* Run any traps that are required */if ((i = InterruptTrapPending) != 0){InterruptTrapPending = 0;RunTrapCommand (i);}}/** Terminate the current environment*/QuitCurrentEnvironment ();return ExitStatus;}/** Set up the value of $-*/void SetShellSwitches (void){char *cp, c;char m['z' - 'a' + 2];for (cp = m, c = 'a'; c <= 'z'; ++c){if (FL_TEST (c))*(cp++) = c;}if (ShellGlobalFlags & FLAGS_MSDOS_FORMAT)*(cp++) = 'M';*cp = 0;SetVariableFromString (ShellOptionsVariable, m);}/** Terminate current environment with an error*/void TerminateCurrentEnvironment (int TValue){FlushStreams (); /* Clear output */if (e.ErrorReturnPoint != (ErrorPoint)NULL)ExitErrorPoint (TValue);/* NOTREACHED */}/** Exit the shell*/void ExitTheShell (bool ReturnRequired){FlushStreams (); /* Clear output */if (ProcessingEXECCommand)TerminateCurrentEnvironment (TERMINATE_COMMAND);#if (OS_TYPE == OS_DOS) && !defined (__EMX__)if (Orig_I24_V == (void (INTERRUPT_TYPE *)())NULL){feputs (NOExit);if (!ReturnRequired)TerminateCurrentEnvironment (TERMINATE_SHELL);}#else(void) ReturnRequired;#endif/* Clean up */ScrapHereList ();FreeAllHereDocuments (1);/* Trap zero on exit */RunTrapCommand (0);/* Dump history on exit */DumpHistory ();CloseAllHandlers ();/* If this is a command only - restore the directory because DOS doesn't* and the user might expect it*/if (*Start_directory)RestoreCurrentDirectory (Start_directory);/* If this happens during startup - we restart */#if (OS_TYPE == OS_DOS) && !defined (__EMX__)if (Orig_I24_V == (void (INTERRUPT_TYPE *)())NULL)return;#endif/** Clean up any Here Document files left in the function tree*/DeleteAllFunctions ();/* Exit - hurray */FinalExitCleanUp (ExitStatus);/* NOTREACHED */}/** Output warning message*/int PrintWarningMessage (char *fmt, ...){va_list ap;va_start (ap, fmt);vfprintf (stderr, fmt, ap);feputc (CHAR_NEW_LINE);ExitStatus = -1;/* If leave on error - exit */if (FL_TEST (FLAG_EXIT_ON_ERROR))ExitTheShell (FALSE);va_end (ap);return 1;}/** Shell error message*/void ShellErrorMessage (char *fmt, ...){va_list ap;/* Error message processing */if (source->file == (char *)NULL)feputs ("sh: ");elsefprintf (stderr, "%s: at line %d, ", source->file, source->line);va_start (ap, fmt);ProcessErrorExit (fmt, ap);va_end (ap);}/** Output error message*/void PrintErrorMessage (char *fmt, ...){va_list ap;/* Error message processing */va_start (ap, fmt);ProcessErrorExit (fmt, ap);va_end (ap);}/** Common processing for PrintErrorMessage and ShellError Message*/static void F_LOCAL ProcessErrorExit (char *fmt, va_list ap){vfprintf (stderr, fmt, ap);feputc (CHAR_NEW_LINE);ExitStatus = -1;if (FL_TEST (FLAG_EXIT_ON_ERROR))ExitTheShell (FALSE);/* Error processing */if (FL_TEST (FLAG_NO_EXECUTE))return;/* If not interactive - exit */if (!InteractiveFlag)ExitTheShell (FALSE);if (e.ErrorReturnPoint != (ErrorPoint)NULL)ExitErrorPoint (TERMINATE_COMMAND);/* CloseAllHandlers (); Removed - caused problems. There may be problems* remaining with files left open?*/}/** Create or delete a new environment. If f is set, delete the environment*/void CreateNewEnvironment (void){ShellFileEnvironment *ep;/* Create a new environment */if ((ep = (ShellFileEnvironment *)GetAllocatedSpace (sizeof (ShellFileEnvironment)))== (ShellFileEnvironment *)NULL){while (e.PreviousEnvironment)QuitCurrentEnvironment ();TerminateCurrentEnvironment (TERMINATE_COMMAND);}*ep = e;e.PreviousEnvironment = ep;e.IOMap = 0L;e.OpenStreams = (Word_B *)NULL;}/** Exit the current environment successfully*/void QuitCurrentEnvironment (void){ShellFileEnvironment *ep;unsigned long FdMap;Word_B *wb;int NEntries;int i;/* Restore old environment, delete the space and close any files opened in* this environment*/if ((ep = e.PreviousEnvironment) != (ShellFileEnvironment *)NULL){/* Close opened streams */wb = e.OpenStreams;NEntries = WordBlockSize (wb);for (i = 0; i < NEntries; i++){if (wb->w_words[i] != (char *)NULL)fclose ((FILE *)wb->w_words[i]);}/* Get the files used in this environment to close */FdMap = e.IOMap;e = *ep;ReleaseMemoryCell ((void *)ep);for (i = 0; i < 32; i++){if (FdMap & (1L << i))S_close (i + FDBASE, TRUE);}}}/** Convert binary to ascii*/char *IntegerToString (int n){static char nt[10];sprintf (nt, "%u", n);return nt;}/** SIGINT interrupt processing*/void _SIGDECL InterruptSignalled (int signo){/* Restore signal processing and set SIGINT detected flag */signal (signo, InterruptSignalled);#ifdef SIGNALDEBUGfprintf (stderr, "Interrupt %d detected\n", signo); fflush (stderr);#endif/* Under OS/2, if the Ignore Interrupts flag is set, ignore them. To do* with starting OS/2 programs in sh3.c*/#if (OS_TYPE != OS_DOS)if (IgnoreInterrupts)return;#endif/* Set interrupt detected */SW_intr = 1;/* Are we talking to the user? Yes - Abandon processing */if (InteractiveFlag)TerminateCurrentEnvironment (TERMINATE_COMMAND);/* No - exit */else{ProcessingEXECCommand = FALSE;ExitStatus = 1;ExitTheShell (FALSE);}}/** Grap some space and check for an error*/void *GetAllocatedSpace (size_t n){void *cp;if ((cp = AllocateMemoryCell (n)) == (void *)NULL)PrintErrorMessage (BasicErrorMessage, ShellNameLiteral, Outofmemory1);return cp;}/** Re-allocate some space*/void *ReAllocateSpace (void *OldSpace, size_t NewSize){void *NewSpace;if ((NewSpace = GetAllocatedSpace (NewSize)) == (void *)NULL)return NewSpace;if (OldSpace != (void *)NULL){size_t OldSize = ((s_region *)((char *)OldSpace -sizeof (s_region)))->nbytes;SetMemoryAreaNumber (NewSpace, GetMemoryAreaNumber (OldSpace));memcpy (NewSpace, OldSpace, OldSize);ReleaseMemoryCell (OldSpace);}return NewSpace;}/** Duplicate a memory Area*/void *DuplicateMemoryCell (void *cell){void *new;size_t len = ((s_region *)((char *)cell - sizeof (s_region)))->nbytes;if ((new = AllocateMemoryCell (len)) == (void *)NULL)PrintErrorMessage (BasicErrorMessage, ShellNameLiteral, Outofmemory1);elsememcpy (new, cell, len);return new;}/** Get memory Area size*/size_t GetMemoryCellSize (void *cell){return ((s_region *)((char *)cell - sizeof (s_region)))->nbytes;}/** Save a string in a given area*/char *StringSave (char *s){char *cp;if ((cp = GetAllocatedSpace (strlen (s) + 1)) != (char *)NULL){SetMemoryAreaNumber ((void *)cp, 0);return strcpy (cp, s);}return null;}/** Duplicate at current Memory level*/char *StringCopy (char *s){char *cp;if ((cp = GetAllocatedSpace (strlen (s) + 1)) != (char *)NULL)return strcpy (cp, s);return null;}/** trap handling - Save signal number and restore signal processing*/void _SIGDECL TerminateSignalled (int i){if (i == SIGINT) /* Need this because swapper sets it */{SW_intr = 0;}InterruptTrapPending = i;signal (i, TerminateSignalled);}/** Execute a trap command** 0 - exit trap* -1 - Debug Trap* -2 - Error Trap*/void RunTrapCommand (int i){Source *s;char *trapstr;char tval[10];char *tvalp = tval;/* Check for special values and recursion */if (i == -1){tvalp = Trap_DEBUG;if (ProcessingDEBUGTrap)return;ProcessingDEBUGTrap = TRUE;}/* Error trap */else if (i == -2){tvalp = Trap_ERR;if (ProcessingERRORTrap)return;ProcessingERRORTrap = TRUE;}elsesprintf (tval, "~%d", i);if ((trapstr = GetVariableAsString (tvalp, FALSE)) == null)return;/* If signal zero, save a copy of the trap value and then delete the trap */if (i == 0){trapstr = StringCopy (trapstr);UnSetVariable (tval, -1, TRUE);}(s = pushs (SSTRING))->str = trapstr;RunACommand (s, (char **)NULL);ProcessingDEBUGTrap = FALSE;ProcessingERRORTrap = FALSE;}/** Find the given name in the dictionary and return its value. If the name was* not previously there, enter it now and return a null value.*/VariableList *LookUpVariable (char *name, /* Variable name */int Index, /* Array Index */bool cflag) /* Create flag */{VariableList *vp;VariableList **vpp;int c;static VariableList dummy;void (_SIGDECL *save_signal)(int);/* Set up the dummy variable */memset (&dummy, 0, sizeof (VariableList));dummy.name = name;dummy.status = STATUS_READONLY;dummy.value = null;/* If digit string - use the dummy to return the value */if (isdigit (*name)){for (c = 0; isdigit (*name) && (c < 1000); name++)c = c * 10 + *name - '0';c += Index;dummy.value = (c <= ParameterCount) ? ParameterArray[c] : null;return &dummy;}/* Look up in list */dummy.index = Index;vpp = (VariableList **)tfind (&dummy, &VariableTree, SearchVariable);/* If we found it, return it */if (vpp != (VariableList **)NULL){vp = *vpp;/* Special processing for SECONDS and RANDOM */if (!strcmp (name, SecondsVariable) &&!(DisabledVariables & DISABLE_SECONDS))SetUpANumericValue (vp, time ((time_t *)NULL) - ShellStartTime, 10);else if (!strcmp (name, RandomVariable) &&!(DisabledVariables & DISABLE_RANDOM))SetUpANumericValue (vp, (long)rand(), 10);return vp;}/* If we don't want to create it, return a dummy */dummy.status |= STATUS_NOEXISTANT;if (!cflag)return &dummy;/* Create a new variable. If no memory, use the dummy */dummy.name = null;if ((vp = (VariableList *)GetAllocatedSpace (sizeof (VariableList)))== (VariableList *)NULL)return &dummy;if ((vp->name = StringCopy (name)) == null){ReleaseMemoryCell ((void *)vp);return &dummy;}/* Set values */vp->value = null;vp->index = Index;vp->status = NSubShells ? 0 : STATUS_GLOBAL;/* Save signals */save_signal = signal (SIGINT, SIG_IGN);/* Add to the tree */if (tsearch (vp, &VariableTree, SearchVariable) == (void *)NULL){ReleaseMemoryCell ((void *)vp->name);ReleaseMemoryCell ((void *)vp);vp = &dummy;}/* OK Added OK - set up memory */else{SetMemoryAreaNumber ((void *)vp, 0);SetMemoryAreaNumber ((void *)vp->name, 0);}/* Restore signals */signal (SIGINT, save_signal);return vp;}/** Count the number of entries in an Array Variable*/int CountVariableArraySize (char *name){if (isdigit (*name))return ParameterCount;Count_Array = 0;Count_Name = name;twalk (VariableTree, CountEnvironment);return Count_Array;}/** TWALK - Count the Environment Variables in an array*/static void CountEnvironment (const void *key, VISIT visit, int level){(void) level;if (((visit == postorder) || (visit == leaf)) &&(strcmp (Count_Name, (*(VariableList **)key)->name) == 0))Count_Array++;}/** TFIND & TSEARCH - Search the VARIABLE TREE for an entry*/int SearchVariable (const void *key1, const void *key2){int diff;if ((diff = strcmp (((VariableList *)key1)->name,((VariableList *)key2)->name)) != 0)return diff;return ((VariableList *)key1)->index - ((VariableList *)key2)->index;}/** Execute an assignment. If a valid assignment, load it into the variable* list.** If value is not NULL, assign it. Otherwise don't*/bool AssignVariableFromString (char *String, /* assignment string */int *Index) /* Index value returned */{char *cp;long value = 0;/* Ignore if not valid environment variable - check alpha and equals */if (!GetVariableName (String, &value, &cp, (bool*)NULL) ||(*cp != CHAR_ASSIGN)){if (value == -1)PrintErrorMessage (LIT_BadArray, String);return FALSE;}/* Change the = to a end of string */*(cp++) = 0;/* Assign the value */SetVariableArrayFromString (String, (int)value, cp);/* Return the index */if (Index != (int *)NULL)*Index = (int)value;return TRUE;}/** Get variable name and index** String either ends in a null or assignment*/bool GetVariableName (char *String, /* The original string */long *Index, /* Array index value found */char **Value, /* Pointer to the value */bool *Array) /* Array detected flag */{char *cp, *sp;char EndName;*Index = 0;/* Ignore if not valid environment variable - check alpha and equals */if (((EndName = IsValidVariableName (String)) != CHAR_ASSIGN) &&(EndName != CHAR_OPEN_BRACKETS) && EndName)return FALSE;if ((cp = strchr (String, CHAR_ASSIGN)) == (char *)NULL)cp = &String[strlen (String)];if (Array != (bool *)NULL)*Array = C2bool (EndName == CHAR_OPEN_BRACKETS);/* Check for valid array */if (EndName == CHAR_OPEN_BRACKETS){if ((Index == (long *)NULL) || (*(cp - 1) != CHAR_CLOSE_BRACKETS))return FALSE;/* Terminate the name and remove the trailing bracket */*(sp = strchr (String, CHAR_OPEN_BRACKETS)) = 0;*(cp - 1) = 0;if ((!ConvertNumericValue (sp + 1, Index, 10)) ||(*Index < 0) || (*Index > INT_MAX)){*Index = -1;return FALSE;}}/* Return pointer to null or assignment */*Value = cp;return TRUE;}/** Duplicate the Variable List for a Subshell** Create a new Var_list environment for a Sub Shell*/int CreateGlobalVariableList (unsigned int Function){int i;S_SubShell *sp;for (sp = SubShells, i = 0; (i < NSubShells) &&(SubShells[i].depth < Execute_stack_depth);i++);/* If depth is greater or equal to the Execute_stack_depth - we should panic* as this should not happen. However, for the moment, I'll ignore it*/if (NSubShells == MSubShells){sp = (S_SubShell *)ReAllocateSpace ((MSubShells == 0) ? (void *)NULL: SubShells,(MSubShells + SSAVE_IO_SIZE) *sizeof (S_SubShell));/* Check for error */if (sp == (S_SubShell *)NULL)return -1;SetMemoryAreaNumber ((void *)sp, 0);SubShells = sp;MSubShells += SSAVE_IO_SIZE;}/* Save the depth and the old Variable Tree value */sp = &SubShells[NSubShells++];sp->OldVariableTree = VariableTree;sp->depth = Execute_stack_depth;sp->GFlags = ShellGlobalFlags | Function;sp->Eflags = flags;VariableTree = (void *)NULL;/* Duplicate the old Variable list */ATNE_Function = Function;twalk (sp->OldVariableTree, AddToNewEnvironment);/* Reset global values */LoadGlobalVariableList ();return 0;}/** TWALK - add to new environment*/static void AddToNewEnvironment (const void *key, VISIT visit, int level){VariableList *vp = *(VariableList **)key;VariableList *vp1;(void) level;if ((visit == postorder) || (visit == leaf)){/* For functions, do not copy the traps */if (ATNE_Function && (*vp->name == CHAR_TILDE) && vp->name[1])return;/* Create a new entry */vp1 = LookUpVariable (vp->name, vp->index, TRUE);if ((!(vp->status & STATUS_INTEGER)) && (vp->value != null))vp1->value = StringSave (vp->value);/* Copy some flags */vp1->status = vp->status;vp1->nvalue = vp->nvalue;vp1->base = vp->base;vp1->width = vp->width;}}/** Delete a SubShell environment and restore the original*/void DeleteGlobalVariableList (void){int j;S_SubShell *sp;VariableList *vp;void (_SIGDECL *save_signal)(int);for (j = NSubShells; j > 0; j--){sp = &SubShells[j - 1];if (sp->depth < Execute_stack_depth)break;/* Reduce number of entries */--NSubShells;/* Disable signals */save_signal = signal (SIGINT, SIG_IGN);/* Restore the previous level information */vp = VariableTree;VariableTree = sp->OldVariableTree;ShellGlobalFlags = (unsigned int)(sp->GFlags & ~FLAGS_FUNCTION);flags = sp->Eflags;/* Release the space */ATOE_GFlags = sp->GFlags;twalk (vp, AddToOldEnvironment);twalk (vp, DeleteEnvironment);/* Restore signals */signal (SIGINT, save_signal);LoadGlobalVariableList ();}}/** TWALK - delete old environment tree*/static void DeleteEnvironment (const void *key, VISIT visit, int level){VariableList *vp = *(VariableList **)key;(void) level;if ((visit == endorder) || (visit == leaf)){if (vp->value == null)ReleaseMemoryCell ((void *)vp->value);ReleaseMemoryCell ((void *)vp->name);ReleaseMemoryCell ((void *)vp);}}/** TWALK - Transfer Current Environment to the Old one*/static void AddToOldEnvironment (const void *key, VISIT visit, int level){VariableList *vp = *(VariableList **)key;VariableList *vp1;(void) level;if ((visit == postorder) || (visit == leaf)){/* Skip local variables and traps */if ((ATOE_GFlags & FLAGS_FUNCTION) && (!(vp->status & STATUS_LOCAL)) &&(((*vp->name != CHAR_TILDE) || !vp->name[1]))){/* Get the entry in the old variable list and update it with the new* parameters*/vp1 = LookUpVariable (vp->name, vp->index, TRUE);if (vp1->value != null)ReleaseMemoryCell ((void *)vp1->value);vp1->value = vp->value;vp->value = null; /* Stop releaseing this as its tx */vp1->status = vp->status;vp1->nvalue = vp->nvalue;vp1->base = vp->base;vp1->width = vp->width;}}}/** Load GLobal Var List values*/static void F_LOCAL LoadGlobalVariableList (void){VariableList *cifs = LookUpVariable (IFS, 0, TRUE);CurrentDirectory = LookUpVariable (tilde, 0, TRUE);RestoreCurrentDirectory (CurrentDirectory->value);SetCharacterTypes (cifs->value, C_IFS);}/** Match a pattern as in sh(1). Enhancement to handle prefix processing** IgnoreCase - ignore case on comparisions.* end - end of match in 'string'.* mode - mode for match processing - see GM_ flags in sh.h** pattern character are prefixed with MAGIC by expand.*/bool GeneralPatternMatch (char *string, /* String */unsigned char *pattern, /* Pattern */bool IgnoreCase, /* Ignorecase */char **end, /* End of match */int mode) /* Mode */{int string_c, pattern_c;char *save_end;if ((string == (char *)NULL) || (pattern == (unsigned char *)NULL))return FALSE;while ((pattern_c = *(pattern++)) != 0){string_c = *(string++);if (pattern_c != CHAR_MAGIC){if (IgnoreCase){string_c = tolower (string_c);pattern_c = tolower (pattern_c);}if (string_c != pattern_c)return FALSE;continue;}/* Magic characters */switch (*(pattern++)){case CHAR_OPEN_BRACKETS: /* Class expression */if ((!string_c) ||((pattern = CheckClassExpression (pattern, string_c,IgnoreCase)) ==(unsigned char *)NULL))return FALSE;break;case CHAR_MATCH_ANY: /* Match any character */if (string_c == 0)return FALSE;break;case CHAR_MATCH_ALL: /* Match as many as possible */--string;save_end = (char *)NULL;do{if (!*pattern ||GeneralPatternMatch (string, pattern, IgnoreCase, end,mode)){if (mode == GM_LONGEST)save_end = *end;elsereturn TRUE;}} while (*(string++));if (end != (char **)NULL)*end = save_end;return C2bool (save_end != (char *)NULL);default: /* Match */if ((unsigned)string_c != pattern[-1])return FALSE;break;}}if (end != (char **)NULL){*end = string;return TRUE;}return C2bool (*string == 0);}/** Process a class expression - []*/static unsigned char * F_LOCAL CheckClassExpression (unsigned char *pattern,int string_c, /* Match char*/bool IgnoreCase)/* Ic flag */{int llimit_c, ulimit_c;bool not = FALSE;bool found;/* Exclusive or inclusive class */if ((*pattern == CHAR_MAGIC) &&((*(pattern + 1) == CHAR_NOT) || (*(pattern + 1) == '!'))){pattern += 2;not = TRUE;}found = not;/* Process the pattern */do{if (*pattern == CHAR_MAGIC)pattern++;if (!*pattern)return (unsigned char *)NULL;/* Get the next character in class, converting to lower case if necessary */llimit_c = IgnoreCase ? tolower (*pattern) : *pattern;/* If this is a range, get the end of range character */if ((*(pattern + 1) == CHAR_MATCH_RANGE) &&(*(pattern + 2) != CHAR_CLOSE_BRACKETS)){ulimit_c = IgnoreCase ? tolower (*(pattern + 2)) : *(pattern + 2);pattern++;}elseulimit_c = llimit_c;/* Is the current character in the class? */if ((llimit_c <= string_c) && (string_c <= ulimit_c))found = C2bool (!not);} while (*(++pattern) != CHAR_CLOSE_BRACKETS);return found ? pattern + 1 : (unsigned char *)NULL;}/** Suffix processing - find the longest/shortest suffix.*/bool SuffixPatternMatch (char *string, /* String to match */char *pattern, /* Pattern to match against */char **start, /* Start position */int mode) /* Match mode */{char *save_start = (char *)NULL;/* Scan the string, looking for a match to the end */while (*string){if (GeneralPatternMatch (string, (unsigned char *)pattern, FALSE,(char **)NULL, GM_ALL)){/* If longest, stop here */if (mode == GM_LONGEST){*start = string;return TRUE;}/* Save the start of the shortest string so far and continue */save_start = string;}++string;}return C2bool ((*start = save_start) != (char *)NULL);}/** Get a string in a malloced area*/char *AllocateMemoryCell (size_t nbytes){s_region *np;void (_SIGDECL *save_signal)(int);#ifdef OS2_DOSALLOCSEL sel;#endifif (nbytes == 0)abort (); /* silly and defeats the algorithm *//* Grab some space */#ifdef OS2_DOSALLOCif (DosAllocSeg (nbytes + sizeof (s_region), &sel, SEG_NONSHARED)){errno = ENOMEM;return (char *)NULL;}np = (s_region *)MAKEP (sel, 0);memset (np, 0, nbytes + sizeof (s_region));#elseif ((np = (s_region *)calloc (nbytes + sizeof (s_region), 1))== (s_region *)NULL){errno = ENOMEM;return (char *)NULL;}#endif/* Disable signals */save_signal = signal (SIGINT, SIG_IGN);/* Link into chain */np->next = MemoryAreaHeader;np->area = MemoryAreaLevel;np->nbytes = nbytes;MemoryAreaHeader = np;/* Restore signals */signal (SIGINT, save_signal);return ((char *)np) + sizeof (s_region);}/** Release a array of strings*/void ReleaseAList (char **list){char **ap = list;while (*ap != (char *)NULL)ReleaseMemoryCell (*(ap++));ReleaseMemoryCell (list);}/** Free a string in a malloced area*/void ReleaseMemoryCell (void *s){s_region *cp = MemoryAreaHeader;s_region *lp = (s_region *)NULL;s_region *sp = (s_region *)((char *)s - sizeof (s_region));void (_SIGDECL *save_signal)(int);/* Disable signals */save_signal = signal (SIGINT, SIG_IGN);/* Find the string in the chain */if (s != (char *)NULL){while (cp != (s_region *)NULL){if (cp != sp){lp = cp;cp = cp->next;continue;}/* First in chain ? */else if (lp == (s_region *)NULL)MemoryAreaHeader = cp->next;/* Delete the current entry and relink */elselp->next = cp->next;RELEASE_MEMORY (cp);break;}}/* Restore signals */signal (SIGINT, save_signal);}/** Check for memory leaks with a dump*/#ifdef DEBUG_MEMORYvoid DumpMemoryCells (int status){s_region *cp = MemoryAreaHeader;size_t i;char buffer[17];char *sp;/* Find the string in the chain */while (cp != (s_region *)NULL){fprintf (stderr, "Segment 0x%.8lx Area %5d Length %5d Link 0x%.8lx\n",cp, cp->area, cp->nbytes, cp->next);memset (buffer, CHAR_SPACE, 17);buffer[16] = 0;sp = ((char *)cp) + sizeof (s_region);for (i = 0; i < (((cp->nbytes - 1)/16) + 1) * 16; i++){if (i >= cp->nbytes){feputs (" ");buffer [i % 16] = CHAR_SPACE;}else{fprintf (stderr, "%.2x ", *sp & 0x0ff);buffer [i % 16] = (char)(isprint (*sp) ? *sp : CHAR_PERIOD);}if (i % 16 == 15)fprintf (stderr, " [%s]\n", buffer);sp++;}feputc (CHAR_NEW_LINE);cp = cp->next;}#undef exitexit (status);#define exit(x) DumpMemoryCells (x)}#endif/** Autodelete space nolonger required. Ie. Free all the strings in a malloced* area*/void ReleaseMemoryArea (int a){s_region *cp = MemoryAreaHeader;s_region *lp = (s_region *)NULL;void (_SIGDECL *save_signal)(int);/* Release the Here documents first */FreeAllHereDocuments (a);/* Disable signals */save_signal = signal (SIGINT, SIG_IGN);while (cp != (s_region *)NULL){/* Is the area number less than that specified - yes, continue */if (cp->area < a){lp = cp;cp = cp->next;}/* OK - delete the area. Is it the first in chain ? Yes, delete, relink* and update start location*/else if (lp == (s_region *)NULL){lp = cp;cp = cp->next;MemoryAreaHeader = cp;RELEASE_MEMORY (lp);lp = (s_region *)NULL;}/* Not first, delete the current entry and relink */else{lp->next = cp->next;RELEASE_MEMORY (cp);cp = lp->next;}}/* Restore signals */signal (SIGINT, save_signal);}/** Set the area number for a malloced string. This allows autodeletion of* space that is nolonger required.*/void SetMemoryAreaNumber (void *cp, int a){s_region *sp = (s_region *)((char *)cp - sizeof (s_region));if (cp != (void *)NULL)sp->area = a;}/** Get the area number for a malloced string*/int GetMemoryAreaNumber (void *cp){s_region *sp = (s_region *)((char *)cp - sizeof (s_region));return sp->area;}/* Output one of the Prompt. We save the prompt for the history part of* the program*/void OutputUserPrompt (char *s){struct tm *tm;time_t xtime = time ((time_t *)NULL);int i;char buf[PATH_MAX + 4];if (LastUserPrompt != (char *)NULL){LastUserPrompt = s; /* Save the Last prompt id */s = GetVariableAsString (s, TRUE); /* Get the string value */if (LastUserPrompt == PS1)s = substitute (s, 0);}elses = LastUserPrompt1;tm = localtime (&xtime);while (*s){/* If a format character, process it */if (*s == CHAR_FORMAT){s++;*s = (char)tolower(*s);if (*s == CHAR_FORMAT)fputchar (CHAR_FORMAT);else{*buf = 0;switch (*(s++)){case 'e': /* Current event number */if (HistoryEnabled)sprintf (buf, "%d", Current_Event + 1);break;case 't': /* time */sprintf (buf,"%.2d:%.2d", tm->tm_hour, tm->tm_min);break;case 'd': /* date */sprintf (buf, "%.3s %.2d-%.2d-%.2d",&"SunMonTueWedThuFriSat"[tm->tm_wday * 3],tm->tm_mday, tm->tm_mon + 1,tm->tm_year % 100);break;case 'p': /* directory */case 'n': /* default drive */strcpy (buf, CurrentDirectory->value);if (*(s - 1) == 'n')buf[1] = 0;break;#if (OS_TYPE != OS_UNIX)case 'v': /* version */sprintf (buf, "%s %.2d:%.2d", LIT_OSname,OS_VERS_N, OS_VERS_M);break;#endif}/* Output the string */foputs (buf);}}/* Escaped character ? */else if (*s == CHAR_META){++s;if ((i = ProcessOutputMetaCharacters (&s)) == -1)i = 0;fputchar ((char)i);}elsefputchar (*(s++));}FlushStreams (); /* Clear output */}/** Get the current path in UNIX format and save it in the environment* variable $~*/void GetCurrentDirectoryPath (void){char ldir[PATH_MAX + 6];char *CurrentPWDValue; /* Current directory */S_getcwd (ldir, 0);/* Save in environment */SetVariableFromString (tilde, ldir);CurrentDirectory = LookUpVariable (tilde, 0, TRUE);/* If we have changed directory, set PWD and OLDPWD */if (strcmp (CurrentPWDValue = GetVariableAsString (PWDVariable, FALSE),ldir)){SetVariableFromString (OldPWDVariable, CurrentPWDValue);SetVariableFromString (PWDVariable, ldir);}}/** Initialise the shell and Patch up various parts of the system for the* shell. At the moment, we modify the ctype table so that _ is an upper* case character.*//* Is the path 'a' subset of 'p' ? */#if (OS_TYPE == OS_NT)#define PATHSMAX 8#define PATHOFF 5static intSubPath( const char *p, const char *a ){int pl, al, i;pl = strlen(p += PATHOFF);al = strlen(a += PATHOFF);for (i = 0; pl-- >= al; i++)if (strnicmp( p++, a, al ) == 0){if (i == 0 && (pl == al))return (0);return (i+1);}return (-1);}#endifstatic bool F_LOCAL Initialise (int argc, char **argv){char *s, *s1;char **ap;char *name = *argv;bool OptionsRflag = FALSE;#if (OS_TYPE == OS_NT)bool Level0 = FALSE;int sc;#endif/** Determine the base OS if we can!*/#if (OS_TYPE == OS_NT)BaseOS = BASE_OS_NT; /* Default to NT - no other */#elif (OS_TYPE == OS_UNIX)BaseOS = BASE_OS_UNIX; /* Default to UNIX - no other */#elif (OS_TYPE == OS_OS2)BaseOS = BASE_OS_OS2; /* Default to OS2 - no other */#elif (OS_TYPE == OS_DOS)BaseOS = BASE_OS_DOS; /* Default to DOS */if (_osmajor > 10) /* Rather crude check */BaseOS = BASE_OS_OS2;/* Know to stop NT ? */else{union REGS r;r.h.ah = 0x16;r.h.al = 0;SystemInterrupt (0x2f, &r, &r);if (r.h.al > 0)BaseOS = BASE_OS_WIN;else{r.x.REG_AX = 0x3306;SystemInterrupt (0x21, &r, &r);if ((r.x.REG_AX == 0x3306) && (r.x.REG_BX == 0x3205))BaseOS = BASE_OS_NT;}}#endif/** For MSDOS: Get original interrupt 24 address and set up our new interrupt* 24 address.* For OS/2 : Set noignore interrupts.*/#if (OS_TYPE == OS_DOS) && !defined (__EMX__)Orig_I24_V = GetInterruptVector (0x24);# if (OS_SIZE == OS_16)# if (XXX)SetInterruptVector (0x24, SW_Int24);# endif# else_harderr (HardErrorHandler);# endif#elif (OS_TYPE != OS_DOS)IgnoreInterrupts = FALSE;#endif/* Create terminations queues - OS/2 only */CreateTerminationQueues ();/* Set up character maps */InitialiseCharacterTypes ();/* Create the integer variables, in case they are loaded from the* environment*/CreateIntegerVariables ();/* Initialise Path Extension lists */BuildExtensionLists ();/* For NT, we need to know now if this is a level 0 shell because NT has a* nasty habit of being case in-sensitive! Not only for file names, but* also environment variables. So scan the command line for the -0 option!*/#if (OS_TYPE == OS_NT)while ((sc = GetOptions (argc, argv, ShellOptions, 0)) != EOF){if (sc == '0'){Level0 = TRUE;break;}}ResetGetOptions ();#endif/* Load the environment into our structures */if ((ap = environ) != NOWORDS){#if (OS_TYPE == OS_NT)const char *paths[PATHSMAX];int pathcnt = 0;#endiffor (ap = environ; *ap != (char *)NULL; ap++){/* Set up any variables. Note there is an assumption that* AssignVariableFromString sets the equals sign to 0, hiding the value;*/if (!strncmp ("SECONDS=", *ap, 8))continue;/* NT is case in-sensitive - what a PAIN! */#if (OS_TYPE == OS_NT)/* special PATH handling */if (strnicmp ("PATH=", *ap, 5) == 0){if (pathcnt < PATHSMAX)paths[ pathcnt++ ] = *ap;continue; /* next -- process on exit */}if (Level0){s = *ap;while (*s && (*s != CHAR_ASSIGN)){ /* convert to upper case */*s = (char)toupper (*s);s++;}}#endifif (AssignVariableFromString (*ap, (int *)NULL))SetVariableStatus (*ap, STATUS_EXPORT);}/*** Can not access varibales from the environment** Set up debug level*/debug_level = GetVariableAsNumeric ("SHDEBUGEXEC" );#if (OS_TYPE == OS_NT)if (pathcnt){ /* import */const char *p; /* path and alternative */int dbg = (debug_level & DEBUG_MULTIPATH);p = paths[0];if (pathcnt > 1){int err, i;int pl, al;pl = -1;for (i=0; i<pathcnt; i++) { /* select greater */if ((al = strlen(paths[i])) > pl) {p = paths[i];pl = al;}}if (dbg) {fprintf ( stderr, "Encountered %d path definitions ...\n", pathcnt );}for (err=0, i=0; i<pathcnt; i++) {/* diff alternatives */if (dbg) {fprintf ( stderr, "[%d] %s", i, paths[i] );}if (p == paths[i]) {if (dbg) { /* .. selection */fprintf ( stderr, " .. using.\n" );}} else {int ret;if ((ret = SubPath(p, paths[i])) == -1){ /* .. different */if (dbg) {fprintf ( stderr, " .. different!\n" );}if (!FL_TEST (FLAG_WARNING)) {if (err++ == 0)PrintWarningMessage ( "sh: %d path definitions encountered.", pathcnt );PrintWarningMessage ( "sh: ignoring differing alternative '%.4s'\n %s", paths[i], paths[i] );}} else if (ret == 0) {if (dbg) { /* .. same value */fprintf ( stderr, " .. same.\n" );}} else { /* .. substr */if (dbg) {fprintf ( stderr, " .. substr.\n" );}}}}if (err) {PrintWarningMessage ( "sh: using '%.4s'\n %s.", p, p );}}else if ( dbg ){fprintf ( stderr, "Only one PATH definition seen\n", pathcnt );}SetVariableFromString (PathLiteral, (char *)(p + PATHOFF));SetVariableStatus (PathLiteral, STATUS_EXPORT);}if (GetVariableAsString (UserLiteral, FALSE) == null){ /* set user */char name[200];u_long size = sizeof(name);if (GetUserName( name, &size )){SetVariableFromString (UserLiteral, name);SetVariableStatus (UserLiteral, STATUS_EXPORT);}}#endif /*OS_NT*/}/* Change COMSPEC to unix format for execution */PATH_TO_UNIX (GetVariableAsString (ComspecVariable, FALSE));SetVariableStatus (ComspecVariable, STATUS_CONVERT_MSDOS);/* Zap all files */CloseAllHandlers ();MemoryAreaLevel = 1;/* Get the current directory */GetCurrentDirectoryPath ();/* Initialise the getopts command */ResetGetoptsValues (TRUE);/* Set up SHELL variable. First check for a restricted shell. Check the* restricted shell*/SetVariableFromString (LastWordVariable, name);/* For OS/2, we prefer the full path name and not that in argv[0]. Save a* copy of the string*/#if (OS_TYPE == OS_OS2) || (OS_TYPE == OS_NT)if (_APgmName){PATH_TO_UNIX ((name = GetAllocatedSpace (strlen (_APgmName) + 4)));SetMemoryAreaNumber ((void *)name, 0);strcpy (name, _APgmName);}#endif/* Has the program name got a .exe extension - Yes probably DOS 3+. So* save it as the Shell name. Under OS/2, make sure we use .exe*/if (GetVariableAsString (ShellVariableName, FALSE) == null){#if (OS_TYPE == OS_OS2) || (OS_TYPE == OS_NT)if ((s1 = strrchr (name, CHAR_PERIOD)) == (char *)NULL)strcat (name, EXEExtension);SetVariableFromString (ShellVariableName, name);ReleaseMemoryCell (name);#elif (OS_TYPE == OS_DOS)if (((s1 = strrchr (name, CHAR_PERIOD)) != (char *)NULL) &&(stricmp (s1 + 1, EXEExtension + 1) == 0))SetVariableFromString (ShellVariableName, PATH_TO_UNIX (name));#elif (OS_TYPE == OS_UNIX)SetVariableFromString (ShellVariableName,(*name == '-') ? name + 1 : name);#endif}/* Default if necessary */if (GetVariableAsString (ShellVariableName, FALSE) == null)SetVariableFromString (ShellVariableName, shellname);PATH_TO_UNIX (s1 = GetVariableAsString (ShellVariableName, FALSE));/* Check for restricted shell */if ((s = FindLastPathCharacter (s1)) == (char *)NULL)s = s1;elses++;if (*s == 'r')OptionsRflag = TRUE;/* Set up home directory */if (GetVariableAsString (HomeVariableName, FALSE) == null){if ((s = GetVariableAsString ("INIT", FALSE)) == null)s = CurrentDirectory->value;SetVariableFromString (HomeVariableName, s);}/* Set up OS Mode */#if (OS_TYPE == OS_UNIX) || defined(WIN32)SetVariableFromNumeric (LIT_OSmode, 3L);#elif defined (__TURBOC__)SetVariableFromNumeric (LIT_OSmode, 0L);#elif (__WATCOMC__)SetVariableFromNumeric (LIT_OSmode, (long)OS_TYPE - 1);#elseSetVariableFromNumeric (LIT_OSmode, (long)_osmode);#endif#if (OS_SIZE == OS_32)SetVariableFromNumeric (LIT_SHmode, 32L);#elseSetVariableFromNumeric (LIT_SHmode, 16L);#endif/* Set up history file location */SetVariableFromNumeric (LIT_Dollar, (long)getpid ());LoadGlobalVariableList ();PATH_TO_UNIX (GetVariableAsString (PathLiteral, FALSE));PATH_TO_UNIX (GetVariableAsString (CDPathLiteral, FALSE));return OptionsRflag;}/** Mail Check processing. Every $MAILCHECK seconds, we check either $MAIL* or $MAILPATH to see if any file has changed its modification time since* we last looked. In $MAILCHECK, the files are separated by semi-colon (;).* If the filename contains a %, the string following the % is the message* to display if the file has changed.*/static void F_LOCAL CheckForMailArriving (void){int delay = (int)GetVariableAsNumeric (MailCheckVariable);char *mail = GetVariableAsString ("MAIL", FALSE);char *mailp = GetVariableAsString ("MAILPATH", FALSE);static time_t last = 0L;time_t current = time ((time_t *)NULL);struct stat st;char *cp, *sp, *ap;/* Have we waited long enough */if (((current - last) < delay) || (DisabledVariables & DISABLE_MAILCHECK))return;/* Yes - Check $MAILPATH. If it is defined, process it. Otherwise, use*/if (mailp != null){/* Check MAILPATH */sp = mailp;/* Look for the next separator */while ((cp = strchr (sp, CHAR_PATH_SEPARATOR)) != (char *)NULL){*cp = 0;/* % in string ? */if ((ap = strchr (sp, CHAR_FORMAT)) != (char *)NULL)*ap = 0;/* Check the file name */if ((S_stat (sp, &st)) && (st.st_mtime > last) && st.st_size){feputs ((ap != (char *)NULL) ? ap + 1 : ymail);feputc (CHAR_NEW_LINE);}/* Restore the % */if (ap != (char *)NULL)*ap = CHAR_FORMAT;/* Restore the semi-colon and find the next one */*cp = CHAR_PATH_SEPARATOR;sp = cp + 1;}}/* Just check MAIL */else if ((mail != null) && (S_stat (mail, &st)) &&(st.st_mtime > last) && st.st_size){feputs (ymail);feputc (CHAR_NEW_LINE);}/* Save the last check time */last = current;}/** Preprocess Argv to get handle of options in the format /x** Some programs invoke the shell using / instead of - to mark the options.* We need to convert to -. Also /c is a special case. The rest of the* command line is the command to execute. So, we get the command line* from the original buffer instead of argv array.*/#if (OS_TYPE != OS_UNIX)static void F_LOCAL Pre_Process_Argv (char **argv, int *argc1){int argc = 1;char *ocl = _ACmdLine;int i;/* Check for these options */while ((*++argv != (char *)NULL) && (strlen (*argv) == 2) &&(**argv == '/')){argc++;*strlwr (*argv) = CHAR_SWITCH;/* Get the original information from the command line */if ((*argv)[1] == 'c'){/** In some case under OS/2, we get /c, but with EMX style. So all we need* to do is change the / to a - and return.*/# if (OS_TYPE == OS_OS2)if (EMXStyleParameters)return;# endif/** Otherwise, parse the command line again, looking for the /c*/while ((*ocl != '/') && (*(ocl + 1) != 'c') &&(*ocl) && (*ocl != CHAR_RETURN))++ocl;if (*ocl != '/')continue;/* Find the start of the string */ocl += 2;while (isspace (*ocl) && (*ocl != CHAR_RETURN))++ocl;if (*ocl == CHAR_RETURN)continue;/* Found the start. Set up next parameter and ignore the rest */if (*(argv + 1) == (char *)NULL)continue;argc++;/* Remove quotes from string, if they are there */if ((*ocl == CHAR_DOUBLE_QUOTE) &&(ocl[i = (strlen (ocl) - 1)] == CHAR_DOUBLE_QUOTE)){ocl[i] = 0;ocl++;}/* Set up new argument array */*(argv + 1) = ocl;*(argv + 2) = (char *)NULL;*argc1 = argc;if ((ocl = strchr (ocl, CHAR_RETURN)) != (char *)NULL)*ocl = 0;return;}}}#endif/** Convert path format to/from UNIX*/char *ConvertPathToFormat (char *path, char in, char out){#if (OS_TYPE == OS_UNIX)return path;#elsechar *s = path;while ((path = strchr (path, in)) != (char *)NULL)*path = out;return s;#endif}/* Load profiles onto I/O Stack */static void F_LOCAL LoadTheProfileFiles (void){char *name;char *Pname;if ((Pname = GetVariableAsString ("ETCPROFILE", FALSE)) == null){Pname = "x:/etc/profile";*Pname = GetDriveLetter (GetRootDiskDrive ());}InteractiveFlag = TRUE;ExecuteShellScript (Pname);/** Check $HOME format. If in DOS format, mark and convert to UNIX*/if (strchr (name = GetVariableAsString (HomeVariableName, FALSE),CHAR_DOS_PATH) != (char *)NULL){PATH_TO_UNIX (name);SetVariableStatus (HomeVariableName, STATUS_CONVERT_MSDOS);}name = BuildFileName ("profile"); /* Set up home profile */ExecuteShellScript (name);ReleaseMemoryCell ((void *)name);}/** Convert Unix PATH to MSDOS PATH*/static void F_LOCAL ConvertUnixPathToMSDOS (char *cp){char *scp = cp;int colon = 0;/* If there is a semi-colon or a backslash, we assume this is a DOS format* path*/if ((strchr (cp, CHAR_PATH_SEPARATOR) != (char *)NULL) ||(strchr (cp, CHAR_DOS_PATH) != (char *)NULL))return;/* Count the number of colons */while ((cp = strchr (cp, CHAR_COLON)) != (char *)NULL){++colon;++cp;}/* If there are no colons or there is one colon as the second character, it* is probably an MSDOS path*/cp = scp;if ((colon == 0) || ((colon == 1) && (*(cp + 1) == CHAR_COLON)))return;/* Otherwise, convert all colons to semis */while ((cp = strchr (cp, CHAR_COLON)) != (char *)NULL)*(cp++) = CHAR_PATH_SEPARATOR;}/* Generate a file name from a directory and name. Return null if an error* occurs or some malloced space containing the file name otherwise*/char *BuildFileName (char *name){char *dir = GetVariableAsString (HomeVariableName, FALSE);char *cp;/* Get some space */if ((cp = AllocateMemoryCell (strlen (dir) + strlen (name) + 2))== (char *)NULL)return null;/* Addend the directory and a / if the directory does not end in one */strcpy (cp, dir);if (!IsPathCharacter (cp[strlen (cp) - 1]))strcat (cp, DirectorySeparator);/* Append the file name */return strcat (cp, name);}/* Clear prompts */static void F_LOCAL ClearUserPrompts (void){ClearVariableStatus (PS1, STATUS_EXPORT);ClearVariableStatus (PS2, STATUS_EXPORT);ClearVariableStatus (PS3, STATUS_EXPORT);SetVariableFromString (PS1, null);SetVariableFromString (PS2, null);SetVariableFromString (PS3, null);}/* Process setting of SECONDS and RANDOM environment variables */static void F_LOCAL SecondAndRandomEV (char *name, long val){if (!strcmp (name, SecondsVariable) &&!(DisabledVariables & DISABLE_SECONDS))ShellStartTime = time ((time_t *)NULL) - val;else if (!strcmp (name, RandomVariable) &&!(DisabledVariables & DISABLE_RANDOM))srand ((int)val);}/** Set up the Window name. Give up if it does not work.*/#if (OS_TYPE == OS_OS2)void SetWindowName (char *title){HSWITCH hswitch;SWCNTRL swctl;char *cp;if ((!(hswitch = WinQuerySwitchHandle (0, getpid ()))) ||(WinQuerySwitchEntry (hswitch, &swctl)))return;if (title != (char *)NULL)cp = title;else if ((DisabledVariables & DISABLE_WINTITLE) ||((cp = GetVariableAsString (WinTitleVariable, FALSE)) == null))cp = DefaultWinTitle;strncpy (swctl.szSwtitle, cp, sizeof (swctl.szSwtitle));swctl.szSwtitle[sizeof (swctl.szSwtitle) - 1] = 0;WinChangeSwitchEntry (hswitch, &swctl);}#endif/* NT Version */#if (OS_TYPE == OS_NT)void SetWindowName (char *title){char *cp;if (title != (char *)NULL)cp = title;else if ((DisabledVariables & DISABLE_WINTITLE) ||((cp = GetVariableAsString (WinTitleVariable, FALSE)) == null))cp = DefaultWinTitle;SetConsoleTitle (cp);}#endif/** In OS/2, check for terminated processes*/#if (OS_TYPE == OS_OS2)static void F_LOCAL CheckForTerminatedProcess (void){RESULTCODES rescResults;PID pidProcess;char *s;OSCALL_RET rc;# if (OS_TYPE == OS_OS2)CheckForSessionEnd ();# endif/* Check for tasks terminating */while (TRUE){while ((rc = DosCwait (DCWA_PROCESSTREE, DCWW_NOWAIT, &rescResults,&pidProcess, 0)) == ERROR_INTERRUPT)continue;/* Ignore errors */if (rc)return;/* Remove the job */DeleteJob (pidProcess);switch (rescResults.codeTerminate){case TC_EXIT:s = "Normal Exit";break;case TC_HARDERROR:s = "Hard error";break;case TC_TRAP:s = "Trapped";break;case TC_KILLPROCESS:s = "Killed";break;default:s = "Unknown";break;}fprintf (stderr, "Process %d terminated - %s (%d)\n", pidProcess, s,rescResults.codeTerminate);}}#endif/* NT Version */#if (OS_TYPE == OS_NT)static void F_LOCAL CheckForTerminatedProcess (void){twalk (JobTree, LookUpJobs);}/** Walk the NT job tree looking for terminated processes*/static void LookUpJobs (const void *key, VISIT visit, int level){JobList *job = *(JobList **)key;HANDLE hp;DWORD res;DWORD ExitCode;(void) level;if ((visit == postorder) || (visit == leaf)){if ((hp = OpenProcess (PROCESS_ALL_ACCESS, (BOOL)TRUE,job->pid)) == NULL){PrintWarningMessage ("sh: Cannot open process %d\n%s", job->pid,GetOSSystemErrorMessage (GetLastError ()));DeleteJob (job->pid);}/* Wait for the object to exit */else if ((res = WaitForSingleObject (hp, 0)) == WAIT_OBJECT_0){DeleteJob (job->pid);if (!GetExitCodeProcess (hp, &ExitCode))PrintWarningMessage ("sh: Cannot get termination code for process %d\n%s",job->pid, GetOSSystemErrorMessage (GetLastError ()));elsefprintf (stderr, "Process %d terminated (%ld)\n", job->pid,ExitCode);}/* Failed? - error */else if (res == WAIT_FAILED){PrintWarningMessage ("sh: Cannot wait for process %d\n%s",job->pid,GetOSSystemErrorMessage (GetLastError ()));DeleteJob (job->pid);}CloseHandle (hp);}}#endif/* UNIX version */#if (OS_TYPE == OS_UNIX)static void F_LOCAL CheckForTerminatedProcess (void){fputs ("UNIX: CheckForTerminatedProcess NI\n", stderr);}#endif/** Check for end of a Session*/#if (OS_TYPE == OS_OS2)static void F_LOCAL CheckForSessionEnd (void){OSCALL_PARAM DataLength;OSCALL_RET rc;BYTE bElemPriority;struct SessionEnd{unsigned short SessionId;unsigned short ExitCode;} *DataAddress;# if (OS_SIZE == OS_32)REQUESTDATA Request;# elseQUEUERESULT Request;# endif/* Check for sessions terminating */while ((rc = DosReadQueue (SessionQueueHandler, &Request, &DataLength,(PVOID *)&DataAddress,0, DCWW_NOWAIT, &bElemPriority,SessionQueueSema)) == NO_ERROR){DeleteJobBySession (DataAddress->SessionId);fprintf (stderr, "Session %d terminated - Normal Exit (%d)\n",DataAddress->SessionId, DataAddress->ExitCode);# if (OS_SIZE == OS_32)DosFreeMem (DataAddress);# elseDosFreeSeg (SELECTOROF ((DataAddress)));# endif}}#endif/** Set up the Parameter Environment variables*/static void F_LOCAL SetUpParameterEV (int argc, char **argv, char *name){Word_B *wb = (Word_B *)NULL;char *Value;int i;if ((Value = StringSave (name)) == null){fprintf (stderr, BasicErrorMessage, ShellNameLiteral, Outofmemory1);return;}wb = AddParameter (Value, wb, ShellNameLiteral);for (i = 1; i < argc; ++i){if ((!AssignVariableFromString (argv[i], (int *)NULL)) &&(wb != (Word_B *)NULL)){if ((Value = StringSave (argv[i])) != null)wb = AddParameter (Value, wb, ShellNameLiteral);else{fprintf (stderr, BasicErrorMessage, ShellNameLiteral,Outofmemory1);return;}}}if (wb != (Word_B *)NULL)wb = AddParameter ((char *)NULL, wb, ShellNameLiteral);}/** Update the Seconds and Random variables*/void HandleSECONDandRANDOM (void){if (!(DisabledVariables & DISABLE_SECONDS))LookUpVariable (SecondsVariable, 0, TRUE);if (!(DisabledVariables & DISABLE_RANDOM))LookUpVariable (RandomVariable, 0, TRUE);}/** Set the status of an environment variable*/void SetVariableStatus (char *name, /* Variable name */int flag) /* New status */{VariableList *vp = LookUpVariable (name, 0, TRUE);if (IS_VariableFC ((int)*vp->name)) /* not an internal symbol ($# etc) */vp->status |= flag;}/** Array version - only 0 is exported*/void SetVariableArrayStatus (char *name, /* Variable name */int index, /* Array index */int flag) /* New status */{VariableList *vp = LookUpVariable (name, index, TRUE);if (IS_VariableFC ((int)*vp->name)) /* not an internal symbol ($# etc) */{vp->status |= flag;if (index)vp->status &= ~STATUS_EXPORT;}}/** Set the status of an environment variable*/void ClearVariableStatus (char *name, int flag){VariableList *vp = LookUpVariable (name, 0, TRUE);if (IS_VariableFC ((int)*vp->name)) /* not an internal symbol ($# etc) */vp->status &= ~flag;}/** Check allowed to set variable*/static bool F_LOCAL AllowedToSetVariable (VariableList *vp){if (vp->status & STATUS_READONLY){ShellErrorMessage (LIT_2Strings, vp->name, LIT_IsReadonly);return FALSE;}/* Check for $PATH, $SHELL or $ENV reset in restricted shell */if ((!strcmp (vp->name, PathLiteral) || !strcmp (vp->name, ENVVariable) ||!strcmp (vp->name, ShellVariableName)) &&CheckForRestrictedShell (PathLiteral))return FALSE;return TRUE;}/** Set up a variable from a string*/void SetVariableFromString (char *name, char *val){SetVariableArrayFromString (name, 0, val);}/** Array index version*/void SetVariableArrayFromString (char *name, int Index, char *val){VariableList *vp = LookUpVariable (name, Index, TRUE);char *xp = null;long nval;/* Check if allowed to set variable */if (!AllowedToSetVariable (vp))return;/* If we change the PATH to a new value, we need to untrack some aliases */if (!strcmp (name, PathLiteral) && strcmp (vp->value, val))UnTrackAllAliases ();CheckOPTIND (name, atol (val));/* Save the new value */if ((!(vp->status & STATUS_INTEGER)) && (val != null) && strlen (val) &&((xp = StringSave (val = SuppressSpacesZeros (vp, val))) == null))return;/* Free the old value if appropriate */if (vp->value != null)ReleaseMemoryCell ((void *)vp->value);vp->value = null;/* String value? */if (!(vp->status & STATUS_INTEGER)){vp->value = xp;if (!vp->width)vp->width = strlen (val);}/* No - Number value */else if (!ValidMathsExpression (val, &nval)){SecondAndRandomEV (name, nval);SetUpANumericValue (vp, nval, -1);}/* Check to see if it should be exported */if (FL_TEST (FLAG_ALL_EXPORT))vp->status |= STATUS_EXPORT;/* Convert UNIX to DOS for PATH variable */if (!strcmp (name, PathLiteral))ConvertUnixPathToMSDOS (GetVariableAsString (PathLiteral, FALSE));else if (!strcmp (name, CDPathLiteral))ConvertUnixPathToMSDOS (GetVariableAsString (CDPathLiteral, FALSE));else if (!strcmp (name, PathExtsLiteral))BuildExtensionLists ();/* Set up IFS characters */else if (!strcmp (name, IFS))SetCharacterTypes (val, C_IFS);/* Check for title change */#if defined (FLAGS_EMACS) || defined (FLAGS_VI) || defined (FLAGS_GMACS)else if (!strcmp (name, FCEditVariable))SetEditorMode (val);else if (!strcmp (name, EditorVariable))SetEditorMode (val);else if (!strcmp (name, VisualVariable))SetEditorMode (val);#endif#if (OS_TYPE != OS_DOS)else if ((!(DisabledVariables & DISABLE_WINTITLE)) &&(!strcmp (name, WinTitleVariable)))SetWindowName ((char *)NULL);#endif}/** Set a variable from a numeric*/void SetVariableFromNumeric (char *name, long value){SetVariableArrayFromNumeric (name, 0, value);}/* Array version */void SetVariableArrayFromNumeric (char *name, int Index, long value){VariableList *vp = LookUpVariable (name, Index, TRUE);char NumericBuffer[20];/* Check if allowed to set variable */if (!AllowedToSetVariable (vp))return;CheckOPTIND (name, value);if (!(vp->status & STATUS_INTEGER)){sprintf (NumericBuffer, "%ld", value);SetVariableFromString (name, NumericBuffer);}/* Save the integer value */elseSetUpANumericValue (vp, value, -1);SecondAndRandomEV (name, value);}/** Get variable as a numeric*/long GetVariableAsNumeric (char *name){return GetVariableArrayAsNumeric (name, 0);}/* Array version */long GetVariableArrayAsNumeric (char *name, int Index){VariableList *vp = LookUpVariable (name, Index, FALSE);if (vp->status & STATUS_INTEGER)return vp->nvalue;elsereturn atol (vp->value);}/** Get variable as a formatted string*/char *GetVariableAsString (char *name, bool Format){return GetVariableArrayAsString (name, 0, Format);}/** Indexed version*/char *GetVariableArrayAsString (char *name, int Index, bool Format){VariableList *vp = LookUpVariable (name, Index, FALSE);char *Value = vp->value;char *xp;size_t len;char *NumericBuffer;if (vp->status & STATUS_INTEGER){if ((NumericBuffer = GetAllocatedSpace (40)) == (char *)NULL)return null;if (vp->base != 10){sprintf (NumericBuffer, LIT_BNumber, vp->base);xp = NumericBuffer + strlen (NumericBuffer);}elsexp = NumericBuffer;ltoa (vp->nvalue, xp, vp->base);return NumericBuffer;}/* Handle a string variable, if no formating required, return it */if (!Format)return vp->value;/* Left justify ? */if (vp->status & STATUS_LEFT_JUSTIFY){xp = SuppressSpacesZeros (vp, Value);if ((Value = GetAllocatedSpace (vp->width + 1)) == (char *)NULL)return null;memset (Value, CHAR_SPACE, vp->width);Value[vp->width] = 0;if ((len = strlen (xp)) > vp->width)len = vp->width;memcpy (Value, xp, len);}/* Right justify ? */else if (vp->status & (STATUS_RIGHT_JUSTIFY | STATUS_ZERO_FILL)){if ((xp = GetAllocatedSpace (vp->width + 1)) == (char *)NULL)return null;if ((len = strlen (Value)) < vp->width){memset (xp, ((vp->status & STATUS_ZERO_FILL) &&(isdigit (*Value))) ? '0' : CHAR_SPACE, vp->width);memcpy (xp + (vp->width - len), Value, len);}elsememcpy (xp, Value + vp->width - len, vp->width);*(xp + vp->width) = 0;Value = xp;}/* Handle upper and lower case conversions */if (vp->status & STATUS_LOWER_CASE)Value = strlwr (StringCopy (Value));if (vp->status & STATUS_UPPER_CASE)Value = strupr (StringCopy (Value));return Value;}/** Set up a numeric value*/static void F_LOCAL SetUpANumericValue (VariableList *vp, long value, int base){vp->nvalue = value;vp->status |= STATUS_INTEGER;if (vp->base == 0)vp->base = (base > 1) ? base: ((LastNumberBase != -1) ? LastNumberBase: 10);if (vp->value != null)ReleaseMemoryCell ((void *)vp->value);vp->value = null;}/** Suppress leading spaces and zeros*/static char * F_LOCAL SuppressSpacesZeros (VariableList *vp, char *value){if (vp->status & STATUS_LEFT_JUSTIFY){while (*value == CHAR_SPACE)value++;if (vp->status & STATUS_ZERO_FILL){while (*value == '0')value++;}}return value;}/** Check to see if a reset of CheckOPTIND has occured*/static void F_LOCAL CheckOPTIND (char *name, long value){if ((value == 1) && (!(DisabledVariables & DISABLE_OPTIND)) &&(strcmp (OptIndVariable, name) == 0))ResetGetoptsValues (FALSE);}/** Initialise the Integer variables by creating them*/static void F_LOCAL CreateIntegerVariables (void){struct ShellVariablesInit *wp = InitialiseShellVariables;while (wp->Name != (char *)NULL){SetVariableStatus (wp->Name, wp->Status);if (wp->CValue != null)SetVariableFromString (wp->Name, wp->CValue);wp++;}}/** Close up and exit*/void FinalExitCleanUp (int status){#if (OS_TYPE == OS_OS)# if (OS_SIZE == OS_32)DosCloseEventSem (SessionQueueSema);DosCloseQueue (SessionQueueHandler);# elseDosCloseSem (SessionQueueSema);DosCloseQueue (SessionQueueHandler);# endif#endifexit (status);}/** Create Session termination semaphores and queues. Also get the number* file handlers.*/#if (OS_TYPE == OS_OS2)static void F_LOCAL CreateTerminationQueues (void){int count = 0;static char Name[25];OSCALL_RET rc;# if (OS_SIZE == OS_32)LONG ReqCount = 0; /* Increment */ULONG CurMaxFH; /* Available File handlers */DosSetRelMaxFH (&ReqCount, &CurMaxFH);MaxNumberofFDs = min (CurMaxFH, 32 + FDBASE);# endif/* Create semaphore for queue */while (TRUE){# if (OS_SIZE == OS_32)sprintf (Name, "\\SEM32\\SHELL\\%.5d", count++);# elsesprintf (Name, "\\SEM\\SHELL\\%.5d", count++);# endif# if (OS_SIZE == OS_32)if ((rc = DosCreateEventSem (Name, &SessionQueueSema,DC_SEM_SHARED, TRUE)) == NO_ERROR)break;# elseif ((rc = DosCreateSem (CSEM_PUBLIC, &SessionQueueSema,Name)) == NO_ERROR){DosSemClear (SessionQueueSema);break;}# endif/* Check for error */# if (OS_SIZE == OS_32)if (rc != ERROR_DUPLICATE_NAME)# elseif (rc != ERROR_ALREADY_EXISTS)# endif{SessionQueueSema = 0;PrintErrorMessage ("DosCreateSem: Cannot create semaphore\n%s",GetOSSystemErrorMessage (rc));}}/* Create the queue */count = 0;while (TRUE){# if (OS_SIZE == OS_32)sprintf (Name, "\\QUEUES\\SHELL\\%.5d", count++);# elsesprintf (Name, "\\QUEUES\\SHQ%.5d", count++);# endifif ((rc = DosCreateQueue (&SessionQueueHandler,#ifdef QUE_CONVERT_ADDRESSQUE_FIFO | QUE_CONVERT_ADDRESS,#elseQUE_FIFO,#endifName)) == NO_ERROR)break;/* Check for error */if (rc != ERROR_QUE_DUPLICATE){SessionQueueHandler = 0;PrintErrorMessage ("DosCreateQueue: Cannot create queue\n%s",GetOSSystemErrorMessage (rc));}}SessionEndQName = Name;}#endif/** Set up Edit mode*/#if defined (FLAGS_EMACS) || defined (FLAGS_VI) || defined (FLAGS_GMACS)static void F_LOCAL SetEditorMode (char *ed){char *rcp;if ((rcp = strrchr (ed, '/')) != (char *)NULL)ed = rcp + 1;# ifdef FLAGS_EMACSif (strstr (ed, "emacs")){ShellGlobalFlags &= ~FLAGS_EDITORS;ShellGlobalFlags |= FLAGS_EMACS;}# endif# ifdef FLAGS_VIif (strstr (ed, "vi")){ShellGlobalFlags &= ~FLAGS_EDITORS;ShellGlobalFlags |= FLAGS_VI;}# endif# ifdef FLAGS_GMACSif (strstr (ed, "gmacs")){ShellGlobalFlags &= ~FLAGS_EDITORS;ShellGlobalFlags |= FLAGS_GMACS;}# endif}#endif