Mercurial > hg > xemacs-beta
diff lib-src/i.c @ 406:b8cc9ab3f761 r21-2-33
Import from CVS: tag r21-2-33
author | cvs |
---|---|
date | Mon, 13 Aug 2007 11:17:09 +0200 |
parents | |
children | 501cfd01ee6d |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib-src/i.c Mon Aug 13 11:17:09 2007 +0200 @@ -0,0 +1,185 @@ +/* I-connector utility + Copyright (C) 2000 Kirill M. Katsnelson + +This file is part of XEmacs. + +XEmacs is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +XEmacs is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with XEmacs; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* When run with an argument, i treats it as a command line, and pipes +command stdin, stdout and stderr to its own respective streams. How +silly it should sound, but windowed program in Win32 cannot do output +to the console from which it has been started, and should be run using +this utility. + +This utility is for running [tx]emacs as part of make process so that +its output goes to the same console as the rest of the make output +does. It can be used also when xemacs should be run as a batch +command ina script, especially when its standart output should be +obtained programmatically. */ + +#include <windows.h> +#include <stdio.h> +#include <string.h> +#include <tchar.h> + +typedef struct +{ + HANDLE source; + HANDLE drain; +} I_connector; + +/* + * Make new handle as that pointed to by PH but + * inheritable, substitute PH with it, and close the + * original one + */ +static void +make_inheritable (HANDLE* ph) +{ + HANDLE htmp; + DuplicateHandle (GetCurrentProcess(), *ph, GetCurrentProcess(), &htmp, + 0, TRUE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS); + *ph = htmp; +} + +/* + * Worker thread proc. Reads source, pumps into drain, + * till either clogs. + */ +static DWORD CALLBACK +pump (LPVOID pv_i) +{ + I_connector* pi = (I_connector*) pv_i; + BYTE buffer [256]; + DWORD really_read, unused; + + while (ReadFile (pi->source, buffer, sizeof (buffer), &really_read, NULL) && + WriteFile (pi->drain, buffer, really_read, &unused, NULL)) + ; + + return 0; +} + +/* + * Launch a pump for the given I-connector + */ +static void +start_pump (I_connector* pi) +{ + DWORD unused; + HANDLE h_thread = CreateThread (NULL, 0, pump, (void*)pi, 0, &unused); + CloseHandle (h_thread); +} + +/* + * Get command line, skip over the executable name, return the rest. + */ +static LPTSTR +get_command (void) +{ + LPTSTR q, ws, cl = GetCommandLine (); + int ix; + + while (1) + { + ix = _tcscspn (cl, _T(" \t\"")); + if (cl[ix] == '\"') + { + cl = _tcschr (cl + ix + 1, '\"'); + if (cl == NULL) + return NULL; /* Unmatched quote */ + cl++; + } + else + { + cl += ix; + cl += _tcsspn (cl, _T(" \t")); + return *cl ? cl : NULL; + } + } +} + +/* + * Brew coffee and bring snickers + */ +void +usage (void) +{ + fprintf (stderr, + "\n" + "usage: i command\n" + "i executes the command and reroutes its standard handles to the calling\n" + "console. Good for seeing output of GUI programs that use standard output." + "\n"); +} + +int +main (void) +{ + STARTUPINFO si; + PROCESS_INFORMATION pi; + I_connector I_in, I_out, I_err; + DWORD exit_code; + + LPTSTR command = get_command (); + if (command == NULL) + { + usage (); + return 1; + } + + ZeroMemory (&si, sizeof (si)); + si.dwFlags = STARTF_USESTDHANDLES; + + I_in.source = GetStdHandle (STD_INPUT_HANDLE); + CreatePipe (&si.hStdInput, &I_in.drain, NULL, 0); + make_inheritable (&si.hStdInput); + + I_out.drain = GetStdHandle (STD_OUTPUT_HANDLE); + CreatePipe (&I_out.source, &si.hStdOutput, NULL, 0); + make_inheritable (&si.hStdOutput); + + I_err.drain = GetStdHandle (STD_ERROR_HANDLE); + CreatePipe (&I_err.source, &si.hStdError, NULL, 0); + make_inheritable (&si.hStdError); + + if (CreateProcess (NULL, command, NULL, NULL, TRUE, 0, + NULL, NULL, &si, &pi) == 0) + { + _ftprintf (stderr, _T("Error %d launching `%s'\n"), + GetLastError (), command); + return 2; + } + + CloseHandle (pi.hThread); + + /* Start pump in each I-connector */ + start_pump (&I_in); + start_pump (&I_out); + start_pump (&I_err); + + /* Wait for the process to complete */ + WaitForSingleObject (pi.hProcess, INFINITE); + GetExitCodeProcess (pi.hProcess, &exit_code); + CloseHandle (pi.hProcess); + + /* Make pump threads eventually die out. Looks rude, I agree */ + CloseHandle (GetStdHandle (STD_INPUT_HANDLE)); + CloseHandle (GetStdHandle (STD_OUTPUT_HANDLE)); + CloseHandle (GetStdHandle (STD_ERROR_HANDLE)); + + return exit_code; +}