406
|
1 /* I-connector utility
|
|
2 Copyright (C) 2000 Kirill M. Katsnelson
|
|
3
|
|
4 This file is part of XEmacs.
|
|
5
|
|
6 XEmacs is free software; you can redistribute it and/or modify it
|
|
7 under the terms of the GNU General Public License as published by the
|
|
8 Free Software Foundation; either version 2, or (at your option) any
|
|
9 later version.
|
|
10
|
|
11 XEmacs is distributed in the hope that it will be useful, but WITHOUT
|
|
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
14 for more details.
|
|
15
|
|
16 You should have received a copy of the GNU General Public License
|
|
17 along with XEmacs; see the file COPYING. If not, write to
|
|
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
19 Boston, MA 02111-1307, USA. */
|
|
20
|
|
21 /* When run with an argument, i treats it as a command line, and pipes
|
|
22 command stdin, stdout and stderr to its own respective streams. How
|
|
23 silly it should sound, but windowed program in Win32 cannot do output
|
|
24 to the console from which it has been started, and should be run using
|
|
25 this utility.
|
|
26
|
|
27 This utility is for running [tx]emacs as part of make process so that
|
|
28 its output goes to the same console as the rest of the make output
|
|
29 does. It can be used also when xemacs should be run as a batch
|
|
30 command ina script, especially when its standart output should be
|
|
31 obtained programmatically. */
|
|
32
|
|
33 #include <windows.h>
|
|
34 #include <stdio.h>
|
|
35 #include <string.h>
|
|
36 #include <tchar.h>
|
|
37
|
|
38 typedef struct
|
|
39 {
|
|
40 HANDLE source;
|
|
41 HANDLE drain;
|
|
42 } I_connector;
|
|
43
|
|
44 /*
|
|
45 * Make new handle as that pointed to by PH but
|
|
46 * inheritable, substitute PH with it, and close the
|
|
47 * original one
|
|
48 */
|
|
49 static void
|
|
50 make_inheritable (HANDLE* ph)
|
|
51 {
|
|
52 HANDLE htmp;
|
|
53 DuplicateHandle (GetCurrentProcess(), *ph, GetCurrentProcess(), &htmp,
|
|
54 0, TRUE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
|
|
55 *ph = htmp;
|
|
56 }
|
|
57
|
|
58 /*
|
|
59 * Worker thread proc. Reads source, pumps into drain,
|
|
60 * till either clogs.
|
|
61 */
|
|
62 static DWORD CALLBACK
|
|
63 pump (LPVOID pv_i)
|
|
64 {
|
|
65 I_connector* pi = (I_connector*) pv_i;
|
|
66 BYTE buffer [256];
|
|
67 DWORD really_read, unused;
|
|
68
|
|
69 while (ReadFile (pi->source, buffer, sizeof (buffer), &really_read, NULL) &&
|
|
70 WriteFile (pi->drain, buffer, really_read, &unused, NULL))
|
|
71 ;
|
|
72
|
|
73 return 0;
|
|
74 }
|
|
75
|
|
76 /*
|
|
77 * Launch a pump for the given I-connector
|
|
78 */
|
|
79 static void
|
|
80 start_pump (I_connector* pi)
|
|
81 {
|
|
82 DWORD unused;
|
|
83 HANDLE h_thread = CreateThread (NULL, 0, pump, (void*)pi, 0, &unused);
|
|
84 CloseHandle (h_thread);
|
|
85 }
|
|
86
|
|
87 /*
|
|
88 * Get command line, skip over the executable name, return the rest.
|
|
89 */
|
|
90 static LPTSTR
|
|
91 get_command (void)
|
|
92 {
|
|
93 LPTSTR q, ws, cl = GetCommandLine ();
|
|
94 int ix;
|
|
95
|
|
96 while (1)
|
|
97 {
|
|
98 ix = _tcscspn (cl, _T(" \t\""));
|
|
99 if (cl[ix] == '\"')
|
|
100 {
|
|
101 cl = _tcschr (cl + ix + 1, '\"');
|
|
102 if (cl == NULL)
|
|
103 return NULL; /* Unmatched quote */
|
|
104 cl++;
|
|
105 }
|
|
106 else
|
|
107 {
|
|
108 cl += ix;
|
|
109 cl += _tcsspn (cl, _T(" \t"));
|
|
110 return *cl ? cl : NULL;
|
|
111 }
|
|
112 }
|
|
113 }
|
|
114
|
|
115 /*
|
|
116 * Brew coffee and bring snickers
|
|
117 */
|
|
118 void
|
|
119 usage (void)
|
|
120 {
|
|
121 fprintf (stderr,
|
|
122 "\n"
|
|
123 "usage: i command\n"
|
|
124 "i executes the command and reroutes its standard handles to the calling\n"
|
|
125 "console. Good for seeing output of GUI programs that use standard output."
|
|
126 "\n");
|
|
127 }
|
|
128
|
|
129 int
|
|
130 main (void)
|
|
131 {
|
|
132 STARTUPINFO si;
|
|
133 PROCESS_INFORMATION pi;
|
|
134 I_connector I_in, I_out, I_err;
|
|
135 DWORD exit_code;
|
|
136
|
|
137 LPTSTR command = get_command ();
|
|
138 if (command == NULL)
|
|
139 {
|
|
140 usage ();
|
|
141 return 1;
|
|
142 }
|
|
143
|
|
144 ZeroMemory (&si, sizeof (si));
|
|
145 si.dwFlags = STARTF_USESTDHANDLES;
|
|
146
|
|
147 I_in.source = GetStdHandle (STD_INPUT_HANDLE);
|
|
148 CreatePipe (&si.hStdInput, &I_in.drain, NULL, 0);
|
|
149 make_inheritable (&si.hStdInput);
|
|
150
|
|
151 I_out.drain = GetStdHandle (STD_OUTPUT_HANDLE);
|
|
152 CreatePipe (&I_out.source, &si.hStdOutput, NULL, 0);
|
|
153 make_inheritable (&si.hStdOutput);
|
|
154
|
|
155 I_err.drain = GetStdHandle (STD_ERROR_HANDLE);
|
|
156 CreatePipe (&I_err.source, &si.hStdError, NULL, 0);
|
|
157 make_inheritable (&si.hStdError);
|
|
158
|
|
159 if (CreateProcess (NULL, command, NULL, NULL, TRUE, 0,
|
|
160 NULL, NULL, &si, &pi) == 0)
|
|
161 {
|
|
162 _ftprintf (stderr, _T("Error %d launching `%s'\n"),
|
|
163 GetLastError (), command);
|
|
164 return 2;
|
|
165 }
|
|
166
|
|
167 CloseHandle (pi.hThread);
|
|
168
|
|
169 /* Start pump in each I-connector */
|
|
170 start_pump (&I_in);
|
|
171 start_pump (&I_out);
|
|
172 start_pump (&I_err);
|
|
173
|
|
174 /* Wait for the process to complete */
|
|
175 WaitForSingleObject (pi.hProcess, INFINITE);
|
|
176 GetExitCodeProcess (pi.hProcess, &exit_code);
|
|
177 CloseHandle (pi.hProcess);
|
|
178
|
|
179 /* Make pump threads eventually die out. Looks rude, I agree */
|
|
180 CloseHandle (GetStdHandle (STD_INPUT_HANDLE));
|
|
181 CloseHandle (GetStdHandle (STD_OUTPUT_HANDLE));
|
|
182 CloseHandle (GetStdHandle (STD_ERROR_HANDLE));
|
|
183
|
|
184 return exit_code;
|
|
185 }
|