/* 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.
# define IF_LINT(Code) /* empty */
#endif
+#ifdef min
+#undef min
+#endif
+#define min(x, y) (((x) < (y)) ? (x) : (y))
+
\f
/* Name used to invoke this program. */
const char *progname;
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;
/* PID of the Emacs server process. */
int emacs_pid = 0;
-static void print_help_and_exit (void) NO_RETURN;
-static void fail (void) NO_RETURN;
+/* If non-NULL, a string that should form a frame parameter alist to
+ be used for the new frame */
+const char *frame_parameters = NULL;
+
+static _Noreturn void print_help_and_exit (void);
struct option longopts[] =
{ "nw", no_argument, NULL, 't' },
{ "create-frame", no_argument, NULL, 'c' },
{ "alternate-editor", required_argument, NULL, 'a' },
+ { "frame-parameters", required_argument, NULL, 'F' },
#ifndef NO_SOCKETS_IN_FILE_SYSTEM
{ "socket-name", required_argument, NULL, 's' },
#endif
\f
/* 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");
)
{
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
{
/* We need MAXPATHLEN here. */
buf = (char *) xmalloc (MAXPATHLEN + 1);
- if (!buf)
- return NULL;
if (getwd (buf) == NULL)
{
int tmp_errno = errno;
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);
{
DWORD size;
- if (size = ExpandEnvironmentStrings (value, NULL, 0))
+ if ((size = ExpandEnvironmentStrings (value, NULL, 0)))
{
char *buffer = (char *) xmalloc (size);
if (ExpandEnvironmentStrings (value, buffer, size))
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
{
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.
{
int opt = getopt_long_only (argc, argv,
#ifndef NO_SOCKETS_IN_FILE_SYSTEM
- "VHneqa:s:f:d:tc",
+ "VHneqa:s:f:d:F:tc",
#else
- "VHneqa:f:d:tc",
+ "VHneqa:f:d:F:tc",
#endif
longopts, 0);
print_help_and_exit ();
break;
+ case 'F':
+ frame_parameters = optarg;
+ break;
+
default:
message (TRUE, "Try `%s --help' for more information\n", progname);
exit (EXIT_FAILURE);
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\
}
\f
-static void
+static _Noreturn void
print_help_and_exit (void)
{
/* Spaces and tabs are significant in this message; they're chosen so the
-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\
+-F ALIST, --frame-parameters=ALIST\n\
+ Set the parameters of a new frame\n\
-e, --eval Evaluate the FILE arguments as ELisp expressions\n\
-n, --no-wait Don't wait for the server to return\n\
-q, --quiet Don't display messages on success\n\
defined-- exit with an errorcode.
Uses argv, but gets it from the global variable main_argv.
*/
-static void
+static _Noreturn void
fail (void)
{
if (alternate_editor)
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;
}
}
* 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
}
}
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)
0 - success: none of the above */
static int
-socket_status (char *name)
+socket_status (const char *name)
{
struct stat statbfr;
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);
static HSOCKET
-set_local_socket (void)
+set_local_socket (const char *local_socket_name)
{
HSOCKET s;
struct sockaddr_un server;
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)
{
size_t n = confstr (_CS_DARWIN_USER_TEMP_DIR, NULL, (size_t) 0);
if (n > 0)
{
- tmpdir = alloca (n);
- confstr (_CS_DARWIN_USER_TEMP_DIR, tmpdir, n);
+ tmpdir = tmpdir_storage = xmalloc (n);
+ confstr (_CS_DARWIN_USER_TEMP_DIR, tmpdir_storage, 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
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;
}
}
+ free (socket_name_storage);
+ free (tmpdir_storage);
+
switch (sock_status)
{
case 1:
set_socket (int no_exit_if_error)
{
HSOCKET s;
+ const char *local_server_file = server_file;
INITIALIZE ();
/* 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",
#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;
{
/* 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;
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;
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)
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 ();
}
/* 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++)
{
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))
+ if (frame_parameters && !current_frame)
+ {
+ send_to_emacs (emacs_socket, "-frame-parameters ");
+ quote_argument (emacs_socket, frame_parameters);
+ send_to_emacs (emacs_socket, " ");
+ }
+
+ /* 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;
/* Now, wait for an answer and print any messages. */
while (exit_status == EXIT_SUCCESS)
{
- char *p;
+ char *p, *end_p;
do
{
errno = 0;
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)