]> code.delx.au - gnu-emacs/blobdiff - lib-src/emacsclient.c
Merged in changes from CVS trunk.
[gnu-emacs] / lib-src / emacsclient.c
index 0f42f096643f0f8c78913d13c5f6239933757927..888c85e868538d1ba429f50135e05b6edab06ebf 100644 (file)
@@ -48,9 +48,6 @@ Boston, MA 02111-1307, USA.  */
 char *getenv (), *getwd ();
 char *getcwd ();
 
-/* This is defined with -D from the compilation command,
-   which extracts it from ../lisp/version.el.  */
-
 #ifndef VERSION
 #define VERSION "unspecified"
 #endif
@@ -70,11 +67,14 @@ int nowait = 0;
 /* Nonzero means args are expressions to be evaluated.  --eval.  */
 int eval = 0;
 
+/* Nonzero means open a new graphical frame. */
+int window_system = 0;
+
 /* The display on which Emacs should work.  --display.  */
 char *display = NULL;
 
 /* Nonzero means open a new Emacs frame on the current terminal. */
-int frame = 0;
+int tty = 0;
 
 /* If non-NULL, the name of an editor to fallback to if the server
    is not running.  --alternate-editor.   */
@@ -92,6 +92,7 @@ struct option longopts[] =
   { "help",    no_argument,       NULL, 'H' },
   { "version", no_argument,       NULL, 'V' },
   { "tty",     no_argument,       NULL, 't' },
+  { "current-frame", no_argument,  NULL, 'c' },
   { "alternate-editor", required_argument, NULL, 'a' },
   { "socket-name",     required_argument, NULL, 's' },
   { "display", required_argument, NULL, 'd' },
@@ -106,16 +107,24 @@ decode_options (argc, argv)
      int argc;
      char **argv;
 {
+  alternate_editor = getenv ("ALTERNATE_EDITOR");
+  display = getenv ("DISPLAY");
+  if (display && strlen (display) == 0)
+    display = NULL;
+
+  if (display)
+    window_system = 1;
+  else
+    tty = 1;
+
   while (1)
     {
       int opt = getopt_long (argc, argv,
-                            "VHnea:s:d:t", longopts, 0);
+                            "VHnea:s:d:tc", longopts, 0);
 
       if (opt == EOF)
        break;
 
-      alternate_editor = getenv ("ALTERNATE_EDITOR");
-
       switch (opt)
        {
        case 0:
@@ -145,29 +154,34 @@ decode_options (argc, argv)
 
        case 'V':
          printf ("emacsclient %s\n", VERSION);
-         exit (0);
+         exit (EXIT_SUCCESS);
          break;
 
         case 't':
-          frame = 1;
+          tty = 1;
+          window_system = 0;
           break;
-          
+
+        case 'c':
+          window_system = 0;
+          tty = 0;
+          break;
+
        case 'H':
          print_help_and_exit ();
          break;
 
        default:
          fprintf (stderr, "Try `%s --help' for more information\n", progname);
-         exit (1);
+         exit (EXIT_FAILURE);
          break;
        }
     }
 
-  if (frame) {
+  if (tty) {
     nowait = 0;
     display = 0;
   }
-  
 }
 
 void
@@ -182,6 +196,7 @@ The following OPTIONS are accepted:\n\
 -V, --version           Just print a version info and return\n\
 -H, --help              Print this usage information message\n\
 -t, --tty               Open a new Emacs frame on the current terminal\n\
+-c, --current-frame    Do not create a new frame; use the current Emacs frame\n\
 -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\
@@ -191,22 +206,53 @@ The following OPTIONS are accepted:\n\
                         Editor to fallback to if the server is not running\n\
 \n\
 Report bugs to bug-gnu-emacs@gnu.org.\n", progname);
-  exit (0);
+  exit (EXIT_SUCCESS);
+}
+
+/* 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;
 }
 
-/* In NAME, insert a & before each &, each space, each newline, and
+/* Like strdup but get a fatal error if memory is exhausted. */
+
+char *
+xstrdup (const char *s)
+{
+  char *result = strdup (s);
+  if (result == NULL)
+    {
+      perror ("strdup");
+      exit (EXIT_FAILURE);
+    }
+  return result;
+}
+
+/* In STR, 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.  */
+   return value never contains a space.
+
+   Does not change the string.  Outputs the result to STREAM.  */
 
 void
-quote_file_name (name, stream)
-     char *name;
+quote_argument (str, stream)
+     char *str;
      FILE *stream;
 {
-  char *copy = (char *) malloc (strlen (name) * 2 + 1);
+  char *copy = (char *) xmalloc (strlen (str) * 2 + 1);
   char *p, *q;
 
-  p = name;
+  p = str;
   q = copy;
   while (*p)
     {
@@ -224,32 +270,53 @@ quote_file_name (name, stream)
        }
       else
        {
-         if (*p == '&' || (*p == '-' && p == name))
+         if (*p == '&' || (*p == '-' && p == str))
            *q++ = '&';
          *q++ = *p++;
        }
     }
   *q++ = 0;
 
-  fprintf (stream, copy);
+  fprintf (stream, "%s", copy);
 
   free (copy);
 }
 
-/* Like malloc but get fatal error if memory is exhausted.  */
 
-long *
-xmalloc (size)
-     unsigned int size;
+/* The inverse of quote_argument.  Removes quoting in string STR by
+   modifying the string in place.   Returns STR. */
+
+char *
+unquote_argument (str)
+     char *str;
 {
-  long *result = (long *) malloc (size);
-  if (result == NULL)
-  {
-    perror ("malloc");
-    exit (1);
-  }
-  return result;
+  char *p, *q;
+
+  if (! str)
+    return str;
+
+  p = str;
+  q = str;
+  while (*p)
+    {
+      if (*p == '&')
+        {
+          p++;
+          if (*p == '&')
+            *p = '&';
+          else if (*p == '_')
+            *p = ' ';
+          else if (*p == 'n')
+            *p = '\n';
+          else if (*p == '-')
+            *p = '-';
+        }
+      *q++ = *p++;
+    }
+  *q = 0;
+  return str;
 }
+
 \f
 /*
   Try to run a different command, or --if no alternate editor is
@@ -266,21 +333,15 @@ fail (void)
     }
   else
     {
-      exit (1);
+      exit (EXIT_FAILURE);
     }
 }
 
+/* The process id of Emacs. */
 int emacs_pid;
 
-#ifdef nec_ews_svr4
-extern char *_sobuf ;
-#else
-#if defined (USG) || defined (DGUX)
-unsigned char _sobuf[BUFSIZ+8];
-#else
-char _sobuf[BUFSIZ];
-#endif
-#endif
+/* File handles for communicating with Emacs. */
+FILE *out, *in;
 
 /* A signal handler that passes the signal to the Emacs process.
    Useful for SIGWINCH.  */
@@ -297,8 +358,62 @@ pass_signal_to_emacs (int signalnum)
   errno = old_errno;
 }
 
+/* Signal handler for SIGCONT; notify the Emacs process that it can
+   now resume our tty frame.  */
+
+SIGTYPE
+handle_sigcont (int signalnum)
+{
+  int old_errno = errno;
+
+  if (tcgetpgrp (1) == getpgrp ())
+    {
+      /* We are in the foreground. */
+      fprintf (out, "-resume \n");
+      fflush (out);
+      fsync (fileno (out));
+    }
+  else
+    {
+      /* We are in the background; cancel the continue. */
+      kill (getpid (), SIGSTOP);
+    }
+  errno = old_errno;
+}
+
+/* Signal handler for SIGTSTP; notify the Emacs process that we are
+   going to sleep.  Normally the suspend is initiated by Emacs via
+   server-handle-suspend-tty, but if the server gets out of sync with
+   reality, we may get a SIGTSTP on C-z.  Handling this signal and
+   notifying Emacs about it should get things under control again. */
+
+SIGTYPE
+handle_sigtstp (int signalnum)
+{
+  int old_errno = errno;
+  sigset_t set;
+  
+  if (out)
+    {
+      fprintf (out, "-suspend \n");
+      fflush (out);
+      fsync (fileno (out));
+    }
+
+  /* Unblock this signal and call the default handler by temprarily
+     changing the handler and resignalling. */
+  sigprocmask (SIG_BLOCK, NULL, &set);
+  sigdelset (&set, signalnum);
+  signal (signalnum, SIG_DFL);
+  kill (getpid (), signalnum);
+  sigprocmask (SIG_SETMASK, &set, NULL); /* Let's the above signal through. */
+  signal (signalnum, handle_sigtstp);
+
+  errno = old_errno;
+}
+
 /* Set up signal handlers before opening a frame on the current tty.  */
-   
+
 void
 init_signals (void)
 {
@@ -312,6 +427,10 @@ init_signals (void)
   signal (SIGINT, pass_signal_to_emacs);
   signal (SIGQUIT, pass_signal_to_emacs);
 #endif
+
+  signal (SIGCONT, handle_sigcont);
+  signal (SIGTSTP, handle_sigtstp);
+  signal (SIGTTOU, handle_sigtstp);
 }
 
 \f
@@ -370,7 +489,7 @@ strprefix (char *prefix, char *string)
 
   if (!string)
     return 0;
-  
+
   for (i = 0; prefix[i]; i++)
     if (!string[i] || string[i] != prefix[i])
       return 0;
@@ -383,7 +502,6 @@ main (argc, argv)
      char **argv;
 {
   int s, i, needlf = 0;
-  FILE *out, *in;
   struct sockaddr_un server;
   char *cwd, *str;
   char string[BUFSIZ];
@@ -395,11 +513,11 @@ main (argc, argv)
   /* Process options.  */
   decode_options (argc, argv);
 
-  if ((argc - optind < 1) && !eval && !frame)
+  if ((argc - optind < 1) && !eval && !tty && !window_system)
     {
       fprintf (stderr, "%s: file name or argument required\n", progname);
       fprintf (stderr, "Try `%s --help' for more information\n", progname);
-      exit (1);
+      exit (EXIT_FAILURE);
     }
 
   /*
@@ -419,9 +537,9 @@ main (argc, argv)
     int sock_status = 0;
     int default_sock = !socket_name;
     int saved_errno = 0;
-    
+
      char *server_name = "server";
+
      if (socket_name && !index (socket_name, '/') && !index (socket_name, '\\'))
        { /* socket_name is a file name component.  */
        server_name = socket_name;
@@ -477,7 +595,7 @@ main (argc, argv)
                  {
                    fprintf (stderr, "%s: socket-name %s too long",
                             argv[0], socket_name);
-                   exit (1);
+                   exit (EXIT_FAILURE);
                  }
 
                sock_status = socket_status (server.sun_path);
@@ -504,11 +622,11 @@ main (argc, argv)
         /* `stat' failed */
         if (saved_errno == ENOENT)
           fprintf (stderr,
-                   "%s: Can't find socket; have you started the server?\n\
+                   "%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",
+          fprintf (stderr, "%s: can't stat %s: %s\n",
                    argv[0], server.sun_path, strerror (saved_errno));
         fail ();
         break;
@@ -523,7 +641,7 @@ To start the server in Emacs, type \"M-x server-start\".\n",
       fail ();
     }
 
-  /* We use the stream OUT to send our command to the server.  */
+  /* We use the stream OUT to send our commands to the server.  */
   if ((out = fdopen (s, "r+")) == NULL)
     {
       fprintf (stderr, "%s: ", argv[0]);
@@ -531,7 +649,7 @@ To start the server in Emacs, type \"M-x server-start\".\n",
       fail ();
     }
 
-  /* We use the stream IN to read the response.
+  /* We use the stream IN to read the responses.
      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;
@@ -554,119 +672,210 @@ To start the server in Emacs, type \"M-x server-start\".\n",
 
 #ifdef HAVE_GETCWD
       fprintf (stderr, "%s: %s (%s)\n", argv[0],
-              "Cannot get current working directory", strerror (errno));
+              "cannot get current working directory", strerror (errno));
 #else
       fprintf (stderr, "%s: %s (%s)\n", argv[0], string, strerror (errno));
 #endif
       fail ();
     }
 
+  /* First of all, send our version number for verification. */
+  fprintf (out, "-version %s ", VERSION);
+
+  /* Send over our environment. */
+  {
+    extern char **environ;
+    int i;
+    for (i = 0; environ[i]; i++)
+      {
+        char *name = xstrdup (environ[i]);
+        char *value = strchr (name, '=');
+        if (value && strlen (value) > 1)
+          {
+            *value++ = 0;
+            fprintf (out, "-env ");
+            quote_argument (name, out);
+            fprintf (out, " ");
+            quote_argument (value, out);
+            fprintf (out, " ");
+            fflush (out);
+          }
+        free (name);
+      }
+  }
+
   if (nowait)
     fprintf (out, "-nowait ");
 
-  if (eval)
-    fprintf (out, "-eval ");
-
   if (display)
     {
       fprintf (out, "-display ");
-      quote_file_name (display, out);
+      quote_argument (display, out);
       fprintf (out, " ");
     }
 
-  if (frame)
+  if (tty)
     {
       char *tty_name = ttyname (fileno (stdin));
+      char *type = getenv ("TERM");
+
       if (! tty_name)
-        fail ();
-      
+        {
+          fprintf (stderr, "%s: could not get terminal name\n", progname);
+          fail ();
+        }
+
+      if (! type)
+        {
+          fprintf (stderr, "%s: please set the TERM variable to your terminal type\n",
+                   progname);
+          fail ();
+        }
+
+      if (! strcmp (type, "eterm"))
+        {
+          /* This causes nasty, MULTI_KBOARD-related input lockouts. */
+          fprintf (stderr, "%s: opening a frame in an Emacs term buffer"
+                   " is not supported\n", progname);
+          fail ();
+        }
+
       init_signals ();
-      
+
       fprintf (out, "-tty ");
-      quote_file_name (tty_name, out);
+      quote_argument (tty_name, out);
       fprintf (out, " ");
-      quote_file_name (getenv("TERM"), out);
+      quote_argument (type, out);
       fprintf (out, " ");
     }
-  
+
+  if (window_system)
+    fprintf (out, "-window-system ");
+
   if ((argc - optind > 0))
     {
       for (i = optind; i < argc; i++)
        {
+          int relative = 0;
+
          if (eval)
-           ; /* Don't prepend any cwd or anything like that.  */
-         else if (*argv[i] == '+')
-           {
+            {
+              /* Don't prepend any cwd or anything like that.  */
+              fprintf (out, "-eval ");
+              quote_argument (argv[i], out);
+              fprintf (out, " ");
+              continue;
+            }
+
+          if (*argv[i] == '+')
+            {
              char *p = argv[i] + 1;
              while (isdigit ((unsigned char) *p) || *p == ':') p++;
-             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, " ");
-       }
+             if (*p == 0)
+                {
+                  fprintf (out, "-position ");
+                  quote_argument (argv[i], out);
+                  fprintf (out, " ");
+                  continue;
+                }
+              else
+                relative = 1;
+            }
+          else if (*argv[i] != '/')
+            relative = 1;
+
+          fprintf (out, "-file ");
+          if (relative)
+            {
+              quote_argument (cwd, out);
+              fprintf (out, "/");
+            }
+          quote_argument (argv[i], out);
+          fprintf (out, " ");
+        }
     }
   else
     {
-      if (!frame)
+      if (!tty && !window_system)
         {
           while ((str = fgets (string, BUFSIZ, stdin)))
             {
-              quote_file_name (str, out);
+              if (eval)
+                fprintf (out, "-eval ");
+              else
+                fprintf (out, "-file ");
+              quote_argument (str, out);
             }
           fprintf (out, " ");
         }
     }
-  
+
   fprintf (out, "\n");
   fflush (out);
+  fsync (fileno (out));
 
-  /* Maybe wait for an answer.   */
-  if (nowait)
-    {
-      return 0;
-    }
-
-  if (!eval && !frame)
+  /* Wait for an answer. */
+  if (!eval && !tty && !nowait)
     {
       printf ("Waiting for Emacs...");
       needlf = 2;
     }
   fflush (stdout);
+  fsync (1);
 
   /* Now, wait for an answer and print any messages.  */
   while ((str = fgets (string, BUFSIZ, in)))
     {
-      if (frame)
+      char *p = str + strlen (str) - 1;
+      while (p > str && *p == '\n')
+        *p-- = 0;
+
+      if (strprefix ("-good-version ", str))
         {
-          if (strprefix ("emacs-pid ", str))
-            {
-              emacs_pid = strtol (string + strlen ("emacs-pid"), NULL, 10);
-            }
+          /* OK, we got the green light. */
         }
-      else
+      else if (strprefix ("-emacs-pid ", str))
         {
-          if (needlf == 2)
+          emacs_pid = strtol (string + strlen ("-emacs-pid"), NULL, 10);
+        }
+      else if (strprefix ("-print ", str))
+        {
+          str = unquote_argument (str + strlen ("-print "));
+          if (needlf)
             printf ("\n");
           printf ("%s", str);
           needlf = str[0] == '\0' ? needlf : str[strlen (str) - 1] != '\n';
         }
+      else if (strprefix ("-error ", str))
+        {
+          str = unquote_argument (str + strlen ("-error "));
+          if (needlf)
+            printf ("\n");
+          printf ("*ERROR*: %s", str);
+          needlf = str[0] == '\0' ? needlf : str[strlen (str) - 1] != '\n';
+        }
+      else if (strprefix ("-suspend ", str))
+        {
+          if (needlf)
+            printf ("\n");
+          needlf = 0;
+          kill (0, SIGSTOP);
+        }
+      else
+        {
+          if (needlf)
+            printf ("\n");
+          printf ("*ERROR*: Unknown message: %s", str);
+          needlf = str[0] == '\0' ? needlf : str[strlen (str) - 1] != '\n';
+        }
     }
 
   if (needlf)
     printf ("\n");
   fflush (stdout);
+  fsync (1);
 
-  return 0;
+  return EXIT_SUCCESS;
 }
 
 #endif /* HAVE_SOCKETS */
@@ -688,3 +897,5 @@ strerror (errnum)
 
 /* arch-tag: f39bb9c4-73eb-477e-896d-50832e2ca9a7
    (do not change this comment) */
+
+/* emacsclient.c ends here */