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