w9xpopen.c 3.56 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 * w9xpopen.c
 *
 * Serves as an intermediate stub Win32 console application to
 * avoid a hanging pipe when redirecting 16-bit console based
 * programs (including MS-DOS console based programs and batch
 * files) on Window 95 and Windows 98.
 *
 * This program is to be launched with redirected standard
 * handles. It will launch the command line specified 16-bit
 * console based application in the same console, forwarding
 * it's own redirected standard handles to the 16-bit child.

 * AKA solution to the problem described in KB: Q150956.
 */    

17
#define WIN32_LEAN_AND_MEAN
18
#include <windows.h>
Mark Hammond's avatar
Mark Hammond committed
19
#include <stdio.h>
20
#include <stdlib.h>  /* for malloc and its friends */
21 22

const char *usage =
Neal Norwitz's avatar
Neal Norwitz committed
23
"This program is used by Python's os.popen function\n"
24
"to work around a limitation in Windows 95/98.  It is\n"
Neal Norwitz's avatar
Neal Norwitz committed
25
"not designed to be used as a stand-alone program.";
26 27 28 29 30 31

int main(int argc, char *argv[])
{
    BOOL bRet;
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
32
    DWORD exit_code=0;
Mark Hammond's avatar
Mark Hammond committed
33 34 35
    int cmdlen = 0;
    int i;
    char *cmdline, *cmdlinefill;
36

Mark Hammond's avatar
Mark Hammond committed
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
    if (argc < 2) {
        if (GetFileType(GetStdHandle(STD_INPUT_HANDLE))==FILE_TYPE_CHAR)
            /* Attached to a console, and therefore not executed by Python
               Display a message box for the inquisitive user
            */
            MessageBox(NULL, usage, argv[0], MB_OK);
        else {
            /* Eeek - executed by Python, but args are screwed!
               Write an error message to stdout so there is at
               least some clue for the end user when it appears
               in their output.
               A message box would be hidden and blocks the app.
             */
            fprintf(stdout, "Internal popen error - no args specified\n%s\n", usage);
        }
52 53
        return 1;
    }
Mark Hammond's avatar
Mark Hammond committed
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
    /* Build up the command-line from the args.
       Args with a space are quoted, existing quotes are escaped.
       To keep things simple calculating the buffer size, we assume
       every character is a quote - ie, we allocate double what we need
       in the worst case.  As this is only double the command line passed
       to us, there is a good chance this is reasonably small, so the total 
       allocation will almost always be < 512 bytes.
    */
    for (i=1;i<argc;i++)
        cmdlen += strlen(argv[i])*2 + 3; /* one space, maybe 2 quotes */
    cmdline = cmdlinefill = (char *)malloc(cmdlen+1);
    if (cmdline == NULL)
        return -1;
    for (i=1;i<argc;i++) {
        const char *arglook;
        int bQuote = strchr(argv[i], ' ') != NULL;
        if (bQuote)
            *cmdlinefill++ = '"';
        /* escape quotes */
        for (arglook=argv[i];*arglook;arglook++) {
            if (*arglook=='"')
                *cmdlinefill++ = '\\';
            *cmdlinefill++ = *arglook;
        }
        if (bQuote)
            *cmdlinefill++ = '"';
        *cmdlinefill++ = ' ';
    }
    *cmdlinefill = '\0';
83 84 85 86 87 88 89 90 91 92

    /* Make child process use this app's standard files. */
    ZeroMemory(&si, sizeof si);
    si.cb = sizeof si;
    si.dwFlags = STARTF_USESTDHANDLES;
    si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
    si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
    si.hStdError = GetStdHandle(STD_ERROR_HANDLE);

    bRet = CreateProcess(
Mark Hammond's avatar
Mark Hammond committed
93
        NULL, cmdline,
94 95 96 97 98 99
        NULL, NULL,
        TRUE, 0,
        NULL, NULL,
        &si, &pi
        );

Mark Hammond's avatar
Mark Hammond committed
100 101
    free(cmdline);

102
    if (bRet) {
103 104 105
        if (WaitForSingleObject(pi.hProcess, INFINITE) != WAIT_FAILED) {
	    GetExitCodeProcess(pi.hProcess, &exit_code);
	}
106 107
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
108
        return exit_code;
109 110 111 112
    }

    return 1;
}