From http://www.jwz.org/xscreensaver/xscreensaver-5.35.tar.gz
[xscreensaver] / utils / textclient.c
index ed2997e0b0a0a63dc8510c97fdf56cd012e1324f..f0d7fe898d65f82877ad862db9aeba4b7c4c0d2d 100644 (file)
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright (c) 2012 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 2012-2016 Jamie Zawinski <jwz@jwz.org>
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
  * usePty: bool                Whether to run the command interactively.
  * metaSendsESC: bool  Whether to send Alt-x as ESC x in pty-mode.
  * swapBSDEL: bool     Swap Backspace and Delete in pty-mode.
+ *
+ * On iOS and Android, textclient-mobile.c is used instead.
  */
 
 #include "utils.h"
 
-#ifndef USE_IPHONE /* whole file -- see OSX/iostextclient.m */
+#if !defined(USE_IPHONE) && !defined(HAVE_ANDROID)  /* whole file */
 
 #include "textclient.h"
 #include "resources.h"
 # ifdef HAVE_UTIL_H
 #  include <util.h>
 # endif
+# ifdef HAVE_SYS_TERMIOS_H
+#  include <sys/termios.h>
+# endif
 #endif /* HAVE_FORKPTY */
 
-/*#define DEBUG*/
+#undef DEBUG
 
 extern const char *progname;
 
@@ -60,6 +65,7 @@ struct text_data {
   Display *dpy;
   char *program;
   int pix_w, pix_h, char_w, char_h;
+  int max_lines;
 
   Bool pty_p;
   XtIntervalId pipe_timer;
@@ -92,16 +98,40 @@ subproc_cb (XtPointer closure, int *source, XtInputId *id)
 }
 
 
+# define BACKSLASH(c) \
+  (! ((c >= 'a' && c <= 'z') || \
+      (c >= 'A' && c <= 'Z') || \
+      (c >= '0' && c <= '9') || \
+      c == '.' || c == '_' || c == '-' || c == '+' || c == '/'))
+
 static void
 launch_text_generator (text_data *d)
 {
   XtAppContext app = XtDisplayToApplicationContext (d->dpy);
   char buf[255];
   const char *oprogram = d->program;
-  char *program = (char *) malloc (strlen (oprogram) + 50);
+  char *s;
+
+# ifdef HAVE_COCOA
+  /* /bin/sh on OS X 10.10 wipes out the PATH. */
+  const char *path = getenv("PATH");
+  char *cmd = s = malloc ((strlen(oprogram) + strlen(path)) * 2 + 100);
+  strcpy (s, "export PATH=");
+  s += strlen (s);
+  while (*path) {
+    char c = *path++;
+    if (BACKSLASH(c)) *s++ = '\\';
+    *s++ = c;
+  }
+  strcpy (s, "; ");
+  s += strlen (s);
+# else
+  char *cmd = s = malloc ((strlen(oprogram)) * 2 + 100);
+# endif
 
-  strcpy (program, "( ");
-  strcat (program, oprogram);
+  strcpy (s, "( ");
+  strcat (s, oprogram);
+  s += strlen (s);
 
   /* Kludge!  Special-case "xscreensaver-text" to tell it how wide
      the screen is.  We used to do this by just always feeding
@@ -110,12 +140,18 @@ launch_text_generator (text_data *d)
      if someone ever uses a --program that includes a % anywhere.
    */
   if (!strcmp (oprogram, "xscreensaver-text"))
-    sprintf (program + strlen(program), " --cols %d", d->char_w);
+    {
+      if (d->char_w)
+        sprintf (s, " --cols %d", d->char_w);
+      if (d->max_lines)
+        sprintf (s, " --lines %d", d->max_lines);
+      s += strlen(s);
+    }
 
-  strcat (program, " ) 2>&1");
+  strcpy (s, " ) 2>&1");
 
 # ifdef DEBUG
-  fprintf (stderr, "%s: textclient: launch %s: %s\n", progname
+  fprintf (stderr, "%s: textclient: launch %s: %s\n", cmd
            (d->pty_p ? "pty" : "pipe"), program);
 # endif
 
@@ -130,7 +166,7 @@ launch_text_generator (text_data *d)
       ws.ws_xpixel = d->pix_w;
       ws.ws_ypixel = d->pix_h;
       
-      d->pipe = NULL;
+      d->pipe = 0;
       if ((d->pid = forkpty(&fd, NULL, NULL, &ws)) < 0)
        {
           /* Unable to fork */
@@ -146,7 +182,7 @@ launch_text_generator (text_data *d)
             abort();
           av[i++] = "/bin/sh";
           av[i++] = "-c";
-          av[i++] = program;
+          av[i++] = cmd;
           av[i] = 0;
           execvp (av[0], av);
           sprintf (buf, "%.100s: %.100s", progname, oprogram);
@@ -156,7 +192,9 @@ launch_text_generator (text_data *d)
       else
        {
           /* This is the parent fork. */
+          if (d->pipe) abort();
          d->pipe = fdopen (fd, "r+");
+          if (d->pipe_id) abort();
          d->pipe_id =
            XtAppAddInput (app, fileno (d->pipe),
                           (XtPointer) (XtInputReadMask | XtInputExceptMask),
@@ -177,8 +215,10 @@ launch_text_generator (text_data *d)
         protected_stdin_p = 1;
       }
 
-      if ((d->pipe = popen (program, "r")))
+      if (d->pipe) abort();
+      if ((d->pipe = popen (cmd, "r")))
        {
+          if (d->pipe_id) abort();
          d->pipe_id =
            XtAppAddInput (app, fileno (d->pipe),
                           (XtPointer) (XtInputReadMask | XtInputExceptMask),
@@ -189,12 +229,12 @@ launch_text_generator (text_data *d)
        }
       else
        {
-          sprintf (buf, "%.100s: %.100s", progname, program);
+          sprintf (buf, "%.100s: %.100s", progname, cmd);
          perror (buf);
        }
     }
 
-  free (program);
+  free (cmd);
 }
 
 
@@ -211,10 +251,58 @@ relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
 }
 
 
+static void
+start_timer (text_data *d)
+{
+  XtAppContext app = XtDisplayToApplicationContext (d->dpy);
+
+# ifdef DEBUG
+  fprintf (stderr, "%s: textclient: relaunching in %d\n", progname, 
+           (int) d->subproc_relaunch_delay);
+# endif
+  if (d->pipe_timer)
+    XtRemoveTimeOut (d->pipe_timer);
+  d->pipe_timer =
+    XtAppAddTimeOut (app, d->subproc_relaunch_delay,
+                     relaunch_generator_timer,
+                     (XtPointer) d);
+}
+
+
+static void
+close_pipe (text_data *d)
+{
+  if (d->pid)
+    {
+# ifdef DEBUG
+      fprintf (stderr, "%s: textclient: kill %d\n", progname, d->pid);
+# endif
+      kill (d->pid, SIGTERM);
+    }
+  d->pid = 0;
+
+  if (d->pipe_id)
+    XtRemoveInput (d->pipe_id);
+  d->pipe_id = 0;
+
+  if (d->pipe)
+    {
+# ifdef DEBUG
+      fprintf (stderr, "%s: textclient: pclose\n", progname);
+# endif
+      pclose (d->pipe);
+    }
+  d->pipe = 0;
+
+
+}
+
+
 void
 textclient_reshape (text_data *d,
                     int pix_w, int pix_h,
-                    int char_w, int char_h)
+                    int char_w, int char_h,
+                    int max_lines)
 {
 # if defined(HAVE_FORKPTY) && defined(TIOCSWINSZ)
 
@@ -222,6 +310,7 @@ textclient_reshape (text_data *d,
   d->pix_h  = pix_h;
   d->char_w = char_w;
   d->char_h = char_h;
+  d->max_lines = max_lines;
 
 # ifdef DEBUG
   fprintf (stderr, "%s: textclient: reshape: %dx%d, %dx%d\n", progname,
@@ -248,14 +337,9 @@ textclient_reshape (text_data *d,
    */
   if (!strcmp (d->program, "xscreensaver-text"))
     {
-      if (d->pid)
-        kill (d->pid, SIGTERM);
-      if (d->pipe_id)
-        XtRemoveInput (d->pipe_id);
-      if (d->pipe)
-        pclose (d->pipe);
+      close_pipe (d);
       d->input_available_p = False;
-      relaunch_generator_timer (d, 0);
+      start_timer (d);
     }
 }
 
@@ -283,7 +367,11 @@ textclient_open (Display *dpy)
     }
 
   d->subproc_relaunch_delay =
-    (1000 * get_integer_resource (dpy, "relaunchDelay", "Time"));
+    get_integer_resource (dpy, "relaunchDelay", "Time");
+  if (d->subproc_relaunch_delay < 1)
+    d->subproc_relaunch_delay = 1;
+  d->subproc_relaunch_delay *= 1000;
+
 
   d->meta_sends_esc_p = get_boolean_resource (dpy, "metaSendsESC", "Boolean");
   d->swap_bs_del_p    = get_boolean_resource (dpy, "swapBSDEL",    "Boolean");
@@ -303,7 +391,7 @@ textclient_open (Display *dpy)
   }
 # endif
 
-  launch_text_generator (d);
+  start_timer (d);
 
   return d;
 }
@@ -316,14 +404,12 @@ textclient_close (text_data *d)
   fprintf (stderr, "%s: textclient: free\n", progname);
 # endif
 
+  close_pipe (d);
   if (d->program)
     free (d->program);
-  if (d->pipe_id)
-    XtRemoveInput (d->pipe_id);
-  if (d->pipe)
-    pclose (d->pipe);
   if (d->pipe_timer)
     XtRemoveTimeOut (d->pipe_timer);
+  d->pipe_timer = 0;
   memset (d, 0, sizeof (*d));
   free (d);
 }
@@ -350,10 +436,6 @@ textclient_getc (text_data *d)
         ret = s[0];
       else             /* EOF */
         {
-          if (d->pipe_id)
-            XtRemoveInput (d->pipe_id);
-          d->pipe_id = 0;
-
          if (d->pid)
            {
 # ifdef DEBUG
@@ -361,17 +443,10 @@ textclient_getc (text_data *d)
                        progname, d->pid);
 # endif
              waitpid (d->pid, NULL, 0);
-             fclose (d->pipe);
               d->pid = 0;
            }
-         else
-           {
-# ifdef DEBUG
-              fprintf (stderr, "%s: textclient: pclose\n", progname);
-# endif
-             pclose (d->pipe);
-           }
-          d->pipe = 0;
+
+          close_pipe (d);
 
           if (d->out_column > 0)
             {
@@ -382,14 +457,7 @@ textclient_getc (text_data *d)
               d->out_buffer = "\r\n\r\n";
             }
 
-# ifdef DEBUG
-          fprintf (stderr, "%s: textclient: relaunching in %d\n", progname, 
-                   (int) d->subproc_relaunch_delay);
-# endif
-          d->pipe_timer =
-            XtAppAddTimeOut (app, d->subproc_relaunch_delay,
-                             relaunch_generator_timer,
-                             (XtPointer) d);
+          start_timer (d);
         }
       d->input_available_p = False;
     }