Subversion Repositories DevTools

Rev

Blame | Last modification | View Log | RSS feed

/*
 * 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 <sys/types.h>
#include <sys/stat.h>
#if defined (__EMX__)
#  include <sys/wait.h>
#endif
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <setjmp.h>
#include <ctype.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <limits.h>
#include <dirent.h>
#include <stdarg.h>
#include <time.h>
#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 <names ...>
 */

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 <arguments>
 */

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 [ <arguments> ] or [[ <arguments> ]] 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 <search string> <new string>      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: . <filename>
 */

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 <expression>
 */

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