dlls/msvcrt: msvcrt-popen-2.diff

Jaco Greeff jaco at puxedo.org
Sat Nov 2 09:41:38 CST 2002


Hi,

This second round of the _popen/_wpopen/_pclose implementation uses
CreateProcess with pipes as per Alexandre's suggestion. This patch will not
work without the wcmd-1 patch sent earlier, since earlier versions of wcmd
create new STDIN and STDOUT handles by allocating a new console,
invalidating the pipes created by the _popen command. Test case to follow...

License:
LGPL

Changelog:
* dlls/msvcrt/msvcrt.spec, dlls/msvcrt/file.c: Jaco Greeff <jaco at puxedo.org>
- Implementation of the _popen/_wpopen/_pclose commands


-- [ inline patch ]--

diff -aurN msvcrt-popen.orig/dlls/msvcrt/file.c
msvcrt-popen.new/dlls/msvcrt/file.c
--- msvcrt-popen.orig/dlls/msvcrt/file.c	2002-11-02 12:07:23.000000000 +0000
+++ msvcrt-popen.new/dlls/msvcrt/file.c	2002-11-02 17:17:51.000000000 +0000
@@ -5,6 +5,7 @@
  * Copyright 1996 Jukka Iivonen
  * Copyright 1997,2000 Uwe Bonnes
  * Copyright 2000 Jon Griffiths
+ * Copyright 2002 Jaco Greeff
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -96,6 +97,62 @@
 #define LOCK_FILES     EnterCriticalSection(&MSVCRT_file_cs)
 #define UNLOCK_FILES   LeaveCriticalSection(&MSVCRT_file_cs)
 
+/* 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     "C:\\Windows\\System32\\cmd.exe /c"
+
+#define LPCWSTR_TO_LPSTR(wszIn, nIn, szOut, nOut) \
+    *nOut = WideCharToMultiByte(CP_ACP, 0, wszIn, nIn, NULL, 0, NULL,
NULL);    \
+    szOut = (CHAR *)MSVCRT_malloc((*nOut+1)*sizeof(CHAR));                
     \
+    if (szOut)                                                            
     \
+    {                                                                     
     \
+        WideCharToMultiByte(CP_ACP, 0, wszIn, nIn, szOut, *nOut+1, NULL,
NULL); \
+        szOut[nLen] = '\0';                                               
     \
+    }                                                                     
     \
+    else                                                                  
     \
+        *nOut = 0;
+
+#define CREATE_PIPE(hRead, hWrite, security) \
+    if (!CreatePipe(&hRead, &hWrite, &security, 0))                       
     \
+    {                                                                     
     \
+        TRACE("Creation of pipe failed\n");                               
     \
+        return NULL;                                                      
     \
+    }
+
+#define REDIRECT_STREAM(type, handle)\
+    if (!SetStdHandle(type, handle))                                      
     \
+    {                                                                     
     \
+        TRACE("Redirection of stream failed");                            
     \
+        return NULL;                                                      
     \
+    }
+
+#define RESTORE_STREAM(type, handle) \
+    if (!SetStdHandle(type, handle))                                      
     \
+        TRACE("Restore of stream failed\n");
+
+#define DUPLICATE_HANDLE(hHandle, hDup, type, hOrig) \
+    if (!DuplicateHandle(GetCurrentProcess(), hHandle,                    
     \
+                         GetCurrentProcess(), &hDup, 0,                   
     \
+                         FALSE, DUPLICATE_SAME_ACCESS))                   
     \
+    {                                                                     
     \
+        TRACE("Duplication of piped handle failed\n");                    
       \
+        RESTORE_STREAM(type, hOrig);                                      
     \
+        return NULL;                                                      
     \
+    }                                                                     
     \
+    else                                                                  
     \
+        CloseHandle(hHandle);
+
+typedef struct
+{
+    MSVCRT_FILE *fProcess;
+    HANDLE      hProcess;
+} POPEN_FILE;
+
+static POPEN_FILE POPEN_Files[POPEN_MAX_FILES];
 
 /* INTERNAL: Get the HANDLE for a fd */
 static HANDLE msvcrt_fdtoh(int fd)
@@ -183,25 +240,34 @@
 /* INTERNAL: Set up stdin, stderr and stdout */
 void msvcrt_init_io(void)
 {
-  int i;
-  memset(MSVCRT__iob,0,3*sizeof(MSVCRT_FILE));
-  MSVCRT_handles[0] = GetStdHandle(STD_INPUT_HANDLE);
-  MSVCRT_flags[0] = MSVCRT__iob[0]._flag = MSVCRT__IOREAD;
-  MSVCRT_handles[1] = GetStdHandle(STD_OUTPUT_HANDLE);
-  MSVCRT_flags[1] = MSVCRT__iob[1]._flag = MSVCRT__IOWRT;
-  MSVCRT_handles[2] = GetStdHandle(STD_ERROR_HANDLE);
-  MSVCRT_flags[2] = MSVCRT__iob[2]._flag = MSVCRT__IOWRT;
-
-  TRACE(":handles (%p)(%p)(%p)\n",MSVCRT_handles[0],
-	MSVCRT_handles[1],MSVCRT_handles[2]);
-
-  for (i = 0; i < 3; i++)
-  {
-    /* FILE structs for stdin/out/err are static and never deleted */
-    MSVCRT_files[i] = &MSVCRT__iob[i];
-    MSVCRT__iob[i]._file = i;
-    MSVCRT_tempfiles[i] = NULL;
-  }
+    int i;
+    memset(MSVCRT__iob,0,3*sizeof(MSVCRT_FILE));
+    MSVCRT_handles[0] = GetStdHandle(STD_INPUT_HANDLE);
+    MSVCRT_flags[0] = MSVCRT__iob[0]._flag = MSVCRT__IOREAD;
+    MSVCRT_handles[1] = GetStdHandle(STD_OUTPUT_HANDLE);
+    MSVCRT_flags[1] = MSVCRT__iob[1]._flag = MSVCRT__IOWRT;
+    MSVCRT_handles[2] = GetStdHandle(STD_ERROR_HANDLE);
+    MSVCRT_flags[2] = MSVCRT__iob[2]._flag = MSVCRT__IOWRT;
+
+    TRACE(":handles (%p)(%p)(%p)\n",MSVCRT_handles[0],
+        MSVCRT_handles[1],MSVCRT_handles[2]);
+
+    for (i = 0; i < 3; i++)
+    {
+        /* FILE structs for stdin/out/err are static and never deleted */
+        MSVCRT_files[i] = &MSVCRT__iob[i];
+        MSVCRT__iob[i]._file = i;
+        MSVCRT_tempfiles[i] = NULL;
+    }
+
+    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].hProcess = NULL;
+        POPEN_Files[i].fProcess = NULL;
+    }
 }
 
 /* free everything on process exit */
@@ -2342,3 +2408,325 @@
     va_end(valist);
     return res;
 }
+
+/*********************************************************************
+ *      POPEN_parseModeFlags (internal)
+ *
+ * Description
+ *     Parses the flags passed to the _popne/_wpopen function and
+ *     setups up the correct binary flags for usage via the
+ *     _popen/_wpopen commands
+ *
+ * FIXME: We are not currently catering for the "t"|"b" flags, rather
+ *        only allowing for the read and write
+ */
+static INT POPEN_parseModeFlags(const CHAR *szMode)
+{
+    INT nFlags = 0;
+    while (szMode && *szMode)
+    {
+        switch (*szMode)
+        {
+            case 'r':
+            {
+                if (nFlags & MSVCRT__IOWRT)
+                    TRACE(": _popen: Cannot set both read and write open
flags, ignoring read flag\n");
+                else
+                    nFlags = nFlags|MSVCRT__IOREAD;
+                break;
+            }
+            case 'w':
+            {
+                if (nFlags & MSVCRT__IOREAD)
+                    TRACE(": _popen: Cannot set both read and write open
flags, ignoring write flag\n");
+                else
+                    nFlags = nFlags|MSVCRT__IOWRT;
+                break;
+            }
+            case 'b':
+            case 't':
+            {
+                FIXME(": _popen: %c mode flag not implemented, ignoring\n",
*szMode);
+                break;
+            }
+            default:
+            {
+                TRACE(": _popen: unknown mode flag %c, ignoring\n", *szMode);
+                break;
+            }
+        }
+        szMode++;
+    }
+    return nFlags;
+}
+
+/*********************************************************************
+ *      POPEN_getOpenFileSlotPos (internal)
+ *
+ * Description
+ *     Return the first available file slot for storing file data
+ */
+static 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;
+        }
+    }
+    ERR("Cannot find any available _popen handles, increase the value of
POPEN_MAX_FILES\n");
+    return -1;
+}
+
+/*********************************************************************
+ *      POPEN_findFileSlotPos (internal)
+ *
+ * Description
+ *     Return the first available file slot for storing file data
+ */
+static 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;
+    }
+    TRACE("Invalid process handle, fProcess == %p\n", fProcess);
+    return -1;
+}
+
+/*********************************************************************
+ *      MSVCRT_popen (MSVCRT.@)
+ *
+ * Description
+ *
+ * Input
+ *     szCommand - The command to execute, this gets executed as
+ *                 "cmd.exe /C szCommand" under Windows
+ *     szMode    - The file mode to read/write from the process. This
+ *                 can be "r", "w" with optional "t"|"b" specifiers
+ *
+ * Output
+ *    Returns a valid MSVCRT_FILE to the opened stream for reading or
+ *    writing to the process. If the call failed, returns NULL
+ *
+ * FIXME: We are not currently catering for the "t"|"b" flags, rather
+ *        only allowing for the read and write
+ */
+MSVCRT_FILE *MSVCRT_popen(const CHAR *szCommand, const CHAR *szMode)
+{
+    MSVCRT_FILE *fProcess = NULL;
+    INT nFlags = 0;
+    CHAR *szExec = NULL;
+    PROCESS_INFORMATION piInfo;
+    STARTUPINFOA siStartup;
+    SECURITY_ATTRIBUTES saSecurity;
+    HANDLE hOrigIn, hOrigOut;
+    HANDLE hNewOutRead, hNewOutWrite, hNewOutReadDup;
+    HANDLE hNewInRead, hNewInWrite, hNewInWriteDup;
+    HANDLE hProcess;
+
+    TRACE("(szCommand == %s, szMode == %s)\n", debugstr_a(szCommand),
debugstr_a(szMode));
+
+    if (!szCommand || !szMode)
+    {
+        TRACE("Invalid wszCommand and/or wszMode specified\n");
+        return NULL;
+    }
+
+    nFlags = POPEN_parseModeFlags(szMode);
+    if (!(nFlags & (MSVCRT__IOREAD|MSVCRT__IOWRT)))
+    {
+        TRACE("No open mode flag r or w specified\n");
+        return NULL;
+    }
+
+    /* setup the security attributes for the child pipes we
+     * are about to create such that it inherts the handles
+     * of our main process
+     */
+    ZeroMemory(&saSecurity, sizeof(SECURITY_ATTRIBUTES));
+    saSecurity.nLength = sizeof(SECURITY_ATTRIBUTES);
+    saSecurity.bInheritHandle = TRUE;
+    saSecurity.lpSecurityDescriptor = NULL;
+
+    /* create duplicate streams for both reading and writing to
+     * the actual child process (potentially this allows us to
+     * open a process "rw", but the call only allows one of these
+     * flags at a time)
+     */
+    hOrigOut = GetStdHandle(STD_OUTPUT_HANDLE);
+    CREATE_PIPE(hNewOutRead, hNewOutWrite, saSecurity);
+    REDIRECT_STREAM(STD_OUTPUT_HANDLE, hNewOutWrite);
+    DUPLICATE_HANDLE(hNewOutRead, hNewOutReadDup, STD_OUTPUT_HANDLE, hOrigOut);
+
+    hOrigIn = GetStdHandle(STD_INPUT_HANDLE);
+    CREATE_PIPE(hNewInRead, hNewInWrite, saSecurity);
+    REDIRECT_STREAM(STD_INPUT_HANDLE, hNewInRead);
+    DUPLICATE_HANDLE(hNewInWrite, hNewInWriteDup, STD_INPUT_HANDLE, hOrigIn);
+
+    /* _popen/_wpopen executes the required command via the command
+     * processor, either command.com (Win 95/98) or cmd.exe (Win NT/2000/XP).
+     * wcmd acts as a replacement, so we will be launching it instead.
+     */
+    if (!(szExec = (CHAR *)malloc(strlen(POPEN_WCMD_EXEC)+strlen("
")+strlen(szCommand)+1)))
+    {
+        RESTORE_STREAM(STD_INPUT_HANDLE, hOrigIn);
+        RESTORE_STREAM(STD_OUTPUT_HANDLE, hOrigOut);
+        TRACE("Unable to allocate memory for process command line\n");
+        return NULL;
+    }
+    sprintf(szExec, "%s %s", POPEN_WCMD_EXEC, szCommand);
+
+    /* create the process with our attributes, using the
+    * redirected stream we just setup
+    */
+    ZeroMemory(&siStartup, sizeof(STARTUPINFOA));
+    siStartup.cb = sizeof(STARTUPINFOA);
+    if (CreateProcessA(NULL, szExec, NULL, NULL, TRUE, 0,
+                       NULL, NULL, &siStartup, &piInfo))
+    {
+        int nPos = -1, fd = -1;
+        DWORD dwLen;
+
+        /* wait for our newly created process to make it's
+        * appearance
+        */
+        WaitForSingleObject(piInfo.hProcess, INFINITE);
+        TRACE("Process created, hProcess = %p, hThread == %p\n",
+              piInfo.hProcess, piInfo.hThread);
+
+        /* create a MSVCRT_FILE * to the actual file stream and save
+        * this together with our process handle so we can close
+        * both when requested
+        */
+        hProcess = (nFlags & MSVCRT__IOREAD) ? hNewOutReadDup : hNewInWriteDup;
+        if ((fd = msvcrt_alloc_fd(hProcess, nFlags)) &&
+            (fProcess = msvcrt_alloc_fp(fd)) &&
+            ((nPos = POPEN_getOpenFileSlotPos()) != -1))
+        {
+            POPEN_Files[nPos].fProcess = fProcess;
+            POPEN_Files[nPos].hProcess = piInfo.hProcess;
+        }
+        else
+        {
+            if (fProcess)
+            {
+                MSVCRT_fclose(fProcess);
+                fProcess = NULL;
+            }
+            TerminateProcess(piInfo.hProcess, 0);
+            TRACE("Unable to create MSVCRT_FILE * from process handle\n");
+        }
+    }
+    else
+        TRACE("Unable to create child process, %s\n", szExec);
+
+    /* here we clean up everything we have allocated: restore our original
+     * STDOUT and STDIN to the state it was before we created the child pipes
+     * and free allocated memory for the command buffer
+     */
+    RESTORE_STREAM(STD_INPUT_HANDLE, hOrigIn);
+    RESTORE_STREAM(STD_OUTPUT_HANDLE, hOrigOut);
+    MSVCRT_free(szExec);
+
+    return fProcess;
+}
+
+/*********************************************************************
+ *      MSVCRT_wpopen (MSVCRT.@)
+ *
+ * Description
+ *    See MSVCRT_popen
+ */
+MSVCRT_FILE *MSVCRT_wpopen(const WCHAR *wszCommand, const WCHAR *wszMode)
+{
+    MSVCRT_FILE *fProcess = NULL;
+    CHAR *szCommand;
+    CHAR *szMode;
+    INT nLen;
+
+    TRACE("(wszCommand == %s, wszMode == %s)\n", debugstr_w(wszCommand),
debugstr_w(wszMode));
+
+    if (!wszCommand || !wszMode)
+    {
+        TRACE("Invalid wszCommand and/or wszMode specified\n");
+        return NULL;
+    }
+
+    LPCWSTR_TO_LPSTR(wszCommand, strlenW(wszCommand), szCommand, &nLen);
+    if (szCommand)
+    {
+        LPCWSTR_TO_LPSTR(wszMode, strlenW(wszMode), szMode, &nLen);
+        if (szMode)
+        {
+            fProcess = MSVCRT_popen(szCommand, szMode);
+            MSVCRT_free(szMode);
+        }
+        else
+            TRACE("Unable to allocate memory for Unicode mode conversion\n");
+        MSVCRT_free(szCommand);
+    }
+    else
+        TRACE("Unable to allocate memory for Unicode command conversion\n");
+
+    return fProcess;
+}
+
+/*********************************************************************
+ *      MSVCRT_pclose (MSVCRT.@)
+ *
+ * Description
+ *     Closes the file stream and process handle of the process
+ *     previously created by a clall to _popen/_wpopen
+ *
+ * Input
+ *     fProcess - The process handle and previously returned by
+ *                a clall to _popen/_wpopen
+ *
+ * Output
+ *     Returns 0 on success, -1 on error
+ */
+int MSVCRT_pclose(MSVCRT_FILE *fProcess)
+{
+    int nRet = -1;
+    int nPos;
+    DWORD dwCode;
+
+    TRACE("(fProcess == %p)\n", fProcess);
+
+    if ((nPos = POPEN_findFileSlotPos(fProcess)) != -1)
+    {
+        /* Close the file stream used previously to read from
+         * or write to the process
+         */
+        if ((nRet = MSVCRT_fclose(POPEN_Files[nPos].fProcess)))
+            TRACE("Unable to close the process file stream\n");
+
+        /* If the process is still running, try to terminate
+         * it. If it is not running, just do a trace on the
+         * actual exit code
+         */
+        if (GetExitCodeProcess(POPEN_Files[nPos].hProcess, &dwCode))
+        {
+            if (dwCode == STILL_ACTIVE)
+            {
+                if (TerminateProcess(POPEN_Files[nPos].hProcess, 0))
+                    TRACE("Unable to terminate the process\n");
+            }
+            else
+                TRACE("Process terminated with exitcode 0x%x\n", (INT)dwCode);
+        }
+        else
+            TRACE("No process exit code available\n");
+
+        /* Close the actual process handle */
+        if (!CloseHandle(POPEN_Files[nPos].hProcess))
+            TRACE("Unable to close the process handle\n");
+    }
+    return nRet;
+}
diff -aurN msvcrt-popen.orig/dlls/msvcrt/msvcrt.spec
msvcrt-popen.new/dlls/msvcrt/msvcrt.spec
--- msvcrt-popen.orig/dlls/msvcrt/msvcrt.spec	2002-11-02 10:43:54.000000000
+0000
+++ msvcrt-popen.new/dlls/msvcrt/msvcrt.spec	2002-11-01 20:52:10.000000000 +0000
@@ -401,11 +401,11 @@
 @ stub _outp #(long long)
 @ stub _outpd #(long long)
 @ stub _outpw #(long long)
-@ stub _pclose #(ptr)
+@ cdecl _pclose(ptr) MSVCRT_pclose
 @ extern _pctype MSVCRT__pctype
 @ stub _pgmptr
 @ stub _pipe #(ptr long long)
-@ stub _popen #(str str)
+@ cdecl _popen(str str) MSVCRT_popen
 @ cdecl _purecall() _purecall
 @ cdecl _putch(long) _putch
 @ cdecl _putenv(str) _putenv
@@ -535,7 +535,7 @@
 @ varargs _wopen(wstr long) _wopen
 @ stub _wperror #(wstr)
 @ stub _wpgmptr
-@ stub _wpopen #(wstr wstr)
+@ cdecl _wpopen(wstr wstr) MSVCRT_popen
 @ cdecl _wputenv(wstr) _wputenv
 @ cdecl _wremove(wstr) _wremove
 @ cdecl _wrename(wstr wstr) _wrename




More information about the wine-patches mailing list