unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: "Juanma Barranquero" <lekktu@gmail.com>
Subject: Re: Back to emacsclient/server
Date: Fri, 27 Oct 2006 02:27:09 +0200	[thread overview]
Message-ID: <f7ccd24b0610261727t1cf8a448v36eb641348fdad1c@mail.gmail.com> (raw)
In-Reply-To: <f7ccd24b0610240139x7a5a0807n4efe09b41e573f5b@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 1982 bytes --]

OK, there's a rough first cut at the emacsclient patch (attached in
context and unified diffs, for easier reading). No doc patches nor
ChangeLogs, yet.

Notes:

  - Should `server-ensure-safe-dir' be used also to create/check
safety of the `server-auth-dir' directory (by default at
~/.emacs.d/server)? If so, `server-ensure-safe-dir' must be changed to
create directories in the path (because `server-auth-dir', unlike
`server-socket-dir', can be customized by the user, and so it can be
at any depth). I've implemented this (by passing the PARENTS arg to
`make-directory'), but I'm not sure that's the right way,
security-wise.

  - Also, I've patched `server-ensure-safe-dir' to ignore Unix-style
file permissions in Windows (checking ACLs would be useful, but I
don't know whether that's possible from Emacs).

  - emasclient.c compiles and works fine with MinGW 3.4.5; I'll try to
test it with MSVC once the code/features settle.

  - With an eye to porting it to MSVC, I've changed the code to use
send/recv instead of streams (it'll be easier to maintain
compatibility, I believe). The Unix sockets code should work OK, but I
don't really know, so please test it.

 - I haven't tried compiling on GNU/Linux or Unix, so there could be
typos or oversights, which should be trivial to fix.

  - There's a new argument --server-file. If --server-file (or
environment variable EMACS_SERVER_FILE) exist, emacsclient uses TCP
sockets, else it uses Unix sockets (either --socket-name, or "emacs"
by default).

  - The only major thing missing is interpreting
--server-file=SERVERNAME as ~/.emacs.d/server/SERVERNAME (currently,
you've got to pass the full path). What would be the easier portable
way to find the user directory? The $HOME (or %HOME in Windows) env
var?

I'd be glad if people could try the patch, with stress on testing on
non-Windows environments and non-TCP sockets (I want to be sure I'm
not breaking anything).

Comments welcome.

                    /L/e/k/t/u

[-- Attachment #2: newserver-c.patch --]
[-- Type: application/octet-stream, Size: 30313 bytes --]

Index: lisp/server.el
===================================================================
RCS file: /cvsroot/emacs/emacs/lisp/server.el,v
retrieving revision 1.113
diff -b -c -2 -r1.113 server.el
*** lisp/server.el	6 Jul 2006 22:48:16 -0000	1.113
--- lisp/server.el	26 Oct 2006 23:06:08 -0000
***************
*** 83,86 ****
--- 83,115 ----
    :group 'external)
  
+ (defcustom server-use-tcp nil
+   "If non-nil, use TCP sockets instead of local sockets."
+   :set #'(lambda (sym val)
+            (if (featurep 'make-network-process '(:family local))
+                (set-default sym val)
+              (unless load-in-progress
+                (message "Local sockets unsupported, using TCP sockets"))
+              (set-default sym t)))
+   :group 'server
+   :type 'boolean
+   :version "22.1")
+ 
+ (defcustom server-host nil
+   "The name or IP address to use as host address of the server process.
+ If set, the server accepts remote connections; otherwise it is local."
+   :group 'server
+   :type '(choice
+           (string :tag "Name or IP address")
+           (const :tag "Local" nil))
+   :version "22.1")
+ (put 'server-host 'risky-local-variable t)
+ 
+ (defcustom server-auth-dir "~/.emacs.d/server/"
+   "Directory for server authentication files."
+   :group 'server
+   :type 'directory
+   :version "22.1")
+ (put 'server-auth-dir 'risky-local-variable t)
+ 
  (defcustom server-visit-hook nil
    "*Hook run when visiting a file for the Emacs server."
***************
*** 101,104 ****
--- 130,136 ----
    "The current server process.")
  
+ (defvar server-auth-string nil
+   "The current server authentication string.")
+ 
  (defvar server-clients nil
    "List of current server clients.
***************
*** 232,242 ****
    (let ((attrs (file-attributes dir)))
      (unless attrs
!       (letf (((default-file-modes) ?\700)) (make-directory dir))
        (setq attrs (file-attributes dir)))
      ;; Check that it's safe for use.
      (unless (and (eq t (car attrs)) (eq (nth 2 attrs) (user-uid))
! 		 (zerop (logand ?\077 (file-modes dir))))
        (error "The directory %s is unsafe" dir))))
  
  ;;;###autoload
  (defun server-start (&optional leave-dead)
--- 264,285 ----
    (let ((attrs (file-attributes dir)))
      (unless attrs
!       (letf (((default-file-modes) ?\700)) (make-directory dir t))
        (setq attrs (file-attributes dir)))
      ;; Check that it's safe for use.
      (unless (and (eq t (car attrs)) (eq (nth 2 attrs) (user-uid))
!                  (or (eq system-type 'windows-nt)
!                      (zerop (logand ?\077 (file-modes dir)))))
        (error "The directory %s is unsafe" dir))))
  
+ (defun server-auth-string ()
+   (or server-auth-string
+       ;; If the authentication string does not exist, create it on the fly:
+       ;; it's a 64-byte string of random chars in the range `!'..`~'.
+       (setq server-auth-string
+ 	    (loop
+ 	       for i below 64
+ 	       collect (+ 33 (random 94)) into auth
+ 	       finally return (concat auth)))))
+ 
  ;;;###autoload
  (defun server-start (&optional leave-dead)
***************
*** 249,259 ****
  Prefix arg means just kill any existing server communications subprocess."
    (interactive "P")
    ;; kill it dead!
!   (if server-process
!       (condition-case () (delete-process server-process) (error nil)))
!   ;; Delete the socket files made by previous server invocations.
!   (condition-case ()
        (delete-file (expand-file-name server-name server-socket-dir))
!     (error nil))
    ;; If this Emacs already had a server, clear out associated status.
    (while server-clients
--- 292,304 ----
  Prefix arg means just kill any existing server communications subprocess."
    (interactive "P")
+   (when server-process
      ;; kill it dead!
!     (ignore-errors (delete-process server-process))
!     (ignore-errors
!       ;; Delete the socket or authentication files made by previous server invocations.
!       (if (eq (process-contact server-process :family) 'local)
            (delete-file (expand-file-name server-name server-socket-dir))
!         (setq server-auth-string nil)
!         (delete-file (expand-file-name server-name server-auth-dir)))))
    ;; If this Emacs already had a server, clear out associated status.
    (while server-clients
***************
*** 263,279 ****
    (unless leave-dead
      ;; Make sure there is a safe directory in which to place the socket.
!     (server-ensure-safe-dir server-socket-dir)
!     (if server-process
  	(server-log (message "Restarting server")))
      (letf (((default-file-modes) ?\700))
        (setq server-process
! 	    (make-network-process
! 	     :name "server" :family 'local :server t :noquery t
! 	     :service (expand-file-name server-name server-socket-dir)
! 	     :sentinel 'server-sentinel :filter 'server-process-filter
  	     ;; We must receive file names without being decoded.
  	     ;; Those are decoded by server-process-filter according
  	     ;; to file-name-coding-system.
! 	     :coding 'raw-text)))))
  
  ;;;###autoload
--- 308,342 ----
    (unless leave-dead
      ;; Make sure there is a safe directory in which to place the socket.
!     (server-ensure-safe-dir (if server-use-tcp server-auth-dir server-socket-dir))
!     (when server-process
        (server-log (message "Restarting server")))
      (letf (((default-file-modes) ?\700))
        (setq server-process
!             (apply #'make-network-process
!                    :name server-name
!                    :server t
!                    :noquery t
!                    :sentinel 'server-sentinel
!                    :filter 'server-process-filter
                     ;; We must receive file names without being decoded.
                     ;; Those are decoded by server-process-filter according
                     ;; to file-name-coding-system.
!                    :coding 'raw-text
!                    ;; The rest of the arguments depend on the kind of socket used
!                    (if server-use-tcp
!                        (list :family nil
!                              :service t
!                              :host (or server-host 'local)
!                              :plist '(:authenticated nil))
!                      (list :family 'local
!                            :service (expand-file-name server-name server-socket-dir)
!                            :plist '(:authenticated t))))))
!     (unless server-process (error "Could not start server process"))
!     (when server-use-tcp
!       (with-temp-file (expand-file-name server-name server-auth-dir)
!         (set-buffer-multibyte nil)
!         (setq buffer-file-coding-system 'no-conversion)
!         (insert (format-network-address (process-contact server-process :local))
!                 "\n" (server-auth-string))))))
  
  ;;;###autoload
***************
*** 290,301 ****
    (server-start (not server-mode)))
  \f
! (defun server-process-filter (proc string)
    "Process a request from the server to edit some files.
  PROC is the server process.  Format of STRING is \"PATH PATH PATH... \\n\"."
    (server-log string proc)
!   (let ((prev (process-get proc 'previous-string)))
      (when prev
        (setq string (concat prev string))
!       (process-put proc 'previous-string nil)))
    ;; If the input is multiple lines,
    ;; process each line individually.
--- 353,375 ----
    (server-start (not server-mode)))
  \f
! (defun* server-process-filter (proc string)
    "Process a request from the server to edit some files.
  PROC is the server process.  Format of STRING is \"PATH PATH PATH... \\n\"."
+   ;; First things first: let's check the authentication
+   (unless (process-get proc :authenticated)
+     (if (and (string-match "-auth \\(.*?\\)\n" string)
+              (string= (match-string 1 string) server-auth-string))
+         (progn
+           (setq string (substring string (match-end 0)))
+           (process-put proc :authenticated t)
+           (server-log "Authentication successful" proc))
+       (server-log "Authentication failed" proc)
+       (delete-process proc)
+       (return-from server-process-filter)))
    (server-log string proc)
!   (let ((prev (process-get proc :previous-string)))
      (when prev
        (setq string (concat prev string))
!       (process-put proc :previous-string nil)))
    ;; If the input is multiple lines,
    ;; process each line individually.
***************
*** 390,394 ****
    ;; Save for later any partial line that remains.
    (when (> (length string) 0)
!     (process-put proc 'previous-string string)))
  
  (defun server-goto-line-column (file-line-col)
--- 464,468 ----
    ;; Save for later any partial line that remains.
    (when (> (length string) 0)
!     (process-put proc :previous-string string)))
  
  (defun server-goto-line-column (file-line-col)
Index: lib-src/emacsclient.c
===================================================================
RCS file: /cvsroot/emacs/emacs/lib-src/emacsclient.c,v
retrieving revision 1.77
diff -b -c -2 -r1.77 emacsclient.c
*** lib-src/emacsclient.c	18 Jul 2006 16:33:45 -0000	1.77
--- lib-src/emacsclient.c	27 Oct 2006 00:06:01 -0000
***************
*** 27,35 ****
  #endif
  
  #undef signal
  
  #include <ctype.h>
  #include <stdio.h>
! #include <getopt.h>
  #ifdef HAVE_UNISTD_H
  #include <unistd.h>
--- 27,40 ----
  #endif
  
+ #ifdef WINDOWSNT
+ #define HAVE_SOCKETS
+ #define NO_SOCKETS_IN_FILE_SYSTEM
+ #endif
+ 
  #undef signal
  
  #include <ctype.h>
  #include <stdio.h>
! #include "getopt.h"
  #ifdef HAVE_UNISTD_H
  #include <unistd.h>
***************
*** 38,43 ****
  #ifdef VMS
  # include "vms-pwd.h"
! #else
  # include <pwd.h>
  #endif /* not VMS */
  
--- 43,52 ----
  #ifdef VMS
  # include "vms-pwd.h"
! #else /* not VMS */
! #ifdef WINDOWSNT
! # include <io.h>
! #else /* not WINDOWSNT */
  # include <pwd.h>
+ #endif /* not WINDOWSNT */
  #endif /* not VMS */
  
***************
*** 49,52 ****
--- 58,76 ----
  #endif
  
+ #define SEND_STRING(data) (send_to_emacs (s, (data)))
+ #define SEND_QUOTED(data) (quote_file_name (s, (data)))
+ 
+ #ifndef EXIT_SUCCESS
+ #define EXIT_SUCCESS 0
+ #endif
+ 
+ #ifndef EXIT_FAILURE
+ #define EXIT_FAILURE 1
+ #endif
+ 
+ #ifndef NO_RETURN
+ #define NO_RETURN
+ #endif
+ \f
  /* Name used to invoke this program.  */
  char *progname;
***************
*** 68,71 ****
--- 92,98 ----
  char *socket_name = NULL;
  
+ /* If non-NULL, the filename of the authentication file.  */
+ char *server_file = NULL;
+ 
  void print_help_and_exit () NO_RETURN;
  
***************
*** 78,81 ****
--- 105,109 ----
    { "alternate-editor", required_argument, NULL, 'a' },
    { "socket-name",	required_argument, NULL, 's' },
+   { "server-file",	required_argument, NULL, 'f' },
    { "display",	required_argument, NULL, 'd' },
    { 0, 0, 0, 0 }
***************
*** 91,99 ****
  {
    alternate_editor = getenv ("ALTERNATE_EDITOR");
  
    while (1)
      {
        int opt = getopt_long (argc, argv,
! 			     "VHnea:s:d:", longopts, 0);
  
        if (opt == EOF)
--- 119,128 ----
  {
    alternate_editor = getenv ("ALTERNATE_EDITOR");
+   server_file = getenv ("EMACS_SERVER_FILE");
  
    while (1)
      {
        int opt = getopt_long (argc, argv,
! 			     "VHnea:s:f:d:", longopts, 0);
  
        if (opt == EOF)
***************
*** 115,118 ****
--- 144,151 ----
  	  break;
  
+ 	case 'f':
+ 	  server_file = optarg;
+ 	  break;
+ 
  	case 'd':
  	  display = optarg;
***************
*** 158,163 ****
  -e, --eval              Evaluate the FILE arguments as ELisp expressions\n\
  -d, --display=DISPLAY   Visit the file in the given display\n\
! -s, --socket-name=FILENAME\n\
                          Set the filename of the UNIX socket for communication\n\
  -a, --alternate-editor=EDITOR\n\
                          Editor to fallback to if the server is not running\n\
--- 191,202 ----
  -e, --eval		Evaluate the FILE arguments as ELisp expressions\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\
+ "
+ #endif
+ "-f, --server-file=FILENAME\n\
+ 			Set the filename of the TCP configuration file\n\
  -a, --alternate-editor=EDITOR\n\
  			Editor to fallback to if the server is not running\n\
***************
*** 167,178 ****
  }
  
  /* In NAME, insert a & before each &, each space, each newline, and
     any initial -.  Change spaces to underscores, too, so that the
     return value never contains a space.  */
- 
  void
! quote_file_name (name, stream)
       char *name;
-      FILE *stream;
  {
    char *copy = (char *) malloc (strlen (name) * 2 + 1);
--- 206,313 ----
  }
  
+ \f
+ /*
+   Try to run a different command, or --if no alternate editor is
+   defined-- exit with an errorcode.
+ */
+ void
+ fail (argc, argv)
+      int argc;
+      char **argv;
+ {
+   if (alternate_editor)
+     {
+       int i = optind - 1;
+       execvp (alternate_editor, argv + i);
+       return;
+     }
+   else
+     {
+       exit (EXIT_FAILURE);
+     }
+ }
+ 
+ \f
+ #if !defined (HAVE_SOCKETS)
+ 
+ int
+ main (argc, argv)
+      int argc;
+      char **argv;
+ {
+   fprintf (stderr, "%s: Sorry, the Emacs server is supported only\n",
+ 	   argv[0]);
+   fprintf (stderr, "on systems with Berkeley sockets.\n");
+ 
+   fail (argc, argv);
+ }
+ 
+ #else /* HAVE_SOCKETS */
+ 
+ #define AUTH_STRING_LENGTH   64
+ #define SEND_BUFFER_SIZE   4096
+ 
+ #ifdef WINDOWSNT
+ # include <winsock2.h>
+ #else
+ # include <sys/types.h>
+ # include <sys/socket.h>
+ # include <sys/un.h>
+ # include <sys/stat.h>
+ # include <errno.h>
+ #endif
+ 
+ extern char *strerror ();
+ extern int errno;
+ 
+ /* 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.  */
+ 
+ /* Let's send the data to Emacs when either
+    - the data ends in "\n", or
+    - the buffer is full (but this shouldn't happen)
+    Otherwise, we just accumulate it.  */
+ void send_to_emacs (s, data)
+      SOCKET s;
+      char *data;
+ {
+   while (data)
+     {
+       int 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;
+ 
+       if (sblen == SEND_BUFFER_SIZE
+ 	  || (sblen > 0 && send_buffer[sblen-1] == '\n'))
+ 	{
+ 	  int sent = send (s, send_buffer, sblen, 0);
+ 	  if (sent != sblen)
+ 	    strcpy (send_buffer, &send_buffer[sent]);
+ 	  sblen -= sent;
+ 	}
+     }
+ }
+ 
  /* In NAME, insert a & before each &, each space, each newline, and
     any initial -.  Change spaces to underscores, too, so that the
     return value never contains a space.	 */
  void
! quote_file_name (s, name)
!      SOCKET s;
       char *name;
  {
    char *copy = (char *) malloc (strlen (name) * 2 + 1);
***************
*** 204,274 ****
    *q++ = 0;
  
!   fprintf (stream, "%s", copy);
  
    free (copy);
  }
  
! /* Like malloc but get fatal error if memory is exhausted.  */
  
! long *
! xmalloc (size)
!      unsigned int size;
  {
!   long *result = (long *) malloc (size);
!   if (result == NULL)
    {
!     perror ("malloc");
      exit (EXIT_FAILURE);
    }
!   return result;
  }
  \f
  /*
!   Try to run a different command, or --if no alternate editor is
!   defined-- exit with an errorcode.
  */
! void
! fail (argc, argv)
!      int argc;
!      char **argv;
  {
!   if (alternate_editor)
      {
!       int i = optind - 1;
!       execvp (alternate_editor, argv + i);
!       return;
      }
    else
      {
        exit (EXIT_FAILURE);
      }
- }
  
  
  \f
! #if !defined (HAVE_SOCKETS) || defined (NO_SOCKETS_IN_FILE_SYSTEM)
  
! int
! main (argc, argv)
!      int argc;
!      char **argv;
  {
!   fprintf (stderr, "%s: Sorry, the Emacs server is supported only\n",
! 	   argv[0]);
!   fprintf (stderr, "on systems with Berkeley sockets.\n");
  
!   fail (argc, argv);
! }
  
! #else /* HAVE_SOCKETS */
  
! #include <sys/types.h>
! #include <sys/socket.h>
! #include <sys/un.h>
! #include <sys/stat.h>
! #include <errno.h>
  
! extern char *strerror ();
! extern int errno;
  
  /* Three possibilities:
--- 339,463 ----
    *q++ = 0;
  
!   SEND_STRING (copy);
  
    free (copy);
  }
  
! #ifdef WINDOWSNT
! /* Wrapper to make WSACleanup a cdecl, as required by atexit().	 */
! void close_winsock ()
! {
!   WSACleanup ();
! }
! #endif /* WINDOWSNT */
  
! void initialize_sockets ()
  {
! #ifdef WINDOWSNT
!   WSADATA wsaData;
! 
!   /* Initialize the WinSock2 library.  */
!   if (WSAStartup (MAKEWORD (2, 0), &wsaData))
      {
!       fprintf (stderr, "%s: error initializing WinSock2", progname);
        exit (EXIT_FAILURE);
      }
! 
!   atexit (close_winsock);
! #endif /* WINDOWSNT */
  }
  
  /*
!  * Read the information needed to set up a TCP comm channel with
!  * the Emacs server: host, port and authentication string.
  */
! void get_server_config (server, authentication)
!      struct sockaddr_in *server;
!      char *authentication;
  {
!   FILE *config;
!   char dotted[32];
!   char *port;
! 
!   if (! (config = fopen (server_file, "rb")))
      {
!       fprintf (stderr, "%s: cannot read configuration file %s",
! 	       progname, server_file);
!       exit (EXIT_FAILURE);
!     }
! 
!   if (fgets (dotted, sizeof dotted, config)
!       && (port = strchr (dotted, ':')))
!     {
!       *port++ = '\0';
      }
    else
      {
+       fprintf (stderr, "%s: invalid configuration info", progname);
        exit (EXIT_FAILURE);
      }
  
+   server->sin_family = AF_INET;
+   server->sin_addr.s_addr = inet_addr (dotted);
+   server->sin_port = htons (atoi (port));
  
+   if (! fread (authentication, AUTH_STRING_LENGTH, 1, config))
+     {
+       fprintf (stderr, "%s: cannot read authentication info", progname);
+       exit (EXIT_FAILURE);
+     }
  
!   fclose (config);
! }
  
! SOCKET
! set_tcp_socket ()
  {
!   SOCKET s;
!   struct sockaddr_in server;
!   unsigned long c_arg = 0;
!   struct linger l_arg = {1, 1};
!   char auth_string[AUTH_STRING_LENGTH + 1];
  
!   initialize_sockets ();
  
!   get_server_config (&server, auth_string);
  
!   /*
!    * Open up an AF_INET socket
!    */
!   if ((s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
!     {
!       fprintf (stderr, "%s: ", progname);
!       perror ("socket");
!       return INVALID_SOCKET;
!     }
  
!   /*
!    * Set up the socket
!    */
!   if (connect (s, (struct sockaddr *) &server, sizeof server) < 0)
!     {
!       fprintf (stderr, "%s: ", progname);
!       perror ("connect");
!       return INVALID_SOCKET;
!     }
! 
!   ioctlsocket (s, FIONBIO, &c_arg);
!   setsockopt (s, SOL_SOCKET, SO_LINGER, (char *) &l_arg, sizeof l_arg);
! 
!   /*
!    * Send the authentication
!    */
!   auth_string[AUTH_STRING_LENGTH] = '\0';
! 
!   SEND_STRING ("-auth ");
!   SEND_STRING (auth_string);
!   SEND_STRING ("\n");
! 
!   return s;
! }
! 
! #if !defined (NO_SOCKETS_IN_FILE_SYSTEM)
  
  /* Three possibilities:
***************
*** 292,317 ****
  }
  
! int
! main (argc, argv)
!      int argc;
!      char **argv;
  {
!   int s, i, needlf = 0;
!   FILE *out, *in;
    struct sockaddr_un server;
-   char *cwd, *str;
-   char string[BUFSIZ];
- 
-   progname = argv[0];
- 
-   /* Process options.  */
-   decode_options (argc, argv);
- 
-   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);
-       exit (EXIT_FAILURE);
-     }
  
    /*
--- 481,489 ----
  }
  
! SOCKET
! set_local_socket ()
  {
!   SOCKET s;
    struct sockaddr_un server;
  
    /*
***************
*** 321,327 ****
    if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) < 0)
      {
!       fprintf (stderr, "%s: ", argv[0]);
        perror ("socket");
!       fail (argc, argv);
      }
  
--- 493,499 ----
    if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) < 0)
      {
!       fprintf (stderr, "%s: ", progname);
        perror ("socket");
!       return INVALID_SOCKET;
      }
  
***************
*** 353,357 ****
        {
  	fprintf (stderr, "%s: socket-name %s too long",
! 		 argv[0], socket_name);
  	exit (EXIT_FAILURE);
        }
--- 525,529 ----
        {
  	fprintf (stderr, "%s: socket-name %s too long",
! 		 progname, socket_name);
  	exit (EXIT_FAILURE);
        }
***************
*** 388,392 ****
  		  {
  		    fprintf (stderr, "%s: socket-name %s too long",
! 			     argv[0], socket_name);
  		    exit (EXIT_FAILURE);
  		  }
--- 560,564 ----
  		  {
  		    fprintf (stderr, "%s: socket-name %s too long",
! 			     progname, socket_name);
  		    exit (EXIT_FAILURE);
  		  }
***************
*** 408,412 ****
  	   {
  	     fprintf (stderr, "%s: Invalid socket owner\n", argv[0]);
! 	     fail (argc, argv);
  	   }
  	 break;
--- 580,584 ----
  	  {
  	    fprintf (stderr, "%s: Invalid socket owner\n", argv[0]);
! 	    return INVALID_SOCKET;
  	  }
  	break;
***************
*** 418,427 ****
  		    "%s: can't find socket; have you started the server?\n\
  To start the server in Emacs, type \"M-x server-start\".\n",
! 		    argv[0]);
  	 else
  	   fprintf (stderr, "%s: can't stat %s: %s\n",
! 		    argv[0], server.sun_path, strerror (saved_errno));
! 	 fail (argc, argv);
! 	 break;
         }
    }
--- 590,598 ----
  		   "%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",
! 		   progname, server.sun_path, strerror (saved_errno));
! 	return INVALID_SOCKET;
        }
    }
***************
*** 430,458 ****
        < 0)
      {
!       fprintf (stderr, "%s: ", argv[0]);
        perror ("connect");
!       fail (argc, argv);
      }
  
!   /* We use the stream OUT to send our command to the server.  */
!   if ((out = fdopen (s, "r+")) == NULL)
      {
!       fprintf (stderr, "%s: ", argv[0]);
!       perror ("fdopen");
!       fail (argc, argv);
      }
  
!   /* We use the stream IN to read the response.
!      We used to use just one stream for both output and input
!      on the socket, but reversing direction works nonportably:
!      on some systems, the output appears as the first input;
!      on other systems it does not.  */
!   if ((in = fdopen (s, "r+")) == NULL)
      {
!       fprintf (stderr, "%s: ", argv[0]);
!       perror ("fdopen");
!       fail (argc, argv);
      }
  
  #ifdef HAVE_GETCWD
    cwd = getcwd (string, sizeof string);
--- 601,654 ----
        < 0)
      {
!       fprintf (stderr, "%s: ", progname);
        perror ("connect");
!       return INVALID_SOCKET;
      }
  
!   return s;
! }
! #endif /* ! NO_SOCKETS_IN_FILE_SYSTEM */
! 
! SOCKET
! set_socket ()
! {
!   if (server_file)
!     return set_tcp_socket ();
!   else
! #ifndef NO_SOCKETS_IN_FILE_SYSTEM
!     return set_local_socket ();
! #else
      {
!       fprintf (stderr, "%s: missing --server-file option", progname);
!       exit (EXIT_FAILURE);
      }
+ #endif
+ }
+ 
+ int
+ main (argc, argv)
+      int argc;
+      char **argv;
+ {
+   SOCKET s;
+   int i, rl, needlf = 0;
+   char *cwd;
+   char string[BUFSIZ+1];
+ 
+   progname = argv[0];
  
!   /* Process options.  */
!   decode_options (argc, argv);
! 
!   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);
!       exit (EXIT_FAILURE);
      }
  
+   if ((s = set_socket ()) == INVALID_SOCKET)
+     fail (argc, argv);
+ 
  #ifdef HAVE_GETCWD
    cwd = getcwd (string, sizeof string);
***************
*** 465,472 ****
  
  #ifdef HAVE_GETCWD
!       fprintf (stderr, "%s: %s (%s)\n", argv[0],
  	       "Cannot get current working directory", strerror (errno));
  #else
!       fprintf (stderr, "%s: %s (%s)\n", argv[0], string, strerror (errno));
  #endif
        fail (argc, argv);
--- 661,668 ----
  
  #ifdef HAVE_GETCWD
!       fprintf (stderr, "%s: %s (%s)\n", progname,
  	       "Cannot get current working directory", strerror (errno));
  #else
!       fprintf (stderr, "%s: %s (%s)\n", progname, string, strerror (errno));
  #endif
        fail (argc, argv);
***************
*** 474,487 ****
  
    if (nowait)
!     fprintf (out, "-nowait ");
  
    if (eval)
!     fprintf (out, "-eval ");
  
    if (display)
      {
!       fprintf (out, "-display ");
!       quote_file_name (display, out);
!       fprintf (out, " ");
      }
  
--- 670,683 ----
  
    if (nowait)
!     SEND_STRING ("-nowait ");
  
    if (eval)
!     SEND_STRING ("-eval ");
  
    if (display)
      {
!       SEND_STRING ("-display ");
!       SEND_QUOTED (display);
!       SEND_STRING (" ");
      }
  
***************
*** 498,531 ****
  	      if (*p != 0)
  		{
! 		  quote_file_name (cwd, out);
! 		  fprintf (out, "/");
  		}
  	    }
  	  else if (*argv[i] != '/')
  	    {
! 	      quote_file_name (cwd, out);
! 	      fprintf (out, "/");
  	    }
  
! 	  quote_file_name (argv[i], out);
! 	  fprintf (out, " ");
  	}
      }
    else
      {
!       while ((str = fgets (string, BUFSIZ, stdin)))
  	{
! 	  quote_file_name (str, out);
  	}
!       fprintf (out, " ");
      }
  
!   fprintf (out, "\n");
!   fflush (out);
  
    /* Maybe wait for an answer.   */
!   if (nowait)
!     return EXIT_SUCCESS;
! 
    if (!eval)
      {
--- 694,734 ----
  	      if (*p != 0)
  		{
! 		  SEND_QUOTED (cwd);
! 		  SEND_STRING ("/");
  		}
  	    }
+ #ifndef WINDOWSNT
  	  else if (*argv[i] != '/')
+ #else
+ 	  else if ((*argv[i] != '/')
+ 		   /* Absolute paths can also start with backslash
+ 		      or drive letters.	 */
+ 		   && (*argv[i] != '\\')
+ 		   && (!islower (tolower (*argv[i]))
+ 		       || (argv[i][1] != ':')))
+ #endif
  	    {
! 	      SEND_QUOTED (cwd);
! 	      SEND_STRING ("/");
  	    }
  
! 	  SEND_QUOTED (argv[i]);
! 	  SEND_STRING (" ");
  	}
      }
    else
      {
!       while (fgets (string, BUFSIZ, stdin))
  	{
! 	  SEND_QUOTED (string);
  	}
!       SEND_STRING (" ");
      }
  
!   SEND_STRING ("\n");
  
    /* Maybe wait for an answer.	 */
!   if (!nowait)
!     {
        if (!eval)
  	{
***************
*** 536,545 ****
  
    /* Now, wait for an answer and print any messages.  */
!   while ((str = fgets (string, BUFSIZ, in)))
      {
        if (needlf == 2)
  	printf ("\n");
!       printf ("%s", str);
!       needlf = str[0] == '\0' ? needlf : str[strlen (str) - 1] != '\n';
      }
  
--- 739,749 ----
  
        /* Now, wait for an answer and print any messages.  */
!       while ((rl = recv (s, string, BUFSIZ, 0)) > 0)
  	{
+ 	  string[rl] = '\0';
  	  if (needlf == 2)
  	    printf ("\n");
! 	  printf ("%s", string);
! 	  needlf = string[0] == '\0' ? needlf : string[strlen (string) - 1] != '\n';
  	}
  
***************
*** 547,551 ****
--- 751,757 ----
  	printf ("\n");
        fflush (stdout);
+     }
  
+   closesocket (s);
    return EXIT_SUCCESS;
  }
Index: lib-src/makefile.w32-in
===================================================================
RCS file: /cvsroot/emacs/emacs/lib-src/makefile.w32-in,v
retrieving revision 2.46
diff -b -c -2 -r2.46 makefile.w32-in
*** lib-src/makefile.w32-in	9 Oct 2006 19:57:43 -0000	2.46
--- lib-src/makefile.w32-in	26 Oct 2006 22:23:12 -0000
***************
*** 21,25 ****
  #
  
! ALL = make-docfile hexl ctags etags movemail ebrowse sorted-doc digest-doc
  
  .PHONY: $(ALL)
--- 21,25 ----
  #
  
! ALL = make-docfile hexl ctags etags movemail ebrowse sorted-doc digest-doc emacsclient
  
  .PHONY: $(ALL)
***************
*** 33,37 ****
  #		$(BLD)/server.exe	\
  #		$(BLD)/emacstool.exe	\
- #		$(BLD)/emacsclient.exe	\
  #		$(BLD)/cvtmail.exe	\
  
--- 33,36 ----
***************
*** 60,63 ****
--- 59,63 ----
  sorted-doc:	$(BLD) $(BLD)/sorted-doc.exe
  digest-doc:	$(BLD) $(BLD)/digest-doc.exe
+ emacsclient:	$(BLD) $(BLD)/emacsclient.exe
  
  test-distrib:	$(BLD) $(BLD)/test-distrib.exe
***************
*** 75,78 ****
--- 75,91 ----
  		$(LINK) $(LINK_OUT)$@ $(LINK_FLAGS) $(MOVEMAILOBJS) $(WSOCK32) $(LIBS)
  
+ ECLIENT_CFLAGS = -DWINDOWSNT -DHAVE_GETCWD -DHAVE_STRERROR -c
+ ECLIENTOBJS =	$(BLD)/emacsclient.$(O) \
+ 		$(BLD)/getopt.$(O) \
+ 		$(BLD)/getopt1.$(O) \
+ 		$(BLD)/ntlib.$(O)
+ 
+ $(BLD)/emacsclient.exe:		$(ECLIENTOBJS)
+ # put wsock32.lib before $(LIBS) to ensure we don't link to ws2_32.lib
+ 		$(LINK) $(LINK_OUT)$@ $(LINK_FLAGS) $(ECLIENTOBJS) $(WSOCK32) $(LIBS)
+ 
+ $(BLD)/emacsclient.$(O):	emacsclient.c
+ 		$(CC) $(ECLIENT_CFLAGS) $(CC_OUT)$@ emacsclient.c
+ 
  ETAGSOBJ      = $(BLD)/etags.$(O) \
  		$(BLD)/getopt.$(O) \
***************
*** 297,300 ****
--- 310,314 ----
  		$(CP) $(BLD)/sorted-doc.exe $(INSTALL_DIR)/bin
  		$(CP) $(BLD)/digest-doc.exe $(INSTALL_DIR)/bin
+ 		$(CP) $(BLD)/emacsclient.exe $(INSTALL_DIR)/bin
  		- mkdir "$(INSTALL_DIR)/etc"
  		$(CP) $(DOC) $(INSTALL_DIR)/etc

[-- Attachment #3: newserver-u.patch --]
[-- Type: application/octet-stream, Size: 24201 bytes --]

Index: lisp/server.el
===================================================================
RCS file: /cvsroot/emacs/emacs/lisp/server.el,v
retrieving revision 1.113
diff -b -u -2 -r1.113 server.el
--- lisp/server.el	6 Jul 2006 22:48:16 -0000	1.113
+++ lisp/server.el	26 Oct 2006 23:06:08 -0000
@@ -83,4 +83,33 @@
   :group 'external)
 
+(defcustom server-use-tcp nil
+  "If non-nil, use TCP sockets instead of local sockets."
+  :set #'(lambda (sym val)
+           (if (featurep 'make-network-process '(:family local))
+               (set-default sym val)
+             (unless load-in-progress
+               (message "Local sockets unsupported, using TCP sockets"))
+             (set-default sym t)))
+  :group 'server
+  :type 'boolean
+  :version "22.1")
+
+(defcustom server-host nil
+  "The name or IP address to use as host address of the server process.
+If set, the server accepts remote connections; otherwise it is local."
+  :group 'server
+  :type '(choice
+          (string :tag "Name or IP address")
+          (const :tag "Local" nil))
+  :version "22.1")
+(put 'server-host 'risky-local-variable t)
+
+(defcustom server-auth-dir "~/.emacs.d/server/"
+  "Directory for server authentication files."
+  :group 'server
+  :type 'directory
+  :version "22.1")
+(put 'server-auth-dir 'risky-local-variable t)
+
 (defcustom server-visit-hook nil
   "*Hook run when visiting a file for the Emacs server."
@@ -101,4 +130,7 @@
   "The current server process.")
 
+(defvar server-auth-string nil
+  "The current server authentication string.")
+
 (defvar server-clients nil
   "List of current server clients.
@@ -232,11 +264,22 @@
   (let ((attrs (file-attributes dir)))
     (unless attrs
-      (letf (((default-file-modes) ?\700)) (make-directory dir))
+      (letf (((default-file-modes) ?\700)) (make-directory dir t))
       (setq attrs (file-attributes dir)))
     ;; Check that it's safe for use.
     (unless (and (eq t (car attrs)) (eq (nth 2 attrs) (user-uid))
-		 (zerop (logand ?\077 (file-modes dir))))
+                 (or (eq system-type 'windows-nt)
+                     (zerop (logand ?\077 (file-modes dir)))))
       (error "The directory %s is unsafe" dir))))
 
+(defun server-auth-string ()
+  (or server-auth-string
+      ;; If the authentication string does not exist, create it on the fly:
+      ;; it's a 64-byte string of random chars in the range `!'..`~'.
+      (setq server-auth-string
+	    (loop
+	       for i below 64
+	       collect (+ 33 (random 94)) into auth
+	       finally return (concat auth)))))
+
 ;;;###autoload
 (defun server-start (&optional leave-dead)
@@ -249,11 +292,13 @@
 Prefix arg means just kill any existing server communications subprocess."
   (interactive "P")
+  (when server-process
   ;; kill it dead!
-  (if server-process
-      (condition-case () (delete-process server-process) (error nil)))
-  ;; Delete the socket files made by previous server invocations.
-  (condition-case ()
+    (ignore-errors (delete-process server-process))
+    (ignore-errors
+      ;; Delete the socket or authentication files made by previous server invocations.
+      (if (eq (process-contact server-process :family) 'local)
       (delete-file (expand-file-name server-name server-socket-dir))
-    (error nil))
+        (setq server-auth-string nil)
+        (delete-file (expand-file-name server-name server-auth-dir)))))
   ;; If this Emacs already had a server, clear out associated status.
   (while server-clients
@@ -263,17 +308,35 @@
   (unless leave-dead
     ;; Make sure there is a safe directory in which to place the socket.
-    (server-ensure-safe-dir server-socket-dir)
-    (if server-process
+    (server-ensure-safe-dir (if server-use-tcp server-auth-dir server-socket-dir))
+    (when server-process
 	(server-log (message "Restarting server")))
     (letf (((default-file-modes) ?\700))
       (setq server-process
-	    (make-network-process
-	     :name "server" :family 'local :server t :noquery t
-	     :service (expand-file-name server-name server-socket-dir)
-	     :sentinel 'server-sentinel :filter 'server-process-filter
+            (apply #'make-network-process
+                   :name server-name
+                   :server t
+                   :noquery t
+                   :sentinel 'server-sentinel
+                   :filter 'server-process-filter
 	     ;; We must receive file names without being decoded.
 	     ;; Those are decoded by server-process-filter according
 	     ;; to file-name-coding-system.
-	     :coding 'raw-text)))))
+                   :coding 'raw-text
+                   ;; The rest of the arguments depend on the kind of socket used
+                   (if server-use-tcp
+                       (list :family nil
+                             :service t
+                             :host (or server-host 'local)
+                             :plist '(:authenticated nil))
+                     (list :family 'local
+                           :service (expand-file-name server-name server-socket-dir)
+                           :plist '(:authenticated t))))))
+    (unless server-process (error "Could not start server process"))
+    (when server-use-tcp
+      (with-temp-file (expand-file-name server-name server-auth-dir)
+        (set-buffer-multibyte nil)
+        (setq buffer-file-coding-system 'no-conversion)
+        (insert (format-network-address (process-contact server-process :local))
+                "\n" (server-auth-string))))))
 
 ;;;###autoload
@@ -290,12 +353,23 @@
   (server-start (not server-mode)))
 \f
-(defun server-process-filter (proc string)
+(defun* server-process-filter (proc string)
   "Process a request from the server to edit some files.
 PROC is the server process.  Format of STRING is \"PATH PATH PATH... \\n\"."
+  ;; First things first: let's check the authentication
+  (unless (process-get proc :authenticated)
+    (if (and (string-match "-auth \\(.*?\\)\n" string)
+             (string= (match-string 1 string) server-auth-string))
+        (progn
+          (setq string (substring string (match-end 0)))
+          (process-put proc :authenticated t)
+          (server-log "Authentication successful" proc))
+      (server-log "Authentication failed" proc)
+      (delete-process proc)
+      (return-from server-process-filter)))
   (server-log string proc)
-  (let ((prev (process-get proc 'previous-string)))
+  (let ((prev (process-get proc :previous-string)))
     (when prev
       (setq string (concat prev string))
-      (process-put proc 'previous-string nil)))
+      (process-put proc :previous-string nil)))
   ;; If the input is multiple lines,
   ;; process each line individually.
@@ -390,5 +464,5 @@
   ;; Save for later any partial line that remains.
   (when (> (length string) 0)
-    (process-put proc 'previous-string string)))
+    (process-put proc :previous-string string)))
 
 (defun server-goto-line-column (file-line-col)
Index: lib-src/emacsclient.c
===================================================================
RCS file: /cvsroot/emacs/emacs/lib-src/emacsclient.c,v
retrieving revision 1.77
diff -b -u -2 -r1.77 emacsclient.c
--- lib-src/emacsclient.c	18 Jul 2006 16:33:45 -0000	1.77
+++ lib-src/emacsclient.c	27 Oct 2006 00:06:01 -0000
@@ -27,9 +27,14 @@
 #endif
 
+#ifdef WINDOWSNT
+#define HAVE_SOCKETS
+#define NO_SOCKETS_IN_FILE_SYSTEM
+#endif
+
 #undef signal
 
 #include <ctype.h>
 #include <stdio.h>
-#include <getopt.h>
+#include "getopt.h"
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
@@ -38,6 +43,10 @@
 #ifdef VMS
 # include "vms-pwd.h"
-#else
+#else /* not VMS */
+#ifdef WINDOWSNT
+# include <io.h>
+#else /* not WINDOWSNT */
 # include <pwd.h>
+#endif /* not WINDOWSNT */
 #endif /* not VMS */
 
@@ -49,4 +58,19 @@
 #endif
 \f
+#define SEND_STRING(data) (send_to_emacs (s, (data)))
+#define SEND_QUOTED(data) (quote_file_name (s, (data)))
+
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS 0
+#endif
+
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1
+#endif
+
+#ifndef NO_RETURN
+#define NO_RETURN
+#endif
+\f
 /* Name used to invoke this program.  */
 char *progname;
@@ -68,4 +92,7 @@
 char *socket_name = NULL;
 
+/* If non-NULL, the filename of the authentication file.  */
+char *server_file = NULL;
+
 void print_help_and_exit () NO_RETURN;
 
@@ -78,4 +105,5 @@
   { "alternate-editor", required_argument, NULL, 'a' },
   { "socket-name",	required_argument, NULL, 's' },
+  { "server-file",	required_argument, NULL, 'f' },
   { "display",	required_argument, NULL, 'd' },
   { 0, 0, 0, 0 }
@@ -91,9 +119,10 @@
 {
   alternate_editor = getenv ("ALTERNATE_EDITOR");
+  server_file = getenv ("EMACS_SERVER_FILE");
 
   while (1)
     {
       int opt = getopt_long (argc, argv,
-			     "VHnea:s:d:", longopts, 0);
+			     "VHnea:s:f:d:", longopts, 0);
 
       if (opt == EOF)
@@ -115,4 +144,8 @@
 	  break;
 
+	case 'f':
+	  server_file = optarg;
+	  break;
+
 	case 'd':
 	  display = optarg;
@@ -158,6 +191,12 @@
 -e, --eval              Evaluate the FILE arguments as ELisp expressions\n\
 -d, --display=DISPLAY   Visit the file in the given display\n\
--s, --socket-name=FILENAME\n\
+"
+#ifndef NO_SOCKETS_IN_FILE_SYSTEM
+"-s, --socket-name=FILENAME\n\
                         Set the filename of the UNIX socket for communication\n\
+"
+#endif
+"-f, --server-file=FILENAME\n\
+			Set the filename of the TCP configuration file\n\
 -a, --alternate-editor=EDITOR\n\
                         Editor to fallback to if the server is not running\n\
@@ -167,12 +206,108 @@
 }
 
+\f
+/*
+  Try to run a different command, or --if no alternate editor is
+  defined-- exit with an errorcode.
+*/
+void
+fail (argc, argv)
+     int argc;
+     char **argv;
+{
+  if (alternate_editor)
+    {
+      int i = optind - 1;
+      execvp (alternate_editor, argv + i);
+      return;
+    }
+  else
+    {
+      exit (EXIT_FAILURE);
+    }
+}
+
+\f
+#if !defined (HAVE_SOCKETS)
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  fprintf (stderr, "%s: Sorry, the Emacs server is supported only\n",
+	   argv[0]);
+  fprintf (stderr, "on systems with Berkeley sockets.\n");
+
+  fail (argc, argv);
+}
+
+#else /* HAVE_SOCKETS */
+
+#define AUTH_STRING_LENGTH   64
+#define SEND_BUFFER_SIZE   4096
+
+#ifdef WINDOWSNT
+# include <winsock2.h>
+#else
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <sys/un.h>
+# include <sys/stat.h>
+# include <errno.h>
+#endif
+
+extern char *strerror ();
+extern int errno;
+
+/* 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.  */
+
+/* Let's send the data to Emacs when either
+   - the data ends in "\n", or
+   - the buffer is full (but this shouldn't happen)
+   Otherwise, we just accumulate it.  */
+void send_to_emacs (s, data)
+     SOCKET s;
+     char *data;
+{
+  while (data)
+    {
+      int 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;
+
+      if (sblen == SEND_BUFFER_SIZE
+	  || (sblen > 0 && send_buffer[sblen-1] == '\n'))
+	{
+	  int sent = send (s, send_buffer, sblen, 0);
+	  if (sent != sblen)
+	    strcpy (send_buffer, &send_buffer[sent]);
+	  sblen -= sent;
+	}
+    }
+}
+
 /* In NAME, insert a & before each &, each space, each newline, and
    any initial -.  Change spaces to underscores, too, so that the
    return value never contains a space.  */
-
 void
-quote_file_name (name, stream)
+quote_file_name (s, name)
+     SOCKET s;
      char *name;
-     FILE *stream;
 {
   char *copy = (char *) malloc (strlen (name) * 2 + 1);
@@ -204,71 +339,125 @@
   *q++ = 0;
 
-  fprintf (stream, "%s", copy);
+  SEND_STRING (copy);
 
   free (copy);
 }
 
-/* Like malloc but get fatal error if memory is exhausted.  */
+#ifdef WINDOWSNT
+/* Wrapper to make WSACleanup a cdecl, as required by atexit().	 */
+void close_winsock ()
+{
+  WSACleanup ();
+}
+#endif /* WINDOWSNT */
 
-long *
-xmalloc (size)
-     unsigned int size;
+void initialize_sockets ()
 {
-  long *result = (long *) malloc (size);
-  if (result == NULL)
+#ifdef WINDOWSNT
+  WSADATA wsaData;
+
+  /* Initialize the WinSock2 library.  */
+  if (WSAStartup (MAKEWORD (2, 0), &wsaData))
   {
-    perror ("malloc");
+      fprintf (stderr, "%s: error initializing WinSock2", progname);
     exit (EXIT_FAILURE);
   }
-  return result;
+
+  atexit (close_winsock);
+#endif /* WINDOWSNT */
 }
 \f
 /*
-  Try to run a different command, or --if no alternate editor is
-  defined-- exit with an errorcode.
+ * Read the information needed to set up a TCP comm channel with
+ * the Emacs server: host, port and authentication string.
 */
-void
-fail (argc, argv)
-     int argc;
-     char **argv;
+void get_server_config (server, authentication)
+     struct sockaddr_in *server;
+     char *authentication;
 {
-  if (alternate_editor)
+  FILE *config;
+  char dotted[32];
+  char *port;
+
+  if (! (config = fopen (server_file, "rb")))
     {
-      int i = optind - 1;
-      execvp (alternate_editor, argv + i);
-      return;
+      fprintf (stderr, "%s: cannot read configuration file %s",
+	       progname, server_file);
+      exit (EXIT_FAILURE);
+    }
+
+  if (fgets (dotted, sizeof dotted, config)
+      && (port = strchr (dotted, ':')))
+    {
+      *port++ = '\0';
     }
   else
     {
+      fprintf (stderr, "%s: invalid configuration info", progname);
       exit (EXIT_FAILURE);
     }
-}
 
+  server->sin_family = AF_INET;
+  server->sin_addr.s_addr = inet_addr (dotted);
+  server->sin_port = htons (atoi (port));
 
+  if (! fread (authentication, AUTH_STRING_LENGTH, 1, config))
+    {
+      fprintf (stderr, "%s: cannot read authentication info", progname);
+      exit (EXIT_FAILURE);
+    }
 \f
-#if !defined (HAVE_SOCKETS) || defined (NO_SOCKETS_IN_FILE_SYSTEM)
+  fclose (config);
+}
 
-int
-main (argc, argv)
-     int argc;
-     char **argv;
+SOCKET
+set_tcp_socket ()
 {
-  fprintf (stderr, "%s: Sorry, the Emacs server is supported only\n",
-	   argv[0]);
-  fprintf (stderr, "on systems with Berkeley sockets.\n");
+  SOCKET s;
+  struct sockaddr_in server;
+  unsigned long c_arg = 0;
+  struct linger l_arg = {1, 1};
+  char auth_string[AUTH_STRING_LENGTH + 1];
 
-  fail (argc, argv);
-}
+  initialize_sockets ();
 
-#else /* HAVE_SOCKETS */
+  get_server_config (&server, auth_string);
 
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/stat.h>
-#include <errno.h>
+  /*
+   * Open up an AF_INET socket
+   */
+  if ((s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
+    {
+      fprintf (stderr, "%s: ", progname);
+      perror ("socket");
+      return INVALID_SOCKET;
+    }
 
-extern char *strerror ();
-extern int errno;
+  /*
+   * Set up the socket
+   */
+  if (connect (s, (struct sockaddr *) &server, sizeof server) < 0)
+    {
+      fprintf (stderr, "%s: ", progname);
+      perror ("connect");
+      return INVALID_SOCKET;
+    }
+
+  ioctlsocket (s, FIONBIO, &c_arg);
+  setsockopt (s, SOL_SOCKET, SO_LINGER, (char *) &l_arg, sizeof l_arg);
+
+  /*
+   * Send the authentication
+   */
+  auth_string[AUTH_STRING_LENGTH] = '\0';
+
+  SEND_STRING ("-auth ");
+  SEND_STRING (auth_string);
+  SEND_STRING ("\n");
+
+  return s;
+}
+
+#if !defined (NO_SOCKETS_IN_FILE_SYSTEM)
 
 /* Three possibilities:
@@ -292,26 +481,9 @@
 }
 
-int
-main (argc, argv)
-     int argc;
-     char **argv;
+SOCKET
+set_local_socket ()
 {
-  int s, i, needlf = 0;
-  FILE *out, *in;
+  SOCKET s;
   struct sockaddr_un server;
-  char *cwd, *str;
-  char string[BUFSIZ];
-
-  progname = argv[0];
-
-  /* Process options.  */
-  decode_options (argc, argv);
-
-  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);
-      exit (EXIT_FAILURE);
-    }
 
   /*
@@ -321,7 +493,7 @@
   if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) < 0)
     {
-      fprintf (stderr, "%s: ", argv[0]);
+      fprintf (stderr, "%s: ", progname);
       perror ("socket");
-      fail (argc, argv);
+      return INVALID_SOCKET;
     }
 
@@ -353,5 +525,5 @@
       {
 	fprintf (stderr, "%s: socket-name %s too long",
-		 argv[0], socket_name);
+		 progname, socket_name);
 	exit (EXIT_FAILURE);
       }
@@ -388,5 +560,5 @@
 		  {
 		    fprintf (stderr, "%s: socket-name %s too long",
-			     argv[0], socket_name);
+			     progname, socket_name);
 		    exit (EXIT_FAILURE);
 		  }
@@ -408,5 +580,5 @@
 	   {
 	     fprintf (stderr, "%s: Invalid socket owner\n", argv[0]);
-	     fail (argc, argv);
+	    return INVALID_SOCKET;
 	   }
 	 break;
@@ -418,10 +590,9 @@
 		    "%s: can't find socket; have you started the server?\n\
 To start the server in Emacs, type \"M-x server-start\".\n",
-		    argv[0]);
+		   progname);
 	 else
 	   fprintf (stderr, "%s: can't stat %s: %s\n",
-		    argv[0], server.sun_path, strerror (saved_errno));
-	 fail (argc, argv);
-	 break;
+		   progname, server.sun_path, strerror (saved_errno));
+	return INVALID_SOCKET;
        }
   }
@@ -430,29 +601,54 @@
       < 0)
     {
-      fprintf (stderr, "%s: ", argv[0]);
+      fprintf (stderr, "%s: ", progname);
       perror ("connect");
-      fail (argc, argv);
+      return INVALID_SOCKET;
     }
 
-  /* We use the stream OUT to send our command to the server.  */
-  if ((out = fdopen (s, "r+")) == NULL)
+  return s;
+}
+#endif /* ! NO_SOCKETS_IN_FILE_SYSTEM */
+
+SOCKET
+set_socket ()
+{
+  if (server_file)
+    return set_tcp_socket ();
+  else
+#ifndef NO_SOCKETS_IN_FILE_SYSTEM
+    return set_local_socket ();
+#else
     {
-      fprintf (stderr, "%s: ", argv[0]);
-      perror ("fdopen");
-      fail (argc, argv);
+      fprintf (stderr, "%s: missing --server-file option", progname);
+      exit (EXIT_FAILURE);
     }
+#endif
+}
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  SOCKET s;
+  int i, rl, needlf = 0;
+  char *cwd;
+  char string[BUFSIZ+1];
+
+  progname = argv[0];
 
-  /* We use the stream IN to read the response.
-     We used to use just one stream for both output and input
-     on the socket, but reversing direction works nonportably:
-     on some systems, the output appears as the first input;
-     on other systems it does not.  */
-  if ((in = fdopen (s, "r+")) == NULL)
+  /* Process options.  */
+  decode_options (argc, argv);
+
+  if ((argc - optind < 1) && !eval)
     {
-      fprintf (stderr, "%s: ", argv[0]);
-      perror ("fdopen");
-      fail (argc, argv);
+      fprintf (stderr, "%s: file name or argument required\n", progname);
+      fprintf (stderr, "Try `%s --help' for more information\n", progname);
+      exit (EXIT_FAILURE);
     }
 
+  if ((s = set_socket ()) == INVALID_SOCKET)
+    fail (argc, argv);
+
 #ifdef HAVE_GETCWD
   cwd = getcwd (string, sizeof string);
@@ -465,8 +661,8 @@
 
 #ifdef HAVE_GETCWD
-      fprintf (stderr, "%s: %s (%s)\n", argv[0],
+      fprintf (stderr, "%s: %s (%s)\n", progname,
 	       "Cannot get current working directory", strerror (errno));
 #else
-      fprintf (stderr, "%s: %s (%s)\n", argv[0], string, strerror (errno));
+      fprintf (stderr, "%s: %s (%s)\n", progname, string, strerror (errno));
 #endif
       fail (argc, argv);
@@ -474,14 +670,14 @@
 
   if (nowait)
-    fprintf (out, "-nowait ");
+    SEND_STRING ("-nowait ");
 
   if (eval)
-    fprintf (out, "-eval ");
+    SEND_STRING ("-eval ");
 
   if (display)
     {
-      fprintf (out, "-display ");
-      quote_file_name (display, out);
-      fprintf (out, " ");
+      SEND_STRING ("-display ");
+      SEND_QUOTED (display);
+      SEND_STRING (" ");
     }
 
@@ -498,34 +694,41 @@
 	      if (*p != 0)
 		{
-		  quote_file_name (cwd, out);
-		  fprintf (out, "/");
+		  SEND_QUOTED (cwd);
+		  SEND_STRING ("/");
 		}
 	    }
+#ifndef WINDOWSNT
 	  else if (*argv[i] != '/')
+#else
+	  else if ((*argv[i] != '/')
+		   /* Absolute paths can also start with backslash
+		      or drive letters.	 */
+		   && (*argv[i] != '\\')
+		   && (!islower (tolower (*argv[i]))
+		       || (argv[i][1] != ':')))
+#endif
 	    {
-	      quote_file_name (cwd, out);
-	      fprintf (out, "/");
+	      SEND_QUOTED (cwd);
+	      SEND_STRING ("/");
 	    }
 
-	  quote_file_name (argv[i], out);
-	  fprintf (out, " ");
+	  SEND_QUOTED (argv[i]);
+	  SEND_STRING (" ");
 	}
     }
   else
     {
-      while ((str = fgets (string, BUFSIZ, stdin)))
+      while (fgets (string, BUFSIZ, stdin))
 	{
-	  quote_file_name (str, out);
+	  SEND_QUOTED (string);
 	}
-      fprintf (out, " ");
+      SEND_STRING (" ");
     }
 
-  fprintf (out, "\n");
-  fflush (out);
+  SEND_STRING ("\n");
 
   /* Maybe wait for an answer.   */
-  if (nowait)
-    return EXIT_SUCCESS;
-
+  if (!nowait)
+    {
   if (!eval)
     {
@@ -536,10 +739,11 @@
 
   /* Now, wait for an answer and print any messages.  */
-  while ((str = fgets (string, BUFSIZ, in)))
+      while ((rl = recv (s, string, BUFSIZ, 0)) > 0)
     {
+	  string[rl] = '\0';
       if (needlf == 2)
 	printf ("\n");
-      printf ("%s", str);
-      needlf = str[0] == '\0' ? needlf : str[strlen (str) - 1] != '\n';
+	  printf ("%s", string);
+	  needlf = string[0] == '\0' ? needlf : string[strlen (string) - 1] != '\n';
     }
 
@@ -547,5 +751,7 @@
     printf ("\n");
   fflush (stdout);
+    }
 
+  closesocket (s);
   return EXIT_SUCCESS;
 }
Index: lib-src/makefile.w32-in
===================================================================
RCS file: /cvsroot/emacs/emacs/lib-src/makefile.w32-in,v
retrieving revision 2.46
diff -b -u -2 -r2.46 makefile.w32-in
--- lib-src/makefile.w32-in	9 Oct 2006 19:57:43 -0000	2.46
+++ lib-src/makefile.w32-in	26 Oct 2006 22:23:12 -0000
@@ -21,5 +21,5 @@
 #
 
-ALL = make-docfile hexl ctags etags movemail ebrowse sorted-doc digest-doc
+ALL = make-docfile hexl ctags etags movemail ebrowse sorted-doc digest-doc emacsclient
 
 .PHONY: $(ALL)
@@ -33,5 +33,4 @@
 #		$(BLD)/server.exe	\
 #		$(BLD)/emacstool.exe	\
-#		$(BLD)/emacsclient.exe	\
 #		$(BLD)/cvtmail.exe	\
 
@@ -60,4 +59,5 @@
 sorted-doc:	$(BLD) $(BLD)/sorted-doc.exe
 digest-doc:	$(BLD) $(BLD)/digest-doc.exe
+emacsclient:	$(BLD) $(BLD)/emacsclient.exe
 
 test-distrib:	$(BLD) $(BLD)/test-distrib.exe
@@ -75,4 +75,17 @@
 		$(LINK) $(LINK_OUT)$@ $(LINK_FLAGS) $(MOVEMAILOBJS) $(WSOCK32) $(LIBS)
 
+ECLIENT_CFLAGS = -DWINDOWSNT -DHAVE_GETCWD -DHAVE_STRERROR -c
+ECLIENTOBJS =	$(BLD)/emacsclient.$(O) \
+		$(BLD)/getopt.$(O) \
+		$(BLD)/getopt1.$(O) \
+		$(BLD)/ntlib.$(O)
+
+$(BLD)/emacsclient.exe:		$(ECLIENTOBJS)
+# put wsock32.lib before $(LIBS) to ensure we don't link to ws2_32.lib
+		$(LINK) $(LINK_OUT)$@ $(LINK_FLAGS) $(ECLIENTOBJS) $(WSOCK32) $(LIBS)
+
+$(BLD)/emacsclient.$(O):	emacsclient.c
+		$(CC) $(ECLIENT_CFLAGS) $(CC_OUT)$@ emacsclient.c
+
 ETAGSOBJ      = $(BLD)/etags.$(O) \
 		$(BLD)/getopt.$(O) \
@@ -297,4 +310,5 @@
 		$(CP) $(BLD)/sorted-doc.exe $(INSTALL_DIR)/bin
 		$(CP) $(BLD)/digest-doc.exe $(INSTALL_DIR)/bin
+		$(CP) $(BLD)/emacsclient.exe $(INSTALL_DIR)/bin
 		- mkdir "$(INSTALL_DIR)/etc"
 		$(CP) $(DOC) $(INSTALL_DIR)/etc

[-- Attachment #4: Type: text/plain, Size: 142 bytes --]

_______________________________________________
Emacs-devel mailing list
Emacs-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/emacs-devel

  reply	other threads:[~2006-10-27  0:27 UTC|newest]

Thread overview: 54+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2006-10-23 14:14 Back to emacsclient/server Juanma Barranquero
2006-10-23 14:54 ` Jason Rumney
2006-10-23 19:48   ` Stefan Monnier
2006-10-23 21:17     ` Jason Rumney
2006-10-23 22:02       ` Andreas Schwab
2006-10-23 16:00 ` Ted Zlatanov
2006-10-23 20:09   ` Eli Zaretskii
2006-10-24  0:02     ` Ted Zlatanov
2006-10-24  4:35       ` Eli Zaretskii
2006-10-24 15:36         ` Ted Zlatanov
2006-10-24 17:31           ` Eli Zaretskii
2006-10-24 19:24             ` Ted Zlatanov
2006-10-24 22:22               ` Stefan Monnier
2006-10-25 16:33                 ` Ted Zlatanov
2006-10-25 11:46               ` Michael Olson
2006-10-25 12:38                 ` David Kastrup
2006-10-23 21:47   ` Juanma Barranquero
2006-10-23 23:50     ` Ted Zlatanov
2006-10-24  2:01   ` Stefan Monnier
2006-10-24 15:40     ` Ted Zlatanov
2006-10-24 18:17       ` Stefan Monnier
2006-10-24 19:31         ` Ted Zlatanov
2006-10-23 16:26 ` Jan D.
2006-10-23 19:52   ` Stefan Monnier
2006-10-23 20:35     ` Jan Djärv
2006-10-23 21:20       ` Jason Rumney
2006-10-23 21:59         ` Jason Rumney
2006-10-24  5:06         ` Jan Djärv
2006-10-24  8:37           ` Juanma Barranquero
2006-10-24 13:27           ` Jason Rumney
2006-10-23 21:57       ` Juanma Barranquero
2006-10-24  5:08         ` Jan Djärv
2006-10-24  7:32           ` Kim F. Storm
2006-10-23 19:46 ` Stefan Monnier
2006-10-23 21:54   ` Juanma Barranquero
2006-10-24  2:04     ` Stefan Monnier
2006-10-24  8:39       ` Juanma Barranquero
2006-10-27  0:27         ` Juanma Barranquero [this message]
2006-10-27 11:08           ` Juanma Barranquero
2006-10-27 12:21             ` Jason Rumney
2006-10-27 13:25               ` Stefan Monnier
2006-10-27 13:35                 ` Juanma Barranquero
2006-10-27 13:29               ` Juanma Barranquero
2006-10-27 13:50                 ` Slawomir Nowaczyk
2006-10-27 14:20                 ` Stefan Monnier
2006-10-27 15:18                   ` Juanma Barranquero
2006-10-28 18:13                     ` Richard Stallman
2006-10-30  1:02                     ` Stefan Monnier
2006-10-31  0:35                       ` Juanma Barranquero
2006-10-28  7:27             ` Richard Stallman
2006-10-28 21:16               ` Juanma Barranquero
2006-10-28 23:38                 ` Kim F. Storm
2006-10-30 13:33                 ` Richard Stallman
     [not found] ` <f7ccd24b0610271000p16dda672mb146860725d47e00@mail.gmail.com>
     [not found]   ` <45426C6B.10401@student.lu.se>
     [not found]     ` <f7ccd24b0610271400x343c7197jbd6b775f49834924@mail.gmail.com>
     [not found]       ` <454276CB.9000002@student.lu.se>
     [not found]         ` <f7ccd24b0610271419p51bf429fjf5fb548e613cd950@mail.gmail.com>
     [not found]           ` <45427A24.8020107@student.lu.se>
     [not found]             ` <f7ccd24b0610271438r64539e94j6b17c251b213a047@mail.gmail.com>
     [not found]               ` <45427F8E.5040507@student.lu.se>
     [not found]                 ` <f7ccd24b0610271700v472f9c53v43017fce05e761e9@mail.gmail.com>
2006-10-28  0:24                   ` Juanma Barranquero

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=f7ccd24b0610261727t1cf8a448v36eb641348fdad1c@mail.gmail.com \
    --to=lekktu@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).