vfprintf (stderr, msg, args);
va_end (args);
- exit (1);
+ exit (-1);
}
void
if (p[0] == escape_char && escape_char != '"')
{
escape_char_run++;
+ p++;
continue;
}
else if (p[0] == '"')
char * p1 = skip_nonspace (p);
memcpy (o, p, p1 - p);
o += (p1 - p);
+ *o = '\0';
p = p1;
}
int i, rc;
/* Search the directory for the program. */
- for (i = 0; i < n_exts; i++)
+ for (i = 0; i < n_exts; i++)
{
rc = SearchPath (dir, exec, exts[i], bufsize, buffer, &dummy);
if (rc > 0)
return 0;
}
-/* Return the absolute name of executable file PROG, including
+/* Return the absolute name of executable file PROG, including
any file extensions. If an absolute name for PROG cannot be found,
return NULL. */
char *
return NULL;
}
- if (GetCurrentDirectory (MAX_PATH, curdir) <= 0)
+ if (GetCurrentDirectory (MAX_PATH, curdir) <= 0)
return NULL;
/* Relative path; search in current dir. */
- if (strpbrk (prog, "\\"))
+ if (strpbrk (prog, "\\"))
{
if (search_dir (curdir, prog, MAX_PATH, absname) > 0)
return strdup (absname);
- else
+ else
return NULL;
}
-
+
/* Just filename; search current directory then PATH. */
path = alloca (strlen (getenv ("PATH")) + strlen (curdir) + 2);
strcpy (path, curdir);
/* Move to the next directory. */
path = p + 1;
- }
+ }
return NULL;
}
char * cmdline = GetCommandLine ();
int arg_bytes = 0;
-
+
}
#endif
return TRUE;
}
+/* Change from normal usage; return value indicates whether spawn
+ succeeded or failed - program return code is returned separately. */
int
-spawn (char * progname, char * cmdline)
+spawn (char * progname, char * cmdline, char * dir, int * retcode)
{
- DWORD rc = 0xff;
+ BOOL success = FALSE;
SECURITY_ATTRIBUTES sec_attrs;
STARTUPINFO start;
+ /* In theory, passing NULL for the environment block to CreateProcess
+ is the same as passing the value of GetEnvironmentStrings, but
+ doing this explicitly seems to cure problems running DOS programs
+ in some cases. */
+ 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, NULL, NULL, &start, &child))
+ 0, envblock, dir, &start, &child))
{
+ success = TRUE;
/* wait for completion and pass on return code */
WaitForSingleObject (child.hProcess, INFINITE);
- GetExitCodeProcess (child.hProcess, &rc);
+ if (retcode)
+ GetExitCodeProcess (child.hProcess, (DWORD *)retcode);
CloseHandle (child.hThread);
CloseHandle (child.hProcess);
child.hProcess = NULL;
}
- return (int) rc;
+ FreeEnvironmentStrings (envblock);
+
+ return success;
+}
+
+/* Return size of current environment block. */
+int
+get_env_size ()
+{
+ char * start = GetEnvironmentStrings ();
+ char * tmp = start;
+
+ while (tmp[0] || tmp[1])
+ ++tmp;
+ FreeEnvironmentStrings (start);
+ return tmp + 2 - start;
}
/******* Main program ********************************************/
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");
+
/* We serve double duty: we can be called either as a proxy for the
real shell (that is, because we are defined to be the user shell),
or in our role as a helper application for running DOS programs.
from the command line.) */
if (!GetModuleFileName (NULL, modname, sizeof (modname)))
- fail ("GetModuleFileName failed");
+ fail ("error: GetModuleFileName failed\n");
+
+ /* Change directory to location of .exe so startup directory can be
+ deleted. */
+ progname = strrchr (modname, '\\');
+ *progname = '\0';
+ SetCurrentDirectory (modname);
+ *progname = '\\';
/* Although Emacs always sets argv[0] to an absolute pathname, we
might get run in other ways as well, so convert argv[0] to an
- absolute name before comparing to the module name. */
+ absolute name before comparing to the module name. Don't get
+ caught out by mixed short and long names. */
+ 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)
{
/* We are being used as a helper to run a DOS app; just pass
command line to DOS app without change. */
/* TODO: fill in progname. */
- return spawn (NULL, GetCommandLine ());
+ if (spawn (NULL, GetCommandLine (), dir, &rc))
+ return rc;
+ fail ("Could not run %s\n", GetCommandLine ());
}
/* Process command line. If running interactively (-c or /c not
/* If no args, spawn real shell for interactive use. */
need_shell = TRUE;
interactive = TRUE;
- /* Ask for a reasonable size environment for command.com. */
- envsize = 1024;
+ /* Ask command.com to create an environment block with a reasonable
+ amount of free space. */
+ envsize = get_env_size () + 300;
+ pass_through_args = (char **) alloca (argc * sizeof(char *));
+ num_pass_through_args = 0;
while (--argc > 0)
{
++argv;
- /* Only support single letter switches (except for -e); allow / as
+ /* Act on switches we recognize (mostly single letter switches,
+ except for -e); all unrecognised switches and extra args are
+ passed on to real shell if used (only really of benefit for
+ interactive use, but allow for batch use as well). Accept / as
switch char for compatability with cmd.exe. */
- if ( ((*argv)[0] == '-' || (*argv)[0] == '/')
- && (*argv)[1] != '\0' && (*argv)[2] == '\0' )
+ if (((*argv)[0] == '-' || (*argv)[0] == '/') && (*argv)[1] != '\0')
{
- if ( ((*argv)[1] == 'c') && ((*argv)[2] == '\0') )
+ if (((*argv)[1] == 'c' || (*argv)[1] == 'C') && ((*argv)[2] == '\0'))
{
if (--argc == 0)
- fail ("error: expecting arg for %s", *argv);
+ fail ("error: expecting arg for %s\n", *argv);
cmdline = *(++argv);
interactive = FALSE;
}
- else if ( ((*argv)[1] == 'i') && ((*argv)[2] == '\0') )
+ else if (((*argv)[1] == 'i' || (*argv)[1] == 'I') && ((*argv)[2] == '\0'))
{
if (cmdline)
- warn ("warning: %s ignored because of -c", *argv);
+ warn ("warning: %s ignored because of -c\n", *argv);
}
- else if ( ((*argv)[1] == 'e') && ((*argv)[2] == ':') )
+ else if (((*argv)[1] == 'e' || (*argv)[1] == 'E') && ((*argv)[2] == ':'))
{
- envsize = atoi (*argv + 3);
- /* Enforce a reasonable minimum size. */
- if (envsize < 256)
- envsize = 256;
+ int requested_envsize = atoi (*argv + 3);
+ /* Enforce a reasonable minimum size, as above. */
+ if (requested_envsize > envsize)
+ envsize = requested_envsize;
+ /* For sanity, enforce a reasonable maximum. */
+ if (envsize > 32768)
+ envsize = 32768;
}
else
{
- warn ("warning: unknown option %s ignored", *argv);
+ /* warn ("warning: unknown option %s ignored", *argv); */
+ pass_through_args[num_pass_through_args++] = *argv;
}
}
else
break;
}
+#if 0
+ /* I think this is probably not useful - cmd.exe ignores extra
+ (non-switch) args in interactive mode, and they cannot be passed on
+ when -c was given. */
+
+ /* Collect any remaining args after (initial) switches. */
+ while (argc-- > 0)
+ {
+ pass_through_args[num_pass_through_args++] = *argv++;
+ }
+#else
+ /* Probably a mistake for there to be extra args; not fatal. */
+ if (argc > 0)
+ warn ("warning: extra args ignored after '%s'\n", argv[-1]);
+#endif
+
+ pass_through_args[num_pass_through_args] = NULL;
+
/* If -c option, determine if we must spawn a real shell, or if we can
execute the command directly ourself. */
if (cmdline)
}
}
+ 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");
+ fail ("error: COMSPEC is not set\n");
canon_filename (progname);
progname = make_absolute (progname);
if (progname == NULL || strchr (progname, '\\') == NULL)
- fail ("make_absolute failed");
+ fail ("error: the program %s could not be found.\n", getenv ("COMSPEC"));
+
+ /* Need to set environment size when running command.com. */
+ run_command_dot_com =
+ (stricmp (strrchr (progname, '\\'), "command.com") == 0);
+
+ /* Work out how much extra space is required for
+ pass_through_args. */
+ for (argv = pass_through_args; *argv != NULL; ++argv)
+ /* We don't expect to have to quote switches. */
+ extra_arg_space += strlen (*argv) + 2;
if (cmdline)
{
+ char * buf;
+
/* Convert to syntax expected by cmd.exe/command.com for
running non-interactively. Always quote program name in
case path contains spaces (fortunately it can't contain
quotes, since they are illegal in path names). */
- wsprintf (p = alloca (strlen (cmdline) + strlen (progname) + 7),
- "\"%s\" /c %s", progname, cmdline);
- cmdline = p;
+
+ buf = p = alloca (strlen (progname) + extra_arg_space +
+ strlen (cmdline) + 16);
+
+ /* Quote progname in case it contains spaces. */
+ p += wsprintf (p, "\"%s\"", progname);
+
+ /* Include pass_through_args verbatim; these are just switches
+ so should not need quoting. */
+ 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
{
- /* Provide dir arg expected by command.com when first started
- interactively (the "command search path"). cmd.exe does
- not require it, but accepts it silently - presumably other
- DOS compatible shells do the same. To avoid potential
- problems with spaces in command dir (which cannot be quoted
- - command.com doesn't like it), we always use the 8.3 form. */
- GetShortPathName (progname, path, sizeof (path));
- p = strrchr (path, '\\');
- /* Trailing slash is acceptable. */
- p++;
-
- /* Set environment size - again cmd.exe ignores this silently. */
- wsprintf (p, " /e:%d", envsize);
+ if (run_command_dot_com)
+ {
+ /* Provide dir arg expected by command.com when first
+ started interactively (the "command search path"). To
+ avoid potential problems with spaces in command dir
+ (which cannot be quoted - command.com doesn't like it),
+ we always use the 8.3 form. */
+ GetShortPathName (progname, path, sizeof (path));
+ p = strrchr (path, '\\');
+ /* Trailing slash is acceptable, so always leave it. */
+ *(++p) = '\0';
+ }
+ else
+ path[0] = '\0';
+
+ cmdline = p = alloca (strlen (progname) + extra_arg_space +
+ strlen (path) + 13);
/* Quote progname in case it contains spaces. */
- wsprintf (cmdline = alloca (strlen (progname) + strlen (path) + 4),
- "\"%s\" %s", progname, path);
+ p += wsprintf (p, "\"%s\" %s", progname, path);
+
+ /* Include pass_through_args verbatim; these are just switches
+ so should not need quoting. */
+ for (argv = pass_through_args; *argv != NULL; ++argv)
+ p += wsprintf (p, " %s", *argv);
+
+ if (run_command_dot_com)
+ wsprintf (p, " /e:%d", envsize);
}
}
if (!cmdline)
cmdline = progname;
- rc = spawn (progname, cmdline);
+ if (spawn (progname, cmdline, dir, &rc))
+ return rc;
- return rc;
+ if (!need_shell)
+ {
+ need_shell = TRUE;
+ goto pass_to_shell;
+ }
+
+ fail ("Could not run %s\n", progname);
+
+ return 0;
}
+
+/* arch-tag: 88678d93-07ac-4e2f-ad63-d4a740ca69ac
+ (do not change this comment) */