diff options
Diffstat (limited to 'src/callproc.c')
-rw-r--r-- | src/callproc.c | 1033 |
1 files changed, 708 insertions, 325 deletions
diff --git a/src/callproc.c b/src/callproc.c index 8f13e98fd11..2d457b3c84c 100644 --- a/src/callproc.c +++ b/src/callproc.c @@ -1,6 +1,6 @@ /* Synchronous subprocess invocation for GNU Emacs. -Copyright (C) 1985-1988, 1993-1995, 1999-2017 Free Software Foundation, +Copyright (C) 1985-1988, 1993-1995, 1999-2022 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -21,16 +21,38 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ #include <config.h> #include <errno.h> -#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> +#ifdef MSDOS +extern char **environ; +#endif + #include <sys/file.h> #include <fcntl.h> +/* In order to be able to use `posix_spawn', it needs to support some + variant of `chdir' as well as `setsid'. */ +#if defined HAVE_SPAWN_H && defined HAVE_POSIX_SPAWN \ + && defined HAVE_POSIX_SPAWNATTR_SETFLAGS \ + && (defined HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR \ + || defined HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR_NP) \ + && defined HAVE_DECL_POSIX_SPAWN_SETSID \ + && HAVE_DECL_POSIX_SPAWN_SETSID == 1 +# include <spawn.h> +# define USABLE_POSIX_SPAWN 1 +#else +# define USABLE_POSIX_SPAWN 0 +#endif + #include "lisp.h" +#ifdef SETUP_SLAVE_PTY +# include <sys/stream.h> +# include <sys/stropts.h> +#endif + #ifdef WINDOWSNT #include <sys/socket.h> /* for fcntl */ #include <windows.h> @@ -63,6 +85,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ #include "nsterm.h" #endif +#ifdef HAVE_PGTK +#include "pgtkterm.h" +#endif + /* Pattern used by call-process-region to make temp files. */ static Lisp_Object Vtemp_file_name_pattern; @@ -83,7 +109,7 @@ static pid_t synch_process_pid; #ifdef MSDOS static Lisp_Object synch_process_tempfile; #else -# define synch_process_tempfile make_number (0) +# define synch_process_tempfile make_fixnum (0) #endif /* Indexes of file descriptors that need closing on call_process_kill. */ @@ -100,20 +126,28 @@ enum CALLPROC_FDS }; -static Lisp_Object call_process (ptrdiff_t, Lisp_Object *, int, ptrdiff_t); +static Lisp_Object call_process (ptrdiff_t, Lisp_Object *, int, specpdl_ref); + +#ifdef DOS_NT +# define CHILD_SETUP_TYPE int +#else +# define CHILD_SETUP_TYPE _Noreturn void +#endif + +static CHILD_SETUP_TYPE child_setup (int, int, int, char **, char **, + const char *); /* Return the current buffer's working directory, or the home - directory if it's unreachable, as a string suitable for a system call. - Signal an error if the result would not be an accessible directory. */ + directory if it's unreachable. If ENCODE is true, return as a string + suitable for a system call; otherwise, return a string in its + internal representation. Signal an error if the result would not be + an accessible directory. */ Lisp_Object -encode_current_directory (void) +get_current_directory (bool encode) { - Lisp_Object dir; - - dir = BVAR (current_buffer, directory); - - dir = Funhandled_file_name_directory (dir); + Lisp_Object curdir = BVAR (current_buffer, directory); + Lisp_Object dir = Funhandled_file_name_directory (curdir); /* If the file name handler says that dir is unreachable, use a sensible default. */ @@ -121,19 +155,12 @@ encode_current_directory (void) dir = build_string ("~"); dir = expand_and_dir_to_file (dir); + Lisp_Object encoded_dir = ENCODE_FILE (remove_slash_colon (dir)); - if (NILP (Ffile_accessible_directory_p (dir))) - report_file_error ("Setting current directory", - BVAR (current_buffer, directory)); - - /* Remove "/:" from DIR and encode it. */ - dir = ENCODE_FILE (remove_slash_colon (dir)); + if (! file_accessible_directory_p (encoded_dir)) + report_file_error ("Setting current directory", curdir); - if (! file_accessible_directory_p (dir)) - report_file_error ("Setting current directory", - BVAR (current_buffer, directory)); - - return dir; + return encode ? encoded_dir : dir; } /* If P is reapable, record it as a deleted process and kill it. @@ -220,19 +247,33 @@ static mode_t const default_output_mode = 0666; DEFUN ("call-process", Fcall_process, Scall_process, 1, MANY, 0, doc: /* Call PROGRAM synchronously in separate process. The remaining arguments are optional. -The program's input comes from file INFILE (nil means `/dev/null'). -Insert output in DESTINATION before point; t means current buffer; nil for DESTINATION - means discard it; 0 means discard and don't wait; and `(:file FILE)', where - FILE is a file name string, means that it should be written to that file - (if the file already exists it is overwritten). + +The program's input comes from file INFILE (nil means `null-device'). +If INFILE is a relative path, it will be looked for relative to the +directory where the process is run (see below). If you want to make the +input come from an Emacs buffer, use `call-process-region' instead. + +Third argument DESTINATION specifies how to handle program's output. +(\"Output\" here means both standard output and standard error +output.) +If DESTINATION is a buffer, or t that stands for the current buffer, + it means insert output in that buffer before point. +If DESTINATION is nil, it means discard output; 0 means discard + and don't wait for the program to terminate. +If DESTINATION is `(:file FILE)', where FILE is a file name string, + it means that output should be written to that file (if the file + already exists it is overwritten). DESTINATION can also have the form (REAL-BUFFER STDERR-FILE); in that case, -REAL-BUFFER says what to do with standard output, as above, -while STDERR-FILE says what to do with standard error in the child. -STDERR-FILE may be nil (discard standard error output), -t (mix it with ordinary output), or a file name string. + REAL-BUFFER says what to do with standard output, as above, + while STDERR-FILE says what to do with standard error in the child. + STDERR-FILE may be nil (discard standard error output), + t (mix it with ordinary output), or a file name string. Fourth arg DISPLAY non-nil means redisplay buffer as output is inserted. -Remaining arguments are strings passed as command arguments to PROGRAM. +Remaining arguments ARGS are strings passed as command arguments to PROGRAM. + +If PROGRAM is not an absolute file name, `call-process' will look for +PROGRAM in `exec-path' (which is a list of directories). If executable PROGRAM can't be found as an executable, `call-process' signals a Lisp error. `call-process' reports errors in execution of @@ -252,28 +293,34 @@ usage: (call-process PROGRAM &optional INFILE DESTINATION DISPLAY &rest ARGS) * { Lisp_Object infile, encoded_infile; int filefd; - ptrdiff_t count = SPECPDL_INDEX (); + specpdl_ref count = SPECPDL_INDEX (); if (nargs >= 2 && ! NILP (args[1])) { - infile = Fexpand_file_name (args[1], BVAR (current_buffer, directory)); + /* Expand infile relative to the current buffer's current + directory, or its unhandled equivalent ("~"). */ + infile = Fexpand_file_name (args[1], get_current_directory (false)); CHECK_STRING (infile); } else infile = build_string (NULL_DEVICE); + /* Remove "/:" from INFILE. */ + infile = remove_slash_colon (infile); + encoded_infile = ENCODE_FILE (infile); filefd = emacs_open (SSDATA (encoded_infile), O_RDONLY, 0); if (filefd < 0) report_file_error ("Opening process input file", infile); record_unwind_protect_int (close_file_unwind, filefd); - return unbind_to (count, call_process (nargs, args, filefd, -1)); + return unbind_to (count, call_process (nargs, args, filefd, + make_invalid_specpdl_ref ())); } /* Like Fcall_process (NARGS, ARGS), except use FILEFD as the input file. - If TEMPFILE_INDEX is nonnegative, it is the specpdl index of an + If TEMPFILE_INDEX is valid, it is the specpdl index of an unwinder that is intended to remove the input temporary file; in this case NARGS must be at least 2 and ARGS[1] is the file's name. @@ -281,7 +328,7 @@ usage: (call-process PROGRAM &optional INFILE DESTINATION DISPLAY &rest ARGS) * static Lisp_Object call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd, - ptrdiff_t tempfile_index) + specpdl_ref tempfile_index) { Lisp_Object buffer, current_dir, path; bool display_p; @@ -289,7 +336,7 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd, int callproc_fd[CALLPROC_FDS]; int status; ptrdiff_t i; - ptrdiff_t count = SPECPDL_INDEX (); + specpdl_ref count = SPECPDL_INDEX (); USE_SAFE_ALLOCA; char **new_argv; @@ -301,7 +348,7 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd, char *tempfile = NULL; #else sigset_t oldset; - pid_t pid; + pid_t pid = -1; #endif int child_errno; int fd_output, fd_error; @@ -324,7 +371,7 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd, #ifndef subprocesses /* Without asynchronous processes we cannot have BUFFER == 0. */ if (nargs >= 3 - && (INTEGERP (CONSP (args[2]) ? XCAR (args[2]) : args[2]))) + && (FIXNUMP (CONSP (args[2]) ? XCAR (args[2]) : args[2]))) error ("Operating system cannot handle asynchronous subprocesses"); #endif /* subprocesses */ @@ -397,17 +444,20 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd, /* If the buffer is (still) a list, it might be a (:file "file") spec. */ if (CONSP (buffer) && EQ (XCAR (buffer), QCfile)) { - output_file = Fexpand_file_name (XCAR (XCDR (buffer)), + Lisp_Object ofile = XCDR (buffer); + if (CONSP (ofile)) + ofile = XCAR (ofile); + CHECK_STRING (ofile); + output_file = Fexpand_file_name (ofile, BVAR (current_buffer, directory)); CHECK_STRING (output_file); buffer = Qnil; } - if (! (NILP (buffer) || EQ (buffer, Qt) || INTEGERP (buffer))) + if (! (NILP (buffer) || EQ (buffer, Qt) || FIXNUMP (buffer))) { - Lisp_Object spec_buffer; - spec_buffer = buffer; - buffer = Fget_buffer_create (buffer); + Lisp_Object spec_buffer = buffer; + buffer = Fget_buffer_create (buffer, Qnil); /* Mention the buffer name for a better error message. */ if (NILP (buffer)) CHECK_BUFFER (spec_buffer); @@ -419,19 +469,25 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd, buffer's current directory, or its unhandled equivalent. We can't just have the child check for an error when it does the chdir, since it's in a vfork. */ - current_dir = encode_current_directory (); + current_dir = get_current_directory (true); if (STRINGP (error_file)) - error_file = ENCODE_FILE (error_file); + { + error_file = remove_slash_colon (error_file); + error_file = ENCODE_FILE (error_file); + } if (STRINGP (output_file)) - output_file = ENCODE_FILE (output_file); + { + output_file = remove_slash_colon (output_file); + output_file = ENCODE_FILE (output_file); + } display_p = INTERACTIVE && nargs >= 4 && !NILP (args[3]); for (i = 0; i < CALLPROC_FDS; i++) callproc_fd[i] = -1; #ifdef MSDOS - synch_process_tempfile = make_number (0); + synch_process_tempfile = make_fixnum (0); #endif record_unwind_protect_ptr (call_process_kill, callproc_fd); @@ -440,7 +496,7 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd, int ok; ok = openp (Vexec_path, args[0], Vexec_suffixes, &path, - make_number (X_OK), false); + make_fixnum (X_OK), false, false); if (ok < 0) report_file_error ("Searching for program", args[0]); } @@ -471,7 +527,7 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd, path = ENCODE_FILE (path); new_argv[0] = SSDATA (path); - discard_output = INTEGERP (buffer) || (NILP (buffer) && NILP (output_file)); + discard_output = FIXNUMP (buffer) || (NILP (buffer) && NILP (output_file)); #ifdef MSDOS if (! discard_output && ! STRINGP (output_file)) @@ -542,8 +598,11 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd, callproc_fd[CALLPROC_STDERR] = fd_error; } + char **env = make_environment_block (current_dir); + #ifdef MSDOS /* MW, July 1993 */ - status = child_setup (filefd, fd_output, fd_error, new_argv, 0, current_dir); + status = child_setup (filefd, fd_output, fd_error, new_argv, env, + SSDATA (current_dir)); if (status < 0) { @@ -562,7 +621,7 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd, callproc_fd[i] = -1; } emacs_close (filefd); - clear_unwind_protect (count - 1); + clear_unwind_protect (specpdl_ref_add (count, -1)); if (tempfile) { @@ -589,92 +648,18 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd, block_input (); block_child_signal (&oldset); -#ifdef WINDOWSNT - pid = child_setup (filefd, fd_output, fd_error, new_argv, 0, current_dir); -#else /* not WINDOWSNT */ - - /* vfork, and prevent local vars from being clobbered by the vfork. */ - { - Lisp_Object volatile buffer_volatile = buffer; - Lisp_Object volatile coding_systems_volatile = coding_systems; - Lisp_Object volatile current_dir_volatile = current_dir; - bool volatile display_p_volatile = display_p; - bool volatile sa_must_free_volatile = sa_must_free; - int volatile fd_error_volatile = fd_error; - int volatile filefd_volatile = filefd; - ptrdiff_t volatile count_volatile = count; - ptrdiff_t volatile sa_avail_volatile = sa_avail; - ptrdiff_t volatile sa_count_volatile = sa_count; - char **volatile new_argv_volatile = new_argv; - int volatile callproc_fd_volatile[CALLPROC_FDS]; - for (i = 0; i < CALLPROC_FDS; i++) - callproc_fd_volatile[i] = callproc_fd[i]; - - pid = vfork (); - - buffer = buffer_volatile; - coding_systems = coding_systems_volatile; - current_dir = current_dir_volatile; - display_p = display_p_volatile; - sa_must_free = sa_must_free_volatile; - fd_error = fd_error_volatile; - filefd = filefd_volatile; - count = count_volatile; - sa_avail = sa_avail_volatile; - sa_count = sa_count_volatile; - new_argv = new_argv_volatile; - - for (i = 0; i < CALLPROC_FDS; i++) - callproc_fd[i] = callproc_fd_volatile[i]; - fd_output = callproc_fd[CALLPROC_STDOUT]; - } - - if (pid == 0) - { -#ifdef DARWIN_OS - /* Work around a macOS bug, where SIGCHLD is apparently - delivered to a vforked child instead of to its parent. See: - https://lists.gnu.org/archive/html/emacs-devel/2017-05/msg00342.html - */ - signal (SIGCHLD, SIG_DFL); -#endif - - unblock_child_signal (&oldset); - -#ifdef DARWIN_OS - /* Darwin doesn't let us run setsid after a vfork, so use - TIOCNOTTY when necessary. */ - int j = emacs_open (DEV_TTY, O_RDWR, 0); - if (j >= 0) - { - ioctl (j, TIOCNOTTY, 0); - emacs_close (j); - } -#else - setsid (); -#endif - - /* Emacs ignores SIGPIPE, but the child should not. */ - signal (SIGPIPE, SIG_DFL); - /* Likewise for SIGPROF. */ -#ifdef SIGPROF - signal (SIGPROF, SIG_DFL); -#endif - - child_setup (filefd, fd_output, fd_error, new_argv, 0, current_dir); - } - -#endif /* not WINDOWSNT */ - - child_errno = errno; + child_errno + = emacs_spawn (&pid, filefd, fd_output, fd_error, new_argv, env, + SSDATA (current_dir), NULL, false, false, &oldset); + eassert ((child_errno == 0) == (0 < pid)); if (pid > 0) { synch_process_pid = pid; - if (INTEGERP (buffer)) + if (FIXNUMP (buffer)) { - if (tempfile_index < 0) + if (!specpdl_ref_valid_p (tempfile_index)) record_deleted_pid (pid, Qnil); else { @@ -690,7 +675,7 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd, unblock_input (); if (pid < 0) - report_file_errno ("Doing vfork", Qnil, child_errno); + report_file_errno (CHILD_SETUP_ERROR_DESC, Qnil, child_errno); /* Close our file descriptors, except for callproc_fd[CALLPROC_PIPEREAD] since we will use that to read input from. */ @@ -701,11 +686,11 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd, callproc_fd[i] = -1; } emacs_close (filefd); - clear_unwind_protect (count - 1); + clear_unwind_protect (specpdl_ref_add (count, -1)); #endif /* not MSDOS */ - if (INTEGERP (buffer)) + if (FIXNUMP (buffer)) return unbind_to (count, Qnil); if (BUFFERP (buffer)) @@ -763,6 +748,8 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd, int carryover = 0; bool display_on_the_fly = display_p; struct coding_system saved_coding = process_coding; + ptrdiff_t prepared_pos = 0; /* prepare_to_modify_buffer was last + called here. */ while (1) { @@ -790,6 +777,33 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd, if (display_on_the_fly) break; } + /* CHANGE FUNCTIONS + For each iteration of the enclosing while (1) loop which + yields data (i.e. nread > 0), before- and + after-change-functions are each invoked exactly once. + This is done directly from the current function only, by + calling prepare_to_modify_buffer and signal_after_change. + It is not done here by directing another function such as + insert_1_both to call them. The call to + prepare_to_modify_buffer follows this comment, and there + is one call to signal_after_change in each of the + branches of the next `else if'. + + Exceptionally, the insertion into the buffer is aborted + at the call to del_range_2 ~45 lines further down, this + function removing the newly inserted data. At this stage + prepare_to_modify_buffer has been called, but + signal_after_change hasn't. A continue statement + restarts the enclosing while (1) loop. A second, + unwanted, call to `prepare_to_modify_buffer' is inhibited + by the test prepared_pos < PT. The data are inserted + again, and this time signal_after_change gets called, + balancing the previous call to prepare_to_modify_buffer. */ + if ((prepared_pos < PT) && nread) + { + prepare_to_modify_buffer (PT, PT, NULL); + prepared_pos = PT; + } /* Now NREAD is the total amount of data in the buffer. */ @@ -797,15 +811,16 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd, ; else if (NILP (BVAR (current_buffer, enable_multibyte_characters)) && ! CODING_MAY_REQUIRE_DECODING (&process_coding)) - insert_1_both (buf, nread, nread, 0, 1, 0); + { + insert_1_both (buf, nread, nread, 0, 0, 0); + signal_after_change (PT - nread, 0, nread); + } else { /* We have to decode the input. */ Lisp_Object curbuf; - ptrdiff_t count1 = SPECPDL_INDEX (); + specpdl_ref count1 = SPECPDL_INDEX (); XSETBUFFER (curbuf, current_buffer); - /* FIXME: Call signal_after_change! */ - prepare_to_modify_buffer (PT, PT, NULL); /* We cannot allow after-change-functions be run during decoding, because that might modify the buffer, while we rely on process_coding.produced to @@ -841,6 +856,8 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd, TEMP_SET_PT_BOTH (PT + process_coding.produced_char, PT_BYTE + process_coding.produced); + signal_after_change (PT - process_coding.produced_char, + 0, process_coding.produced_char); carryover = process_coding.carryover_bytes; if (carryover > 0) memcpy (buf, process_coding.carryover, @@ -872,7 +889,7 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd, coding-system used to decode the process output. */ if (inherit_process_coding_system) call1 (intern ("after-insert-file-set-buffer-file-coding-system"), - make_number (total_read)); + make_fixnum (total_read)); } bool wait_ok = true; @@ -885,8 +902,7 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd, when exiting. */ synch_process_pid = 0; - SAFE_FREE (); - unbind_to (count, Qnil); + SAFE_FREE_UNBIND_TO (count, Qnil); if (!wait_ok) return build_unibyte_string ("internal error"); @@ -906,7 +922,7 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd, } eassert (WIFEXITED (status)); - return make_number (WEXITSTATUS (status)); + return make_fixnum (WEXITSTATUS (status)); } /* Create a temporary file suitable for storing the input data of @@ -946,7 +962,6 @@ create_temp_file (ptrdiff_t nargs, Lisp_Object *args, { Lisp_Object pattern = Fexpand_file_name (Vtemp_file_name_pattern, tmpdir); char *tempfile; - ptrdiff_t count; #ifdef WINDOWSNT /* Cannot use the result of Fexpand_file_name, because it @@ -966,7 +981,7 @@ create_temp_file (ptrdiff_t nargs, Lisp_Object *args, filename_string = Fcopy_sequence (ENCODE_FILE (pattern)); tempfile = SSDATA (filename_string); - count = SPECPDL_INDEX (); + specpdl_ref count = SPECPDL_INDEX (); record_unwind_protect_nothing (); fd = mkostemp (tempfile, O_BINARY | O_CLOEXEC); if (fd < 0) @@ -998,7 +1013,7 @@ create_temp_file (ptrdiff_t nargs, Lisp_Object *args, val = complement_process_encoding_system (val); { - ptrdiff_t count1 = SPECPDL_INDEX (); + specpdl_ref count1 = SPECPDL_INDEX (); specbind (intern ("coding-system-for-write"), val); /* POSIX lets mk[s]temp use "."; don't invoke jka-compr if we @@ -1043,7 +1058,11 @@ STDERR-FILE may be nil (discard standard error output), t (mix it with ordinary output), or a file name string. Sixth arg DISPLAY non-nil means redisplay buffer as output is inserted. -Remaining args are passed to PROGRAM at startup as command args. +Remaining arguments ARGS are passed to PROGRAM at startup as command-line +arguments. + +If PROGRAM is not an absolute file name, `call-process-region' will +look for PROGRAM in `exec-path' (which is a list of directories). If BUFFER is 0, `call-process-region' returns immediately with value nil. Otherwise it waits for PROGRAM to terminate @@ -1054,7 +1073,7 @@ usage: (call-process-region START END PROGRAM &optional DELETE BUFFER DISPLAY &r (ptrdiff_t nargs, Lisp_Object *args) { Lisp_Object infile, val; - ptrdiff_t count = SPECPDL_INDEX (); + specpdl_ref count = SPECPDL_INDEX (); Lisp_Object start = args[0]; Lisp_Object end = args[1]; bool empty_input; @@ -1069,7 +1088,7 @@ usage: (call-process-region START END PROGRAM &optional DELETE BUFFER DISPLAY &r validate_region (&args[0], &args[1]); start = args[0]; end = args[1]; - empty_input = XINT (start) == XINT (end); + empty_input = XFIXNUM (start) == XFIXNUM (end); } if (!empty_input) @@ -1084,7 +1103,17 @@ usage: (call-process-region START END PROGRAM &optional DELETE BUFFER DISPLAY &r } if (nargs > 3 && !NILP (args[3])) - Fdelete_region (start, end); + { + if (NILP (start)) + { + /* No need to save restrictions since we delete everything + anyway. */ + Fwiden (); + del_range (BEG, Z); + } + else + Fdelete_region (start, end); + } if (nargs > 3) { @@ -1098,7 +1127,8 @@ usage: (call-process-region START END PROGRAM &optional DELETE BUFFER DISPLAY &r } args[1] = infile; - val = call_process (nargs, args, fd, empty_input ? -1 : count); + val = call_process (nargs, args, fd, + empty_input ? make_invalid_specpdl_ref () : count); return unbind_to (count, val); } @@ -1144,7 +1174,7 @@ add_env (char **env, char **new_env, char *string) mess up the allocator's data structures in the parent. Report the error and exit the child. */ -static _Noreturn void +static AVOID exec_failed (char const *name, int err) { /* Avoid deadlock if the child's perror writes to a full pipe; the @@ -1157,16 +1187,6 @@ exec_failed (char const *name, int err) _exit (err == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE); } -#else - -/* Do nothing. There is no need to fail, as DOS_NT platforms do not - fork and exec, and handle alloca exhaustion in a different way. */ - -static void -exec_failed (char const *name, int err) -{ -} - #endif /* This is the last thing run in a newly forked inferior @@ -1175,23 +1195,24 @@ exec_failed (char const *name, int err) Initialize inferior's priority, pgrp, connected dir and environment. then exec another program based on new_argv. - If SET_PGRP, put the subprocess into a separate process group. - CURRENT_DIR is an elisp string giving the path of the current directory the subprocess should have. Since we can't really signal a decent error from within the child, this should be verified as an executable directory by the parent. On GNUish hosts, either exec or return an error number. - On MS-Windows, either return a pid or signal an error. + On MS-Windows, either return a pid or return -1 and set errno. On MS-DOS, either return an exit status or signal an error. */ -CHILD_SETUP_TYPE -child_setup (int in, int out, int err, char **new_argv, bool set_pgrp, - Lisp_Object current_dir) +static CHILD_SETUP_TYPE +child_setup (int in, int out, int err, char **new_argv, char **env, + const char *current_dir) { - char **env; +#ifdef MSDOS char *pwd_var; + char *temp; + ptrdiff_t i; +#endif #ifdef WINDOWSNT int cpid; HANDLE handles[3]; @@ -1205,24 +1226,6 @@ child_setup (int in, int out, int err, char **new_argv, bool set_pgrp, src/alloca.c) it is safe because that changes the superior's static variables as if the superior had done alloca and will be cleaned up in the usual way. */ - { - char *temp; - ptrdiff_t i; - - i = SBYTES (current_dir); -#ifdef MSDOS - /* MSDOS must have all environment variables malloc'ed, because - low-level libc functions that launch subsidiary processes rely - on that. */ - pwd_var = xmalloc (i + 5); -#else - if (MAX_ALLOCA - 5 < i) - exec_failed (new_argv[0], ENOMEM); - pwd_var = alloca (i + 5); -#endif - temp = pwd_var + 4; - memcpy (pwd_var, "PWD=", 4); - lispstpcpy (temp, current_dir); #ifndef DOS_NT /* We can't signal an Elisp error here; we're in a vfork. Since @@ -1230,107 +1233,16 @@ child_setup (int in, int out, int err, char **new_argv, bool set_pgrp, should only return an error if the directory's permissions are changed between the check and this chdir, but we should at least check. */ - if (chdir (temp) < 0) + if (chdir (current_dir) < 0) _exit (EXIT_CANCELED); -#else /* DOS_NT */ - /* Get past the drive letter, so that d:/ is left alone. */ - if (i > 2 && IS_DEVICE_SEP (temp[1]) && IS_DIRECTORY_SEP (temp[2])) - { - temp += 2; - i -= 2; - } -#endif /* DOS_NT */ - - /* Strip trailing slashes for PWD, but leave "/" and "//" alone. */ - while (i > 2 && IS_DIRECTORY_SEP (temp[i - 1])) - temp[--i] = 0; - } - - /* Set `env' to a vector of the strings in the environment. */ - { - register Lisp_Object tem; - register char **new_env; - char **p, **q; - register int new_length; - Lisp_Object display = Qnil; - - new_length = 0; - - for (tem = Vprocess_environment; - CONSP (tem) && STRINGP (XCAR (tem)); - tem = XCDR (tem)) - { - if (strncmp (SSDATA (XCAR (tem)), "DISPLAY", 7) == 0 - && (SDATA (XCAR (tem)) [7] == '\0' - || SDATA (XCAR (tem)) [7] == '=')) - /* DISPLAY is specified in process-environment. */ - display = Qt; - new_length++; - } - - /* If not provided yet, use the frame's DISPLAY. */ - if (NILP (display)) - { - Lisp_Object tmp = Fframe_parameter (selected_frame, Qdisplay); - if (!STRINGP (tmp) && CONSP (Vinitial_environment)) - /* If still not found, Look for DISPLAY in Vinitial_environment. */ - tmp = Fgetenv_internal (build_string ("DISPLAY"), - Vinitial_environment); - if (STRINGP (tmp)) - { - display = tmp; - new_length++; - } - } - - /* new_length + 2 to include PWD and terminating 0. */ - if (MAX_ALLOCA / sizeof *env - 2 < new_length) - exec_failed (new_argv[0], ENOMEM); - env = new_env = alloca ((new_length + 2) * sizeof *env); - /* If we have a PWD envvar, pass one down, - but with corrected value. */ - if (egetenv ("PWD")) - *new_env++ = pwd_var; - - if (STRINGP (display)) - { - if (MAX_ALLOCA - sizeof "DISPLAY=" < SBYTES (display)) - exec_failed (new_argv[0], ENOMEM); - char *vdata = alloca (sizeof "DISPLAY=" + SBYTES (display)); - lispstpcpy (stpcpy (vdata, "DISPLAY="), display); - new_env = add_env (env, new_env, vdata); - } - - /* Overrides. */ - for (tem = Vprocess_environment; - CONSP (tem) && STRINGP (XCAR (tem)); - tem = XCDR (tem)) - new_env = add_env (env, new_env, SSDATA (XCAR (tem))); - - *new_env = 0; - - /* Remove variable names without values. */ - p = q = env; - while (*p != 0) - { - while (*q != 0 && strchr (*q, '=') == NULL) - q++; - *p = *q++; - if (*p != 0) - p++; - } - } - +#endif #ifdef WINDOWSNT prepare_standard_handles (in, out, err, handles); - set_process_dir (SSDATA (current_dir)); + set_process_dir (current_dir); /* Spawn the child. (See w32proc.c:sys_spawnve). */ cpid = spawnve (_P_NOWAIT, new_argv[0], new_argv, env); reset_standard_handles (in, out, err, handles); - if (cpid == -1) - /* An error occurred while trying to spawn the process. */ - report_file_error ("Spawning child process", Qnil); return cpid; #else /* not WINDOWSNT */ @@ -1353,6 +1265,22 @@ child_setup (int in, int out, int err, char **new_argv, bool set_pgrp, exec_failed (new_argv[0], errnum); #else /* MSDOS */ + i = strlen (current_dir); + pwd_var = xmalloc (i + 5); + temp = pwd_var + 4; + memcpy (pwd_var, "PWD=", 4); + stpcpy (temp, current_dir); + + if (i > 2 && IS_DEVICE_SEP (temp[1]) && IS_DIRECTORY_SEP (temp[2])) + { + temp += 2; + i -= 2; + } + + /* Strip trailing slashes for PWD, but leave "/" and "//" alone. */ + while (i > 2 && IS_DIRECTORY_SEP (temp[i - 1])) + temp[--i] = 0; + pid = run_msdos_command (new_argv, pwd_var + 4, in, out, err, env); xfree (pwd_var); if (pid == -1) @@ -1363,6 +1291,352 @@ child_setup (int in, int out, int err, char **new_argv, bool set_pgrp, #endif /* not WINDOWSNT */ } +#if USABLE_POSIX_SPAWN + +/* Set up ACTIONS and ATTRIBUTES for `posix_spawn'. Return an error + number. */ + +static int +emacs_posix_spawn_init_actions (posix_spawn_file_actions_t *actions, + int std_in, int std_out, int std_err, + const char *cwd) +{ + int error = posix_spawn_file_actions_init (actions); + if (error != 0) + return error; + + error = posix_spawn_file_actions_adddup2 (actions, std_in, + STDIN_FILENO); + if (error != 0) + goto out; + + error = posix_spawn_file_actions_adddup2 (actions, std_out, + STDOUT_FILENO); + if (error != 0) + goto out; + + error = posix_spawn_file_actions_adddup2 (actions, + std_err < 0 ? std_out + : std_err, + STDERR_FILENO); + if (error != 0) + goto out; + + error = +#ifdef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR + posix_spawn_file_actions_addchdir +#else + posix_spawn_file_actions_addchdir_np +#endif + (actions, cwd); + if (error != 0) + goto out; + + out: + if (error != 0) + posix_spawn_file_actions_destroy (actions); + return error; +} + +static int +emacs_posix_spawn_init_attributes (posix_spawnattr_t *attributes, + const sigset_t *oldset) +{ + int error = posix_spawnattr_init (attributes); + if (error != 0) + return error; + + error = posix_spawnattr_setflags (attributes, + POSIX_SPAWN_SETSID + | POSIX_SPAWN_SETSIGDEF + | POSIX_SPAWN_SETSIGMASK); + if (error != 0) + goto out; + + sigset_t sigdefault; + sigemptyset (&sigdefault); + +#ifdef DARWIN_OS + /* Work around a macOS bug, where SIGCHLD is apparently + delivered to a vforked child instead of to its parent. See: + https://lists.gnu.org/r/emacs-devel/2017-05/msg00342.html + */ + sigaddset (&sigdefault, SIGCHLD); +#endif + + sigaddset (&sigdefault, SIGINT); + sigaddset (&sigdefault, SIGQUIT); +#ifdef SIGPROF + sigaddset (&sigdefault, SIGPROF); +#endif + + /* Emacs ignores SIGPIPE, but the child should not. */ + sigaddset (&sigdefault, SIGPIPE); + /* Likewise for SIGPROF. */ +#ifdef SIGPROF + sigaddset (&sigdefault, SIGPROF); +#endif + + error = posix_spawnattr_setsigdefault (attributes, &sigdefault); + if (error != 0) + goto out; + + /* Stop blocking SIGCHLD in the child. */ + error = posix_spawnattr_setsigmask (attributes, oldset); + if (error != 0) + goto out; + + out: + if (error != 0) + posix_spawnattr_destroy (attributes); + + return error; +} + +#endif + +/* Start a new asynchronous subprocess. If successful, return zero + and store the process identifier of the new process in *NEWPID. + Use STDIN, STDOUT, and STDERR as standard streams for the new + process. Use ARGV as argument vector for the new process; use + process image file ARGV[0]. Use ENVP for the environment block for + the new process. Use CWD as working directory for the new process. + If PTY is not NULL, it must be a pseudoterminal device. If PTY is + NULL, don't perform any terminal setup. OLDSET must be a pointer + to a signal set initialized by `block_child_signal'. Before + calling this function, call `block_input' and `block_child_signal'; + afterwards, call `unblock_input' and `unblock_child_signal'. Be + sure to call `unblock_child_signal' only after registering NEWPID + in a list where `handle_child_signal' can find it! */ + +int +emacs_spawn (pid_t *newpid, int std_in, int std_out, int std_err, + char **argv, char **envp, const char *cwd, + const char *pty_name, bool pty_in, bool pty_out, + const sigset_t *oldset) +{ +#if USABLE_POSIX_SPAWN + /* Prefer the simpler `posix_spawn' if available. `posix_spawn' + doesn't yet support setting up pseudoterminals, so we fall back + to `vfork' if we're supposed to use a pseudoterminal. */ + + bool use_posix_spawn = pty_name == NULL; + + posix_spawn_file_actions_t actions; + posix_spawnattr_t attributes; + + if (use_posix_spawn) + { + /* Initialize optional attributes before blocking. */ + int error = emacs_posix_spawn_init_actions (&actions, std_in, + std_out, std_err, cwd); + if (error != 0) + return error; + + error = emacs_posix_spawn_init_attributes (&attributes, oldset); + if (error != 0) + return error; + } +#endif + + int pid; + int vfork_error; + + eassert (input_blocked_p ()); + +#if USABLE_POSIX_SPAWN + if (use_posix_spawn) + { + vfork_error = posix_spawn (&pid, argv[0], &actions, &attributes, + argv, envp); + if (vfork_error != 0) + pid = -1; + + int error = posix_spawn_file_actions_destroy (&actions); + if (error != 0) + { + errno = error; + emacs_perror ("posix_spawn_file_actions_destroy"); + } + + error = posix_spawnattr_destroy (&attributes); + if (error != 0) + { + errno = error; + emacs_perror ("posix_spawnattr_destroy"); + } + + goto fork_done; + } +#endif + +#ifndef WINDOWSNT + /* vfork, and prevent local vars from being clobbered by the vfork. */ + pid_t *volatile newpid_volatile = newpid; + const char *volatile cwd_volatile = cwd; + const char *volatile ptyname_volatile = pty_name; + bool volatile ptyin_volatile = pty_in; + bool volatile ptyout_volatile = pty_out; + char **volatile argv_volatile = argv; + int volatile stdin_volatile = std_in; + int volatile stdout_volatile = std_out; + int volatile stderr_volatile = std_err; + char **volatile envp_volatile = envp; + const sigset_t *volatile oldset_volatile = oldset; + +#ifdef DARWIN_OS + /* Darwin doesn't let us run setsid after a vfork, so use fork when + necessary. Below, we reset SIGCHLD handling after a vfork, as + apparently macOS can mistakenly deliver SIGCHLD to the child. */ + if (pty_in || pty_out) + pid = fork (); + else + pid = VFORK (); +#else + pid = vfork (); +#endif + + newpid = newpid_volatile; + cwd = cwd_volatile; + pty_name = ptyname_volatile; + pty_in = ptyin_volatile; + pty_out = ptyout_volatile; + argv = argv_volatile; + std_in = stdin_volatile; + std_out = stdout_volatile; + std_err = stderr_volatile; + envp = envp_volatile; + oldset = oldset_volatile; + + if (pid == 0) +#endif /* not WINDOWSNT */ + { + /* Make the pty be the controlling terminal of the process. */ +#ifdef HAVE_PTYS + dissociate_controlling_tty (); + + /* Make the pty's terminal the controlling terminal. */ + if (pty_in && std_in >= 0) + { +#ifdef TIOCSCTTY + /* We ignore the return value + because faith@cs.unc.edu says that is necessary on Linux. */ + ioctl (std_in, TIOCSCTTY, 0); +#endif + } +#if defined (LDISC1) + if (pty_in && std_in >= 0) + { + struct termios t; + tcgetattr (std_in, &t); + t.c_lflag = LDISC1; + if (tcsetattr (std_in, TCSANOW, &t) < 0) + emacs_perror ("create_process/tcsetattr LDISC1"); + } +#else +#if defined (NTTYDISC) && defined (TIOCSETD) + if (pty_in && std_in >= 0) + { + /* Use new line discipline. */ + int ldisc = NTTYDISC; + ioctl (std_in, TIOCSETD, &ldisc); + } +#endif +#endif + +#if !defined (DONT_REOPEN_PTY) +/*** There is a suggestion that this ought to be a + conditional on TIOCSPGRP, or !defined TIOCSCTTY. + Trying the latter gave the wrong results on Debian GNU/Linux 1.1; + that system does seem to need this code, even though + both TIOCSCTTY is defined. */ + /* Now close the pty (if we had it open) and reopen it. + This makes the pty the controlling terminal of the subprocess. */ + if (pty_name) + { + + /* I wonder if emacs_close (emacs_open (pty, ...)) + would work? */ + if (pty_in && std_in >= 0) + emacs_close (std_in); + int ptyfd = emacs_open_noquit (pty_name, O_RDWR, 0); + if (pty_in) + std_in = ptyfd; + if (pty_out) + std_out = ptyfd; + if (std_in < 0) + { + emacs_perror (pty_name); + _exit (EXIT_CANCELED); + } + + } +#endif /* not DONT_REOPEN_PTY */ + +#ifdef SETUP_SLAVE_PTY + if (pty_in && std_in >= 0) + { + SETUP_SLAVE_PTY; + } +#endif /* SETUP_SLAVE_PTY */ +#endif /* HAVE_PTYS */ + +#ifdef DARWIN_OS + /* Work around a macOS bug, where SIGCHLD is apparently + delivered to a vforked child instead of to its parent. See: + https://lists.gnu.org/r/emacs-devel/2017-05/msg00342.html + */ + signal (SIGCHLD, SIG_DFL); +#endif + + signal (SIGINT, SIG_DFL); + signal (SIGQUIT, SIG_DFL); +#ifdef SIGPROF + signal (SIGPROF, SIG_DFL); +#endif + + /* Emacs ignores SIGPIPE, but the child should not. */ + signal (SIGPIPE, SIG_DFL); + /* Likewise for SIGPROF. */ +#ifdef SIGPROF + signal (SIGPROF, SIG_DFL); +#endif + +#ifdef subprocesses + /* Stop blocking SIGCHLD in the child. */ + unblock_child_signal (oldset); + + if (pty_out) + child_setup_tty (std_out); +#endif + + if (std_err < 0) + std_err = std_out; +#ifdef WINDOWSNT + pid = child_setup (std_in, std_out, std_err, argv, envp, cwd); +#else /* not WINDOWSNT */ + child_setup (std_in, std_out, std_err, argv, envp, cwd); +#endif /* not WINDOWSNT */ + } + + /* Back in the parent process. */ + + vfork_error = pid < 0 ? errno : 0; + +#if USABLE_POSIX_SPAWN + fork_done: +#endif + if (pid < 0) + { + eassert (0 < vfork_error); + return vfork_error; + } + + eassert (0 < pid); + *newpid = pid; + return 0; +} + static bool getenv_internal_1 (const char *var, ptrdiff_t varlen, char **value, ptrdiff_t *valuelen, Lisp_Object env) @@ -1424,6 +1698,7 @@ getenv_internal (const char *var, ptrdiff_t varlen, char **value, /* For DISPLAY try to get the values from the frame or the initial env. */ if (strcmp (var, "DISPLAY") == 0) { +#ifndef HAVE_PGTK Lisp_Object display = Fframe_parameter (NILP (frame) ? selected_frame : frame, Qdisplay); if (STRINGP (display)) @@ -1432,6 +1707,7 @@ getenv_internal (const char *var, ptrdiff_t varlen, char **value, *valuelen = SBYTES (display); return 1; } +#endif /* If still not found, Look for DISPLAY in Vinitial_environment. */ if (getenv_internal_1 (var, varlen, value, valuelen, Vinitial_environment)) @@ -1486,38 +1762,146 @@ egetenv_internal (const char *var, ptrdiff_t len) return 0; } +/* Create a new environment block. You can pass the returned pointer + to `execve'. Add unwind protections for all newly-allocated + objects. Don't call any Lisp code or the garbage collector while + the block is active. */ + +char ** +make_environment_block (Lisp_Object current_dir) +{ + char **env; + char *pwd_var; + + { + char *temp; + ptrdiff_t i; + + i = SBYTES (current_dir); + pwd_var = xmalloc (i + 5); + record_unwind_protect_ptr (xfree, pwd_var); + temp = pwd_var + 4; + memcpy (pwd_var, "PWD=", 4); + lispstpcpy (temp, current_dir); + +#ifdef DOS_NT + /* Get past the drive letter, so that d:/ is left alone. */ + if (i > 2 && IS_DEVICE_SEP (temp[1]) && IS_DIRECTORY_SEP (temp[2])) + { + temp += 2; + i -= 2; + } +#endif /* DOS_NT */ + + /* Strip trailing slashes for PWD, but leave "/" and "//" alone. */ + while (i > 2 && IS_DIRECTORY_SEP (temp[i - 1])) + temp[--i] = 0; + } + + /* Set `env' to a vector of the strings in the environment. */ + + { + register Lisp_Object tem; + register char **new_env; + char **p, **q; + register int new_length; + Lisp_Object display = Qnil; + + new_length = 0; + + for (tem = Vprocess_environment; + CONSP (tem) && STRINGP (XCAR (tem)); + tem = XCDR (tem)) + { + if (strncmp (SSDATA (XCAR (tem)), "DISPLAY", 7) == 0 + && (SDATA (XCAR (tem)) [7] == '\0' + || SDATA (XCAR (tem)) [7] == '=')) + /* DISPLAY is specified in process-environment. */ + display = Qt; + new_length++; + } + + /* If not provided yet, use the frame's DISPLAY. */ + if (NILP (display)) + { + Lisp_Object tmp = Fframe_parameter (selected_frame, Qdisplay); + +#ifdef HAVE_PGTK + /* The only time GDK actually returns correct information is + when it's running under X Windows. DISPLAY shouldn't be + set to a Wayland display either, since that's an X specific + variable. */ + if (FRAME_WINDOW_P (SELECTED_FRAME ()) + && strcmp (G_OBJECT_TYPE_NAME (FRAME_X_DISPLAY (SELECTED_FRAME ())), + "GdkX11Display")) + tmp = Qnil; +#endif + + if (!STRINGP (tmp) && CONSP (Vinitial_environment)) + /* If still not found, Look for DISPLAY in Vinitial_environment. */ + tmp = Fgetenv_internal (build_string ("DISPLAY"), + Vinitial_environment); + if (STRINGP (tmp)) + { + display = tmp; + new_length++; + } + } + + /* new_length + 2 to include PWD and terminating 0. */ + env = new_env = xnmalloc (new_length + 2, sizeof *env); + record_unwind_protect_ptr (xfree, env); + /* If we have a PWD envvar, pass one down, + but with corrected value. */ + if (egetenv ("PWD")) + *new_env++ = pwd_var; + + if (STRINGP (display)) + { + char *vdata = xmalloc (sizeof "DISPLAY=" + SBYTES (display)); + record_unwind_protect_ptr (xfree, vdata); + lispstpcpy (stpcpy (vdata, "DISPLAY="), display); + new_env = add_env (env, new_env, vdata); + } + + /* Overrides. */ + for (tem = Vprocess_environment; + CONSP (tem) && STRINGP (XCAR (tem)); + tem = XCDR (tem)) + new_env = add_env (env, new_env, SSDATA (XCAR (tem))); + + *new_env = 0; + + /* Remove variable names without values. */ + p = q = env; + while (*p != 0) + { + while (*q != 0 && strchr (*q, '=') == NULL) + q++; + *p = *q++; + if (*p != 0) + p++; + } + } + + return env; +} + /* This is run before init_cmdargs. */ void init_callproc_1 (void) { -#ifdef HAVE_NS - const char *etc_dir = ns_etc_directory (); - const char *path_exec = ns_exec_path (); -#endif - - Vdata_directory = decode_env_path ("EMACSDATA", -#ifdef HAVE_NS - etc_dir ? etc_dir : -#endif - PATH_DATA, 0); + Vdata_directory = decode_env_path ("EMACSDATA", PATH_DATA, 0); Vdata_directory = Ffile_name_as_directory (Fcar (Vdata_directory)); - Vdoc_directory = decode_env_path ("EMACSDOC", -#ifdef HAVE_NS - etc_dir ? etc_dir : -#endif - PATH_DOC, 0); + Vdoc_directory = decode_env_path ("EMACSDOC", PATH_DOC, 0); Vdoc_directory = Ffile_name_as_directory (Fcar (Vdoc_directory)); /* Check the EMACSPATH environment variable, defaulting to the PATH_EXEC path from epaths.h. */ - Vexec_path = decode_env_path ("EMACSPATH", -#ifdef HAVE_NS - path_exec ? path_exec : -#endif - PATH_EXEC, 0); + Vexec_path = decode_env_path ("EMACSPATH", PATH_EXEC, 0); Vexec_directory = Ffile_name_as_directory (Fcar (Vexec_path)); /* FIXME? For ns, path_exec should go at the front? */ Vexec_path = nconc2 (decode_env_path ("PATH", "", 0), Vexec_path); @@ -1532,10 +1916,6 @@ init_callproc (void) char *sh; Lisp_Object tempdir; -#ifdef HAVE_NS - if (data_dir == 0) - data_dir = ns_etc_directory () != 0; -#endif if (!NILP (Vinstallation_directory)) { @@ -1547,15 +1927,8 @@ init_callproc (void) /* MSDOS uses wrapped binaries, so don't do this. */ if (NILP (Fmember (tem, Vexec_path))) { -#ifdef HAVE_NS - const char *path_exec = ns_exec_path (); -#endif /* Running uninstalled, so default to tem rather than PATH_EXEC. */ - Vexec_path = decode_env_path ("EMACSPATH", -#ifdef HAVE_NS - path_exec ? path_exec : -#endif - SSDATA (tem), 0); + Vexec_path = decode_env_path ("EMACSPATH", SSDATA (tem), 0); Vexec_path = nconc2 (decode_env_path ("PATH", "", 0), Vexec_path); } @@ -1580,27 +1953,24 @@ init_callproc (void) source directory. */ if (data_dir == 0) { - Lisp_Object tem, tem1, srcdir; + Lisp_Object tem, srcdir; Lisp_Object lispdir = Fcar (decode_env_path (0, PATH_DUMPLOADSEARCH, 0)); srcdir = Fexpand_file_name (build_string ("../src/"), lispdir); tem = Fexpand_file_name (build_string ("NEWS"), Vdata_directory); - tem1 = Ffile_exists_p (tem); - if (!NILP (Fequal (srcdir, Vinvocation_directory)) || NILP (tem1)) + if (!NILP (Fequal (srcdir, Vinvocation_directory)) + || NILP (Ffile_exists_p (tem)) || !NILP (Vinstallation_directory)) { Lisp_Object newdir; newdir = Fexpand_file_name (build_string ("../etc/"), lispdir); tem = Fexpand_file_name (build_string ("NEWS"), newdir); - tem1 = Ffile_exists_p (tem); - if (!NILP (tem1)) + if (!NILP (Ffile_exists_p (tem))) Vdata_directory = newdir; } } -#ifndef CANNOT_DUMP - if (initialized) -#endif + if (!will_dump_p ()) { tempdir = Fdirectory_file_name (Vexec_directory); if (! file_accessible_directory_p (tempdir)) @@ -1617,9 +1987,22 @@ init_callproc (void) Lisp_Object gamedir = Qnil; if (PATH_GAME) { - Lisp_Object path_game = build_unibyte_string (PATH_GAME); + const char *cpath_game = PATH_GAME; +#ifdef WINDOWSNT + /* On MS-Windows, PATH_GAME normally starts with a literal + "%emacs_dir%", so it will never work without some tweaking. */ + cpath_game = w32_relocate (cpath_game); +#endif + Lisp_Object path_game = build_unibyte_string (cpath_game); if (file_accessible_directory_p (path_game)) gamedir = path_game; + else if (errno != ENOENT && errno != ENOTDIR +#ifdef DOS_NT + /* DOS/Windows sometimes return EACCES for bad file names */ + && errno != EACCES +#endif + ) + dir_warning ("game dir", path_game); } Vshared_game_score_directory = gamedir; } @@ -1647,7 +2030,7 @@ syms_of_callproc (void) staticpro (&Vtemp_file_name_pattern); #ifdef MSDOS - synch_process_tempfile = make_number (0); + synch_process_tempfile = make_fixnum (0); staticpro (&synch_process_tempfile); #endif |