This patch is to be applied over the "msvcrt-popen-1" patch released earlier today. This patch fixes the issue of only one "_popen" call allowed open at any point, i.e. multiple calls to _popen/_wpopen can be made without clsing the first _popen call first. License: LGPL Changelog: * dlls/msvcrt/msvcrt.h, dlls/msvcrt/main.c. dlls/msvcrt/process.c: Jaco Greeff <jaco@puxedo.org> - Add the ability to have multiple open processes via the _popen/_wpopen/_pclose calls ---------------------------- diff -aurN msvcrt-popen.orig/dlls/msvcrt/main.c msvcrt-popen.new/dlls/msvcrt/main.c --- msvcrt-popen.orig/dlls/msvcrt/main.c Fri Nov 1 14:24:21 2002 +++ msvcrt-popen.new/dlls/msvcrt/main.c Fri Nov 1 14:26:38 2002 @@ -58,6 +58,7 @@ msvcrt_init_io(); msvcrt_init_console(); msvcrt_init_args(); + msvcrt_init_process(); MSVCRT_setlocale(0, "C"); TRACE("finished process init\n"); break; diff -aurN msvcrt-popen.orig/dlls/msvcrt/msvcrt.h msvcrt-popen.new/dlls/msvcrt/msvcrt.h --- msvcrt-popen.orig/dlls/msvcrt/msvcrt.h Fri Nov 1 14:24:03 2002 +++ msvcrt-popen.new/dlls/msvcrt/msvcrt.h Fri Nov 1 14:25:52 2002 @@ -76,6 +76,7 @@ extern void msvcrt_init_args(void); extern void msvcrt_free_args(void); extern void msvcrt_init_vtables(void); +extern void msvcrt_init_process(void); /* run-time error codes */ #define _RT_STACK 0 diff -aurN msvcrt-popen.orig/dlls/msvcrt/process.c msvcrt-popen.new/dlls/msvcrt/process.c --- msvcrt-popen.orig/dlls/msvcrt/process.c Fri Nov 1 14:07:35 2002 +++ msvcrt-popen.new/dlls/msvcrt/process.c Fri Nov 1 15:07:09 2002 @@ -50,7 +50,12 @@ #define POPEN_FLAG_TEXT 0x0004 #define POPEN_FLAG_BINARY 0x0008 +/* FIXME: Don't know what this value should be, 10 seems enough as + * the maximum number of simultaneous _popen'ed processes + */ #define POPEN_MAX_FILES 10 +#define POPEN_FILE_IN_USE 0x0001 + #define POPEN_WCMD_EXEC "wcmd /c " #define LPCWSTR_TO_LPSTR(wszIn, nIn, szOut, nOut) \ @@ -71,11 +76,7 @@ int fd; } POPEN_FILE; -/* FIXME: we can only spawn one process at a time presently, this - * needs to be extended to allow for any number. As a first pass, - * this might be good enough... - */ -static POPEN_FILE *pPOPEN_File = NULL; +static POPEN_FILE POPEN_Files[POPEN_MAX_FILES]; /* FIXME: Check file extensions for app to run */ static const unsigned int EXE = 'e' << 16 | 'x' << 8 | 'e'; @@ -83,6 +84,25 @@ static const unsigned int CMD = 'c' << 16 | 'm' << 8 | 'd'; static const unsigned int COM = 'c' << 16 | 'o' << 8 | 'm'; +/********************************************************************* + * msvcrt_init_process (internal) + * + * Used to initialise the internal process tables + */ +void msvcrt_init_process(void) +{ + int i = 0; + for (i = 0; i < POPEN_MAX_FILES; i++) + { + /* clear everything that is used to hold the process + * file handles by setting it to zero. + */ + POPEN_Files[i].fSysProcess = NULL; + POPEN_Files[i].fProcess = NULL; + POPEN_Files[i].fd = -1; + } +} + /* INTERNAL: Spawn a child process */ static int msvcrt_spawn(int flags, const char* exe, char* cmdline, char* env) { @@ -554,14 +574,49 @@ } /********************************************************************* + * POPEN_getOpenFileSlotPos (internal) + * + * Return the first available file slot for storing file data + */ +INT POPEN_getOpenFileSlotPos(VOID) +{ + INT i = 0; + for (i = 0; i < POPEN_MAX_FILES; i++) + { + if (!POPEN_Files[i].fProcess) + { + POPEN_Files[i].fProcess = (MSVCRT_FILE *)POPEN_FILE_IN_USE; + return i; + } + } + return -1; +} + +/********************************************************************* + * POPEN_findFileSlotPos (internal) + * + * Return the first available file slot for storing file data + */ +INT POPEN_findFileSlotPos(MSVCRT_FILE *fProcess) +{ + INT i = 0; + for (i = 0; i < POPEN_MAX_FILES; i++) + { + if (POPEN_Files[i].fProcess == fProcess) + return i; + } + return -1; +} + +/********************************************************************* * MSVCRT_popen (MSVCRT.@) */ MSVCRT_FILE *MSVCRT_popen(const CHAR *szCommand, const CHAR *szMode) { MSVCRT_FILE *fProcess = NULL; - FILE *fIntl = NULL; INT nFlags = 0; CHAR *szExec = NULL; + INT nPos; TRACE("(szCommand == %s, szMode == %s)\n", debugstr_a(szCommand), debugstr_a(szMode)); @@ -582,42 +637,32 @@ if ((szExec = (CHAR *)malloc(strlen(POPEN_WCMD_EXEC)+strlen(szCommand)+1))) { sprintf(szExec, "%s%s", POPEN_WCMD_EXEC, szCommand); - + /* convert from the specific FILE* to the MSVCRT_FILE*. We need * to make doubly sure that in the process we are not leaking * handles since we go from File -> fd -> MSVCRT_FILE. (The route * back in _pclose is even more interresting which means that we * need to keep track of the original FILE* so we can close it...) - * - * FIXME: We only have one available slot at present so we can only - * have one _popen command active at any time. */ - if (!pPOPEN_File) + if ((nPos = POPEN_getOpenFileSlotPos()) != -1) { - FIXME("Allowance for multiple _popen streams to be made\n"); - - if ((pPOPEN_File = (POPEN_FILE *)MSVCRT_malloc(sizeof(POPEN_FILE))) && - (fIntl = popen(szExec, (nFlags & POPEN_FLAG_READ) ? "r" : "w"))) + if ((POPEN_Files[nPos].fSysProcess = popen(szExec, (nFlags & POPEN_FLAG_READ) ? "r" : "w"))) { - int fd = fileno(fIntl); - fProcess = _fdopen(fd, (nFlags & POPEN_FLAG_READ) ? "r" : "w"); - - pPOPEN_File->fSysProcess = fIntl; - pPOPEN_File->fProcess = fProcess; - pPOPEN_File->fd = fd; + POPEN_Files[nPos].fd = fileno(POPEN_Files[nPos].fSysProcess); + POPEN_Files[nPos].fProcess = _fdopen(POPEN_Files[nPos].fd, + (nFlags & POPEN_FLAG_READ) ? "r" : "w"); } else { - if (pPOPEN_File) - { - MSVCRT_free(pPOPEN_File); - pPOPEN_File = NULL; - } + /* we've setup POPEN_Files[nPos].fProcess to POPEN_FILE_IN_USE, + * reset it to available not that we won't be using it after all + */ + POPEN_Files[nPos].fProcess = NULL; ERR("Execution of %s failed\n", debugstr_a(szExec)); } } else - FIXME("Attempt to use multiple _popen streams, failing\n"); + FIXME("Maxiumum number of process streams opened, failing\n"); MSVCRT_free(szExec); } @@ -660,23 +705,22 @@ int MSVCRT_pclose(MSVCRT_FILE *fProcess) { int nRet = -1; + int nPos; TRACE("(fProcess == %p)\n", fProcess); - /* FIXME: single process time again, we must allow for multiple streams - * here and in the _popen function (read comments in _popen) - */ - if (pPOPEN_File && fProcess && pPOPEN_File->fProcess == fProcess) + if ((nPos = POPEN_findFileSlotPos(fProcess)) != -1) { - nRet = pclose(pPOPEN_File->fSysProcess); + nRet = pclose(POPEN_Files[nPos].fSysProcess); /* FIXME: we need to make doubly sure that we are not leaking handles. Since * all references refer to the same file, we should be safe. We are closing * fProcess/pPOPEN_File->fProcess just to clean up the internal msvcrt stuff. */ MSVCRT_fclose(fProcess); - MSVCRT_free(pPOPEN_File); - pPOPEN_File = NULL; + POPEN_Files[nPos].fSysProcess = NULL; + POPEN_Files[nPos].fProcess = NULL; + POPEN_Files[nPos].fd = -1; } else ERR("%p is not a valid process handle\n", fProcess);