From 5a63af876bc131b07e066aa9d60780de0562bcb0 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sun, 17 Apr 2022 17:20:03 +0300 Subject: [PATCH] Fix 'restart-emacs' on MS-Windows * src/w32.c (w32_reexec_emacs): New function, emulation of 'execvp' on Posix systems. * src/w32.h (w32_reexec_emacs): Add prototype. * src/emacs.c (main) [WINDOWSNT]: Save the original command line and working directory. (Fkill_emacs) [WINDOWSNT]: Call 'w32_reexec_emacs' instead of 'execvp'. (Bug#17036) --- src/emacs.c | 12 ++++++++++++ src/w32.c | 43 +++++++++++++++++++++++++++++++++++++++++++ src/w32.h | 3 +++ 3 files changed, 58 insertions(+) diff --git a/src/emacs.c b/src/emacs.c index a16e702ab7b..8c897762a2b 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -159,6 +159,10 @@ Lisp_Object empty_unibyte_string, empty_multibyte_string; #ifdef WINDOWSNT /* Cache for externally loaded libraries. */ Lisp_Object Vlibrary_cache; +/* Original command line string as received from the OS. */ +static char *initial_cmdline; +/* Original working directory when invoked. */ +static const char *initial_wd; #endif struct gflags gflags; @@ -1319,6 +1323,7 @@ main (int argc, char **argv) } } init_heap (use_dynamic_heap); + initial_cmdline = GetCommandLine (); #endif #if defined WINDOWSNT || defined HAVE_NTGUI /* Set global variables used to detect Windows version. Do this as @@ -1465,6 +1470,9 @@ main (int argc, char **argv) #endif emacs_wd = emacs_get_current_dir_name (); +#ifdef WINDOWSNT + initial_wd = emacs_wd; +#endif #ifdef HAVE_PDUMPER if (dumped_with_pdumper_p ()) pdumper_record_wd (emacs_wd); @@ -2811,7 +2819,11 @@ killed. */ (on some systems) with no argv. */ if (initial_argc < 1) error ("No command line arguments known; unable to re-execute Emacs"); +#ifdef WINDOWSNT + if (w32_reexec_emacs (initial_cmdline, initial_wd) < 1) +#else if (execvp (*initial_argv, initial_argv) < 1) +#endif error ("Unable to re-execute Emacs"); } diff --git a/src/w32.c b/src/w32.c index 0dc874eac40..acd7d004e53 100644 --- a/src/w32.c +++ b/src/w32.c @@ -10614,6 +10614,49 @@ realpath (const char *file_name, char *resolved_name) return xstrdup (tgt); } +/* A replacement for Posix execvp, used to restart Emacs. This is + needed because the low-level Windows API to start processes accepts + the command-line arguments as a single string, so we cannot safely + use the MSVCRT execvp emulation, because elements of argv[] that + have embedded blanks and tabs will not be passed correctly to the + restarted Emacs. */ +int +w32_reexec_emacs (char *cmd_line, const char *wdir) +{ + STARTUPINFO si; + SECURITY_ATTRIBUTES sec_attrs; + BOOL status; + PROCESS_INFORMATION proc_info; + + GetStartupInfo (&si); /* Use the same startup info as the caller. */ + sec_attrs.nLength = sizeof (sec_attrs); + sec_attrs.lpSecurityDescriptor = NULL; + sec_attrs.bInheritHandle = FALSE; + + /* Make sure we are in the original directory, in case the command + line specifies the program as a relative file name. */ + chdir (wdir); + + status = CreateProcess (NULL, /* program */ + cmd_line, /* command line */ + &sec_attrs, /* process attributes */ + NULL, /* thread attributes */ + TRUE, /* inherit handles? */ + NORMAL_PRIORITY_CLASS, + NULL, /* environment */ + wdir, /* initial directory */ + &si, /* startup info */ + &proc_info); + if (status) + { + CloseHandle (proc_info.hThread); + CloseHandle (proc_info.hProcess); + exit (0); + } + errno = ENOEXEC; + return -1; +} + /* globals_of_w32 is used to initialize those global variables that must always be initialized on startup even when the global variable diff --git a/src/w32.h b/src/w32.h index 4941170bdcf..dc91c595c43 100644 --- a/src/w32.h +++ b/src/w32.h @@ -244,6 +244,9 @@ extern int w32_init_random (void *, ptrdiff_t); extern Lisp_Object w32_read_registry (HKEY, Lisp_Object, Lisp_Object); +/* Used instead of execvp to restart Emacs. */ +extern int w32_reexec_emacs (char *, const char *); + #ifdef HAVE_GNUTLS #include -- 2.39.2