]> code.delx.au - gnu-emacs/blobdiff - nt/cmdproxy.c
*** empty log message ***
[gnu-emacs] / nt / cmdproxy.c
index 4a270cfadce7e37415ffcbeda00610d513d4f44b..52760402f1b6ae19fbdb468b4bcf320f7489626f 100644 (file)
@@ -90,7 +90,7 @@ fail (char * msg, ...)
   vfprintf (stderr, msg, args);
   va_end (args);
 
-  exit (1);
+  exit (-1);
 }
 
 void
@@ -158,6 +158,7 @@ get_next_token (char * buf, char ** pSrc)
          if (p[0] == escape_char && escape_char != '"')
            {
              escape_char_run++;
+             p++;
              continue;
            }
          else if (p[0] == '"')
@@ -209,6 +210,7 @@ get_next_token (char * buf, char ** pSrc)
       char * p1 = skip_nonspace (p);
       memcpy (o, p, p1 - p);
       o += (p1 - p);
+      *o = '\0';
       p = p1;
     }
 
@@ -228,7 +230,7 @@ search_dir (char *dir, char *exec, int bufsize, char *buffer)
   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)
@@ -238,7 +240,7 @@ search_dir (char *dir, char *exec, int bufsize, char *buffer)
   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 *
@@ -270,18 +272,18 @@ make_absolute (char *prog)
        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);
@@ -302,7 +304,7 @@ make_absolute (char *prog)
 
       /* Move to the next directory.  */
       path = p + 1;
-    } 
+    }
 
   return NULL;
 }
@@ -320,7 +322,7 @@ setup_argv (void)
   char * cmdline = GetCommandLine ();
   int arg_bytes = 0;
 
-  
+
 }
 #endif
 
@@ -365,32 +367,56 @@ console_event_handler (DWORD event)
   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  ********************************************/
@@ -403,14 +429,20 @@ main (int argc, char ** argv)
   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.
@@ -424,18 +456,31 @@ main (int argc, char ** argv)
      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
@@ -453,45 +498,72 @@ main (int argc, char ** argv)
   /* 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)
@@ -522,49 +594,89 @@ main (int argc, char ** argv)
        }
     }
 
+ 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);
        }
     }
 
@@ -574,7 +686,19 @@ main (int argc, char ** argv)
   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) */