/* 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.
#ifdef WINDOWSNT
+/* config.h defines these, which disables sockets altogether! */
+# undef _WINSOCKAPI_
+# undef _WINSOCK_H
+
# include <malloc.h>
# include <stdlib.h>
+# include <windows.h>
-# define HAVE_SOCKETS
-# define HAVE_INET_SOCKETS
# define NO_SOCKETS_IN_FILE_SYSTEM
# define HSOCKET SOCKET
#else /* !WINDOWSNT */
+# include <sys/types.h>
+
# ifdef HAVE_INET_SOCKETS
# include <netinet/in.h>
# endif
#undef signal
+#include <stdarg.h>
#include <ctype.h>
#include <stdio.h>
#include "getopt.h"
/* If non-NULL, the filename of the authentication file. */
char *server_file = NULL;
+/* PID of the Emacs server process. */
+int emacs_pid = 0;
+
void print_help_and_exit () NO_RETURN;
struct option longopts[] =
{ 0, 0, 0, 0 }
};
+/* Message functions. */
+
+#ifdef WINDOWSNT
+int
+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
+
+void
+message (int is_error, char *message, ...)
+{
+ char msg [2048];
+ va_list args;
+
+ va_start (args, message);
+ vsprintf (msg, message, args);
+ va_end (args);
+
+#ifdef WINDOWSNT
+ if (w32_window_app ())
+ {
+ if (is_error)
+ MessageBox (NULL, msg, "Emacsclient ERROR", MB_ICONERROR);
+ else
+ MessageBox (NULL, msg, "Emacsclient", MB_ICONINFORMATION);
+ }
+ else
+#endif
+ {
+ FILE *f = is_error ? stderr : stdout;
+
+ fputs (msg, f);
+ fflush (f);
+ }
+}
+
/* Decode the options from argv and argc.
The global variable `optind' will say how many arguments we used up. */
break;
case 'V':
- printf ("emacsclient %s\n", VERSION);
+ message (FALSE, "emacsclient %s\n", VERSION);
exit (EXIT_SUCCESS);
break;
break;
default:
- fprintf (stderr, "Try `%s --help' for more information\n", progname);
+ message (TRUE, "Try `%s --help' for more information\n", progname);
exit (EXIT_FAILURE);
break;
}
void
print_help_and_exit ()
{
- printf (
+ 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 a version info and return\n\
--H, --help Print this usage information message\n\
--n, --no-wait Don't wait for the server to return\n\
--e, --eval Evaluate the FILE arguments as ELisp expressions\n\
--d, --display=DISPLAY Visit the file in the given display\n"
+\n\
+-V, --version Just print version info and return\n\
+-H, --help Print this usage information message\n\
+-e, --eval Evaluate FILE arguments as Lisp 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 the filename of the UNIX socket for communication\n"
+ Set filename of the UNIX socket for communication\n"
#endif
"-f, --server-file=FILENAME\n\
- Set the filename of the TCP configuration 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 server is not running\n\
\n\
Report bugs to bug-gnu-emacs@gnu.org.\n", progname);
exit (EXIT_SUCCESS);
}
\f
+#ifdef WINDOWSNT
+
+/*
+ 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 */
+
/*
Try to run a different command, or --if no alternate editor is
defined-- exit with an errorcode.
if (alternate_editor)
{
int i = optind - 1;
-#ifdef WINDOWSNT
- argv[i] = (char *)alternate_editor;
-#endif
+
execvp (alternate_editor, argv + i);
- fprintf (stderr, "%s: error executing alternate editor \"%s\"\n",
+ message (TRUE, "%s: error executing alternate editor \"%s\"\n",
progname, alternate_editor);
}
exit (EXIT_FAILURE);
int argc;
char **argv;
{
- fprintf (stderr, "%s: Sorry, the Emacs server is supported only\n",
+ message (TRUE, "%s: Sorry, the Emacs server is supported only\non systems with Berkely sockets.\n",
argv[0]);
- fprintf (stderr, "on systems with Berkeley sockets.\n");
fail (argc, argv);
}
if (filename[0] == '\0') return FALSE;
#ifdef WINDOWSNT
- /* X:\xxx is always absolute; X:xxx is an error and will fail. */
- if (islower (tolower (filename[0]))
- && filename[1] == ':' && filename[2] == '\\')
+ /* 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 ()
{
if (WSAStartup (MAKEWORD (2, 0), &wsaData))
{
- fprintf (stderr, "%s: error initializing WinSock2", progname);
+ message (TRUE, "%s: error initializing WinSock2", progname);
exit (EXIT_FAILURE);
}
\f
/*
* Read the information needed to set up a TCP comm channel with
- * the Emacs server: host, port and authentication string.
-*/
+ * the Emacs server: host, port, pid and authentication string.
+ */
int
get_server_config (server, authentication)
struct sockaddr_in *server;
{
char dotted[32];
char *port;
+ char *pid;
FILE *config = NULL;
if (file_name_absolute_p (server_file))
else
{
char *home = getenv ("HOME");
-#ifdef WINDOWSNT
- if (! home)
- home = getenv ("APPDATA");
-#endif
+
if (home)
{
char *path = alloca (32 + strlen (home) + strlen (server_file));
sprintf (path, "%s/.emacs.d/server/%s", home, server_file);
config = fopen (path, "rb");
}
+#ifdef WINDOWSNT
+ if (!config && (home = getenv ("APPDATA")))
+ {
+ char *path = alloca (32 + strlen (home) + strlen (server_file));
+ sprintf (path, "%s/.emacs.d/server/%s", home, server_file);
+ config = fopen (path, "rb");
+ }
+#endif
}
if (! config)
return FALSE;
if (fgets (dotted, sizeof dotted, config)
- && (port = strchr (dotted, ':')))
+ && (port = strchr (dotted, ':'))
+ && (pid = strchr (port, ' ')))
{
*port++ = '\0';
+ *pid++ = '\0';
}
else
{
- fprintf (stderr, "%s: invalid configuration info", progname);
+ message (TRUE, "%s: invalid configuration info", progname);
exit (EXIT_FAILURE);
}
if (! fread (authentication, AUTH_KEY_LENGTH, 1, config))
{
- fprintf (stderr, "%s: cannot read authentication info", progname);
+ message (TRUE, "%s: cannot read authentication info", progname);
exit (EXIT_FAILURE);
}
fclose (config);
+ emacs_pid = atoi (pid);
+
return TRUE;
}
return INVALID_SOCKET;
if (server.sin_addr.s_addr != inet_addr ("127.0.0.1"))
- fprintf (stderr, "%s: connected to remote socket at %s\n",
+ message (FALSE, "%s: connected to remote socket at %s\n",
progname, inet_ntoa (server.sin_addr));
/*
*/
if ((s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
- fprintf (stderr, "%s: ", progname);
- perror ("socket");
+ message (TRUE, "%s: socket: %s\n", progname, strerror (errno));
return INVALID_SOCKET;
}
*/
if (connect (s, (struct sockaddr *) &server, sizeof server) < 0)
{
- fprintf (stderr, "%s: ", progname);
- perror ("connect");
+ message (TRUE, "%s: connect: %s\n", progname, strerror (errno));
return INVALID_SOCKET;
}
if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) < 0)
{
- fprintf (stderr, "%s: ", progname);
- perror ("socket");
+ message (TRUE, "%s: socket: %s\n", progname, strerror (errno));
return INVALID_SOCKET;
}
strcpy (server.sun_path, socket_name);
else
{
- fprintf (stderr, "%s: socket-name %s too long",
+ message (TRUE, "%s: socket-name %s too long",
progname, socket_name);
exit (EXIT_FAILURE);
}
strcpy (server.sun_path, socket_name);
else
{
- fprintf (stderr, "%s: socket-name %s too long",
+ message (TRUE, "%s: socket-name %s too long",
progname, socket_name);
exit (EXIT_FAILURE);
}
we are root. */
if (0 != geteuid ())
{
- fprintf (stderr, "%s: Invalid socket owner\n", progname);
+ message (TRUE, "%s: Invalid socket owner\n", progname);
return INVALID_SOCKET;
}
break;
case 2:
/* `stat' failed */
if (saved_errno == ENOENT)
- fprintf (stderr,
+ message (TRUE,
"%s: can't find socket; have you started the server?\n\
To start the server in Emacs, type \"M-x server-start\".\n",
progname);
else
- fprintf (stderr, "%s: can't stat %s: %s\n",
+ message (TRUE, "%s: can't stat %s: %s\n",
progname, server.sun_path, strerror (saved_errno));
return INVALID_SOCKET;
}
if (connect (s, (struct sockaddr *) &server, strlen (server.sun_path) + 2)
< 0)
{
- fprintf (stderr, "%s: ", progname);
- perror ("connect");
+ message (TRUE, "%s: connect: %s\n", progname, strerror (errno));
return INVALID_SOCKET;
}
if ((s != INVALID_SOCKET) || alternate_editor)
return s;
- fprintf (stderr, "%s: error accessing socket \"%s\"",
+ message (TRUE, "%s: error accessing socket \"%s\"",
progname, socket_name);
exit (EXIT_FAILURE);
}
if ((s != INVALID_SOCKET) || alternate_editor)
return s;
- fprintf (stderr, "%s: error accessing server file \"%s\"",
+ message (TRUE, "%s: error accessing server file \"%s\"",
progname, server_file);
exit (EXIT_FAILURE);
}
return s;
/* No implicit or explicit socket, and no alternate editor. */
- fprintf (stderr, "%s: No socket or alternate editor. Please use:\n\n"
+ message (TRUE, "%s: No socket or alternate editor. Please use:\n\n"
#ifndef NO_SOCKETS_IN_FILE_SYSTEM
"\t--socket-name\n"
#endif
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;
if ((argc - optind < 1) && !eval)
{
- fprintf (stderr, "%s: file name or argument required\n", progname);
- fprintf (stderr, "Try `%s --help' for more information\n", progname);
+ message (TRUE, "%s: file name or argument required\nTry `%s --help' for more information\n",
+ progname, progname);
exit (EXIT_FAILURE);
}
if (cwd == 0)
{
/* getwd puts message in STRING if it fails. */
+ message (TRUE, "%s: %s (%s)\n", progname,
#ifdef HAVE_GETCWD
- fprintf (stderr, "%s: %s (%s)\n", progname,
- "Cannot get current working directory", strerror (errno));
+ "Cannot get current working directory",
#else
- fprintf (stderr, "%s: %s (%s)\n", progname, string, strerror (errno));
+ string,
#endif
+ strerror (errno));
fail (argc, argv);
}
+#ifdef WINDOWSNT
+ w32_give_focus ();
+#endif
+
if (nowait)
SEND_STRING ("-nowait ");