/* MS-DOS System Function with Swaping - system (3C) * * MS-DOS System - Copyright (c) 1990,1,2 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. * * Author: * Ian Stewartson * Data Logic, Queens House, Greenhill Way * Harrow, Middlesex HA1 1YR, UK. * istewart@datlog.co.uk or ukc!datlog!istewart * * $Header: /usr/users/istewart/src/shell/sh2.1/RCS/system.c,v 2.0 1992/05/21 16:49:54 Ian_Stewartson Exp $ * * $Log: system.c,v $ * Revision 2.0 1992/05/21 16:49:54 Ian_Stewartson * MS-Shell 2.0 Baseline release * * * MODULE DEFINITION: * * This is a version of the standard system(3c) library function. The only * difference is that it supports swapping and MS-SHELL EXTENDED_LINE * processing. * * To get the OS2 version, compile with -DOS2 * * There are four macros which can be changed: * * GET_ENVIRON To get a variable value from the environment * FAIL_ENVIRON The result on failure * FATAL_ERROR Handle a fatal error message * SHELL_SWITCH The command switch to the SHELL. * * This module replaces the standard Microsoft SYSTEM (3C) call. It should * work with most models. It has been tested in Large and Small model. * When you link a program using the swapper, the swapper front end * (swap.obj) must be the first object model on the linker command line so * that it is located immediately after the PSP. For example: * * link swap+x1+x2+x3+system,x1; * or * cl -o z1 swap.obj x1.obj x2.obj x3.obj system * * The location of the system object is not relevent. */ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DL_MAKE #include "make.h" #endif #ifdef OS2 #define INCL_DOSSESMGR #define INCL_DOSMEMMGR #define INCL_DOSPROCESS #define INCL_WINSWITCHLIST #include #else #include #endif /* * Externals declared by the swapper */ #ifndef OS2 extern char far cmd_line[]; /* Command line */ extern char far path_line[]; /* Process path */ extern unsigned int far SW_intr; /* interrupt pending */ extern unsigned int far SW_Blocks; /* Number of blocks to read */ extern int far SW_fp; /* File or EMS Handler */ extern unsigned int far SW_EMsize; /* Number of extend memory blks */ extern unsigned long far SW_EMstart; /* Start addr of extend mem */ #define SWAP_TO_DISK 1 /* Swap to disk */ #define SWAP_TO_Ext 2 /* Swap to extended memory */ /* Not recommended - no mgt */ #define SWAP_TO_EMS 3 /* Swap to EMS */ #define SWAP_TO_XMS 4 /* Swap to XMS */ extern unsigned int far SW_Mode; /* Type of swapping to do */ extern unsigned int far SW_EMSFrame; /* EMS Frame segment */ extern bool far SW_I23_InShell; /* In the shell */ /* Functions */ extern int far SA_spawn (char **); extern void (interrupt far *SW_I23_V) (void); /* Int 23 address */ extern void (far *SW_XMS_Driver) (void); /* XMS Driver Interface */ extern int far SW_XMS_Gversion (void); extern int far SW_XMS_Allocate (unsigned int); extern int far SW_XMS_Free (int); extern unsigned int far SW_XMS_Available (void); extern void interrupt far SW_Int23 (void); /* Int 23 New address */ extern void interrupt far SW_Int00 (void); /* Int 00 New address */ #endif #define FFNAME_MAX (PATH_MAX + NAME_MAX + 3) /* Set these to the appropriate values to get environment variables. For * make the following values work. Normally, getenv and (char *)NULL should * be used. */ #ifdef DL_MAKE #define GET_ENVIRON(p) GetMacroValue (p) #define FAIL_ENVIRON Nullstr #define FATAL_ERROR(a) PrintFatalError (a) #define SHELL_SWITCH "-ec" #else #define FATAL_ERROR(a) { fputs (a, stderr); fputc ('\n', stderr); exit (1); } #define GET_ENVIRON(p) getenv (p) #define FAIL_ENVIRON (char *)NULL #define SHELL_SWITCH "-c" #endif /* Declarations */ /* Open in create mode */ #define O_CMASK (O_WRONLY | O_CREAT | O_TRUNC | O_TEXT) /* Open in create mode for swap file */ #define O_SMASK (O_RDWR | O_CREAT | O_TRUNC | O_BINARY) #ifndef OS2 #define CMD_LINE_MAX 127 /* Max command line length */ /* MSDOS Memory Control Block chain structure */ #pragma pack (1) struct MCB_list { char MCB_type; /* M or Z */ unsigned int MCB_pid; /* Process ID */ unsigned int MCB_len; /* MCB length */ }; #pragma pack () #define MCB_CON 'M' /* More MCB's */ #define MCB_END 'Z' /* Last MCB's */ /* Swap Mode */ #define SWAP_OFF 0x0000 /* No swapping */ #define SWAP_DISK 0x0001 /* Disk only */ #define SWAP_EXTEND 0x0002 /* Extended memory */ #define SWAP_EXPAND 0x0004 /* Expanded memory */ static int Swap_Mode = (SWAP_DISK | SWAP_EXPAND | SWAP_EXTEND); static char *Swap_File = (char *)NULL; static char *NoSwapFiles = "No Swap files\n"; static char *MS_emsg = "Warning: %s Error (%x)\n"; static char *XMS_Space = "Warning: %s out of space\n"; static char *SwapFailed = "%s swap failed (%x)\n"; #else #define CMD_LINE_MAX 255 /* Max command line length */ #endif char DOS_CommandPath[PATH_MAX + NAME_MAX + 3]; char DOS_CommandLine[CMD_LINE_MAX]; /* Command line */ static char *Extend_file = (char *)NULL; static char *Extensions [] = { "", ".exe", ".com"}; /* * Extract field from a line */ #define MAX_LINEFIELDS 6 /* Max number of line fields */ typedef struct Fields { FILE *FP; /* File handler */ char *Line; /* Line buffer */ int LineLength; /* Line Length */ char *Field[MAX_LINEFIELDS]; /* ptr to the start of fields */ } LineFields; /* * Program type */ static struct ExecutableProcessing { unsigned char Flags; unsigned char FieldSep; char *Name; } ExecProcessingMode; /* Flags set a bit to indicate program mode */ #define EP_NONE 0x00 /* Use PSP command line */ #define EP_DOSMODE 0x01 /* Use DOS mode extended line */ #define EP_UNIXMODE 0x02 /* Use UNIX mode extended line */ #define EP_NOEXPAND 0x04 /* Use -f for this command */ #define EP_ENVIRON 0x08 /* Use environ for variable */ #define EP_NOSWAP 0x10 /* Do not swap for this command */ #define EP_COMSPEC 0x20 /* Special for .bat files */ #define EP_EXPORT 0x40 /* Use -m for this command */ #define EP_CONVERT 0x80 /* Use conversion */ /* * Common fields in EXTENDED_LINE file */ #define COMMON_FIELD_COUNT 4 static struct CommonFields { char *Name; unsigned char Flag; } CommonFields [] = { { "switch", EP_CONVERT }, { "export", EP_EXPORT }, { "noswap", EP_NOSWAP }, { "noexpand", EP_NOEXPAND } }; /* * Functions */ #ifndef OS2 static bool near Get_XMS_Driver (void); static bool near Get_EMS_Driver (void); static bool near EMS_error (char *, int); static bool near XMS_error (char *, int); static int near XMS_Close (void); static int near EMS_Close (void); static int near SwapToDiskError (int, char *); static int near SwapToMemory (int); static void near SetUpSwapper (void); #endif static void near ClearExtendedLineFile (void); static int near ExecuteProgram (char *, char **); static bool near FindLocationOfExecutable (char *, char *); static char * near GenerateTemporaryFileName (void); static char * near BuildNextFullPathName (char *, char *, char *); static void near CheckProgramMode (char *); static unsigned char near CheckForCommonOptions (LineFields *, int, int); static void near SetCurrentDrive (unsigned int); static int near SpawnProcess (void); static int near BuildCommandLine (char *, char **); static char * near GenerateFullExecutablePath (char *); static bool near WriteToExtendedFile (int, char *); static size_t near WhiteSpaceLength (char *, bool *); static int near StartTheProcess (char *, char **); static char * near ConvertPathToFormat (char *); static char * near BuildOS2String (char **, char); static int ExtractFieldsFromLine (LineFields *); /* * System function with swapping */ int system (char const *arg2) { char *argv[4]; char *ep; int res, serrno, len; char p_name[PATH_MAX + NAME_MAX + 3]; char cdirectory[PATH_MAX + 4]; /* Current directory */ char *SaveEV = (char *)NULL; /* Set up argument array */ argv[1] = SHELL_SWITCH; if ((argv[0] = GET_ENVIRON ("SHELL")) == FAIL_ENVIRON) { argv[0] = GET_ENVIRON ("COMSPEC"); argv[1] = "/c"; } if (argv[0] == FAIL_ENVIRON) FATAL_ERROR ("No Shell available"); argv[2] = (char *)arg2; argv[3] = (char *)NULL; /* Check to see if the file exists. First check for command.com to use / * instead of - */ if ((ep = strrchr (argv[0], '/')) == (char *)NULL) ep = argv[0]; else ++ep; /* Check the program mode */ CheckProgramMode (*argv); /* Check for command.com */ #ifndef OS2 if (!stricmp (ep, "command.com") || !stricmp (ep, "command")) { union REGS r; r.x.ax = 0x3700; intdos (&r, &r); if ((r.h.al == 0) && (_osmajor < 4)) *argv[1] = (char)(r.h.dl); if (ExecProcessingMode.Flags & EP_CONVERT) ExecProcessingMode.Flags |= EP_COMSPEC; } #endif /* Convert arguments. If this is COMSPEC for a batch file command, skip over * the first switch */ if (ExecProcessingMode.Flags & EP_COMSPEC) len = 2; /* * Convert from UNIX to DOS format: Slashes to Backslashes in paths and * dash to slash for switches */ if (ExecProcessingMode.Flags & EP_CONVERT) { while ((ep = argv[len++]) != (char *)NULL) { if (*ep == '-') *ep = '/'; else ConvertPathToFormat (ep); } } /* Save the current directory */ getcwd (cdirectory, PATH_MAX + 3); /* If pass in environment, set up environment variable */ if (ExecProcessingMode.Flags & EP_ENVIRON) { if ((SaveEV = GET_ENVIRON (ExecProcessingMode.Name)) != FAIL_ENVIRON) SaveEV = strdup (ExecProcessingMode.Name); /* Get some space for the environment variable */ if ((ep = malloc (strlen (ExecProcessingMode.Name) + strlen (argv[1]) + strlen (argv[2]) + 3)) == (char *)NULL) { if (SaveEV != (char *)NULL) free (SaveEV); free (ExecProcessingMode.Name); return -1; } sprintf (ep, "%s=%s%c%s", ExecProcessingMode.Name, argv[1], ExecProcessingMode.FieldSep, argv[2]); /* Stick it in the environment */ if (putenv (ep)) { free (ExecProcessingMode.Name); return -1; } argv[1] = ExecProcessingMode.Name; argv[2] = (char *)NULL; } /* Start off on the search path for the executable file */ res = (FindLocationOfExecutable (p_name, argv[0])) ? ExecuteProgram (p_name, argv) : -1; serrno = errno; /* Restore the current directory */ SetCurrentDrive (tolower(*cdirectory) - 'a' + 1); if (chdir (&cdirectory[2]) != 0) { fputs ("Warning: current directory reset to /\n", stderr); chdir ("/"); } /* Clean up environment. Restore original value */ if (ExecProcessingMode.Flags & EP_ENVIRON) { len = strlen (ExecProcessingMode.Name) + 2; if (SaveEV != (char *)NULL) len += strlen (SaveEV); if ((ep = malloc (len)) != (char *)NULL) { sprintf (ep, "%s=", ExecProcessingMode.Name, (SaveEV == (char *)NULL) ? "" : SaveEV); putenv (ep); } /* Release memory */ if (SaveEV != (char *)NULL) free (SaveEV); free (ExecProcessingMode.Name); } errno = serrno; return res; } /* Exec or spawn the program ? */ static int near ExecuteProgram (char *path, char **parms) { int res; #ifndef OS2 unsigned int size = 0; int serrno; unsigned int c_cur = (unsigned int)(_psp - 1); struct MCB_list far *mp = (struct MCB_list far *)((unsigned long)c_cur << 16L); #endif /* Check to see if the file exists */ strcpy (DOS_CommandPath, path); /* Check we have access to the file */ if (access (DOS_CommandPath, F_OK) != 0) return -1; /* Process the command line. If no swapping, we have executed the program */ res = BuildCommandLine (DOS_CommandPath, parms); #ifdef OS2 SetWindowName (); ClearExtendedLineFile (); return res; #else if ((ExecProcessingMode.Flags & EP_NOSWAP) || (Swap_Mode == SWAP_OFF) || res) { ClearExtendedLineFile (); return res; } /* Find the length of the swap area */ while ((mp = (struct MCB_list far *)((unsigned long)c_cur << 16L))->MCB_type == MCB_CON) { if ((mp->MCB_pid != _psp) && (mp->MCB_pid != 0) && (mp->MCB_type != MCB_END)) { ClearExtendedLineFile (); FATAL_ERROR ("Fatal: Memory chain corrupt"); return -1; } c_cur += (mp->MCB_len + 1); size += mp->MCB_len + 1; } /* * Convert swap size from paragraphs to 16K blocks. */ if (size == 0) size = mp->MCB_len + 1; SW_Blocks = (size / 0x0400) + 1; /* OK Now we've set up the FCB's, command line and opened the swap file. * Get some sys info for the swapper and execute my little assembler * function to swap us out */ /* Ok - 3 methods of swapping. First tranfer to command line to the * swapper. */ SetUpSwapper (); /* If expanded memory - try that */ if ((Swap_Mode & SWAP_EXPAND) && Get_EMS_Driver ()) { SW_Mode = 3; /* Set Expanded memory swap */ if ((res = SwapToMemory (SWAP_EXPAND)) != -2) return res; } if ((Swap_Mode & SWAP_EXTEND) && Get_XMS_Driver ()) { SW_Mode = (SW_fp == -1) ? 2 : 4;/* Set Extended memory or XMS driver */ if ((res = SwapToMemory (SWAP_EXTEND)) != -2) return res; } /* Try the disk if available */ if (Swap_Mode & SWAP_DISK) { if ((SW_fp = open ((Swap_File = GenerateTemporaryFileName ()), O_SMASK, 0600)) < 0) return SwapToDiskError (ENOSPC, NoSwapFiles); SW_Mode = 1; /* Set Disk file swap */ /* Execute the program */ res = SpawnProcess (); /* Close the extended command line file */ ClearExtendedLineFile (); /* Close the swap file */ serrno = errno; close (SW_fp); unlink (Swap_File); errno = serrno; /* Check for out of swap space */ if (res == -2) return SwapToDiskError (errno, "Swap file write failed\n"); /* Return the result */ return res; } /* No swapping available - give up */ ClearExtendedLineFile (); fputs ("swap: All Swapping methods failed\n", stderr); errno = ENOSPC; return -1; #endif } #ifndef OS2 /* * OS2 does not require swapping * * Get the XMS Driver information */ static bool near Get_XMS_Driver (void) { union REGS or; struct SREGS sr; unsigned int SW_EMsize; /* Number of extend memory blks */ /* Get max Extended memory pages, and convert to 16K blocks. If Extended * memory swapping disabled, set to zero */ SW_fp = -1; /* Set EMS/XMS handler not */ /* defined */ /* Is a XMS memory driver installed */ or.x.ax = 0x4300; int86 (0x2f, &or, &or); if (or.h.al != 0x80) { or.x.ax = 0x8800; int86 (0x15, &or, &or); SW_EMsize = or.x.ax / 16; if ((SW_EMsize <= SW_Blocks) || (((long)(SW_EMstart - 0x100000L) + ((long)(SW_Blocks - SW_EMsize) * 16L * 1024L)) < 0L)) return XMS_error (XMS_Space, 0); else return TRUE; } /* Get the driver interface */ or.x.ax = 0x4310; int86x (0x2f, &or, &or, &sr); SW_XMS_Driver = (void (far *)())((unsigned long)(sr.es) << 16L | or.x.bx); /* Support for version 3 of XMS driver */ if ((SW_XMS_Gversion () & 0xff00) < 0x0200) return XMS_error ("Warning: %s Version < 2\n", 0); else if (SW_XMS_Available () < (SW_Blocks * 16)) return XMS_error (XMS_Space, 0); else if ((SW_fp = SW_XMS_Allocate (SW_Blocks * 16)) == -1) return XMS_error (MS_emsg, errno); return TRUE; } /* Get the EMS Driver information */ static bool near Get_EMS_Driver (void) { union REGS or; struct SREGS sr; char far *sp; /* Set EMS/XMS handler not defined */ SW_fp = -1; or.x.ax = 0x3567; intdosx (&or, &or, &sr); sp = (char far *)((unsigned long)(sr.es) << 16L | 10L); /* If not there - disable */ if (_fmemcmp ("EMMXXXX0", sp, 8) != 0) return EMS_error ("Warning: %s not available\n", 0); or.h.ah = 0x40; /* Check status */ int86 (0x67, &or, &or); if (or.h.ah != 0) return EMS_error (MS_emsg, or.h.ah); /* Check version greater than 3.2 */ or.h.ah = 0x46; int86 (0x67, &or, &or); if ((or.h.ah != 0) || (or.h.al < 0x32)) return EMS_error ("Warning: %s Version < 3.2\n", 0); /* get page frame address */ or.h.ah = 0x41; int86 (0x67, &or, &or); if (or.h.ah != 0) return EMS_error (MS_emsg, or.h.ah); SW_EMSFrame = or.x.bx; /* Save the page frame */ /* Get the number of pages required */ or.h.ah = 0x43; or.x.bx = SW_Blocks; int86 (0x67, &or, &or); if (or.h.ah != 0) return EMS_error (MS_emsg, or.h.ah); /* Save the EMS Handler */ SW_fp = or.x.dx; /* save EMS page map */ or.h.ah = 0x47; or.x.dx = SW_fp; int86 (0x67, &or, &or); return (or.h.ah != 0) ? EMS_error (MS_emsg, or.h.ah) : TRUE; } /* Print EMS error message */ static bool near EMS_error (char *s, int v) { fprintf (stderr, s, "EMS", v); Swap_Mode &= ~(SWAP_EXPAND); EMS_Close (); return FALSE; } /* Print XMS error message */ static bool near XMS_error (char *s, int v) { fprintf (stderr, s, "XMS", v); Swap_Mode &= ~(SWAP_EXTEND); XMS_Close (); return FALSE; } /* If the XMS handler is defined - close it */ static int near XMS_Close (void) { int res = 0; /* Release XMS page */ if (SW_fp != -1) res = SW_XMS_Free (SW_fp); SW_fp = -1; return res; } /* If the EMS handler is defined - close it */ static int near EMS_Close (void) { union REGS or; int res = 0; if (SW_fp == -1) return 0; /* Restore EMS page */ or.h.ah = 0x48; or.x.dx = SW_fp; int86 (0x67, &or, &or); if (or.h.ah != 0) res = or.h.al; or.h.ah = 0x45; or.x.dx = SW_fp; int86 (0x67, &or, &or); SW_fp = -1; return (res) ? res : or.h.ah; } #endif /* * Find the location of an executable and return it's full path * name */ static bool near FindLocationOfExecutable (char *FullPath, char *name) { register char *sp; /* Path pointers */ char *ep; char *xp1; int i; /* Scan the path for an executable */ sp = ((strchr (name, '/') != (char *)NULL) || (*(name + 1) == ':')) ? "" : GET_ENVIRON ("PATH"); do { sp = BuildNextFullPathName (sp, name, FullPath); ep = &FullPath[strlen (FullPath)]; /* Get start of file name */ if ((xp1 = strrchr (FullPath, '/')) == (char *)NULL) xp1 = FullPath; else ++xp1; /* Look up all 3 types */ for (i = 0; i < 3; i++) { strcpy (ep, Extensions[i]); if (access (FullPath, X_OK) == 0) return TRUE; } } while (sp != (char *)NULL); /* Not found */ errno = ENOENT; return FALSE; } /* * Generate a temporary filename */ static char * near GenerateTemporaryFileName (void) { static char tmpfile[FFNAME_MAX]; char *tmpdir; /* Points to directory prefix of pipe */ static int temp_count = 0; char *sep = "/"; /* Find out where we should put temporary files */ if (((tmpdir = GET_ENVIRON ("TMP")) == FAIL_ENVIRON) && ((tmpdir = GET_ENVIRON ("HOME")) == FAIL_ENVIRON) && ((tmpdir = GET_ENVIRON ("TMPDIR")) == FAIL_ENVIRON)) tmpdir = "."; if (strchr ("/\\", tmpdir[strlen (tmpdir) - 1]) != (char *)NULL) sep = ""; /* Get a unique temporary file name */ for (;;) { sprintf (tmpfile, "%s%ssap%.5u.tmp", tmpdir, sep, temp_count++); if (access (tmpfile, F_OK) != 0) break; } return tmpfile; } /* * Extract the next path from a string and build a new path from the * extracted path and a file name * * path_s - Path string * file_s - File name string * output_s - Output path */ static char * near BuildNextFullPathName (register char *path_s, register char *file_s, char *output_s) { register char *s = output_s; int fsize = 0; while (*path_s && (*path_s != ';') && (fsize++ < FFNAME_MAX)) *s++ = *path_s++; if ((output_s != s) && (*(s - 1) != '/') && (fsize++ < FFNAME_MAX)) *s++ = '/'; *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); } #ifndef OS2 /* * Swap to Memory */ static int near SwapToMemory (int mode) { int res; int cr; /* Swap and close memory handler */ res = SpawnProcess (); cr = (SW_Mode != 3) ? XMS_Close () : EMS_Close (); if ((res != -2) && cr) /* Report Close error ? */ { res = -2; errno = cr; } if (res == -2) (SW_Mode != 3) ? XMS_error (SwapFailed, errno) : EMS_error (SwapFailed, errno); else { ClearExtendedLineFile (); return res; } /* Failed - disabled */ Swap_Mode &= (~mode); return res; } /* * Swap to disk error */ static int near SwapToDiskError (int error, char *ErrorMessage) { /* Close the swap file, if open */ if (SW_fp >= 0) close (SW_fp); /* Clean up */ unlink (Swap_File); Swap_File = (char *)NULL; Swap_Mode &= (~SWAP_DISK); fprintf (stderr, ErrorMessage); errno = error; return -1; } #endif /* Clear Extended command line file */ static void near ClearExtendedLineFile (void) { if (Extend_file != (char *)NULL) { unlink (Extend_file); free ((char *)Extend_file); } Extend_file = (char *)NULL; } /* * Check the program type */ static void near CheckProgramMode (char *Pname) { char *sp, *sp1; /* Line pointers */ int nFields; char *SPname; LineFields LF; struct ExecutableProcessing *PMode = &ExecProcessingMode; /* Set not found */ PMode->Flags = EP_NONE; PMode->Name = (char *)NULL; /* Check not a function */ if ((Pname == (char *)NULL) || ((sp = GET_ENVIRON ("EXTENDED_LINE")) == FAIL_ENVIRON)) return; /* Get some memory for the input line and the file name */ sp1 = ((sp1 = strrchr (Pname, '/')) == (char *)NULL) ? Pname : sp1 + 1; if ((SPname = strdup (sp1)) == (char *)NULL) return; if ((LF.Line = malloc (LF.LineLength = 200)) == (char *)NULL) { free (SPname); return; } /* Remove terminating .exe etc */ if ((sp1 = strrchr (SPname, '.')) != (char *)NULL) *sp1 = 0; /* Open the file */ if ((LF.FP = fopen (sp, "rt")) == (FILE *)NULL) { free ((char *)LF.Line); free (SPname); return; } /* Scan for the file name */ while ((nFields = ExtractFieldsFromLine (&LF)) != -1) { if (nFields < 2) continue; /* Remove terminating .exe etc */ if ((sp = strrchr (LF.Field[0], '.')) != (char *)NULL) *sp = 0; if (stricmp (LF.Field[0], SPname)) continue; /* What type? */ if (stricmp (LF.Field[1], "unix") == 0) PMode->Flags = (unsigned char)(EP_UNIXMODE | CheckForCommonOptions (&LF, nFields, 2)); else if (stricmp (LF.Field[1], "dos") == 0) PMode->Flags = (unsigned char)(EP_DOSMODE | CheckForCommonOptions (&LF, nFields, 2)); /* Must have a valid name and we can get memory for it */ else if ((stricmp (LF.Field[1], "environ") == 0) && (nFields >= 3) && ((PMode->Name = strdup (LF.Field[2])) != (char *)NULL)) { PMode->Flags = EP_ENVIRON; PMode->FieldSep = 0; if (nFields >= 4) PMode->FieldSep = (unsigned char)strtol (LF.Field[3], (char **)NULL, 0); if (!PMode->FieldSep) PMode->FieldSep = ' '; } else PMode->Flags = CheckForCommonOptions (&LF, nFields, 1); break; } fclose (LF.FP); free ((char *)LF.Line); free (SPname); } /* * Check for common fields */ static unsigned char near CheckForCommonOptions (LineFields *LF, int nFields, int Start) { unsigned char Flags = 0; int i, j; for (i = Start; i < nFields; i++) { for (j = 0; j < COMMON_FIELD_COUNT; ++j) { if (!stricmp (LF->Field[i], CommonFields[j].Name)) { Flags |= CommonFields[j].Flag; break; } } } return Flags; } /* * Set the current drive number and return the number of drives. */ static void near SetCurrentDrive (unsigned int drive) { #ifdef OS2 DosSelectDisk ((USHORT)drive); #else unsigned int ndrives; _dos_setdrive (drive, &ndrives); #endif } /* * Convert UNIX format lines to DOS format if appropriate. * Build Environment variable for some programs. */ #ifndef OS2 static int near SpawnProcess (void) { void (interrupt far *SW_I00_V) (void); /* Int 00 address */ void (interrupt far *SW_I23_V) (void); /* Int 23 address*/ int res; #if 0 union REGS r; unsigned char Save; r.x.ax = 0x3300; intdos (&r, &r); Save = r.h.al; fprintf (stderr, "Break Status: %s (%u)\n", Save ? "on" : "off", Save); r.x.ax = 0x3301; r.h.dl = 1; intdos (&r, &r); fprintf (stderr, "Break Status: %s (%u)\n", r.h.al ? "on" : "off", r.h.al); #endif /* * Save current vectors */ SW_I00_V = _dos_getvect (0x00); SW_I23_V = _dos_getvect (0x23); /* * Set In shell flag for Interrupt 23, and set to new interrupts */ SW_I23_InShell = 0; _dos_setvect (0x23, SW_Int23); _dos_setvect (0x00, SW_I00_V); res = SA_spawn (environ); /* * Restore interrupt vectors */ _dos_setvect (0x23, SW_I23_V); #if 0 r.x.ax = 0x3300; intdos (&r, &r); fprintf (stderr, "Break Status: %s (%u)\n", r.h.al ? "on" : "off", r.h.al); r.x.ax = 0x3301; r.h.dl = Save; intdos (&r, &r); #endif /* * Check for an interrupt */ if (SW_intr) raise (SIGINT); return res; } #endif /* Set up command line. If the EXTENDED_LINE variable is set, we create * a temporary file, write the argument list (one entry per line) to the * this file and set the command line to @. If NOSWAPPING, we * execute the program because I have to modify the argument line */ static int near BuildCommandLine (char *path, char **argv) { char **pl = argv; int res, fd; bool found; char *ep; char *new_args[3]; /* Translate process name to MSDOS format */ if (GenerateFullExecutablePath (path) == (char *)NULL) return -1; /* Extended command line processing */ Extend_file = (char *)NULL; /* Set no file */ found = ((ExecProcessingMode.Flags & EP_UNIXMODE) || (ExecProcessingMode.Flags & EP_DOSMODE)) ? TRUE : FALSE; if ((*(++pl) != (char *)NULL) && found) { char **pl1 = pl; /* Check parameters don't contain a re-direction parameter */ while (*pl1 != (char *)NULL) { if (**(pl1++) == '@') { found = FALSE; break; } } /* If we find it - create a temporary file and write the stuff */ if ((found) && ((fd = open (Extend_file = GenerateTemporaryFileName (), O_CMASK, 0600)) >= 0)) { Extend_file = strdup (Extend_file); /* Copy to end of list */ while (*pl != (char *)NULL) { if (!WriteToExtendedFile (fd, *(pl++))) return -1; } /* Completed write OK */ close (fd); /* Set up DOS_CommandLine[1] to contain the filename */ memset (DOS_CommandLine, 0, CMD_LINE_MAX); DOS_CommandLine[1] = ' '; DOS_CommandLine[2] = '@'; strcpy (&DOS_CommandLine[3], Extend_file); DOS_CommandLine[0] = (char)(strlen (Extend_file) + 2); /* Correctly terminate DOS_CommandLine in no swap mode */ #ifndef OS2 if (!(ExecProcessingMode.Flags & EP_NOSWAP) && (Swap_Mode != SWAP_OFF)) DOS_CommandLine[DOS_CommandLine[0] + 2] = 0x0d; #endif /* If the name in the file is in upper case - use \ for separators */ if (ExecProcessingMode.Flags & EP_DOSMODE) ConvertPathToFormat (&DOS_CommandLine[2]); /* OK we are ready to execute */ #ifndef OS2 if ((ExecProcessingMode.Flags & EP_NOSWAP) || (Swap_Mode == SWAP_OFF)) { #endif new_args[0] = *argv; new_args[1] = &DOS_CommandLine[1]; new_args[2] = (char *)NULL; return StartTheProcess (path, new_args); #ifndef OS2 } else return 0; #endif } } /* Check length of Parameter list */ res = 0; DOS_CommandLine[0] = 0; DOS_CommandLine[1] = 0x0d; /* Skip the first parameter and get the length of the rest */ if (*argv != (char *)NULL) { *(ep = DOS_CommandLine + 1) = 0; while (*pl != (char *)NULL) { res += WhiteSpaceLength (*pl, &found); if (res >= CMD_LINE_MAX) { errno = E2BIG; return -1; } if (found) strcat (strcat (strcat (ep, " \""), *(pl++)), "\""); else strcat (strcat (ep, " "), *(pl++)); } DOS_CommandLine[res + 1] = 0x0d; } /* Terminate the line and insert the line length */ DOS_CommandLine[0] = (char)res; /* If swapping disabled - just execute it */ return StartTheProcess (path, argv); } /* * Convert the executable path to the full path name */ static char * near GenerateFullExecutablePath (char *path) { char cpath[PATH_MAX + 4]; char npath[PATH_MAX + NAME_MAX + 4]; char n1path[PATH_MAX + 4]; char *p; int drive; /* Get path in DOS format */ ConvertPathToFormat (path); #ifndef OS2 strupr (path); #else if (!IsHPFSFileSystem (path)) strupr (path); #endif /* Get the current path */ getcwd (cpath, PATH_MAX + 3); strcpy (npath, cpath); /* In current directory ? */ if ((p = strrchr (path, '\\')) == (char *)NULL) { p = path; /* Check for a:program case */ if (*(p + 1) == ':') { p += 2; /* Get the path of the other drive */ _getdcwd (tolower (*path) - 'a' + 1, npath, PATH_MAX + 3); } } /* In root directory */ else if ((p - path) == 0) { ++p; strcpy (npath, "/"); *npath = *path; *npath = *cpath; } else if (((p - path) == 2) && (*(path + 1) == ':')) { ++p; strcpy (npath, "/"); *npath = *path; } /* Find the directory */ else { *(p++) = 0; /* Change to the directory containing the executable */ drive = (*(path + 1) == ':') ? tolower (*path) - 'a' + 1 : 0; /* Save the current directory on this drive */ _getdcwd (drive, n1path, PATH_MAX + 3); /* Find the directory we want */ if (chdir (path) < 0) return (char *)NULL; _getdcwd (drive, npath, PATH_MAX + 3); /* Save its full name */ chdir (n1path); /* Restore the original */ /* Restore our original directory */ if (chdir (cpath) < 0) return (char *)NULL; } if (npath[strlen (npath) - 1] != '\\') strcat (npath, "\\"); strcat (npath, p); return strcpy (path, npath); } /* * Write to the Extended File */ static bool near WriteToExtendedFile (int fd, char *string) { char *sp = string; char *cp = string; bool WriteOk = TRUE; int Length; if (strlen (string)) { /* Write the string, converting newlines to backslash newline */ while (WriteOk && (cp != (char *)NULL)) { if ((cp = strchr (sp, '\n')) != (char *)NULL) *cp = 0; if ((Length = strlen (sp)) && (write (fd, sp, Length) != Length)) WriteOk = FALSE; if (WriteOk && (cp != (char *)NULL)) WriteOk = (write (fd, "\\\n", 2) == 2) ? TRUE : FALSE; sp = cp + 1; } } if (WriteOk && (write (fd, "\n", 1) == 1)) return TRUE; close (fd); ClearExtendedLineFile (); errno = ENOSPC; return FALSE; } /* Check string for white space */ static size_t near WhiteSpaceLength (char *s, bool *wsf) { char *os = s; *wsf = FALSE; while (*s) { if (isspace (*s)) *wsf = TRUE; ++s; } return (size_t)(s - os) + (*wsf ? 3 : 1); } /* * Execute or spawn the process */ static int near StartTheProcess (char *path, char **argv) { #ifdef OS2 void (*sig_int)(); /* Interrupt signal */ int RetVal; USHORT usType; STARTDATA stdata; #endif /* Is this a start session option */ #ifndef OS2 return ((ExecProcessingMode.Flags & EP_NOSWAP) || (Swap_Mode == SWAP_OFF)) ? spawnve (P_WAIT, path, argv, environ) : 0; #else /* In OS/2, we need the type of the program because PM programs have to be * started in a session (or at least that was the only way I could get them * to work). */ if (DosQAppType (path, &usType)) { errno = ENOENT; return -1; } /* In OS/2, need to set signal to default so child will process it */ else { sig_int = signal (SIGINT, SIG_DFL); if ((usType & 3) == WINDOWAPI) { stdata.Length = sizeof (STARTDATA); stdata.Related = FALSE; stdata.FgBg = FALSE; stdata.TraceOpt = 0; stdata.PgmTitle = (char *)NULL; stdata.TermQ = 0; stdata.Environment = (char *)NULL; /* Build Env */ stdata.InheritOpt = 0; stdata.SessionType = 3; stdata.IconFile = (char *)NULL; stdata.PgmHandle = 0L; stdata.PgmControl = 8; stdata.InitXPos = 0; stdata.InitYPos = 0; stdata.InitXSize = 100; stdata.InitYSize = 100; RetVal = StartTheSession (&stdata, path, argv) if (stdata.Environment != (char *)NULL) free (stdata.Environment); if (stdata.PgmInputs != (char *)NULL) free (stdata.PgmInputs); } else RetVal = spawnve (P_WAIT, path, argv, environ); signal (SIGINT, sig_int); } return RetVal; #endif } /* * Convert path format to/from UNIX */ static char * near ConvertPathToFormat (char *path) { char *s = path; while ((path = strchr (path, '/')) != (char *)NULL) *path = '\\'; return s; } #ifdef OS2 static int near StartTheSession (STARTDATA *SessionData, char *path, char **argv) { USHORT usType; USHORT idSession; USHORT pid; /* Ensure we always start a PM session in PM */ if (DosQAppType (path, &usType)) { errno = ENOENT; return -1; } if ((usType & 3) == WINDOWAPI) SessionData->SessionType = 3; SessionData->PgmName = path; if ((SessionData->Environment = BuildOS2String (environ, 0)) == (char *)NULL) return -1; if ((SessionData->PgmInputs = BuildOS2String (&argv[1], 0)) == (char *)NULL) return -1; if (!(usType = DosStartSession (SessionData, &idSession, &pid))) return 0; else { errno = ENOENT; return -1; } } #endif /* * Build the OS2 format \0\0 etc \0 */ static char * near BuildOS2String (char **Array, char sep) { int i = 0; int Length = 0; char *Output; char *sp, *cp; while ((sp = Array[i++]) != (char *)NULL) Length += strlen (sp) + 1; Length += 2; if ((Output = malloc (Length)) == (char *)NULL) return (char *)NULL; i = 0; sp = Output; /* Build the string */ while ((cp = Array[i++]) != (char *)NULL) { while (*sp = *(cp++)) ++sp; *(sp++) = sep; } *sp = 0; return Output; } /* * Get and process configuration line: * * = # comment * * return the number of fields found. */ static int ExtractFieldsFromLine (LineFields *fd) { char *cp; int fieldno; if (fgets (fd->Line, fd->LineLength - 1, fd->FP) == (char *)NULL) { fclose (fd->FP); return -1; } /* Remove the EOL */ if ((cp = strchr (fd->Line, '\n')) != (char *)NULL) *cp = 0; /* Remove the comments at end */ if ((cp = strchr (fd->Line, '#')) != (char *)NULL) *cp = 0; /* Extract the fields */ cp = fd->Line; for (fieldno = 0; fieldno < MAX_LINEFIELDS; fieldno++) { while (isspace (*cp)) ++cp; if (!*cp) return fieldno; fd->Field[fieldno] = cp; /* First field must be followed by equals */ if (!fieldno) { while (!isspace (*cp) && *cp && (*cp != '=')) ++cp; if (*cp && (*cp != '=')) { *(cp++) = 0; while (isspace (*cp)) ++cp; } if (*cp != '=') return fieldno + 1; } /* Process second and third fields */ else { while (!isspace (*cp) && *cp) ++cp; } *(cp++) = 0; } return fieldno; } /* * For multiple model support, we need to transfer to command lines * to the swapper in far space */ #ifndef OS2 static void near SetUpSwapper (void) { /* Transfer path name */ _fstrcpy (path_line, DOS_CommandPath); /* Transfer command line */ _fmemcpy (cmd_line, DOS_CommandLine, CMD_LINE_MAX); } #endif /* * Test program */ #ifdef TEST int main (int argc, char **argv) { int i; for (i = 1; i < argc; i++) printf ("Result = %d\n", system (argv[i])); return 0; } #endif