X-Git-Url: http://git.hcoop.net/bpt/emacs.git/blobdiff_plain/bfab7c6ec74dc55d640ef36f8cb1790a1420f991..81f82586c406a8d9d6cf09c3d3236c0778ab31d0:/lib-src/emacsclient.c diff --git a/lib-src/emacsclient.c b/lib-src/emacsclient.c index f505fe58f1..22b5273f84 100644 --- a/lib-src/emacsclient.c +++ b/lib-src/emacsclient.c @@ -4,10 +4,10 @@ This file is part of GNU Emacs. -GNU Emacs is free software; you can redistribute it and/or modify +GNU Emacs is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 3, or (at your option) -any later version. +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. GNU Emacs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -15,9 +15,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with GNU Emacs; see the file COPYING. If not, write to -the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -Boston, MA 02110-1301, USA. */ +along with GNU Emacs. If not, see . */ #ifdef HAVE_CONFIG_H @@ -43,17 +41,23 @@ Boston, MA 02110-1301, USA. */ #else /* !WINDOWSNT */ -# include +# include "syswait.h" # ifdef HAVE_INET_SOCKETS # include # endif +# include + # define INVALID_SOCKET -1 # define HSOCKET int # define CLOSE_SOCKET close # define INITIALIZE() +# ifndef WCONTINUED +# define WCONTINUED 8 +# endif + #endif /* !WINDOWSNT */ #undef signal @@ -66,15 +70,11 @@ Boston, MA 02110-1301, USA. */ #include #endif -#ifdef VMS -# include "vms-pwd.h" -#else /* not VMS */ #ifdef WINDOWSNT # include #else /* not WINDOWSNT */ # include #endif /* not WINDOWSNT */ -#endif /* not VMS */ #include #include @@ -115,6 +115,10 @@ char *w32_getenv (); #ifndef NO_RETURN #define NO_RETURN #endif + +/* Additional space when allocating buffers for filenames, etc. */ +#define EXTRA_SPACE 100 + /* Name used to invoke this program. */ char *progname; @@ -131,9 +135,6 @@ int eval = 0; /* Nonzero means don't open a new frame. Inverse of --create-frame. */ int current_frame = 1; -/* Nonzero means open a new graphical frame. */ -int window_system = 0; - /* The display on which Emacs should work. --display. */ char *display = NULL; @@ -162,6 +163,7 @@ struct option longopts[] = { "help", no_argument, NULL, 'H' }, { "version", no_argument, NULL, 'V' }, { "tty", no_argument, NULL, 't' }, + { "nw", no_argument, NULL, 't' }, { "create-frame", no_argument, NULL, 'c' }, { "alternate-editor", required_argument, NULL, 'a' }, #ifndef NO_SOCKETS_IN_FILE_SYSTEM @@ -321,8 +323,8 @@ w32_get_resource (predefined, key, type) { result = (char *) xmalloc (cbData); - if ((RegQueryValueEx (hrootkey, key, NULL, type, result, &cbData) != ERROR_SUCCESS) || - (*result == 0)) + if ((RegQueryValueEx (hrootkey, key, NULL, type, result, &cbData) != ERROR_SUCCESS) + || (*result == 0)) { free (result); result = NULL; @@ -354,8 +356,13 @@ w32_getenv (envvar) if (! (value = w32_get_resource (HKEY_CURRENT_USER, envvar, &dwType)) && ! (value = w32_get_resource (HKEY_LOCAL_MACHINE, envvar, &dwType))) - /* Not found in the registry. */ - return NULL; + { + /* "w32console" is what Emacs on Windows uses for tty-type under -nw. */ + if (strcmp (envvar, "TERM") == 0) + return xstrdup ("w32console"); + /* Found neither in the environment nor in the registry. */ + return NULL; + } if (dwType == REG_SZ) /* Registry; no need to expand. */ @@ -436,6 +443,13 @@ w32_execvp (path, argv) #undef execvp #define execvp w32_execvp +/* Emulation of ttyname for Windows. */ +char * +ttyname (int fd) +{ + return "CONOUT$"; +} + #endif /* WINDOWSNT */ /* Display a normal or error message. @@ -480,7 +494,7 @@ decode_options (argc, argv) while (1) { - int opt = getopt_long (argc, argv, + int opt = getopt_long_only (argc, argv, #ifndef NO_SOCKETS_IN_FILE_SYSTEM "VHnea:s:f:d:tc", #else @@ -516,11 +530,9 @@ decode_options (argc, argv) to allow it, for the occasional case where the user is connecting with a w32 client to a server compiled with X11 support. */ -#if 1 /* !defined WINDOWS */ case 'd': display = optarg; break; -#endif case 'n': nowait = 1; @@ -555,40 +567,36 @@ decode_options (argc, argv) } } - /* We used to set `display' to $DISPLAY by default, but this changed the - default behavior and is sometimes inconvenient. So instead of forcing - users to say "--display ''" when they want to use Emacs's existing tty - or display connection, we force them to use "--display $DISPLAY" if - they want Emacs to connect to their current display. - -c still implicitly passes --display $DISPLAY unless -t was specified - so as to try and mimick the behavior of `emacs' which either uses - the current tty or the current $DISPLAY. */ + /* If the -c option is used (without -t) and no --display argument + is provided, try $DISPLAY. + Without the -c option, we used to set `display' to $DISPLAY by + default, but this changed the default behavior and is sometimes + inconvenient. So we force users to use "--display $DISPLAY" if + they want Emacs to connect to their current display. */ if (!current_frame && !tty && !display) display = egetenv ("DISPLAY"); + /* A null-string display is invalid. */ if (display && strlen (display) == 0) display = NULL; - if (!tty && display) - window_system = 1; -#if !defined (WINDOWSNT) && !defined (HAVE_CARBON) - else if (!current_frame) + /* If no display is available, new frames are tty frames. */ + if (!current_frame && !display) tty = 1; -#endif /* --no-wait implies --current-frame on ttys when there are file - arguments or expressions given. */ + arguments or expressions given. */ if (nowait && tty && argc - optind > 0) current_frame = 1; - if (current_frame) +#ifdef WINDOWSNT + if (alternate_editor && alternate_editor[0] == '\0') { - tty = 0; - window_system = 0; + message (TRUE, "--alternate-editor argument or ALTERNATE_EDITOR variable cannot be\n\ +an empty string"); + exit (EXIT_FAILURE); } - - if (tty) - window_system = 0; +#endif /* WINDOWSNT */ } @@ -607,7 +615,7 @@ Every FILE can be either just a FILENAME or [+LINE[:COLUMN]] FILENAME.\n\ The following OPTIONS are accepted:\n\ -V, --version Just print version info and return\n\ -H, --help Print this usage information message\n\ --t, --tty Open a new Emacs frame on the current terminal\n\ +-nw, -t, --tty Open a new Emacs frame on the current terminal\n\ -c, --create-frame Create a new frame instead of trying to\n\ use the current Emacs frame\n\ -e, --eval Evaluate the FILE arguments as ELisp expressions\n\ @@ -620,8 +628,12 @@ The following OPTIONS are accepted:\n\ "-f, --server-file=FILENAME\n\ Set filename of the TCP authentication file\n\ -a, --alternate-editor=EDITOR\n\ - Editor to fallback to if the server is not running\n\ -\n\ + Editor to fallback to if the server is not running\n" +#ifdef WINDOWSNT +" If EDITOR is the empty string, start Emacs in daemon\n\ + mode and try connecting again\n" +#endif /* WINDOWSNT */ +"\n\ Report bugs to bug-gnu-emacs@gnu.org.\n", progname); exit (EXIT_SUCCESS); } @@ -898,14 +910,16 @@ get_server_config (server, authentication) if (home) { - char *path = alloca (32 + strlen (home) + strlen (server_file)); + char *path = alloca (strlen (home) + strlen (server_file) + + EXTRA_SPACE); sprintf (path, "%s/.emacs.d/server/%s", home, server_file); config = fopen (path, "rb"); } #ifdef WINDOWSNT if (!config && (home = egetenv ("APPDATA"))) { - char *path = alloca (32 + strlen (home) + strlen (server_file)); + char *path = alloca (strlen (home) + strlen (server_file) + + EXTRA_SPACE); sprintf (path, "%s/.emacs.d/server/%s", home, server_file); config = fopen (path, "rb"); } @@ -1000,6 +1014,57 @@ strprefix (char *prefix, char *string) return !strncmp (prefix, string, strlen (prefix)); } +/* Get tty name and type. If successful, return the type in TTY_TYPE + and the name in TTY_NAME, and return 1. Otherwise, fail if NOABORT + is zero, or return 0 if NOABORT is non-zero. */ + +int +find_tty (char **tty_type, char **tty_name, int noabort) +{ + char *type = egetenv ("TERM"); + char *name = ttyname (fileno (stdout)); + + if (!name) + { + if (noabort) + return 0; + else + { + message (TRUE, "%s: could not get terminal name\n", progname); + fail (); + } + } + + if (!type) + { + if (noabort) + return 0; + else + { + message (TRUE, "%s: please set the TERM variable to your terminal type\n", + progname); + fail (); + } + } + + if (strcmp (type, "eterm") == 0) + { + if (noabort) + return 0; + else + { + /* This causes nasty, MULTI_KBOARD-related input lockouts. */ + message (TRUE, "%s: opening a frame in an Emacs term buffer" + " is not supported\n", progname); + fail (); + } + } + + *tty_name = name; + *tty_type = type; + return 1; +} + #if !defined (NO_SOCKETS_IN_FILE_SYSTEM) @@ -1088,6 +1153,8 @@ handle_sigtstp (int signalnum) errno = old_errno; } + + /* Set up signal handlers before opening a frame on the current tty. */ void @@ -1133,6 +1200,7 @@ set_local_socket () int default_sock = !socket_name; int saved_errno = 0; char *server_name = "server"; + char *tmpdir; if (socket_name && !index (socket_name, '/') && !index (socket_name, '\\')) { /* socket_name is a file name component. */ @@ -1143,9 +1211,13 @@ set_local_socket () if (default_sock) { - socket_name = alloca (100 + strlen (server_name)); - sprintf (socket_name, "/tmp/emacs%d/%s", - (int) geteuid (), server_name); + tmpdir = egetenv ("TMPDIR"); + if (!tmpdir) + tmpdir = "/tmp"; + socket_name = alloca (strlen (tmpdir) + strlen (server_name) + + EXTRA_SPACE); + sprintf (socket_name, "%s/emacs%d/%s", + tmpdir, (int) geteuid (), server_name); } if (strlen (socket_name) < sizeof (server.sun_path)) @@ -1179,9 +1251,10 @@ set_local_socket () if (pw && (pw->pw_uid != geteuid ())) { /* We're running under su, apparently. */ - socket_name = alloca (100 + strlen (server_name)); - sprintf (socket_name, "/tmp/emacs%d/%s", - (int) pw->pw_uid, server_name); + socket_name = alloca (strlen (tmpdir) + strlen (server_name) + + EXTRA_SPACE); + sprintf (socket_name, "%s/emacs%d/%s", + tmpdir, (int) pw->pw_uid, server_name); if (strlen (socket_name) < sizeof (server.sun_path)) strcpy (server.sun_path, socket_name); @@ -1238,7 +1311,7 @@ To start the server in Emacs, type \"M-x server-start\".\n", #endif /* ! NO_SOCKETS_IN_FILE_SYSTEM */ HSOCKET -set_socket () +set_socket (int no_exit_if_error) { HSOCKET s; @@ -1249,7 +1322,7 @@ set_socket () if (socket_name) { s = set_local_socket (); - if ((s != INVALID_SOCKET) || alternate_editor) + if ((s != INVALID_SOCKET) || no_exit_if_error) return s; message (TRUE, "%s: error accessing socket \"%s\"\n", progname, socket_name); @@ -1264,7 +1337,7 @@ set_socket () if (server_file) { s = set_tcp_socket (); - if ((s != INVALID_SOCKET) || alternate_editor) + if ((s != INVALID_SOCKET) || no_exit_if_error) return s; message (TRUE, "%s: error accessing server file \"%s\"\n", @@ -1282,7 +1355,7 @@ set_socket () /* Implicit server file. */ server_file = "server"; s = set_tcp_socket (); - if ((s != INVALID_SOCKET) || alternate_editor) + if ((s != INVALID_SOCKET) || no_exit_if_error) return s; /* No implicit or explicit socket, and no alternate editor. */ @@ -1352,6 +1425,60 @@ w32_give_focus () } #endif +/* Start the emacs daemon and try to connect to it. */ + +void +start_daemon_and_retry_set_socket (void) +{ +#ifndef WINDOWSNT + pid_t dpid; + int status; + + dpid = fork (); + + if (dpid > 0) + { + pid_t w; + w = waitpid (dpid, &status, WUNTRACED | WCONTINUED); + + if ((w == -1) || !WIFEXITED (status) || WEXITSTATUS(status)) + { + message (TRUE, "Error: Could not start the Emacs daemon\n"); + exit (EXIT_FAILURE); + } + + /* Try connecting, the daemon should have started by now. */ + message (TRUE, "Emacs daemon should have started, trying to connect again\n"); + if ((emacs_socket = set_socket (1)) == INVALID_SOCKET) + { + message (TRUE, "Error: Cannot connect even after starting the Emacs daemon\n"); + exit (EXIT_FAILURE); + } + } + else if (dpid < 0) + { + fprintf (stderr, "Error: Cannot fork!\n"); + exit (1); + } + else + { + char *d_argv[] = {"emacs", "--daemon", 0 }; + if (socket_name != NULL) + { + /* Pass --daemon=socket_name as argument. */ + char *deq = "--daemon="; + char *daemon_arg = alloca (strlen (deq) + + strlen (socket_name) + 1); + strcpy (daemon_arg, deq); + strcat (daemon_arg, socket_name); + d_argv[1] = daemon_arg; + } + execvp ("emacs", d_argv); + message (TRUE, "%s: error starting emacs daemon\n", progname); + } +#endif /* WINDOWSNT */ +} + int main (argc, argv) int argc; @@ -1360,6 +1487,7 @@ main (argc, argv) int i, rl, needlf = 0; char *cwd, *str; char string[BUFSIZ+1]; + int null_socket_name, null_server_file, start_daemon_if_needed; main_argv = argv; progname = argv[0]; @@ -1367,7 +1495,7 @@ main (argc, argv) /* Process options. */ decode_options (argc, argv); - if ((argc - optind < 1) && !eval && !tty && !window_system) + if ((argc - optind < 1) && !eval && current_frame) { message (TRUE, "%s: file name or argument required\n" "Try `%s --help' for more information\n", @@ -1375,9 +1503,34 @@ main (argc, argv) exit (EXIT_FAILURE); } - if ((emacs_socket = set_socket ()) == INVALID_SOCKET) - fail (); + /* If alternate_editor is the empty string, start the emacs daemon + in case of failure to connect. */ + start_daemon_if_needed = (alternate_editor + && (alternate_editor[0] == '\0')); + if (start_daemon_if_needed) + { + /* set_socket changes the values for socket_name and + server_file, we need to reset them, if they were NULL before + for the second call to set_socket. */ + null_socket_name = (socket_name == NULL); + null_server_file = (server_file == NULL); + } + if ((emacs_socket = set_socket (alternate_editor + || start_daemon_if_needed)) == INVALID_SOCKET) + if (start_daemon_if_needed) + { + /* Reset socket_name and server_file if they were NULL + before the set_socket call. */ + if (null_socket_name) + socket_name = NULL; + if (null_server_file) + server_file = NULL; + + start_daemon_and_retry_set_socket (); + } + else + fail (); cwd = get_current_dir_name (); if (cwd == 0) @@ -1392,7 +1545,7 @@ main (argc, argv) w32_give_focus (); #endif - /* Send over our environment. */ + /* Send over our environment and current directory. */ if (!current_frame) { extern char **environ; @@ -1405,11 +1558,6 @@ main (argc, argv) quote_argument (emacs_socket, environ[i]); send_to_emacs (emacs_socket, " "); } - } - - /* Send over our current directory. */ - if (!current_frame) - { send_to_emacs (emacs_socket, "-dir "); quote_argument (emacs_socket, cwd); send_to_emacs (emacs_socket, "/"); @@ -1430,46 +1578,27 @@ main (argc, argv) send_to_emacs (emacs_socket, " "); } - if (tty) + /* If using the current frame, send tty information to Emacs anyway. + In daemon mode, Emacs may need to occupy this tty if no other + frame is available. */ + if (tty || (current_frame && !eval)) { - char *type = egetenv ("TERM"); - char *tty_name = NULL; -#ifndef WINDOWSNT - tty_name = ttyname (fileno (stdin)); -#endif - - if (! tty_name) - { - message (TRUE, "%s: could not get terminal name\n", progname); - fail (); - } + char *tty_type, *tty_name; - if (! type) - { - message (TRUE, "%s: please set the TERM variable to your terminal type\n", - progname); - fail (); - } - - if (! strcmp (type, "eterm")) - { - /* This causes nasty, MULTI_KBOARD-related input lockouts. */ - message (TRUE, "%s: opening a frame in an Emacs term buffer" - " is not supported\n", progname); - fail (); - } + if (find_tty (&tty_type, &tty_name, !tty)) + { #if !defined (NO_SOCKETS_IN_FILE_SYSTEM) - init_signals (); + init_signals (); #endif - - send_to_emacs (emacs_socket, "-tty "); - quote_argument (emacs_socket, tty_name); - send_to_emacs (emacs_socket, " "); - quote_argument (emacs_socket, type); - send_to_emacs (emacs_socket, " "); + send_to_emacs (emacs_socket, "-tty "); + quote_argument (emacs_socket, tty_name); + send_to_emacs (emacs_socket, " "); + quote_argument (emacs_socket, tty_type); + send_to_emacs (emacs_socket, " "); + } } - if (window_system) + if (!current_frame && !tty) send_to_emacs (emacs_socket, "-window-system "); if ((argc - optind > 0)) @@ -1510,7 +1639,7 @@ main (argc, argv) file:stream, and treated as absolute. The user can still pass a file:stream if desired (for example, .\X:Y), but it is not very useful, as Emacs currently does a - very bad job of dealing wih NTFS streams. */ + very bad job of dealing with NTFS streams. */ { char *filename = (char *) xmalloc (MAX_PATH); DWORD size; @@ -1536,20 +1665,15 @@ main (argc, argv) send_to_emacs (emacs_socket, " "); } } - else + else if (eval) { - if (!tty && !window_system) - { - while ((str = fgets (string, BUFSIZ, stdin))) - { - if (eval) - send_to_emacs (emacs_socket, "-eval "); - else - send_to_emacs (emacs_socket, "-file "); - quote_argument (emacs_socket, str); - } - send_to_emacs (emacs_socket, " "); - } + /* Read expressions interactively. */ + while ((str = fgets (string, BUFSIZ, stdin))) + { + send_to_emacs (emacs_socket, "-eval "); + quote_argument (emacs_socket, str); + } + send_to_emacs (emacs_socket, " "); } send_to_emacs (emacs_socket, "\n"); @@ -1582,7 +1706,6 @@ main (argc, argv) { /* -window-system-unsupported: Emacs was compiled without X support. Try again on the terminal. */ - window_system = 0; nowait = 0; tty = 1; goto retry;