http://ftp.x.org/contrib/applications/xscreensaver-3.10.tar.gz
[xscreensaver] / driver / subprocs.c
index 166ae5996f573b5e7a4a24d04497268362b83676..8b6187d9a1b6732cfb17ee13c895e3c570df625c 100644 (file)
 #include <X11/Xlib.h>          /* not used for much... */
 
 #ifndef ESRCH
-#include <errno.h>
+# include <errno.h>
 #endif
 
 #include <sys/time.h>          /* sys/resource.h needs this for timeval */
 
-#ifndef VMS
-
-# include <sys/resource.h>     /* for setpriority() and PRIO_PROCESS */
+#ifdef HAVE_SYS_WAIT_H
 # include <sys/wait.h>         /* for waitpid() and associated macros */
+#endif
 
-#else  /* VMS */
-
-# if __DECC_VER >= 50200000
-#  include <sys/wait.h>
-# endif
+#if defined(HAVE_SETPRIORITY) && defined(PRIO_PROCESS)
+# include <sys/resource.h>     /* for setpriority() and PRIO_PROCESS */
+#endif
 
+#ifdef VMS
 # include <processes.h>
 # include <unixio.h>           /* for close */
 # include <unixlib.h>          /* for getpid */
-# define pid_t    int
-# define fork     vfork
-
+# define pid_t int
+# define fork  vfork
 #endif /* VMS */
 
 #include <signal.h>            /* for the signal names */
 
 #if !defined(SIGCHLD) && defined(SIGCLD)
-#define SIGCHLD SIGCLD
+# define SIGCHLD SIGCLD
 #endif
 
+#if 0 /* putenv() is declared in stdlib.h on modern linux systems. */
 #ifdef HAVE_PUTENV
 extern int putenv (/* const char * */);        /* getenv() is in stdlib.h... */
 #endif
+#endif
+
 extern int kill (pid_t, int);          /* signal() is in sys/signal.h... */
 
 /* This file doesn't need the Xt headers, so stub these types out... */
@@ -71,9 +71,6 @@ extern int kill (pid_t, int);         /* signal() is in sys/signal.h... */
 
 extern saver_info *global_si_kludge;   /* I hate C so much... */
 
-static void hack_subproc_environment (saver_screen_info *ssi);
-
-
 static void
 nice_subproc (int nice_level)
 {
@@ -248,7 +245,8 @@ exec_screenhack (saver_info *si, const char *command)
   saver_preferences *p = &si->prefs;
 
 #ifndef VMS
-  Bool hairy_p = !!strpbrk (command, "*?$&!<>[];`'\\\"");
+  Bool hairy_p = !!strpbrk (command, "*?$&!<>[];`'\\\"=");
+  /* note: = is in the above because of the sh syntax "FOO=bar cmd". */
 
   if (p->verbose_p)
     fprintf (stderr, "%s: spawning \"%s\" in pid %lu%s.\n",
@@ -702,6 +700,14 @@ init_sigchld (void)
 
 \f
 
+static Bool
+hack_enabled_p (const char *hack)
+{
+  const char *s = hack;
+  while (isspace(*s)) s++;
+  return (*s != '-');
+}
+
 static Bool
 select_visual_of_hack (saver_screen_info *ssi, const char *hack)
 {
@@ -712,6 +718,9 @@ select_visual_of_hack (saver_screen_info *ssi, const char *hack)
   const char *in = hack;
   char *out = vis;
   while (isspace(*in)) in++;           /* skip whitespace */
+  if (*in == '-') in++;                        /* skip optional "-" */
+  while (isspace(*in)) in++;           /* skip whitespace */
+
   while (!isspace(*in) && *in != ':')
     *out++ = *in++;                    /* snarf first token */
   while (isspace(*in)) in++;           /* skip whitespace */
@@ -722,15 +731,15 @@ select_visual_of_hack (saver_screen_info *ssi, const char *hack)
   else
     selected = select_visual(ssi, 0);
 
-  if (!selected && (p->verbose_p || si->demo_mode_p))
+  if (!selected && (p->verbose_p || si->demoing_p))
     {
       if (*in == ':') in++;
       while (isspace(*in)) in++;
       fprintf (stderr,
-              (si->demo_mode_p
+              (si->demoing_p
                ? "%s: warning, no \"%s\" visual for \"%s\".\n"
                : "%s: no \"%s\" visual; skipping \"%s\".\n"),
-              blurb(), (vis ? vis : "???"), in);
+              blurb(), (*vis ? vis : "???"), in);
     }
 
   return selected;
@@ -745,59 +754,81 @@ spawn_screenhack_1 (saver_screen_info *ssi, Bool first_time_p)
   raise_window (si, first_time_p, True, False);
   XFlush (si->dpy);
 
-  if (p->screenhacks_count || si->demo_mode_p)
+  if (p->screenhacks_count)
     {
       char *hack;
       pid_t forked;
       char buf [255];
       int new_hack;
+      int retry_count = 0;
+      Bool force = False;
 
-      if (si->demo_mode_p)
-       {
-         hack = si->demo_hack;
+    AGAIN:
+
+      if (p->screenhacks_count == 1)
+       /* If there is only one hack in the list, there is no choice. */
+       new_hack = 0;
+
+      else if (si->selection_mode == -1)
+       /* Select the next hack, wrapping. */
+       new_hack = (ssi->current_hack + 1) % p->screenhacks_count;
 
-         /* Ignore visual-selection failure if in demo mode. */
-         (void) select_visual_of_hack (ssi, hack);
+      else if (si->selection_mode == -2)
+       /* Select the previous hack, wrapping. */
+       new_hack = ((ssi->current_hack + p->screenhacks_count - 1)
+                   % p->screenhacks_count);
+
+      else if (si->selection_mode > 0)
+       /* Select a specific hack, by number.  No negotiation. */
+       {
+         new_hack = ((si->selection_mode - 1) % p->screenhacks_count);
+         force = True;
        }
       else
        {
-         int retry_count = 0;
-
-       AGAIN:
-         if (p->screenhacks_count == 1)
-           new_hack = 0;
-         else if (si->next_mode_p == 1)
-           new_hack = (ssi->current_hack + 1) % p->screenhacks_count;
-         else if (si->next_mode_p == 2)
-           new_hack = ((ssi->current_hack + p->screenhacks_count - 1)
-                       % p->screenhacks_count);
-         else
-           while ((new_hack = random () % p->screenhacks_count)
-                  == ssi->current_hack)
-             ;
-         ssi->current_hack = new_hack;
-         hack = p->screenhacks[ssi->current_hack];
+         /* Select a random hack (but not the one we just ran.) */
+         while ((new_hack = random () % p->screenhacks_count)
+                == ssi->current_hack)
+           ;
+       }
 
-         if (!select_visual_of_hack (ssi, hack))
+      ssi->current_hack = new_hack;
+      hack = p->screenhacks[ssi->current_hack];
+
+      /* If the hack is disabled, or there is no visual for this hack,
+        then try again (move forward, or backward, or re-randomize.)
+        Unless this hack was specified explicitly, in which case,
+        use it regardless.
+       */
+      if (force)
+        select_visual_of_hack (ssi, hack);
+        
+      if (!force &&
+         (!hack_enabled_p (hack) ||
+          !select_visual_of_hack (ssi, hack)))
+       {
+         if (++retry_count > (p->screenhacks_count*4))
            {
-             if (++retry_count > (p->screenhacks_count*4))
-               {
-                 /* Uh, oops.  Odds are, there are no suitable visuals,
-                    and we're looping.  Give up.  (This is totally lame,
-                    what we should do is make a list of suitable hacks at
-                    the beginning, then only loop over them.)
-                 */
-                 if (p->verbose_p)
-                   fprintf(stderr,
-                           "%s: no suitable visuals for these programs.\n",
-                           blurb());
-                 return;
-               }
-             else
-               goto AGAIN;
+             /* Uh, oops.  Odds are, there are no suitable visuals,
+                and we're looping.  Give up.  (This is totally lame,
+                what we should do is make a list of suitable hacks at
+                the beginning, then only loop over them.)
+             */
+             if (p->verbose_p)
+               fprintf(stderr,
+                       "%s: no suitable visuals for these programs.\n",
+                       blurb());
+             return;
            }
+         else
+           goto AGAIN;
        }
-      si->next_mode_p = 0;
+
+      /* Turn off "next" and "prev" modes now, but "demo" mode is only
+        turned off by explicit action.
+       */
+      if (si->selection_mode < 0)
+       si->selection_mode = 0;
 
 
       /* If there's a visual description on the front of the command, nuke it.
@@ -805,6 +836,8 @@ spawn_screenhack_1 (saver_screen_info *ssi, Bool first_time_p)
       {
        char *in = hack;
        while (isspace(*in)) in++;                      /* skip whitespace */
+       if (*in == '-') in++;                           /* skip optional "-" */
+       while (isspace(*in)) in++;                      /* skip whitespace */
        hack = in;
        while (!isspace(*in) && *in != ':') in++;       /* snarf first token */
        while (isspace(*in)) in++;                      /* skip whitespace */
@@ -927,53 +960,34 @@ screenhack_running_p (saver_info *si)
 }
 
 \f
-/* Restarting the xscreensaver process from scratch. */
+/* Environment variables. */
 
-static char **saved_argv;
 
+/* Modifies $PATH in the current environment, so that if DEFAULT_PATH_PREFIX
+   is defined, the xscreensaver daemon will search that directory for hacks.
+ */
 void
-save_argv (int argc, char **argv)
+hack_environment (saver_info *si)
 {
-  saved_argv = (char **) malloc ((argc + 2) * sizeof (char *));
-  saved_argv [argc] = 0;
-  while (argc--)
+#if defined(HAVE_PUTENV) && defined(DEFAULT_PATH_PREFIX)
+  static const char *def_path = DEFAULT_PATH_PREFIX;
+  if (def_path && *def_path)
     {
-      int i = strlen (argv [argc]) + 1;
-      saved_argv [argc] = (char *) malloc (i);
-      memcpy (saved_argv [argc], argv [argc], i);
+      const char *opath = getenv("PATH");
+      char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
+      strcpy (npath, "PATH=");
+      strcat (npath, def_path);
+      strcat (npath, ":");
+      strcat (npath, opath);
+
+      if (putenv (npath))
+       abort ();
     }
+#endif /* HAVE_PUTENV && DEFAULT_PATH_PREFIX */
 }
 
-void
-restart_process (saver_info *si)
-{
-  fflush (real_stdout);
-  fflush (real_stderr);
-  execvp (saved_argv [0], saved_argv); /* shouldn't return */
-  {
-    char buf [512];
-    sprintf (buf, "%s: could not restart process", blurb());
-    perror(buf);
-    fflush(stderr);
-  }
-}
 
-/* Like restart_process(), but ensures that when it restarts,
-   it comes up in demo-mode. */
 void
-demo_mode_restart_process (saver_info *si)
-{
-  int i;
-  for (i = 0; saved_argv [i]; i++);
-  /* add the -initial-demo-mode switch; save_argv() left room for this. */
-  saved_argv [i] = "-initial-demo-mode";
-  saved_argv [i+1] = 0;
-  restart_process (si);                /* shouldn't return */
-  saved_argv [i] = 0;
-  XBell(si->dpy, 0);
-}
-
-static void
 hack_subproc_environment (saver_screen_info *ssi)
 {
   /* Store $DISPLAY into the environment, so that the $DISPLAY variable that
@@ -994,7 +1008,6 @@ hack_subproc_environment (saver_screen_info *ssi)
   for (screen_number = 0; screen_number < si->nscreens; screen_number++)
     if (ssi == &si->screens[screen_number])
       break;
-  if (screen_number >= si->nscreens) abort();
 
   strcpy (ndpy, "DISPLAY=");
   s = ndpy + strlen(ndpy);
@@ -1015,23 +1028,50 @@ hack_subproc_environment (saver_screen_info *ssi)
 #endif /* HAVE_PUTENV */
 }
 
+\f
+/* Restarting the xscreensaver process from scratch. */
+
+static char **saved_argv;
 
 void
-hack_environment (saver_info *si)
+save_argv (int argc, char **argv)
 {
-#if defined(HAVE_PUTENV) && defined(DEFAULT_PATH_PREFIX)
-  static const char *def_path = DEFAULT_PATH_PREFIX;
-  if (def_path && *def_path)
+  saved_argv = (char **) calloc (argc+2, sizeof (char *));
+  saved_argv [argc] = 0;
+  while (argc--)
     {
-      const char *opath = getenv("PATH");
-      char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
-      strcpy (npath, "PATH=");
-      strcat (npath, def_path);
-      strcat (npath, ":");
-      strcat (npath, opath);
+      int i = strlen (argv [argc]) + 1;
+      saved_argv [argc] = (char *) malloc (i);
+      memcpy (saved_argv [argc], argv [argc], i);
+    }
+}
 
-      if (putenv (npath))
-       abort ();
+
+/* Re-execs the process with the arguments in saved_argv.
+   Does not return unless there was an error.
+ */
+void
+restart_process (saver_info *si)
+{
+  if (si->prefs.verbose_p)
+    {
+      int i;
+      fprintf (real_stderr, "%s: re-executing", blurb());
+      for (i = 0; saved_argv[i]; i++)
+       fprintf (real_stderr, " %s", saved_argv[i]);
+      fprintf (real_stderr, "\n");
     }
-#endif /* HAVE_PUTENV && DEFAULT_PATH_PREFIX */
+  describe_uids (si, real_stderr);
+  fprintf (real_stderr, "\n");
+
+  fflush (real_stdout);
+  fflush (real_stderr);
+  execvp (saved_argv [0], saved_argv); /* shouldn't return */
+  {
+    char buf [512];
+    sprintf (buf, "%s: could not restart process", blurb());
+    perror(buf);
+    fflush(stderr);
+  }
+  XBell(si->dpy, 0);
 }