X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/0f74f7850de124626d556442d22d143d85457816..41b7f8bc6c4511ab6fcf7f6b1f9e7fdcd8592821:/lib-src/emacsclient.c diff --git a/lib-src/emacsclient.c b/lib-src/emacsclient.c index 2af139aee6..2950444540 100644 --- a/lib-src/emacsclient.c +++ b/lib-src/emacsclient.c @@ -1,5 +1,5 @@ /* Client process that communicates with GNU Emacs acting as server. - Copyright (C) 1986-1987, 1994, 1999-2011 Free Software Foundation, Inc. + Copyright (C) 1986-1987, 1994, 1999-2012 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -119,6 +119,11 @@ char *(getcwd) (char *, size_t); # define IF_LINT(Code) /* empty */ #endif +#ifdef min +#undef min +#endif +#define min(x, y) (((x) < (y)) ? (x) : (y)) + /* Name used to invoke this program. */ const char *progname; @@ -152,7 +157,7 @@ int tty = 0; const char *alternate_editor = NULL; /* If non-NULL, the filename of the UNIX socket. */ -char *socket_name = NULL; +const char *socket_name = NULL; /* If non-NULL, the filename of the authentication file. */ const char *server_file = NULL; @@ -194,10 +199,10 @@ struct option longopts[] = /* Like malloc but get fatal error if memory is exhausted. */ -static long * -xmalloc (unsigned int size) +static void * +xmalloc (size_t size) { - long *result = (long *) malloc (size); + void *result = malloc (size); if (result == NULL) { perror ("malloc"); @@ -250,32 +255,33 @@ get_current_dir_name (void) ) { buf = (char *) xmalloc (strlen (pwd) + 1); - if (!buf) - return NULL; strcpy (buf, pwd); } #ifdef HAVE_GETCWD else { size_t buf_size = 1024; - buf = (char *) xmalloc (buf_size); - if (!buf) - return NULL; for (;;) { + int tmp_errno; + buf = malloc (buf_size); + if (! buf) + break; if (getcwd (buf, buf_size) == buf) break; - if (errno != ERANGE) + tmp_errno = errno; + free (buf); + if (tmp_errno != ERANGE) { - int tmp_errno = errno; - free (buf); errno = tmp_errno; return NULL; } buf_size *= 2; - buf = (char *) realloc (buf, buf_size); - if (!buf) - return NULL; + if (! buf_size) + { + errno = ENOMEM; + return NULL; + } } } #else @@ -283,8 +289,6 @@ get_current_dir_name (void) { /* We need MAXPATHLEN here. */ buf = (char *) xmalloc (MAXPATHLEN + 1); - if (!buf) - return NULL; if (getwd (buf) == NULL) { int tmp_errno = errno; @@ -360,7 +364,7 @@ w32_getenv (char *envvar) char *value; DWORD dwType; - if (value = getenv (envvar)) + if ((value = getenv (envvar))) /* Found in the environment. strdup it, because values returned by getenv cannot be free'd. */ return xstrdup (value); @@ -383,7 +387,7 @@ w32_getenv (char *envvar) { DWORD size; - if (size = ExpandEnvironmentStrings (value, NULL, 0)) + if ((size = ExpandEnvironmentStrings (value, NULL, 0))) { char *buffer = (char *) xmalloc (size); if (ExpandEnvironmentStrings (value, buffer, size)) @@ -494,16 +498,17 @@ static void message (int, const char *, ...) ATTRIBUTE_FORMAT_PRINTF (2, 3); static void message (int is_error, const char *format, ...) { - char msg[2048]; va_list args; va_start (args, format); - vsprintf (msg, format, args); - va_end (args); #ifdef WINDOWSNT if (w32_window_app ()) { + char msg[2048]; + vsnprintf (msg, sizeof msg, format, args); + msg[sizeof msg - 1] = '\0'; + if (is_error) MessageBox (NULL, msg, "Emacsclient ERROR", MB_ICONERROR); else @@ -514,9 +519,11 @@ message (int is_error, const char *format, ...) { FILE *f = is_error ? stderr : stdout; - fputs (msg, f); + vfprintf (f, format, args); fflush (f); } + + va_end (args); } /* Decode the options from argv and argc. @@ -640,26 +647,25 @@ decode_options (int argc, char **argv) if (!current_frame && !display) tty = 1; - /* --no-wait implies --current-frame on ttys when there are file - arguments or expressions given. */ - if (nowait && tty && argc - optind > 0) - current_frame = 1; - #ifdef WINDOWSNT + /* Emacs on Windows does not support graphical and text terminal + frames in the same instance. So, treat the -t and -c options as + equivalent, and open a new frame on the server's terminal. + Ideally, we would only set tty = 1 when the serve is running in a + console, but alas we don't know that. As a workaround, always + ask for a tty frame, and let server.el figure it out. */ + if (!current_frame) + { + display = NULL; + tty = 1; + } + if (alternate_editor && alternate_editor[0] == '\0') { message (TRUE, "--alternate-editor argument or ALTERNATE_EDITOR variable cannot be\n\ an empty string"); exit (EXIT_FAILURE); } - - /* TTY frames not supported on Windows. Continue using GUI rather than - forcing the user to change their command-line. This is required since - tty is set above if certain options are given and $DISPLAY is not set, - which is not obvious to users. */ - if (tty) - tty = 0; - #endif /* WINDOWSNT */ } @@ -782,33 +788,35 @@ sock_err_message (const char *function_name) static void send_to_emacs (HSOCKET s, const char *data) { - while (data) + size_t dlen; + + if (!data) + return; + + dlen = strlen (data); + while (*data) { - size_t dlen = strlen (data); - if (dlen + sblen >= SEND_BUFFER_SIZE) - { - int part = SEND_BUFFER_SIZE - sblen; - strncpy (&send_buffer[sblen], data, part); - data += part; - sblen = SEND_BUFFER_SIZE; - } - else if (dlen) - { - strcpy (&send_buffer[sblen], data); - data = NULL; - sblen += dlen; - } - else - break; + size_t part = min (dlen, SEND_BUFFER_SIZE - sblen); + memcpy (&send_buffer[sblen], data, part); + data += part; + sblen += part; if (sblen == SEND_BUFFER_SIZE || (sblen > 0 && send_buffer[sblen-1] == '\n')) { int sent = send (s, send_buffer, sblen, 0); + if (sent < 0) + { + message (TRUE, "%s: failed to send %d bytes to socket: %s\n", + progname, sblen, strerror (errno)); + fail (); + } if (sent != sblen) - strcpy (send_buffer, &send_buffer[sent]); + memmove (send_buffer, &send_buffer[sent], sblen - sent); sblen -= sent; } + + dlen -= part; } } @@ -945,32 +953,39 @@ initialize_sockets (void) * the Emacs server: host, port, and authentication string. */ static int -get_server_config (struct sockaddr_in *server, char *authentication) +get_server_config (const char *config_file, struct sockaddr_in *server, + char *authentication) { char dotted[32]; char *port; FILE *config = NULL; - if (file_name_absolute_p (server_file)) - config = fopen (server_file, "rb"); + if (file_name_absolute_p (config_file)) + config = fopen (config_file, "rb"); else { const char *home = egetenv ("HOME"); if (home) { - char *path = alloca (strlen (home) + strlen (server_file) - + EXTRA_SPACE); - sprintf (path, "%s/.emacs.d/server/%s", home, server_file); + char *path = xmalloc (strlen (home) + strlen (config_file) + + EXTRA_SPACE); + strcpy (path, home); + strcat (path, "/.emacs.d/server/"); + strcat (path, config_file); config = fopen (path, "rb"); + free (path); } #ifdef WINDOWSNT if (!config && (home = egetenv ("APPDATA"))) { - char *path = alloca (strlen (home) + strlen (server_file) - + EXTRA_SPACE); - sprintf (path, "%s/.emacs.d/server/%s", home, server_file); + char *path = xmalloc (strlen (home) + strlen (config_file) + + EXTRA_SPACE); + strcpy (path, home); + strcat (path, "/.emacs.d/server/"); + strcat (path, config_file); config = fopen (path, "rb"); + free (path); } #endif } @@ -1003,14 +1018,14 @@ get_server_config (struct sockaddr_in *server, char *authentication) } static HSOCKET -set_tcp_socket (void) +set_tcp_socket (const char *local_server_file) { HSOCKET s; struct sockaddr_in server; struct linger l_arg = {1, 1}; char auth_string[AUTH_KEY_LENGTH + 1]; - if (! get_server_config (&server, auth_string)) + if (! get_server_config (local_server_file, &server, auth_string)) return INVALID_SOCKET; if (server.sin_addr.s_addr != inet_addr ("127.0.0.1") && !quiet) @@ -1185,7 +1200,7 @@ handle_sigtstp (int signalnum) send_to_emacs (emacs_socket, "-suspend \n"); /* Unblock this signal and call the default handler by temporarily - changing the handler and resignalling. */ + changing the handler and resignaling. */ sigprocmask (SIG_BLOCK, NULL, &set); sigdelset (&set, signalnum); signal (signalnum, SIG_DFL); @@ -1220,7 +1235,7 @@ init_signals (void) static HSOCKET -set_local_socket (void) +set_local_socket (const char *local_socket_name) { HSOCKET s; struct sockaddr_un server; @@ -1238,23 +1253,20 @@ set_local_socket (void) server.sun_family = AF_UNIX; { - int sock_status = 0; - int default_sock = !socket_name; - int saved_errno = 0; - const char *server_name = "server"; + int sock_status; + int use_tmpdir = 0; + int saved_errno; + const char *server_name = local_socket_name; const char *tmpdir IF_LINT ( = NULL); + char *tmpdir_storage = NULL; + char *socket_name_storage = NULL; - if (socket_name && !strchr (socket_name, '/') - && !strchr (socket_name, '\\')) + if (!strchr (local_socket_name, '/') && !strchr (local_socket_name, '\\')) { /* socket_name is a file name component. */ - server_name = socket_name; - socket_name = NULL; - default_sock = 1; /* Try both UIDs. */ - } - - if (default_sock) - { + long uid = geteuid (); + ptrdiff_t tmpdirlen; + use_tmpdir = 1; tmpdir = egetenv ("TMPDIR"); if (!tmpdir) { @@ -1265,32 +1277,35 @@ set_local_socket (void) size_t n = confstr (_CS_DARWIN_USER_TEMP_DIR, NULL, (size_t) 0); if (n > 0) { - tmpdir = alloca (n); + tmpdir = tmpdir_storage = xmalloc (n); confstr (_CS_DARWIN_USER_TEMP_DIR, tmpdir, n); } else #endif tmpdir = "/tmp"; } - socket_name = alloca (strlen (tmpdir) + strlen (server_name) - + EXTRA_SPACE); - sprintf (socket_name, "%s/emacs%d/%s", - tmpdir, (int) geteuid (), server_name); + tmpdirlen = strlen (tmpdir); + socket_name_storage = + xmalloc (tmpdirlen + strlen (server_name) + EXTRA_SPACE); + strcpy (socket_name_storage, tmpdir); + sprintf (socket_name_storage + tmpdirlen, "/emacs%ld/", uid); + strcat (socket_name_storage + tmpdirlen, server_name); + local_socket_name = socket_name_storage; } - if (strlen (socket_name) < sizeof (server.sun_path)) - strcpy (server.sun_path, socket_name); + if (strlen (local_socket_name) < sizeof (server.sun_path)) + strcpy (server.sun_path, local_socket_name); else { message (TRUE, "%s: socket-name %s too long\n", - progname, socket_name); + progname, local_socket_name); fail (); } /* See if the socket exists, and if it's owned by us. */ sock_status = socket_status (server.sun_path); saved_errno = errno; - if (sock_status && default_sock) + if (sock_status && use_tmpdir) { /* Failing that, see if LOGNAME or USER exist and differ from our euid. If so, look for a socket based on the UID @@ -1309,19 +1324,23 @@ set_local_socket (void) if (pw && (pw->pw_uid != geteuid ())) { /* We're running under su, apparently. */ - 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); + long uid = pw->pw_uid; + ptrdiff_t tmpdirlen = strlen (tmpdir); + char *user_socket_name + = xmalloc (tmpdirlen + strlen (server_name) + EXTRA_SPACE); + strcpy (user_socket_name, tmpdir); + sprintf (user_socket_name + tmpdirlen, "/emacs%ld/", uid); + strcat (user_socket_name + tmpdirlen, server_name); + + if (strlen (user_socket_name) < sizeof (server.sun_path)) + strcpy (server.sun_path, user_socket_name); else { message (TRUE, "%s: socket-name %s too long\n", - progname, socket_name); + progname, user_socket_name); exit (EXIT_FAILURE); } + free (user_socket_name); sock_status = socket_status (server.sun_path); saved_errno = errno; @@ -1331,6 +1350,9 @@ set_local_socket (void) } } + free (socket_name_storage); + free (tmpdir_storage); + switch (sock_status) { case 1: @@ -1372,6 +1394,7 @@ static HSOCKET set_socket (int no_exit_if_error) { HSOCKET s; + const char *local_server_file = server_file; INITIALIZE (); @@ -1379,7 +1402,7 @@ set_socket (int no_exit_if_error) /* Explicit --socket-name argument. */ if (socket_name) { - s = set_local_socket (); + s = set_local_socket (socket_name); if ((s != INVALID_SOCKET) || no_exit_if_error) return s; message (TRUE, "%s: error accessing socket \"%s\"\n", @@ -1389,30 +1412,29 @@ set_socket (int no_exit_if_error) #endif /* Explicit --server-file arg or EMACS_SERVER_FILE variable. */ - if (!server_file) - server_file = egetenv ("EMACS_SERVER_FILE"); + if (!local_server_file) + local_server_file = egetenv ("EMACS_SERVER_FILE"); - if (server_file) + if (local_server_file) { - s = set_tcp_socket (); + s = set_tcp_socket (local_server_file); if ((s != INVALID_SOCKET) || no_exit_if_error) return s; message (TRUE, "%s: error accessing server file \"%s\"\n", - progname, server_file); + progname, local_server_file); exit (EXIT_FAILURE); } #ifndef NO_SOCKETS_IN_FILE_SYSTEM /* Implicit local socket. */ - s = set_local_socket (); + s = set_local_socket ("server"); if (s != INVALID_SOCKET) return s; #endif /* Implicit server file. */ - server_file = "server"; - s = set_tcp_socket (); + s = set_tcp_socket ("server"); if ((s != INVALID_SOCKET) || no_exit_if_error) return s; @@ -1526,8 +1548,8 @@ start_daemon_and_retry_set_socket (void) { /* Pass --daemon=socket_name as argument. */ const char *deq = "--daemon="; - char *daemon_arg = alloca (strlen (deq) - + strlen (socket_name) + 1); + char *daemon_arg = xmalloc (strlen (deq) + + strlen (socket_name) + 1); strcpy (daemon_arg, deq); strcat (daemon_arg, socket_name); d_argv[1] = daemon_arg; @@ -1544,8 +1566,6 @@ main (int argc, char **argv) int rl = 0, needlf = 0; char *cwd, *str; char string[BUFSIZ+1]; - int null_socket_name IF_LINT ( = 0); - int null_server_file IF_LINT ( = 0); int start_daemon_if_needed; int exit_status = EXIT_SUCCESS; @@ -1573,14 +1593,6 @@ main (int argc, char **argv) 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); - } emacs_socket = set_socket (alternate_editor || start_daemon_if_needed); if (emacs_socket == INVALID_SOCKET) @@ -1588,13 +1600,6 @@ main (int argc, char **argv) if (! start_daemon_if_needed) fail (); - /* 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 (); } @@ -1614,7 +1619,11 @@ main (int argc, char **argv) /* Send over our environment and current directory. */ if (!current_frame) { +#ifndef WINDOWSNT + /* This is defined in stdlib.h on MS-Windows. It's defined in + unistd.h on some POSIX hosts, but not all (Bug#10155). */ extern char **environ; +#endif int i; for (i = 0; environ[i]; i++) { @@ -1656,10 +1665,10 @@ main (int argc, char **argv) send_to_emacs (emacs_socket, " "); } - /* 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)) + /* Unless we are certain we don't want to occupy the tty, send our + tty information to Emacs. For example, in daemon mode Emacs may + need to occupy this tty if no other frame is available. */ + if (!current_frame || !eval) { const char *tty_type, *tty_name; @@ -1757,7 +1766,7 @@ main (int argc, char **argv) /* Now, wait for an answer and print any messages. */ while (exit_status == EXIT_SUCCESS) { - char *p; + char *p, *end_p; do { errno = 0; @@ -1772,61 +1781,72 @@ main (int argc, char **argv) string[rl] = '\0'; - p = string + strlen (string) - 1; - while (p > string && *p == '\n') - *p-- = 0; + /* Loop over all NL-terminated messages. */ + for (end_p = p = string; end_p != NULL && *end_p != '\0'; p = end_p) + { + end_p = strchr (p, '\n'); + if (end_p != NULL) + *end_p++ = '\0'; - if (strprefix ("-emacs-pid ", string)) - { - /* -emacs-pid PID: The process id of the Emacs process. */ - emacs_pid = strtol (string + strlen ("-emacs-pid"), NULL, 10); - } - else if (strprefix ("-window-system-unsupported ", string)) - { - /* -window-system-unsupported: Emacs was compiled without X - support. Try again on the terminal. */ - nowait = 0; - tty = 1; - goto retry; - } - else if (strprefix ("-print ", string)) - { - /* -print STRING: Print STRING on the terminal. */ - str = unquote_argument (string + strlen ("-print ")); - if (needlf) - printf ("\n"); - printf ("%s", str); - needlf = str[0] == '\0' ? needlf : str[strlen (str) - 1] != '\n'; - } - else if (strprefix ("-error ", string)) - { - /* -error DESCRIPTION: Signal an error on the terminal. */ - str = unquote_argument (string + strlen ("-error ")); - if (needlf) - printf ("\n"); - fprintf (stderr, "*ERROR*: %s", str); - needlf = str[0] == '\0' ? needlf : str[strlen (str) - 1] != '\n'; - exit_status = EXIT_FAILURE; - } + if (strprefix ("-emacs-pid ", p)) + { + /* -emacs-pid PID: The process id of the Emacs process. */ + emacs_pid = strtol (p + strlen ("-emacs-pid"), NULL, 10); + } + else if (strprefix ("-window-system-unsupported ", p)) + { + /* -window-system-unsupported: Emacs was compiled without X + support. Try again on the terminal. */ + nowait = 0; + tty = 1; + goto retry; + } + else if (strprefix ("-print ", p)) + { + /* -print STRING: Print STRING on the terminal. */ + str = unquote_argument (p + strlen ("-print ")); + if (needlf) + printf ("\n"); + printf ("%s", str); + needlf = str[0] == '\0' ? needlf : str[strlen (str) - 1] != '\n'; + } + else if (strprefix ("-print-nonl ", p)) + { + /* -print-nonl STRING: Print STRING on the terminal. + Used to continue a preceding -print command. */ + str = unquote_argument (p + strlen ("-print-nonl ")); + printf ("%s", str); + needlf = str[0] == '\0' ? needlf : str[strlen (str) - 1] != '\n'; + } + else if (strprefix ("-error ", p)) + { + /* -error DESCRIPTION: Signal an error on the terminal. */ + str = unquote_argument (p + strlen ("-error ")); + if (needlf) + printf ("\n"); + fprintf (stderr, "*ERROR*: %s", str); + needlf = str[0] == '\0' ? needlf : str[strlen (str) - 1] != '\n'; + exit_status = EXIT_FAILURE; + } #ifdef SIGSTOP - else if (strprefix ("-suspend ", string)) - { - /* -suspend: Suspend this terminal, i.e., stop the process. */ - if (needlf) - printf ("\n"); - needlf = 0; - kill (0, SIGSTOP); - } + else if (strprefix ("-suspend ", p)) + { + /* -suspend: Suspend this terminal, i.e., stop the process. */ + if (needlf) + printf ("\n"); + needlf = 0; + kill (0, SIGSTOP); + } #endif - else - { - /* Unknown command. */ - if (needlf) - printf ("\n"); - printf ("*ERROR*: Unknown message: %s", string); - needlf = string[0] - == '\0' ? needlf : string[strlen (string) - 1] != '\n'; - } + else + { + /* Unknown command. */ + if (needlf) + printf ("\n"); + needlf = 0; + printf ("*ERROR*: Unknown message: %s\n", p); + } + } } if (needlf)