#include <windows.h>
#include <stdarg.h>
#include <malloc.h>
#include <stdlib.h>
#include <string.h>
#define stdin GetStdHandle (STD_INPUT_HANDLE)
#define stdout GetStdHandle (STD_OUTPUT_HANDLE)
#define stderr GetStdHandle (STD_ERROR_HANDLE)
int
vfprintf(HANDLE hnd, char * msg, va_list args)
{
DWORD bytes_written;
char buf[1024];
wvsprintf (buf, msg, args);
return WriteFile (hnd, buf, strlen (buf), &bytes_written, NULL);
}
int
fprintf(HANDLE hnd, char * msg, ...)
{
va_list args;
int rc;
va_start (args, msg);
rc = vfprintf (hnd, msg, args);
va_end (args);
return rc;
}
int
printf(char * msg, ...)
{
va_list args;
int rc;
va_start (args, msg);
rc = vfprintf (stdout, msg, args);
va_end (args);
return rc;
}
void
fail (char * msg, ...)
{
va_list args;
va_start (args, msg);
vfprintf (stderr, msg, args);
va_end (args);
exit (-1);
}
void
warn (char * msg, ...)
{
va_list args;
va_start (args, msg);
vfprintf (stderr, msg, args);
va_end (args);
}
char *
canon_filename (char *fname)
{
char *p = fname;
while (*p)
{
if (*p == '/')
*p = '\\';
p++;
}
return fname;
}
char *
skip_space (char *str)
{
while (isspace (*str)) str++;
return str;
}
char *
skip_nonspace (char *str)
{
while (*str && !isspace (*str)) str++;
return str;
}
int escape_char = '\\';
int
get_next_token (char * buf, char ** pSrc)
{
char * p = *pSrc;
char * o = buf;
p = skip_space (p);
if (*p == '"')
{
int escape_char_run = 0;
p++;
while (1)
{
if (p[0] == escape_char && escape_char != '"')
{
escape_char_run++;
p++;
continue;
}
else if (p[0] == '"')
{
while (escape_char_run > 1)
{
*o++ = escape_char;
escape_char_run -= 2;
}
if (escape_char_run > 0)
{
*o++ = *p++;
escape_char_run = 0;
}
else if (p[1] == escape_char && escape_char == '"')
{
*o++ = *p;
p += 2;
}
else
{
*o = '\0';
p++;
break;
}
}
else if (p[0] == '\0')
{
*o = '\0';
break;
}
else
{
*o++ = *p++;
}
}
}
else
{
char * p1 = skip_nonspace (p);
memcpy (o, p, p1 - p);
o += (p1 - p);
*o = '\0';
p = p1;
}
*pSrc = p;
return o - buf;
}
int
search_dir (char *dir, char *exec, int bufsize, char *buffer)
{
char *exts[] = {".bat", ".cmd", ".exe", ".com"};
int n_exts = sizeof (exts) / sizeof (char *);
char *dummy;
int i, rc;
for (i = 0; i < n_exts; i++)
{
rc = SearchPath (dir, exec, exts[i], bufsize, buffer, &dummy);
if (rc > 0)
return rc;
}
return 0;
}
char *
make_absolute (char *prog)
{
char absname[MAX_PATH];
char dir[MAX_PATH];
char curdir[MAX_PATH];
char *p, *fname;
char *path;
int i;
if ((isalpha (prog[0]) && prog[1] == ':') ||
(prog[0] == '\\'))
{
fname = strrchr (prog, '\\');
if (!fname)
fname = prog + 2;
strncpy (dir, prog, fname - prog);
dir[fname - prog] = '\0';
if (search_dir (dir, prog, MAX_PATH, absname) > 0)
return strdup (absname);
else
return NULL;
}
if (GetCurrentDirectory (MAX_PATH, curdir) <= 0)
return NULL;
if (strpbrk (prog, "\\"))
{
if (search_dir (curdir, prog, MAX_PATH, absname) > 0)
return strdup (absname);
else
return NULL;
}
path = alloca (strlen (getenv ("PATH")) + strlen (curdir) + 2);
strcpy (path, curdir);
strcat (path, ";");
strcat (path, getenv ("PATH"));
while (*path)
{
p = path;
while (*p && *p != ';') p++;
strncpy (dir, path, p - path);
dir[p - path] = '\0';
if (search_dir (dir, prog, MAX_PATH, absname) > 0)
return strdup (absname);
path = p + 1;
}
return NULL;
}
#if 0
char ** _argv;
int _argc;
void
setup_argv (void)
{
char * cmdline = GetCommandLine ();
int arg_bytes = 0;
}
#endif
PROCESS_INFORMATION child;
int interactive = TRUE;
BOOL
console_event_handler (DWORD event)
{
switch (event)
{
case CTRL_C_EVENT:
case CTRL_BREAK_EVENT:
if (!interactive)
{
if (child.hProcess &&
WaitForSingleObject (child.hProcess, 500) != WAIT_OBJECT_0)
TerminateProcess (child.hProcess, 0);
exit (STATUS_CONTROL_C_EXIT);
}
break;
#if 0
default:
fail ("cmdproxy: received %d event\n", event);
if (child.hProcess)
TerminateProcess (child.hProcess, 0);
#endif
}
return TRUE;
}
int
spawn (char * progname, char * cmdline, char * dir, int * retcode)
{
BOOL success = FALSE;
SECURITY_ATTRIBUTES sec_attrs;
STARTUPINFO start;
char * envblock = GetEnvironmentStrings ();
sec_attrs.nLength = sizeof (sec_attrs);
sec_attrs.lpSecurityDescriptor = NULL;
sec_attrs.bInheritHandle = FALSE;
memset (&start, 0, sizeof (start));
start.cb = sizeof (start);
if (CreateProcess (progname, cmdline, &sec_attrs, NULL, TRUE,
0, envblock, dir, &start, &child))
{
success = TRUE;
WaitForSingleObject (child.hProcess, INFINITE);
if (retcode)
GetExitCodeProcess (child.hProcess, (DWORD *)retcode);
CloseHandle (child.hThread);
CloseHandle (child.hProcess);
child.hProcess = NULL;
}
FreeEnvironmentStrings (envblock);
return success;
}
int
get_env_size ()
{
char * start = GetEnvironmentStrings ();
char * tmp = start;
while (tmp[0] || tmp[1])
++tmp;
FreeEnvironmentStrings (start);
return tmp + 2 - start;
}
int
main (int argc, char ** argv)
{
int rc;
int need_shell;
char * cmdline;
char * progname;
int envsize;
char **pass_through_args;
int num_pass_through_args;
char modname[MAX_PATH];
char path[MAX_PATH];
char dir[MAX_PATH];
interactive = TRUE;
SetConsoleCtrlHandler ((PHANDLER_ROUTINE) console_event_handler, TRUE);
if (!GetCurrentDirectory (sizeof (dir), dir))
fail ("error: GetCurrentDirectory failed\n");
if (!GetModuleFileName (NULL, modname, sizeof (modname)))
fail ("error: GetModuleFileName failed\n");
progname = strrchr (modname, '\\');
*progname = '\0';
SetCurrentDirectory (modname);
*progname = '\\';
GetShortPathName (modname, modname, sizeof (modname));
path[0] = '\0';
if (!SearchPath (NULL, argv[0], ".exe", sizeof (path), path, &progname)
|| !GetShortPathName (path, path, sizeof (path))
|| stricmp (modname, path) != 0)
{
if (spawn (NULL, GetCommandLine (), dir, &rc))
return rc;
fail ("Could not run %s\n", GetCommandLine ());
}
progname = NULL;
cmdline = NULL;
need_shell = TRUE;
interactive = TRUE;
envsize = get_env_size () + 300;
pass_through_args = (char **) alloca (argc * sizeof(char *));
num_pass_through_args = 0;
while (--argc > 0)
{
++argv;
if (((*argv)[0] == '-' || (*argv)[0] == '/') && (*argv)[1] != '\0')
{
if (((*argv)[1] == 'c' || (*argv)[1] == 'C') && ((*argv)[2] == '\0'))
{
if (--argc == 0)
fail ("error: expecting arg for %s\n", *argv);
cmdline = *(++argv);
interactive = FALSE;
}
else if (((*argv)[1] == 'i' || (*argv)[1] == 'I') && ((*argv)[2] == '\0'))
{
if (cmdline)
warn ("warning: %s ignored because of -c\n", *argv);
}
else if (((*argv)[1] == 'e' || (*argv)[1] == 'E') && ((*argv)[2] == ':'))
{
int requested_envsize = atoi (*argv + 3);
if (requested_envsize > envsize)
envsize = requested_envsize;
if (envsize > 32768)
envsize = 32768;
}
else
{
pass_through_args[num_pass_through_args++] = *argv;
}
}
else
break;
}
#if 0
while (argc-- > 0)
{
pass_through_args[num_pass_through_args++] = *argv++;
}
#else
if (argc > 0)
warn ("warning: extra args ignored after '%s'\n", argv[-1]);
#endif
pass_through_args[num_pass_through_args] = NULL;
if (cmdline)
{
static char copout_chars[] = "|<>&";
if (strpbrk (cmdline, copout_chars) == NULL)
{
char *args;
args = cmdline;
if (!get_next_token (path, &args))
fail ("error: no program name specified.\n");
canon_filename (path);
progname = make_absolute (path);
if (progname != NULL)
need_shell = FALSE;
}
}
pass_to_shell:
if (need_shell)
{
char * p;
int extra_arg_space = 0;
int run_command_dot_com;
progname = getenv ("COMSPEC");
if (!progname)
fail ("error: COMSPEC is not set\n");
canon_filename (progname);
progname = make_absolute (progname);
if (progname == NULL || strchr (progname, '\\') == NULL)
fail ("error: the program %s could not be found.\n", getenv ("COMSPEC"));
run_command_dot_com =
(stricmp (strrchr (progname, '\\'), "command.com") == 0);
for (argv = pass_through_args; *argv != NULL; ++argv)
extra_arg_space += strlen (*argv) + 2;
if (cmdline)
{
char * buf;
buf = p = alloca (strlen (progname) + extra_arg_space +
strlen (cmdline) + 16);
p += wsprintf (p, "\"%s\"", progname);
for (argv = pass_through_args; *argv != NULL; ++argv)
p += wsprintf (p, " %s", *argv);
if (run_command_dot_com)
wsprintf(p, " /e:%d /c %s", envsize, cmdline);
else
wsprintf(p, " /c %s", cmdline);
cmdline = buf;
}
else
{
if (run_command_dot_com)
{
GetShortPathName (progname, path, sizeof (path));
p = strrchr (path, '\\');
*(++p) = '\0';
}
else
path[0] = '\0';
cmdline = p = alloca (strlen (progname) + extra_arg_space +
strlen (path) + 13);
p += wsprintf (p, "\"%s\" %s", progname, path);
for (argv = pass_through_args; *argv != NULL; ++argv)
p += wsprintf (p, " %s", *argv);
if (run_command_dot_com)
wsprintf (p, " /e:%d", envsize);
}
}
if (!progname)
fail ("Internal error: program name not defined\n");
if (!cmdline)
cmdline = progname;
if (spawn (progname, cmdline, dir, &rc))
return rc;
if (!need_shell)
{
need_shell = TRUE;
goto pass_to_shell;
}
fail ("Could not run %s\n", progname);
return 0;
}