442
+ − 1 /* I-connector utility
+ − 2 Copyright (C) 2000 Kirill M. Katsnelson
1346
+ − 3 Copyright (C) 2002, 2003 Ben Wing.
442
+ − 4
+ − 5 This file is part of XEmacs.
+ − 6
+ − 7 XEmacs is free software; you can redistribute it and/or modify it
+ − 8 under the terms of the GNU General Public License as published by the
+ − 9 Free Software Foundation; either version 2, or (at your option) any
+ − 10 later version.
+ − 11
+ − 12 XEmacs is distributed in the hope that it will be useful, but WITHOUT
+ − 13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ − 14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ − 15 for more details.
+ − 16
+ − 17 You should have received a copy of the GNU General Public License
+ − 18 along with XEmacs; see the file COPYING. If not, write to
+ − 19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ − 20 Boston, MA 02111-1307, USA. */
+ − 21
+ − 22 /* When run with an argument, i treats it as a command line, and pipes
+ − 23 command stdin, stdout and stderr to its own respective streams. How
+ − 24 silly it should sound, but windowed program in Win32 cannot do output
+ − 25 to the console from which it has been started, and should be run using
+ − 26 this utility.
+ − 27
+ − 28 This utility is for running [tx]emacs as part of make process so that
+ − 29 its output goes to the same console as the rest of the make output
+ − 30 does. It can be used also when xemacs should be run as a batch
+ − 31 command ina script, especially when its standart output should be
+ − 32 obtained programmatically. */
+ − 33
+ − 34 #include <windows.h>
+ − 35 #include <stdio.h>
+ − 36 #include <string.h>
+ − 37 #include <tchar.h>
+ − 38
+ − 39 typedef struct
+ − 40 {
+ − 41 HANDLE source;
+ − 42 HANDLE drain;
+ − 43 } I_connector;
+ − 44
+ − 45 /*
+ − 46 * Make new handle as that pointed to by PH but
+ − 47 * inheritable, substitute PH with it, and close the
+ − 48 * original one
+ − 49 */
+ − 50 static void
+ − 51 make_inheritable (HANDLE* ph)
+ − 52 {
+ − 53 HANDLE htmp;
+ − 54 DuplicateHandle (GetCurrentProcess(), *ph, GetCurrentProcess(), &htmp,
+ − 55 0, TRUE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
+ − 56 *ph = htmp;
+ − 57 }
+ − 58
+ − 59 /*
+ − 60 * Worker thread proc. Reads source, pumps into drain,
+ − 61 * till either clogs.
+ − 62 */
+ − 63 static DWORD CALLBACK
+ − 64 pump (LPVOID pv_i)
+ − 65 {
+ − 66 I_connector* pi = (I_connector*) pv_i;
+ − 67 BYTE buffer [256];
+ − 68 DWORD really_read, unused;
+ − 69
2426
+ − 70 /* I said:
+ − 71
+ − 72 [[ The docs for ReadFile claim:
1346
+ − 73
+ − 74 The ReadFile function returns when one of the following is true: a write
+ − 75 operation completes on the write end of the pipe, the number of bytes
+ − 76 requested has been read, or an error occurs.
+ − 77
+ − 78 But this is just not true. ReadFile never seems to block, and unless we
2426
+ − 79 Sleep(), we will chew up all the CPU time. --ben ]]
+ − 80
+ − 81 But in fact
+ − 82
+ − 83 [a] this does not appear to be the case any more [maybe a temporary
+ − 84 bug in some versions of Win2000?]
+ − 85 [b] it causes data lossage. [#### Why should this be? Seems extremely
+ − 86 fishy. I tried commenting out the calls to close the standard
+ − 87 handles at the bottom of the program, but it made no difference.
+ − 88 Would we need some kind of additional handshaking? If we get
+ − 89 data loss with the sleep, then we are a race condition waiting
+ − 90 to happen. */
442
+ − 91 while (ReadFile (pi->source, buffer, sizeof (buffer), &really_read, NULL) &&
+ − 92 WriteFile (pi->drain, buffer, really_read, &unused, NULL))
2426
+ − 93 /* Sleep (100) */ ;
442
+ − 94
+ − 95 return 0;
+ − 96 }
+ − 97
+ − 98 /*
+ − 99 * Launch a pump for the given I-connector
+ − 100 */
+ − 101 static void
+ − 102 start_pump (I_connector* pi)
+ − 103 {
+ − 104 DWORD unused;
+ − 105 HANDLE h_thread = CreateThread (NULL, 0, pump, (void*)pi, 0, &unused);
+ − 106 CloseHandle (h_thread);
+ − 107 }
+ − 108
826
+ − 109 static HANDLE external_event;
+ − 110
+ − 111 static BOOL
+ − 112 ctrl_c_handler (unsigned long type)
+ − 113 {
+ − 114 SetEvent (external_event);
+ − 115 return FALSE;
+ − 116 }
+ − 117
+ − 118 /* Skip over the executable name in the given command line. Correctly
+ − 119 handles quotes in the name. Return NULL upon error. If
+ − 120 REQUIRE_FOLLOWING is non-zero, it's an error if no argument follows the
+ − 121 executable name. */
+ − 122
442
+ − 123 static LPTSTR
826
+ − 124 skip_executable_name (LPTSTR cl, int require_following)
442
+ − 125 {
+ − 126 int ix;
+ − 127
+ − 128 while (1)
+ − 129 {
+ − 130 ix = _tcscspn (cl, _T(" \t\""));
+ − 131 if (cl[ix] == '\"')
+ − 132 {
+ − 133 cl = _tcschr (cl + ix + 1, '\"');
+ − 134 if (cl == NULL)
+ − 135 return NULL; /* Unmatched quote */
+ − 136 cl++;
+ − 137 }
+ − 138 else
+ − 139 {
+ − 140 cl += ix;
+ − 141 cl += _tcsspn (cl, _T(" \t"));
826
+ − 142 if (!require_following)
+ − 143 return cl;
442
+ − 144 return *cl ? cl : NULL;
+ − 145 }
+ − 146 }
+ − 147 }
+ − 148
+ − 149 /*
+ − 150 * Brew coffee and bring snickers
+ − 151 */
+ − 152 void
+ − 153 usage (void)
+ − 154 {
+ − 155 fprintf (stderr,
+ − 156 "\n"
+ − 157 "usage: i command\n"
+ − 158 "i executes the command and reroutes its standard handles to the calling\n"
+ − 159 "console. Good for seeing output of GUI programs that use standard output."
+ − 160 "\n");
+ − 161 }
+ − 162
+ − 163 int
+ − 164 main (void)
+ − 165 {
+ − 166 STARTUPINFO si;
+ − 167 PROCESS_INFORMATION pi;
+ − 168 I_connector I_in, I_out, I_err;
+ − 169 DWORD exit_code;
826
+ − 170 LPTSTR command = skip_executable_name (GetCommandLine (), 1);
+ − 171
442
+ − 172 if (command == NULL)
+ − 173 {
+ − 174 usage ();
+ − 175 return 1;
+ − 176 }
+ − 177
+ − 178 ZeroMemory (&si, sizeof (si));
+ − 179 si.dwFlags = STARTF_USESTDHANDLES;
+ − 180
+ − 181 I_in.source = GetStdHandle (STD_INPUT_HANDLE);
+ − 182 CreatePipe (&si.hStdInput, &I_in.drain, NULL, 0);
+ − 183 make_inheritable (&si.hStdInput);
+ − 184
+ − 185 I_out.drain = GetStdHandle (STD_OUTPUT_HANDLE);
+ − 186 CreatePipe (&I_out.source, &si.hStdOutput, NULL, 0);
+ − 187 make_inheritable (&si.hStdOutput);
+ − 188
+ − 189 I_err.drain = GetStdHandle (STD_ERROR_HANDLE);
+ − 190 CreatePipe (&I_err.source, &si.hStdError, NULL, 0);
+ − 191 make_inheritable (&si.hStdError);
+ − 192
826
+ − 193 {
+ − 194 SECURITY_ATTRIBUTES sa;
+ − 195 LPTSTR new_command =
+ − 196 (LPTSTR) malloc (666 + sizeof (TCHAR) * _tcslen (command));
+ − 197 LPTSTR past_exe;
+ − 198
+ − 199 if (!new_command)
+ − 200 {
+ − 201 _ftprintf (stderr, _T ("Out of memory when launching `%s'\n"),
+ − 202 command);
+ − 203 return 2;
+ − 204 }
+ − 205
+ − 206 past_exe = skip_executable_name (command, 0);
+ − 207 if (!past_exe)
+ − 208 {
+ − 209 usage ();
+ − 210 return 1;
+ − 211 }
+ − 212
+ − 213 /* Since XEmacs isn't a console application, it can't easily be
+ − 214 terminated using ^C. Therefore, we set up a communication path with
+ − 215 it so that when a ^C is sent to us (using GenerateConsoleCtrlEvent),
+ − 216 we in turn signals it to commit suicide. (This is cleaner than using
+ − 217 TerminateProcess()). This makes (e.g.) the "Stop Build" command
+ − 218 from VC++ correctly terminate XEmacs.
+ − 219
+ − 220 #### This will cause problems if i.exe is used for commands other
+ − 221 than XEmacs. We need to make behavior this a command-line
+ − 222 option. */
442
+ − 223
826
+ − 224 /* Create the event as inheritable so that we can use it to communicate
+ − 225 with the child process */
+ − 226 sa.nLength = sizeof (sa);
+ − 227 sa.bInheritHandle = TRUE;
+ − 228 sa.lpSecurityDescriptor = NULL;
+ − 229 external_event = CreateEvent (&sa, FALSE, FALSE, NULL);
+ − 230 if (!external_event)
+ − 231 {
+ − 232 _ftprintf (stderr, _T ("Error %d creating signal event for `%s'\n"),
+ − 233 GetLastError (), command);
+ − 234 return 2;
+ − 235 }
+ − 236
+ − 237 SetConsoleCtrlHandler ((PHANDLER_ROUTINE) ctrl_c_handler, TRUE);
+ − 238 _tcsncpy (new_command, command, past_exe - command);
+ − 239 _stprintf (new_command + (past_exe - command),
+ − 240 /* start with space in case no args past command name */
+ − 241 " -mswindows-termination-handle %d ", (long) external_event);
+ − 242 _tcscat (new_command, past_exe);
+ − 243
+ − 244 if (CreateProcess (NULL, new_command, NULL, NULL, TRUE, 0,
+ − 245 NULL, NULL, &si, &pi) == 0)
+ − 246 {
+ − 247 _ftprintf (stderr, _T("Error %d launching `%s'\n"),
+ − 248 GetLastError (), command);
+ − 249 return 2;
+ − 250 }
+ − 251
+ − 252 CloseHandle (pi.hThread);
+ − 253 }
+ − 254
442
+ − 255
+ − 256 /* Start pump in each I-connector */
+ − 257 start_pump (&I_in);
+ − 258 start_pump (&I_out);
+ − 259 start_pump (&I_err);
+ − 260
+ − 261 /* Wait for the process to complete */
+ − 262 WaitForSingleObject (pi.hProcess, INFINITE);
+ − 263 GetExitCodeProcess (pi.hProcess, &exit_code);
+ − 264 CloseHandle (pi.hProcess);
+ − 265
+ − 266 /* Make pump threads eventually die out. Looks rude, I agree */
+ − 267 CloseHandle (GetStdHandle (STD_INPUT_HANDLE));
+ − 268 CloseHandle (GetStdHandle (STD_OUTPUT_HANDLE));
+ − 269 CloseHandle (GetStdHandle (STD_ERROR_HANDLE));
+ − 270
+ − 271 return exit_code;
+ − 272 }