]> code.delx.au - gnu-emacs/blobdiff - lib-src/emacsclient.c
(last_node): Make it a global variable.
[gnu-emacs] / lib-src / emacsclient.c
index ae9fb36a9c997692f822dfdac138cbf7a0cce963..27bce2d0b1dabb1e5c288634e99f7d7fe7e25edc 100644 (file)
@@ -1,5 +1,6 @@
 /* Client process that communicates with GNU Emacs acting as server.
-   Copyright (C) 1986, 1987, 1994 Free Software Foundation, Inc.
+   Copyright (C) 1986, 1987, 1994, 1999, 2000, 2001
+   Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -20,19 +21,26 @@ Boston, MA 02111-1307, USA.  */
 
 
 #define NO_SHORTNAMES
-#include <../src/config.h>
-#undef read
-#undef write
-#undef open
-#undef close
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
 #undef signal
 
+#include <ctype.h>
 #include <stdio.h>
 #include <getopt.h>
-#ifdef STDC_HEADERS
-#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
 #endif
 
+#ifdef VMS
+# include "vms-pwd.h"
+#else
+# include <pwd.h>
+#endif /* not VMS */
+
 char *getenv (), *getwd ();
 char *getcwd ();
 
@@ -56,9 +64,13 @@ struct option longopts[] =
   { "no-wait", no_argument,       NULL, 'n' },
   { "help",    no_argument,       NULL, 'H' },
   { "version", no_argument,       NULL, 'V' },
+  { "alternate-editor",required_argument, NULL, 'a' },
   { 0 }
 };
 
+
+const char * alternate_editor = NULL;
+
 /* Decode the options from argv and argc.
    The global variable `optind' will say how many arguments we used up.  */
 
@@ -70,11 +82,13 @@ decode_options (argc, argv)
   while (1)
     {
       int opt = getopt_long (argc, argv,
-                            "VHn", longopts, 0);
+                            "VHna:", longopts, 0);
 
       if (opt == EOF)
        break;
 
+      alternate_editor = getenv ("ALTERNATE_EDITOR");
+
       switch (opt)
        {
        case 0:
@@ -82,12 +96,16 @@ decode_options (argc, argv)
             long-named option.  We should do nothing.  */
          break;
 
+       case 'a':
+         alternate_editor = optarg;
+         break;
+
        case 'n':
          nowait = 1;
          break;
 
        case 'V':
-         fprintf (stderr, "Version %s\n", VERSION);
+         fprintf (stderr, "emacsclient %s\n", VERSION);
          exit (1);
          break;
 
@@ -102,10 +120,13 @@ void
 print_help_and_exit ()
 {
   fprintf (stderr,
-          "Usage: %s [-n] [--no-wait] [+LINENUMBER] FILENAME\n",
+          "Usage: %s [-a ALTERNATE-EDITOR] [-n] [--no-wait] [+LINE[:COLUMN]] FILENAME\n",
+          progname);
+  fprintf (stderr,
+          "Or %s --version\n",
           progname);
   fprintf (stderr,
-          "Report bugs to bug-gnu-emacs@prep.ai.mit.edu.\n");
+          "Report bugs to bug-gnu-emacs@gnu.org.\n");
   exit (1);
 }
 
@@ -143,14 +164,13 @@ quote_file_name (name)
   return copy;
 }
 
-#ifdef C_ALLOCA
 /* Like malloc but get fatal error if memory is exhausted.  */
 
-char *
+long *
 xmalloc (size)
      unsigned int size;
 {
-  char *result = (char *) malloc (size);
+  long *result = (long *) malloc (size);
   if (result == NULL)
   {
     perror ("malloc");
@@ -158,10 +178,33 @@ xmalloc (size)
   }
   return result;
 }
-#endif /* C_ALLOCA */
+\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 (1);
+    }
+}
+
+
 \f
 #if !defined (HAVE_SOCKETS) && !defined (HAVE_SYSVIPC)
 
+int
 main (argc, argv)
      int argc;
      char **argv;
@@ -169,7 +212,8 @@ main (argc, argv)
   fprintf (stderr, "%s: Sorry, the Emacs server is supported only\n",
           argv[0]);
   fprintf (stderr, "on systems with Berkeley sockets or System V IPC.\n");
-  exit (1);
+
+  fail (argc, argv);
 }
 
 #else /* HAVE_SOCKETS or HAVE_SYSVIPC */
@@ -182,23 +226,44 @@ main (argc, argv)
 #include <sys/un.h>
 #include <sys/stat.h>
 #include <errno.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
 
 extern char *strerror ();
 extern int errno;
 
+/* Three possibilities:
+   2 - can't be `stat'ed               (sets errno)
+   1 - isn't owned by us
+   0 - success: none of the above */
+
+static int
+socket_status (socket_name)
+     char *socket_name;
+{
+  struct stat statbfr;
+
+  if (stat (socket_name, &statbfr) == -1)
+    return 2;
+
+  if (statbfr.st_uid != geteuid ())
+    return 1;
+
+  return 0;
+}
+
 int
 main (argc, argv)
      int argc;
      char **argv;
 {
-  char system_name[32];
+  char *system_name;
+  int system_name_length;
   int s, i;
   FILE *out, *in;
   struct sockaddr_un server;
-  char *homedir, *cwd, *str;
+#ifdef SERVER_HOME_DIR
+  char *homedir;
+#endif
+  char *cwd, *str;
   char string[BUFSIZ];
 
   progname = argv[0];
@@ -209,7 +274,7 @@ main (argc, argv)
   if (argc - optind < 1)
     print_help_and_exit ();
 
-  /* 
+  /*
    * Open up an AF_UNIX socket in this person's home directory
    */
 
@@ -217,42 +282,94 @@ main (argc, argv)
     {
       fprintf (stderr, "%s: ", argv[0]);
       perror ("socket");
-      exit (1);
+      fail (argc, argv);
     }
+
   server.sun_family = AF_UNIX;
-#ifndef SERVER_HOME_DIR
-  {
-    struct stat statbfr;
 
-    gethostname (system_name, sizeof (system_name));
-    sprintf (server.sun_path, "/tmp/esrv%d-%s", geteuid (), system_name);
+  {
+    system_name_length = 32;
 
-    if (stat (server.sun_path, &statbfr) == -1)
+    while (1)
       {
-       if (errno == ENOENT)
-         fprintf (stderr,
-                  "%s: can't find socket; have you started the server?\n",
-                  argv[0]);
-       else
-         fprintf (stderr, "%s: can't stat %s: %s\n",
-                  argv[0], server.sun_path, strerror (errno));
-       exit (1);
+       system_name = (char *) xmalloc (system_name_length + 1);
+
+       /* system_name must be null-terminated string.  */
+       system_name[system_name_length] = '\0';
+
+       if (gethostname (system_name, system_name_length) == 0)
+         break;
+
+       free (system_name);
+       system_name_length *= 2;
       }
-    if (statbfr.st_uid != geteuid ())
+  }
+
+#ifndef SERVER_HOME_DIR
+  {
+    int sock_status = 0;
+
+    sprintf (server.sun_path, "/tmp/esrv%d-%s", (int) geteuid (), system_name);
+
+    /* See if the socket exists, and if it's owned by us. */
+    sock_status = socket_status (server.sun_path);
+    if (sock_status)
       {
-       fprintf (stderr, "%s: Invalid socket owner\n", argv[0]);
-       exit (1);
+       /* Failing that, see if LOGNAME or USER exist and differ from
+          our euid.  If so, look for a socket based on the UID
+          associated with the name.  This is reminiscent of the logic
+          that init_editfns uses to set the global Vuser_full_name.  */
+
+       char *user_name = (char *) getenv ("LOGNAME");
+       if (!user_name)
+         user_name = (char *) getenv ("USER");
+
+       if (user_name)
+         {
+           struct passwd *pw = getpwnam (user_name);
+           if (pw && (pw->pw_uid != geteuid ()))
+             {
+               /* We're running under su, apparently. */
+               sprintf (server.sun_path, "/tmp/esrv%d-%s",
+                        (int) pw->pw_uid, system_name);
+               sock_status = socket_status (server.sun_path);
+             }
+         }
       }
+
+     switch (sock_status)
+       {
+       case 1:
+        /* There's a socket, but it isn't owned by us.  This is OK if
+           we are root. */
+        if (0 != geteuid ())
+          {
+            fprintf (stderr, "%s: Invalid socket owner\n", argv[0]);
+            fail (argc, argv);
+          }
+        break;
+
+       case 2:
+        /* `stat' failed */
+        if (errno == ENOENT)
+          fprintf (stderr,
+                   "%s: can't find socket; have you started the server?\n",
+                   argv[0]);
+        else
+          fprintf (stderr, "%s: can't stat %s: %s\n",
+                   argv[0], server.sun_path, strerror (errno));
+        fail (argc, argv);
+        break;
+       }
   }
 #else
   if ((homedir = getenv ("HOME")) == NULL)
     {
       fprintf (stderr, "%s: No home directory\n", argv[0]);
-      exit (1);
+      fail (argc, argv);
     }
   strcpy (server.sun_path, homedir);
   strcat (server.sun_path, "/.emacs-server-");
-  gethostname (system_name, sizeof (system_name));
   strcat (server.sun_path, system_name);
 #endif
 
@@ -261,7 +378,7 @@ main (argc, argv)
     {
       fprintf (stderr, "%s: ", argv[0]);
       perror ("connect");
-      exit (1);
+      fail (argc, argv);
     }
 
   /* We use the stream OUT to send our command to the server.  */
@@ -269,7 +386,7 @@ main (argc, argv)
     {
       fprintf (stderr, "%s: ", argv[0]);
       perror ("fdopen");
-      exit (1);
+      fail (argc, argv);
     }
 
   /* We use the stream IN to read the response.
@@ -281,7 +398,7 @@ main (argc, argv)
     {
       fprintf (stderr, "%s: ", argv[0]);
       perror ("fdopen");
-      exit (1);
+      fail (argc, argv);
     }
 
 #ifdef BSD_SYSTEM
@@ -299,7 +416,7 @@ main (argc, argv)
               "Cannot get current working directory",
 #endif
               strerror (errno));
-      exit (1);
+      fail (argc, argv);
     }
 
   if (nowait)
@@ -310,12 +427,12 @@ main (argc, argv)
       if (*argv[i] == '+')
        {
          char *p = argv[i] + 1;
-         while (*p >= '0' && *p <= '9') p++;
+         while (isdigit ((unsigned char) *p) || *p == ':') p++;
          if (*p != 0)
-           fprintf (out, "%s/", cwd);
+           fprintf (out, "%s/", quote_file_name (cwd));
        }
       else if (*argv[i] != '/')
-       fprintf (out, "%s/", cwd);
+       fprintf (out, "%s/", quote_file_name (cwd));
 
       fprintf (out, "%s ", quote_file_name (argv[i]));
     }
@@ -333,8 +450,8 @@ main (argc, argv)
      the first line we read will actually be the output we just sent.
      We can't predict whether that will happen, so if it does, we
      detect it by recognizing `Client: ' at the beginning.  */
-  
-  while (str = fgets (string, BUFSIZ, in))
+
+  while ((str = fgets (string, BUFSIZ, in)))
     printf ("%s", str);
 
   return 0;
@@ -430,7 +547,7 @@ main (argc, argv)
       fprintf (stderr, "%s: Cannot get current working directory: %s\n",
               argv[0], strerror (errno));
 #endif
-      exit (1);
+      fail (argc, argv);
     }
 
   msgp->mtext[0] = 0;
@@ -453,7 +570,8 @@ main (argc, argv)
       if (*modified_arg == '+')
        {
          char *p = modified_arg + 1;
-         while (*p >= '0' && *p <= '9') p++;
+         while (isdigit (*p) || *p == ':')
+           p++;
          if (*p != 0)
            need_cwd = 1;
        }
@@ -463,7 +581,8 @@ main (argc, argv)
       modified_arg = quote_file_name (modified_arg);
 
       if (need_cwd)
-       used += strlen (cwd);
+       /* Overestimate in case we have to quote something in CWD.  */
+       used += 2 * strlen (cwd);
       used += strlen (modified_arg) + 1;
       while (used + 2 > size_allocated)
        {
@@ -474,7 +593,7 @@ main (argc, argv)
        }
 
       if (need_cwd)
-       strcat (msgp->mtext, cwd);
+       strcat (msgp->mtext, quote_file_name (cwd));
 
       strcat (msgp->mtext, modified_arg);
       strcat (msgp->mtext, " ");
@@ -485,7 +604,7 @@ main (argc, argv)
   if (strlen (msgp->mtext) >= 512)
     {
       fprintf (stderr, "%s: args too long for msgsnd\n", progname);
-      exit (1);
+      fail (argc, argv);
     }
 #endif
   msgp->mtype = 1;
@@ -493,7 +612,7 @@ main (argc, argv)
     {
       fprintf (stderr, "%s: ", progname);
       perror ("msgsnd");
-      exit (1);
+      fail (argc, argv);
     }
 
   /* Maybe wait for an answer.   */