--- ScriptHappy.cpp.cvs Sat Mar 16 06:35:54 2002 +++ ScriptHappy.cpp Sun Mar 17 05:55:23 2002 @@ -38,6 +38,13 @@ #include "ut_Script.h" +#ifdef WIN32 +#define SETENV_MISSING +#include +HANDLE hnd_popen(char *command); +void preProcessLine(char *data, int len, int bufSize); +#endif + /*! * This plugin doesn't actually do anything... well, ok maybe it does * This plugin exports 2 editmethods that other plugins can call @@ -104,6 +111,7 @@ { mErrorMsg = "ScriptHappy not installed" ; UT_DEBUGMSG(("DOM: scriptHappy_Capture not installed??\n")); + return UT_ERROR ; } } @@ -222,7 +230,7 @@ UT_String var ( key ) ; var += "=" ; var += value ; - putenv ( var ) ; + putenv ( var.c_str() ) ; #else setenv ( key, value, 1 ) ; #endif @@ -345,8 +353,13 @@ UT_DEBUGMSG(("DOM: trying to capture '%s'\n",command)); // open up a pipe for reading +#ifdef WIN32 + HANDLE hPipe = hnd_popen( command ); + if (hPipe == INVALID_HANDLE_VALUE) +#else FILE * pipe = popen ( command, "r" ) ; if ( !pipe ) +#endif { UT_DEBUGMSG(("Couldn't execute pipe\n")); DELETEPV ( command ); @@ -354,16 +367,29 @@ } DELETEPV ( command ) ; +#ifdef WIN32 + char script_output[ 1024 ]; + DWORD bytesRead; +#else char script_output [ 64 ] = "" ; // prime the buffer - not sure why this is needed, it just doesn't // work without this pre-read fgets ( script_output, sizeof(script_output), pipe ) ; +#endif // suck in our data +#ifdef WIN32 + while ( ReadFile(hPipe, script_output, sizeof(script_output)-1, &bytesRead, NULL) && bytesRead) +#else while ( fgets ( script_output, sizeof ( script_output ), pipe ) != NULL ) +#endif { int howMany = 0 ; +#ifdef WIN32 + // note we require sizeof(script_output) to <= MAX_SIGNED_INT, and assume we read at least 1 less bytes + preProcessLine(script_output, (int)bytesRead, sizeof(script_output)); +#endif UT_UCSChar * script_ucsdata = ScriptHappy_asciiToUcs ( script_output, howMany ) ; if ( howMany ) view->cmdCharInsert ( script_ucsdata, howMany ); @@ -371,7 +397,11 @@ } // close the pipe +#ifdef WIN32 + CloseHandle(hPipe); +#else pclose ( pipe ) ; +#endif return true ; } @@ -439,3 +469,172 @@ UT_ScriptLibrary::instance().registerScript ( mSniffer ) ; UT_DEBUGMSG(("DOM: registered script!\n")); } + + +// Windows utility functions +#ifdef WIN32 + +// spawn specified command and inherit handles [stdout] +static bool CreateChildProcess(char * appName, char *cmdline, HANDLE hChildStdIn, HANDLE hChildStdOut, HANDLE hChildStdErr, WORD showWnd=0) +{ + PROCESS_INFORMATION procInfo; + STARTUPINFO startInfo; + + //initialize structures used to return info + ZeroMemory( &procInfo, sizeof(PROCESS_INFORMATION) ); + ZeroMemory( &startInfo, sizeof(STARTUPINFO) ); + startInfo.cb = sizeof(STARTUPINFO); + startInfo.dwFlags = STARTF_USESTDHANDLES; + if (showWnd) startInfo.dwFlags |= STARTF_USESHOWWINDOW; + startInfo.wShowWindow = showWnd; /* eg SW_HIDE */ + + startInfo.hStdOutput = hChildStdOut; + startInfo.hStdInput = hChildStdIn; + startInfo.hStdError = hChildStdErr; + + // Create the child process. + return (CreateProcess( + appName, // application module to execute + cmdline, // command line + NULL, // process security attributes + NULL, // primary thread security attributes + TRUE, // handles are inherited *** + 0, // creation flags + NULL, // use parent's environment + NULL, // use parent's current directory + &startInfo, // STARTUPINFO pointer + &procInfo // receives PROCESS_INFORMATION + ) != 0); +} + +// Win32 popen-ish read mode for spawned process, ie returns HANDLE to child's stdout +// Only those Windows programs that actually write to standard output will work as expected. +// based on MS Win32 docs +HANDLE hnd_popen(char *command) +{ + HANDLE hChildStdoutRd, hChildStdoutWr, hChildStdoutRdDup, hSaveStdout; + + SECURITY_ATTRIBUTES saAttr; + bool fSuccess; + + // Set the bInheritHandle flag so pipe handles are inherited. + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + + // The steps for redirecting child process's STDOUT: + // 1. Save current STDOUT, to be restored later. + // 2. Create anonymous pipe to be STDOUT for child process. + // 3. Set STDOUT of the parent process to be write handle to + // the pipe, so it is inherited by the child process. + // 4. Create a noninheritable duplicate of the read handle and + // close the inheritable read handle. + + + // Save the handle to the current STDOUT. + hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE); + + // Create a pipe for the child process's STDOUT. + if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) + { + UT_DEBUGMSG(("KJD: Stdout pipe creation failed\n")); + return INVALID_HANDLE_VALUE; + } + + // Set a write handle to the pipe to be STDOUT. + if (! SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr)) + { + UT_DEBUGMSG(("KJD: Redirecting STDOUT failed\n")); + CloseHandle(hChildStdoutRd); + CloseHandle(hChildStdoutWr); + return INVALID_HANDLE_VALUE; + } + + // Create noninheritable read handle and close the inheritable read handle. + if (DuplicateHandle(GetCurrentProcess(), hChildStdoutRd, + GetCurrentProcess(), &hChildStdoutRdDup , 0, + FALSE, DUPLICATE_SAME_ACCESS) == 0) + { + UT_DEBUGMSG(("KJD: DuplicateHandle failed\n")); + if (! SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout)) + { + UT_DEBUGMSG(("KJD: Restoring Stdout failed\n")); + } + CloseHandle(hChildStdoutRd); + CloseHandle(hChildStdoutWr); + return INVALID_HANDLE_VALUE; + } + CloseHandle(hChildStdoutRd); + + // Now create the child process. + // TODO: popen runs /bin/sh 'command', perhaps we should do something similar + // or at least change it to call associated program, eg active perl for *.pl files + // currently only *.exe, *.com, & *.bat work + if (! (fSuccess = CreateChildProcess(NULL, command, NULL, hChildStdoutWr, NULL) )) + { + UT_DEBUGMSG(("KJD: Create process failed\n")); + } + + // After process creation, restore the saved STDOUT. + if (! SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout)) + { + UT_DEBUGMSG(("KJD: Restoring Stdout failed. (postSpawn)\n")); + fSuccess = false; + } + + CloseHandle(hChildStdoutWr); + if (!fSuccess) + { + CloseHandle(hChildStdoutRdDup); + return INVALID_HANDLE_VALUE; + } + else + { + return hChildStdoutRdDup; + } +} + + +// converts \r\n or \n\r to just \n +// ensure's '\0' terminated (if adjusted_len==bufSize then last character lost) +// convert any OEM data to something displayable (instead of blocks[]) +void preProcessLine(char *data, int len, int bufSize) +{ + if (data==NULL) return; + char *buffer = new char[bufSize]; + if (buffer == NULL) return; + + int j = 0; + for (register int i = 0; i < len; i++, j++) + { + register char value = data[i]; + switch (value) + { + case '\n': + case '\r': + { + register char lookingfor = (value == '\r')?'\n':'\r'; + if ((i+1 < len) && (data[i+1] == lookingfor)) + i++; // skip next character + + buffer[j] = '\n'; + break; + } + default: + { + buffer[j] = value; + break; + } + } + } + + len = (j < bufSize)?j:j-1; + buffer[len] = '\0'; // ensure '\0' terminated + memcpy(data, buffer, len+1); + delete[] buffer; + + // a better approach of course would be to map to unicode equivalent during conversion to UCS + OemToCharA(data, data); +} + +#endif /* WIN32 */