/* Client process that communicates with GNU Emacs acting as server.
Copyright (C) 1986, 1987, 1994, 1999, 2000, 2001, 2002, 2003, 2004,
- 2005, 2006 Free Software Foundation, Inc.
+ 2005, 2006, 2007 Free Software Foundation, Inc.
This file is part of GNU Emacs.
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 2, or (at your option)
+the Free Software Foundation; either version 3, or (at your option)
any later version.
GNU Emacs is distributed in the hope that it will be useful,
#include <signal.h>
#include <errno.h>
-/* From lisp.h */
-#ifndef DIRECTORY_SEP
-#define DIRECTORY_SEP '/'
-#endif
-#ifndef IS_DIRECTORY_SEP
-#define IS_DIRECTORY_SEP(_c_) ((_c_) == DIRECTORY_SEP)
-#endif
-#ifndef IS_DEVICE_SEP
-#ifndef DEVICE_SEP
-#define IS_DEVICE_SEP(_c_) 0
-#else
-#define IS_DEVICE_SEP(_c_) ((_c_) == DEVICE_SEP)
-#endif
-#endif
-#ifndef IS_ANY_SEP
-#define IS_ANY_SEP(_c_) (IS_DIRECTORY_SEP (_c_))
-#endif
-
-
\f
char *getenv (), *getwd ();
char *(getcwd) ();
#define VERSION "unspecified"
#endif
\f
-#define SEND_STRING(data) (send_to_emacs (s, (data)))
-#define SEND_QUOTED(data) (quote_argument (s, (data)))
#ifndef EXIT_SUCCESS
#define EXIT_SUCCESS 0
/* Name used to invoke this program. */
char *progname;
-/* The first argument to main. */
-int main_argc;
-
/* The second argument to main. */
char **main_argv;
/* Nonzero means args are expressions to be evaluated. --eval. */
int eval = 0;
-/* Nonzero means don't open a new frame. --current-frame. */
-int current_frame = 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;
/* PID of the Emacs server process. */
int emacs_pid = 0;
-/* File handles for communicating with Emacs. */
-FILE *out, *in;
-
void print_help_and_exit () NO_RETURN;
struct option longopts[] =
{ "help", no_argument, NULL, 'H' },
{ "version", no_argument, NULL, 'V' },
{ "tty", no_argument, NULL, 't' },
- { "current-frame", no_argument, NULL, 'c' },
+ { "create-frame", no_argument, NULL, 'c' },
{ "alternate-editor", required_argument, NULL, 'a' },
#ifndef NO_SOCKETS_IN_FILE_SYSTEM
{ "socket-name", required_argument, NULL, 's' },
/* From sysdep.c */
#if !defined (HAVE_GET_CURRENT_DIR_NAME) || defined (BROKEN_GET_CURRENT_DIR_NAME)
+/* From lisp.h */
+#ifndef DIRECTORY_SEP
+#define DIRECTORY_SEP '/'
+#endif
+#ifndef IS_DIRECTORY_SEP
+#define IS_DIRECTORY_SEP(_c_) ((_c_) == DIRECTORY_SEP)
+#endif
+#ifndef IS_DEVICE_SEP
+#ifndef DEVICE_SEP
+#define IS_DEVICE_SEP(_c_) 0
+#else
+#define IS_DEVICE_SEP(_c_) ((_c_) == DEVICE_SEP)
+#endif
+#endif
+#ifndef IS_ANY_SEP
+#define IS_ANY_SEP(_c_) (IS_DIRECTORY_SEP (_c_))
+#endif
+
+
/* Return the current working directory. Returns NULL on errors.
Any other returned value must be freed with free. This is used
only when get_current_dir_name is not defined on the system. */
/* Message functions. */
#ifdef WINDOWSNT
-/* I first tried to check for STDOUT. The check did not work,
- I get a valid handle also in nonconsole apps.
- Instead I test for console title, which seems to work. */
int
-w32_window_app()
+w32_window_app ()
{
static int window_app = -1;
char szTitle[MAX_PATH];
if (window_app < 0)
+ /* Checking for STDOUT does not work; it's a valid handle also in
+ nonconsole apps. Testing for the console title seems to work. */
window_app = (GetConsoleTitleA (szTitle, MAX_PATH) == 0);
return window_app;
}
-#endif
+
+/*
+ execvp wrapper for Windows. Quotes arguments with embedded spaces.
+
+ This is necessary due to the broken implementation of exec* routines in
+ the Microsoft libraries: they concatenate the arguments together without
+ quoting special characters, and pass the result to CreateProcess, with
+ predictably bad results. By contrast, Posix execvp passes the arguments
+ directly into the argv array of the child process.
+*/
+int
+w32_execvp (path, argv)
+ char *path;
+ char **argv;
+{
+ int i;
+
+ /* Required to allow a .BAT script as alternate editor. */
+ argv[0] = (char *) alternate_editor;
+
+ for (i = 0; argv[i]; i++)
+ if (strchr (argv[i], ' '))
+ {
+ char *quoted = alloca (strlen (argv[i]) + 3);
+ sprintf (quoted, "\"%s\"", argv[i]);
+ argv[i] = quoted;
+ }
+
+ return execvp (path, argv);
+}
+
+#undef execvp
+#define execvp w32_execvp
+
+#endif /* WINDOWSNT */
void
message (int is_error, char *message, ...)
case 't':
tty = 1;
+ current_frame = 0;
break;
case 'c':
- current_frame = 1;
+ current_frame = 0;
break;
case 'H':
if (!tty && display)
window_system = 1;
+#if !defined (WINDOWSNT) && !defined (HAVE_CARBON)
else
tty = 1;
+#endif
/* --no-wait implies --current-frame on ttys when there are file
arguments or expressions given. */
void
print_help_and_exit ()
{
+ /* Spaces and tabs are significant in this message; they're chosen so the
+ message aligns properly both in a tty and in a Windows message box.
+ Please try to preserve them; otherwise the output is very hard to read
+ when using emacsclientw. */
message (FALSE,
"Usage: %s [OPTIONS] FILE...\n\
Tell the Emacs server to visit the specified files.\n\
Every FILE can be either just a FILENAME or [+LINE[:COLUMN]] FILENAME.\n\
\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\
--c, --current-frame Do not create a new frame; use the current Emacs frame\n\
--e, --eval Evaluate the FILE arguments as ELisp expressions\n\
--n, --no-wait Don't wait for the server to return\n\
--d, --display=DISPLAY Visit the file in the given display\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\
+-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\
+-n, --no-wait Don't wait for the server to return\n\
+-d, --display=DISPLAY Visit the file in the given display\n"
#ifndef NO_SOCKETS_IN_FILE_SYSTEM
"-s, --socket-name=FILENAME\n\
- Set filename of the UNIX socket for communication\n"
+ Set filename of the UNIX socket for communication\n"
#endif
"-f, --server-file=FILENAME\n\
- Set filename of the TCP authentication file\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\
+ Editor to fallback to if the server is not running\n\
\n\
Report bugs to bug-gnu-emacs@gnu.org.\n", progname);
exit (EXIT_SUCCESS);
/*
Try to run a different command, or --if no alternate editor is
defined-- exit with an errorcode.
+ Uses argv, but gets it from the global variable main_argv.
*/
void
fail (void)
if (alternate_editor)
{
int i = optind - 1;
-#ifdef WINDOWSNT
- main_argv[i] = (char *)alternate_editor;
-#endif
+
execvp (alternate_editor, main_argv + i);
message (TRUE, "%s: error executing alternate editor \"%s\"\n",
progname, alternate_editor);
int argc;
char **argv;
{
- main_argc = argc;
main_argv = argv;
progname = argv[0];
message (TRUE, "%s: Sorry, the Emacs server is supported only\n"
- "on systems with Berkeley sockets.\n",
+ "on systems with Berkeley sockets.\n",
argv[0]);
fail ();
}
# include <sys/types.h>
# include <sys/socket.h>
# include <sys/un.h>
-# include <sys/stat.h>
-# include <errno.h>
#endif
#define AUTH_KEY_LENGTH 64
/* Buffer to accumulate data to send in TCP connections. */
char send_buffer[SEND_BUFFER_SIZE + 1];
int sblen = 0; /* Fill pointer for the send buffer. */
+/* Socket used to communicate with the Emacs server process. */
+HSOCKET emacs_socket = 0;
/* Let's send the data to Emacs when either
- the data ends in "\n", or
}
*q++ = 0;
- SEND_STRING (copy);
+ send_to_emacs (s, copy);
free (copy);
}
if (filename[0] == '\0') return FALSE;
#ifdef WINDOWSNT
- /* X:\xxx is always absolute; X:xxx is an error and will fail. */
+ /* X:\xxx is always absolute. */
if (isalpha (filename[0])
&& filename[1] == ':' && (filename[2] == '\\' || filename[2] == '/'))
return TRUE;
/* Both \xxx and \\xxx\yyy are absolute. */
if (filename[0] == '\\') return TRUE;
+
+ /*
+ FIXME: There's a corner case not dealt with, "x:y", where:
+
+ 1) x is a valid drive designation (usually a letter in the A-Z range)
+ and y is a path, relative to the current directory on drive x. This
+ is absolute, *after* fixing the y part to include the current
+ directory in x.
+
+ 2) x is a relative file name, and y is an NTFS stream name. This is a
+ correct relative path, but it is very unusual.
+
+ The trouble is that first case items are also valid examples of the
+ second case, i.e., "c:test" can be understood as drive:path or as
+ file:stream.
+
+ The "right" fix would involve checking whether
+ - the current drive/partition is NTFS,
+ - x is a valid (and accesible) drive designator,
+ - x:y already exists as a file:stream in the current directory,
+ - y already exists on the current directory of drive x,
+ - the auspices are favorable,
+ and then taking an "informed decision" based on the above.
+
+ Whatever the result, Emacs currently does a very bad job of dealing
+ with NTFS file:streams: it cannot visit them, and the only way to
+ create one is by setting `buffer-file-name' to point to it (either
+ manually or with emacsclient). So perhaps resorting to 1) and ignoring
+ 2) for now is the right thing to do.
+
+ Anyway, something to decide After the Release.
+ */
#endif
return FALSE;
}
#ifdef WINDOWSNT
-/* Wrapper to make WSACleanup a cdecl, as required by atexit(). */
+/* Wrapper to make WSACleanup a cdecl, as required by atexit. */
void
__cdecl close_winsock ()
{
/*
* Read the information needed to set up a TCP comm channel with
* the Emacs server: host, port, pid and authentication string.
-*/
+ */
int
get_server_config (server, authentication)
struct sockaddr_in *server;
*/
auth_string[AUTH_KEY_LENGTH] = '\0';
- SEND_STRING ("-auth ");
- SEND_STRING (auth_string);
- SEND_STRING ("\n");
+ send_to_emacs (s, "-auth ");
+ send_to_emacs (s, auth_string);
+ send_to_emacs (s, "\n");
return s;
}
+
+/* Returns 1 if PREFIX is a prefix of STRING. */
+static int
+strprefix (char *prefix, char *string)
+{
+ return !strncmp (prefix, string, strlen (prefix));
+}
+
+
#if !defined (NO_SOCKETS_IN_FILE_SYSTEM)
/* Three possibilities:
if (tcgetpgrp (1) == getpgrp ())
{
/* We are in the foreground. */
- fprintf (out, "-resume \n");
- fflush (out);
- fsync (fileno (out));
+ send_to_emacs (emacs_socket, "-resume \n");
}
else
{
{
int old_errno = errno;
sigset_t set;
-
- if (out)
- {
- fprintf (out, "-suspend \n");
- fflush (out);
- fsync (fileno (out));
- }
+
+ if (emacs_socket)
+ send_to_emacs (emacs_socket, "-suspend \n");
/* Unblock this signal and call the default handler by temprarily
changing the handler and resignalling. */
errno = old_errno;
}
-
/* Set up signal handlers before opening a frame on the current tty. */
void
}
-
-/* Returns 1 if PREFIX is a prefix of STRING. */
-static int
-strprefix (char *prefix, char *string)
-{
- int i;
- if (! prefix)
- return 1;
-
- if (!string)
- return 0;
-
- for (i = 0; prefix[i]; i++)
- if (!string[i] || string[i] != prefix[i])
- return 0;
- return 1;
-}
-
-
HSOCKET
set_local_socket ()
{
int sock_status = 0;
int default_sock = !socket_name;
int saved_errno = 0;
-
char *server_name = "server";
if (socket_name && !index (socket_name, '/') && !index (socket_name, '\\'))
set_socket ()
{
HSOCKET s;
-
+
INITIALIZE ();
-
+
#ifndef NO_SOCKETS_IN_FILE_SYSTEM
/* Explicit --socket-name argument. */
if (socket_name)
s = set_tcp_socket ();
if ((s != INVALID_SOCKET) || alternate_editor)
return s;
-
+
message (TRUE, "%s: error accessing server file \"%s\"",
progname, server_file);
exit (EXIT_FAILURE);
}
-
+
#ifndef NO_SOCKETS_IN_FILE_SYSTEM
/* Implicit local socket. */
s = set_local_socket ();
exit (EXIT_FAILURE);
}
+#ifdef WINDOWSNT
+FARPROC set_fg; /* Pointer to AllowSetForegroundWindow. */
+FARPROC get_wc; /* Pointer to RealGetWindowClassA. */
+
+BOOL CALLBACK
+w32_find_emacs_process (hWnd, lParam)
+ HWND hWnd;
+ LPARAM lParam;
+{
+ DWORD pid;
+ char class[6];
+
+ /* Reject any window not of class "Emacs". */
+ if (! get_wc (hWnd, class, sizeof (class))
+ || strcmp (class, "Emacs"))
+ return TRUE;
+
+ /* We only need the process id, not the thread id. */
+ (void) GetWindowThreadProcessId (hWnd, &pid);
+
+ /* Not the one we're looking for. */
+ if (pid != (DWORD) emacs_pid) return TRUE;
+
+ /* OK, let's raise it. */
+ set_fg (emacs_pid);
+
+ /* Stop enumeration. */
+ return FALSE;
+}
+
+/*
+ * Search for a window of class "Emacs" and owned by a process with
+ * process id = emacs_pid. If found, allow it to grab the focus.
+ */
+void
+w32_give_focus ()
+{
+ HMODULE hUser32;
+
+ /* It shouldn't happen when dealing with TCP sockets. */
+ if (!emacs_pid) return;
+
+ if (!(hUser32 = LoadLibrary ("user32.dll"))) return;
+
+ /* Modern Windows restrict which processes can set the foreground window.
+ emacsclient can allow Emacs to grab the focus by calling the function
+ AllowSetForegroundWindow. Unfortunately, older Windows (W95, W98 and
+ NT) lack this function, so we have to check its availability. */
+ if ((set_fg = GetProcAddress (hUser32, "AllowSetForegroundWindow"))
+ && (get_wc = GetProcAddress (hUser32, "RealGetWindowClassA")))
+ EnumWindows (w32_find_emacs_process, (LPARAM) 0);
+
+ FreeLibrary (hUser32);
+}
+#endif
+
int
main (argc, argv)
int argc;
char **argv;
{
- HSOCKET s;
int i, rl, needlf = 0;
char *cwd, *str;
char string[BUFSIZ+1];
- main_argc = argc;
main_argv = argv;
progname = argv[0];
exit (EXIT_FAILURE);
}
- if ((s = set_socket ()) == INVALID_SOCKET)
+ if ((emacs_socket = set_socket ()) == INVALID_SOCKET)
fail ();
}
#ifdef WINDOWSNT
- /*
- Modern Windows restrict which processes can set the foreground window.
- emacsclient can allow Emacs to grab the focus by calling the function
- AllowSetForegroundWindow(). Unfortunately, older Windows (W95, W98
- and NT) lack this function, so we have to check its availability.
- */
- if (emacs_pid)
- {
- HMODULE hUser32;
-
- if (hUser32 = LoadLibrary ("user32.dll"))
- {
- FARPROC set_fg;
- if (set_fg = GetProcAddress (hUser32, "AllowSetForegroundWindow"))
- set_fg (emacs_pid);
- FreeLibrary (hUser32);
- }
- }
+ w32_give_focus ();
#endif
- /* First of all, send our version number for verification. */
- SEND_STRING ("-version ");
- SEND_STRING (VERSION);
- SEND_STRING (" ");
-
/* Send over our environment. */
if (!current_frame)
{
{
char *name = xstrdup (environ[i]);
char *value = strchr (name, '=');
- SEND_STRING ("-env ");
- SEND_QUOTED (environ[i]);
- SEND_STRING (" ");
+ send_to_emacs (emacs_socket, "-env ");
+ quote_argument (emacs_socket, environ[i]);
+ send_to_emacs (emacs_socket, " ");
}
}
/* Send over our current directory. */
if (!current_frame)
{
- SEND_STRING ("-dir ");
- SEND_QUOTED (cwd);
- SEND_STRING ("/");
- SEND_STRING (" ");
+ send_to_emacs (emacs_socket, "-dir ");
+ quote_argument (emacs_socket, cwd);
+ send_to_emacs (emacs_socket, "/");
+ send_to_emacs (emacs_socket, " ");
}
retry:
if (nowait)
- SEND_STRING ("-nowait ");
+ send_to_emacs (emacs_socket, "-nowait ");
if (current_frame)
- SEND_STRING ("-current-frame ");
-
+ send_to_emacs (emacs_socket, "-current-frame ");
+
if (display)
{
- SEND_STRING ("-display ");
- SEND_QUOTED (display);
- SEND_STRING (" ");
+ send_to_emacs (emacs_socket, "-display ");
+ quote_argument (emacs_socket, display);
+ send_to_emacs (emacs_socket, " ");
}
if (tty)
{
- char *tty_name = ttyname (fileno (stdin));
char *type = getenv ("TERM");
+ char *tty_name = NULL;
+#ifndef WINDOWSNT
+ tty_name = ttyname (fileno (stdin));
+#endif
if (! tty_name)
{
" is not supported\n", progname);
fail ();
}
-
+#if !defined (NO_SOCKETS_IN_FILE_SYSTEM)
init_signals ();
+#endif
- SEND_STRING ("-tty ");
- SEND_QUOTED (tty_name);
- SEND_STRING (" ");
- SEND_QUOTED (type);
- SEND_STRING (" ");
+ 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, " ");
}
if (window_system)
- SEND_STRING ("-window-system ");
+ send_to_emacs (emacs_socket, "-window-system ");
if ((argc - optind > 0))
{
if (eval)
{
/* Don't prepend cwd or anything like that. */
- SEND_STRING ("-eval ");
- SEND_QUOTED (argv[i]);
- SEND_STRING (" ");
+ send_to_emacs (emacs_socket, "-eval ");
+ quote_argument (emacs_socket, argv[i]);
+ send_to_emacs (emacs_socket, " ");
continue;
}
while (isdigit ((unsigned char) *p) || *p == ':') p++;
if (*p == 0)
{
- SEND_STRING ("-position ");
- SEND_QUOTED (argv[i]);
- SEND_STRING (" ");
+ send_to_emacs (emacs_socket, "-position ");
+ quote_argument (emacs_socket, argv[i]);
+ send_to_emacs (emacs_socket, " ");
continue;
}
else
else if (! file_name_absolute_p (argv[i]))
relative = 1;
- SEND_STRING ("-file ");
+ send_to_emacs (emacs_socket, "-file ");
if (relative)
{
- SEND_QUOTED (cwd);
- SEND_STRING ("/");
+ quote_argument (emacs_socket, cwd);
+ send_to_emacs (emacs_socket, "/");
}
- SEND_QUOTED (argv[i]);
- SEND_STRING (" ");
+ quote_argument (emacs_socket, argv[i]);
+ send_to_emacs (emacs_socket, " ");
}
}
else
while ((str = fgets (string, BUFSIZ, stdin)))
{
if (eval)
- SEND_STRING ("-eval ");
+ send_to_emacs (emacs_socket, "-eval ");
else
- SEND_STRING ("-file ");
- SEND_QUOTED (out);
+ send_to_emacs (emacs_socket, "-file ");
+ quote_argument (emacs_socket, str);
}
- SEND_STRING (" ");
+ send_to_emacs (emacs_socket, " ");
}
}
- SEND_STRING ("\n");
+ send_to_emacs (emacs_socket, "\n");
/* Wait for an answer. */
if (!eval && !tty && !nowait)
fsync (1);
/* Now, wait for an answer and print any messages. */
- while ((rl = recv (s, string, BUFSIZ, 0)) > 0)
+ while ((rl = recv (emacs_socket, string, BUFSIZ, 0)) > 0)
{
char *p;
string[rl] = '\0';
while (p > string && *p == '\n')
*p-- = 0;
- if (strprefix ("-good-version ", string))
- {
- /* -good-version: The versions match. */
- }
- else if (strprefix ("-emacs-pid ", string))
+ if (strprefix ("-emacs-pid ", string))
{
/* -emacs-pid PID: The process id of the Emacs process. */
emacs_pid = strtol (string + strlen ("-emacs-pid"), NULL, 10);
fprintf (stderr, "*ERROR*: %s", str);
needlf = str[0] == '\0' ? needlf : str[strlen (str) - 1] != '\n';
}
- else if (strprefix ("-suspend ", str))
+#ifdef SIGSTOP
+ else if (strprefix ("-suspend ", string))
{
/* -suspend: Suspend this terminal, i.e., stop the process. */
if (needlf)
needlf = 0;
kill (0, SIGSTOP);
}
+#endif
else
{
/* Unknown command. */
fflush (stdout);
fsync (1);
- CLOSE_SOCKET (s);
+ CLOSE_SOCKET (emacs_socket);
return EXIT_SUCCESS;
}