Subject: bug#77389: 31.0.50; Restarting Emacs with (kill-emacs ... t) looses noninteractivity

From: Jens Schmidt <[email-masked]>
Date: Sat, 5 Apr 2025 14:44:12 +0200

On 2025-04-05  08:46, Eli Zaretskii wrote:

>>> Why does this matter?  Your patch doesn't eliminate initial_argv, so
>>> initial_argv[0] still holds the name of the Emacs program, albeit only
>>> in the form it was provided by the calling shell.  Right?  If so, then
>>> for reporting errors (and ones that should never happen at that), I
>>> don't see any need to jump through hoops.
>>
>> But my patch, at least the second one [1] on message #20
>> of this bug, plus your earlier request to #ifdef away
>> function and call to copy_raw_args on Windows, would skip
>> the initialization of initial_argv on Windows, no?
> 
> Ah, I've missed that part.  Then the call to 'error' could just use
> argv[0] instead of initial_argv[0], right?

Then what do you think about the attached patch?
From 11a4f6c976eef8ad010da7ba405c7b496245e9cb Mon Sep 17 00:00:00 2001
From: Jens Schmidt <[email-masked]>
Date: Wed, 2 Apr 2025 22:48:31 +0200
Subject: [PATCH] Use a pristine copy of argv to restart Emacs

argv as left after main has proccessed the command-line can differ both
in order and contents of the original command-line arguments, which can
lead to surprising results when restarting emacs on the cooked argv
through `kill-emacs'.

Starting from that observation, consistenly use variables
initial_cmdline on Windows, initial_argc, initial_argv on non-Windows,
and initial_argv0 on all ports.

* src/lisp.h: Declare initial_argv0, limit declaration of initial_argv
and initial_argc to non-Windows ports.
* src/emacs.c: Likewise, but for the definitions.
(init_cmdargs): Move initialization of initial_argv and initial_argc ...
(copy_args) [!WINDOWSNT]: ... to this new function ...
(main): ... and call that in main, also initializing initial_argv0,
before the command-line processing.

* src/emacs.c (Fkill_emacs):
* src/pgtkterm.c (pgtk_term_init):
* src/sysdep.c (emacs_perror):
* src/xterm.c (x_term_init): Use initial_argv0 where only that is
required.  (Bug#77389)
---
 src/emacs.c    | 50 +++++++++++++++++++++++++++++++++++++++++++++-----
 src/lisp.h     |  3 +++
 src/pgtkterm.c |  2 +-
 src/sysdep.c   |  3 +--
 src/xterm.c    |  2 +-
 5 files changed, 51 insertions(+), 9 deletions(-)

diff --git a/src/emacs.c b/src/emacs.c
index 6ff7b632c0f..79604d09a37 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -238,14 +238,32 @@ #define MAIN_PROGRAM
 HANDLE w32_daemon_event;
 #endif
 
-/* Save argv and argc.  */
+/* Save argv and argc.
+
+   initial_argc, initial_argv:
+     A pristine copy of the command-line arguments as passed into main,
+     saved away before main has had a chance to modify them.  On
+     Windows, we use initial_cmdline instead.
+
+   initial_argv0:
+     argv[0] as passed into main.  Available on all ports.
+
+   initial_emacs_executable:
+     Path to the current executable.  Based on argv[0] but verified to
+     point to an existing executable if non-NULL.  */
+#ifndef WINDOWSNT
 char **initial_argv;
 int initial_argc;
+#endif
+char *initial_argv0;
 static char *initial_emacs_executable = NULL;
 
 /* The name of the working directory, or NULL if this info is unavailable.  */
 char const *emacs_wd;
 
+#ifndef WINDOWSNT
+static void copy_args (int argc, char **argv);
+#endif
 static void sort_args (int argc, char **argv);
 static void syms_of_emacs (void);
 
@@ -476,9 +494,6 @@ init_cmdargs (int argc, char **argv, int skip_args, char const *original_pwd)
   Lisp_Object raw_name;
   AUTO_STRING (slash_colon, "/:");
 
-  initial_argv = argv;
-  initial_argc = argc;
-
 #ifdef WINDOWSNT
   /* Must use argv[0] converted to UTF-8, as it begets many standard
      file and directory names.  */
@@ -1396,6 +1411,11 @@ android_emacs_init (int argc, char **argv, char *dump_file)
   init_standard_fds ();
   atexit (close_output_streams);
 
+#ifndef WINDOWSNT
+  copy_args (argc, argv);
+#endif
+  initial_argv0 = argv[0];
+
   /* Command-line argument processing.
 
      The arguments in the argv[] array are sorted in the descending
@@ -2696,6 +2716,26 @@ android_emacs_init (int argc, char **argv, char *dump_file)
   { "-kill", "--kill", -10, 0 },
 };
 
+#ifndef WINDOWSNT
+
+/* Copy the elements of ARGV (assumed to have ARGC elements) and store
+   the copy in initial_argv.  Store ARGC in initial_argc.  */
+
+static void
+copy_args (int argc, char **argv)
+{
+  char **new = xmalloc ((argc + 1) * sizeof *new);
+  int i;
+  new[0] = argv[0];
+  for (i = 1; i < argc; i++)
+    new[i] = xstrdup (argv[i]);
+  new[argc] = argv[argc];
+  initial_argv = new;
+  initial_argc = argc;
+}
+
+#endif
+
 /* Reorder the elements of ARGV (assumed to have ARGC elements)
    so that the highest priority ones come first.
    Do not change the order of elements of equal priority.
@@ -2900,7 +2940,7 @@ DEFUN ("kill-emacs", Fkill_emacs, Skill_emacs, 0, 2, "P",
 	error ("Unknown Emacs executable");
 
       if (!file_access_p (initial_emacs_executable, F_OK))
-	error ("Emacs executable \"%s\" can't be found", initial_argv[0]);
+	error ("Emacs executable \"%s\" can't be found", initial_argv0);
     }
 #endif
 
diff --git a/src/lisp.h b/src/lisp.h
index 243e8cc7f36..bca58b9c12d 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -5136,8 +5136,11 @@ fast_c_string_match_ignore_case (Lisp_Object regexp,
 extern void syms_of_frame (void);
 
 /* Defined in emacs.c.  */
+#ifndef WINDOWSNT
 extern char **initial_argv;
 extern int initial_argc;
+#endif
+extern char *initial_argv0;
 extern char const *emacs_wd;
 #if defined (HAVE_X_WINDOWS) || defined (HAVE_PGTK) || defined (HAVE_NS)
 extern bool display_arg;
diff --git a/src/pgtkterm.c b/src/pgtkterm.c
index a2e23a5616b..3d59a239ccd 100644
--- a/src/pgtkterm.c
+++ b/src/pgtkterm.c
@@ -7075,7 +7075,7 @@ #define NUM_ARGV 10
           argv[argc] = 0;
 
         argc = 0;
-        argv[argc++] = initial_argv[0];
+        argv[argc++] = initial_argv0;
 
 	if (strlen (dpy_name) != 0)
 	  {
diff --git a/src/sysdep.c b/src/sysdep.c
index a161b4af100..042de2acf80 100644
--- a/src/sysdep.c
+++ b/src/sysdep.c
@@ -2854,8 +2854,7 @@ emacs_perror (char const *message)
 {
   int err = errno;
   char const *error_string = emacs_strerror (err);
-  char const *command = (initial_argv && initial_argv[0]
-			 ? initial_argv[0] : "emacs");
+  char const *command = (initial_argv0 ? initial_argv0 : "emacs");
   /* Write it out all at once, if it's short; this is less likely to
      be interleaved with other output.  */
   char buf[min (PIPE_BUF, MAX_ALLOCA)];
diff --git a/src/xterm.c b/src/xterm.c
index b21efd5a2a2..18a9231e75a 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -30619,7 +30619,7 @@ #define NUM_ARGV 10
           argv[argc] = 0;
 
         argc = 0;
-        argv[argc++] = initial_argv[0];
+        argv[argc++] = initial_argv0;
 
         if (! NILP (display_name))
           {
-- 
2.39.5