Rev 227 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
/** MS-DOS SHELL - Unix File I/O Emulation** MS-DOS SHELL - Copyright (c) 1990,4 Data Logic Limited** This code is subject to the following copyright restrictions:** 1. Redistribution and use in source and binary forms are permitted* provided that the above copyright notice is duplicated in the* source form and the copyright notice in file sh6.c is displayed* on entry to the program.** 2. The sources (or parts thereof) or objects generated from the sources* (or parts of sources) cannot be sold under any circumstances.** The directory functions opendir, readdir, closedir are based on the public* domain implementation for MS-DOS written by Michael Rendell* ({uunet,utai}michael@garfield), August 1897** startup () is based on EMX/GCC startup.c in emx/lib/src/startup. Copyright* (c) 1990-1993 by Eberhard Mattes.** $Header: /cvsroot/device/DEVL/UTILS/SH/Sh8.c,v 1.2 2004/05/10 09:30:07 ayoung Exp $** $Log: Sh8.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:35 adamy* imported (reference only)** Revision 1.2 2001/08/03 08:30:55 ayoung* removed mutliple dot conversion under WIN32** Revision 1.1 2001/07/20 05:55:45 ayoung* WIN32 support** Revision 1.2 2000/09/27 08:33:36 adamy* Added EXTENDED_LINE cache and use __systeml_mode during DOS builds.** Revision 1.1.1.1 1999/12/02 01:11:12 gordonh* UTIL** Revision 2.16 1994/08/25 20:49:11 istewart* MS Shell 2.3 Release** Revision 2.15 1994/02/23 09:23:38 istewart* Beta 233 updates** Revision 2.14 1994/02/01 10:25:20 istewart* Release 2.3 Beta 2, including first NT port** Revision 2.13 1994/01/20 14:51:43 istewart* Release 2.3 Beta 1** Revision 2.12 1994/01/11 17:55:25 istewart* Release 2.3 Beta 0 patches** Revision 2.11 1993/11/09 10:39:49 istewart* Beta 226 checking** Revision 2.10 1993/08/25 16:03:57 istewart* Beta 225 - see Notes file** Revision 2.9 1993/07/02 10:21:35 istewart* 224 Beta fixes** Revision 2.8 1993/06/14 11:00:12 istewart* More changes for 223 beta** Revision 2.7 1993/06/02 09:52:35 istewart* Beta 223 Updates - see Notes file** Revision 2.6 1993/02/16 16:03:15 istewart* Beta 2.22 Release** Revision 2.5 1993/01/26 18:35:09 istewart* Release 2.2 beta 0** Revision 2.4 1992/12/14 10:54:56 istewart* BETA 215 Fixes and 2.1 Release** Revision 2.3 1992/11/06 10:03:44 istewart* 214 Beta test updates** Revision 2.2 1992/07/16 14:33:34 istewart* Beta 212 Baseline** Revision 2.1 1992/07/10 10:52:48 istewart* 211 Beta updates** Revision 2.0 1992/04/13 17:39:09 Ian_Stewartson* MS-Shell 2.0 Baseline release**/#if defined (__EMX__)# include <sys/emx.h>#endif#include <sys/types.h>#include <sys/stat.h>#include <stdio.h>#include <signal.h>#include <errno.h>#include <setjmp.h>#include <stdlib.h>#include <fcntl.h>#include <string.h>#include <unistd.h>#include <limits.h>#include <dirent.h>#include <ctype.h>#include <stdarg.h>#if defined (__EMX__)# include <sys/ioctl.h>#endif/** There appears to be no alloca in TurboC*/#if defined (__TURBOC__)# include <alloc.h># include <dir.h># define alloca(x) malloc (x)# define alloca_free(x) free (x)#elif defined(_MSC_VER)# include <malloc.h># define alloca(x) calloc (x,1)# define alloca_free(x) free (x)#else# include <malloc.h># define alloca_free(x)#endif#include "sh.h"#define MAX_LINE 160 /* Max line length */#define F_START 4#define NSTART 16 /* default number of words to *//* allow for initially */#define IS_OCTAL(a) (((a) >= '0') && ((a) <= '7'))static char *nopipe = "can't create pipe";/* List of open files to allow us to simulate the Unix open and unlink* operation for temporary files*/typedef struct flist {struct flist *fl_next; /* Next link */char *fl_name; /* File name */bool fl_close; /* Delete on close flag */int fl_size; /* Size of fl_fd array */int fl_count; /* Number of entries in array */int fl_mode; /* File open mode */int *fl_fd; /* File ID array (for dup) */} s_flist;static s_flist *list_start = (s_flist *)NULL;static s_flist * F_LOCAL find_entry (int);static void F_LOCAL SaveFileDescriptor (FILE *);static char * F_LOCAL _Ex_SkipWhiteSpace (char *);#if (OS_TYPE != OS_UNIX)static void F_LOCAL _Ex_ExpandIndirectFile (char *, Word_B **);static char * F_LOCAL _Ex_GetSpace (int, char *);static void F_LOCAL _Ex_ExpandField (char *, Word_B **);static char * F_LOCAL _Ex_ConvertEnvVariables (char *);static void F_LOCAL _Ex_SaveArgvValue (char *, bool, Word_B **);static void F_LOCAL _Ex_CommandLine (char *, Word_B **);static unsigned long F_LOCAL QueryApplicationType1 (int);#endif#if (OS_TYPE == OS_OS2)static void F_LOCAL _Ex_ProcessEMXArguments (char *, Word_B **);#endif#if (OS_TYPE == OS_DOS) && (__WATCOMC__)extern char *_LpCmdLine;extern char *_LpPgmName;static void F_LOCAL OutputBIOSString (char *);#endif#ifdef __WATCOMC__char *_getdcwd (int, char *, int);#endif#ifdef __EMX__char *_getdcwd (int, char *, int);#endif/** Command Line pointers*/#if defined (__TURBOC__) /* Borland C */# define ARG_ARRAY _argv# define ARG_COUNT _argc# define ENTRY_POINT _setargv#elif defined (__WATCOMC__) /* WatCom C */# define ARG_ARRAY ___Argv# define ARG_COUNT ___Argc# define ENTRY_POINT __Init_Argv#elif defined (__IBMC__) /* IBM C Set/2 */# define ARG_ARRAY _argv# define ARG_COUNT _argc# define ENTRY_POINT _setuparg#elif defined (__EMX__) /* Gcc/EMX */# define ARG_ARRAY _argv# define ARG_COUNT _argc# define ENTRY_POINT GetArgcV#elif defined (_MSC_VER)/* Microsoft C */# define ARG_ARRAY __argv# define ARG_COUNT __argc# define ENTRY_POINT __cdecl _setargv#endif#if (OS_TYPE != OS_UNIX)# if defined (__EMX__) /* Gcc/EMX */extern void __startup (int argc, char **argv);extern void ENTRY_POINT (void);static char **ARG_ARRAY; /* Current argument address */static int ARG_COUNT; /* Current argument count */# elseextern void ENTRY_POINT (void);extern char **ARG_ARRAY; /* Current argument address */extern int ARG_COUNT; /* Current argument count */# endif/** Some EMX startup statics*/# if defined (__EMX__)/* The version of this libc.*/static const char _libc_version[] = " libc for emx 0.8h";/* Display this warning if emx.dll or emx.exe is out of date. */static const char _version_warning[] ="WARNING: emx 0.8h or later required\r\n";/* The buffer for stdin. */static char ibuf[BUFSIZ];# endif/** General OS independent start of the command line and program name.*/char *_ACmdLine;char *_APgmName; /* Program name */# if defined (OS2)extern ushort far _aenvseg;extern ushort far _acmdln;# endif/** Directory functions internals structure.*/typedef struct _dircontents DIRCONT;static void FreeDirectoryListing (DIRCONT *);#endif/** Open a file and add it to the Open file list. Errors are the same as* for a normal open.*/int S_open (bool d_flag, const char *pName, int mode){int pmask;s_flist *fp = (s_flist *)NULL;int *f_list = (int *)NULL;char *f_name = (char *)NULL;void (_SIGDECL *save_signal)(int);#ifdef __IBMC__HFILE shfFileHandle;ULONG ActionTaken;ULONG ulFileAttribute;ULONG ulOpenFlag;ULONG ulOpenMode;APIRET rc; /* Return code */#endifchar name[ MAX_PATH ];/* Check this is a valid file name */CheckDOSFileName (pName, name);/* Grap some space. If it fails, free space and return an error */if (((fp = (s_flist *) GetAllocatedSpace (sizeof (s_flist)))== (s_flist *)NULL) ||((f_list = (int *) GetAllocatedSpace (sizeof (int) * F_START))== (int *)NULL) ||((f_name = StringSave (name)) == null)){if (f_list == (int *)NULL)ReleaseMemoryCell ((void *)f_list);if (fp == (s_flist *)NULL)ReleaseMemoryCell ((void *)fp);return -1;}/* Disable signals */save_signal = signal (SIGINT, SIG_IGN);/* Set up the structure. Change two Unix device names to the DOS* equivalents and disable create*/#if (OS_TYPE != OS_UNIX)if (strnicmp (name, DeviceNameHeader, LEN_DEVICE_NAME_HEADER) == 0){if (stricmp (&name[5], "tty") == 0)strcpy (&name[5], "con");else if (stricmp (&name[5], "null") == 0)strcpy (&name[5], "nul");mode &= ~(O_CREAT | O_TRUNC);}#endiffp->fl_name = strcpy (f_name, name);fp->fl_close = d_flag;fp->fl_size = F_START;fp->fl_count = 1;fp->fl_fd = f_list;fp->fl_mode = mode;/** The IBM C Set/2 OS/2 2.x library function, open, does not appear work* correctly. So I've replaced it with a emulator here*/#ifdef __IBMC__/* Set up open actions */if (mode & O_CREAT)ulOpenFlag = OPEN_ACTION_CREATE_IF_NEW |((mode & O_EXCL)? OPEN_ACTION_FAIL_IF_EXISTS: ((mode & O_TRUNC)? OPEN_ACTION_REPLACE_IF_EXISTS: OPEN_ACTION_OPEN_IF_EXISTS));elseulOpenFlag = OPEN_ACTION_FAIL_IF_NEW |((mode & O_TRUNC)? OPEN_ACTION_REPLACE_IF_EXISTS: OPEN_ACTION_OPEN_IF_EXISTS);/* Set up open mode */ulOpenMode = OPEN_FLAGS_SEQUENTIAL | OPEN_SHARE_DENYNONE;if (mode & O_NOINHERIT)ulOpenMode |= OPEN_FLAGS_NOINHERIT;if (mode & O_WRONLY)ulOpenMode |= OPEN_ACCESS_WRITEONLY;else if (mode & O_RDONLY)ulOpenMode |= OPEN_ACCESS_READONLY;elseulOpenMode |= OPEN_ACCESS_READWRITE;DISABLE_HARD_ERRORS;rc = DosOpen (name, &shfFileHandle, &ActionTaken, (ULONG)0, FILE_NORMAL,ulOpenFlag, ulOpenMode, (PEAOP2)NULL);ENABLE_HARD_ERRORS;if (!rc){if (mode & O_TEXT)setmode (shfFileHandle, O_TEXT);else if (mode & O_BINARY)setmode (shfFileHandle, O_BINARY);fp->fl_fd[0] = shfFileHandle;}elsefp->fl_fd[0] = -1;#elseDISABLE_HARD_ERRORS;fp->fl_fd[0] = open (name, mode, S_IREAD | S_IWRITE);ENABLE_HARD_ERRORS;#endif/* Open the file */if (fp->fl_fd[0] < 0){ReleaseMemoryCell ((void *)f_name);ReleaseMemoryCell ((void *)f_list);ReleaseMemoryCell ((void *)fp);pmask = -1;}/* Make sure everything is in area 0 */else{SetMemoryAreaNumber ((void *)fp, 0);SetMemoryAreaNumber ((void *)f_list, 0);/* List into the list */fp->fl_next = list_start;list_start = fp;/* Return the file descriptor */pmask = fp->fl_fd[0];/* Mark in use */ChangeFileDescriptorStatus (pmask, TRUE);}/* Restore signals */signal (SIGINT, save_signal);return pmask;}/** Scan the File list for the appropriate entry for the specified ID*/static s_flist * F_LOCAL find_entry (int fid){s_flist *fp = list_start;int i;while (fp != (s_flist *)NULL){for (i = 0; i < fp->fl_count; i++){if (fp->fl_fd[i] == fid)return fp;}fp = fp->fl_next;}return (s_flist *)NULL;}/* Close the file** We need a version of close that does everything but close for dup2 as* new file id is closed. If c_flag is TRUE, close the file as well.*/int S_close (int fid, bool c_flag){s_flist *fp = find_entry (fid);s_flist *last = (s_flist *)NULL;s_flist *fp1 = list_start;int i = 0;bool release = TRUE;bool delete = FALSE;char *fname = 0;void (_SIGDECL *save_signal)(int);/* Disable signals */save_signal = signal (SIGINT, SIG_IGN);/* Find the entry for this ID */if (fp != (s_flist *)NULL){for (i = 0; i < fp->fl_count; i++){if (fp->fl_fd[i] == fid)fp->fl_fd[i] = -1;if (fp->fl_fd[i] != -1)release = FALSE;}/* Are all the Fids closed ? */if (release){fname = fp->fl_name;delete = fp->fl_close;ReleaseMemoryCell ((void *)fp->fl_fd);/* Scan the list and remove the entry */while (fp1 != (s_flist *)NULL){if (fp1 != fp){last = fp1;fp1 = fp1->fl_next;continue;}if (last == (s_flist *)NULL)list_start = fp->fl_next;elselast->fl_next = fp->fl_next;break;}/* OK - delete the area */ReleaseMemoryCell ((void *)fp);}}/* Flush these two in case they were re-directed */FlushStreams ();/* Close the file anyway */if (c_flag){i = close (fid);ChangeFileDescriptorStatus (fid, FALSE);}/* Delete the file ? */if (delete){unlink (fname);ReleaseMemoryCell ((void *)fname);}/* Restore signals */signal (SIGINT, save_signal);return i;}/** Version of fclose*/void S_fclose (FILE *FP, bool d_flag){CloseFile (FP);S_close (fileno (FP), d_flag);}/** Duplicate file handler. Add the new handler to the ID array for this* file.*/int S_dup (int old_fid){int new_fid;if ((new_fid = dup (old_fid)) >= 0)S_Remap (old_fid, new_fid);return new_fid;}/** Add the ID to the ID array for this file*/int S_Remap (int old_fid, int new_fid){s_flist *fp = find_entry (old_fid);int *flist;int i;ChangeFileDescriptorStatus (new_fid, TRUE);if (fp == (s_flist *)NULL)return new_fid;/* Is there an empty slot ? */for (i = 0; i < fp->fl_count; i++){if (fp->fl_fd[i] == -1)return (fp->fl_fd[i] = new_fid);}/* Is there any room at the end ? No - grap somemore space and effect a* re-alloc. What to do if the re-alloc fails - should really get here.* Safty check only??*/if (fp->fl_count == fp->fl_size){flist = (int *)ReAllocateSpace ((void *)fp->fl_fd,(fp->fl_size + F_START) * sizeof (int));if (flist == (int *)NULL)return new_fid;SetMemoryAreaNumber ((void *)flist, 0);fp->fl_fd = flist;fp->fl_size += F_START;}return (fp->fl_fd[fp->fl_count++] = new_fid);}/** Duplicate file handler onto specific handler*/int S_dup2 (int old_fid, int new_fid){int res = -1;int i;Save_IO *sp;/* If duping onto stdin, stdout or stderr, Search the Save IO stack for an* entry matching us*/if ((new_fid >= STDIN_FILENO) && (new_fid <= STDERR_FILENO)){for (sp = SSave_IO, i = 0;(i < NSave_IO_E) && (SSave_IO[i].depth < Execute_stack_depth);i++, ++sp)continue;/* If depth is greater the Execute_stack_depth - we should panic as this* should not happen. However, for the moment, I'll ignore it*//* If there an entry for this depth ? */if (i == NSave_IO_E){/* Do we need more space? */if (NSave_IO_E == MSave_IO_E){sp = (Save_IO *)ReAllocateSpace ((MSave_IO_E != 0)? SSave_IO : (void *)NULL,(MSave_IO_E + SSAVE_IO_SIZE) *sizeof (Save_IO));/* Check for error */if (sp == (Save_IO *)NULL)return -1;/* Save original data */SetMemoryAreaNumber ((void *)sp, 1);SSave_IO = sp;MSave_IO_E += SSAVE_IO_SIZE;}/* Initialise the new entry */sp = &SSave_IO[NSave_IO_E++];sp->depth = Execute_stack_depth;sp->fp[STDIN_FILENO] = -1;sp->fp[STDOUT_FILENO] = -1;sp->fp[STDERR_FILENO] = -1;}if (sp->fp[new_fid] == -1)sp->fp[new_fid] = ReMapIOHandler (new_fid);FlushStreams ();}/* OK - Dup the descriptor */if ((old_fid != -1) && ((res = dup2 (old_fid, new_fid)) >= 0)){S_close (new_fid, FALSE);res = S_Remap (old_fid, new_fid);}return res;}/** Restore the Stdin, Stdout and Stderr to original values. If change is* FALSE, just remove entries from stack. A special case for exec.*/int RestoreStandardIO (int rv, bool change){int j, i;Save_IO *sp;/* Start at the top and remove any entries above the current execute stack* depth*/for (j = NSave_IO_E; j > 0; j--){sp = &SSave_IO[j - 1];if (sp->depth < Execute_stack_depth)break;/* Reduce number of entries */--NSave_IO_E;/* If special case (changed at this level) - continue */if (!change && (sp->depth == Execute_stack_depth))continue;/* Close and restore any files */for (i = STDIN_FILENO; i <= STDERR_FILENO; i++){if (sp->fp[i] != -1){S_close (i, TRUE);dup2 (sp->fp[i], i);S_close (sp->fp[i], TRUE);}}}return rv;}/** Create a Pipe*/int OpenAPipe (void){int i;if ((i = S_open (TRUE, GenerateTemporaryFileName (), O_PMASK)) < 0)PrintErrorMessage (nopipe);return i;}/** Close a pipe*/void CloseThePipe (int pv){if (pv != -1)S_close (pv, TRUE);}/** Check for restricted shell*/bool CheckForRestrictedShell (char *s){if (RestrictedShellFlag){PrintErrorMessage (BasicErrorMessage, s, "restricted");return TRUE;}return FALSE;}/** Check to see if a file is a shell script. If it is, return the file* handler for the file*/static char *Extensions[] = { null, SHELLExtension, KSHELLExtension};int OpenForExecution (char *path, char **params, int *nargs){int RetVal = -1;int j;int EndP = strlen (path);char *local_path;/* Work on a copy of the path */if ((local_path = AllocateMemoryCell (EndP + 5)) == (char *)NULL)return -1;strcpy (local_path, path);/* Try the file name and then with a .sh and then .ksh appended */for (j = 0; j < 3; j++){strcpy (&local_path[EndP], Extensions[j]);if ((RetVal = CheckForScriptFile (local_path, params, nargs)) >= 0)break;}ReleaseMemoryCell ((void *)local_path);return RetVal;}/** Check for shell script*/int CheckForScriptFile (char *path, char **params, int *nargs){char buf[512]; /* Input buffer */int fp; /* File handler */int nbytes; /* Number of bytes read */char *bp; /* Pointers into buffers */char *ep;if ((fp = S_open (FALSE, path, O_RMASK)) < 0)return -1;/* zero or less bytes - not a script */memset (buf, 0, 512);nbytes = read (fp, buf, 512);lseek (fp, 0L, SEEK_SET);for (ep = &buf[nbytes], bp = buf;(bp < ep) && ((unsigned char)*bp >= 0x08); ++bp)continue;/* If non-ascii file or length is less than 1 - not a script */if ((bp != ep) || (nbytes < 1)){S_close (fp, TRUE);return -1;}/* Ensure end of buffer detected */buf[511] = 0;/* Initialise the return parameters, if specified */if (params != (char **)NULL)*params = null;if (nargs != (int *)NULL)*nargs = 0;/* We don't care how many bytes were read now, so use it to count the* additional arguments*/nbytes = 0;/* Find the end of the first line */if ((bp = strchr (buf, CHAR_NEW_LINE)) != (char *)NULL)*bp = 0;bp = buf;ep = (char *)NULL;/* Check for script */if ((*(bp++) != CHAR_COMMENT) || (*(bp++) != '!'))return fp;while (*bp){/* Save the start of the arguments */if (*(bp = _Ex_SkipWhiteSpace (bp))){if (ep == (char *)NULL)ep = bp;/* Count the arguments */++nbytes;}bp = SkipToWhiteSpace (bp);}/* Set up the return parameters, if appropriate */if ((params != (char **)NULL) && (strlen (ep) != 0)){if ((*params = AllocateMemoryCell (strlen (ep) + 1)) == (char *)NULL){*params = null;S_close (fp, TRUE);return -1;}strcpy (*params, ep);}if (nargs != (int *)NULL)*nargs = nbytes;return fp;}/** Get the file descriptor type.*/#if (OS_TYPE == OS_OS2)int GetDescriptorType (int fp){OSCALL_PARAM fsType = HANDTYPE_FILE;OSCALL_PARAM usDeviceAttr;if (DosQHandType (fp, &fsType, &usDeviceAttr))return DESCRIPTOR_UNKNOWN;if (LOBYTE (fsType) == HANDTYPE_PIPE)return DESCRIPTOR_PIPE;else if (LOBYTE (fsType) != HANDTYPE_DEVICE)return DESCRIPTOR_FILE;/* The value 0x8083 seems to be the value returned by the console */else if (usDeviceAttr == 0x8083)return DESCRIPTOR_CONSOLE;elsereturn DESCRIPTOR_DEVICE;}#endif/* NT version */#if (OS_TYPE == OS_NT)int GetDescriptorType (int fp){DWORD fdwMode;HANDLE osfp = (HANDLE)_get_osfhandle (fp);switch (GetFileType (osfp)){default:return DESCRIPTOR_UNKNOWN;case FILE_TYPE_DISK:return DESCRIPTOR_FILE;case FILE_TYPE_PIPE:return DESCRIPTOR_PIPE;case FILE_TYPE_CHAR:{if (!GetConsoleMode (osfp, &fdwMode))return DESCRIPTOR_DEVICE;elsereturn DESCRIPTOR_CONSOLE;}}}#endif/* DOS Version */#if (OS_TYPE == OS_DOS)int GetDescriptorType (int fp){union REGS r;r.x.REG_AX = 0x4400;r.x.REG_BX = fp;DosInterrupt (&r, &r);if (r.x.REG_CFLAGS)return DESCRIPTOR_UNKNOWN;if ((r.x.REG_DX & 0x0080) == 0)return DESCRIPTOR_FILE;if ((r.x.REG_DX & 0x0081) != 0x0081)return DESCRIPTOR_DEVICE;/** Check to see if the console is really the console or something else.* Look at /dev/con and see if the console i/o bits are set*/r.x.REG_AX = 0x4400;r.x.REG_BX = open ("con", 0);DosInterrupt (&r, &r);close (r.x.REG_BX);if ((r.x.REG_CFLAGS) || ((r.x.REG_DX & 0x0083) != 0x0083))return DESCRIPTOR_DEVICE;return DESCRIPTOR_CONSOLE;}#endif/* UNIX Version */#if (OS_TYPE == OS_UNIX)int GetDescriptorType (int fp){struct stat s;if (fstat (fp, &s) != 0)return DESCRIPTOR_UNKNOWN;else if (S_ISREG (s.st_mode))return DESCRIPTOR_FILE;else if (S_ISCHR (s.st_mode))return DESCRIPTOR_CONSOLE;return DESCRIPTOR_UNKNOWN;}#endif/** Get the current drive number*/#if (OS_TYPE == OS_OS2)unsigned int GetCurrentDrive (void){OSCALL_PARAM usDisk;ULONG ulDrives;DosQCurDisk (&usDisk, &ulDrives); /* gets current drive */return (unsigned int)usDisk;}#endif/* NT Version */#if (OS_TYPE == OS_NT)unsigned int GetCurrentDrive (void){char szCurDir [MAX_PATH];GetCurrentDirectory (MAX_PATH, szCurDir);return szCurDir[0] - 'A' + 1;}#endif/* DOS Version */#if (OS_TYPE == OS_DOS)unsigned int GetCurrentDrive (void){# if defined (__TURBOC__)return (unsigned int)getdisk () + 1;# elif defined (__EMX__)union REGS r;r.h.ah = 0x19;DosInterrupt (&r, &r);return r.h.al + 1;# elseunsigned int CurrentDrive;_dos_getdrive (&CurrentDrive);return CurrentDrive;# endif}#endif/** Set the current drive number and return the number of drives.*/#if (OS_TYPE == OS_OS2)int SetCurrentDrive (unsigned int drive){OSCALL_RET usError;OSCALL_PARAM usDisk;FSALLOCATE FsBlock;ULONG ulDrives;int i;/* Check the drive exists */DISABLE_HARD_ERRORS;usError = DosQFSInfo (drive, FSIL_ALLOC, (PBYTE)&FsBlock,sizeof (FSALLOCATE));ENABLE_HARD_ERRORS;if (usError || DosSelectDisk ((USHORT)drive))return -1;/* Get the current disk and check that to see the number of drives */DosQCurDisk (&usDisk, &ulDrives); /* gets current drive */for (i = 25; (!(ulDrives & (1L << i))) && i >= 0; --i)continue;return i + 1;}#endif/* NT Version */#if (OS_TYPE == OS_NT)int SetCurrentDrive (unsigned int drive){char szNewDrive[3];DWORD dwLogicalDrives;int i;szNewDrive[0] = drive + 'A' - 1;szNewDrive[1] = CHAR_DRIVE;szNewDrive[2] = 0;if (!SetCurrentDirectory (szNewDrive))return -1;dwLogicalDrives = GetLogicalDrives();for (i = 25; (!(dwLogicalDrives & (1L << i))) && i >= 0; --i)continue;return i + 1;}#endif/* DOS Version */#if (OS_TYPE == OS_DOS)int SetCurrentDrive (unsigned int drive){# if defined (__TURBOC__)return setdisk (drive - 1);# elif defined (__EMX__)union REGS r;r.h.ah = 0x0e;r.h.dl = drive;DosInterrupt (&r, &r);return r.h.al;# elseunsigned int ndrives;_dos_setdrive (drive, &ndrives);return (int)ndrives;# endif}#endif/** Get and process configuration line:** <field> = <field> <field> # comment** return the number of fields found.*/int ExtractFieldsFromLine (LineFields *fd){char *cp;int len;Word_B *wb;if (fgets (fd->Line, fd->LineLength - 1, fd->FP) == (char *)NULL){CloseFile (fd->FP);return -1;}/* Remove the EOL */if ((cp = strchr (fd->Line, CHAR_NEW_LINE)) != (char *)NULL)*cp = 0;/* Remove the comments at end */if ((cp = strchr (fd->Line, CHAR_COMMENT)) != (char *)NULL)*cp = 0;/* Extract the fields */if (fd->Fields != (Word_B *)NULL)fd->Fields->w_nword = 0;fd->Fields = SplitString (fd->Line, fd->Fields);if (WordBlockSize (fd->Fields) < 2)return 1;/* Check for =. At end of first field? */wb = fd->Fields;len = strlen (wb->w_words[0]) - 1;if (wb->w_words[0][len] == CHAR_ASSIGN)wb->w_words[0][len] = 0;/* Check second field for just being equals */if (strcmp (wb->w_words[1], "=") == 0){if ((--(wb->w_nword)) > 1)memcpy (wb->w_words + 1, wb->w_words + 2,(wb->w_nword - 1) * sizeof (void *));}/* Check the second field for starting with an equals */else if (*(wb->w_words[1]) == CHAR_ASSIGN)strcpy (wb->w_words[1], wb->w_words[1] + 1);return wb->w_nword;}/** Split the string up into words*/Word_B *SplitString (char *string, Word_B *wb){while (*string){while (isspace (*string))*(string++) = 0;if (*string)wb = AddWordToBlock (string, wb);string = SkipToWhiteSpace (string);}return wb;}/** Test to see if a file is a directory*/bool IsDirectory (const char *Name){struct stat s;return C2bool (S_stat (Name, &s) && S_ISDIR (s.st_mode & S_IFMT));}/** Check that filename conforms to DOS format. Convert additional .'s to ~.*/#if (OS_TYPE != OS_UNIX)const char *CheckDOSFileName (const char *name, char *tmp){static char tname[ MAX_PATH ];char *s;char *s1;int count = 0;if (tmp == NULL)tmp = tname;(void) strncpy(tmp, name, MAX_PATH);tmp[ MAX_PATH-1 ] = '\0';#if !defined(WIN32)if (!IsHPFSFileSystem (tmp)){/* Find start of file name */if ((s = FindLastPathCharacter (tmp)) == (char *)NULL)s = tmp;else++s;/* Skip over the directory entries */if ((strcmp (s, CurrentDirLiteral) == 0) ||(strcmp (s, ParentDirLiteral) == 0))/*SKIP*/;/* Name starts with a dot? */else if (*s == CHAR_PERIOD)count = 2;/* Count the number of dots */else{s1 = s;while ((s1 = strchr (s1, CHAR_PERIOD)) != (char *)NULL){count++;s1++;}}/* Check the dot count */if (count > 1){if (!FL_TEST (FLAG_WARNING))fprintf(stderr, "sh: File <%s> has too many dots, changed to ",tmp);/* Transform the very first if necessary */if (*s == CHAR_PERIOD)*s = CHAR_TILDE;s1 = s;count = 0;/* Convert all except the first */while ((s1 = strchr (s1, CHAR_PERIOD)) != (char *)NULL){if (++count != 1)*s1 = CHAR_TILDE;s1++;}if (!FL_TEST (FLAG_WARNING))PrintWarningMessage ("<%s>", tmp);}}#endif /*WIN32*//* Check for double slashes */s = tmp;while ((s = FindPathCharacter (s)) != (char *)NULL){if (IsPathCharacter (*(++s)))strcpy (s, s + 1);}return tmp;}#endif/** Get a valid numeric value*/bool ConvertNumericValue (char *string, long *value, int base){char *ep;*value = strtol (string, &ep, base);return C2bool (!*ep);}/** Character types - replaces is???? functions*/void SetCharacterTypes (char *String, int Type){int i;/** If we're changing C_IFS (interfield separators), clear them all*/if ((Type & C_IFS)){for (i = 0; i < UCHAR_MAX+1; i++)CharTypes[i] &=~ C_IFS;CharTypes[0] |= C_IFS; /* include \0 as an C_IFS */}/** Allow leading \0 in string*/CharTypes[(unsigned char) *(String++)] |= Type;while (*String != 0)CharTypes[(unsigned char) *(String++)] |= Type;}/** Initialise the Ctypes values*/void InitialiseCharacterTypes (){int c;memset (CharTypes, 0, UCHAR_MAX);for (c = 'a'; c <= 'z'; c++)CharTypes[c] = C_ALPHA;for (c = 'A'; c <= 'Z'; c++)CharTypes[c] = C_ALPHA;CharTypes['_'] = C_ALPHA;CharTypes[';'] = C_SEMICOLON;SetCharacterTypes ("0123456789", C_DIGIT);SetCharacterTypes ("\0 \t\n|&;<>()", C_LEX1);SetCharacterTypes ("*@#!$-?", C_VAR1);SetCharacterTypes ("=-+?#%", C_SUBOP);SetCharacterTypes ("?*[", C_WILD);}/** Word Block Functions** Add a new word to a Word Block or list*/Word_B *AddWordToBlock (char *wd, Word_B *wb){/* Do we require more space ? */if ((wb == (Word_B *)NULL) || (wb->w_nword >= wb->w_bsize)){int NewCount = (wb == (Word_B *)NULL) ? NSTART : wb->w_nword * 2;if ((wb = ReAllocateSpace (wb, (NewCount * sizeof (char *)) +sizeof (Word_B))) == (Word_B *)NULL)return (Word_B *)NULL;wb->w_bsize = NewCount;}/* Add to the list */wb->w_words[wb->w_nword++] = (void *)wd;return wb;}/** Get the number of words in a block*/int WordBlockSize (Word_B *wb){return (wb == (Word_B *)NULL) ? 0 : wb->w_nword;}/** Convert a word block structure into a array of strings*/char **GetWordList (Word_B *wb){char **wd;int nb;/* If the word block is empty or does not exist, return no list */if (wb == (Word_B *)NULL)return NOWORDS;/* Get some space for the array and set it up */if (((nb = sizeof (char *) * wb->w_nword) == 0) ||((wd = (char **)GetAllocatedSpace (nb)) == (char **)NULL)){ReleaseMemoryCell ((void *)wb);return NOWORDS;}memcpy (wd, wb->w_words, nb);ReleaseMemoryCell ((void *)wb); /* perhaps should done by caller */return wd;}/** Build up the parameter variables*/Word_B *AddParameter (char *value, Word_B *wb, char *function){char **NewArray;int Count;int i;/* Add to block */if ((wb = AddWordToBlock (value, wb)) == (Word_B *)NULL){fprintf (stderr, BasicErrorMessage, function, Outofmemory1);return (Word_B *)NULL;}/* If not end, return */if (value != NOWORD)return wb;/* Get number of entries */Count = wb->w_nword - 1;/* Convert to array */if ((NewArray = GetWordList (wb)) == NOWORDS){fprintf (stderr, BasicErrorMessage, function, Outofmemory1);return (Word_B *)NULL;}/* Release old array. Note: never release entry zero */if (ParameterArray != NOWORDS){for (i = 1; i < ParameterCount; ++i)ReleaseMemoryCell ((void *)ParameterArray [i]);ReleaseMemoryCell ((void *)ParameterArray);}/* Set new array to no-release */for (i = 0; i < Count; ++i)SetMemoryAreaNumber ((void *)NewArray[i], 0);SetMemoryAreaNumber ((void *)NewArray, 0);/* Reset globals and environment */ParameterCount = Count - 1;ParameterArray = NewArray;SetVariableFromNumeric (ParameterCountVariable, (long)ParameterCount);return wb;}/** Is this a valid variable name*/char IsValidVariableName (char *s){if (!IS_VariableFC ((int)*s))return *s;while (*s && IS_VariableSC ((int)*s))++s;return *s;}/** File Open Stream Save*/FILE *FOpenFile (const char *name, const char *mode){FILE *fp;DISABLE_HARD_ERRORS;fp = fopen (name, mode);ENABLE_HARD_ERRORS;if (fp != (FILE *)NULL){SaveFileDescriptor (fp);setvbuf (fp, (char *)NULL, _IOFBF, BUFSIZ);ChangeFileDescriptorStatus (fileno (fp), TRUE);}return fp;}/** File D Open Stream Save*/FILE *ReOpenFile (int fid, const char *mode){FILE *fp = fdopen (fid, mode);if (fp != (FILE *)NULL){SaveFileDescriptor (fp);setvbuf (fp, (char *)NULL, _IOFBF, BUFSIZ);}return fp;}/** File Close Stream Save*/int CloseFile (FILE *fp){Word_B *wb = e.OpenStreams;int NEntries = WordBlockSize (wb);int i;for (i = 0; i < NEntries; i++){if (wb->w_words[i] == (char *)fp)wb->w_words[i] = (char *)NULL;}ChangeFileDescriptorStatus (fileno (fp), FALSE);return fclose (fp);}/** Flush output streams*/void FlushStreams (void){fflush (stdout);fflush (stderr);}/** Save Descriptor in Environment*/static void F_LOCAL SaveFileDescriptor (FILE *fp){Word_B *wb = e.OpenStreams;int NEntries = WordBlockSize (wb);int i;for (i = 0; i < NEntries; i++){if (wb->w_words[i] == (char *)NULL){wb->w_words[i] = (char *)fp;return;}}e.OpenStreams = AddWordToBlock ((char *)fp, wb);SetMemoryAreaNumber ((void *)e.OpenStreams, 0);}/** Local Stat function to do some additional checks*/bool S_stat (const char *FileName, struct stat *Status){#if (OS_TYPE != OS_UNIX)char tname[ MAX_PATH ];#endifint rc;FileName = CheckDOSFileName (FileName, tname);DISABLE_HARD_ERRORS;rc = stat (FileName, Status);ENABLE_HARD_ERRORS;/** Watcom has a problem stat'ing the root directory!*/#if defined (__WATCOMC__) /* WatCom C */if (rc){char *fullpath;char *cp;if ((fullpath = AllocateMemoryCell (FFNAME_MAX)) != (char *)NULL){if (((cp = FindLastPathCharacter (strcpy (fullpath, FileName)))!= (char *)NULL) &&((strcmp (++cp, ParentDirLiteral) == 0) ||(strcmp (cp, CurrentDirLiteral) == 0)))strcat (cp, "/");/* Generate the full path */GenerateFullExecutablePath (fullpath);/* Root directory is a special case */if (strcmp (fullpath + 1, ":\\.") == 0)fullpath[3] = 0;DISABLE_HARD_ERRORS;rc = stat (fullpath, Status);ENABLE_HARD_ERRORS;ReleaseMemoryCell (fullpath);}}#endifreturn rc ? FALSE : TRUE;}/** Local access function to do some additional checks*/bool S_access (const char *FileName, int mode){#if (OS_TYPE != OS_UNIX)char tname[ MAX_PATH ];#endifint rc;FileName = CheckDOSFileName (FileName, tname);DISABLE_HARD_ERRORS;rc = access (FileName, mode);ENABLE_HARD_ERRORS;return rc ? FALSE : TRUE;}/** Local change directory function to do some additional checks*/bool S_chdir (const char *PathName){#if (OS_TYPE != OS_UNIX)char tname[ MAX_PATH ];#endifint rc;PathName = CheckDOSFileName (PathName, tname);DISABLE_HARD_ERRORS;rc = chdir (PathName);ENABLE_HARD_ERRORS;return rc ? FALSE : TRUE;}/** Local get current directory function to do some additional checks** Assumes that PathName is a string of length PATH_MAX + 6.*/bool S_getcwd (char *PathName, int drive){char *res = (char *)NULL;#if (OS_TYPE == OS_NT)unsigned int cdrive;#endif#if defined (__TURBOC__)*(strcpy (PathName, RootDirectory)) = GetDriveLetter (drive);#endifDISABLE_HARD_ERRORS;#if (OS_TYPE != OS_UNIX)if (drive){# if defined (__TURBOC__)res = getcurdir (drive, PathName + 3) ? (char *)NULL : PathName;# elif (OS_TYPE != OS_NT)res = _getdcwd (drive, PathName, PATH_MAX + 4);# elsecdrive = GetCurrentDrive ();if (SetCurrentDrive (drive) != -1){res = !GetCurrentDirectory (PATH_MAX + 4, PathName) ? (char *)NULL: PathName;SetCurrentDrive (cdrive);}# endif}else# if defined (__EMX__)res = _getdcwd (GetCurrentDrive (), PathName, PATH_MAX + 4);# elseres = getcwd (PathName, PATH_MAX + 4);# endif#elseres = getcwd (PathName, PATH_MAX + 4);#endifENABLE_HARD_ERRORS;PathName[PATH_MAX + 5] = 0;/* Convert to Unix format */PATH_TO_UNIX (PathName);PATH_TO_LOWER_CASE (PathName);/* Drive letter in lower case */#if (OS_TYPE != OS_UNIX)*PathName = (char)tolower (*PathName);#endifreturn (res == (char *)NULL) ? FALSE : TRUE;}/** Open the directory stream*** OS/2 version*/#if (OS_TYPE == OS_OS2)DIR * _CDECL opendir (name)const char *name;{DIR *dirp;char *last;DIRCONT *dp;char *nbuf;HDIR d_handle = HDIR_SYSTEM;bool HPFS;int len = strlen (name);unsigned long rc;# if (OS_SIZE == OS_32)FILEFINDBUF3 dtabuf;# define DTA_NAME dtabuf.achNameULONG d_count = 1;# elseFILEFINDBUF dtabuf;# define DTA_NAME dtabuf.achNameUSHORT d_count = 1;# endifif (!len){errno = ENOTDIR;return (DIR *)NULL;}if ((nbuf = AllocateMemoryCell (len + 5)) == (char *)NULL)return (DIR *) NULL;strcpy (nbuf, name);last = &nbuf[len - 1];/* Ok, DOS is very picky about its directory names. The following are* valid.** c:/* c:.* c:name/name1** c:name/ is not valid*/if (((*last == CHAR_DOS_PATH) || IsPathCharacter (*last)) &&(len > 1) && (!((len == 3) && IsDriveCharacter (name[1]))))*(last--) = 0;/* Check its a directory and get some space */if ((!IsDirectory (nbuf)) ||((dirp = (DIR *) AllocateMemoryCell (sizeof (DIR))) == (DIR *) NULL)){ReleaseMemoryCell (nbuf);return (DIR *)NULL;}/* Set up to find everything */if ((*last != CHAR_DOS_PATH) && !IsPathCharacter (*last))strcat (last, DirectorySeparator);strcat (last, "*.*");/* For OS/2, find the file system type */HPFS = IsHPFSFileSystem (nbuf);dirp->dd_loc = 0;dirp->dd_cp = (DIRCONT *) NULL;dirp->dd_contents = (DIRCONT *) NULL;DISABLE_HARD_ERRORS;# if (OS_SIZE == OS_32)rc = DosFindFirst (nbuf, &d_handle, OS_FILE_ATTRIBUTES, &dtabuf,sizeof (FILEFINDBUF3), &d_count, FIL_STANDARD);# elserc = DosFindFirst (nbuf, &d_handle, OS_FILE_ATTRIBUTES, &dtabuf,sizeof (FILEFINDBUF), &d_count, (ULONG)0);# endifENABLE_HARD_ERRORS;if (rc){ReleaseMemoryCell (nbuf);ReleaseMemoryCell (dirp);return (DIR *) NULL;}/* Wander through the directory! */do{if (((dp = (DIRCONT *) AllocateMemoryCell (sizeof (DIRCONT)))== (DIRCONT *)NULL) ||((dp->_d_entry = StringCopy (DTA_NAME)) == null)){if (dp->_d_entry != (char *)NULL)ReleaseMemoryCell ((char *)dp);ReleaseMemoryCell (nbuf);FreeDirectoryListing (dirp->dd_contents);DosFindClose (d_handle);return (DIR *) NULL;}if (!HPFS)strlwr (dp->_d_entry);if (dirp->dd_contents != (DIRCONT *) NULL)dirp->dd_cp = dirp->dd_cp->_d_next = dp;elsedirp->dd_contents = dirp->dd_cp = dp;dp->_d_next = (DIRCONT *) NULL;d_count = 1;} while (DosFindNext (d_handle, &dtabuf, sizeof (FILEFINDBUF),&d_count) == 0);dirp->dd_cp = dirp->dd_contents;ReleaseMemoryCell (nbuf);DosFindClose (d_handle);return dirp;}#endif/** NT version (removed)*/#if (OS_TYPE == OS_NT) && (0)DIR * _CDECL opendir (name)const char *name;{DIR *dirp;char *last;DIRCONT *dp;char *nbuf;HANDLE d_handle;bool HPFS;int len = strlen (name);WIN32_FIND_DATA dtabuf;if (!len){errno = ENOTDIR;return (DIR *)NULL;}if ((nbuf = AllocateMemoryCell (len + 5)) == (char *)NULL)return (DIR *) NULL;strcpy (nbuf, name);last = &nbuf[len - 1];/* Ok, DOS is very picky about its directory names. The following are* valid.** c:/* c:.* c:name/name1** c:name/ is not valid*/if (((*last == CHAR_DOS_PATH) || IsPathCharacter (*last)) &&(len > 1) && (!((len == 3) && IsDriveCharacter (name[1]))))*(last--) = 0;/* Check its a directory and get some space */if ((!IsDirectory (nbuf)) ||((dirp = (DIR *) AllocateMemoryCell (sizeof (DIR))) == (DIR *) NULL)){ReleaseMemoryCell (nbuf);return (DIR *)NULL;}/* Set up to find everything */if ((*last != CHAR_DOS_PATH) && !IsPathCharacter (*last))strcat (last, DirectorySeparator);strcat (last, "*.*");/* For OS/2, find the file system type */HPFS = IsHPFSFileSystem (nbuf);dirp->dd_loc = 0;dirp->dd_cp = (DIRCONT *) NULL;dirp->dd_contents = (DIRCONT *) NULL;DISABLE_HARD_ERRORS;d_handle = FindFirstFile (nbuf, &dtabuf);ENABLE_HARD_ERRORS;if (d_handle == INVALID_HANDLE_VALUE){ReleaseMemoryCell (nbuf);ReleaseMemoryCell (dirp);return (DIR *) NULL;}/* Wander through the directory! */do{if (((dp = (DIRCONT *) AllocateMemoryCell (sizeof (DIRCONT)))== (DIRCONT *)NULL) ||((dp->_d_entry = StringCopy (dtabuf.cFileName)) == null)){if (dp->_d_entry != (char *)NULL)ReleaseMemoryCell ((char *)dp);ReleaseMemoryCell (nbuf);FreeDirectoryListing (dirp->dd_contents);FindClose (d_handle);return (DIR *) NULL;}if (!HPFS)strlwr (dp->_d_entry);if (dirp->dd_contents != (DIRCONT *) NULL)dirp->dd_cp = dirp->dd_cp->_d_next = dp;elsedirp->dd_contents = dirp->dd_cp = dp;dp->_d_next = (DIRCONT *) NULL;} while (FindNextFile (d_handle, &dtabuf));dirp->dd_cp = dirp->dd_contents;ReleaseMemoryCell (nbuf);FindClose (d_handle);return dirp;}#endif/** DOS Version*/#if (OS_TYPE == OS_DOS) && !defined(__WATCOMC__)DIR * _CDECL opendir (name)const char *name;{DIR *dirp;char *last;DIRCONT *dp;char *nbuf;int len = strlen (name);unsigned long rc;# if defined (__TURBOC__)struct ffblk dtabuf;# define DTA_NAME dtabuf.ff_name# define DIR_FINDNEXT(a) findnext (a)# define DIR_FINDFIRST(F,A,B) findfirst (F, B, A)# elif defined (__EMX__)struct _find dtabuf;# define DTA_NAME dtabuf.name# define DIR_FINDNEXT(a) __findnext (a)# define DIR_FINDFIRST(F,A,B) __findfirst (F, A, B)# elsestruct find_t dtabuf;# define DTA_NAME dtabuf.name# define DIR_FINDNEXT(a) _dos_findnext (a)# define DIR_FINDFIRST(F,A,B) _dos_findfirst (F, A, B)# endifif (!len){errno = ENOTDIR;return (DIR *)NULL;}if ((nbuf = AllocateMemoryCell (len + 5)) == (char *)NULL)return (DIR *) NULL;strcpy (nbuf, name);last = &nbuf[len - 1];/* Ok, DOS is very picky about its directory names. The following are* valid.** c:/* c:.* c:name/name1** c:name/ is not valid*/if (((*last == CHAR_DOS_PATH) || IsPathCharacter (*last)) &&(len > 1) && (!((len == 3) && IsDriveCharacter (name[1]))))*(last--) = 0;/* Check its a directory and get some space */if ((!IsDirectory (nbuf)) ||((dirp = (DIR *) AllocateMemoryCell (sizeof (DIR))) == (DIR *) NULL)){ReleaseMemoryCell (nbuf);return (DIR *)NULL;}/* Set up to find everything */if ((*last != CHAR_DOS_PATH) && !IsPathCharacter (*last))strcat (last, DirectorySeparator);strcat (last, "*.*");dirp->dd_loc = 0;dirp->dd_cp = (DIRCONT *) NULL;dirp->dd_contents = (DIRCONT *) NULL;DISABLE_HARD_ERRORS;rc = DIR_FINDFIRST (nbuf, OS_FILE_ATTRIBUTES, &dtabuf);ENABLE_HARD_ERRORS;if (rc){ReleaseMemoryCell (nbuf);ReleaseMemoryCell (dirp);return (DIR *) NULL;}do{if (((dp = (DIRCONT *) AllocateMemoryCell (sizeof (DIRCONT)))== (DIRCONT *)NULL) ||((dp->_d_entry = StringCopy (DTA_NAME)) == null)){if (dp->_d_entry != (char *)NULL)ReleaseMemoryCell ((char *)dp);ReleaseMemoryCell (nbuf);FreeDirectoryListing (dirp->dd_contents);return (DIR *) NULL;}strlwr (dp->_d_entry);if (dirp->dd_contents != (DIRCONT *) NULL)dirp->dd_cp = dirp->dd_cp->_d_next = dp;elsedirp->dd_contents = dirp->dd_cp = dp;dp->_d_next = (DIRCONT *) NULL;} while (DIR_FINDNEXT (&dtabuf) == 0);dirp->dd_cp = dirp->dd_contents;ReleaseMemoryCell (nbuf);return dirp;}#endif/** Close the directory stream*/#if (OS_TYPE != OS_UNIX) && \((OS_TYPE == OS_DOS && !defined(__WATCOMC__)) || \(OS_TYPE == OS_NT && (0)) )int _CDECL closedir (dirp)DIR *dirp;{if (dirp != (DIR *)NULL){FreeDirectoryListing (dirp->dd_contents);ReleaseMemoryCell ((char *)dirp);}return 0;}/** Read the next record from the stream*/struct dirent * _CDECL readdir (dirp)DIR *dirp;{static struct dirent dp;if ((dirp == (DIR *)NULL) || (dirp->dd_cp == (DIRCONT *) NULL))return (struct dirent *) NULL;dp.d_reclen = strlen (strcpy (dp.d_name, dirp->dd_cp->_d_entry));dp.d_off = dirp->dd_loc * 32;dp.d_ino = (ino_t)++dirp->dd_loc;dirp->dd_cp = dirp->dd_cp->_d_next;return &dp;}/** Release the internal structure*/static void FreeDirectoryListing (dp)DIRCONT *dp;{DIRCONT *odp;while ((odp = dp) != (DIRCONT *)NULL){if (dp->_d_entry != (char *)NULL)ReleaseMemoryCell (dp->_d_entry);dp = dp->_d_next;ReleaseMemoryCell ((char *)odp);}}#endif/** For OS/2 and NT, we need to know if we have to convert to lower case. This* only applies to non-HPFS (FAT, NETWARE etc) file systems.*/#if (OS_TYPE == OS_OS2)/** Define the know FAT systems*/static char *FATSystems[] = {"FAT", "NETWARE", (char *)NULL};/** Check for Long filenames*/bool IsHPFSFileSystem (char *directory){BYTE bData[128];BYTE bName[3];int i;char *FName;unsigned long rc;OSCALL_PARAM cbData;unsigned int nDrive;# if (OS_SIZE == OS_32)PFSQBUFFER2 pFSQ = (PFSQBUFFER2)bData;# endif/** Mike tells me there are IFS calls to determine this, but he carn't* remember which. So we read the partition info and check for HPFS.*/if (isalpha (directory[0]) && IsDriveCharacter (directory[1]))nDrive = toupper (directory[0]) - '@';elsenDrive = GetCurrentDrive ();/* Set up the drive name */bName[0] = (char) (nDrive + '@');bName[1] = CHAR_DRIVE;bName[2] = 0;cbData = sizeof (bData);/* Read the info, if we fail - assume non-HPFS */DISABLE_HARD_ERRORS;# if (OS_SIZE == OS_32)rc = DosQFSAttach (bName, 0, FSAIL_QUERYNAME, pFSQ, &cbData);# elserc = DosQFSAttach (bName, 0, FSAIL_QUERYNAME, bData, &cbData, 0L);# endifENABLE_HARD_ERRORS;if (rc)return FALSE;# if (OS_SIZE == OS_32)FName = pFSQ->szName + pFSQ->cbName + 1;# elseFName = bData + (*((USHORT *) (bData + 2)) + 7);# endiffor (i = 0; FATSystems[i] != (char *)NULL; i++){if (stricmp (FName, FATSystems[i]) == 0)return FALSE;}return TRUE;}#endif/** Windows NT version (removed, see dirent services)*/#if (OS_TYPE == OS_NT) && (0)bool IsHPFSFileSystem (char *directory){char bName[4];DWORD flags;DWORD maxname;BOOL rc;unsigned int nDrive;/** Mike tells me there are IFS calls to determine this, but he carn't* remember which. So we read the partition info and check for HPFS.*/if (isalpha (directory[0]) && IsDriveCharacter (directory[1]))nDrive = toupper (directory[0]) - '@';elsenDrive = GetCurrentDrive ();/* Set up the drive name */bName[0] = (char) (nDrive + '@');bName[1] = CHAR_DRIVE;bName[2] = CHAR_DOS_PATH;bName[3] = 0;/* Read the volume info, if we fail - assume non-HPFS */DISABLE_HARD_ERRORS;rc = GetVolumeInformation (bName, (LPTSTR)NULL, 0, (LPDWORD)NULL,&maxname, &flags, (LPTSTR)NULL, 0);ENABLE_HARD_ERRORS;return C2bool ((rc) && (flags & FS_CASE_SENSITIVE));}#endif/** Get directory on drive x*/#if defined (__WATCOMC__)char *_getdcwd (int drive, char *PathName, int len){char TPath [PATH_MAX + 3];# if (OS_TYPE == OS_DOS)union REGS r;/* This is really a bit of a cheat. I'm not sure why it works, but is* does. The missing bit is that you really should set the DS register up* but that causes a memory violation. SO!!*/r.x.REG_AX = 0x4700;r.h.dl = drive;#ifdef __386__r.x.esi = FP_OFF (TPath);#elser.x.si = FP_OFF (TPath);#endifDosInterrupt (&r, &r);if (r.x.cflag & INTR_CF)return (char *)NULL;# elseULONG cbBuf = PATH_MAX + 3;if (DosQueryCurrentDir (drive, TPath, &cbBuf))return (char *)NULL;# endif/* Insert the drive and root directory */*(strcpy (PathName, RootDirectory)) = GetDriveLetter (drive);return strcat (PathName, TPath);}#endif#if defined (__EMX__)char *_getdcwd (int drive, char *PathName, int len){char TPath [PATH_MAX + 3];if (_getcwd1 (TPath, drive + 'A' - 1) != 0)return (char *)NULL;/* Insert the drive and root directory */*(strcpy (PathName, RootDirectory)) = GetDriveLetter (drive);return strcpy (PathName + 2, TPath);}#endif/** MODULE ABSTRACT: _setargv** UNIX like command line expansion*//** OS/2 2.x (32-bit) version*/#if (OS_TYPE == OS_OS2)# if (OS_SIZE == OS_32)void ENTRY_POINT (void){APIRET rc;PTIB ptib;PPIB ppib;char *MName;Word_B *Alist = (Word_B *)NULL;/* Get the command line and program name */if ((rc = DosGetInfoBlocks (&ptib, &ppib)))PrintErrorMessage ("DosGetInfoBlocks: Cannot find command line\n%s\n",GetOSSystemErrorMessage (rc));if ((MName = GetAllocatedSpace (FFNAME_MAX)) == (char *)NULL)PrintErrorMessage (Outofmemory1);if ((rc = DosQueryModuleName (ppib->pib_hmte, FFNAME_MAX - 1, MName)))PrintErrorMessage ("DosQueryModuleName: Cannot get program name\n%s\n",GetOSSystemErrorMessage (rc));/* Save the program name and process the command line */_APgmName = MName;_Ex_ProcessEMXArguments (ppib->pib_pchcmd, &Alist);/* Terminate */_Ex_SaveArgvValue ((char *)NULL, FALSE, &Alist);}# else /* (OS_SIZE == OS_16) *//** OS/2 1.x (16-bit) version*/void ENTRY_POINT (void){char far *argvp = (char far *)((((long)_aenvseg) << 16));ushort off = _acmdln;Word_B *Alist = (Word_B *)NULL;while (--off){if (argvp[off - 1] == 0)break;}/* Add program name */_APgmName = &argvp[off];if (argvp[_acmdln] == 0)_Ex_SaveArgvValue (_APgmName, TRUE, &Alist);else{argvp += _acmdln;_Ex_ProcessEMXArguments (argvp, &Alist);}/* Terminate */_Ex_SaveArgvValue ((char *)NULL, FALSE, &Alist);}# endif#elif (OS_TYPE == OS_DOS)/** MSDOS version*/void ENTRY_POINT (void){Word_B *Alist = (Word_B *)NULL;char *s; /* Temporary string pointer */# if defined (__WATCOMC__){extern void __setenvp(void); /* sets up environ */__setenvp(); /* found by grovelling */}_ACmdLine = _LpCmdLine;s = _LpPgmName;# elif (OS_SIZE == OS_16)/* Set up pointer to command line */unsigned int envs = *(int far *)((((long)_psp) << 16) + 0x02cL);union REGS r;unsigned int Length;/* For reasons that escape me, MSC 6.0 does sets up _osmajor and _osminor* in the command line parser!*/r.h.ah = 0x30;DosInterrupt (&r, &r);_osminor = r.h.ah;_osmajor = r.h.al;/* Check the length */_ACmdLine = (char *)((((long)_psp) << 16) + 0x080L);if ((Length = (unsigned int)*(_ACmdLine++)) > 127)Length = 127;_ACmdLine[Length] = 0;/* Command line can be null or 0x0d terminated - convert to null */if ((s = strchr (_ACmdLine, CHAR_RETURN)) != (char *)NULL)*s = 0;/* Get the program name */if ((_osmajor <= 2) || (envs == 0))s = "unknown";/* In the case of DOS 3+, we look in the environment space */else{s = (char far *)(((long)envs) << 16);while (*s){while (*(s++) != 0)continue;}s += 3;}# endif/* Add the program name */_APgmName = s;_Ex_SaveArgvValue (s, TRUE, &Alist);_Ex_CommandLine (_ACmdLine, &Alist);_Ex_SaveArgvValue ((char *)NULL, FALSE, &Alist);}#elif (OS_TYPE == OS_NT)/** NT version*/void ENTRY_POINT (void){char *MName;Word_B *Alist = (Word_B *)NULL;_ACmdLine = GetCommandLine ();/* Get the command line and program name */if ((MName = GetAllocatedSpace (MAX_PATH)) == (char *)NULL)PrintErrorMessage (Outofmemory1);if (!GetModuleFileName (0, MName, MAX_PATH))PrintErrorMessage ("GetModuleFileName: Cannot get program name\n%s\n",GetOSSystemErrorMessage (GetLastError ()));/* Save the program name and process the command line */_APgmName = MName;if (*_ACmdLine)_Ex_CommandLine (_ACmdLine, &Alist);else_Ex_SaveArgvValue (MName, TRUE, &Alist);_Ex_SaveArgvValue ((char *)NULL, FALSE, &Alist);}#endif/** A fix for EMX, so that startup gets the arguments, I hope.** Initialize the C run-time library. This function is called from crt0.s.* We know argc and argv are on the stack, and crt0.s does not remove them* from the start before calling main, so we just replace the values, I* hope.*/#if defined (__EMX__)void _startup (int argc, char **argv){int i, j;/** Print a warning message on handle 2 (stderr) if emx.dll or* emx.exe is out of date.*/if (_emx_vcmp < 0x302e3868) /* 0.8h */__write (2, _version_warning, strlen (_version_warning));/** Fix the stack*/# if defined (EMX_DOS)for (i = 0; i < argc; i++){__write (2, "arg = <", 7);__write (2, argv[i], strlen (argv[i]));__write (2, ">\n", 2);}# elseGetArgcV ();argc = ARG_COUNT;argv = ARG_ARRAY;# endif/* Initialize the file handles and the streams. */for (i = 0; i < _nfiles; ++i){_files[i] = 0;_lookahead[i] = -1;_streamv[i].flags = 0;/** Get the handle type. If this fails, the handle is not open.*/if (ioctl (i, FGETHTYPE, &j) >= 0){_files[i] |= O_TEXT;if (HT_ISDEV (j))_files[i] |= F_DEV;if (j == HT_NPIPE)_files[i] |= F_NPIPE;_streamv[i].flags |= _IOOPEN;_streamv[i].ptr = NULL;_streamv[i].buffer = NULL;_streamv[i].rcount = 0;_streamv[i].wcount = 0;_streamv[i].handle = i;_streamv[i].buf_size = 0;_streamv[i].tmpidx = 0;_streamv[i].pid = 0;_streamv[i].flush = _flushstream;switch (i){/** stdin is always buffered.*/case 0:_files[0] |= O_RDONLY;_streamv[0].flags |= _IOREAD | _IOFBF | _IOBUFUSER;_streamv[0].ptr = ibuf;_streamv[0].buffer = ibuf;_streamv[0].buf_size = BUFSIZ;break;/** stdout is buffered unless it's connected to a device.*/case 1:_files[i] |= O_WRONLY;_streamv[i].flags |= (_IOWRT | _IOBUFNONE |(HT_ISDEV (j) ? _IONBF : _IOFBF));break;/** stderr is always unbuffered.*/case 2:_files[i] |= O_WRONLY;_streamv[i].flags |= _IOWRT | _IOBUFNONE | _IONBF;break;/** All other handles can be read and written. A handle is buffered* unless it's connected to a device.*/default:_files[i] |= O_RDWR;_streamv[i].flags |= (_IORW | _IOBUFNONE |(HT_ISDEV (j) ? _IONBF : _IOFBF));break;}}}}#endif/** Expand the DOS Command line*/#if (OS_TYPE != OS_UNIX)static void F_LOCAL _Ex_CommandLine (char *argvp, Word_B **Alist){char *spos; /* End of string pointer */char *cpos; /* Start of string pointer */char *tpos;char *fn; /* Extracted file name string *//* Search for next separator */spos = argvp;while (*(cpos = _Ex_SkipWhiteSpace (spos))){/* Extract string argument */if ((*cpos == CHAR_DOUBLE_QUOTE) || (*cpos == CHAR_SINGLE_QUOTE)){spos = cpos + 1;do{if ((spos = strchr (spos, *cpos)) != NULL){spos++;if (spos[-2] != CHAR_META)break;}elsespos = &spos[strlen (cpos)];} while (*spos);fn = _Ex_GetSpace (spos - cpos - 2, cpos + 1);/* Remove escaped characters */tpos = fn;while ((tpos = strchr (tpos, *cpos)) != (char *)NULL)strcpy (tpos - 1, tpos);}/* Extract normal argument */else{spos = SkipToWhiteSpace (cpos);fn = _Ex_GetSpace (spos - cpos, cpos);}/* Process argument */if (*cpos != CHAR_SINGLE_QUOTE)fn = _Ex_ConvertEnvVariables (fn);switch (*cpos){case CHAR_INDIRECT: /* Expand file */_Ex_ExpandIndirectFile (fn, Alist);break;case CHAR_DOUBLE_QUOTE: /* Expand string */case CHAR_SINGLE_QUOTE:_Ex_SaveArgvValue (fn, FALSE, Alist);break;default: /* Expand field */_Ex_ExpandField (fn, Alist);}ReleaseMemoryCell (fn);}}#endif/** Expand an indirect file Argument*/#if (OS_TYPE != OS_UNIX)static void F_LOCAL _Ex_ExpandIndirectFile (char *file, Word_B **Alist){FILE *fp; /* File descriptor */char *EoLFound; /* Pointer */int c_maxlen = MAX_LINE;char *line; /* Line buffer */char *eolp;/* If file open fails, expand as a field */if ((fp = fopen (file + 1, sOpenReadMode)) == (FILE *)NULL)PrintErrorMessage ("Cannot open re-direct file - %s (%s)\n",file + 1, strerror (errno));/* Grab some memory for the line */line = (char *)GetAllocatedSpace (c_maxlen);/* For each line in the file, remove EOF characters and add argument */while (fgets (line, c_maxlen, fp) != (char *)NULL){EoLFound = strchr (line, CHAR_NEW_LINE);eolp = line;/* Handle continuation characters */while (TRUE){/* Check for a continuation character */if (((EoLFound = strchr (eolp, CHAR_NEW_LINE)) != (char *)NULL) &&(*(EoLFound - 1) == CHAR_META)){*(EoLFound - 1) = CHAR_NEW_LINE;*EoLFound = 0;EoLFound = (char *)NULL;}else if (EoLFound == (char *)NULL)EoLFound = strchr (line, 0x1a);if (EoLFound != (char *)NULL)break;/* Find the end of the line */c_maxlen = strlen (line);/* Get some more space */line = (char *)ReAllocateSpace (line, c_maxlen + MAX_LINE);eolp = &line[c_maxlen];if (fgets (eolp, MAX_LINE, fp) == (char *)NULL)break;}/* Terminate the line and add it to the argument list */if (EoLFound != (char *)NULL)*EoLFound = 0;_Ex_SaveArgvValue (line, FALSE, Alist);}if (ferror(fp))PrintErrorMessage ("%s (%s)", file + 1, strerror (errno));ReleaseMemoryCell (line);fclose (fp);/* Delete tempoary files */if (((line = strrchr (file + 1, CHAR_PERIOD)) != (char *)NULL) &&(stricmp (line, ".tmp") == 0))unlink (file + 1); /* Delete it */}#endif/** Get space for an argument name*/#if (OS_TYPE != OS_UNIX)static char * F_LOCAL _Ex_GetSpace (int length, char *in_s){char *out_s; /* Malloced space address *//* Copy string for specified length */out_s = strncpy ((char *)GetAllocatedSpace (length + 1), in_s, length);out_s[length] = 0;return (out_s);}#endif/** Skip over spaces*/static char * F_LOCAL _Ex_SkipWhiteSpace (char *a){while (isspace (*a))a++;return a;}/** Skip over spaces*/char *SkipToWhiteSpace (char *a){while (*a && !isspace (*a))a++;return a;}/* Find the location of meta-characters. If no meta, add the argument and* return NULL. If meta characters, return position of end of directory* name. If not multiple directories, return -1*/#if (OS_TYPE != OS_UNIX)static void F_LOCAL _Ex_ExpandField (char *file, Word_B **Alist){int i;int j;char *vecp[2];char **FileList;if (strpbrk (file, "?*[]\\") == (char *)NULL){_Ex_SaveArgvValue (file, FALSE, Alist);return;}vecp[0] = file;vecp[1] = (char *)NULL;FileList = ExpandWordList (vecp, EXPAND_GLOBBING | EXPAND_TILDE,(ExeMode *)NULL);j = CountNumberArguments (FileList);for (i = 0; i < j; )_Ex_SaveArgvValue (FileList[i++], FALSE, Alist);ReleaseAList (FileList);}#endif/** Process Environment - note that field is a malloc'ed field*/#if (OS_TYPE != OS_UNIX)static char * F_LOCAL _Ex_ConvertEnvVariables (char *field){char *sp, *cp, *np, *ep;char save;int b_flag;sp = field;/* Replace any $ strings */while ((sp = strchr (sp, CHAR_VARIABLE)) != (char *)NULL){/* If ${...}, find the terminating } */if (*(cp = ++sp) == CHAR_OPEN_BRACES){b_flag = 1;++cp;while (*cp && (*cp != CHAR_CLOSE_BRACES))cp++;}/* Else must be $..., find the terminating non-alphanumeric */else{b_flag = 0;while (isalnum (*cp))cp++;}/* Grab the environment variable */if (cp == sp)continue;/* Get its value */save = *cp;*cp = 0;ep = getenv (sp + b_flag);*cp = save;if (ep != (char *)NULL){np = _Ex_GetSpace (strlen(field) - (cp - sp) + strlen (ep) - 1,field);strcpy (&np[sp - field - 1], ep);ReleaseMemoryCell (field);strcpy ((sp = &np[strlen(np)]), cp + b_flag);field = np;}}return field;}#endif/** Handle EMX style arguments*/#if (OS_TYPE == OS_OS2)static void F_LOCAL _Ex_ProcessEMXArguments (char *argvp, Word_B **Alist){char *cp;char *s = argvp;_Ex_SaveArgvValue (argvp, TRUE, Alist);argvp += strlen (argvp) + 1;_ACmdLine = argvp;/** Add support in OS2 version for Eberhard Mattes EMX interface to commands.*/if ((*argvp) && (*(cp = argvp + strlen (argvp) + 1) == CHAR_TILDE) &&(strcmp (s, PATH_TO_UNIX (cp + 1)) == 0)){/* Skip over the program name at string 2 to the start of the first* argument at string 3*/EMXStyleParameters = TRUE;argvp += strlen (argvp) + 1;argvp += strlen (argvp) + 1;while (*argvp){cp = (*argvp == CHAR_TILDE) ? argvp + 1 : argvp;if (*cp == CHAR_INDIRECT)_Ex_ExpandIndirectFile (cp, Alist);else_Ex_SaveArgvValue (cp, FALSE, Alist);argvp += strlen (argvp) + 1;}}else_Ex_CommandLine (argvp, Alist);}#endif/** Save the next argument in the word block*/#if (OS_TYPE != OS_UNIX)static void F_LOCAL _Ex_SaveArgvValue (char *value, bool Convert,Word_B **Alist){if (Convert)PATH_TO_UNIX (value);*Alist = AddWordToBlock (value == (char *)NULL ? value : StringSave (value),*Alist);if (value == (char *)NULL){ARG_ARRAY = GetWordList (*Alist);ARG_COUNT = CountNumberArguments (ARG_ARRAY);}}#endif#if (OS_TYPE == OS_DOS) && (OS_SIZE == OS_32) && !defined (__EMX__)/** INTERRUPT 24 - ERROR HANDLER - Output message** doserror - Error code* devhdr - Device header address.* deverror -** Bits Meaning** 15 Disk error if false (0).* Other device error if true (1).* 14 Not used.* 13 "Ignore" response not allowed if false.* 12 "Retry" response not allowed if false.* 11 "Fail" response not allowed if false. (Note that DOS changes* "fail" to "abort".)* 9-10 Location (for disk error)** 0 - DOS* 1 - File Allocation Table (FAT)* 2 - Directory* 3 - Data area** 8 Read error if false; write error if true* 0 - 7 Device Number*/char *I24_Errors [] = {"Write-protect error","Unknown unit","Drive not ready","Unknown command","CRC error","Bad request structure length","Seek error","Unknown media type","Sector not found","Out of paper","Write fault","Read fault","General failure","Sharing violation","Lock violation","Invalid disk change","FCB unavailable","Sharing buffer overflow","Unknown error"};#define I24_ERROR_LAST (ARRAY_SIZE (I24_Errors) - 1)#define I24_ERROR_DEFAULT I24_ERROR_LAST/** Error locus*/char *I24_Locus [] = {"Unknown","Unknown","Block","Network","Serial","Memory",};#define I24_LOCUS_LAST (ARRAY_SIZE (I24_Locus) - 1)#define I24_LOCUS_DEFAULT 0/** Action*/char *I24_Action [] = {"None recommended","Retry, then abort or ignore","Retry with delay, then abort or ignore","Correct information supplied","Abort with cleanup","Abort without cleanup","Ignore","Retry after correcting",};#define I24_ACTION_LAST (ARRAY_SIZE (I24_Action) - 1)#define I24_ACTION_DEFAULT 0/** Class*/char *I24_Class [] = {"Unknown error","Out of resource","Temporary failure","Authorisation error","MSDOS Internal error","Hardware error","System error","Application error","Item missing","Item invalid","Item interlocked","Media problem"};#define I24_CLASS_LAST (ARRAY_SIZE (I24_Class) - 1)#define I24_CLASS_DEFAULT 0static char *I24_Space = "\n\r ";int __far HardErrorHandler (unsigned int deverror,unsigned int doserror,unsigned int *devhdr){char DeviceName[10];static char MessageBuffer[300];char *mp;int ch;struct DOSERROR Ecodes;/* If Ignore set, ignore */if (IgnoreHardErrors)_hardresume (_HARDERR_FAIL);/* Initialise device name */memset (DeviceName, 0, 10);/* Get extended error codes */dosexterr (&Ecodes);/* Output on message */if (deverror & 0x8000){memcpy (DeviceName, (((char *)devhdr) + 10), 8);if ((mp = strchr (DeviceName, CHAR_SPACE)) == (char *)NULL)mp = DeviceName + 8;strcpy (mp, ":");}elsesprintf (DeviceName, "%c:", (deverror & 0x0ff) + 'A');sprintf (MessageBuffer, "\n\r%s when %s %s %s\n\r",(doserror > I24_ERROR_LAST) ? I24_Errors[I24_ERROR_DEFAULT]: I24_Errors [doserror],(deverror & 0x0100) ? "writing" : "reading",(deverror & 0x8000) ? "device " : "disk",DeviceName);OutputBIOSString (MessageBuffer);sprintf (MessageBuffer, "[Extended Code : 0x%.4x%sClass : %s%sAction: %s%sLocus : %s device]\n\r",Ecodes.exterror, I24_Space,(Ecodes.class > I24_CLASS_LAST) ? I24_Class[I24_CLASS_DEFAULT]: I24_Class[Ecodes.class],I24_Space,(Ecodes.action > I24_ACTION_LAST) ? I24_Action[I24_ACTION_DEFAULT]: I24_Action[Ecodes.action],I24_Space,(Ecodes.locus > I24_LOCUS_LAST) ? I24_Locus[I24_LOCUS_DEFAULT]: I24_Locus[Ecodes.locus]);OutputBIOSString (MessageBuffer);/* Allowed actions */strcpy (MessageBuffer, "Abort");if (deverror & 0x2000)strcat (MessageBuffer, ", Ignore");if (deverror & 0x1000)strcat (MessageBuffer, ", Retry");if (deverror & 0x0800)strcat (MessageBuffer, ", Fail");OutputBIOSString (strcat (MessageBuffer, "? "));/* Use BIOS to get a key. */while (TRUE){ch = _bios_keybrd (_KEYBRD_READ) & 0x00ff;if ((ch = tolower (ch)) == 'a'){OutputBIOSString ("A\n\r");_hardresume (_HARDERR_ABORT);}else if ((ch == 'i') && (deverror & 0x2000)){OutputBIOSString ("I\n\r");_hardresume (_HARDERR_IGNORE);}else if ((ch == 'r') && (deverror & 0x1000)){OutputBIOSString ("R\n\r");_hardresume (_HARDERR_RETRY);}else if ((ch == 'f') && (deverror & 0x0800)){OutputBIOSString ("F\n\r");# if defined (__WATCOMC__)_hardresume (_HARDERR_FAIL);# else_hardretn (doserror);# endif}OutputBIOSString ("\007");}# if defined (__WATCOMC__)return _HARDERR_FAIL;# endif}/** Display a string using BIOS interrupt 0x0e (Write TTY).*/static void F_LOCAL OutputBIOSString (char *p){union REGS r;while (*p){r.h.ah = 0x0e;r.h.al = *(p++);SystemInterrupt (0x10, &r, &r);}}#endif/** Output to Stderr*/int feputc (int c){return putc (c, stderr);}int feputs (const char *s){return fputs (s, stderr);}int foputs (const char *s){return fputs (s, stdout);}/** Convert drives*/#if (OS_TYPE != OS_UNIX)unsigned int GetDriveNumber (char letter){return tolower (letter) - 'a' + 1;}char GetDriveLetter (unsigned int drive){return (char)(drive + 'a' - 1);}#endif/** IO Map functions - Set or clear the inuse bit in the environment.*/void ChangeFileDescriptorStatus (int fd, bool InUse){if (fd < FDBASE)return;else if (InUse)e.IOMap |= 1L << (fd - FDBASE);elsee.IOMap &= ~(1L << (fd - FDBASE));}/** Convert a string to a number*/int GetNumericValue (char *as){long value;return ConvertNumericValue (as, &value, 10) ? (int) value : -1;}/** ProcessOutputMetaCharacters - Convert an escaped character to a binary value.** Returns the binary value and updates the string pointer.*/static struct {char Escaped;int NewValue;} ConvertMetaCharacters [] ={{ 'b', 0x08}, { 'f', 0x0c}, { 'v', 0x0b}, { 'n', 0x0a},{ 'r', 0x0d}, { 't', 0x09}, { CHAR_META, CHAR_META}, { 'c', -1},{ 0, 0}};int ProcessOutputMetaCharacters (char **cp){int c_val = **cp; /* Current character */int j = 0;if (c_val)(*cp)++;/* Process escaped characters */while (ConvertMetaCharacters[j].Escaped != 0){if (ConvertMetaCharacters[j].Escaped == (char)c_val)return ConvertMetaCharacters[j].NewValue;++j;}/* Check for an octal string */if (IS_OCTAL (c_val) && IS_OCTAL (**cp) && IS_OCTAL (*((*cp) + 1))){c_val = ((c_val & 0x07) << 6) |((**cp & 0x07) << 3) |((*((*cp) + 1) & 0x07));(*cp) += 2;return c_val;}return c_val;}/** Extract the next path from a string and build a new path from the* extracted path and a file name*/char *BuildNextFullPathName (char *path_s, /* Path string */char *file_s, /* File name string */char *output_s) /* Output path */{char *s = output_s;int fsize = 0;while (*path_s && (*path_s != CHAR_PATH_SEPARATOR) && (fsize++ < FFNAME_MAX))*s++ = *path_s++;#if (OS_TYPE == OS_NT) /* handle quoted paths */if (fsize > 2 && output_s[0] == '"' && s[-1] == '"'){fsize -= 2; /* stripe */memmove( output_s, output_s+1, fsize );s = output_s + fsize;}#endifif ((output_s != s) && !IsPathCharacter (*(s - 1)) && (fsize++ < FFNAME_MAX))*s++ = CHAR_UNIX_DIRECTORY;*s = 0;if (file_s != (char *)NULL)strncpy (s, file_s, FFNAME_MAX - fsize);output_s[FFNAME_MAX - 1] = 0;return (*path_s ? ++path_s : (char *)NULL);}/** Go To the specified directory*/bool GotoDirectory (char *CNDirectory, unsigned int cdrive){if (IsDriveCharacter (*(CNDirectory + 1))){if (SetCurrentDrive (GetDriveNumber (*CNDirectory)) == -1)return FALSE;CNDirectory += 2;}/* Was the change successful? */if ((!*CNDirectory) || (S_chdir (CNDirectory)))return TRUE;SetCurrentDrive (cdrive);return FALSE;}/** Find out the application type*/unsigned long QueryApplicationType (const char *pathname){#if (OS_TYPE == OS_UNIX)return EXETYPE_UNIX_NATIVE;#elseint fd;unsigned long res;size_t len;if (pathname == NULL)res = EXETYPE_BAD_FILE;/* Open the file */else if ((fd = open (pathname, O_RDONLY | O_BINARY)) == -1)res = EXETYPE_BAD_FILE;else{res = QueryApplicationType1 (fd);close (fd);/* Check for .com file */if ((res == EXETYPE_UNKNOWN) && ((len = strlen (pathname)) > 5) &&(stricmp (&pathname[len - 4], ".com") == 0))res = EXETYPE_DOS_CUI;}return res;#endif}/** Do the actual work! Under UNIX, we don't care*/#if (OS_TYPE != OS_UNIX)static unsigned long F_LOCAL QueryApplicationType1 (int fd){union {struct ExecOS2_16Header OS2aHead;struct ExecOS2_32header OS2bHead;struct ExecNTHeader NTHead;struct ExecDosHeader DosHead;} OS_Headers;struct stat fstatus;if ((read (fd, &OS_Headers, sizeof (struct ExecDosHeader)) !=sizeof (struct ExecDosHeader)) ||(OS_Headers.DosHead.e_magic != SIG_DOS)){return EXETYPE_UNKNOWN;}/** If the header size in the header is not a new header or the relocation* section starts before the end of the new header, it must be a DOS program*/if ((OS_Headers.DosHead.e_cparhdr * 16 < sizeof (struct ExecDosHeader)) ||(OS_Headers.DosHead.e_lfarlc < sizeof (struct ExecDosHeader))){return EXETYPE_DOS_CUI;}if ((fstat (fd, &fstatus) == -1) ||(fstatus.st_size < (off_t) OS_Headers.DosHead.e_lfanew)){return EXETYPE_DOS_CUI;}if ((lseek (fd, (off_t) OS_Headers.DosHead.e_lfanew, SEEK_SET) ==(off_t) -1) ||(read (fd, &OS_Headers, sizeof (OS_Headers)) != sizeof (OS_Headers))){return EXETYPE_BAD_IMAGE;}/* NT */if (OS_Headers.NTHead.Signature == SIG_NT){// Note: X64 OptionalHeaderSize is longerif (OS_Headers.NTHead.FileHeader.SizeOfOptionalHeader < NT_OPTIONAL_HEADER)return EXETYPE_UNKNOWN;switch (OS_Headers.NTHead.OptionalHeader.Subsystem){default:return EXETYPE_UNKNOWN;case NT_SS_NATIVE:return EXETYPE_NT_NATIVE;case NT_SS_WINDOWS_GUI:return EXETYPE_NT_WINDOWS_GUI;case NT_SS_WINDOWS_CUI:return EXETYPE_NT_WINDOWS_CUI;case NT_SS_OS2_CUI:return EXETYPE_NT_OS2;case NT_SS_POSIX_CUI:return EXETYPE_NT_POSIX;}}/* OS2 1.x */else if ((OS_Headers.OS2aHead.ne_magic == SIG_OS2_16) ||(OS_Headers.OS2aHead.ne_magic == SIG_OS2_16LE)){#ifdef APPDEBUGif (FL_TEST(FLAG_DEBUG_EXECUTE)) {fprintf(stderr, "ne_flags = %.4x ne_flagsothers = %.4x\n",OS_Headers.OS2aHead.ne_flags,OS_Headers.OS2aHead.ne_flagsothers);printf("ne_exetyp = %.4x (R %.2x V %.2x)\n",OS_Headers.OS2aHead.ne_exetyp,OS_Headers.OS2aHead.ne_ver,OS_Headers.OS2aHead.ne_rev);}#endifif (OS_Headers.OS2aHead.ne_flags & (OS2_16_NOTP | OS2_16_IERR))return EXETYPE_BAD_IMAGE;if (OS_Headers.OS2aHead.ne_exetyp == OS2_16_WINDOWS)return EXETYPE_DOS_GUI;/* This appears to be what Watcom generates */else if ((OS_Headers.OS2aHead.ne_exetyp == OS2_16_UNKNOWN) &&(OS_Headers.OS2aHead.ne_flags == 0))return EXETYPE_DOS_32;/* Under OS/2, A bound app is an OS/2 app otherwise its a DOS app */#if (OS_TYPE != OS_OS2)else if (OS_Headers.OS2aHead.ne_exetyp == OS2_16_UNKNOWN)return EXETYPE_DOS_CUI;else if (OS_Headers.OS2aHead.ne_flags & OS2_16_BOUND)return EXETYPE_DOS_BOUND;#elseelse if (OS_Headers.OS2aHead.ne_exetyp == OS2_16_UNKNOWN)return EXETYPE_OS2_CUI;#endif/* Real OS/2 app */else if (OS_Headers.OS2aHead.ne_exetyp == OS2_16_OS2){switch (OS_Headers.OS2aHead.ne_flags & OS2_16_APPTYP){case OS2_16_NOTWINCOMPAT:return EXETYPE_OS2_CUI;case OS2_16_WINCOMPAT:return EXETYPE_OS2_CGUI;case OS2_16_WINAPI:return EXETYPE_OS2_GUI;case 0:#if (OS_TYPE == OS_OS2)return EXETYPE_OS2_CUI;#elsereturn EXETYPE_DOS_BOUND;#endif}}}/* OS2 2.x */else if (OS_Headers.OS2bHead.e32_magic == SIG_OS2_32){#ifdef APPDEBUGif (FL_TEST(FLAG_DEBUG_EXECUTE))fprintf(stderr, "Mflags = %.8lx\n", OS_Headers.OS2bHead.e32_mflags);#endifif ((OS_Headers.OS2bHead.e32_mflags & (OS2_NOTP | OS2_NOLOAD)) ||(OS_Headers.OS2bHead.e32_mflags & OS2_MODMASK))return EXETYPE_NOT_EXE;if ((OS_Headers.OS2bHead.e32_mflags & OS2_APPMASK) == OS2_NOPMW)return EXETYPE_OS2_CUI | EXETYPE_OS2_32;else if ((OS_Headers.OS2bHead.e32_mflags & OS2_APPMASK) == OS2_PMW)return EXETYPE_OS2_CGUI | EXETYPE_OS2_32;else if ((OS_Headers.OS2bHead.e32_mflags & OS2_APPMASK) == OS2_PMAPI)return EXETYPE_OS2_GUI | EXETYPE_OS2_32;}/* Give UP! */return EXETYPE_UNKNOWN;}#endif/** Need case change for UNIX*/#if (OS_TYPE == OS_UNIX)/* Convert a string to lower case */char *strlwr (char *s){char *original = s;if (s != (char *)NULL){while (*s){*s = tolower (*s);s++;}}return (original);}/** Convert a string to upper case*/char *strupr (char *s){char *original = s;if (s != (char *)NULL){while (*s){*s = toupper (*s);s++;}}return (original);}/** String compare - ignore case*/int stricmp (char *a, char *b){int diff;while ((!(diff = tolower(*a) - toupper (*b))) && *a){a++;b++;}return diff;}#endif/** Convert long to based number string*/#if (OS_TYPE == OS_UNIX) || defined (__EMX__)char *ltoa (long n, char *ibuffer, int base){char flag = 0;int r;char lbuffer[32 + 2];char *c_pos = lbuffer + 32 + 2;if ((base < 2) || (base > 36)){errno = ERANGE;return (char *)NULL;}*(--c_pos) = 0;if (n < 0){flag++;n = -n;}if (n == 0)*(--c_pos) = '0';else{while (n > 0){r = (int)(n % (long)base);*(--c_pos) = r + ((r > 9) ? 'a' - 10 : '0');n /= (long)base;}}if (flag)*(--c_pos) = (char)'-';return strcpy (ibuffer, c_pos);}#endif/** EMX does not have a cwait function*/#if defined (__EMX__) && (OS_TYPE == OS_OS2)int cwait (int *TermCode, int pid, int action){ULONG rc;RESULTCODES res;PID rpid;PID apid = pid;if ((rc = DosWaitChild (action == WAIT_GRANDCHILD ? DCWA_PROCESSTREE: DCWA_PROCESS,DCWW_WAIT, &res, &rpid, apid))){if (rc == ERROR_INVALID_PROCID)errno = EINVAL;elseerrno = ECHILD;return (-1);}*TermCode = res.codeResult;return rpid;}#endif/** DEBUG code*/#ifdef DEBUG_ONvoid db_printf (char *fmt, ...){va_list ap;va_start (ap, fmt);vfprintf (stderr, fmt, ap);fputc (CHAR_NEW_LINE, stderr);fflush (stderr);va_end (ap);}#endif/** Get file attributes - EMX version*/#if (OS_TYPE == OS_DOS) && defined (__EMX__)unsigned _dos_getfileattr(char *file, unsigned int *attr){struct _find dtabuf;int rc;DISABLE_HARD_ERRORS;rc = __findfirst (file, OS_FILE_ATTRIBUTES, &dtabuf);ENABLE_HARD_ERRORS;*attr = (char)dtabuf.attr;return (unsigned int)rc;}#endif