This commit was generated by cvs2svn to compensate for changes in r5086,
[chise/xemacs-chise.git.1] / lib-src / i.c
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 }