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