dlls/msvcrt: process.c (msvcrt-popen-1)

Jaco Greeff jaco at puxedo.org
Fri Nov 1 03:59:58 CST 2002


Hi,

I would appreciate comments on the following implementation of 
_popen/_wpopen/_pclose. For me there is one uncertainty and one issue:

1. Calling the popen function returns a FILE* which needs to be 
converted to a MSVCRT_FILE*. The apprach taken here is potentially not 
the best route but the only one I could think of. Is it suffiecient?

2. I only allow for one process to be open at any single time. As a 
first pass, it will problably do the job, I just need to find the most 
efficient approach to store all the open handles to allow the functions 
to do the concersions from MSVCRT_FILE to FILE as used in _pclose.

Here it is as a first pass, please be gentle but keep the comments 
comming...

License:
LGPL

Changelog:
* dlls/msvcrt/process.c: Jaco Greeff <jaco at puxedo.org>
- Implementation of the _popen/_wpopen/_pclose functions

diff -aurN msvcrt-popen.orig/dlls/msvcrt/msvcrt.spec 
msvcrt-popen.new/dlls/msvcrt/msvcrt.spec
--- msvcrt-popen.orig/dlls/msvcrt/msvcrt.spec	Fri Nov  1 11:44:09 2002
+++ msvcrt-popen.new/dlls/msvcrt/msvcrt.spec	Fri Nov  1 08:23:27 2002
@@ -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
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 11:44:10 2002
+++ msvcrt-popen.new/dlls/msvcrt/process.c	Fri Nov  1 11:57:41 2002
@@ -1,10 +1,11 @@
  /*
- * msvcrt.dll spawn/exec functions
+ * msvcrt.dll spawn/exec/popen functions
   *
   * Copyright 1996,1998 Marcus Meissner
   * 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
@@ -28,6 +29,7 @@
  #include "config.h"

  #include <stdarg.h>
+#include <stdio.h>

  #include "msvcrt.h"
  #include "msvcrt/errno.h"
@@ -36,10 +38,45 @@
  #include "msvcrt/stdlib.h"
  #include "msvcrt/string.h"

+#include "msvcrt/stdio.h"
+#include "wine/unicode.h"
+
  #include "wine/debug.h"

  WINE_DEFAULT_DEBUG_CHANNEL(msvcrt);

+#define POPEN_FLAG_READ     0x0001
+#define POPEN_FLAG_WRITE    0x0002
+#define POPEN_FLAG_TEXT     0x0004
+#define POPEN_FLAG_BINARY   0x0008
+
+#define POPEN_MAX_FILES     10
+#define POPEN_WCMD_EXEC     "wcmd /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;
+
+typedef struct
+{
+    FILE        *fSysProcess;
+    MSVCRT_FILE *fProcess;
+    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;
+
  /* FIXME: Check file extensions for app to run */
  static const unsigned int EXE = 'e' << 16 | 'x' << 8 | 'e';
  static const unsigned int BAT = 'b' << 16 | 'a' << 8 | 't';
@@ -469,4 +506,180 @@
      MSVCRT__set_errno(err);
      return err;
    }
+}
+
+/*********************************************************************
+ *      POPEN_parseModeFlags (internal)
+ *
+ * Parses the flags passed to the _popne/_wpopen function and sets up
+ * the correct binary flags for usage via these commands
+ */
+INT POPEN_parseModeFlags(const CHAR *szMode)
+{
+    INT nFlags = 0;
+    while (szMode && *szMode)
+    {
+        switch (*szMode)
+        {
+            case 'r':
+            {
+                if (nFlags & POPEN_FLAG_WRITE)
+                    WARN(": _popen: Cannot set both read and write open 
flags, ignoring read flag\n");
+                else
+                    nFlags = nFlags & POPEN_FLAG_READ;
+                break;
+            }
+            case 'w':
+            {
+                if (nFlags & POPEN_FLAG_READ)
+                    WARN(": _popen: Cannot set both read and write open 
flags, ignoring write flag\n");
+                else
+                    nFlags = nFlags & POPEN_FLAG_WRITE;
+                break;
+            }
+            case 'b':
+            case 't':
+            {
+                FIXME(": _popen: %c mode flag not implemented, 
ignoring\n", *szMode);
+                break;
+            }
+            default:
+            {
+                WARN(": _popen: unknown mode flag %c, ignoring\n", 
*szMode);
+                break;
+            }
+        }
+    }
+    return nFlags;
+}
+
+/*********************************************************************
+ *      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;
+
+    TRACE("(szCommand == %s, szMode == %s)\n", debugstr_a(szCommand), 
debugstr_a(szMode));
+
+    if (!szCommand || !szMode)
+        return NULL;
+
+    nFlags = POPEN_parseModeFlags(szMode);
+    if (!(nFlags & (POPEN_FLAG_READ|POPEN_FLAG_WRITE)))
+    {
+        ERR("No open mode flag r or w specified\n");
+        return NULL;
+    }
+
+    /* _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, launch it instead...
+     */
+    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)
+        {
+            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")))
+            {
+                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;
+            }
+            else
+            {
+                if (pPOPEN_File)
+                {
+                    MSVCRT_free(pPOPEN_File);
+                    pPOPEN_File = NULL;
+                }
+                ERR("Execution of %s failed\n", debugstr_a(szExec));
+            }
+        }
+        else
+            FIXME("Attempt to use multiple _popen streams, failing\n");
+        MSVCRT_free(szExec);
+    }
+
+    return fProcess;
+}
+
+/*********************************************************************
+ *      MSVCRT_wpopen (MSVCRT.@)
+ */
+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)
+        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);
+        }
+        MSVCRT_free(szCommand);
+    }
+
+    return fProcess;
+}
+
+/*********************************************************************
+ *      MSVCRT_pclose (MSVCRT.@)
+ */
+int MSVCRT_pclose(MSVCRT_FILE *fProcess)
+{
+    int nRet = -1;
+
+    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)
+    {
+        nRet = pclose(pPOPEN_File->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;
+    }
+    else
+        ERR("%p is not a valid process handle\n", fProcess);
+
+    return nRet;
  }




More information about the wine-patches mailing list