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