summaryrefslogtreecommitdiff
path: root/src/emacs.c
diff options
context:
space:
mode:
authorStefan Monnier <monnier@iro.umontreal.ca>2022-09-25 16:15:16 -0400
committerStefan Monnier <monnier@iro.umontreal.ca>2022-09-25 16:15:16 -0400
commit650c20f1ca4e07591a727e1cfcc74b3363d15985 (patch)
tree85d11f6437cde22f410c25e0e5f71a3131ebd07d /src/emacs.c
parent8869332684c2302b5ba1ead4568bbc7ba1c0183e (diff)
parent4b85ae6a24380fb67a3315eaec9233f17a872473 (diff)
downloademacs-650c20f1ca4e07591a727e1cfcc74b3363d15985.tar.gz
emacs-650c20f1ca4e07591a727e1cfcc74b3363d15985.tar.bz2
emacs-650c20f1ca4e07591a727e1cfcc74b3363d15985.zip
Merge 'master' into noverlay
Diffstat (limited to 'src/emacs.c')
-rw-r--r--src/emacs.c1600
1 files changed, 1264 insertions, 336 deletions
diff --git a/src/emacs.c b/src/emacs.c
index 0fe7d9113b4..91bf0a9b59e 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -1,6 +1,6 @@
/* Fully extensible Emacs, running on Unix, intended for GNU.
-Copyright (C) 1985-1987, 1993-1995, 1997-1999, 2001-2017 Free Software
+Copyright (C) 1985-1987, 1993-1995, 1997-1999, 2001-2022 Free Software
Foundation, Inc.
This file is part of GNU Emacs.
@@ -26,10 +26,9 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <stdlib.h>
#include <sys/file.h>
+#include <sys/stat.h>
#include <unistd.h>
-#include <close-stream.h>
-
#define MAIN_PROGRAM
#include "lisp.h"
#include "sysstdio.h"
@@ -38,6 +37,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <fcntl.h>
#include <sys/socket.h>
#include <mbstring.h>
+#include <filename.h> /* for IS_ABSOLUTE_FILE_NAME */
#include "w32.h"
#include "w32heap.h"
#endif
@@ -62,10 +62,26 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
# include <sys/socket.h>
#endif
+#if defined HAVE_LINUX_SECCOMP_H && defined HAVE_LINUX_FILTER_H \
+ && HAVE_DECL_SECCOMP_SET_MODE_FILTER \
+ && HAVE_DECL_SECCOMP_FILTER_FLAG_TSYNC
+# define SECCOMP_USABLE 1
+#else
+# define SECCOMP_USABLE 0
+#endif
+
+#if SECCOMP_USABLE
+# include <linux/seccomp.h>
+# include <linux/filter.h>
+# include <sys/prctl.h>
+# include <sys/syscall.h>
+#endif
+
#ifdef HAVE_WINDOW_SYSTEM
#include TERM_HEADER
#endif /* HAVE_WINDOW_SYSTEM */
+#include "bignum.h"
#include "intervals.h"
#include "character.h"
#include "buffer.h"
@@ -83,7 +99,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "charset.h"
#include "composite.h"
#include "dispextern.h"
-#include "regex.h"
+#include "regex-emacs.h"
#include "sheap.h"
#include "syntax.h"
#include "sysselect.h"
@@ -93,10 +109,18 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "getpagesize.h"
#include "gnutls.h"
-#if (defined PROFILING \
- && (defined __FreeBSD__ || defined GNU_LINUX || defined __MINGW32__))
+#ifdef HAVE_HAIKU
+#include <kernel/OS.h>
+#endif
+
+#ifdef PROFILING
# include <sys/gmon.h>
extern void moncontrol (int mode);
+# ifdef __MINGW32__
+extern unsigned char etext asm ("etext");
+# else
+extern char etext;
+# endif
#endif
#ifdef HAVE_SETLOCALE
@@ -112,31 +136,38 @@ extern void moncontrol (int mode);
#include <sys/resource.h>
#endif
+#include "pdumper.h"
+#include "fingerprint.h"
+#include "epaths.h"
+
+/* Include these only because of INLINE. */
+#include "comp.h"
+#include "thread.h"
+
static const char emacs_version[] = PACKAGE_VERSION;
static const char emacs_copyright[] = COPYRIGHT;
static const char emacs_bugreport[] = PACKAGE_BUGREPORT;
+/* Put version info into the executable in the form that 'ident' uses. */
+char const EXTERNALLY_VISIBLE RCS_Id[]
+ = "$Id" ": GNU Emacs " PACKAGE_VERSION
+ " (" EMACS_CONFIGURATION " " EMACS_CONFIG_FEATURES ") $";
+
/* Empty lisp strings. To avoid having to build any others. */
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
-/* Set after Emacs has started up the first time.
- Prevents reinitialization of the Lisp world and keymaps
- on subsequent starts. */
+struct gflags gflags;
bool initialized;
-#ifndef CANNOT_DUMP
-/* Set to true if this instance of Emacs might dump. */
-# ifndef DOUG_LEA_MALLOC
-static
-# endif
-bool might_dump;
-#endif
-
/* If true, Emacs should not attempt to use a window-specific code,
but instead should use the virtual terminal under which it was started. */
bool inhibit_window_system;
@@ -150,24 +181,28 @@ bool running_asynch_code;
bool display_arg;
#endif
-#if defined GNU_LINUX && !defined CANNOT_DUMP
+#if defined GNU_LINUX && defined HAVE_UNEXEC
/* The gap between BSS end and heap start as far as we can tell. */
-static uprintmax_t heap_bss_diff;
+static uintmax_t heap_bss_diff;
#endif
/* To run as a background daemon under Cocoa or Windows,
we must do a fork+exec, not a simple fork.
- On Cocoa, CoreFoundation lib fails in forked process:
- http://developer.apple.com/ReleaseNotes/
- CoreFoundation/CoreFoundation.html)
+ On Cocoa, CoreFoundation lib fails in forked process, see Mac OS X
+ Leopard Developer Release Notes for CoreFoundation Framework:
+
+ https://web.archive.org/web/20090225231934/http://developer.apple.com/ReleaseNotes/CoreFoundation/CoreFoundation.html
On Windows, a Cygwin fork child cannot access the USER subsystem.
We mark being in the exec'd process by a daemon name argument of
form "--daemon=\nFD0,FD1\nNAME" where FD are the pipe file descriptors,
- NAME is the original daemon name, if any. */
-#if defined NS_IMPL_COCOA || (defined HAVE_NTGUI && defined CYGWIN)
+ NAME is the original daemon name, if any.
+
+ On Haiku, the table of semaphores used for looper locks doesn't
+ persist across forked processes. */
+#if defined NS_IMPL_COCOA || defined CYGWIN || defined HAVE_HAIKU
# define DAEMON_MUST_EXEC
#endif
@@ -183,7 +218,8 @@ bool build_details;
/* Name for the server started by the daemon.*/
static char *daemon_name;
-/* 0 not a daemon, 1 new-style (foreground), 2 old-style (background). */
+/* 0 not a daemon, 1 new-style (foreground), 2 old-style (background).
+ A negative value means the daemon initialization was already done. */
int daemon_type;
#ifndef WINDOWSNT
@@ -197,6 +233,10 @@ HANDLE w32_daemon_event;
/* Save argv and argc. */
char **initial_argv;
int initial_argc;
+static char *initial_emacs_executable = NULL;
+
+/* The name of the working directory, or NULL if this info is unavailable. */
+char const *emacs_wd;
static void sort_args (int argc, char **argv);
static void syms_of_emacs (void);
@@ -229,6 +269,17 @@ Initialization options:\n\
--module-assertions assert behavior of dynamic modules\n\
",
#endif
+#ifdef HAVE_PDUMPER
+ "\
+--dump-file FILE read dumped state from FILE\n\
+--fingerprint output fingerprint and exit\n\
+",
+#endif
+#if SECCOMP_USABLE
+ "\
+--seccomp=FILE read Seccomp BPF filter from FILE\n\
+"
+#endif
"\
--no-build-details do not add build details such as time stamps\n\
--no-desktop do not load a saved desktop\n\
@@ -245,6 +296,9 @@ Initialization options:\n\
-q --no-site-file --no-site-lisp --no-splash\n\
--no-x-resources\n\
--script FILE run FILE as an Emacs Lisp script\n\
+-x to be used in #!/usr/bin/emacs -x\n\
+ and has approximately the same meaning\n\
+ as -Q --script\n\
--terminal, -t DEVICE use DEVICE for terminal I/O\n\
--user, -u USER load ~USER/.emacs instead of your own\n\
\n\
@@ -330,7 +384,7 @@ section of the Emacs manual or the file BUGS.\n"
bool fatal_error_in_progress;
#ifdef HAVE_NS
-/* NS autrelease pool, for memory management. */
+/* NS autorelease pool, for memory management. */
static void *ns_pool;
#endif
@@ -346,7 +400,10 @@ setlocale (int cat, char const *locale)
static bool
using_utf8 (void)
{
-#ifdef HAVE_WCHAR_H
+ /* We don't want to compile in mbrtowc on WINDOWSNT because that
+ will prevent Emacs from starting on older Windows systems, while
+ the result is known in advance anyway... */
+#if defined HAVE_WCHAR_H && !defined WINDOWSNT
wchar_t wc;
mbstate_t mbs = { 0 };
return mbrtowc (&wc, "\xc4\x80", 2, &mbs) == 2 && wc == 0x100;
@@ -358,7 +415,7 @@ using_utf8 (void)
/* Report a fatal error due to signal SIG, output a backtrace of at
most BACKTRACE_LIMIT lines, and exit. */
-_Noreturn void
+AVOID
terminate_due_to_signal (int sig, int backtrace_limit)
{
signal (sig, SIG_DFL);
@@ -372,7 +429,14 @@ terminate_due_to_signal (int sig, int backtrace_limit)
totally_unblock_input ();
if (sig == SIGTERM || sig == SIGHUP || sig == SIGINT)
- Fkill_emacs (make_number (sig));
+ {
+ /* Avoid abort in shut_down_emacs if we were interrupted
+ by SIGINT in noninteractive usage, as in that case we
+ don't care about the message stack. */
+ if (sig == SIGINT && noninteractive)
+ clear_message_stack ();
+ Fkill_emacs (make_fixnum (sig), Qnil);
+ }
shut_down_emacs (sig, Qnil);
emacs_backtrace (backtrace_limit);
@@ -396,15 +460,15 @@ terminate_due_to_signal (int sig, int backtrace_limit)
/* This shouldn't be executed, but it prevents a warning. */
exit (1);
}
+
/* Code for dealing with Lisp access to the Unix command line. */
-
static void
-init_cmdargs (int argc, char **argv, int skip_args, char *original_pwd)
+init_cmdargs (int argc, char **argv, int skip_args, char const *original_pwd)
{
int i;
Lisp_Object name, dir, handler;
- ptrdiff_t count = SPECPDL_INDEX ();
+ specpdl_ref count = SPECPDL_INDEX ();
Lisp_Object raw_name;
AUTO_STRING (slash_colon, "/:");
@@ -440,8 +504,8 @@ init_cmdargs (int argc, char **argv, int skip_args, char *original_pwd)
if (NILP (Vinvocation_directory))
{
Lisp_Object found;
- int yes = openp (Vexec_path, Vinvocation_name,
- Vexec_suffixes, &found, make_number (X_OK), false);
+ int yes = openp (Vexec_path, Vinvocation_name, Vexec_suffixes,
+ &found, make_fixnum (X_OK), false, false);
if (yes == 1)
{
/* Add /: to the front of the name
@@ -468,9 +532,6 @@ init_cmdargs (int argc, char **argv, int skip_args, char *original_pwd)
if (!NILP (Vinvocation_directory))
{
- if (NILP (Vpurify_flag) && !NILP (Ffboundp (Qfile_truename)))
- Vinvocation_directory = call1 (Qfile_truename, Vinvocation_directory);
-
dir = Vinvocation_directory;
#ifdef WINDOWSNT
/* If we are running from the build directory, set DIR to the
@@ -479,8 +540,15 @@ init_cmdargs (int argc, char **argv, int skip_args, char *original_pwd)
if (SBYTES (dir) > sizeof ("/i386/") - 1
&& 0 == strcmp (SSDATA (dir) + SBYTES (dir) - sizeof ("/i386/") + 1,
"/i386/"))
- dir = Fexpand_file_name (build_string ("../.."), dir);
-#else /* !WINDOWSNT */
+ {
+ if (NILP (Vpurify_flag))
+ {
+ Lisp_Object file_truename = intern ("file-truename");
+ if (!NILP (Ffboundp (file_truename)))
+ dir = call1 (file_truename, dir);
+ }
+ dir = Fexpand_file_name (build_string ("../.."), dir);
+ }
#endif
name = Fexpand_file_name (Vinvocation_name, dir);
while (1)
@@ -510,8 +578,7 @@ init_cmdargs (int argc, char **argv, int skip_args, char *original_pwd)
etc_exists = Ffile_exists_p (tem);
if (!NILP (etc_exists))
{
- Vinstallation_directory
- = Ffile_name_as_directory (dir);
+ Vinstallation_directory = Ffile_name_as_directory (dir);
break;
}
}
@@ -536,8 +603,7 @@ init_cmdargs (int argc, char **argv, int skip_args, char *original_pwd)
if (!NILP (etc_exists))
{
tem = Fexpand_file_name (build_string (".."), dir);
- Vinstallation_directory
- = Ffile_name_as_directory (tem);
+ Vinstallation_directory = Ffile_name_as_directory (tem);
break;
}
}
@@ -625,7 +691,9 @@ argmatch (char **argv, int argc, const char *sstr, const char *lstr,
}
arglen = (valptr != NULL && (p = strchr (arg, '=')) != NULL
? p - arg : strlen (arg));
- if (lstr == 0 || arglen < minlen || strncmp (arg, lstr, arglen) != 0)
+ if (!lstr)
+ return 0;
+ if (arglen < minlen || strncmp (arg, lstr, arglen) != 0)
return 0;
else if (valptr == NULL)
{
@@ -650,25 +718,509 @@ argmatch (char **argv, int argc, const char *sstr, const char *lstr,
}
}
-/* Close standard output and standard error, reporting any write
- errors as best we can. This is intended for use with atexit. */
-static void
-close_output_streams (void)
+/* Find a name (absolute or relative) of the Emacs executable whose
+ name (as passed into this program) is ARGV0. Called early in
+ initialization by portable dumper loading code, so avoid Lisp and
+ associated machinery. Return a heap-allocated string giving a name
+ of the Emacs executable, or an empty heap-allocated string or NULL
+ if not found. Store into *CANDIDATE_SIZE a lower bound on the size
+ of any heap allocation. */
+static char *
+find_emacs_executable (char const *argv0, ptrdiff_t *candidate_size)
{
- if (close_stream (stdout) != 0)
+ *candidate_size = 0;
+
+ /* Use xstrdup etc. to allocate storage, so as to call our private
+ implementation of malloc, since the caller calls our free. */
+#ifdef WINDOWSNT
+ char *prog_fname = w32_my_exename ();
+ if (prog_fname)
+ *candidate_size = strlen (prog_fname) + 1;
+ return prog_fname ? xstrdup (prog_fname) : NULL;
+#else /* !WINDOWSNT */
+ char *candidate = NULL;
+
+ /* If the executable name contains a slash, we have some kind of
+ path already, so just resolve symlinks and return the result. */
+ eassert (argv0);
+ if (strchr (argv0, DIRECTORY_SEP))
{
- emacs_perror ("Write error to standard output");
- _exit (EXIT_FAILURE);
+ char *real_name = realpath (argv0, NULL);
+
+ if (real_name)
+ {
+ *candidate_size = strlen (real_name) + 1;
+ return real_name;
+ }
+
+ char *val = xstrdup (argv0);
+ *candidate_size = strlen (val) + 1;
+ return val;
+ }
+ ptrdiff_t argv0_length = strlen (argv0);
+
+ const char *path = getenv ("PATH");
+ if (!path)
+ {
+ /* Default PATH is implementation-defined, so we don't know how
+ to conduct the search. */
+ return NULL;
+ }
+
+ /* Actually try each concatenation of a path element and the
+ executable basename. */
+ do
+ {
+ static char const path_sep[] = { SEPCHAR, '\0' };
+ ptrdiff_t path_part_length = strcspn (path, path_sep);
+ const char *path_part = path;
+ path += path_part_length;
+ if (path_part_length == 0)
+ {
+ path_part = ".";
+ path_part_length = 1;
+ }
+ ptrdiff_t needed = path_part_length + 1 + argv0_length + 1;
+ if (*candidate_size <= needed)
+ {
+ xfree (candidate);
+ candidate = xpalloc (NULL, candidate_size,
+ needed - *candidate_size + 1, -1, 1);
+ }
+ memcpy (candidate + 0, path_part, path_part_length);
+ candidate[path_part_length] = DIRECTORY_SEP;
+ memcpy (candidate + path_part_length + 1, argv0, argv0_length + 1);
+ struct stat st;
+ if (file_access_p (candidate, X_OK)
+ && stat (candidate, &st) == 0 && S_ISREG (st.st_mode))
+ {
+ /* People put on PATH a symlink to the real Emacs
+ executable, with all the auxiliary files where the real
+ executable lives. Support that. */
+ if (lstat (candidate, &st) == 0 && S_ISLNK (st.st_mode))
+ {
+ char *real_name = realpath (candidate, NULL);
+
+ if (real_name)
+ {
+ *candidate_size = strlen (real_name) + 1;
+ return real_name;
+ }
+ }
+ return candidate;
+ }
+ *candidate = '\0';
}
+ while (*path++ != '\0');
+
+ return candidate;
+#endif /* !WINDOWSNT */
+}
- /* Do not close stderr if addresses are being sanitized, as the
- sanitizer might report to stderr after this function is
- invoked. */
- if (!ADDRESS_SANITIZER && close_stream (stderr) != 0)
- _exit (EXIT_FAILURE);
+#ifdef HAVE_PDUMPER
+
+static const char *
+dump_error_to_string (int result)
+{
+ switch (result)
+ {
+ case PDUMPER_LOAD_SUCCESS:
+ return "success";
+ case PDUMPER_LOAD_OOM:
+ return "out of memory";
+ case PDUMPER_NOT_LOADED:
+ return "not loaded";
+ case PDUMPER_LOAD_FILE_NOT_FOUND:
+ return "could not open file";
+ case PDUMPER_LOAD_BAD_FILE_TYPE:
+ return "not a dump file";
+ case PDUMPER_LOAD_FAILED_DUMP:
+ return "dump file is result of failed dump attempt";
+ case PDUMPER_LOAD_VERSION_MISMATCH:
+ return "not built for this Emacs executable";
+ default:
+ return (result <= PDUMPER_LOAD_ERROR
+ ? "generic error"
+ : strerror (result - PDUMPER_LOAD_ERROR));
+ }
}
-/* ARGSUSED */
+/* This function returns the Emacs executable. */
+static char *
+load_pdump (int argc, char **argv)
+{
+ const char *const suffix = ".pdmp";
+ int result;
+ char *emacs_executable = argv[0];
+ ptrdiff_t hexbuf_size;
+ char *hexbuf;
+ const char *strip_suffix =
+#if defined DOS_NT || defined CYGWIN
+ ".exe"
+#else
+ NULL
+#endif
+ ;
+ const char *argv0_base =
+#ifdef NS_SELF_CONTAINED
+ "Emacs"
+#else
+ "emacs"
+#endif
+ ;
+
+ /* TODO: maybe more thoroughly scrub process environment in order to
+ make this use case (loading a dump file in an unexeced emacs)
+ possible? Right now, we assume that things we don't touch are
+ zero-initialized, and in an unexeced Emacs, this assumption
+ doesn't hold. */
+ if (initialized)
+ fatal ("cannot load dump file in unexeced Emacs");
+
+ /* Look for an explicitly-specified dump file. */
+ const char *path_exec = PATH_EXEC;
+ char *dump_file = NULL;
+ int skip_args = 0;
+ while (skip_args < argc - 1)
+ {
+ if (argmatch (argv, argc, "-dump-file", "--dump-file", 6,
+ &dump_file, &skip_args)
+ || argmatch (argv, argc, "--", NULL, 2, NULL, &skip_args))
+ break;
+ skip_args++;
+ }
+
+ /* Where's our executable? */
+ ptrdiff_t bufsize;
+#ifndef NS_SELF_CONTAINED
+ ptrdiff_t exec_bufsize;
+#endif
+ emacs_executable = find_emacs_executable (argv[0], &bufsize);
+#ifndef NS_SELF_CONTAINED
+ exec_bufsize = bufsize;
+#endif
+
+ /* If we couldn't find our executable, go straight to looking for
+ the dump in the hardcoded location. */
+ if (!(emacs_executable && *emacs_executable))
+ goto hardcoded;
+
+ if (dump_file)
+ {
+ result = pdumper_load (dump_file, emacs_executable);
+
+ if (result != PDUMPER_LOAD_SUCCESS)
+ fatal ("could not load dump file \"%s\": %s",
+ dump_file, dump_error_to_string (result));
+ return emacs_executable;
+ }
+
+ /* Look for a dump file in the same directory as the executable; it
+ should have the same basename. Take care to search PATH to find
+ the executable if needed. We're too early in init to use Lisp,
+ so we can't use decode_env_path. We're working in whatever
+ encoding the system natively uses for filesystem access, so
+ there's no need for character set conversion. */
+ ptrdiff_t exenamelen = strlen (emacs_executable);
+ if (strip_suffix)
+ {
+ ptrdiff_t strip_suffix_length = strlen (strip_suffix);
+ ptrdiff_t prefix_length = exenamelen - strip_suffix_length;
+ if (0 <= prefix_length
+ && !memcmp (&emacs_executable[prefix_length], strip_suffix,
+ strip_suffix_length))
+ exenamelen = prefix_length;
+ }
+ ptrdiff_t needed = exenamelen + strlen (suffix) + 1;
+ dump_file = xpalloc (NULL, &bufsize, needed - bufsize, -1, 1);
+ memcpy (dump_file, emacs_executable, exenamelen);
+ strcpy (dump_file + exenamelen, suffix);
+ result = pdumper_load (dump_file, emacs_executable);
+ if (result == PDUMPER_LOAD_SUCCESS)
+ goto out;
+
+ if (result != PDUMPER_LOAD_FILE_NOT_FOUND)
+ fatal ("could not load dump file \"%s\": %s",
+ dump_file, dump_error_to_string (result));
+
+ hardcoded:
+
+#ifdef WINDOWSNT
+ /* On MS-Windows, PATH_EXEC normally starts with a literal
+ "%emacs_dir%", so it will never work without some tweaking. */
+ path_exec = w32_relocate (path_exec);
+#elif defined (HAVE_NS)
+ path_exec = ns_relocate (path_exec);
+#endif
+
+ /* Look for "emacs-FINGERPRINT.pdmp" in PATH_EXEC. We hardcode
+ "emacs" in "emacs-FINGERPRINT.pdmp" so that the Emacs binary
+ still works if the user copies and renames it. */
+ hexbuf_size = 2 * sizeof fingerprint;
+ hexbuf = xmalloc (hexbuf_size + 1);
+ hexbuf_digest (hexbuf, (char *) fingerprint, sizeof fingerprint);
+ hexbuf[hexbuf_size] = '\0';
+ needed = (strlen (path_exec)
+ + 1
+ + strlen (argv0_base)
+ + 1
+ + strlen (hexbuf)
+ + strlen (suffix)
+ + 1);
+ if (bufsize < needed)
+ {
+ xfree (dump_file);
+ dump_file = xpalloc (NULL, &bufsize, needed - bufsize, -1, 1);
+ }
+ sprintf (dump_file, "%s%c%s-%s%s",
+ path_exec, DIRECTORY_SEP, argv0_base, hexbuf, suffix);
+#if !defined (NS_SELF_CONTAINED)
+ if (!(emacs_executable && *emacs_executable))
+ {
+ /* If we didn't find the Emacs binary, assume that it lives in a
+ sibling directory as set up by the default installation
+ configuration. */
+ const char *go_up = "../../../../bin/";
+ needed += (strip_suffix ? strlen (strip_suffix) : 0)
+ - strlen (suffix) + strlen (go_up);
+ if (exec_bufsize < needed)
+ {
+ xfree (emacs_executable);
+ emacs_executable = xpalloc (NULL, &exec_bufsize,
+ needed - exec_bufsize, -1, 1);
+ }
+ sprintf (emacs_executable, "%s%c%s%s%s",
+ path_exec, DIRECTORY_SEP, go_up, argv0_base,
+ strip_suffix ? strip_suffix : "");
+ }
+#endif
+ result = pdumper_load (dump_file, emacs_executable);
+
+ if (result == PDUMPER_LOAD_FILE_NOT_FOUND)
+ {
+ /* Finally, look for basename(argv0)+".pdmp" in PATH_EXEC.
+ This way, they can rename both the executable and its pdump
+ file in PATH_EXEC, and have several Emacs configurations in
+ the same versioned libexec subdirectory. */
+ char *p, *last_sep = NULL;
+ for (p = argv[0]; *p; p++)
+ {
+ if (IS_DIRECTORY_SEP (*p))
+ last_sep = p;
+ }
+ argv0_base = last_sep ? last_sep + 1 : argv[0];
+ ptrdiff_t needed = (strlen (path_exec)
+ + 1
+ + strlen (argv0_base)
+ + strlen (suffix)
+ + 1);
+ if (bufsize < needed)
+ {
+ xfree (dump_file);
+ dump_file = xmalloc (needed);
+ }
+#ifdef DOS_NT
+ ptrdiff_t argv0_len = strlen (argv0_base);
+ if (argv0_len >= 4
+ && c_strcasecmp (argv0_base + argv0_len - 4, ".exe") == 0)
+ sprintf (dump_file, "%s%c%.*s%s", path_exec, DIRECTORY_SEP,
+ (int)(argv0_len - 4), argv0_base, suffix);
+ else
+#endif
+ sprintf (dump_file, "%s%c%s%s",
+ path_exec, DIRECTORY_SEP, argv0_base, suffix);
+ result = pdumper_load (dump_file, emacs_executable);
+ }
+
+ if (result != PDUMPER_LOAD_SUCCESS)
+ {
+ if (result != PDUMPER_LOAD_FILE_NOT_FOUND)
+ fatal ("could not load dump file \"%s\": %s",
+ dump_file, dump_error_to_string (result));
+ }
+
+ out:
+ xfree (dump_file);
+
+ return emacs_executable;
+}
+#endif /* HAVE_PDUMPER */
+
+#if SECCOMP_USABLE
+
+/* Wrapper function for the `seccomp' system call on GNU/Linux. This
+ system call usually doesn't have a wrapper function. See the
+ manual page of `seccomp' for the signature. */
+
+static int
+emacs_seccomp (unsigned int operation, unsigned int flags, void *args)
+{
+#ifdef SYS_seccomp
+ return syscall (SYS_seccomp, operation, flags, args);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+/* Read SIZE bytes into BUFFER. Return the number of bytes read, or
+ -1 if reading failed altogether. */
+
+static ptrdiff_t
+read_full (int fd, void *buffer, ptrdiff_t size)
+{
+ eassert (0 <= fd);
+ eassert (buffer != NULL);
+ eassert (0 <= size);
+ enum
+ {
+ /* See MAX_RW_COUNT in sysdep.c. */
+#ifdef MAX_RW_COUNT
+ max_size = MAX_RW_COUNT
+#else
+ max_size = INT_MAX >> 18 << 18
+#endif
+ };
+ if (PTRDIFF_MAX < size || max_size < size)
+ {
+ errno = EFBIG;
+ return -1;
+ }
+ char *ptr = buffer;
+ ptrdiff_t read = 0;
+ while (size != 0)
+ {
+ ptrdiff_t n = emacs_read (fd, ptr, size);
+ if (n < 0)
+ return -1;
+ if (n == 0)
+ break; /* Avoid infinite loop on encountering EOF. */
+ eassert (n <= size);
+ size -= n;
+ ptr += n;
+ read += n;
+ }
+ return read;
+}
+
+/* Attempt to load Secure Computing filters from FILE. Return false
+ if that doesn't work for some reason. */
+
+static bool
+load_seccomp (const char *file)
+{
+ bool success = false;
+ void *buffer = NULL;
+ int fd
+ = emacs_open_noquit (file, O_RDONLY | O_CLOEXEC | O_BINARY, 0);
+ if (fd < 0)
+ {
+ emacs_perror ("open");
+ goto out;
+ }
+ struct stat stat;
+ if (fstat (fd, &stat) != 0)
+ {
+ emacs_perror ("fstat");
+ goto out;
+ }
+ if (! S_ISREG (stat.st_mode))
+ {
+ fprintf (stderr, "seccomp file %s is not regular\n", file);
+ goto out;
+ }
+ struct sock_fprog program;
+ if (stat.st_size <= 0 || SIZE_MAX <= stat.st_size
+ || PTRDIFF_MAX <= stat.st_size
+ || stat.st_size % sizeof *program.filter != 0)
+ {
+ fprintf (stderr, "seccomp filter %s has invalid size %ld\n",
+ file, (long) stat.st_size);
+ goto out;
+ }
+ size_t size = stat.st_size;
+ size_t count = size / sizeof *program.filter;
+ eassert (0 < count && count < SIZE_MAX);
+ if (USHRT_MAX < count)
+ {
+ fprintf (stderr, "seccomp filter %s is too big\n", file);
+ goto out;
+ }
+ /* Try reading one more byte to detect file size changes. */
+ buffer = malloc (size + 1);
+ if (buffer == NULL)
+ {
+ emacs_perror ("malloc");
+ goto out;
+ }
+ ptrdiff_t read = read_full (fd, buffer, size + 1);
+ if (read < 0)
+ {
+ emacs_perror ("read");
+ goto out;
+ }
+ eassert (read <= SIZE_MAX);
+ if (read != size)
+ {
+ fprintf (stderr,
+ "seccomp filter %s changed size while reading\n",
+ file);
+ goto out;
+ }
+ if (emacs_close (fd) != 0)
+ emacs_perror ("close"); /* not a fatal error */
+ fd = -1;
+ program.len = count;
+ program.filter = buffer;
+
+ /* See man page of `seccomp' why this is necessary. Note that we
+ intentionally don't check the return value: a parent process
+ might have made this call before, in which case it would fail;
+ or, if enabling privilege-restricting mode fails, the `seccomp'
+ syscall will fail anyway. */
+ prctl (PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ /* Install the filter. Make sure that potential other threads can't
+ escape it. */
+ if (emacs_seccomp (SECCOMP_SET_MODE_FILTER,
+ SECCOMP_FILTER_FLAG_TSYNC, &program)
+ != 0)
+ {
+ emacs_perror ("seccomp");
+ goto out;
+ }
+ success = true;
+
+ out:
+ if (0 <= fd)
+ emacs_close (fd);
+ free (buffer);
+ return success;
+}
+
+/* Load Secure Computing filter from file specified with the --seccomp
+ option. Exit if that fails. */
+
+static void
+maybe_load_seccomp (int argc, char **argv)
+{
+ int skip_args = 0;
+ char *file = NULL;
+ while (skip_args < argc - 1)
+ {
+ if (argmatch (argv, argc, "-seccomp", "--seccomp", 9, &file,
+ &skip_args)
+ || argmatch (argv, argc, "--", NULL, 2, NULL, &skip_args))
+ break;
+ ++skip_args;
+ }
+ if (file == NULL)
+ return;
+ if (! load_seccomp (file))
+ fatal ("cannot enable seccomp filter from %s", file);
+}
+
+#endif /* SECCOMP_USABLE */
+
int
main (int argc, char **argv)
{
@@ -676,9 +1228,13 @@ main (int argc, char **argv)
for pointers. */
void *stack_bottom_variable;
- bool do_initial_setlocale;
- bool dumping;
- int skip_args = 0;
+ /* First, check whether we should apply a seccomp filter. This
+ should come at the very beginning to allow the filter to protect
+ the initialization phase. */
+#if SECCOMP_USABLE
+ maybe_load_seccomp (argc, argv);
+#endif
+
bool no_loadup = false;
char *junk = 0;
char *dname_arg = 0;
@@ -688,54 +1244,100 @@ main (int argc, char **argv)
char *ch_to_dir = 0;
/* If we use --chdir, this records the original directory. */
- char *original_pwd = 0;
+ char const *original_pwd = 0;
/* Record (approximately) where the stack begins. */
stack_bottom = (char *) &stack_bottom_variable;
-#ifndef CANNOT_DUMP
- dumping = !initialized && (strcmp (argv[argc - 1], "dump") == 0
- || strcmp (argv[argc - 1], "bootstrap") == 0);
-#else
- dumping = false;
+ const char *dump_mode = NULL;
+ int skip_args = 0;
+ char *temacs = NULL;
+ while (skip_args < argc - 1)
+ {
+ if (argmatch (argv, argc, "-temacs", "--temacs", 8, &temacs, &skip_args)
+ || argmatch (argv, argc, "--", NULL, 2, NULL, &skip_args))
+ break;
+ skip_args++;
+ }
+#ifdef HAVE_PDUMPER
+ bool attempt_load_pdump = false;
#endif
- /* True if address randomization interferes with memory allocation. */
-# ifdef __PPC64__
- bool disable_aslr = true;
-# else
- bool disable_aslr = dumping;
-# endif
-
- if (disable_aslr && disable_address_randomization ())
+ /* Look for this argument first, before any heap allocation, so we
+ can set heap flags properly if we're going to unexec. */
+ if (!initialized && temacs)
{
- /* Set this so the personality will be reverted before execs
- after this one. */
- xputenv ("EMACS_HEAP_EXEC=true");
-
- /* Address randomization was enabled, but is now disabled.
- Re-execute Emacs to get a clean slate. */
- execvp (argv[0], argv);
-
- /* If the exec fails, warn and then try anyway. */
- perror (argv[0]);
+#ifdef HAVE_UNEXEC
+ if (strcmp (temacs, "dump") == 0 ||
+ strcmp (temacs, "bootstrap") == 0)
+ gflags.will_dump_with_unexec_ = true;
+#endif
+#ifdef HAVE_PDUMPER
+ if (strcmp (temacs, "pdump") == 0 ||
+ strcmp (temacs, "pbootstrap") == 0)
+ gflags.will_dump_with_pdumper_ = true;
+#endif
+#if defined HAVE_PDUMPER || defined HAVE_UNEXEC
+ if (strcmp (temacs, "bootstrap") == 0 ||
+ strcmp (temacs, "pbootstrap") == 0)
+ gflags.will_bootstrap_ = true;
+ gflags.will_dump_ =
+ will_dump_with_pdumper_p () ||
+ will_dump_with_unexec_p ();
+ if (will_dump_p ())
+ dump_mode = temacs;
+#endif
+ if (!dump_mode)
+ fatal ("Invalid temacs mode '%s'", temacs);
+ }
+ else if (temacs)
+ {
+ fatal ("--temacs not supported for unexeced emacs");
+ }
+ else
+ {
+ eassert (!temacs);
+#ifndef HAVE_UNEXEC
+ eassert (!initialized);
+#endif
+#ifdef HAVE_PDUMPER
+ if (!initialized)
+ attempt_load_pdump = true;
+#endif
}
-#ifndef CANNOT_DUMP
- might_dump = !initialized;
+#ifdef HAVE_UNEXEC
+ if (!will_dump_with_unexec_p ())
+ gflags.will_not_unexec_ = true;
+#endif
-# ifdef GNU_LINUX
- if (!initialized)
+#ifdef WINDOWSNT
+ /* Grab our malloc arena space now, before anything important
+ happens. This relies on the static heap being needed only in
+ temacs and only if we are going to dump with unexec. */
+ bool use_dynamic_heap = true;
+ if (temacs)
{
- char *heap_start = my_heap_start ();
- heap_bss_diff = heap_start - max (my_endbss, my_endbss_static);
+ char *temacs_str = NULL, *p;
+ for (p = argv[0]; (p = strstr (p, "temacs")) != NULL; p++)
+ temacs_str = p;
+ if (temacs_str != NULL
+ && (temacs_str == argv[0] || IS_DIRECTORY_SEP (temacs_str[-1])))
+ {
+ /* Note that gflags are set at this point only if we have been
+ called with the --temacs=METHOD option. We assume here that
+ temacs is always called that way, otherwise the functions
+ that rely on gflags, like will_dump_with_pdumper_p below,
+ will not do their job. */
+ use_dynamic_heap = will_dump_with_pdumper_p ();
+ }
}
-# endif
+ 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
- early as possible. (unexw32.c calls this function as well, but
+ early as possible. (w32proc.c calls this function as well, but
the additional call here is harmless.) */
cache_system_info ();
#ifdef WINDOWSNT
@@ -746,17 +1348,37 @@ main (int argc, char **argv)
/* Initialize the codepage for file names, needed to decode
non-ASCII file names during startup. */
w32_init_file_name_codepage ();
+ /* Initialize the startup directory, needed for emacs_wd below. */
+ w32_init_current_directory ();
#endif
w32_init_main_thread ();
#endif
+#ifdef HAVE_PDUMPER
+ if (attempt_load_pdump)
+ initial_emacs_executable = load_pdump (argc, argv);
+#else
+ ptrdiff_t bufsize;
+ initial_emacs_executable = find_emacs_executable (argv[0], &bufsize);
+#endif
+
+ argc = maybe_disable_address_randomization (argc, argv);
+
+#if defined GNU_LINUX && defined HAVE_UNEXEC
+ if (!initialized)
+ {
+ char *heap_start = my_heap_start ();
+ heap_bss_diff = heap_start - max (my_endbss, my_endbss_static);
+ }
+#endif
+
#ifdef RUN_TIME_REMAP
if (initialized)
run_time_remap (argv[0]);
#endif
/* If using unexmacosx.c (set by s/darwin.h), we must do this. */
-#if defined DARWIN_OS && !defined CANNOT_DUMP
+#if defined DARWIN_OS && defined HAVE_UNEXEC
if (!initialized)
unexec_init_emacs_zone ();
#endif
@@ -764,50 +1386,78 @@ main (int argc, char **argv)
init_standard_fds ();
atexit (close_output_streams);
+ /* Command-line argument processing.
+
+ The arguments in the argv[] array are sorted in the descending
+ order of their priority as defined in the standard_args[] array
+ below. Then the sorted arguments are processed from the highest
+ to the lowest priority. Each command-line argument that is
+ recognized by 'main', if found in argv[], causes skip_args to be
+ incremented, effectively removing the processed argument from the
+ command line.
+
+ Then init_cmdargs is called, and conses a list of the unprocessed
+ command-line arguments, as strings, in 'command-line-args'. It
+ ignores all the arguments up to the one indexed by skip_args, as
+ those were already processed.
+
+ The arguments in 'command-line-args' are further processed by
+ startup.el, functions 'command-line' and 'command-line-1'. The
+ first of them handles the arguments which need to be processed
+ before loading the user init file and initializing the
+ window-system. The second one processes the arguments that are
+ related to the GUI system, like -font, -geometry, and -title, and
+ then processes the rest of arguments whose priority is below
+ those that are related to the GUI system. The arguments
+ processed by 'command-line' are removed from 'command-line-args';
+ the arguments processed by 'command-line-1' aren't, they are only
+ removed from 'command-line-args-left'.
+
+ 'command-line-1' emits an error message for any argument it
+ doesn't recognize, so any command-line arguments processed in C
+ below whose priority is below the GUI system related switches
+ should be explicitly recognized, ignored, and removed from
+ 'command-line-args-left' in 'command-line-1'. */
+
+ bool only_version = false;
sort_args (argc, argv);
argc = 0;
while (argv[argc]) argc++;
+ skip_args = 0;
if (argmatch (argv, argc, "-version", "--version", 3, NULL, &skip_args))
+ only_version = true;
+
+#ifdef HAVE_PDUMPER
+ if (argmatch (argv, argc, "-fingerprint", "--fingerprint", 4,
+ NULL, &skip_args)
+ && !only_version)
{
- const char *version, *copyright;
if (initialized)
- {
- Lisp_Object tem, tem2;
- tem = Fsymbol_value (intern_c_string ("emacs-version"));
- tem2 = Fsymbol_value (intern_c_string ("emacs-copyright"));
- if (!STRINGP (tem))
- {
- fprintf (stderr, "Invalid value of 'emacs-version'\n");
- exit (1);
- }
- if (!STRINGP (tem2))
- {
- fprintf (stderr, "Invalid value of 'emacs-copyright'\n");
- exit (1);
- }
- else
- {
- version = SSDATA (tem);
- copyright = SSDATA (tem2);
- }
- }
+ {
+ dump_fingerprint (stdout, "",
+ (unsigned char *) fingerprint);
+ exit (0);
+ }
else
- {
- version = emacs_version;
- copyright = emacs_copyright;
- }
- printf ("%s %s\n", PACKAGE_NAME, version);
- printf ("%s\n", copyright);
- printf ("%s comes with ABSOLUTELY NO WARRANTY.\n", PACKAGE_NAME);
- printf ("You may redistribute copies of %s\n", PACKAGE_NAME);
- printf ("under the terms of the GNU General Public License.\n");
- printf ("For more information about these matters, ");
- printf ("see the file named COPYING.\n");
- exit (0);
+ {
+ fputs ("Not initialized\n", stderr);
+ exit (1);
+ }
}
+#endif
- if (argmatch (argv, argc, "-chdir", "--chdir", 4, &ch_to_dir, &skip_args))
+ 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);
+#endif
+
+ if (argmatch (argv, argc, "-chdir", "--chdir", 4, &ch_to_dir, &skip_args)
+ && !only_version)
{
#ifdef WINDOWSNT
/* argv[] array is kept in its original ANSI codepage encoding,
@@ -817,13 +1467,18 @@ main (int argc, char **argv)
filename_from_ansi (ch_to_dir, newdir);
ch_to_dir = newdir;
#endif
- original_pwd = emacs_get_current_dir_name ();
if (chdir (ch_to_dir) != 0)
{
fprintf (stderr, "%s: Can't chdir to %s: %s\n",
argv[0], ch_to_dir, strerror (errno));
exit (1);
}
+ original_pwd = emacs_wd;
+#ifdef WINDOWSNT
+ /* Reinitialize Emacs's notion of the startup directory. */
+ w32_init_current_directory ();
+#endif
+ emacs_wd = emacs_get_current_dir_name ();
}
#if defined (HAVE_SETRLIMIT) && defined (RLIMIT_STACK) && !defined (CYGWIN)
@@ -839,9 +1494,9 @@ main (int argc, char **argv)
{
rlim_t lim = rlim.rlim_cur;
- /* Approximate the amount regex.c needs per unit of
+ /* Approximate the amount regex-emacs.c needs per unit of
emacs_re_max_failures, then add 33% to cover the size of the
- smaller stacks that regex.c successively allocates and
+ smaller stacks that regex-emacs.c successively allocates and
discards on its way to the maximum. */
int min_ratio = 20 * sizeof (char *);
int ratio = min_ratio + min_ratio / 3;
@@ -851,10 +1506,7 @@ main (int argc, char **argv)
frames. */
int extra = (30 * 1000) * 50;
- bool try_to_grow_stack = true;
-#ifndef CANNOT_DUMP
- try_to_grow_stack = !noninteractive || initialized;
-#endif
+ bool try_to_grow_stack = !noninteractive || initialized;
if (try_to_grow_stack)
{
@@ -873,22 +1525,25 @@ main (int argc, char **argv)
newlim = rlim.rlim_max;
newlim -= newlim % pagesize;
- if (pagesize <= newlim - lim)
+ if (newlim > lim /* in case rlim_t is an unsigned type */
+ && pagesize <= newlim - lim)
{
rlim.rlim_cur = newlim;
if (setrlimit (RLIMIT_STACK, &rlim) == 0)
lim = newlim;
}
}
- /* If the stack is big enough, let regex.c more of it before
- falling back to heap allocation. */
- emacs_re_safe_alloca = max
- (min (lim - extra, SIZE_MAX) * (min_ratio / ratio),
- MAX_ALLOCA);
+ /* If the stack is big enough, let regex-emacs.c use more of it
+ before falling back to heap allocation. */
+ if (lim < extra)
+ lim = extra; /* avoid wrap-around in unsigned subtraction */
+ ptrdiff_t max_failures
+ = min (lim - extra, min (PTRDIFF_MAX, SIZE_MAX)) / ratio;
+ emacs_re_safe_alloca = max (max_failures * min_ratio, MAX_ALLOCA);
}
#endif /* HAVE_SETRLIMIT and RLIMIT_STACK and not CYGWIN */
- clearerr_unlocked (stdin);
+ clearerr (stdin);
emacs_backtrace (-1);
@@ -908,37 +1563,39 @@ main (int argc, char **argv)
set_binary_mode (STDOUT_FILENO, O_BINARY);
#endif /* MSDOS */
- /* Skip initial setlocale if LC_ALL is "C", as it's not needed in that case.
- The build procedure uses this while dumping, to ensure that the
- dumped Emacs does not have its system locale tables initialized,
- as that might cause screwups when the dumped Emacs starts up. */
- {
- char *lc_all = getenv ("LC_ALL");
- do_initial_setlocale = ! lc_all || strcmp (lc_all, "C");
- }
-
- /* Set locale now, so that initial error messages are localized properly.
- fixup_locale must wait until later, since it builds strings. */
- if (do_initial_setlocale)
- setlocale (LC_ALL, "");
+ /* Set locale, so that initial error messages are localized properly.
+ However, skip this if LC_ALL is "C", as it's not needed in that case.
+ Skipping helps if dumping with unexec, to ensure that the dumped
+ Emacs does not have its system locale tables initialized, as that
+ might cause screwups when the dumped Emacs starts up. */
+ char *lc_all = getenv ("LC_ALL");
+ if (! (lc_all && strcmp (lc_all, "C") == 0))
+ {
+ #ifdef HAVE_NS
+ ns_pool = ns_alloc_autorelease_pool ();
+ ns_init_locale ();
+ #endif
+ setlocale (LC_ALL, "");
+ fixup_locale ();
+ }
text_quoting_flag = using_utf8 ();
inhibit_window_system = 0;
/* Handle the -t switch, which specifies filename to use as terminal. */
- while (1)
+ while (!only_version)
{
char *term;
if (argmatch (argv, argc, "-t", "--terminal", 4, &term, &skip_args))
{
emacs_close (STDIN_FILENO);
emacs_close (STDOUT_FILENO);
- int result = emacs_open (term, O_RDWR, 0);
+ int result = emacs_open_noquit (term, O_RDWR, 0);
if (result != STDIN_FILENO
|| (fcntl (STDIN_FILENO, F_DUPFD_CLOEXEC, STDOUT_FILENO)
!= STDOUT_FILENO))
{
- char *errstring = strerror (errno);
+ const char *errstring = strerror (errno);
fprintf (stderr, "%s: %s: %s\n", argv[0], term, errstring);
exit (EXIT_FAILURE);
}
@@ -964,7 +1621,8 @@ main (int argc, char **argv)
/* Handle the -batch switch, which means don't do interactive display. */
noninteractive = 0;
- if (argmatch (argv, argc, "-batch", "--batch", 5, NULL, &skip_args))
+ if (argmatch (argv, argc, "-batch", "--batch", 5, NULL, &skip_args)
+ || only_version)
{
noninteractive = 1;
Vundo_outer_limit = Qnil;
@@ -981,12 +1639,13 @@ main (int argc, char **argv)
}
/* Handle the --help option, which gives a usage message. */
- if (argmatch (argv, argc, "-help", "--help", 3, NULL, &skip_args))
+ if (argmatch (argv, argc, "-help", "--help", 3, NULL, &skip_args)
+ && !only_version)
{
int i;
printf ("Usage: %s [OPTION-OR-FILENAME]...\n", argv[0]);
for (i = 0; i < ARRAYELTS (usage_message); i++)
- fputs_unlocked (usage_message[i], stdout);
+ fputs (usage_message[i], stdout);
exit (0);
}
@@ -1002,20 +1661,27 @@ main (int argc, char **argv)
int sockfd = -1;
- if (argmatch (argv, argc, "-fg-daemon", "--fg-daemon", 10, NULL, &skip_args)
- || argmatch (argv, argc, "-fg-daemon", "--fg-daemon", 10, &dname_arg, &skip_args))
+ if (!only_version)
{
- daemon_type = 1; /* foreground */
- }
- else if (argmatch (argv, argc, "-daemon", "--daemon", 5, NULL, &skip_args)
- || argmatch (argv, argc, "-daemon", "--daemon", 5, &dname_arg, &skip_args)
- || argmatch (argv, argc, "-bg-daemon", "--bg-daemon", 10, NULL, &skip_args)
- || argmatch (argv, argc, "-bg-daemon", "--bg-daemon", 10, &dname_arg, &skip_args))
- {
- daemon_type = 2; /* background */
+ if (argmatch (argv, argc, "-fg-daemon", "--fg-daemon", 10, NULL,
+ &skip_args)
+ || argmatch (argv, argc, "-fg-daemon", "--fg-daemon", 10, &dname_arg,
+ &skip_args))
+ {
+ daemon_type = 1; /* foreground */
+ }
+ else if (argmatch (argv, argc, "-daemon", "--daemon", 5, NULL, &skip_args)
+ || argmatch (argv, argc, "-daemon", "--daemon", 5, &dname_arg,
+ &skip_args)
+ || argmatch (argv, argc, "-bg-daemon", "--bg-daemon", 10, NULL,
+ &skip_args)
+ || argmatch (argv, argc, "-bg-daemon", "--bg-daemon", 10,
+ &dname_arg, &skip_args))
+ {
+ daemon_type = 2; /* background */
+ }
}
-
if (daemon_type > 0)
{
#ifndef DOS_NT
@@ -1044,7 +1710,7 @@ main (int argc, char **argv)
before exiting. */
if (emacs_pipe (daemon_pipe) != 0)
{
- fprintf (stderr, "Cannot pipe!\n");
+ fputs ("Cannot pipe!\n", stderr);
exit (1);
}
} /* daemon_type == 2 */
@@ -1054,21 +1720,35 @@ main (int argc, char **argv)
int systemd_socket = sd_listen_fds (1);
if (systemd_socket > 1)
- fprintf (stderr,
- ("\n"
- "Warning: systemd passed more than one socket to Emacs.\n"
- "Try 'Accept=false' in the Emacs socket unit file.\n"));
+ fputs (("\n"
+ "Warning: systemd passed more than one socket to Emacs.\n"
+ "Try 'Accept=false' in the Emacs socket unit file.\n"),
+ stderr);
else if (systemd_socket == 1
&& (0 < sd_is_socket (SD_LISTEN_FDS_START,
AF_UNSPEC, SOCK_STREAM, 1)))
sockfd = SD_LISTEN_FDS_START;
#endif /* HAVE_LIBSYSTEMD */
-#ifdef USE_GTK
- fprintf (stderr, "\nWarning: due to a long standing Gtk+ bug\nhttp://bugzilla.gnome.org/show_bug.cgi?id=85715\n\
+ /* On X, the bug happens because we call abort to avoid GLib
+ crashes upon a longjmp in our X error handler.
+
+ On PGTK, GTK calls exit in its own error handlers for either
+ X or Wayland. Display different messages depending on the
+ window system to avoid referring users to the wrong GTK bug
+ report. */
+#ifdef HAVE_PGTK
+ fputs ("Due to a limitation in GTK 3, Emacs built with PGTK will simply exit when a\n"
+ "display connection is closed. The problem is especially difficult to fix,\n"
+ "such that Emacs on Wayland with multiple displays is unlikely ever to be able\n"
+ "to survive disconnects.\n",
+ stderr);
+#elif defined USE_GTK
+ fputs ("\nWarning: due to a long standing Gtk+ bug\nhttps://gitlab.gnome.org/GNOME/gtk/issues/221\n\
Emacs might crash when run in daemon mode and the X11 connection is unexpectedly lost.\n\
-Using an Emacs configured with --with-x-toolkit=lucid does not have this problem.\n");
-#endif /* USE_GTK */
+Using an Emacs configured with --with-x-toolkit=lucid does not have this problem.\n",
+ stderr);
+#endif
if (daemon_type == 2)
{
@@ -1099,12 +1779,12 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
if (retval < 0)
{
- fprintf (stderr, "Error reading status from child\n");
+ fputs ("Error reading status from child\n", stderr);
exit (1);
}
else if (retval == 0)
{
- fprintf (stderr, "Error: server did not start correctly\n");
+ fputs ("Error: server did not start correctly\n", stderr);
exit (1);
}
@@ -1130,7 +1810,7 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
if (! (0 <= fdStrlen && fdStrlen < sizeof fdStr))
{
- fprintf (stderr, "daemon: child name too long\n");
+ fputs ("daemon: child name too long\n", stderr);
exit (EXIT_CANNOT_INVOKE);
}
@@ -1144,12 +1824,13 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
}
/* In exec'd: parse special dname into pipe and name info. */
- if (!dname_arg || !strchr (dname_arg, '\n')
- || strlen (dname_arg) < 1 || strlen (dname_arg) > 70)
- {
- fprintf (stderr, "emacs daemon: daemon name absent or too long\n");
- exit (EXIT_CANNOT_INVOKE);
- }
+ if (!dname_arg || !*dname_arg || strnlen (dname_arg, 71) == 71
+ || !strchr (dname_arg, '\n'))
+ {
+ fputs ("emacs daemon: daemon name absent or too long\n",
+ stderr);
+ exit (EXIT_CANNOT_INVOKE);
+ }
dname_arg2[0] = '\0';
sscanf (dname_arg, "\n%d,%d\n%s", &(daemon_pipe[0]), &(daemon_pipe[1]),
dname_arg2);
@@ -1173,7 +1854,7 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
exit (1);
}
#else /* MSDOS */
- fprintf (stderr, "This platform does not support daemon mode.\n");
+ fputs ("This platform does not support daemon mode.\n", stderr);
exit (1);
#endif /* MSDOS */
if (dname_arg)
@@ -1182,17 +1863,15 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
#if defined HAVE_PTHREAD && !defined SYSTEM_MALLOC \
&& !defined DOUG_LEA_MALLOC && !defined HYBRID_MALLOC
-# ifndef CANNOT_DUMP
/* Do not make gmalloc thread-safe when creating bootstrap-emacs, as
that causes an infinite recursive loop with FreeBSD. See
Bug#14569. The part of this bug involving Cygwin is no longer
relevant, now that Cygwin defines HYBRID_MALLOC. */
- if (!noninteractive || initialized)
-# endif
+ if (!noninteractive || !will_dump_p ())
malloc_enable_thread ();
#endif
- init_signals (dumping);
+ init_signals ();
noninteractive1 = noninteractive;
@@ -1201,8 +1880,8 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
if (!initialized)
{
init_alloc_once ();
- init_threads_once ();
- init_obarray ();
+ init_pdumper_once ();
+ init_obarray_once ();
init_eval_once ();
init_charset_once ();
init_coding_once ();
@@ -1224,7 +1903,7 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
/* Call syms_of_keyboard before init_window_once because
keyboard sets up symbols that include some face names that
the X support will want to use. This can happen when
- CANNOT_DUMP is defined. */
+ Emacs starts up from scratch (e.g., temacs). */
syms_of_keyboard ();
/* Called before syms_of_fileio, because it sets up Qerror_condition. */
@@ -1240,7 +1919,7 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
/* Before init_window_once, because it sets up the
Vcoding_system_hash_table. */
syms_of_coding (); /* This should be after syms_of_fileio. */
-
+ init_frame_once (); /* Before init_window_once. */
init_window_once (); /* Init the window system. */
#ifdef HAVE_WINDOW_SYSTEM
init_fringe_once (); /* Swap bitmaps if necessary. */
@@ -1248,19 +1927,24 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
}
init_alloc ();
+ init_bignum ();
init_threads ();
-
- if (do_initial_setlocale)
- {
- fixup_locale ();
- Vsystem_messages_locale = Vprevious_system_messages_locale;
- Vsystem_time_locale = Vprevious_system_time_locale;
- }
-
init_eval ();
- init_atimer ();
running_asynch_code = 0;
init_random ();
+ init_xfaces ();
+
+#if defined HAVE_JSON && !defined WINDOWSNT
+ init_json ();
+#endif
+
+ if (!initialized)
+ syms_of_comp ();
+
+ /* Do less garbage collection in batch mode (since these tend to be
+ more short-lived, and the memory is returned to the OS on exit
+ anyway). */
+ Vgc_cons_percentage = make_float (noninteractive? 1.0: 0.1);
no_loadup
= argmatch (argv, argc, "-nl", "--no-loadup", 6, NULL, &skip_args);
@@ -1275,7 +1959,7 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
bool module_assertions
= argmatch (argv, argc, "-module-assertions", "--module-assertions", 15,
NULL, &skip_args);
- if (dumping && module_assertions)
+ if (will_dump_p () && module_assertions && !only_version)
{
fputs ("Module assertions are not supported during dumping\n", stderr);
exit (1);
@@ -1284,33 +1968,31 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
#endif
#ifdef HAVE_NS
- ns_pool = ns_alloc_autorelease_pool ();
-#ifdef NS_IMPL_GNUSTEP
- /* GNUstep stupidly resets our locale settings after we made them. */
- fixup_locale ();
-#endif
-
if (!noninteractive)
{
#ifdef NS_IMPL_COCOA
/* Started from GUI? */
- /* FIXME: Do the right thing if getenv returns NULL, or if
- chdir fails. */
- if (! inhibit_window_system && ! isatty (STDIN_FILENO) && ! ch_to_dir)
- chdir (getenv ("HOME"));
+ bool go_home = (!ch_to_dir && !inhibit_window_system
+ && !isatty (STDIN_FILENO));
if (skip_args < argc)
{
if (!strncmp (argv[skip_args], "-psn", 4))
{
skip_args += 1;
- if (! ch_to_dir) chdir (getenv ("HOME"));
+ go_home |= !ch_to_dir;
}
else if (skip_args+1 < argc && !strncmp (argv[skip_args+1], "-psn", 4))
{
skip_args += 2;
- if (! ch_to_dir) chdir (getenv ("HOME"));
+ go_home |= !ch_to_dir;
}
}
+ if (go_home)
+ {
+ char const *home = get_homedir ();
+ if (*home && chdir (home) == 0)
+ emacs_wd = emacs_get_current_dir_name ();
+ }
#endif /* COCOA */
}
#endif /* HAVE_NS */
@@ -1325,7 +2007,7 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
int count_before = skip_args;
/* Skip any number of -d options, but only use the last one. */
- while (1)
+ while (!only_version)
{
int count_before_this = skip_args;
@@ -1361,6 +2043,16 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
no_site_lisp = 1;
}
+ if (argmatch (argv, argc, "-x", 0, 1, &junk, &skip_args))
+ {
+ noninteractive = 1;
+ no_site_lisp = 1;
+ /* This is picked up in startup.el. */
+ argv[skip_args - 1] = (char *) "-scripteval";
+ skip_args -= 1;
+ sort_args (argc, argv);
+ }
+
/* Don't actually discard this arg. */
skip_args = count_before;
}
@@ -1399,11 +2091,6 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
globals_of_gfilenotify ();
#endif
-#ifdef HAVE_NS
- /* Initialize the locale from user defaults. */
- ns_init_locale ();
-#endif
-
/* Initialize and GC-protect Vinitial_environment and
Vprocess_environment before set_initial_environment fills them
in. */
@@ -1412,9 +2099,12 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
/* egetenv is a pretty low-level facility, which may get called in
many circumstances; it seems flimsy to put off initializing it
until calling init_callproc. Do not do it when dumping. */
- if (! dumping)
+ if (!will_dump_p ())
set_initial_environment ();
+ /* Has to run after the environment is set up. */
+ init_atimer ();
+
#ifdef WINDOWSNT
globals_of_w32 ();
#ifdef HAVE_W32NOTIFY
@@ -1426,7 +2116,7 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
variables from the parent process without modifications from
Emacs. */
init_environment (argv);
- init_ntproc (dumping); /* must precede init_editfns. */
+ init_ntproc (will_dump_p ()); /* must precede init_editfns. */
#endif
/* AIX crashes are reported in system versions 3.2.3 and 3.2.4
@@ -1438,9 +2128,10 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
#endif
/* Init buffer storage and default directory of main buffer. */
- init_buffer (initialized);
+ init_buffer ();
- init_callproc_1 (); /* Must precede init_cmdargs and init_sys_modes. */
+ /* Must precede init_cmdargs and init_sys_modes. */
+ init_callproc_1 ();
/* Must precede init_lread. */
init_cmdargs (argc, argv, skip_args, original_pwd);
@@ -1458,6 +2149,72 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
init_callproc (); /* Must follow init_cmdargs but not init_sys_modes. */
init_fileio ();
init_lread ();
+
+ /* If "-version" was specified, produce version information and
+ exit. We do it here because the code below needs to call Lisp
+ primitives, which cannot be done safely before we call all the
+ init_FOO initialization functions above. */
+ if (only_version)
+ {
+ const char *version, *copyright;
+
+ if (initialized)
+ {
+ Lisp_Object tem = Fsymbol_value (intern_c_string ("emacs-version"));
+ Lisp_Object tem2 = Fsymbol_value (intern_c_string ("emacs-copyright"));
+ if (!STRINGP (tem))
+ {
+ fputs ("Invalid value of 'emacs-version'\n", stderr);
+ exit (1);
+ }
+ if (!STRINGP (tem2))
+ {
+ fputs ("Invalid value of 'emacs-copyright'\n", stderr);
+ exit (1);
+ }
+ else
+ {
+ version = SSDATA (tem);
+ copyright = SSDATA (tem2);
+ }
+ }
+ else
+ {
+ version = emacs_version;
+ copyright = emacs_copyright;
+ }
+ printf ("%s %s\n", PACKAGE_NAME, version);
+
+ if (initialized)
+ {
+ Lisp_Object rversion, rbranch, rtime;
+
+ rversion
+ = Fsymbol_value (intern_c_string ("emacs-repository-version"));
+ rbranch
+ = Fsymbol_value (intern_c_string ("emacs-repository-branch"));
+ rtime
+ = Fsymbol_value (intern_c_string ("emacs-build-time"));
+
+ if (!NILP (rversion) && !NILP (rbranch) && !NILP (rtime))
+ printf ("Development version %s on %s branch; build date %s.\n",
+ SSDATA (Fsubstring (rversion, make_fixnum (0),
+ make_fixnum (12))),
+ SSDATA (rbranch),
+ SSDATA (Fformat_time_string (build_string ("%Y-%m-%d"),
+ rtime, Qnil)));
+ }
+
+ printf (("%s\n"
+ "%s comes with ABSOLUTELY NO WARRANTY.\n"
+ "You may redistribute copies of %s\n"
+ "under the terms of the GNU General Public License.\n"
+ "For more information about these matters, "
+ "see the file named COPYING.\n"),
+ copyright, PACKAGE_NAME, PACKAGE_NAME);
+ exit (0);
+ }
+
#ifdef WINDOWSNT
/* Check to see if Emacs has been installed correctly. */
check_windows_init_file ();
@@ -1499,6 +2256,8 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
syms_of_minibuf ();
syms_of_process ();
syms_of_search ();
+ syms_of_sysdep ();
+ syms_of_timefns ();
syms_of_frame ();
syms_of_syntax ();
syms_of_terminal ();
@@ -1522,6 +2281,7 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
#endif
syms_of_window ();
syms_of_xdisp ();
+ syms_of_sqlite ();
syms_of_font ();
#ifdef HAVE_WINDOW_SYSTEM
syms_of_fringe ();
@@ -1532,7 +2292,6 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
syms_of_xfns ();
syms_of_xmenu ();
syms_of_fontset ();
- syms_of_xwidget ();
syms_of_xsettings ();
#ifdef HAVE_X_SM
syms_of_xsmfns ();
@@ -1542,9 +2301,7 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
#endif
#endif /* HAVE_X_WINDOWS */
-#ifdef HAVE_LIBXML2
syms_of_xml ();
-#endif
#ifdef HAVE_LCMS2
syms_of_lcms2 ();
@@ -1563,6 +2320,10 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
syms_of_fontset ();
#endif /* HAVE_NTGUI */
+#if defined HAVE_NTGUI || defined CYGWIN
+ syms_of_w32cygwinx ();
+#endif
+
#if defined WINDOWSNT || defined HAVE_NTGUI
syms_of_w32select ();
#endif
@@ -1582,6 +2343,27 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
syms_of_fontset ();
#endif /* HAVE_NS */
+#ifdef HAVE_PGTK
+ syms_of_pgtkterm ();
+ syms_of_pgtkfns ();
+ syms_of_pgtkselect ();
+ syms_of_pgtkmenu ();
+ syms_of_pgtkim ();
+ syms_of_fontset ();
+ syms_of_xsettings ();
+#endif /* HAVE_PGTK */
+#ifdef HAVE_HAIKU
+ syms_of_haikuterm ();
+ syms_of_haikufns ();
+ syms_of_haikumenu ();
+ syms_of_haikufont ();
+ syms_of_haikuselect ();
+#ifdef HAVE_NATIVE_IMAGE_API
+ syms_of_haikuimage ();
+#endif
+ syms_of_fontset ();
+#endif /* HAVE_HAIKU */
+
syms_of_gnutls ();
#ifdef HAVE_INOTIFY
@@ -1607,15 +2389,21 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
#endif /* HAVE_W32NOTIFY */
#endif /* WINDOWSNT */
+ syms_of_xwidget ();
syms_of_threads ();
syms_of_profiler ();
+ syms_of_pdumper ();
+
+#ifdef HAVE_JSON
+ syms_of_json ();
+#endif
- keys_of_casefiddle ();
- keys_of_cmds ();
- keys_of_buffer ();
keys_of_keyboard ();
- keys_of_keymap ();
- keys_of_window ();
+
+#ifdef HAVE_NATIVE_COMP
+ /* Must be after the last defsubr has run. */
+ hash_native_abi ();
+#endif
}
else
{
@@ -1632,17 +2420,22 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
#endif
}
+#ifdef HAVE_HAIKU
+ init_haiku_select ();
+#endif
+
init_charset ();
- /* This calls putenv and so must precede init_process_emacs. Also,
- it sets Voperating_system_release, which init_process_emacs uses. */
- init_editfns (dumping);
+ /* This calls putenv and so must precede init_process_emacs. */
+ init_timefns ();
+
+ init_editfns ();
/* These two call putenv. */
#ifdef HAVE_DBUS
init_dbusbind ();
#endif
-#ifdef USE_GTK
+#if defined(USE_GTK) && !defined(HAVE_PGTK)
init_xterm ();
#endif
@@ -1652,10 +2445,9 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
init_process_emacs (sockfd);
init_keyboard (); /* This too must precede init_sys_modes. */
- if (!noninteractive)
- init_display (); /* Determine terminal type. Calls init_sys_modes. */
+ init_display (); /* Determine terminal type. Calls init_sys_modes. */
#if HAVE_W32NOTIFY
- else
+ if (noninteractive)
init_crit (); /* w32notify.c needs this in batch mode. */
#endif /* HAVE_W32NOTIFY */
init_xdisp ();
@@ -1683,36 +2475,47 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
/* Unless next switch is -nl, load "loadup.el" first thing. */
if (! no_loadup)
Vtop_level = list2 (Qload, build_string ("loadup.el"));
+
+#ifdef HAVE_NATIVE_COMP
+ /* If we are going to load stuff in a non-initialized Emacs,
+ update the value of native-comp-eln-load-path, so that the
+ *.eln files will be found if they are there. */
+ if (!NILP (Vtop_level) && !temacs)
+ Vnative_comp_eln_load_path =
+ Fcons (Fexpand_file_name (XCAR (Vnative_comp_eln_load_path),
+ Vinvocation_directory),
+ Qnil);
+#endif
}
/* Set up for profiling. This is known to work on FreeBSD,
GNU/Linux and MinGW. It might work on some other systems too.
Give it a try and tell us if it works on your system. To compile
for profiling, use the configure option --enable-profiling. */
-#if defined (__FreeBSD__) || defined (GNU_LINUX) || defined (__MINGW32__)
#ifdef PROFILING
if (initialized)
{
-#ifdef __MINGW32__
- extern unsigned char etext asm ("etext");
-#else
- extern char etext;
-#endif
-
atexit (_mcleanup);
monstartup ((uintptr_t) __executable_start, (uintptr_t) &etext);
}
else
moncontrol (0);
#endif
-#endif
- initialized = 1;
+ initialized = true;
+
+ if (dump_mode)
+ Vdump_mode = build_string (dump_mode);
+
+#ifdef HAVE_PDUMPER
+ /* Allow code to be run (mostly useful after redumping). */
+ safe_run_hooks (Qafter_pdump_load_hook);
+#endif
/* Enter editor command loop. This never returns. */
+ set_initial_minibuffer_mode ();
Frecursive_edit ();
- /* NOTREACHED */
- return 0;
+ eassume (false);
}
/* Sort the args so we can find the most important ones
@@ -1731,6 +2534,9 @@ struct standard_args
static const struct standard_args standard_args[] =
{
{ "-version", "--version", 150, 0 },
+#ifdef HAVE_PDUMPER
+ { "-fingerprint", "--fingerprint", 140, 0 },
+#endif
{ "-chdir", "--chdir", 130, 1 },
{ "-t", "--terminal", 120, 1 },
{ "-nw", "--no-window-system", 110, 0 },
@@ -1754,8 +2560,10 @@ static const struct standard_args standard_args[] =
/* (Note that to imply -nsl, -Q is partially handled here.) */
{ "-Q", "--quick", 55, 0 },
{ "-quick", 0, 55, 0 },
+ { "-x", 0, 55, 0 },
{ "-q", "--no-init-file", 50, 0 },
{ "-no-init-file", 0, 50, 0 },
+ { "-init-directory", "--init-directory", 30, 1 },
{ "-no-x-resources", "--no-x-resources", 40, 0 },
{ "-no-site-file", "--no-site-file", 40, 0 },
{ "-u", "--user", 30, 1 },
@@ -1797,6 +2605,15 @@ static const struct standard_args standard_args[] =
{ "-color", "--color", 5, 0},
{ "-no-splash", "--no-splash", 3, 0 },
{ "-no-desktop", "--no-desktop", 3, 0 },
+ /* The following three must be just above the file-name args, to get
+ them out of our way, but without mixing them with file names. */
+ { "-temacs", "--temacs", 1, 1 },
+#ifdef HAVE_PDUMPER
+ { "-dump-file", "--dump-file", 1, 1 },
+#endif
+#if SECCOMP_USABLE
+ { "-seccomp", "--seccomp", 1, 1 },
+#endif
#ifdef HAVE_NS
{ "-NSAutoLaunch", 0, 5, 1 },
{ "-NXAutoLaunch", 0, 5, 1 },
@@ -1994,26 +2811,64 @@ sort_args (int argc, char **argv)
xfree (priority);
}
-DEFUN ("kill-emacs", Fkill_emacs, Skill_emacs, 0, 1, "P",
+DEFUN ("kill-emacs", Fkill_emacs, Skill_emacs, 0, 2, "P",
doc: /* Exit the Emacs job and kill it.
If ARG is an integer, return ARG as the exit program code.
If ARG is a string, stuff it as keyboard input.
+Any other value of ARG, or ARG omitted, means return an
+exit code that indicates successful program termination.
+
+If RESTART is non-nil, instead of just exiting at the end, start a new
+Emacs process, using the same command line arguments as the currently
+running Emacs process.
This function is called upon receipt of the signals SIGTERM
or SIGHUP, and upon SIGINT in batch mode.
-The value of `kill-emacs-hook', if not void,
-is a list of functions (of no args),
-all of which are called before Emacs is actually killed. */
+The value of `kill-emacs-hook', if not void, is a list of functions
+(of no args), all of which are called before Emacs is actually
+killed. */
attributes: noreturn)
- (Lisp_Object arg)
+ (Lisp_Object arg, Lisp_Object restart)
{
int exit_code;
+#ifndef WINDOWSNT
+ /* Do some checking before shutting down Emacs, because errors
+ can't be meaningfully reported afterwards. */
+ if (!NILP (restart))
+ {
+ /* This is very unlikely, but it's possible to execute a binary
+ (on some systems) with no argv. */
+ if (initial_argc < 1)
+ error ("No command line arguments known; unable to re-execute Emacs");
+
+ /* Check that the binary hasn't gone away. */
+ if (!initial_emacs_executable)
+ error ("Unknown Emacs executable");
+
+ if (!file_access_p (initial_emacs_executable, F_OK))
+ error ("Emacs executable \"%s\" can't be found", initial_argv[0]);
+ }
+#endif
+
+#ifdef HAVE_LIBSYSTEMD
+ /* Notify systemd we are shutting down, but only if we have notified
+ it about startup. */
+ if (daemon_type == -1)
+ sd_notify(0, "STOPPING=1");
+#endif /* HAVE_LIBSYSTEMD */
+
/* Fsignal calls emacs_abort () if it sees that waiting_for_input is
set. */
waiting_for_input = 0;
- run_hook (Qkill_emacs_hook);
+ if (!NILP (find_symbol_value (Qkill_emacs_hook)))
+ {
+ if (noninteractive)
+ safe_run_hooks (Qkill_emacs_hook);
+ else
+ call1 (Qrun_hook_query_error_with_timeout, Qkill_emacs_hook);
+ }
#ifdef HAVE_X_WINDOWS
/* Transfer any clipboards we own to the clipboard manager. */
@@ -2036,10 +2891,25 @@ all of which are called before Emacs is actually killed. */
unlink (SSDATA (listfile));
}
- if (INTEGERP (arg))
- exit_code = (XINT (arg) < 0
- ? XINT (arg) | INT_MIN
- : XINT (arg) & INT_MAX);
+#ifdef HAVE_NATIVE_COMP
+ eln_load_path_final_clean_up ();
+#endif
+
+ if (!NILP (restart))
+ {
+#ifdef WINDOWSNT
+ if (w32_reexec_emacs (initial_cmdline, initial_wd) < 0)
+#else
+ initial_argv[0] = initial_emacs_executable;
+ if (execvp (*initial_argv, initial_argv) < 1)
+#endif
+ emacs_perror ("Unable to re-execute Emacs");
+ }
+
+ if (FIXNUMP (arg))
+ exit_code = (XFIXNUM (arg) < 0
+ ? XFIXNUM (arg) | INT_MIN
+ : XFIXNUM (arg) & INT_MAX);
else
exit_code = EXIT_SUCCESS;
exit (exit_code);
@@ -2068,23 +2938,33 @@ shut_down_emacs (int sig, Lisp_Object stuff)
/* If we are controlling the terminal, reset terminal modes. */
#ifndef DOS_NT
- {
- pid_t pgrp = getpgrp ();
- pid_t tpgrp = tcgetpgrp (0);
- if ((tpgrp != -1) && tpgrp == pgrp)
- {
- reset_all_sys_modes ();
- if (sig && sig != SIGTERM)
- {
- static char const format[] = "Fatal error %d: ";
- char buf[sizeof format - 2 + INT_STRLEN_BOUND (int)];
- int buflen = sprintf (buf, format, sig);
- char const *sig_desc = safe_strsignal (sig);
+ pid_t tpgrp = tcgetpgrp (STDIN_FILENO);
+ if (tpgrp != -1 && tpgrp == getpgrp ())
+ {
+ reset_all_sys_modes ();
+ if (sig && sig != SIGTERM)
+ {
+ static char const fmt[] = "Fatal error %d: %n%s\n";
+#ifdef HAVE_HAIKU
+ if (haiku_debug_on_fatal_error)
+ debugger ("Fatal error in Emacs");
+#endif
+ char buf[max ((sizeof fmt - sizeof "%d%n%s\n"
+ + INT_STRLEN_BOUND (int) + 1),
+ min (PIPE_BUF, MAX_ALLOCA))];
+ char const *sig_desc = safe_strsignal (sig);
+ int nlen;
+ int buflen = snprintf (buf, sizeof buf, fmt, sig, &nlen, sig_desc);
+ if (0 <= buflen && buflen < sizeof buf)
emacs_write (STDERR_FILENO, buf, buflen);
- emacs_write (STDERR_FILENO, sig_desc, strlen (sig_desc));
- }
- }
- }
+ else
+ {
+ emacs_write (STDERR_FILENO, buf, nlen);
+ emacs_write (STDERR_FILENO, sig_desc, strlen (sig_desc));
+ emacs_write (STDERR_FILENO, fmt + sizeof fmt - 2, 1);
+ }
+ }
+ }
#else
fflush (stdout);
reset_all_sys_modes ();
@@ -2110,6 +2990,10 @@ shut_down_emacs (int sig, Lisp_Object stuff)
check_message_stack ();
}
+#ifdef HAVE_NATIVE_COMP
+ eln_load_path_final_clean_up ();
+#endif
+
#ifdef MSDOS
dos_cleanup ();
#endif
@@ -2129,7 +3013,7 @@ shut_down_emacs (int sig, Lisp_Object stuff)
-#ifndef CANNOT_DUMP
+#ifdef HAVE_UNEXEC
#include "unexec.h"
@@ -2143,33 +3027,35 @@ You must run Emacs in batch mode in order to dump it. */)
{
Lisp_Object tem;
Lisp_Object symbol;
- ptrdiff_t count = SPECPDL_INDEX ();
+ specpdl_ref count = SPECPDL_INDEX ();
check_pure_size ();
if (! noninteractive)
error ("Dumping Emacs works only in batch mode");
- if (!might_dump)
- error ("Emacs can be dumped only once");
+ if (dumped_with_unexec_p ())
+ error ("Emacs can be dumped using unexec only once");
+
+ if (definitely_will_not_unexec_p ())
+ error ("This Emacs instance was not started in temacs mode");
-#if defined GNU_LINUX && !defined CANNOT_DUMP
+# if defined GNU_LINUX && defined HAVE_UNEXEC
/* Warn if the gap between BSS end and heap start is larger than this. */
-# define MAX_HEAP_BSS_DIFF (1024*1024)
+# define MAX_HEAP_BSS_DIFF (1024 * 1024)
if (heap_bss_diff > MAX_HEAP_BSS_DIFF)
- {
- fprintf (stderr, "**************************************************\n");
- fprintf (stderr, "Warning: Your system has a gap between BSS and the\n");
- fprintf (stderr, "heap (%"pMu" bytes). This usually means that exec-shield\n",
- heap_bss_diff);
- fprintf (stderr, "or something similar is in effect. The dump may\n");
- fprintf (stderr, "fail because of this. See the section about\n");
- fprintf (stderr, "exec-shield in etc/PROBLEMS for more information.\n");
- fprintf (stderr, "**************************************************\n");
- }
-#endif /* GNU_LINUX */
+ fprintf (stderr,
+ ("**************************************************\n"
+ "Warning: Your system has a gap between BSS and the\n"
+ "heap (%"PRIuMAX" bytes). This usually means that exec-shield\n"
+ "or something similar is in effect. The dump may\n"
+ "fail because of this. See the section about\n"
+ "exec-shield in etc/PROBLEMS for more information.\n"
+ "**************************************************\n"),
+ heap_bss_diff);
+# endif
/* Bind `command-line-processed' to nil before dumping,
so that the dumped Emacs will process its command line
@@ -2193,7 +3079,7 @@ You must run Emacs in batch mode in order to dump it. */)
tem = Vpurify_flag;
Vpurify_flag = Qnil;
-#ifdef HYBRID_MALLOC
+# ifdef HYBRID_MALLOC
{
static char const fmt[] = "%d of %d static heap bytes used";
char buf[sizeof fmt + 2 * (INT_STRLEN_BOUND (int) - 2)];
@@ -2202,18 +3088,21 @@ You must run Emacs in batch mode in order to dump it. */)
/* Don't log messages, because at this point buffers cannot be created. */
message1_nolog (buf);
}
-#endif
+# endif
- fflush_unlocked (stdout);
+ fflush (stdout);
/* Tell malloc where start of impure now is. */
/* Also arrange for warnings when nearly out of space. */
-#if !defined SYSTEM_MALLOC && !defined HYBRID_MALLOC
-#ifndef WINDOWSNT
+# if !defined SYSTEM_MALLOC && !defined HYBRID_MALLOC && !defined WINDOWSNT
/* On Windows, this was done before dumping, and that once suffices.
Meanwhile, my_edata is not valid on Windows. */
memory_warnings (my_edata, malloc_warning);
-#endif /* not WINDOWSNT */
-#endif /* not SYSTEM_MALLOC and not HYBRID_MALLOC */
+# endif
+
+ struct gflags old_gflags = gflags;
+ gflags.will_dump_ = false;
+ gflags.will_dump_with_unexec_ = false;
+ gflags.dumped_with_unexec_ = true;
alloc_unexec_pre ();
@@ -2221,19 +3110,19 @@ You must run Emacs in batch mode in order to dump it. */)
alloc_unexec_post ();
-#ifdef WINDOWSNT
+ gflags = old_gflags;
+
+# ifdef WINDOWSNT
Vlibrary_cache = Qnil;
-#endif
-#ifdef HAVE_WINDOW_SYSTEM
- reset_image_types ();
-#endif
+# endif
Vpurify_flag = tem;
return unbind_to (count, Qnil);
}
-#endif /* not CANNOT_DUMP */
+#endif
+
#if HAVE_SETLOCALE
/* Recover from setlocale (LC_ALL, ""). */
@@ -2253,25 +3142,25 @@ synchronize_locale (int category, Lisp_Object *plocale, Lisp_Object desired_loca
if (! EQ (*plocale, desired_locale))
{
*plocale = desired_locale;
-#ifdef WINDOWSNT
+ char const *locale_string
+ = STRINGP (desired_locale) ? SSDATA (desired_locale) : "";
+# ifdef WINDOWSNT
/* Changing categories like LC_TIME usually requires specifying
an encoding suitable for the new locale, but MS-Windows's
'setlocale' will only switch the encoding when LC_ALL is
specified. So we ignore CATEGORY, use LC_ALL instead, and
then restore LC_NUMERIC to "C", so reading and printing
numbers is unaffected. */
- setlocale (LC_ALL, (STRINGP (desired_locale)
- ? SSDATA (desired_locale)
- : ""));
+ setlocale (LC_ALL, locale_string);
fixup_locale ();
-#else /* !WINDOWSNT */
- setlocale (category, (STRINGP (desired_locale)
- ? SSDATA (desired_locale)
- : ""));
-#endif /* !WINDOWSNT */
+# else /* !WINDOWSNT */
+ setlocale (category, locale_string);
+# endif /* !WINDOWSNT */
}
}
+static Lisp_Object Vprevious_system_time_locale;
+
/* Set system time locale to match Vsystem_time_locale, if possible. */
void
synchronize_system_time_locale (void)
@@ -2280,15 +3169,19 @@ synchronize_system_time_locale (void)
Vsystem_time_locale);
}
+# ifdef LC_MESSAGES
+static Lisp_Object Vprevious_system_messages_locale;
+# endif
+
/* Set system messages locale to match Vsystem_messages_locale, if
possible. */
void
synchronize_system_messages_locale (void)
{
-#ifdef LC_MESSAGES
+# ifdef LC_MESSAGES
synchronize_locale (LC_MESSAGES, &Vprevious_system_messages_locale,
Vsystem_messages_locale);
-#endif
+# endif
}
#endif /* HAVE_SETLOCALE */
@@ -2307,6 +3200,9 @@ decode_env_path (const char *evarname, const char *defalt, bool empty)
{
const char *path, *p;
Lisp_Object lpath, element, tem;
+#ifdef NS_SELF_CONTAINED
+ void *autorelease = NULL;
+#endif
/* Default is to use "." for empty path elements.
But if argument EMPTY is true, use nil instead. */
Lisp_Object empty_element = empty ? Qnil : build_string (".");
@@ -2333,7 +3229,13 @@ decode_env_path (const char *evarname, const char *defalt, bool empty)
path = 0;
if (!path)
{
+#ifdef NS_SELF_CONTAINED
+ /* ns_relocate needs a valid autorelease pool around it. */
+ autorelease = ns_alloc_autorelease_pool ();
+ path = ns_relocate (defalt);
+#else
path = defalt;
+#endif
#ifdef WINDOWSNT
defaulted = 1;
#endif
@@ -2400,7 +3302,7 @@ decode_env_path (const char *evarname, const char *defalt, bool empty)
&& strncmp (path, emacs_dir_env, emacs_dir_len) == 0)
element = Fexpand_file_name (Fsubstring
(element,
- make_number (emacs_dir_len),
+ make_fixnum (emacs_dir_len),
Qnil),
build_unibyte_string (emacs_dir));
#endif
@@ -2432,6 +3334,11 @@ decode_env_path (const char *evarname, const char *defalt, bool empty)
else
break;
}
+
+#ifdef NS_SELF_CONTAINED
+ if (autorelease)
+ ns_release_autorelease_pool (autorelease);
+#endif
return Fnreverse (lpath);
}
@@ -2467,12 +3374,19 @@ from the parent process and its tty file descriptors. */)
error ("This function can only be called after loading the init files");
#ifndef WINDOWSNT
+ if (daemon_type == 1)
+ {
+#ifdef HAVE_LIBSYSTEMD
+ sd_notify(0, "READY=1");
+#endif /* HAVE_LIBSYSTEMD */
+ }
+
if (daemon_type == 2)
{
int nfd;
/* Get rid of stdin, stdout and stderr. */
- nfd = emacs_open ("/dev/null", O_RDWR, 0);
+ nfd = emacs_open_noquit ("/dev/null", O_RDWR, 0);
err |= nfd < 0;
err |= dup2 (nfd, STDIN_FILENO) < 0;
err |= dup2 (nfd, STDOUT_FILENO) < 0;
@@ -2491,7 +3405,7 @@ from the parent process and its tty file descriptors. */)
}
/* Set it to an invalid value so we know we've already run this function. */
- daemon_type = -1;
+ daemon_type = -daemon_type;
#else /* WINDOWSNT */
/* Signal the waiting emacsclient process. */
@@ -2513,8 +3427,10 @@ syms_of_emacs (void)
DEFSYM (Qrisky_local_variable, "risky-local-variable");
DEFSYM (Qkill_emacs, "kill-emacs");
DEFSYM (Qkill_emacs_hook, "kill-emacs-hook");
+ DEFSYM (Qrun_hook_query_error_with_timeout,
+ "run-hook-query-error-with-timeout");
-#ifndef CANNOT_DUMP
+#ifdef HAVE_UNEXEC
defsubr (&Sdump_emacs);
#endif
@@ -2539,6 +3455,7 @@ Special values:
`ms-dos' compiled as an MS-DOS application.
`windows-nt' compiled as a native W32 application.
`cygwin' compiled using the Cygwin library.
+ `haiku' compiled for a Haiku system.
Anything else (in Emacs 26, the possibilities are: aix, berkeley-unix,
hpux, usg-unix-v) indicates some sort of Unix system. */);
Vsystem_type = intern_c_string (SYSTEM_TYPE);
@@ -2562,7 +3479,7 @@ Don't rely on it for testing whether a feature you want to use is available. */
Vsystem_configuration_features = build_string (EMACS_CONFIG_FEATURES);
DEFVAR_BOOL ("noninteractive", noninteractive1,
- doc: /* Non-nil means Emacs is running without interactive terminal. */);
+ doc: /* Non-nil means Emacs is running without interactive terminal. */);
DEFVAR_LISP ("kill-emacs-hook", Vkill_emacs_hook,
doc: /* Hook run when `kill-emacs' is called.
@@ -2603,19 +3520,16 @@ build directory. */);
DEFVAR_LISP ("system-messages-locale", Vsystem_messages_locale,
doc: /* System locale for messages. */);
Vsystem_messages_locale = Qnil;
-
- DEFVAR_LISP ("previous-system-messages-locale",
- Vprevious_system_messages_locale,
- doc: /* Most recently used system locale for messages. */);
+#ifdef LC_MESSAGES
Vprevious_system_messages_locale = Qnil;
+ staticpro (&Vprevious_system_messages_locale);
+#endif
DEFVAR_LISP ("system-time-locale", Vsystem_time_locale,
doc: /* System locale for time. */);
Vsystem_time_locale = Qnil;
-
- DEFVAR_LISP ("previous-system-time-locale", Vprevious_system_time_locale,
- doc: /* Most recently used system locale for time. */);
Vprevious_system_time_locale = Qnil;
+ staticpro (&Vprevious_system_time_locale);
DEFVAR_LISP ("before-init-time", Vbefore_init_time,
doc: /* Value of `current-time' before Emacs begins initialization. */);
@@ -2647,6 +3561,9 @@ component .BUILD is present. This is now stored separately in
doc: /* Address of mailing list for GNU Emacs bugs. */);
Vreport_emacs_bug_address = build_string (emacs_bugreport);
+ DEFVAR_LISP ("dump-mode", Vdump_mode,
+ doc: /* Non-nil when Emacs is dumping itself. */);
+
DEFVAR_LISP ("dynamic-library-alist", Vdynamic_library_alist,
doc: /* Alist of dynamic libraries vs external files implementing them.
Each element is a list (LIBRARY FILE...), where the car is a symbol
@@ -2662,7 +3579,18 @@ because they do not depend on external libraries and are always available.
Also note that this is not a generic facility for accessing external
libraries; only those already known by Emacs will be loaded. */);
+#ifdef WINDOWSNT
+ /* FIXME: We may need to load libgccjit when dumping before
+ term/w32-win.el defines `dynamic-library-alist`. This will fail
+ if that variable is empty, so add libgccjit-0.dll to it. */
+ if (will_dump_p ())
+ Vdynamic_library_alist = list1 (list2 (Qgccjit,
+ build_string ("libgccjit-0.dll")));
+ else
+ Vdynamic_library_alist = Qnil;
+#else
Vdynamic_library_alist = Qnil;
+#endif
Fput (intern_c_string ("dynamic-library-alist"), Qrisky_local_variable, Qt);
#ifdef WINDOWSNT