/* * MS-DOS SHELL - Internal Command Processing * * 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. The * code for the test (dotest) command was based on code written by * Erik Baalbergen. The following copyright conditions apply: * * 1. Redistribution and use in source and binary forms are permitted * provided that the above copyright notice is duplicated in the * source form and the copyright notice in file sh6.c is displayed * on entry to the program. * * 2. The sources (or parts thereof) or objects generated from the sources * (or parts of sources) cannot be sold under any circumstances. * * $Header: /cvsroot/device/DEVL/UTILS/SH/Sh7.c,v 1.2 2004/05/10 09:30:07 ayoung Exp $ * * $Log: Sh7.c,v $ * 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.1 2001/07/20 05:55:44 ayoung * WIN32 support * * Revision 1.1.1.1 1999/12/02 01:11:12 gordonh * UTIL * * Revision 2.18 1994/08/25 20:49:11 istewart * MS Shell 2.3 Release * * Revision 2.17 1994/02/23 09:23:38 istewart * Beta 233 updates * * Revision 2.16 1994/02/01 10:25:20 istewart * Release 2.3 Beta 2, including first NT port * * Revision 2.15 1994/01/20 14:51:43 istewart * Release 2.3 Beta 1 * * Revision 2.14 1994/01/11 17:55:25 istewart * Release 2.3 Beta 0 patches * * Revision 2.13 1993/12/01 11:58:34 istewart * Release 226 beta * * 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 21:33:35 Ian_Stewartson * MS-Shell 2.0 Baseline release * */ #include #include #if defined (__EMX__) # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sh.h" #define SECS 60L #define MINS 3600L #if (OS_TYPE == OS_OS2) # if defined (__EMX__) # define PGM_TITLE_TYPE unsigned char # else # define PGM_TITLE_TYPE char # endif #endif /* Definitions for echo and print */ #define ECHO_ESCAPE 0x01 #define ECHO_NO_EOL 0x02 #define ECHO_HISTORY 0x04 /* Definitions for test */ #define END_OF_INPUT 0 #define FILE_READABLE 1 #define FILE_WRITABLE 2 #define FILE_REGULAR 3 #define FILE_DIRECTRY 4 #define FILE_NONZERO 5 #define FILE_TERMINAL 6 #define STRING_ZERO 7 #define STRING_NONZERO 8 #define STRING_EQUAL 9 #define STRING_NOTEQUAL 10 #define NUMBER_EQUAL 11 #define NUMBER_NOTEQUAL 12 #define NUMBER_EQ_GREAT 13 #define NUMBER_GREATER 14 #define NUMBER_EQ_LESS 15 #define NUMBER_LESS 16 #define UNARY_NOT 17 #define BINARY_AND 18 #define BINARY_OR 19 #define LPAREN 20 #define RPAREN 21 #define OPERAND 22 #define FILE_EXECUTABLE 23 #define FILE_USER 24 #define FILE_GROUP 25 #define FILE_TEXT 26 #define FILE_BLOCK 27 #define FILE_CHARACTER 28 #define FILE_FIFO 29 #define FILE_NEWER 30 #define FILE_OLDER 31 #define STRING_LESS 32 #define STRING_GREATER 33 #define FILE_EXISTS 34 #define TEST_OPTION 35 #define FILE_SYMBOLIC 36 #define FILE_OWNER 37 #define FILE_GROUPER 38 #define FILE_SOCKET 39 #define FILE_EQUAL 40 #define UNARY_OP 1 #define BINARY_OP 2 #define B_UNARY_OP 3 #define B_BINARY_OP 4 #define PAREN 5 /* This is the list of operators and the conversion values */ static struct TestOperator { char *OperatorName; short OperatorID; short OperatorType; } ListOfTestOperators[] = { /* These two entries are modified depending on the test program. The * alternative values are shown in the following comment. */ {"-a", FILE_EXISTS, UNARY_OP}, /* {"-a", BINARY_AND, B_BINARY_OP}, */ {"-o", TEST_OPTION, UNARY_OP}, /* {"-o", BINARY_OR, B_BINARY_OP}, */ /* Add new entries after here */ {"-r", FILE_READABLE, UNARY_OP}, {"-w", FILE_WRITABLE, UNARY_OP}, {"-x", FILE_EXECUTABLE, UNARY_OP}, {"-f", FILE_REGULAR, UNARY_OP}, {"-d", FILE_DIRECTRY, UNARY_OP}, {"-s", FILE_NONZERO, UNARY_OP}, {"-t", FILE_TERMINAL, UNARY_OP}, {"-z", STRING_ZERO, UNARY_OP}, {"-n", STRING_NONZERO, UNARY_OP}, {"=", STRING_EQUAL, BINARY_OP}, {"!=", STRING_NOTEQUAL, BINARY_OP}, {"<", STRING_LESS, BINARY_OP}, {">", STRING_GREATER, BINARY_OP}, {"-eq", NUMBER_EQUAL, BINARY_OP}, {"-ne", NUMBER_NOTEQUAL, BINARY_OP}, {"-ge", NUMBER_EQ_GREAT, BINARY_OP}, {"-gt", NUMBER_GREATER, BINARY_OP}, {"-le", NUMBER_EQ_LESS, BINARY_OP}, {"-lt", NUMBER_LESS, BINARY_OP}, {"!", UNARY_NOT, B_UNARY_OP}, {"(", LPAREN, PAREN}, {")", RPAREN, PAREN}, {"&&", BINARY_AND, B_BINARY_OP}, {"||", BINARY_OR, B_BINARY_OP}, {"-c", FILE_CHARACTER, UNARY_OP}, {"-b", FILE_BLOCK, UNARY_OP}, {"-u", FILE_USER, UNARY_OP}, {"-g", FILE_GROUP, UNARY_OP}, {"-k", FILE_TEXT, UNARY_OP}, {"-p", FILE_FIFO, UNARY_OP}, {"-h", FILE_SYMBOLIC, UNARY_OP}, {"-L", FILE_SYMBOLIC, UNARY_OP}, {"-O", FILE_OWNER, UNARY_OP}, {"-G", FILE_GROUPER, UNARY_OP}, {"-S", FILE_SOCKET, UNARY_OP}, {"-nt", FILE_NEWER, BINARY_OP}, {"-ot", FILE_OLDER, BINARY_OP}, {"-ef", FILE_EQUAL, BINARY_OP}, {(char *)NULL, 0, 0} }; /* * -o values for set */ static struct SetOptions { char *OptionName; /* Option name */ unsigned int FlagValue; /* Option flag */ bool HasOptionValue; /* Has -x value */ } SetOptions[] = { { "alternation", FLAGS_ALTERNATION, FALSE }, { "allexport", 'a', TRUE }, #ifdef FLAGS_BREAK_SWITCH { "break", FLAGS_BREAK_SWITCH, FALSE }, #endif #ifdef FLAGS_EMACS { "emacs", FLAGS_EMACS, FALSE }, #endif { "errexit", 'e', TRUE }, #ifdef FLAGS_GMACS { "gmacs", FLAGS_GMACS, FALSE }, #endif #if (OS_TYPE == OS_NT) || (OS_TYPE == OS_OS2) { "ignorecase", FLAGS_NOCASE, FALSE }, #endif { "ignoreeof", FLAGS_IGNOREEOF, FALSE }, { "keyword", 'k', TRUE }, { "markdirs", FLAGS_MARKDIRECTORY, FALSE }, { "msdos", FLAGS_MSDOS_FORMAT, FALSE }, { "noclobber", FLAGS_NOCLOBER, FALSE }, { "noexec", 'n', TRUE }, { "noglob", 'f', TRUE }, { "nounset", 'u', TRUE }, #ifdef FLAGS_SET_OS2 { "os2", FLAGS_SET_OS2, FALSE }, #endif { "privileged", 'p', TRUE }, #if (OS_TYPE == OS_NT) || (OS_TYPE == OS_OS2) { "realpipes", FLAGS_REALPIPES, FALSE }, #endif { "trackall", 'h', TRUE }, #ifdef FLAGS_VI { "vi", FLAGS_VI, FALSE }, #endif { "verbose", 'v', TRUE }, { "verify", FLAGS_VERIFY_SWITCH, FALSE }, #ifdef FLAGS_SET_NT { "winnt", FLAGS_SET_NT, FALSE }, #endif { "xtrace", 'x', TRUE }, { (char *)NULL, 0, FALSE } }; /* * Getopts values */ static GetoptsIndex GetOptsIndex = { 1, 1 }; /* * Signal Name structure * * Note that the first two entries are constructed such that the character * before the name is a ~. */ #define MAX_TRAP_SIGNALS ARRAY_SIZE (TrapSignalList) #define SIG_SPECIAL -1 /* Error or trap */ #define SIG_NO_MAP -2 /* No DOS mapping */ #if (OS_TYPE == OS_UNIX) #define MAX_SIG_MAP NSIG #else #define MAX_SIG_MAP ARRAY_SIZE (UnixToDosSignals) #endif /* * Signal name to number mapping */ static struct TrapSignalList { char *name; int signo; } TrapSignalList [] = { { Trap_DEBUG + 1, SIG_SPECIAL }, { Trap_ERR + 1, SIG_SPECIAL }, { LIT_exit, 0 }, { "SIGINT", SIGINT }, { "SIGFPE", SIGFPE }, { "SIGILL", SIGILL }, { "SIGSEGV", SIGSEGV }, { "SIGABRT", SIGABRT }, #ifdef SIGTERM { "SIGTERM", SIGTERM }, #endif #ifdef SIGBREAK { "SIGBREAK", SIGBREAK }, #endif #ifdef SIGUSR1 { "SIGUSR1", SIGUSR1 }, #endif #ifdef SIGUSR2 { "SIGUSR2", SIGUSR2 }, #endif #ifdef SIGUSR3 { "SIGUSR3", SIGUSR3 }, #endif #ifdef SIGIDIVZ { "SIGIDIVZ", SIGIDIVZ }, #endif #ifdef SIGIOVFL { "SIGIOVFL", SIGIOVFL }, #endif #if (OS_TYPE == OS_UNIX) { "SIGHUP", SIGHUP }, { "SIGQUIT", SIGQUIT }, { "SIGTRAP", SIGTRAP }, { "SIGIOT", SIGIOT }, { "SIGEMT", SIGEMT }, { "SIGKILL", SIGKILL }, { "SIGBUS", SIGBUS }, { "SIGSYS", SIGSYS }, { "SIGPIPE", SIGPIPE }, { "SIGALRM", SIGALRM }, { "SIGTERM", SIGTERM }, { "SIGUSR1", SIGUSR1 }, { "SIGUSR2", SIGUSR2 }, { "SIGCLD", SIGCLD }, { "SIGPWR", SIGPWR }, { "SIGWINCH", SIGWINCH }, { "SIGURG", SIGURG }, { "SIGPOLL", SIGPOLL }, { "SIGIO", SIGIO }, { "SIGSTOP", SIGSTOP }, { "SIGTSTP", SIGTSTP }, { "SIGCONT", SIGCONT }, { "SIGTTIN", SIGTTIN }, { "SIGTTOU", SIGTTOU }, { "SIGVTALRM", SIGVTALRM }, { "SIGPROF", SIGPROF }, { "SIGXCPU", SIGXCPU }, { "SIGXFSZ", SIGXFSZ }, #endif }; /* * UNIX to DOS signal number mapping. We only have 15 mappings because * only the fdirst 15 signal numbers are common */ #if (OS_TYPE != OS_UNIX) static int UnixToDosSignals [] = { 0, /* 0 - On exit */ SIG_NO_MAP, /* 1 - hangup */ SIGINT, /* 2 - interrupt (DEL) */ SIG_NO_MAP, /* 3 - quit (ASCII FS) */ SIGILL, /* 4 - illegal instruction */ SIG_NO_MAP, /* 5 - trace trap */ SIG_NO_MAP, /* 6 - IOT instruction */ SIG_NO_MAP, /* 7 - EMT instruction */ SIGFPE, /* 8 - floating point exception */ SIG_NO_MAP, /* 9 - kill */ SIG_NO_MAP, /* 10 - bus error */ SIGSEGV, /* 11 - segmentation violation */ SIG_NO_MAP, /* 12 - bad argument to system call */ SIG_NO_MAP, /* 13 - write on a pipe with no reader */ SIG_NO_MAP, /* 14 - alarm clock */ SIGTERM /* 15 - software termination signal */ }; #endif /* * General Functions */ static int DeleteAllVariables (const void *, const void *); static int F_LOCAL doOutofMemory (char *); static int F_LOCAL TestProcessNextExpression (int); static int F_LOCAL TestANDExpression (int); static int F_LOCAL TestPrimaryExpression (int); static int F_LOCAL TestUnaryOperand (int); static int F_LOCAL TestBinaryOperand (void); static int F_LOCAL TestLexicalAnalysis (void); static struct TestOperator * F_LOCAL TestLookupOperator (char *); static long F_LOCAL GetNumberForTest (char *); static void F_LOCAL TestSyntaxError (void); static void F_LOCAL SetVerifyStatus (bool); static void F_LOCAL WhenceLocation (bool, const char *, const char *); static void F_LOCAL WhenceType (const char *); static int F_LOCAL PrintOptionSettings (void); static int F_LOCAL CheckFAccess (const char *, int); static int F_LOCAL CheckFType (const char *, int); static int F_LOCAL CheckFMode (const char *, int); static int F_LOCAL CheckFSize (const char *); static int F_LOCAL CheckForFD (const char *); #if (OS_TYPE != OS_UNIX) static OSCALL_RET F_LOCAL OS_GetFHAttributes (int, OSCALL_PARAM *); #endif #if (OS_TYPE == OS_DOS) static void F_LOCAL SetBreakStatus (bool); #else # define SetBreakStatus(a) #endif static bool F_LOCAL CheckPhysLogical (char *, bool *); static char * F_LOCAL GetPhysicalPath (char *, bool); static bool F_LOCAL ReadALine (int, char *, bool, bool, int *); static bool F_LOCAL WriteOutLine (int); static bool F_LOCAL ChangeOptionValue (char *, bool); static void F_LOCAL SetClearFlag (int, bool); static void F_LOCAL RemoveVariable (char *, int); static int F_LOCAL BreakContinueProcessing (char *, int); static int F_LOCAL SetUpNewParameterVariables (char **, int, int, char *); static int F_LOCAL UsageError (char *); static void F_LOCAL PrintEntry (VariableList *, bool, unsigned int); static int F_LOCAL UpdateVariableStatus (char **, unsigned int); static int F_LOCAL TypesetVariables (char **); static int F_LOCAL ListAllVariables (unsigned int, bool); static int F_LOCAL HandleFunction (char *); static int F_LOCAL TestOptionValue (char *, bool); static int F_LOCAL GetUnitNumber (char *); static struct SetOptions * F_LOCAL LookUpOptionValue (char *); static void DisplayVariableEntry (const void *, VISIT, int); static struct TrapSignalList * F_LOCAL LookupSignalName (char *); #if (OS_TYPE != OS_DOS) static bool F_LOCAL ConvertJobToPid (char *, PID *); # if (OS_TYPE == OS_OS2) static void F_LOCAL DisplayStartData (STARTDATA *); # endif # if (OS_TYPE == OS_OS2) && (OS_SIZE == OS_32) APIRET DosQFileMode (PSZ, PULONG); # if !defined (__EMX__) # define Dos32FlagProcess DosFlagProcess # pragma linkage (DosFlagProcess, far16 pascal) # else USHORT _THUNK_FUNCTION (Dos16FlagProcess) (); # endif extern USHORT Dos32FlagProcess (PID, USHORT, USHORT, USHORT); # endif # if (OS_TYPE == OS_OS2) && (OS_SIZE == OS_16) # define Dos32FlagProcess DosFlagProcess # endif # if (OS_TYPE == OS_NT) int DosQFileMode (const char *, DWORD *); # endif #endif /* * Builtin Commands */ static int doexport (int, char **); static int doreadonly (int, char **); static int domsdos (int, char **); static int dotypeset (int, char **); static int dounalias (int, char **); static int doalias (int, char **); static int dolabel (int, char **); static int dofalse (int, char **); static int dochdir (int, char **); static int dodrive (int, char **); static int doshift (int, char **); static int doumask (int, char **); static int dodot (int, char **); static int doecho (int, char **); static int dolet (int, char **); static int doshellinfo (int, char **); #if (OS_TYPE == OS_OS2) static int dostart (int, char **); #endif #if (OS_TYPE != OS_DOS) static int dodetach (int, char **); static int dokill (int, char **); static int dojobs (int, char **); static int dowait (int, char **); #endif #if defined (FLAGS_EMACS) || defined (FLAGS_GMACS) static int dobind (int, char **); #endif #if (OS_TYPE == OS_OS2) && (OS_SIZE == OS_32) && !defined (__WATCOMC__) static int dotimes (int, char **); #endif #if (OS_TYPE == OS_NT) || (OS_TYPE == OS_UNIX) static int dotimes (int, char **); #endif static int dogetopts (int, char **); static int dopwd (int, char **); static int doswap (int, char **); static int dounset (int, char **); static int dowhence (int, char **); static int dofc (int, char **); static int dotest (int, char **); static int dover (int, char **); static int doread (int, char **); static int doeval (int, char **); static int dotrap (int, char **); static int dobuiltin (int, char **); static int dobreak (int, char **); static int docontinue (int, char **); static int doexit (int, char **); static int doreturn (int, char **); static int doset (int, char **); static int dofunctions (int, char **); static int dohistory (int, char **); /* * TWALK global values for DisplayVariable */ static unsigned int DVE_Mask; static bool DVE_PrintValue; /* * Local data structure for test command */ static char **TestArgumentList; static struct TestOperator *CurrentTestOperator; static jmp_buf TestErrorReturn; static char *TestProgram; static bool NewTestProgram; /* * Static structure for typeset */ static struct TypesetValues { unsigned int Flags_On; unsigned int Flags_Off; int Base; int Width; } TypesetValues; /* * Current position in Getoption string */ static int GetOptionPosition = 1; /* Current position */ static int BadOptionValue = 0; /* The bad option value */ /* * Common strings */ static char *DoubleHypen = "--"; static char *TypeSetUsage = "typeset [ [ [+|-][Hflprtux] ] [+|-][LRZi[n]] [ name [=value] ...]"; static char *NotBuiltinCommand = "not a builtin"; static char *NotAnAlias = "%s: %s is not an alias"; static char *NotValidAlias = "Invalid alias name"; static char *Reply_Array[] = {LIT_REPLY, (char *)NULL}; static char *BadDrive = "%c: bad drive"; static char *ShellInternalCommand = "is a shell internal command"; static char *FCTooLong = "fc: command line too long"; static char LIT_alias[] = "alias"; static char LIT_print[] = "print"; static char LIT_read[] = "read"; static char LIT_shift[] = "shift"; static char LIT_break[] = "break"; static char LIT_builtin[] = "builtin"; static char LIT_devfd[] = "/dev/fd/"; static char *BuiltinUsage = "builtin [ -ads ] [ commands ]"; static char *WhenceUsage = "whence [ -pvt ] [ commands ]"; static char LIT_continue[] = "continue"; static char LIT_type[] = "type"; static char LIT_unalias[] = "unalias"; static char LIT_unfunction[] = "unfunction"; static char *LIT_bun = "bad unit number"; static char *HistoryUsage = "history [ -iedsl ] [ number ]"; static char *ReturnUsage = "return [ value ]"; #if (OS_TYPE == OS_OS2) static char *StartUsage = "start -O [dos | pm] [ -hHfWPFxibID ] [ -c [ vilsna ]] [ -t title ]\n [ -e string ]\n start [ -dfWPFibCISxhH ] [ -c [ vilsna ]] [ -t title ] [ -e string ]\n [ -X directory ] [ args.. ]\n start -A sessionId"; static char *Start_NoSession = "start: Cannot switch to session %lu\n%s"; #endif #if (OS_TYPE != OS_DOS) static char *JobUsage = "jobs [-lp] [ -P pid]"; static char *KillUsage = "kill [ [-l] | [ [-sig] [ process id | %job number ] ... ] ]"; #endif /* * Disable variables mapping */ struct DisableVariableMap { char *name; int flag; } DisableVariableMap [] = { { MailCheckVariable, DISABLE_MAILCHECK }, { OptArgVariable, DISABLE_OPTARG }, { OptIndVariable, DISABLE_OPTIND }, { SecondsVariable, DISABLE_SECONDS }, { RandomVariable, DISABLE_RANDOM }, { LastWordVariable, DISABLE_LASTWORD }, { LineCountVariable, DISABLE_LINECOUNT }, #if (OS_TYPE != OS_DOS) { WinTitleVariable, DISABLE_WINTITLE }, #endif { (char *)NULL, 0 }, }; /* * built-in commands: : and true */ static int dolabel (int argc, char **argv) { (void) argc, argv; return 0; } static int dofalse (int argc, char **argv) { (void) argc, argv; return 1; } /* * Getopts - split arguments. getopts optstring name [ arg ... ] */ static int dogetopts (int argc, char **argv) { char **Arguments; char *OptionString; int result; char SResult[3]; char BadResult[2]; int Mode = GETOPT_MESSAGE | GETOPT_PLUS; if (argc < 3) return UsageError ("getopts optstring name [ arg ... ]"); memset (SResult, 0, 3); /* * A leading : in optstring causes getopts to store the letter of an * invalid option in OPTARG, and to set name to ? for an unknown option and * to : when a required option is missing. */ if (*(OptionString = argv[1]) == ':') { OptionString++; Mode = GETOPT_PLUS; } /* * Use positional parameters */ if (argc == 3) { argc = ParameterCount + 1; Arguments = ParameterArray; } /* No - use supplied */ else { Arguments = &argv[2]; /* Adjust pointers */ argc -= 2; } /* * Get the value of OPTIND and initialise the getopt function */ if (!(DisabledVariables & DISABLE_OPTIND)) OptionIndex = (int)GetVariableAsNumeric (OptIndVariable); else OptionIndex = GetOptsIndex.Index; /* Initialise the other values */ GetOptionPosition = GetOptsIndex.SubIndex; OptionArgument = (char *)NULL; result = GetOptions (argc, Arguments, OptionString, Mode); /* Save new positions */ SaveGetoptsValues (OptionIndex, GetOptionPosition); /* Check for EOF */ if (result == EOF) return 1; /* Set up result string */ *SResult = (char)result; /* Did we get an error. Yes. If message output, don't put value * in OPTARG */ if (result == '?') { if (Mode & GETOPT_MESSAGE) OptionArgument = (char *)NULL; /* Error, set up values in optarg and the result */ else { SResult[0] = (char)((OptionArgument == (char *)NULL) ? '?' : ':'); *(OptionArgument = BadResult) = (char)BadOptionValue; *(OptionArgument + 1) = 0; } } /* If the argument started with a +, tell them */ else if (OptionStart == '+') { SResult[1] = (char)result; SResult[0] = '+'; } /* If we got an argument, set the various shell variables */ if ((OptionArgument != (char *)NULL) && (!(DisabledVariables & DISABLE_OPTARG))) SetVariableFromString (OptArgVariable, OptionArgument); SetVariableFromString (argv[2], SResult); return 0; } /* * Reset the getopts values */ void ResetGetoptsValues (bool Variable) { if (Variable && (!(DisabledVariables & DISABLE_OPTIND))) SetVariableFromNumeric (OptIndVariable, 1L); GetOptsIndex.Index = 1; GetOptsIndex.SubIndex = 1; } /* * Save the new Getopts values */ void SaveGetoptsValues (int Index, int Position) { if (!(DisabledVariables & DISABLE_OPTIND)) SetVariableFromNumeric (OptIndVariable, (long)Index); GetOptsIndex.Index = Index; GetOptsIndex.SubIndex = Position; } /* * Get the current Getopts values */ void GetGetoptsValues (GetoptsIndex *values) { values->Index = GetOptsIndex.Index; values->SubIndex = GetOptsIndex.SubIndex; } /* * Echo the parameters: echo [ -n ] parameters */ static int doecho (int argc, char **argv) { int flags = ECHO_ESCAPE; int fid = 1; char *ip; /* Input pointer */ char *op; int c, c1; int R_flag = GETOPT_PRINT; /* Enable -n test */ ResetGetOptions (); /* Reset GetOptions */ /* Echo or print? */ if (strcmp (*argv, LIT_print) == 0) { R_flag = 0; /* Reset */ while ((c = GetOptions (argc, argv, "Rnprsu:", R_flag)) != EOF) { switch (c) { case 'R': R_flag = GETOPT_PRINT; flags &= ~ECHO_ESCAPE; break; case 'n': flags = ECHO_NO_EOL; break; case 'r': flags &= ~ECHO_ESCAPE; break; case 's': flags |= ECHO_HISTORY; break; case 'p': break; case 'u': if ((fid = GetUnitNumber (LIT_print)) == -1) return 1; break; default: return UsageError ("print [ -Rpnrsu[unit] ] ..."); } } } if ((OptionIndex < argc) && (R_flag == GETOPT_PRINT) && (!strcmp (argv[OptionIndex], "-n"))) { flags |= ECHO_NO_EOL; ++OptionIndex; } argv += OptionIndex; /* Clear the history buffer so we can use it */ FlushHistoryBuffer (); op = ConsoleLineBuffer; /* Process the arguments. Process \ as a special if necessary */ while ((ip = *(argv++)) != NOWORD) { /* Process the character */ while ((c = (int)(*(ip++))) != 0) { /* If echo too big - disable history save */ if ((op - ConsoleLineBuffer) > LINE_MAX - 4) { *op = 0; if (!WriteOutLine (fid)) return 1; op = ConsoleLineBuffer; if (flags & ECHO_HISTORY) fprintf (stderr, BasicErrorMessage, "Line too long for history", LIT_print); flags &= ~ECHO_HISTORY; } if ((flags & ECHO_ESCAPE) && (c == CHAR_META)) { c1 = *ip; if ((c = ProcessOutputMetaCharacters (&ip)) == -1) { flags |= ECHO_NO_EOL; continue; } /* If unchanged - output backslash */ else if ((c == c1) && (c != CHAR_META)) *(op++) = CHAR_META; } *(op++) = (char)c; } /* End of string - check to see if a space if required */ if (*argv != NOWORD) *(op++) = CHAR_SPACE; } /* Is EOL required ? */ if (!(flags & ECHO_NO_EOL)) { #if (OS_TYPE == OS_OS2) && (OS_SIZE == OS_32) if (IS_Console (fid)) *(op++) = CHAR_RETURN; #endif *(op++) = CHAR_NEW_LINE; } *op = 0; if (!WriteOutLine (fid)) return 1; /* Save history */ if (flags & ECHO_HISTORY) { CleanUpBuffer (op - ConsoleLineBuffer, ConsoleLineBuffer, 0x1a); AddHistory (FALSE); } return 0; } /* * Write out the current line */ static bool F_LOCAL WriteOutLine (int fid) { size_t Len = strlen (ConsoleLineBuffer); if (write (fid, ConsoleLineBuffer, Len) != (int)Len) { PrintWarningMessage ("print: write error on unit %d\n", fid); return FALSE; } return TRUE; } /* * Display the current version: ver */ static int dover (int argc, char **argv) { (void) argc, argv; PrintVersionNumber (stdout); return 0; } #ifdef OS_SWAPPING static char *swap_device[] = {"disk", "extend", "expand"}; #endif /* * Modify swapping information: swap [ options ] */ static int doswap (int argc, char **argv) { #if (OS_TYPE != OS_DOS) (void) argc, argv; printf ("Swapping not available on %s", LIT_OSname); #elif !defined (OS_SWAPPING) (void) argc, argv; puts ("Swapping not available in 32-bit mode"); #else int n = 1; char *cp; /* Display current values ? */ if (argv[1] == NOWORD) { if (Swap_Mode == SWAP_OFF) puts ("Swapping disabled"); else { int j; foputs ("Swap devices: "); for (j = 0, n = 1; j < 3; ++j, n <<= 1) { if (Swap_Mode & n) { printf ("%s ", swap_device[j]); } } fputchar (CHAR_NEW_LINE); } return 0; } /* Set up new values */ Swap_Mode = SWAP_OFF; while ((cp = argv[n++]) != NOWORD) { if (strcmp (cp, "off") == 0) Swap_Mode = SWAP_OFF; else if (strcmp (cp, "on") == 0) Swap_Mode = SWAP_DISK | SWAP_EXPAND | SWAP_EXTEND; /* Scan for valid arguments */ else { int j, k; for (j = 0, k = 1; j < 3; ++j, k <<= 1) { if (strcmp (cp, swap_device[j]) == 0) { Swap_Mode |= k; break; } } if (j == 3) return UsageError ("swap [ on | off | disk | expand | extend [ address ] ] ..."); } } #endif return 0; } /* * Output the current path: pwd [drives] */ static int dopwd (int argc, char **argv) { int i; int Start = 1; int RetVal = 0; char *sp; char ndrive; char ldir[PATH_MAX + 6]; bool Physical = FALSE; if ((argc > 1) && CheckPhysLogical (argv[1], &Physical)) Start++; /* Print the current directories on the selected drive */ i = Start; while ((sp = argv[i++]) != NOWORD) { /* Select the drive and get the path */ while ((ndrive = *(sp++)) != 0) { errno = 0; if (!S_getcwd (ldir, GetDriveNumber (ndrive)) || errno) RetVal = PrintWarningMessage (BadDrive, ndrive); else if (Physical) printf ("%c: path is %s\n", tolower (ndrive), GetPhysicalPath (ldir, TRUE)); else puts (ldir); } } /* Print the current directory */ if (argv[Start] == NOWORD) { strcpy (ldir, CurrentDirectory->value); puts ((Physical) ? GetPhysicalPath (ldir, TRUE) : ldir); } return RetVal; } /* * Unset a variable: unset [ flag ] [ variable name... ] * Delete a function: unfunction */ static int dounset (int argc, char **argv) { int n = 1; bool Fnc = FALSE; FunctionList *fp; char *cp; int i; /* -f, functions */ if (strcmp (*argv, LIT_unfunction) == 0) Fnc = TRUE; else if ((argc > 1) && (strcmp (argv[1], "-f") == 0)) { n++; Fnc = TRUE; } /* Unset the variables, flags or functions */ while ((cp = argv[n++]) != NOWORD) { if (!Fnc) { UnSetVariable (cp, -1, FALSE); for (i = 0; DisableVariableMap[i].name != (char *)NULL; i++) { if (strcmp (DisableVariableMap[i].name, cp) == 0) { DisabledVariables |= DisableVariableMap[i].flag; break; } } } else if ((fp = LookUpFunction (cp, TRUE)) != (FunctionList *)NULL) DeleteFunction (fp->tree); } return 0; } /* Delete a variable. If all is set, system variables can be deleted. * This is used to delete the trap functions */ void UnSetVariable (char *cp, /* Variable name */ int OIndex, /* Index preprocessed value */ bool all) /* Any variable */ { long Index; char *term; bool ArrayDetected; /* Unset a flag */ if (*cp == '-') { while (*(++cp) != 0) { if (islower (*cp)) FL_CLEAR (*cp); } SetShellSwitches (); return; } /* Ok - unset a variable and not a local value */ if (!all && !(IS_VariableFC ((int)*cp))) return; /* Check in list */ if (!GetVariableName (cp, &Index, &term, &ArrayDetected) || *term) return; if (OIndex != -1) Index = OIndex; /* Delete a specific entry or all? */ if (!((ArrayDetected || (OIndex != -1)))) Index = -1; RemoveVariable (cp, (int)Index); } /* * Delete a variable * * Index = -1 implies all references */ static void F_LOCAL RemoveVariable (char *name, /* Variable name */ int Index) /* Array index */ { VariableList **vpp; VariableList *vp; VariableList dummy; void (_SIGDECL *save_signal)(int); while (TRUE) { dummy.name = name; dummy.index = (int)Index; vpp = (VariableList **)tfind (&dummy, &VariableTree, DeleteAllVariables); /* If not found, Ignore unset request */ if (vpp == (VariableList **)NULL) return; /* Error if read-only */ vp = *vpp; if (vp->status & (STATUS_READONLY | STATUS_CANNOT_UNSET)) { PrintWarningMessage ("unset: %s %s", vp->name, (vp->status & STATUS_CANNOT_UNSET) ? "cannot be unset" : LIT_IsReadonly); return; } /* Disable signals */ save_signal = signal (SIGINT, SIG_IGN); /* Delete it */ dummy.index = vp->index; tdelete (&dummy, &VariableTree, SearchVariable); ReleaseMemoryCell ((void *)vp->name); if (vp->value != null) ReleaseMemoryCell ((void *)vp->value); ReleaseMemoryCell ((void *)vp); /* Restore signals */ signal (SIGINT, save_signal); } } /* * TFIND - Search the VARIABLE TREE for an entry to delete */ static int DeleteAllVariables (const void *key1, const void *key2) { int diff; if ((diff = strcmp (((VariableList *)key1)->name, ((VariableList *)key2)->name)) != 0) return diff; if (((VariableList *)key1)->index == -1) return 0; return ((VariableList *)key1)->index - ((VariableList *)key2)->index; } /* * Execute a test: test */ static int dotest (int argc, char **argv) { int st = 0; char *End; (void)argc; NewTestProgram = (bool)(strcmp (TestProgram = *argv, LIT_Test) == 0); /* Note that -a and -o change meaning if [[ ... ]] is used */ if (NewTestProgram) { End = "]]"; ListOfTestOperators[0].OperatorID = FILE_EXISTS; ListOfTestOperators[0].OperatorType = UNARY_OP; ListOfTestOperators[1].OperatorID = TEST_OPTION; ListOfTestOperators[1].OperatorType = UNARY_OP; } else { End = "]"; ListOfTestOperators[0].OperatorID = BINARY_AND; ListOfTestOperators[0].OperatorType = B_BINARY_OP; ListOfTestOperators[1].OperatorID = BINARY_OR; ListOfTestOperators[1].OperatorType = B_BINARY_OP; } /* Check out */ CurrentTestOperator = (struct TestOperator *)NULL; /* If [ ] or [[ ]] form, check for end ] or ]] and * remove it */ if (NewTestProgram || (strcmp (*argv, "[") == 0)) { while (argv[++st] != NOWORD) ; if (strcmp (argv[--st], End) != 0) return PrintWarningMessage ("%s: missing '%s'", TestProgram, End); else argv[st] = NOWORD; } /* Check for null expression */ if (*(TestArgumentList = &argv[1]) == NOWORD) return 1; /* Set abort address */ if (setjmp (TestErrorReturn)) return 1; st = !TestProcessNextExpression (TestLexicalAnalysis ()); if (*(TestArgumentList + 1) != NOWORD) TestSyntaxError (); return (st); } /* * Process next test expression */ static int F_LOCAL TestProcessNextExpression (int n) { int res; if (n == END_OF_INPUT) TestSyntaxError (); res = TestANDExpression (n); TestArgumentList++; if (TestLexicalAnalysis () == BINARY_OR) { TestArgumentList++; return TestProcessNextExpression (TestLexicalAnalysis ()) || res; } TestArgumentList--; return res; } /* * Binary expression ( a AND b) */ static int F_LOCAL TestANDExpression (int n) { int res; if (n == END_OF_INPUT) TestSyntaxError (); res = TestPrimaryExpression (n); TestArgumentList++; if (TestLexicalAnalysis () == BINARY_AND) { TestArgumentList++; return TestANDExpression (TestLexicalAnalysis ()) && res; } TestArgumentList--; return res; } /* * Handle Primary expression */ static int F_LOCAL TestPrimaryExpression (int n) { int res; switch (n) { case END_OF_INPUT: TestSyntaxError (); case UNARY_NOT: TestArgumentList++; return !TestPrimaryExpression (TestLexicalAnalysis ()); case LPAREN: TestArgumentList++; res = TestProcessNextExpression (TestLexicalAnalysis ()); TestArgumentList++; if (TestLexicalAnalysis () != RPAREN) TestSyntaxError (); return res; /* Operand */ case OPERAND: return TestBinaryOperand (); /* unary expression */ default: if ((CurrentTestOperator->OperatorType != UNARY_OP) || (*++TestArgumentList == 0)) TestSyntaxError (); return TestUnaryOperand (n); } } /* * Handle a Binary Operand */ static int F_LOCAL TestBinaryOperand (void) { char *opnd1, *opnd2; struct stat s, s1; short op; opnd1 = *(TestArgumentList++); (void) TestLexicalAnalysis (); if ((CurrentTestOperator != (struct TestOperator *)NULL) && (CurrentTestOperator->OperatorType == BINARY_OP)) { op = CurrentTestOperator->OperatorID; TestArgumentList++; if ((opnd2 = *TestArgumentList) == NOWORD) TestSyntaxError (); switch (op) { /* String lengths */ case STRING_EQUAL: return strcmp (opnd1, opnd2) == 0; case STRING_NOTEQUAL: return strcmp (opnd1, opnd2) != 0; case STRING_LESS: return strcmp (opnd1, opnd2) < 0; case STRING_GREATER: return strcmp (opnd1, opnd2) > 0; /* Numeric comparisions */ case NUMBER_EQUAL: return GetNumberForTest (opnd1) == GetNumberForTest (opnd2); case NUMBER_NOTEQUAL: return GetNumberForTest (opnd1) != GetNumberForTest (opnd2); case NUMBER_EQ_GREAT: return GetNumberForTest (opnd1) >= GetNumberForTest (opnd2); case NUMBER_GREATER: return GetNumberForTest (opnd1) > GetNumberForTest (opnd2); case NUMBER_EQ_LESS: return GetNumberForTest (opnd1) <= GetNumberForTest (opnd2); case NUMBER_LESS: return GetNumberForTest (opnd1) < GetNumberForTest (opnd2); /* Older and Newer - if file not found - set to current time */ case FILE_NEWER: case FILE_OLDER: if (!S_stat (opnd1, &s)) return 0; if (!S_stat (opnd2, &s1)) s1.st_mtime = 0L; return (op == FILE_NEWER) ? (s.st_mtime > s1.st_mtime) : (s.st_mtime < s1.st_mtime); /* * Equals - difficult on DOS. So just do want UNIX says, but first compare * the absolute path names */ case FILE_EQUAL: { #if (OS_TYPE != OS_UNIX) char *a_opnd1; char *a_opnd2; int res; #endif if ((!S_stat (opnd1, &s)) || (!S_stat (opnd2, &s1))) return 0; #if (OS_TYPE == OS_UNIX) return ((s.st_dev == s1.st_dev) && (s.st_ino == s1.st_ino)); #else a_opnd1 = AllocateMemoryCell (FFNAME_MAX); a_opnd2 = AllocateMemoryCell (FFNAME_MAX); if ((a_opnd1 == (char *)NULL) || (a_opnd1 == (char *)NULL)) { doOutofMemory (LIT_Test); return 0; } GenerateFullExecutablePath (strcpy (a_opnd1, opnd1)); GenerateFullExecutablePath (strcpy (a_opnd2, opnd2)); /* If this is OS/2, we need to decide if to check for HPFS */ # if (OS_TYPE != OS_DOS) res = ((!IsHPFSFileSystem (a_opnd1)) || (ShellGlobalFlags & FLAGS_NOCASE)) ? stricmp (a_opnd1, a_opnd2) : strcmp (a_opnd1, a_opnd2); # else res = stricmp (a_opnd1, a_opnd2); # endif ReleaseMemoryCell (a_opnd1); ReleaseMemoryCell (a_opnd2); return (res == 0) ? 1 : ((s.st_dev == s1.st_dev) && (s.st_ino == s1.st_ino)); #endif } } } TestArgumentList--; return strlen (opnd1) != 0; } /* * Handle a Unary Operand */ static int F_LOCAL TestUnaryOperand (int n) { switch (n) { case STRING_ZERO: return strlen (*TestArgumentList) == 0; case STRING_NONZERO: return strlen (*TestArgumentList) != 0; case TEST_OPTION: return TestOptionValue (*TestArgumentList, TRUE) != 0; /* File functions */ case FILE_EXISTS: return CheckFAccess (*TestArgumentList, F_OK); case FILE_READABLE: return CheckFAccess (*TestArgumentList, R_OK); case FILE_WRITABLE: return CheckFAccess (*TestArgumentList, W_OK); case FILE_EXECUTABLE: return CheckFAccess (*TestArgumentList, X_OK); case FILE_REGULAR: return CheckFType (*TestArgumentList, S_IFREG); case FILE_DIRECTRY: return CheckFType (*TestArgumentList, S_IFDIR); case FILE_NONZERO: return CheckFSize (*TestArgumentList); case FILE_TERMINAL: return IS_TTY ((int)GetNumberForTest (*TestArgumentList)); /* The following have no meaning on MSDOS or OS/2. So we always return * fail for compatability */ #if (OS_TYPE == OS_UNIX) case FILE_USER: return CheckFMode (*TestArgumentList, S_ISUID); case FILE_GROUP: return CheckFMode (*TestArgumentList, S_ISGID); case FILE_TEXT: return CheckFMode (*TestArgumentList, S_ISVTX); #else case FILE_USER: return CheckFMode (*TestArgumentList, OS_FILE_HIDDEN); case FILE_GROUP: return CheckFMode (*TestArgumentList, OS_FILE_SYSTEM); case FILE_TEXT: return CheckFMode (*TestArgumentList, OS_FILE_ARCHIVED); #endif case FILE_BLOCK: return CheckFType (*TestArgumentList, S_IFBLK); case FILE_CHARACTER: return CheckFType (*TestArgumentList, S_IFCHR); #if (OS_TYPE == OS_UNIX) case FILE_FIFO: return CheckFType (*TestArgumentList, S_IFIFO); case FILE_SYMBOLIC: return CheckFType (*TestArgumentList, S_IFLNK); case FILE_SOCKET: return CheckFType (*TestArgumentList, S_IFSOCK); #else case FILE_FIFO: case FILE_SYMBOLIC: case FILE_SOCKET: return 0; #endif /* Under MSDOS and OS/2, we always own the file. Not necessarily true on * networked versions. But there is no common way of finding out */ #if (OS_TYPE == OS_UNIX) case FILE_OWNER: return CheckFOwner (*TestArgumentList); case FILE_GROUPER: return CheckFGroup (*TestArgumentList); #else case FILE_OWNER: case FILE_GROUPER: return 1; #endif } return 0; } /* Operator or Operand ? */ static int F_LOCAL TestLexicalAnalysis (void) { struct TestOperator *op; char *s = *TestArgumentList; if (s == NOWORD) return END_OF_INPUT; /* This is a real pain. If the current string is a unary operator and the * next string is a binary operator, assume the current string is a parameter * to the binary operator and not a unary operator. */ if ((CurrentTestOperator = TestLookupOperator (s)) != (struct TestOperator *)NULL) { s = *(TestArgumentList + 1); if ((CurrentTestOperator->OperatorType != UNARY_OP) || (s == (char *)NULL) || ((op = TestLookupOperator (s)) == (struct TestOperator *)NULL) || (op->OperatorType != BINARY_OP)) return CurrentTestOperator->OperatorID; } CurrentTestOperator = (struct TestOperator *)NULL; return OPERAND; } /* * Look up the string and test for operator */ static struct TestOperator * F_LOCAL TestLookupOperator (char *s) { struct TestOperator *op = ListOfTestOperators; while (op->OperatorName) { if (strcmp (s, op->OperatorName) == 0) return op; op++; } return (struct TestOperator *)NULL; } /* * Get a long numeric value */ static long F_LOCAL GetNumberForTest (char *s) { long l; if (!ConvertNumericValue (s, &l, 10)) TestSyntaxError (); return l; } /* * test syntax error - abort */ static void F_LOCAL TestSyntaxError (void) { PrintWarningMessage (BasicErrorMessage, TestProgram, LIT_SyntaxError); longjmp (TestErrorReturn, 1); } /* * Select a new drive: x: * * Select the drive, get the current directory and check that we have * actually selected the drive */ static int dodrive (int argc, char **argv) { unsigned int ndrive = GetDriveNumber (**argv); char ldir[PATH_MAX + 6]; bool bad = FALSE; if (argc != 1) return UsageError ("drive:"); if (SetCurrentDrive (ndrive) == -1) bad = TRUE; else if (!S_getcwd (ldir, ndrive)) bad = TRUE; else { GetCurrentDirectoryPath (); if (ndrive != GetCurrentDrive ()) bad = TRUE; } if (bad) return PrintWarningMessage (BadDrive, **argv); else return 0; } /* * Check for -L or -P switch */ static bool F_LOCAL CheckPhysLogical (char *string, bool *physical) { if (strcmp (string, "-L") == 0) { *physical = FALSE; return TRUE; } if (strcmp (string, "-P") == 0) { *physical = TRUE; return TRUE; } return FALSE; } /* * Select a new directory: * * cd [ directory ] Select new directory * cd - Select previous directory * cd Select directory based on current * cd Select Home directory * cd [-L | -P] Use Logical or physical mapping * sort of symbolic links */ static int dochdir (int argc, char **argv) { char *NewDirectory; /* Original new directory */ char *CNDirectory; /* New directory */ char *cp; /* In CDPATH Pointer */ char *directory; bool first = TRUE; unsigned int Length; unsigned int cdrive; bool Physical = FALSE; int Start = 1; int dcount; /* If restricted shell - illegal */ if (CheckForRestrictedShell ("cd")) return 1; if ((argc > 1) && CheckPhysLogical (argv[1], &Physical)) Start = 2; if (argc - Start > 2) return UsageError ("cd [ -P | -L ] [ directory | - | search replace ]"); /* Use default ? */ if (((NewDirectory = argv[Start]) == NOWORD) && ((NewDirectory = GetVariableAsString (HomeVariableName, FALSE)) == null)) return PrintWarningMessage ("cd: no home directory"); if ((directory = AllocateMemoryCell (FFNAME_MAX)) == (char *)NULL) return doOutofMemory ("cd"); if ((strcmp (NewDirectory, ShellOptionsVariable) == 0) && ((NewDirectory = GetVariableAsString (OldPWDVariable, FALSE)) == null)) return PrintWarningMessage ("cd: no previous directory"); /* Check for substitue */ if ((argv[Start] != NOWORD) && (argv[Start + 1] != NOWORD)) { if ((cp = strstr (CurrentDirectory->value, argv[Start])) == (char *)NULL) return PrintWarningMessage ("cd: string not in pwd: %s", argv[Start]); if (strlen (CurrentDirectory->value) - strlen (argv[Start]) + strlen (argv[Start + 1]) >= (size_t)FFNAME_MAX) return PrintWarningMessage ("cd: new directory string too long: %s", argv[Start]); /* Do the substitution */ Length = cp - CurrentDirectory->value; strncpy (NewDirectory, CurrentDirectory->value, Length); strcpy (NewDirectory + Length, argv[Start + 1]); strcat (NewDirectory, CurrentDirectory->value + strlen (argv[Start]) + Length); } /* Save the current drive */ cdrive = GetCurrentDrive (); /* Remove trailing / */ Length = strlen (NewDirectory) - 1; if (IsPathCharacter (NewDirectory[Length]) && (!((!Length) || ((Length == 2) && IsDriveCharacter (NewDirectory[1]))))) NewDirectory[Length] = 0; /* * Special case for . and .., it seems */ if ((strcmp (NewDirectory, CurrentDirLiteral) == 0) && S_chdir (NewDirectory)) { GetCurrentDirectoryPath (); return 0; } if (strcmp (NewDirectory, ParentDirLiteral) == 0) { if (S_chdir (NewDirectory)) { GetCurrentDirectoryPath (); return 0; } /* If we are in the root directory, .. does not move you! */ dcount = 0; cp = CurrentDirectory->value; while ((cp = strchr (cp, CHAR_UNIX_DIRECTORY)) != (char *)NULL) { cp++; if (++dcount == 2) break; } if (dcount == 1) { GetCurrentDirectoryPath (); return 0; } } /* Scan for the directory. If there is not a / or : at start, use the * CDPATH variable */ cp = (IsPathCharacter (*NewDirectory) || IsDriveCharacter (*(NewDirectory + 1))) ? null : GetVariableAsString (CDPathLiteral, FALSE); do { cp = BuildNextFullPathName (cp, NewDirectory, directory); /* Check for new disk drive */ if (Physical && (GetPhysicalPath (directory, FALSE) == (char *)NULL)) return doOutofMemory ("cd"); CNDirectory = directory; /* Was the change successful? */ if (GotoDirectory (CNDirectory, cdrive)) { /* OK - reset the current directory (in the shell) and display the new * path if appropriate */ GetCurrentDirectoryPath (); if (!first) puts (CurrentDirectory->value); return 0; } first = FALSE; } while (cp != (char *)NULL); /* Restore our original drive and restore directory info */ GetCurrentDirectoryPath (); return PrintWarningMessage (BasicErrorMessage, NewDirectory, "bad directory"); } /* * Execute a shift command: shift [ n ] */ static int doshift (int argc, char **argv) { int n; char *Nvalue = argv[1]; if (argc > 2) return UsageError ("shift [ count ]"); n = (Nvalue != NOWORD) ? GetNumericValue (Nvalue) : 1; if (n < 0) return PrintWarningMessage (LIT_Emsg, LIT_shift, "bad shift value", Nvalue); if (ParameterCount < n) return PrintWarningMessage (BasicErrorMessage, LIT_shift, "nothing to shift"); return SetUpNewParameterVariables (ParameterArray, n + 1, ParameterCount + 1, LIT_shift); } /* * Execute a umask command: umask [ n ] */ static int doumask (int argc, char **argv) { int i; char *cp; long value = 0; if (argc > 2) return UsageError ("umask [ new mask ]"); if ((cp = argv[1]) == NOWORD) { umask ((i = umask (0))); printf ("%o\n", i); } else if (!ConvertNumericValue (cp, &value, 8)) return PrintWarningMessage ("umask: bad mask (%s)", cp); #if (__OS2__) else if (umask ((int)value) == 0xffff) return PrintWarningMessage ("umask: bad value (%s)", cp); #else umask ((int)value); #endif return 0; } /* * Execute an exec command: exec [ arguments ] */ int doexec (C_Op *t) { int i = 0; jmp_buf ex; ErrorPoint OldERP; /* Shift arguments */ do { t->args[i] = t->args[i + 1]; i++; } while (t->args[i] != NOWORD); /* Left the I/O as it is */ if (i == 1) return RestoreStandardIO (0, FALSE); ProcessingEXECCommand = TRUE; OldERP = e.ErrorReturnPoint; /* Set execute function recursive level to zero */ Execute_stack_depth = 0; t->ioact = (IO_Actions **)NULL; if (SetErrorPoint (ex) == 0) ExecuteParseTree (t, NOPIPE, NOPIPE, EXEC_WITHOUT_FORK); /* Clear the extended file if an interrupt happened */ ClearExtendedLineFile (); e.ErrorReturnPoint = OldERP; ProcessingEXECCommand = FALSE; return 1; } /* * Execute a script in the current shell: . */ static int dodot (int argc, char **argv) { int i; char *sp; char *cp; char *l_path; Source *s; FILE *Fp; (void)argc; if ((cp = argv[1]) == NOWORD) return 0; /* Get some space */ if ((l_path = AllocateMemoryCell (FFNAME_MAX)) == (char *)NULL) return doOutofMemory ("."); /* Save the current drive */ sp = ((FindPathCharacter (cp) != (char *)NULL) || IsDriveCharacter (*(cp + 1))) ? null : GetVariableAsString (PathLiteral, FALSE); do { sp = BuildNextFullPathName (sp, cp, l_path); if ((i = OpenForExecution (l_path, (char **)NULL, (int *)NULL)) >= 0) { if ((Fp = ReOpenFile (ReMapIOHandler (i), sOpenReadMode)) == (FILE *)NULL) return PrintWarningMessage ("Cannot remap file"); (s = pushs (SFILE))->u.file = Fp; s->file = cp; RunACommand (s, &argv[1]); S_fclose (Fp, TRUE); return (int)GetVariableAsNumeric (StatusVariable); } } while (sp != (char *)NULL); return PrintWarningMessage (BasicErrorMessage, cp, NotFound); } /* * Read from standard input into a variable list * * read [-prs] [-u unit] [ variable list ] */ static int doread (int argc, char **argv) { char *cp, *op; int i; int Unit = STDIN_FILENO; bool EndOfInputDetected = FALSE; int PreviousCharacter = 0; bool SaveMode = FALSE; bool RawMode = FALSE; char *Prompt = (char *)NULL; char *NewBuffer; int eofc; if ((NewBuffer = AllocateMemoryCell (LINE_MAX + 2)) == (char *)NULL) return doOutofMemory (LIT_read); /* Check for variable name. If not defined, use $REPLY */ ResetGetOptions (); /* Reset GetOptions */ while ((i = GetOptions (argc, argv, "prsu:", 0)) != EOF) { switch (i) { case 'p': /* Clean up process */ break; case 'r': /* Raw Mode */ RawMode = TRUE; break; case 's': /* Save a command */ SaveMode = TRUE; break; case 'u': /* Specify input unit */ if ((Unit = GetUnitNumber (LIT_read)) == -1) return 2; default: return UsageError ("read [ -prs ] [ -u unit ] [ name?prompt ] [ name ... ]"); } } argv += OptionIndex; argc -= OptionIndex; if (!argc) argv = Reply_Array; /* Get the prompt and write it */ LastUserPrompt = null; if ((Prompt = strchr (argv[0], '?')) != (char *)NULL) { *(Prompt++) = 0; LastUserPrompt1 = Prompt; LastUserPrompt = (char *)NULL; } /* Check we have a name */ if (!strlen (argv[0])) argv = Reply_Array; /* Check for valid names */ for (i = 0; argv[i] != (char *)NULL; i++) { if (IsValidVariableName (argv[i])) { PrintErrorMessage (BasicErrorMessage, argv[i], LIT_BadID); return 1; } } /* Clear the history buffer */ FlushHistoryBuffer (); /* Read the line from the device */ if (ReadALine (Unit, Prompt, SaveMode, FALSE, &eofc)) return 1; cp = ConsoleLineBuffer; /* For each variable, read the data until a white space is detected */ while (*argv != NOWORD) { /* Read in until end of line, file or a field separator is detected */ op = (char *)NULL; while (!EndOfInputDetected && *cp) { /* End of file */ if (*cp == (char)eofc) return 1; /* Check for Newline or IFS character */ if ((*cp == CHAR_NEW_LINE) || ((argv[1] != NOWORD) && strchr (GetVariableAsString (IFS, FALSE), *cp) != (char *)NULL)) { if (*cp != CHAR_NEW_LINE) ; else if ((PreviousCharacter != CHAR_META) || (RawMode)) EndOfInputDetected = TRUE; /* Handle continuation line */ else if (ReadALine (Unit, Prompt, SaveMode, TRUE, &eofc)) return 1; else { cp = ConsoleLineBuffer; PreviousCharacter = 0; continue; } break; } /* Save the current character */ if (op == (char *)NULL) op = NewBuffer; *(op++) = *cp; PreviousCharacter = *(cp++); } /* Skip over terminating character */ if (*cp) ++cp; /* Check for null string */ if (op == (char *)NULL) op = null; /* Terminate the string */ else { *op = 0; if (!strlen (NewBuffer)) continue; else op = NewBuffer; } /* Save the string value */ SetVariableFromString (*(argv++), op); } ReleaseMemoryCell ((void *)NewBuffer); return 0; } /* * Read a line from either the console or a file for the read command. * The line is returned in the ConsoleLineBuffer. */ static bool F_LOCAL ReadALine (int Unit, /* Unit to read from */ char *Prompt, /* User prompt */ bool SaveMode, /* Save in history */ bool Append, /* Append to history */ int *eofc) /* EOF character */ { int NumberBytesRead; char *cp; int x; /* Generate the prompt */ if ((Prompt != (char *)NULL) && (!IS_Console (Unit)) && (!IS_TTY (Unit) || (write (Unit, Prompt, strlen (Prompt)) == -1))) feputs (Prompt); /* Read the line */ *eofc = 0x1a; if (IS_Console (Unit)) { *eofc = GetEOFKey (); if (!GetConsoleInput ()) /* get input */ strcpy (ConsoleLineBuffer, LIT_NewLine); } else { NumberBytesRead = 0; cp = ConsoleLineBuffer; while (NumberBytesRead++ < LINE_MAX) { if ((x = read (Unit, cp, 1)) == -1) return TRUE; /* EOF detected as first character on line */ if ((NumberBytesRead == 1) && ((!x) || (*cp == (char)*eofc))) return TRUE; /* End read if NEWLINE or EOF character detected */ if ((!x) || (*cp == CHAR_NEW_LINE) || (*cp == (char)*eofc)) break; cp++; } /* Terminate the line the same in all cases */ *(cp++) = CHAR_NEW_LINE; *cp = 0; } /* Save the history. Clean it up first */ if (SaveMode) { char save; save = CleanUpBuffer (strlen (ConsoleLineBuffer), ConsoleLineBuffer, *eofc); AddHistory (Append); ConsoleLineBuffer[strlen(ConsoleLineBuffer)] = save; } return FALSE; } /* * Evaluate an expression: eval */ static int doeval (int argc, char **argv) { Source *s; (void)argc; (s = pushs (SWORDS))->u.strv = argv + 1; return RunACommand (s, (char **)NULL); } /* * Map signals. * * Numeric values are assumed to be UNIX - map to appropriate DOS signal. * Character values are just mapped to the DOS signal */ static struct TrapSignalList * F_LOCAL LookupSignalName (char *name) { static struct TrapSignalList Numeric = {NULL, 0}; int n; if (isdigit (*name)) { if (((n = GetNumericValue (name)) < 0) || (n >= MAX_SIG_MAP)) return (struct TrapSignalList *)NULL; #if (OS_TYPE == OS_UNIX) Numeric.signo = n; #else Numeric.signo = UnixToDosSignals [n]; #endif return &Numeric; } /* Check the character names */ for (n = 0; n < MAX_TRAP_SIGNALS; n++) { if (stricmp (name, TrapSignalList[n].name) == 0) return &TrapSignalList[n]; } return (struct TrapSignalList *)NULL; } /* * Execute a trap: trap [ number... ] [ command ] */ static int dotrap (int argc, char **argv) { int i; bool SetSignal; char tval[10]; char *cp; struct TrapSignalList *SignalInfo; void (_SIGDECL *NewSignalFunc)(int); if ((argc == 2) && (strcmp (argv[1], "-l") == 0)) { for (i = 3; i < MAX_TRAP_SIGNALS; i++) puts (TrapSignalList[i].name); return 0; } /* Display active traps ? */ if (argc < 2) { /* Display trap - look up each trap and print those we find */ for (i = 0; i < NSIG; i++) { sprintf (tval, "~%d", i); if ((cp = GetVariableAsString (tval, FALSE)) != null) printf ("%u: %s\n", i, cp); } if ((cp = GetVariableAsString (Trap_DEBUG, FALSE)) != null) printf (BasicErrorMessage, &Trap_DEBUG[1], cp); if ((cp = GetVariableAsString (Trap_ERR, FALSE)) != null) printf (BasicErrorMessage, &Trap_ERR[1], cp); return 0; } /* Check to see if signal re-set */ SetSignal = C2bool(LookupSignalName (argv[1]) == (struct TrapSignalList *)NULL); for (i = SetSignal ? 2 : 1; argv[i] != NOWORD; ++i) { if ((SignalInfo = LookupSignalName (argv[i])) == (struct TrapSignalList *)NULL) return PrintWarningMessage ("trap: bad signal number - %s", argv[i]); /* Check for no UNIX to DOS mapping */ if (SignalInfo->signo == SIG_NO_MAP) { if (!FL_TEST (FLAG_WARNING)) PrintWarningMessage ("trap: No UNIX to DOS signal map for %s", argv[i]); continue; } /* Check for ERR or DEBUG. cp points to the variable name */ if (SignalInfo->signo == SIG_SPECIAL) cp = (SignalInfo->name) - 1; /* Generate the variable name for a numeric value */ else sprintf (cp = tval, "~%d", SignalInfo->signo); /* Remove the old processing */ RemoveVariable (cp, 0); /* Default to new function of ignore! */ NewSignalFunc = SIG_DFL; /* Re-define signal processing */ if (SetSignal) { if (*argv[1] != 0) { SetVariableFromString (cp, argv[1]); NewSignalFunc = TerminateSignalled; } else NewSignalFunc = SIG_IGN; } /* Clear signal processing */ else if (InteractiveFlag) { if (SignalInfo->signo == SIGINT) NewSignalFunc = InterruptSignalled; #ifdef SIGQUIT else NewSignalFunc = (SignalInfo->signo == SIGQUIT) ? SIG_IGN : SIG_DFL; #endif } /* Set up new signal function */ if (SignalInfo->signo > 0) signal (SignalInfo->signo, NewSignalFunc); } return 0; } /* * BREAK and CONTINUE processing: break/continue [ n ] */ static int dobreak (int argc, char **argv) { if (argc > 2) return UsageError ("break [ count ]"); return BreakContinueProcessing (argv[1], BC_BREAK); } static int docontinue (int argc, char **argv) { if (argc > 2) return UsageError ("continue [ count ]"); return BreakContinueProcessing (argv[1], BC_CONTINUE); } static int F_LOCAL BreakContinueProcessing (char *NumberOfLevels, int Type) { Break_C *Break_Loc; int LevelNumber; char *cType = (Type == BC_BREAK) ? LIT_break : LIT_continue; LevelNumber = (NumberOfLevels == (char *)NULL) ? 1 : GetNumericValue (NumberOfLevels); if (LevelNumber < 0) return PrintWarningMessage (LIT_Emsg, cType, "bad level number", NumberOfLevels); /* If level is invalid - clear all levels */ if (LevelNumber <= 0) LevelNumber = 999; /* Move down the stack */ do { if ((Break_Loc = Break_List) == (Break_C *)NULL) break; Break_List = Break_Loc->NextExitLevel; } while (--LevelNumber); /* Check level */ if (LevelNumber) return PrintWarningMessage (BasicErrorMessage, cType, "bad level"); longjmp (Break_Loc->CurrentReturnPoint, Type); /* NOTREACHED */ return 1; } /* * Exit function: exit [ status ] */ static int doexit (int argc, char **argv) { Break_C *SShell_Loc = SShell_List; ProcessingEXECCommand = FALSE; if (argc > 2) return UsageError ("exit [ status ]"); /* Set up error codes */ ExitStatus = (argv[1] != NOWORD) ? GetNumericValue (argv[1]) : 0; SetVariableFromNumeric (StatusVariable, (long)abs (ExitStatus)); /* Are we in a subshell. Yes - do a longjmp instead of an exit */ if (SShell_Loc != (Break_C *)NULL) { SShell_List = SShell_Loc->NextExitLevel; longjmp (SShell_Loc->CurrentReturnPoint, ExitStatus); } /* * Check for active jobs */ #if (OS_TYPE != OS_DOS) if (!ExitWithJobsActive) { if (NumberOfActiveJobs () && InteractiveFlag) { feputs ("You have running jobs.\n"); ExitWithJobsActive = TRUE; return 0; } } #endif ExitTheShell (FALSE); return ExitStatus; } /* * Function return: return [ status ] * * Set exit value and return via a long jmp */ static int doreturn (int argc, char **argv) { Break_C *Return_Loc = Return_List; int Retval = 0; if ((argc > 2) || ((argc == 2) && ((Retval = GetNumericValue (argv[1])) == -1))) return UsageError (ReturnUsage); SetVariableFromNumeric (StatusVariable, (long) abs (Retval)); /* If the return address is defined - return to it. Otherwise, return * the value */ if (Return_Loc != (Break_C *)NULL) { Return_List = Return_Loc->NextExitLevel; longjmp (Return_Loc->CurrentReturnPoint, 1); } return Retval; } /* * Set function: set [ -/+flags ] [ parameters... ] */ static int doset (int argc, char **argv) { int i; /* Display ? */ if (argc < 2) return ListAllVariables (0xffff, TRUE); /* Set/Unset a flag ? */ ResetGetOptions (); /* Reset GetOptions */ while ((i = GetOptions (argc, argv, "VMA:abcdefghijklmno:pqrstuvwxyz", GETOPT_PLUS | GETOPT_AMISSING)) != EOF) { switch (i) { case '?': /* Unknown */ if (BadOptionValue != 'o') return UsageError ("set [ [-|+][switches] ] [ [-|+]o option ] [ parameter=value ] args"); return PrintOptionSettings (); case 'r': return PrintWarningMessage ("set: r switch cannot be changed"); case 'o': if ((!ChangeInitialisationValue (OptionArgument, OptionStart == '-')) && (!ChangeOptionValue (OptionArgument, (bool)(OptionStart == '-')))) return PrintWarningMessage ("set: -o bad option (%s)", OptionArgument); break; case 'A': if (OptionStart == '-') /* If -, remove all values */ RemoveVariable (OptionArgument, -1); i = 0; while (argv[OptionIndex] != NOWORD) SetVariableArrayFromString (OptionArgument, i++, argv[OptionIndex++]); return 0; case 'M': if (OptionStart == '-') /* If -, remove all values */ ShellGlobalFlags |= FLAGS_MSDOS_FORMAT; else ShellGlobalFlags &= ~FLAGS_MSDOS_FORMAT; break; case 'V': SetVerifyStatus (C2bool (OptionStart == '-')); break; default: if ((i == 'e') && InteractiveFlag) continue; SetClearFlag (i, (bool)(OptionStart == '-')); } } SetShellSwitches (); /* Check for --, ++, - and +, which we skip */ if ((OptionIndex != argc) && ((!strcmp (argv[OptionIndex], DoubleHypen)) || (!strcmp (argv[OptionIndex], ShellOptionsVariable)) || (!strcmp (argv[OptionIndex], "+")))) OptionIndex++; /* Set up parameters ? */ if (OptionIndex != argc) { ResetGetoptsValues (TRUE); return SetUpNewParameterVariables (argv, OptionIndex, argc, "set"); } else return 0; } /* * Print the list of functions: functions [ names ] */ static int dofunctions (int argc, char **argv) { FunctionList *fp; int i; if (argc < 2) return PrintAllFunctions (); for (i = 1; argv[i] != NOWORD; ++i) { if ((fp = LookUpFunction (argv[i], TRUE)) != (FunctionList *)NULL) PrintFunction (fp->tree, PF_MODE_NORMAL); else PrintWarningMessage ("functions: %s is not a function", argv[i]); } return 0; } /* * History functions - history [-ieds] */ static int dohistory (int argc, char **argv) { int i; int Start; long value; if (!InteractiveFlag) return 1; if (argc < 2) Start = GetLastHistoryEvent () + 1; /* * Check for options */ else if (**(argv + 1) == CHAR_SWITCH) { ResetGetOptions (); /* Reset GetOptions */ while ((i = GetOptions (argc, argv, "sidel", 0)) != EOF) { switch (i) { case 's': DumpHistory (); break; case 'i': ClearHistory (); break; case 'l': LoadHistory (); break; case 'd': HistoryEnabled = FALSE; break; case 'e': HistoryEnabled = TRUE; break; default: return UsageError (HistoryUsage); } } return (OptionIndex != argc) ? UsageError (HistoryUsage) : 0; } /* Check for number of display */ else if ((argc == 2) && ConvertNumericValue (*(argv + 1), &value, 0)) Start = (int)value + 1; else return UsageError (HistoryUsage); /* * Display history */ if ((i = (GetLastHistoryEvent () + 1) - Start) < 0) i = 0; PrintHistory (FALSE, TRUE, i, GetLastHistoryEvent () + 1, stdout); return 0; } /* * Type function: type [ command ] * * For each name, indicate how it would be interpreted */ static int dowhence (int argc, char **argv) { char *cp; int n; /* Argument count */ int inb; /* Inbuilt function */ bool v_flag; bool p_flag = FALSE; bool t_flag = FALSE; char *l_path; AliasList *al; /* Get some memory for the buffer */ if ((l_path = AllocateMemoryCell (FFNAME_MAX + 4)) == (char *)NULL) return doOutofMemory (*argv); v_flag = (bool)(strcmp (*argv, LIT_type) == 0); ResetGetOptions (); /* Reset GetOptions */ while ((n = GetOptions (argc, argv, "pvt", 0)) != EOF) { switch (n) { case 'v': v_flag = TRUE; break; case 'p': p_flag = TRUE; break; case 't': t_flag = TRUE; break; default: return UsageError (WhenceUsage); } } /* Process each parameter */ while ((cp = argv[OptionIndex++]) != NOWORD) { /* Check for alias */ if ((al = LookUpAlias (cp, FALSE)) != (AliasList *)NULL) { if (v_flag) printf ("%s is %s alias for %s", cp, (al->AFlags & ALIAS_TRACKED) ? "a tracked" : "an", al->value); else foputs (al->value); } /* Check for currently use inbuilt version */ else if (!p_flag && IsCommandBuiltIn (cp, &inb) && (inb & BLT_CURRENT)) WhenceLocation (v_flag, cp, ShellInternalCommand); /* Check for a function */ else if (!p_flag && (LookUpFunction (cp, FALSE) != (FunctionList *)NULL)) WhenceLocation (v_flag, cp, "is a function"); /* Scan the path for an executable */ else if (FindLocationOfExecutable (l_path, cp) != EXTENSION_NOT_FOUND) { PATH_TO_LOWER_CASE (l_path); if (v_flag) printf ("%s is ", cp); foputs (PATH_TO_UNIX (l_path)); if (t_flag) WhenceType (l_path); } /* If not found, check for inbuilt version */ else if (!p_flag && (IsCommandBuiltIn (cp, &inb))) WhenceLocation (v_flag, cp, ShellInternalCommand); else if (!p_flag && LookUpSymbol (cp)) WhenceLocation (v_flag, cp, "is a shell keyword"); else if (v_flag) { PrintWarningMessage (LIT_2Strings, cp, NotFound); continue; } fputchar (CHAR_NEW_LINE); } return 0; } /* * Output file type */ static char *ExeType_Error[] = { "Not known", "Bad image", "Not executable", "File not found" }; static char *ExeType_Dos[] = { "DOS Character", "Windows 16-bit", "Watcom 32 bit", "OS/2 Bound" }; static char *ExeType_OS2[] = { "Not PM compatible", "PM compatible", "PM" }; static char *ExeType_NT[] = { "Native", "Windows GUI", "Windows CUI", "OS2", "POSIX" }; static void F_LOCAL WhenceType (const char *path) { unsigned long type = QueryApplicationType (path); foputs (" ["); if (type & EXETYPE_ERROR) foputs (ExeType_Error [type - 1]); else if (type & EXETYPE_DOS) foputs (ExeType_Dos [(type >> 4) - 1]); else if (type & EXETYPE_OS2) printf ("OS2 %dbit %s", (type & EXETYPE_OS2_32) ? 32 : 16, ExeType_OS2 [((type & EXETYPE_OS2_TYPE) >> 8) - 1]); else if (type & EXETYPE_NT) printf ("Win NT %s subsystem", ExeType_NT [(type >> 12) - 1]); fputchar (']'); } void WhenceTypeDebug (unsigned long type) { fprintf(stderr, " Type: %04x = ", type ); if (type & EXETYPE_ERROR) fprintf (stderr, ExeType_Error [type - 1]); else if (type & EXETYPE_DOS) fprintf (stderr, ExeType_Dos [(type >> 4) - 1]); else if (type & EXETYPE_OS2) fprintf (stderr, "OS2 %dbit %s", (type & EXETYPE_OS2_32) ? 32 : 16, ExeType_OS2 [((type & EXETYPE_OS2_TYPE) >> 8) - 1]); else if (type & EXETYPE_NT) fprintf (stderr, "Win NT %s subsystem", ExeType_NT [(type >> 12) - 1]); fprintf(stderr, "\n"); } /* * Output location */ static void F_LOCAL WhenceLocation (bool v_flag, const char *cp, const char *mes) { foputs (cp); if (v_flag) printf (" %s", mes); } /* * Table of internal commands. Note that this table is sort in alphabetic * order. */ static struct builtin builtin[] = { { "((", dolet, (BLT_ALWAYS | BLT_CURRENT) }, { ".", dodot, (BLT_ALWAYS | BLT_CURRENT | BLT_CENVIRON) }, { ":", dolabel, (BLT_ALWAYS | BLT_CURRENT | BLT_CENVIRON) }, { "[", dotest, BLT_CURRENT }, { LIT_Test, dotest, (BLT_ALWAYS | BLT_CURRENT | BLT_CENVIRON | BLT_NOGLOB) }, { LIT_alias, doalias, (BLT_ALWAYS | BLT_CURRENT | BLT_CENVIRON | BLT_NOGLOB | BLT_NOWORDS) }, #if defined (FLAGS_EMACS) || defined (FLAGS_GMACS) { "bind", dobind, (BLT_CURRENT | BLT_CENVIRON) }, #endif { LIT_break, dobreak, (BLT_CURRENT | BLT_CENVIRON) }, { LIT_builtin, dobuiltin, (BLT_ALWAYS | BLT_CURRENT) }, { "cd", dochdir, BLT_CURRENT }, { "chdir", dochdir, (BLT_ALWAYS | BLT_CURRENT) }, { LIT_continue, docontinue, (BLT_CURRENT | BLT_CENVIRON) }, #if (OS_TYPE != OS_DOS) { "detach", dodetach, (BLT_ALWAYS | BLT_CURRENT) }, #endif { "echo", doecho, BLT_CURRENT }, { "eval", doeval, (BLT_CURRENT | BLT_CENVIRON) }, { LIT_exec, (int (*)(int, char **)) doexec, (BLT_CURRENT | BLT_CENVIRON) }, { LIT_exit, doexit, (BLT_CURRENT | BLT_CENVIRON) }, { LIT_export, doexport, (BLT_CURRENT | BLT_CENVIRON | BLT_NOGLOB | BLT_NOWORDS) }, #if (OS_TYPE == OS_OS2) { "extproc", dolabel, (BLT_CURRENT | BLT_CENVIRON) }, #endif { "false", dofalse, BLT_CURRENT }, { "fc", dofc, BLT_CURRENT }, { "functions", dofunctions, (BLT_ALWAYS | BLT_CURRENT) }, { "getopts", dogetopts, BLT_CURRENT }, { LIT_history, dohistory, BLT_CURRENT }, #if (OS_TYPE != OS_DOS) { "jobs", dojobs, BLT_CURRENT }, { "kill", dokill, BLT_CURRENT }, #endif { "let", dolet, BLT_CURRENT }, { "msdos", domsdos, BLT_CURRENT }, { LIT_print, doecho, BLT_CURRENT }, { "pwd", dopwd, BLT_CURRENT }, { LIT_read, doread, BLT_CURRENT }, { "readonly", doreadonly, (BLT_CURRENT | BLT_CENVIRON | BLT_NOGLOB | BLT_NOWORDS) }, { "return", doreturn, (BLT_CURRENT | BLT_CENVIRON) }, { "set", doset, BLT_CURRENT }, { "shellinfo", doshellinfo, BLT_CURRENT }, { LIT_shift, doshift, (BLT_CURRENT | BLT_CENVIRON) }, #if (OS_TYPE == OS_OS2) { "start", dostart, BLT_CURRENT }, #endif { "swap", doswap, BLT_CURRENT }, { "test", dotest, BLT_CURRENT }, #if (OS_TYPE == OS_OS2) && (OS_SIZE == OS_32) && !defined (__WATCOMC__) { "times", dotimes, BLT_CURRENT }, #endif #if (OS_TYPE == OS_NT) || (OS_TYPE == OS_UNIX) { "times", dotimes, BLT_CURRENT }, #endif { "trap", dotrap, (BLT_CURRENT | BLT_CENVIRON) }, { "true", dolabel, BLT_CURRENT }, { LIT_type, dowhence, BLT_CURRENT }, { "typeset", dotypeset, (BLT_CURRENT | BLT_CENVIRON | BLT_NOGLOB | BLT_NOWORDS) }, { "umask", doumask, BLT_CURRENT }, { LIT_unalias, dounalias, (BLT_ALWAYS | BLT_CURRENT) }, { LIT_unfunction, dounset, BLT_CURRENT }, { "unset", dounset, BLT_CURRENT }, { "ver", dover, BLT_CURRENT }, #if (OS_TYPE != OS_DOS) { "wait", dowait, BLT_CURRENT }, #endif { "whence", dowhence, BLT_CURRENT }, { (char *)NULL, (int (*)(int, char **))NULL, 0 } }; /* * Look up a built in command */ int (*IsCommandBuiltIn (char *s, int *b))(int, char **) { struct builtin *bp; int res; *b = 0; /* Check for change drive */ if ((strlen (s) == 2) && isalpha (*s) && IsDriveCharacter (*(s + 1))) { *b = BLT_ALWAYS | BLT_CURRENT; return (int (*)(int, char **)) dodrive; } /* Search for command */ for (bp = builtin; bp->command != (char *)NULL; bp++) { if ((res = NOCASE_COMPARE (bp->command, s)) >= 0) { if (res != 0) { if (!isalpha (*(bp->command))) continue; break; } *b = bp->mode; return bp->fn; } } return (int (*)(int, char **))NULL; } /* * Builtin - either list builtins or execute it * * builtin [-asd] [ names ] */ static int dobuiltin (int argc, char **argv) { struct builtin *bp; int (*shcom)(int, char **) = (int (*)(int, char **))NULL; int ReturnValue = 0; char *carg; int mode; int action = -1; int i; if (argc < 2) { for (bp = builtin; bp->command != (char *)NULL; bp++) printf (LIT_3Strings, LIT_builtin, bp->command, (bp->mode & BLT_CURRENT) ? " - preferred" : ""); return 0; } /* Check for changing options */ ResetGetOptions (); /* Reset GetOptions */ while ((i = GetOptions (argc, argv, "sad", 0)) != EOF) { switch (i) { case 's': action = 0; break; case 'a': action = 1; break; case 'd': action = 2; break; default: return UsageError (BuiltinUsage); } } /* Check to see if we know about the builtin version */ if (action == -1) { if ((shcom = IsCommandBuiltIn (argv[1], &mode)) == (int (*)(int, char **))NULL) { printf (BasicErrorMessage, argv[1], NotBuiltinCommand); return 1; } /* Yes - so execute the builtin version. Set up the word list correctly */ argv++; ReturnValue = (*shcom)(CountNumberArguments (argv), argv); argv--; return ReturnValue; } /* Execute the requested functions against the builtin commands */ while ((carg = argv[OptionIndex++]) != NOWORD) { for (bp = builtin; (bp->command != (char *)NULL) && (NOCASE_COMPARE (bp->command, carg) != 0); bp++) continue; /* Command is not builtin */ if (bp->command == (char *)NULL) { printf (BasicErrorMessage, carg, NotBuiltinCommand); ReturnValue = 1; continue; } /* Update on the action */ switch (action) { case 0: printf (BasicErrorMessage, carg, (bp->mode & BLT_CURRENT) ? LIT_builtin : "external"); break; case 1: bp->mode |= BLT_CURRENT; break; case 2: if (bp->mode & BLT_ALWAYS) printf (BasicErrorMessage, carg, "always builtin"); else bp->mode &= ~BLT_CURRENT; break; } } return ReturnValue; } /* * Report Usage error */ static int F_LOCAL UsageError (char *string) { return PrintWarningMessage ("Usage: %s", string) + 1; } /* * Alias command: alias [ -t] [ name [=commands] ]... */ static int doalias (int argc, char **argv) { int ReturnValue = 0; int i = 1; bool tracked = FALSE; char *path = (char *)NULL; char *cp; /* Check for tracked aliases */ if ((argc > 1) && (strcmp (argv[1], "-t") == 0)) { ++i; tracked = TRUE; } /* List only? */ if (argc <= i) return PrintAllAlias (tracked); /* Set them up or print them */ while (i < argc) { /* Not tracked - either set alias value if there is an equals or display * the alias value */ if (!tracked) { if ((cp = strchr (argv[i], '=')) != (char *)NULL) *(cp++) = 0; /* Check for valid name */ if (!IsValidAliasName (argv[i], TRUE)) return PrintWarningMessage (LIT_Emsg, LIT_alias, NotValidAlias, argv[i]); /* Save it if appropriate */ if (cp != (char *)NULL) { if (!SaveAlias (argv[i], cp, tracked)) ReturnValue = 1; } /* Print it */ else if (LookUpAlias (argv[i], FALSE) == (AliasList *)NULL) { PrintWarningMessage (NotAnAlias, LIT_alias, argv[i]); ReturnValue = 1; } else PrintAlias (argv[i]); } /* Set up tracked alias */ else if (!IsValidAliasName (argv[i], TRUE)) return PrintWarningMessage (LIT_Emsg, LIT_alias, NotValidAlias, argv[i]); else if ((path == (char *)NULL) && ((path = AllocateMemoryCell (FFNAME_MAX + 4)) == (char *)NULL)) return doOutofMemory (*argv); /* Save the new path for the alias */ else if (SaveAlias (argv[i], (FindLocationOfExecutable (path, argv[i]) != EXTENSION_NOT_FOUND) ? path : null, tracked)) ReturnValue = 1; ++i; } return ReturnValue; } /* * UnAlias command: unalias name... */ static int dounalias (int argc, char **argv) { int i; /* Set them up or print them */ for (i = 1; i < argc; ++i) { if (LookUpAlias (argv[i], FALSE) != (AliasList *)NULL) DeleteAlias (argv[i]); else PrintWarningMessage (NotAnAlias, LIT_unalias, argv[i]); } return 0; } /* * OS2 detach function */ #if (OS_TYPE != OS_DOS) static int dodetach (int argc, char **argv) { int RetVal; if (argc < 2) return UsageError ("detach program [ parameters ... ]"); argv++; RetVal = ExecuteACommand (argv, EXEC_SPAWN_DETACH); argv--; return RetVal; } #endif /* * Start a session */ #if (OS_TYPE == OS_OS2) static int dostart (int argc, char **argv) { bool Direct = FALSE; bool UseCMD = FALSE; STARTDATA stdata; STARTDATA *sdp = &stdata; Word_B *wb = (Word_B *)NULL; Word_B *ep = (Word_B *)NULL; int c; bool options = FALSE; bool SetDefault = FALSE; char ChangeEnv = 0; bool FirstArg = TRUE; bool Display = FALSE; OSCALL_RET rc; OSCALL_PARAM ulSessID; char *sp; char p_name[FFNAME_MAX]; char *SetPWD = GetVariableAsString (PWDVariable, FALSE); /* Initialise the session control info */ memset (&stdata, 0, sizeof (STARTDATA)); stdata.Length = sizeof (STARTDATA); stdata.FgBg = SSF_FGBG_BACK; /* Set background session */ stdata.PgmTitle = (PGM_TITLE_TYPE *)NULL; stdata.Related = SSF_RELATED_CHILD; stdata.TraceOpt = SSF_TRACEOPT_NONE; stdata.InheritOpt = SSF_INHERTOPT_SHELL; stdata.IconFile = (char *)NULL; stdata.PgmHandle = 0L; stdata.PgmControl = SSF_CONTROL_NOAUTOCLOSE; stdata.InitXPos = 0; stdata.InitYPos = 0; stdata.InitXSize = 100; stdata.InitYSize = 100; /* These values get reset somewhere along the line */ # if (OS_SIZE == OS_16) stdata.SessionType = SSF_TYPE_WINDOWABLEVIO; # else stdata.SessionType = SSF_TYPE_DEFAULT; # endif stdata.Environment = (PBYTE)1; /* Build the environment */ /* Switch on the arguments */ ResetGetOptions (); /* Reset GetOptions */ while ((c = GetOptions (argc, argv, SetDefault ? "t:hHfWPFibIDxe:c:" : "O:St:dhHfWPFxibCIe:c:A:X:", 0)) != EOF) { switch (c) { case 'O': if (!FirstArg) return UsageError (StartUsage); else if (!stricmp (OptionArgument, LIT_dos)) sdp = &DOS_SessionControlBlock; else if (!stricmp (OptionArgument, "pm")) sdp = &PM_SessionControlBlock; else return UsageError (StartUsage); SetDefault = TRUE; break; case 'A': if ((options) || (OptionIndex != argc)) return UsageError (StartUsage); ulSessID = (OSCALL_PARAM) strtoul (OptionArgument, &sp, 0); if (*sp) return UsageError (StartUsage); # if (OS_SIZE == OS_16) if ((rc = DosSelectSession (ulSessID, 0L))) # else if ((rc = DosSelectSession (ulSessID))) # endif return PrintWarningMessage (Start_NoSession, ulSessID, GetOSSystemErrorMessage (rc)); return 0; case 'c': /* Set program control */ { sdp->PgmControl = 0; sp = OptionArgument; while (*sp) { switch (*(sp++)) { case 'v': sdp->PgmControl |= SSF_CONTROL_VISIBLE; sdp->PgmControl &= ~SSF_CONTROL_INVISIBLE; break; case 'i': sdp->PgmControl |= SSF_CONTROL_INVISIBLE; sdp->PgmControl &= ~SSF_CONTROL_VISIBLE; break; case 'l': sdp->PgmControl |= SSF_CONTROL_MAXIMIZE; sdp->PgmControl &= ~SSF_CONTROL_MINIMIZE; break; case 's': sdp->PgmControl |= SSF_CONTROL_MINIMIZE; sdp->PgmControl &= ~SSF_CONTROL_MAXIMIZE; break; case 'n': sdp->PgmControl |= SSF_CONTROL_NOAUTOCLOSE; break; case 'a': sdp->PgmControl &= ~SSF_CONTROL_NOAUTOCLOSE; break; default: return PrintWarningMessage ("start: option must be one of [vilsna]") + 1; } } break; } case 'X': /* Set startup directory */ SetPWD = OptionArgument; break; case 't': /* Set title */ if (SetDefault && (sdp->PgmTitle != (PGM_TITLE_TYPE *)NULL)) ReleaseMemoryCell (sdp->PgmTitle); sdp->PgmTitle = (!strlen (OptionArgument)) ? (char *)NULL : SetDefault ? StringSave (OptionArgument) : OptionArgument; if (strlen (OptionArgument) > 32) sdp->PgmTitle[33] = 0; break; case 'S': /* Script */ Direct = FALSE; break; case 'd': /* Direct */ Direct = TRUE; break; case 'H': /* Inherit options */ sdp->InheritOpt = SSF_INHERTOPT_SHELL; break; case 'h': /* Inherit options */ sdp->InheritOpt = SSF_INHERTOPT_PARENT; break; case 'f': /* Foreground */ sdp->FgBg = SSF_FGBG_FORE; break; case 'b': /* Background */ sdp->FgBg = SSF_FGBG_BACK; break; case 'I': sdp->Related = SSF_RELATED_INDEPENDENT; break; case 'x': sdp->Related = SSF_RELATED_CHILD; break; case 'W': /* PM Window */ sdp->FgBg = SSF_FGBG_FORE; sdp->SessionType = (sdp == &DOS_SessionControlBlock) ? SSF_TYPE_WINDOWABLEVIO : SSF_TYPE_WINDOWEDVDM; break; case 'P': /* PM Session */ sdp->FgBg = SSF_FGBG_FORE; sdp->SessionType = (sdp == &DOS_SessionControlBlock) ? SSF_TYPE_PM : SSF_TYPE_WINDOWEDVDM; break; case 'F': /* Full Screen */ sdp->FgBg = SSF_FGBG_FORE; sdp->SessionType = (sdp == &DOS_SessionControlBlock) ? SSF_TYPE_FULLSCREEN : SSF_TYPE_VDM; break; case 'C': /* Use the CMD processor */ UseCMD = TRUE; break; case 'D': /* Show options */ Display = TRUE; break; case 'e': /* Define environment */ if (ChangeEnv == 'i') return UsageError (StartUsage); if ((sdp->Environment != (PBYTE)NULL) && (sdp->Environment != (PBYTE)1)) { ReleaseMemoryCell (sdp->Environment); sdp->Environment = (PBYTE)NULL; } ChangeEnv = 'e'; ep = AddWordToBlock (OptionArgument, ep); break; case 'i': /* Inherit environment */ if (ChangeEnv == 'e') return UsageError (StartUsage); if ((sdp->Environment != (PBYTE)NULL) && (sdp->Environment != (PBYTE)1)) ReleaseMemoryCell (sdp->Environment); ChangeEnv = 'i'; sdp->Environment = (PBYTE)NULL; break; default: return UsageError (StartUsage); } FirstArg = FALSE; options = TRUE; } /* If setting default, no more parameters allowed */ if (SetDefault) { if (OptionIndex != argc) return UsageError (StartUsage); } else { /* Check for script */ if ((OptionIndex != argc) && (!UseCMD) && (!Direct) && (FindLocationOfExecutable (p_name, argv[OptionIndex]) == EXTENSION_EXECUTABLE)) Direct = TRUE; /* Find the program to start */ if ((OptionIndex == argc) || (!Direct)) { if (!UseCMD) { wb = AddWordToBlock (GetVariableAsString (ShellVariableName, FALSE), wb); if (SetPWD != null) { wb = AddWordToBlock ("-X", wb); wb = AddWordToBlock (SetPWD, wb); } } else { wb = AddWordToBlock (GetVariableAsString (ComspecVariable, FALSE), wb); if (OptionIndex != argc) wb = AddWordToBlock ("/c", wb); } } else wb = AddWordToBlock (argv[OptionIndex++], wb); } /* Set up environment */ if ((ep != (Word_B *)NULL) && ((sdp->Environment = BuildOS2String (GetWordList (AddWordToBlock (NOWORD, ep)), 0)) == (PBYTE)NULL)) return doOutofMemory ("start"); /* OK, if we set the default, processing is completed */ if (SetDefault) { if (ep != (Word_B *)NULL) SetMemoryAreaNumber (sdp->Environment, 0); if (Display) DisplayStartData (sdp); return 0; } /* Set up the session block and execute the command */ SessionControlBlock = &stdata; /* Build the argument block */ while (OptionIndex != argc) wb = AddWordToBlock (argv[OptionIndex++], wb); /* Start the session */ argv = GetWordList (AddWordToBlock (NOWORD, wb)); return (ExecuteACommand (argv, 0) == -1) ? 1 : 0; } /* * Clean up the start data structure, removing allocated space */ static void F_LOCAL DisplayStartData (STARTDATA *sdp) { char *sp; printf ("Start session defaults for %s mode\n", (sdp == &DOS_SessionControlBlock) ? LIT_dos : "PM"); printf (" Window mode: %s%s%s %sautoclose.\n", (sdp->PgmControl & SSF_CONTROL_INVISIBLE) ? "invisible" : "visible", (sdp->PgmControl & SSF_CONTROL_MINIMIZE) ? " minimised" : null, (sdp->PgmControl & SSF_CONTROL_MAXIMIZE) ? " maximised" : null, (sdp->PgmControl & SSF_CONTROL_NOAUTOCLOSE) ? "no " : ""); if (sdp->PgmTitle != (PGM_TITLE_TYPE *)NULL) printf (" Program Title: %s\n", sdp->PgmTitle); printf (" Run in %s.\n", (sdp->FgBg == SSF_FGBG_FORE) ? "foreground" : "background"); printf (" %s session.\n", (sdp->Related == SSF_RELATED_INDEPENDENT) ? "Independent" : "Dependent"); printf (" Session type: %s.\n", (sdp->SessionType == SSF_TYPE_WINDOWABLEVIO) ? "Windowed" : (sdp->SessionType == SSF_TYPE_PM) ? "PM" : (sdp->SessionType == SSF_TYPE_FULLSCREEN) ? "Full screen" : (sdp->SessionType == SSF_TYPE_WINDOWEDVDM) ? "DOS Windowed" : (sdp->SessionType == SSF_TYPE_VDM) ? "DOS Full screen" : "Default"); printf (" Inherit %s environment\n", (sdp->InheritOpt == SSF_INHERTOPT_SHELL) ? "start up" : "current"); if (sdp->Environment == (PBYTE)1) puts (" Use current environment variables."); else if (sdp->Environment == (PBYTE)NULL) puts (" Use start up environment variables."); else { sp = sdp->Environment; printf (" Environment variables:\n"); while (*sp) { printf (" %s\n", sp); sp += strlen (sp) + 1; } } } #endif /* * Set up new Parameter Variables */ static int F_LOCAL SetUpNewParameterVariables (char **Array,/* New values*/ int Offset, /* Start offset */ int Max, /* Number */ char *function) { int n; Word_B *wb = (Word_B *)NULL; char *cp; bool Duplicate = (bool)(strcmp (function, LIT_shift) == 0); if ((wb = AddParameter (ParameterArray[0], wb, function)) == (Word_B *)NULL) return 1; for (n = Offset; n < Max; ++n) { if ((cp = (Duplicate) ? StringSave (Array[n]) : Array[n]) == null) return doOutofMemory (function); if ((wb = AddParameter (cp, wb, function)) == (Word_B *)NULL) return 1; } return (AddParameter (NOWORD, wb, function) == (Word_B *)NULL) ? 1 : 0; } /* * dolet - Each arg is an arithmetic expression to be evaluated. */ static int dolet (int argc, char **argv) { long Value = 0; int i; /* If (( )), ignore the terminating )) */ if ((strcmp (argv[0], "((") == 0) && (strcmp (argv[argc - 1], "))") == 0)) argv[argc - 1] = NOWORD; ExpansionErrorDetected = FALSE; for (i = 1; !ExpansionErrorDetected && (argv[i] != NOWORD); ++i) Value = EvaluateMathsExpression (argv[i]); return !Value || ExpansionErrorDetected ? 1 : 0; } /* * Out of memory error */ static int F_LOCAL doOutofMemory (char *s) { return PrintWarningMessage (BasicErrorMessage, s, Outofmemory1); } /* * MSDOS, EXPORT and READONLY functions: xxxx [ variable names... ] */ static int doexport (int argc, char **argv) { (void)argc; return UpdateVariableStatus (argv + 1, STATUS_EXPORT); } static int doreadonly (int argc, char **argv) { (void)argc; return UpdateVariableStatus (argv + 1, STATUS_READONLY); } static int domsdos (int argc, char **argv) { (void)argc; return UpdateVariableStatus (argv + 1, STATUS_CONVERT_MSDOS); } static int F_LOCAL UpdateVariableStatus (char **argv, unsigned int Mask) { if (*argv == NOWORD) return ListAllVariables (Mask, FALSE); else { memset (&TypesetValues, 0, sizeof (TypesetValues)); TypesetValues.Flags_On = Mask; return TypesetVariables (argv); } } /* * List All variables matching a STATUS */ static int F_LOCAL ListAllVariables (unsigned int Mask, bool PrintValue) { HandleSECONDandRANDOM (); DVE_Mask = Mask; DVE_PrintValue = PrintValue; twalk (VariableTree, DisplayVariableEntry); return 0; } /* * TWALK Function - display VARIABLE tree */ static void DisplayVariableEntry (const void *key, VISIT visit, int level) { VariableList *vp = *(VariableList **)key; (void)level; if ((visit == postorder) || (visit == leaf)) { if ((IS_VariableFC ((int)*vp->name)) && ((vp->status & DVE_Mask) || (((vp->status & ~STATUS_GLOBAL) == 0) && (DVE_Mask == 0xffff)))) PrintEntry (vp, DVE_PrintValue, (DVE_Mask == 0xffff) ? 0 : DVE_Mask); } } /* * typeset function - [ [ [+-][Hflprtux] ] [+-][LRZi[n]] [ name [=value] ...] */ static int dotypeset (int argc, char **argv) { int ReturnValue = 0; bool f_flag = FALSE; unsigned int *CurrentFlags; int tmp = 0; char c_opt; char *cp; /* Initialise save area */ memset (&TypesetValues, 0, sizeof (TypesetValues)); OptionIndex = 1; /* Scan the options */ while ((cp = argv[OptionIndex]) != NOWORD) { if ((*cp != '-') && (*cp != '+')) break; CurrentFlags = (*cp == '-') ? &TypesetValues.Flags_On : &TypesetValues.Flags_Off; while (*(++cp)) { switch (*cp) { case 'p': fprintf (stderr, "typeset: Option (%c) not supported\n", *cp); break; case 'H': *CurrentFlags |= STATUS_CONVERT_MSDOS; break; case 'f': /* Function only */ f_flag = TRUE; break; case 'l': *CurrentFlags |= STATUS_LOWER_CASE; break; case 'r': *CurrentFlags |= STATUS_READONLY; break; case 't': *CurrentFlags |= STATUS_TAGGED; break; case 'u': *CurrentFlags |= STATUS_UPPER_CASE; break; case 'x': *CurrentFlags |= STATUS_EXPORT; break; case 'L': case 'R': case 'Z': case 'i': { switch (c_opt = *cp) { case 'L': *CurrentFlags |= STATUS_LEFT_JUSTIFY; break; case 'R': *CurrentFlags |= STATUS_RIGHT_JUSTIFY; break; case 'Z': *CurrentFlags |= STATUS_ZERO_FILL; break; case 'i': *CurrentFlags |= STATUS_INTEGER; break; } /* Only set width on on */ if (CurrentFlags != &TypesetValues.Flags_On) break; /* Check for following numeric */ if (isdigit (*(cp + 1))) tmp = (int)strtol (cp + 1, &cp, 10); else if ((*(cp + 1) == 0) && (OptionIndex + 1 < argc) && isdigit (*argv[OptionIndex + 1])) tmp = (int)strtol (argv[++OptionIndex], &cp, 10); else break; /* Check for invalid numeric */ if (!tmp || *(cp--)) return UsageError (TypeSetUsage); /* Width or base */ if (c_opt == 'i') TypesetValues.Base = tmp; else TypesetValues.Width = tmp; break; } default: return UsageError (TypeSetUsage); } } ++OptionIndex; } /* Check for f flag - function processing. */ if (f_flag) { if (((TypesetValues.Flags_On | TypesetValues.Flags_Off) & ~(STATUS_TAGGED | STATUS_EXPORT)) || (!(TypesetValues.Flags_On | TypesetValues.Flags_Off))) return PrintWarningMessage ("typeset: Only -xt allowed with -f"); for (tmp = OptionIndex; tmp < argc; tmp++) ReturnValue |= HandleFunction (argv[tmp]); return ReturnValue; } /* Process variable assignments */ return TypesetVariables (&argv[OptionIndex]); } static int F_LOCAL TypesetVariables (char **argv) { bool PrintValue = C2bool (TypesetValues.Flags_Off); VariableList *vp; char *CurrentName; char *NewValue; char *OldValue; int OldStatus; long NValue; char *xp; int Retval = 0; unsigned int Mask; long Index; if ((Mask = (TypesetValues.Flags_On | TypesetValues.Flags_Off)) == 0) Mask = 0xffff; /* Switch off any appropriate flags */ if (TypesetValues.Flags_On & STATUS_LOWER_CASE) TypesetValues.Flags_Off |= STATUS_UPPER_CASE; if (TypesetValues.Flags_On & STATUS_UPPER_CASE) TypesetValues.Flags_Off |= STATUS_LOWER_CASE; if (TypesetValues.Flags_On & STATUS_RIGHT_JUSTIFY) TypesetValues.Flags_Off |= STATUS_LEFT_JUSTIFY; if (TypesetValues.Flags_On & STATUS_LEFT_JUSTIFY) TypesetValues.Flags_Off |= STATUS_RIGHT_JUSTIFY; /* If no arguments, print all values matching the mask */ if (*argv == NOWORD) return ListAllVariables (Mask, PrintValue); /* Process each argument. If no flags, print it */ while ((CurrentName = *(argv++)) != NOWORD) { if (!GetVariableName (CurrentName, &Index, &NewValue, (bool *)NULL)) { PrintErrorMessage (BasicErrorMessage, CurrentName, LIT_BadID); return 1; } /* Convert the = to a null so we get the name and value */ if (*NewValue) *(NewValue++) = 0; else NewValue = (char *)NULL; /* If valid - update, otherwise print a message */ if ((Mask != 0xffff) || (NewValue != (char *)NULL)) { /* Get the original value */ vp = LookUpVariable (CurrentName, (int)Index, TRUE); OldStatus = vp->status; OldValue = GetVariableArrayAsString (CurrentName, (int)Index, FALSE); /* Update status */ vp->status &= ~(TypesetValues.Flags_Off); vp->status |= TypesetValues.Flags_On; if ((CurrentFunction != (FunctionList *)NULL) && (!(vp->status & STATUS_GLOBAL))) vp->status |= STATUS_LOCAL; if (Index) vp->status &= ~(STATUS_EXPORT); /* Set up a new integer value. If the variable was not an integer * originally and there is an error, unset it */ xp = (NewValue != (char *)NULL) ? NewValue : OldValue; if (vp->status & STATUS_INTEGER) { if (ValidMathsExpression (xp, &NValue)) { Retval = PrintWarningMessage (LIT_Emsg, "bad numeric value", CurrentName, xp); if (!(OldStatus & STATUS_INTEGER)) UnSetVariable (CurrentName, (int)Index, FALSE); continue; } else if (OldStatus & STATUS_READONLY) { Retval = PrintWarningMessage (LIT_2Strings, vp->name, LIT_IsReadonly); continue; } /* Save the new integer value and set up base etc */ vp->nvalue = NValue; if (!vp->base) vp->base = (LastNumberBase != -1) ? LastNumberBase : 10; if (TypesetValues.Base) vp->base = TypesetValues.Base; if (vp->value != null) ReleaseMemoryCell ((void *)vp->value); vp->value = null; } /* String - update if appropriate, both the value and the width */ else if ((OldStatus & STATUS_READONLY) || (!(vp->status & STATUS_READONLY))) SetVariableArrayFromString (CurrentName, (int)Index, xp); /* New status is readonly - allow set, then stop them */ else { vp->status &= ~STATUS_READONLY; SetVariableArrayFromString (CurrentName, (int)Index, xp); vp->status |= STATUS_READONLY; } if (TypesetValues.Width) vp->width = TypesetValues.Width; } /* Print if appropriate */ else PrintEntry (LookUpVariable (CurrentName, (int)Index, FALSE), PrintValue, Mask); } return Retval; } static void F_LOCAL PrintEntry (VariableList *vp, bool PrintValue, unsigned int Mask) { unsigned int Flags = vp->status & Mask; if (vp->status & STATUS_NOEXISTANT) return; if (Flags & STATUS_INTEGER) printf ("integer "); if (Flags & STATUS_LEFT_JUSTIFY) printf ("left justified %d ", vp->width); if (Flags & STATUS_RIGHT_JUSTIFY) printf ("right justified %d ", vp->width); if (Flags & STATUS_ZERO_FILL) printf ("zero filled %d ", vp->width); if (Flags & STATUS_CONVERT_MSDOS) printf ("MS-DOS Format "); if (Flags & STATUS_LOWER_CASE) printf ("lowercase "); if (Flags & STATUS_UPPER_CASE) printf ("uppercase "); if (Flags & STATUS_READONLY) printf ("readonly "); if (Flags & STATUS_TAGGED) printf ("tagged "); if (Flags & STATUS_EXPORT) printf ("exported "); /* Print the value */ foputs (vp->name); if (vp->index || CountVariableArraySize (vp->name) > 1) printf (LIT_BNumber, vp->index); if (PrintValue) printf ("=%s", GetVariableArrayAsString (vp->name, vp->index, TRUE)); fputchar (CHAR_NEW_LINE); } /* * Handle typeset -f */ static int F_LOCAL HandleFunction (char *name) { FunctionList *fop; if (strchr (name, CHAR_ASSIGN) != (char *)NULL) return PrintWarningMessage ("typeset: cannot assign to functions"); if ((fop = LookUpFunction (name, FALSE)) == (FunctionList *)NULL) return PrintWarningMessage ("typeset: function %s does not exist", name); fop->Traced = C2bool (TypesetValues.Flags_On & STATUS_TAGGED); return 0; } /* * Modified version of getopt for shell */ void ResetGetOptions (void) { OptionIndex = 1; /* Reset the optind flag */ GetOptionPosition = 1; /* Current position */ } int GetOptions (int argc, /* Argument count */ char **argv, /* Argument array */ char *optstring, /* Options string */ int flags) /* Control flags */ { int cur_option; /* Current option */ char *cp; /* Character pointer */ BadOptionValue = 0; if (GetOptionPosition == 1) { /* Special for doecho */ if (flags & GETOPT_PRINT) return EOF; /* Check for out of range, correct start character and not single */ if ((OptionIndex >= argc) || (!(((OptionStart = *argv[OptionIndex]) == '-') || (((flags & GETOPT_PLUS) && (OptionStart == '+'))))) || (!argv[OptionIndex][1])) return EOF; if (!strcmp (argv[OptionIndex], DoubleHypen)) return EOF; } /* Get the current character from the current argument vector */ cur_option = argv[OptionIndex][GetOptionPosition]; /* Validate it */ if ((cur_option == ':') || ((cp = strchr (optstring, cur_option)) == (char *)NULL)) { if (flags & GETOPT_MESSAGE) PrintWarningMessage ("%s: illegal option -- %c", argv[0], cur_option); /* Save the bad option value and move to the next offset */ BadOptionValue = cur_option; if (!argv[OptionIndex][++GetOptionPosition]) { OptionIndex++; GetOptionPosition = 1; } return '?'; } /* Parameters following ? */ OptionArgument = (char *)NULL; if (*(++cp) == ':') { if (argv[OptionIndex][GetOptionPosition + 1]) OptionArgument = &argv[OptionIndex++][GetOptionPosition + 1]; else if (++OptionIndex >= argc) { if ((flags & (GETOPT_MESSAGE | GETOPT_AMISSING)) == GETOPT_MESSAGE) PrintWarningMessage ("%s: option (%c) requires an argument", argv[0], cur_option); BadOptionValue = cur_option; OptionArgument = (char *)-1; GetOptionPosition = 1; return '?'; } else OptionArgument = argv[OptionIndex++]; GetOptionPosition = 1; } else if (!argv[OptionIndex][++GetOptionPosition]) { GetOptionPosition = 1; OptionIndex++; } return cur_option; } /* * Kill the specified processes */ #if (OS_TYPE != OS_DOS) static struct KillSignalList { char *Name; int SigVal; } KillSignalList [] = { {"term", -1 }, # if (OS_TYPE == OS_OS2) {"usr1", PFLG_A }, {"usr2", PFLG_B }, {"usr3", PFLG_C }, # elif (OS_TYPE == OS_NT) {"break", CTRL_BREAK_EVENT}, {"int", CTRL_C_EVENT}, # endif }; #define MAX_KILL_SIGNALS ARRAY_SIZE (KillSignalList) static int dokill (int argc, char **argv) { int i; int Sigv = -1; char *cp; PID pidProcess = 0; long value = 0; # if (OS_TYPE == OS_NT) HANDLE hp = 0; # elif (OS_TYPE == OS_OS2) USHORT rc; # endif if (argc < 2) return UsageError (KillUsage); /* List signals ? */ if (!strcmp (argv[1], "-l")) { for (i = 0; i < MAX_KILL_SIGNALS; ++i) puts (KillSignalList[i].Name); return 0; } /* Look up signal name */ if (**(++argv) == '-') { cp = &argv[0][1]; for (i = 0; i < MAX_KILL_SIGNALS; ++i) { if (!stricmp (KillSignalList[i].Name, cp)) break; } if (i == MAX_KILL_SIGNALS) return PrintWarningMessage ("kill: bad signal name (%s)", cp); Sigv = KillSignalList[i].SigVal; if (*(++argv) == NOWORD) return UsageError (KillUsage); } /* Kill the processes */ while (*argv != NOWORD) { /* Find the PID */ if (((**argv == CHAR_JOBID) && !ConvertJobToPid ((*argv) + 1, &pidProcess)) || ((**argv != CHAR_JOBID) && !ConvertNumericValue (*argv, &value, 0))) return PrintWarningMessage ("kill: bad process/job id (%s)", *argv); /* If Process ID, its in value */ if (**argv != CHAR_JOBID) pidProcess = (PID)value; /* Send the signal */ # if (OS_TYPE == OS_OS2) if (Sigv == -1) { # if (OS_SIZE == OS_16) rc = DosKillProcess (DKP_PROCESSTREE, pidProcess); # else if ((rc = DosKillProcess (DKP_PROCESSTREE, pidProcess))) rc = (USHORT)DosSendSignalException (pidProcess, XCPT_SIGNAL_BREAK); # endif } else rc = Dos32FlagProcess (pidProcess, FLGP_SUBTREE, Sigv, 0); if (rc) return PrintWarningMessage ("kill: Cannot signal process %s\n%s", *argv, GetOSSystemErrorMessage (rc)); # elif (OS_TYPE == OS_NT) /* Check for Ctl C or Break */ if ((Sigv == CTRL_BREAK_EVENT) || (Sigv == CTRL_C_EVENT)) { if (!GenerateConsoleCtrlEvent (Sigv, pidProcess)) return PrintWarningMessage ("kill: Cannot kill process %s\n%s", *argv, GetOSSystemErrorMessage (GetLastError ())); } /* Open the process and terminate it */ else if ((hp = OpenProcess (PROCESS_ALL_ACCESS, TRUE, pidProcess)) == NULL) return PrintWarningMessage ("kill: Cannot access process %s\n%s", *argv, GetOSSystemErrorMessage (GetLastError ())); else if (!TerminateProcess (hp, 1)) { PrintWarningMessage ("kill: Cannot kill process %s\n%s", *argv, GetOSSystemErrorMessage (GetLastError ())); CloseHandle (hp); return 1; } CloseHandle (hp); # endif argv++; } return 0; } /* * Wait for process to end */ static int dowait (int argc, char **argv) { PID pidProcess; int TermStat; # if (OS_TYPE != OS_NT) int ReturnValue; # endif long value; /* Check usage */ if (argc > 2) return UsageError ("wait [ job ]"); /* Wait for all jobs ? */ if (argc < 2) { # if (OS_TYPE == OS_NT) return PrintWarningMessage ("wait: any job not supported on NT"); # else TermStat = -1; /* Yes - wait until wait returns an error */ while ((pidProcess = wait (&ReturnValue)) != -1) { DeleteJob (pidProcess); TermStat = ReturnValue; } # endif } /* Wait for a specific process. Job or PID? */ else { /* Move to the ID */ argv++; /* Find the PID */ if (((**argv == CHAR_JOBID) && !ConvertJobToPid ((*argv) + 1, &pidProcess)) || ((**argv != CHAR_JOBID) && !ConvertNumericValue (*argv, &value, 0))) return PrintWarningMessage ("wait: bad process/job id (%s)", *argv); /* If Process ID, its in value */ if (**argv != CHAR_JOBID) pidProcess = (PID)value; /* Wait for the specified process */ # if (OS_TYPE == OS_UNIX) fputs ("UNIX: Wait for process NI\n", stderr); # else if (cwait (&TermStat, pidProcess, WAIT_GRANDCHILD) == -1) return PrintWarningMessage ("wait: Process id (%s) not active", *argv); # endif DeleteJob (pidProcess); } /* Convert termination status to return code */ if (TermStat == -1) return -1; else if (TermStat & 0x00ff) return TermStat & 0x00ff; else return (TermStat >> 8) & 0x00ff; } /* * Print the job info */ static int dojobs (int argc, char **argv) { bool ListMode = FALSE; # if (OS_TYPE == OS_OS2) || (OS_TYPE == OS_UNIX) pid_t p = getpid (); bool ListTree = FALSE; long value; # endif int i; /* List signals ? */ ResetGetOptions (); /* Reset GetOptions */ while ((i = GetOptions (argc, argv, "plP:", 0)) != EOF) { switch (i) { case 'l': ListMode = TRUE; break; # if (OS_TYPE == OS_OS2) || (OS_TYPE == OS_UNIX) case 'p': ListTree = TRUE; break; case 'P': ListTree = TRUE; if (!ConvertNumericValue (OptionArgument, &value, 0)) return PrintWarningMessage ("jobs: bad process id (%s)", OptionArgument); p = (pid_t)value; break; # endif default: return UsageError (JobUsage); } } # if (OS_TYPE == OS_OS2) || (OS_TYPE == OS_UNIX) if (ListTree) return PrintProcessTree (p); # endif /* Look up job name */ return PrintJobs (ListMode); } #endif /* * Print the option settings */ static int F_LOCAL PrintOptionSettings (void) { int i; puts ("Current option settings:"); for (i = 0; SetOptions[i].OptionName != (char *)NULL; i++) printf ("%-16s%s\n", SetOptions[i].OptionName, TestOptionValue (SetOptions[i].OptionName, FALSE) ? "on" : "off"); return 0; } /* * Change option value */ static bool F_LOCAL ChangeOptionValue (char *value, bool set) { struct SetOptions *entry = LookUpOptionValue (value); if (entry == (struct SetOptions *)NULL) return FALSE; else if (entry->HasOptionValue) SetClearFlag (entry->FlagValue, set); /* If one of the Editor flags, disable all editor flags first */ else if (entry->FlagValue == FLAGS_VERIFY_SWITCH) SetVerifyStatus (set); #ifdef FLAGS_BREAK_SWITCH else if (entry->FlagValue == FLAGS_BREAK_SWITCH) SetBreakStatus (set); #endif #ifdef FLAGS_SET_OS2 else if (entry->FlagValue == FLAGS_SET_OS2) BaseOS = (set) ? BASE_OS_OS2 : BASE_OS_DOS; #endif #ifdef FLAGS_SET_NT else if (entry->FlagValue == FLAGS_SET_NT) BaseOS = (set) ? BASE_OS_NT : BASE_OS_DOS; #endif /* Change the editor flags */ else if (set) { if (entry->FlagValue & FLAGS_EDITORS) ShellGlobalFlags &= ~(FLAGS_EDITORS); ShellGlobalFlags |= entry->FlagValue; } else ShellGlobalFlags &= ~(entry->FlagValue); return TRUE; } /* * Update shell switches */ static void F_LOCAL SetClearFlag (int Flag, bool set) { if (set) FL_SET (Flag); else FL_CLEAR (Flag); } /* * Test an option */ static int F_LOCAL TestOptionValue (char *value, bool AllowJump) { struct SetOptions *entry = LookUpOptionValue (value); if (entry == (struct SetOptions *)NULL) { PrintWarningMessage ("%s: unknown option - %s", TestProgram, value); if (AllowJump) longjmp (TestErrorReturn, 1); return 0; } else if (entry->FlagValue == FLAGS_VERIFY_SWITCH) { #if (OS_TYPE == OS_OS2) BOOL fVerifyOn; DosQVerify (&fVerifyOn); return fVerifyOn; #elif (OS_TYPE != OS_DOS) return FALSE; #elif (OS_TYPE == OS_DOS) union REGS r; r.x.REG_AX = 0x5400; DosInterrupt (&r, &r); return r.h.al; #endif } #if (OS_TYPE == OS_DOS) else if (entry->FlagValue == FLAGS_BREAK_SWITCH) { union REGS r; r.x.REG_AX = 0x3300; DosInterrupt (&r, &r); return r.h.dl; } #endif #ifdef FLAGS_SET_OS2 else if (entry->FlagValue == FLAGS_SET_OS2) return BaseOS == BASE_OS_OS2; #endif #ifdef FLAGS_SET_NT else if (entry->FlagValue == FLAGS_SET_NT) return BaseOS == BASE_OS_NT; #endif else if (entry->HasOptionValue) return (FL_TEST (entry->FlagValue) != 0); return (ShellGlobalFlags & entry->FlagValue); } /* * Find an Option entry */ static struct SetOptions * F_LOCAL LookUpOptionValue (char *value) { int i = 0; char *cp; while ((cp = SetOptions[i].OptionName) != (char *)NULL) { if (!strcmp (cp, value)) return &SetOptions[i]; ++i; } return (struct SetOptions *)NULL; } /* * Get Unit number */ static int F_LOCAL GetUnitNumber (char *prog) { int Unit; if (((Unit = GetNumericValue (OptionArgument)) < 0) || (Unit > 9)) { PrintWarningMessage (LIT_Emsg, LIT_bun, prog, OptionArgument); return -1; } return Unit; } /* * fc function - fc [-e EditorName] [-nlr] [First [Last]] * fc -e - [Old=New] [Command] */ static int dofc (int argc, char **argv) { char *Editor = GetVariableAsString (FCEditVariable, FALSE); bool n_flag = TRUE; bool l_flag = FALSE; bool r_flag = FALSE; int EventNumber[2]; char *Temp; char *Change = (char *)NULL; char *Change1; char *NewBuffer; char *NewArg[3]; int i; FILE *fp; /* Check status */ if (!(InteractiveFlag && IS_TTY (0))) return PrintWarningMessage ("fc: only available in interactive mode"); if ((NewBuffer = AllocateMemoryCell (LINE_MAX + 3)) == (char *)NULL) return doOutofMemory ("fc"); /* Process options */ ResetGetOptions (); /* Reset GetOptions */ while ((i = GetOptions (argc, argv, "e:nlr", 0)) != EOF) { switch (i) { case 'e': /* editor name */ Editor = OptionArgument; break; case 'n': n_flag = FALSE; break; case 'l': l_flag = TRUE; break; case 'r': r_flag = TRUE; break; default: return UsageError ("fc [ -e EditorName ] [ -nlr ] [ First [Last]]\n fc -e - [ Old=New ] [ Command ]"); } } argv += OptionIndex; argc -= OptionIndex; /* Check for [old=new] */ if (argc && ((Change1 = strchr (*argv, CHAR_ASSIGN)) != (char *)NULL)) { Change = *(argv++); *(Change1++) = 0; --argc; } if (!l_flag) DeleteLastHistory (); /* Get the first and last event number */ for (i = 0; i < 2; i++) { EventNumber[i] = 0; if (argc) { EventNumber[i] = (int)strtol (*argv, &Temp, 10); if (*Temp) EventNumber[i] = SearchHistory (*argv); else if (EventNumber[i] < 0) EventNumber[i] += GetLastHistoryEvent () - 1; if (EventNumber[i] <= 0) return PrintWarningMessage ("fc: event <%s> not found", *argv); argv++; --argc; } } /* Set up first and last values */ i = GetLastHistoryEvent () - 1; if (!EventNumber[0]) { if ((EventNumber[0] = (l_flag) ? i - 16 : i) <= 0) EventNumber[0] = 1; } if (!EventNumber[1]) EventNumber[1] = (l_flag) ? i : EventNumber[0]; /* If l - print */ if (l_flag) fp = stdout; else if (Editor == null) return PrintWarningMessage ("fc: editor not defined"); else if ((fp = FOpenFile ((Temp = GenerateTemporaryFileName ()), sOpenWriteBinaryMode)) == (FILE *)NULL) return PrintWarningMessage ("fc: cannot create %s", Temp); /* Process history */ if (!l_flag) n_flag = FALSE; PrintHistory (r_flag, n_flag, EventNumber[0], EventNumber[1], fp); if (l_flag) return 0; /* Check that we found some history */ if (!ftell (fp)) l_flag = TRUE; if (fp != stdout) CloseFile (fp); if (l_flag) { unlink (Temp); return PrintWarningMessage ("fc: no matches"); } /* Invoke the editor */ if (strcmp (Editor, ShellOptionsVariable)) { NewArg[0] = Editor; NewArg[1] = Temp; NewArg[2] = (char *)NULL; if (ExecuteACommand (NewArg, 0) == -1) { unlink (Temp); return 1; } } /* Now execute it */ if ((i = S_open (TRUE, Temp, O_RMASK)) < 0) return PrintWarningMessage ("fc: cannot re-open edit file (%s)", Temp); argc = read (i, NewBuffer, LINE_MAX + 1); S_close (i, TRUE); if (argc <= 0) return (argc == 0) ? 0 : 1; else if (argc >= LINE_MAX - 1) return PrintWarningMessage (FCTooLong); /* Strip off trailing EOFs and EOLs */ CleanUpBuffer (argc, NewBuffer, 0x1a); /* Check for substitution */ if (Change == (char *)NULL) strcpy (ConsoleLineBuffer, NewBuffer); else { if ((Temp = strstr (NewBuffer, Change)) == (char *)NULL) return PrintWarningMessage ("fc: string not found"); if ((i = strlen (NewBuffer) - strlen (Change) + strlen (Change1)) >= LINE_MAX - 2) return PrintWarningMessage (FCTooLong); /* Do the substitution */ i = Temp - NewBuffer; strncpy (ConsoleLineBuffer, NewBuffer, i); strcpy (ConsoleLineBuffer + i, Change1); strcat (ConsoleLineBuffer, NewBuffer + strlen (Change) + i); } ReleaseMemoryCell ((void *)NewBuffer); /* Tell the user what we've done */ puts (ConsoleLineBuffer); /* Flag the console driver not to read from the console, but use the * current contents of the ConsoleLineBuffer */ UseConsoleBuffer = TRUE; return 0; } /* * Convert Job ID to process id */ #if (OS_TYPE != OS_DOS) static bool F_LOCAL ConvertJobToPid (char *String, PID *pid) { long value; JobList *jp; /* If numeric value, look up the job number */ if (ConvertNumericValue (String, &value, 0)) jp = LookUpJob ((int)value); else jp = SearchForJob (String); if (jp == (JobList *)NULL) return FALSE; PreviousJob = CurrentJob; CurrentJob = jp->pid; *pid = jp->pid; return TRUE; } #endif /* * Missing OS2 2.x API */ #if (OS_TYPE == OS_OS2) && (OS_SIZE == OS_32) APIRET DosQFileMode (PSZ pszFName, PULONG pusAttr) { APIRET rc; FILESTATUS3 status; if ((rc = DosQueryPathInfo (pszFName, FIL_STANDARD, &status, sizeof (FILESTATUS3))) == 0) *pusAttr = status.attrFile; return rc; } #endif #if (OS_TYPE == OS_NT) int DosQFileMode (const char *pszFName, DWORD *pusAttr) { *pusAttr = GetFileAttributes (pszFName); return (*pusAttr == 0xffffffff) ? -1 : 0; } #endif /* * A sort of version of times */ #if (OS_TYPE == OS_OS2) && (OS_SIZE == OS_32) && !defined (__WATCOMC__) static int dotimes (int argc, char **argv) { (void) argc; (void) argv; return PrintTimes (); } #endif #if (OS_TYPE == OS_NT) || (OS_TYPE == OS_UNIX) static int dotimes (int argc, char **argv) { (void) argc; (void) argv; return PrintTimes (); } #endif /* * Convert logical path to physical path, skipping out SUBST drives */ #if (OS_TYPE == OS_OS2) static char * F_LOCAL GetPhysicalPath (char *inpath, bool AllowNetwork) { # ifndef __EMX__ char *op; char *res; if ((op = AllocateMemoryCell (FFNAME_MAX)) == (char *)NULL) return inpath; if ((res = _fullpath (op, inpath, PATH_MAX + 6)) != (char *)NULL) strcpy (inpath, op); PATH_TO_LOWER_CASE (inpath); ReleaseMemoryCell (op); # endif (void) AllowNetwork; return PATH_TO_UNIX (inpath); } #endif /* NT Version */ #if (OS_TYPE == OS_NT) static char * F_LOCAL GetPhysicalPath (char *inpath, bool AllowNetwork) { char *op; char *res; (void) AllowNetwork; if ((op = AllocateMemoryCell (FFNAME_MAX)) == (char *)NULL) return inpath; if (!GetFullPathName (inpath, FFNAME_MAX, op, &res)) strcpy (inpath, op); PATH_TO_LOWER_CASE (inpath); ReleaseMemoryCell (op); return PATH_TO_UNIX (inpath); } #endif /* DOS version */ #if (OS_TYPE == OS_DOS) static char * F_LOCAL GetPhysicalPath (char *inpath, bool AllowNetwork) { # ifndef __EMX__ char *op; union REGS r; struct SREGS sr; if ((op = AllocateMemoryCell (FFNAME_MAX)) == (char *)NULL) return inpath; PATH_TO_DOS (inpath); r.h.ah = 0x60; r.x.REG_SI = FP_OFF (inpath); sr.ds = FP_SEG (inpath); r.x.REG_DI = FP_OFF (op); sr.es = FP_SEG (op); DosExtendedInterrupt (&r, &r, &sr); /* If we are succesfully and this is not a networked drive, replace the * original path */ if ((!(r.x.REG_CFLAGS & 1)) && (AllowNetwork || (strncmp (op, "\\\\", 2) != 0))) { strlwr (strcpy (inpath, op)); ReleaseMemoryCell (op); } # endif (void) AllowNetwork; return PATH_TO_UNIX (inpath); } #endif /* UNIX Version */ #if (OS_TYPE == OS_UNIX) static char * F_LOCAL GetPhysicalPath (char *inpath, bool AllowNetwork) { # ifdef S_IFLNK char *op; struct stat s; char *res; int len; if (((op = AllocateMemoryCell (FFNAME_MAX)) == (char *)NULL) || (lstat (inpath, &s) != 0) || (s.st_mode & S_IFMT) != S_IFLNK) return inpath; if ((len = readlink (inpath, op, FFNAME_MAX)) == -1) return inpath; op [len] = 0; strcpy (inpath, op); ReleaseMemoryCell (op); # endif return inpath; } #endif /* * Change the verify status. No NT or UNIX functionality. */ static void F_LOCAL SetVerifyStatus (bool On) { #if (OS_TYPE == OS_OS2) DosSetVerify (On); #elif (OS_TYPE == OS_DOS) union REGS r; r.x.REG_AX = On ? 0x2e01 : 0x2e00; r.x.REG_DX = 0; DosInterrupt (&r, &r); #else (void) On; #endif } /* * Change the break status */ #if (OS_TYPE == OS_DOS) static void F_LOCAL SetBreakStatus (bool On) { union REGS r; r.x.REG_AX = 0x3301; r.h.dl = (unsigned char)(On ? 1 : 0); DosInterrupt (&r, &r); } #endif /* * Bind an EMACS keystroke to a editing command */ #if defined (FLAGS_EMACS) || defined (FLAGS_GMACS) static int dobind (int argc, char **argv) { bool macro = FALSE; char *cp; int c; ResetGetOptions (); /* Reset GetOptions */ while ((c = GetOptions (argc, argv, "m", 0)) != EOF) { if (c == 'm') macro = TRUE; else return UsageError ("bind [ -m ] [ keystroke=edit command ] ..."); } argv += OptionIndex; /* List All ? */ if (*argv == (char *)NULL) /* list all */ return BindKeyStroke ((char *)NULL, (char *)NULL, FALSE); /* Otherwise, bind */ c = 0; while (*argv != (char *)NULL) { if ((cp = strchr (*argv, '=')) != (char *)NULL) *(cp++) = 0; c |= BindKeyStroke (*(argv++), cp, macro); } return c; } #endif static int doshellinfo (int argc, char **argv) { static char *OSName [] = { LIT_dos, "Windows", "OS/2", "Win NT", "UNIX"}; (void) argc; (void) argv; printf ("Global Flags = 0x%.4x\n", ShellGlobalFlags); printf ("Max # of File descriptors = %d\n", MaxNumberofFDs); printf ("Disabled Variables = 0x%.4x\n", DisabledVariables); printf ("Execute stack depth = %d\n", Execute_stack_depth); printf ("Memory stack depth = %d\n", MemoryAreaLevel); printf ("IO stack depth = %d\n", NSave_IO_E); printf ("Subshell stack depth = %d\n", NSubShells); printf ("Underlying OS = <%s>\n", OSName[BaseOS]); return 0; } /* * Flag OS/2 process for _EMX_ */ #if defined (__EMX__) && (OS_TYPE == OS_OS2) USHORT Dos32FlagProcess (PID pid, USHORT fScope, USHORT usFlagNum, USHORT usFlagArg) { return ((USHORT) (_THUNK_PROLOG (2 + 2 + 2 + 2); _THUNK_SHORT (pid); _THUNK_SHORT (fScope); _THUNK_SHORT (usFlagNum); _THUNK_SHORT (usFlagArg); _THUNK_CALL (Dos16FlagProcess))); } #endif /* * Check file access */ static int F_LOCAL CheckFAccess (const char *Name, int mode) { int fdn = CheckForFD (CheckDOSFileName (Name, NULL)); #if (OS_TYPE == OS_UNIX) struct stat s; int tm = 0; #else OSCALL_PARAM usAttr; #endif if (fdn < 0) return S_access (Name, mode); #if (OS_TYPE == OS_UNIX) if (fstat (fdn, &s) < 0) return 0; if (mode == 0) return 1; /* Find which bits to check */ if (mode & W_OK) tm |= S_IWUSR; if (mode & R_OK) tm |= S_IRUSR; if (mode & X_OK) tm |= S_IXUSR; /* Are we the owner, or group owner? */ if (getuid () != st.st_uid) { tm >>= 3; if (getgid () != st.st_gid) tm >>= 3; } return s.st_mode & tm; #else /* Non-Unix is almost as much pain */ if (OS_GetFHAttributes (fdn, &usAttr) != 0) return 0; if ((mode & W_OK) && (usAttr & OS_FILE_READONLY)) return 0; if (mode & X_OK) return 0; return 1; #endif } /* * Check file type */ static int F_LOCAL CheckFType (const char *Name, int mode) { struct stat s; int fdn = CheckForFD (CheckDOSFileName (Name, NULL)); #if (OS_TYPE != OS_UNIX) int ftype; #endif /* * File Name */ if (fdn < 0) return (S_stat (Name, &s) && ((s.st_mode & S_IFMT) == mode)) ? 1 : 0; /* * File descriptor */ #if (OS_TYPE == OS_UNIX) return ((fstat (fdn, &s) >= 0) && ((s.st_mode & S_IFMT) == mode)) ? 1 : 0; #else /* Usual OS/2, MSDOS, WINNT pain */ ftype = GetDescriptorType (fdn); if ((mode == S_IFREG) && (ftype == DESCRIPTOR_FILE)) return 1; if ((mode == S_IFCHR) && ((ftype == DESCRIPTOR_DEVICE) || (ftype == DESCRIPTOR_CONSOLE))) return 1; return 0; #endif } /* * Check file mode */ static int F_LOCAL CheckFMode (const char *Name, int mode) { int fdn = CheckForFD (CheckDOSFileName (Name, NULL)); #if (OS_TYPE == OS_UNIX) struct stat s; #else OSCALL_PARAM usAttr; #endif #if (OS_TYPE == OS_UNIX) if (((fdn < 0) && !S_stat (Name, &s)) || ((fdn >= 0) && (fstat (fdn, &s) < 0))) return 0; return (s.st_mode & mode); #else if (fdn < 0) return (OS_GetFileAttributes (Name, &usAttr) == 0) && (usAttr & mode); return (OS_GetFHAttributes (fdn, &usAttr) == 0) && (usAttr & mode); #endif } /* * Check file size */ static int F_LOCAL CheckFSize (const char *Name) { struct stat s; int fdn = CheckForFD (CheckDOSFileName (Name, NULL)); if (((fdn < 0) && !S_stat (Name, &s)) || #if (OS_TYPE == OS_UNIX) ((fdn >= 0) && (fstat (fdn, &s) < 0))) #else ((fdn >= 0) && ((s.st_size = filelength (fdn)) < 0))) #endif return 0; return (s.st_size > 0L); } /* * Does this refer to a file descriptor - check for /dev/fd/n */ static int F_LOCAL CheckForFD (const char *name) { if (strncmp (name, LIT_devfd, sizeof (LIT_devfd) - 1) != 0) return -1; return atoi (name + sizeof (LIT_devfd) - 1); } /* * Get File Handler Attributes */ /* * Under DOS, there is no way to get the file attributes */ #if (OS_TYPE == OS_DOS) static OSCALL_RET F_LOCAL OS_GetFHAttributes (int fd, OSCALL_PARAM *usAttr) { if (lseek (fd, 0L, SEEK_CUR) == -1L) return -1; *usAttr = 0; return 0; } #endif /* * OS/2 Version */ #if (OS_TYPE == OS_OS2) static OSCALL_RET F_LOCAL OS_GetFHAttributes (int fd, OSCALL_PARAM *usAttr) { OSCALL_RET rc; # if (OS_SIZE == OS_32) FILESTATUS3 info; # else FILESTATUS info; # endif rc = DosQFileInfo (fd, FIL_STANDARD, &info, sizeof (info)); if (rc != 0) return rc; *usAttr = info.attrFile; return 0; } #endif /* * NT Version */ #if (OS_TYPE == OS_NT) static OSCALL_RET F_LOCAL OS_GetFHAttributes (int fd, OSCALL_PARAM *usAttr) { extern long _CRTAPI1 _get_osfhandle (int); BY_HANDLE_FILE_INFORMATION info; HANDLE osfp = (HANDLE)_get_osfhandle (fd); switch (GetFileType (osfp)) if (!GetFileInformationByHandle(osfp, &info)) return 1; *usAttr = info.dwFileAttributes; return 0; } #endif