]> code.delx.au - gnu-emacs/blobdiff - lib-src/emacsclient.c
(last_node): Make it a global variable.
[gnu-emacs] / lib-src / emacsclient.c
index a08eab6ec0ba6ca90f8ea2ceb3c914ad1d06485e..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,22 +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>
-#endif
 #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 ();
 
@@ -59,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.  */
 
@@ -73,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:
@@ -85,6 +96,10 @@ decode_options (argc, argv)
             long-named option.  We should do nothing.  */
          break;
 
+       case 'a':
+         alternate_editor = optarg;
+         break;
+
        case 'n':
          nowait = 1;
          break;
@@ -105,7 +120,7 @@ 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",
@@ -163,9 +178,33 @@ xmalloc (size)
   }
   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 (1);
+    }
+}
+
+
 \f
 #if !defined (HAVE_SOCKETS) && !defined (HAVE_SYSVIPC)
 
+int
 main (argc, argv)
      int argc;
      char **argv;
@@ -173,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 */
@@ -190,6 +230,26 @@ main (argc, argv)
 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;
@@ -200,7 +260,10 @@ main (argc, argv)
   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];
@@ -211,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
    */
 
@@ -219,8 +282,9 @@ main (argc, argv)
     {
       fprintf (stderr, "%s: ", argv[0]);
       perror ("socket");
-      exit (1);
+      fail (argc, argv);
     }
+
   server.sun_family = AF_UNIX;
 
   {
@@ -243,32 +307,66 @@ main (argc, argv)
 
 #ifndef SERVER_HOME_DIR
   {
-    struct stat statbfr;
+    int sock_status = 0;
 
-    sprintf (server.sun_path, "/tmp/esrv%d-%s", geteuid (), system_name);
+    sprintf (server.sun_path, "/tmp/esrv%d-%s", (int) geteuid (), system_name);
 
-    if (stat (server.sun_path, &statbfr) == -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);
-      }
-    if (statbfr.st_uid != geteuid ())
+    /* 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-");
@@ -280,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.  */
@@ -288,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.
@@ -300,7 +398,7 @@ main (argc, argv)
     {
       fprintf (stderr, "%s: ", argv[0]);
       perror ("fdopen");
-      exit (1);
+      fail (argc, argv);
     }
 
 #ifdef BSD_SYSTEM
@@ -318,7 +416,7 @@ main (argc, argv)
               "Cannot get current working directory",
 #endif
               strerror (errno));
-      exit (1);
+      fail (argc, argv);
     }
 
   if (nowait)
@@ -329,7 +427,7 @@ 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/", quote_file_name (cwd));
        }
@@ -352,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;
@@ -449,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;
@@ -472,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;
        }
@@ -505,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;
@@ -513,7 +612,7 @@ main (argc, argv)
     {
       fprintf (stderr, "%s: ", progname);
       perror ("msgsnd");
-      exit (1);
+      fail (argc, argv);
     }
 
   /* Maybe wait for an answer.   */