http://ftp.x.org/contrib/applications/xscreensaver-3.10.tar.gz
[xscreensaver] / driver / prefs.c
1 /* dotfile.c --- management of the ~/.xscreensaver file.
2  * xscreensaver, Copyright (c) 1998 Jamie Zawinski <jwz@jwz.org>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16
17 #include <stdlib.h>
18
19 #ifdef HAVE_UNISTD_H
20 # include <unistd.h>
21 #endif
22
23 #include <stdio.h>
24 #include <ctype.h>
25 #include <string.h>
26 #include <sys/stat.h>
27 #include <sys/time.h>
28
29 #include <X11/Xlib.h>
30 #include <X11/Xresource.h>
31
32 #ifndef VMS
33 # include <pwd.h>
34 #else /* VMS */
35 # include "vms-pwd.h"
36 #endif /* VMS */
37
38
39 /* This file doesn't need the Xt headers, so stub these types out... */
40 #undef XtPointer
41 #define XtAppContext void*
42 #define XtIntervalId void*
43 #define XtPointer    void*
44 #define Widget       void*
45
46
47 /* Just in case there's something pathological about stat.h... */
48 #ifndef  S_IRUSR
49 # define S_IRUSR 00400
50 #endif
51 #ifndef  S_IWUSR
52 # define S_IWUSR 00200
53 #endif
54 #ifndef  S_IXUSR
55 # define S_IXUSR 00100
56 #endif
57 #ifndef  S_IXGRP
58 # define S_IXGRP 00010
59 #endif
60 #ifndef  S_IXOTH
61 # define S_IXOTH 00001
62 #endif
63
64
65 #include "prefs.h"
66 #include "resources.h"
67
68
69 extern char *progname;
70 extern char *progclass;
71 extern const char *blurb (void);
72
73
74
75 static void get_screenhacks (saver_preferences *p);
76
77
78 const char *
79 init_file_name (void)
80 {
81   static char *file = 0;
82
83   if (!file)
84     {
85       struct passwd *p = getpwuid (getuid ());
86
87       if (!p || !p->pw_name || !*p->pw_name)
88         {
89           fprintf (stderr, "%s: couldn't get user info of uid %d\n",
90                    blurb(), getuid ());
91           file = "";
92         }
93       else if (!p->pw_dir || !*p->pw_dir)
94         {
95           fprintf (stderr, "%s: couldn't get home directory of \"%s\"\n",
96                    blurb(), (p->pw_name ? p->pw_name : "???"));
97           file = "";
98         }
99       else
100         {
101           const char *home = p->pw_dir;
102           const char *name = ".xscreensaver";
103           file = (char *) malloc(strlen(home) + strlen(name) + 2);
104           strcpy(file, home);
105           if (!*home || home[strlen(home)-1] != '/')
106             strcat(file, "/");
107           strcat(file, name);
108         }
109     }
110
111   if (file && *file)
112     return file;
113   else
114     return 0;
115 }
116
117
118 static const char *
119 init_file_tmp_name (void)
120 {
121   static char *file = 0;
122   if (!file)
123     {
124       const char *name = init_file_name();
125       const char *suffix = ".tmp";
126       if (!name || !*name)
127         file = "";
128       else
129         {
130           file = (char *) malloc(strlen(name) + strlen(suffix) + 2);
131           strcpy(file, name);
132           strcat(file, suffix);
133         }
134     }
135
136   if (file && *file)
137     return file;
138   else
139     return 0;
140 }
141
142
143 static const char * const prefs[] = {
144   "timeout",
145   "cycle",
146   "lock",
147   "lockVTs",
148   "lockTimeout",
149   "passwdTimeout",
150   "visualID",
151   "installColormap",
152   "verbose",
153   "timestamp",
154   "splash",                     /* not saved -- same as "splashDuration: 0" */
155   "splashDuration",
156   "demoCommand",
157   "prefsCommand",
158   "helpURL",
159   "loadURL",
160   "nice",
161   "fade",
162   "unfade",
163   "fadeSeconds",
164   "fadeTicks",
165   "captureStderr",
166   "captureStdout",              /* not saved -- obsolete */
167   "font",
168   "",
169   "programs",
170   "",
171   "pointerPollTime",
172   "windowCreationTimeout",
173   "initialDelay",
174   "sgiSaverExtension",
175   "mitSaverExtension",
176   "xidleExtension",
177   "procInterrupts",
178   "overlayStderr",
179   "overlayTextBackground",      /* not saved -- X resources only */
180   "overlayTextForeground",      /* not saved -- X resources only */
181   "bourneShell",                /* not saved -- X resources only */
182   0
183 };
184
185 static char *
186 strip (char *s)
187 {
188   char *s2;
189   while (*s == '\t' || *s == ' ' || *s == '\r' || *s == '\n')
190     s++;
191   for (s2 = s; *s2; s2++)
192     ;
193   for (s2--; s2 >= s; s2--) 
194     if (*s2 == '\t' || *s2 == ' ' || *s2 == '\r' || *s2 =='\n') 
195       *s2 = 0;
196     else
197       break;
198   return s;
199 }
200
201 \f
202 /* Reading
203  */
204
205 static int
206 handle_entry (XrmDatabase *db, const char *key, const char *value,
207               const char *filename, int line)
208 {
209   int i;
210   for (i = 0; prefs[i]; i++)
211     if (*prefs[i] && !strcasecmp(key, prefs[i]))
212       {
213         char *val = strdup(value);
214         char *spec = (char *) malloc(strlen(progclass) + strlen(prefs[i]) +10);
215         strcpy(spec, progclass);
216         strcat(spec, ".");
217         strcat(spec, prefs[i]);
218
219         XrmPutStringResource (db, spec, val);
220
221         free(spec);
222         free(val);
223         return 0;
224       }
225
226   fprintf(stderr, "%s: %s:%d: unknown option \"%s\"\n",
227           blurb(), filename, line, key);
228   return 1;
229 }
230
231
232 static int
233 parse_init_file (saver_preferences *p)
234 {
235   time_t write_date = 0;
236   const char *name = init_file_name();
237   int line = 0;
238   struct stat st;
239   FILE *in;
240   int buf_size = 1024;
241   char *buf;
242
243   if (!name) return 0;
244
245   if (stat(name, &st) != 0)
246     {
247       p->init_file_date = 0;
248       return 0;
249     }
250
251   in = fopen(name, "r");
252   if (!in)
253     {
254       char *buf = (char *) malloc(1024 + strlen(name));
255       sprintf(buf, "%s: error reading \"%s\"", blurb(), name);
256       perror(buf);
257       free(buf);
258       return -1;
259     }
260
261   if (fstat (fileno(in), &st) == 0)
262     {
263       write_date = st.st_mtime;
264     }
265   else
266     {
267       char *buf = (char *) malloc(1024 + strlen(name));
268       sprintf(buf, "%s: couldn't re-stat \"%s\"", blurb(), name);
269       perror(buf);
270       free(buf);
271       return -1;
272     }
273
274   buf = (char *) malloc(buf_size);
275
276   while (fgets (buf, buf_size-1, in))
277     {
278       char *key, *value;
279       int L = strlen(buf);
280
281       line++;
282       while (L > 2 &&
283              (buf[L-1] != '\n' ||       /* whole line didn't fit in buffer */
284               buf[L-2] == '\\'))        /* or line ended with backslash */
285         {
286           if (buf[L-2] == '\\')         /* backslash-newline gets swallowed */
287             {
288               buf[L-2] = 0;
289               L -= 2;
290             }
291           buf_size += 1024;
292           buf = (char *) realloc(buf, buf_size);
293           if (!buf) exit(1);
294
295           line++;
296           if (!fgets (buf + L, buf_size-L-1, in))
297             break;
298           L = strlen(buf);
299         }
300
301       /* Now handle other backslash escapes. */
302       {
303         int i, j;
304         for (i = 0; buf[i]; i++)
305           if (buf[i] == '\\')
306             {
307               switch (buf[i+1])
308                 {
309                 case 'n': buf[i] = '\n'; break;
310                 case 'r': buf[i] = '\r'; break;
311                 case 't': buf[i] = '\t'; break;
312                 default:  buf[i] = buf[i+1]; break;
313                 }
314               for (j = i+2; buf[j]; j++)
315                 buf[j-1] = buf[j];
316               buf[j-1] = 0;
317             }
318       }
319
320       key = strip(buf);
321
322       if (*key == '#' || *key == '!' || *key == ';' ||
323           *key == '\n' || *key == 0)
324         continue;
325
326       value = strchr (key, ':');
327       if (!value)
328         {
329           fprintf(stderr, "%s: %s:%d: unparsable line: %s\n", blurb(),
330                   name, line, key);
331           continue;
332         }
333       else
334         {
335           *value++ = 0;
336           value = strip(value);
337         }
338
339       if (!p->db) abort();
340       handle_entry (&p->db, key, value, name, line);
341     }
342   free(buf);
343
344   p->init_file_date = write_date;
345   return 0;
346 }
347
348
349 Bool
350 init_file_changed_p (saver_preferences *p)
351 {
352   const char *name = init_file_name();
353   struct stat st;
354
355   if (!name) return False;
356
357   if (stat(name, &st) != 0)
358     return False;
359
360   if (p->init_file_date == st.st_mtime)
361     return False;
362
363   return True;
364 }
365
366 \f
367 /* Writing
368  */
369
370 static int
371 tab_to (FILE *out, int from, int to)
372 {
373   int tab_width = 8;
374   int to_mod = (to / tab_width) * tab_width;
375   while (from < to_mod)
376     {
377       fprintf(out, "\t");
378       from = (((from / tab_width) + 1) * tab_width);
379     }
380   while (from < to)
381     {
382       fprintf(out, " ");
383       from++;
384     }
385   return from;
386 }
387
388 static void
389 write_entry (FILE *out, const char *key, const char *value)
390 {
391   char *v = strdup(value ? value : "");
392   char *v2 = v;
393   char *nl = 0;
394   int col;
395   Bool do_visual_kludge = (!strcmp(key, "programs"));
396   Bool do_wrap = do_visual_kludge;
397   int tab = (do_visual_kludge ? 16 : 23);
398   int tab2 = 3;
399   Bool first = True;
400
401   fprintf(out, "%s:", key);
402   col = strlen(key) + 1;
403
404   while (1)
405     {
406       char *s;
407       Bool disabled_p = False;
408
409       v2 = strip(v2);
410       nl = strchr(v2, '\n');
411       if (nl)
412         *nl = 0;
413
414       if (do_visual_kludge && *v2 == '-')
415         {
416           disabled_p = True;
417           v2++;
418           v2 = strip(v2);
419         }
420
421       if (first && disabled_p)
422         first = False;
423
424       if (first)
425         first = False;
426       else
427         {
428           col = tab_to(out, col, 75);
429           fprintf(out, " \\n\\\n");
430           col = 0;
431         }
432
433       if (disabled_p)
434         {
435           fprintf(out, "-");
436           col++;
437         }
438
439       s = (do_visual_kludge ? strpbrk(v2, " \t\n:") : 0);
440       if (s && *s == ':')
441         col = tab_to (out, col, tab2);
442       else
443         col = tab_to (out, col, tab);
444
445       if (do_wrap &&
446           strlen(v2) + col > 75)
447         {
448           int L = strlen(v2);
449           int start = 0;
450           int end = start;
451           while (start < L)
452             {
453               while (v2[end] == ' ' || v2[end] == '\t')
454                 end++;
455               while (v2[end] != ' ' && v2[end] != '\t' &&
456                      v2[end] != '\n' && v2[end] != 0)
457                 end++;
458               if (col + (end - start) >= 74)
459                 {
460                   col = tab_to (out, col, 75);
461                   fprintf(out, "   \\\n");
462                   col = tab_to (out, 0, tab + 2);
463                   while (v2[start] == ' ' || v2[start] == '\t')
464                     start++;
465                 }
466
467               while (start < end)
468                 {
469                   fputc(v2[start++], out);
470                   col++;
471                 }
472             }
473         }
474       else
475         {
476           fprintf (out, "%s", v2);
477           col += strlen(v2);
478         }
479
480       if (nl)
481         v2 = nl + 1;
482       else
483         break;
484     }
485
486   fprintf(out, "\n");
487   free(v);
488 }
489
490 void
491 write_init_file (saver_preferences *p, const char *version_string)
492 {
493   const char *name = init_file_name();
494   const char *tmp_name = init_file_tmp_name();
495   struct stat st;
496   int i, j;
497
498   /* Kludge, since these aren't in the saver_preferences struct as strings...
499    */
500   char *visual_name;
501   char *programs;
502   Bool capture_stderr_p;
503   Bool overlay_stderr_p;
504   char *stderr_font;
505   FILE *out;
506
507   if (!name) return;
508
509   if (p->verbose_p)
510     fprintf (stderr, "%s: writing \"%s\".\n", blurb(), name);
511
512   unlink (tmp_name);
513   out = fopen(tmp_name, "w");
514   if (!out)
515     {
516       char *buf = (char *) malloc(1024 + strlen(name));
517       sprintf(buf, "%s: error writing \"%s\"", blurb(), name);
518       perror(buf);
519       free(buf);
520       return;
521     }
522
523   /* Give the new .xscreensaver file the same permissions as the old one;
524      except ensure that it is readable and writable by owner, and not
525      executable.
526    */
527   if (stat(name, &st) == 0)
528     {
529       mode_t mode = st.st_mode;
530       mode |= S_IRUSR | S_IWUSR;
531       mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH);
532       if (fchmod (fileno(out), mode) != 0)
533         {
534           char *buf = (char *) malloc(1024 + strlen(name));
535           sprintf (buf, "%s: error fchmodding \"%s\" to 0%o", blurb(),
536                    tmp_name, (unsigned int) mode);
537           perror(buf);
538           free(buf);
539           return;
540         }
541     }
542
543   /* Kludge, since these aren't in the saver_preferences struct... */
544   visual_name = get_string_resource ("visualID", "VisualID");
545   programs = 0;
546   capture_stderr_p = get_boolean_resource ("captureStderr", "Boolean");
547   overlay_stderr_p = get_boolean_resource ("overlayStderr", "Boolean");
548   stderr_font = get_string_resource ("font", "Font");
549
550   i = 0;
551   for (j = 0; j < p->screenhacks_count; j++)
552     i += strlen(p->screenhacks[j]) + 2;
553   {
554     char *ss = programs = (char *) malloc(i + 10);
555     *ss = 0;
556     for (j = 0; j < p->screenhacks_count; j++)
557       {
558         strcat(ss, p->screenhacks[j]);
559         ss += strlen(ss);
560         *ss++ = '\n';
561         *ss = 0;
562       }
563   }
564
565   {
566     struct passwd *pw = getpwuid (getuid ());
567     char *whoami = (pw && pw->pw_name && *pw->pw_name
568                     ? pw->pw_name
569                     : "<unknown>");
570     time_t now = time ((time_t *) 0);
571     char *timestr = (char *) ctime (&now);
572     char *nl = (char *) strchr (timestr, '\n');
573     if (nl) *nl = 0;
574     fprintf (out,
575              "# %s Preferences File\n"
576              "# Written by %s %s for %s on %s.\n"
577              "# http://www.jwz.org/xscreensaver/\n"
578              "\n",
579              progclass, progname, version_string, whoami, timestr);
580   }
581
582   for (j = 0; prefs[j]; j++)
583     {
584       char buf[255];
585       const char *pr = prefs[j];
586       enum pref_type { pref_str, pref_int, pref_bool, pref_time
587       } type = pref_str;
588       const char *s = 0;
589       int i = 0;
590       Bool b = False;
591       Time t = 0;
592
593       if (pr && !*pr)
594         {
595           fprintf(out, "\n");
596           continue;
597         }
598
599 # undef CHECK
600 # define CHECK(X) else if (!strcmp(pr, X))
601       if (!pr || !*pr)          ;
602       CHECK("timeout")          type = pref_time, t = p->timeout;
603       CHECK("cycle")            type = pref_time, t = p->cycle;
604       CHECK("lock")             type = pref_bool, b = p->lock_p;
605 # if 0 /* #### not ready yet */
606       CHECK("lockVTs")          type = pref_bool, b = p->lock_vt_p;
607 # else
608       CHECK("lockVTs")          continue;  /* don't save */
609 # endif
610       CHECK("lockTimeout")      type = pref_time, t = p->lock_timeout;
611       CHECK("passwdTimeout")    type = pref_time, t = p->passwd_timeout;
612       CHECK("visualID")         type = pref_str,  s =    visual_name;
613       CHECK("installColormap")  type = pref_bool, b = p->install_cmap_p;
614       CHECK("verbose")          type = pref_bool, b = p->verbose_p;
615       CHECK("timestamp")        type = pref_bool, b = p->timestamp_p;
616       CHECK("splash")           continue;  /* don't save */
617       CHECK("splashDuration")   type = pref_time, t = p->splash_duration;
618       CHECK("demoCommand")      type = pref_str,  s = p->demo_command;
619       CHECK("prefsCommand")     type = pref_str,  s = p->prefs_command;
620       CHECK("helpURL")          type = pref_str,  s = p->help_url;
621       CHECK("loadURL")          type = pref_str,  s = p->load_url_command;
622       CHECK("nice")             type = pref_int,  i = p->nice_inferior;
623       CHECK("fade")             type = pref_bool, b = p->fade_p;
624       CHECK("unfade")           type = pref_bool, b = p->unfade_p;
625       CHECK("fadeSeconds")      type = pref_time, t = p->fade_seconds;
626       CHECK("fadeTicks")        type = pref_int,  i = p->fade_ticks;
627       CHECK("captureStderr")    type = pref_bool, b =    capture_stderr_p;
628       CHECK("captureStdout")    continue;  /* don't save */
629       CHECK("font")             type = pref_str,  s =    stderr_font;
630       CHECK("programs")         type = pref_str,  s =    programs;
631       CHECK("pointerPollTime")  type = pref_time, t = p->pointer_timeout;
632       CHECK("windowCreationTimeout")type=pref_time,t= p->notice_events_timeout;
633       CHECK("initialDelay")     type = pref_time, t = p->initial_delay;
634       CHECK("sgiSaverExtension")type = pref_bool, b=p->use_sgi_saver_extension;
635       CHECK("mitSaverExtension")type = pref_bool, b=p->use_mit_saver_extension;
636       CHECK("xidleExtension")   type = pref_bool, b = p->use_xidle_extension;
637       CHECK("procInterrupts")   type = pref_bool, b = p->use_proc_interrupts;
638       CHECK("overlayStderr")    type = pref_bool, b = overlay_stderr_p;
639       CHECK("overlayTextBackground") continue;  /* don't save */
640       CHECK("overlayTextForeground") continue;  /* don't save */
641       CHECK("bourneShell")      continue;
642       else                      abort();
643 # undef CHECK
644
645       switch (type)
646         {
647         case pref_str:
648           break;
649         case pref_int:
650           sprintf(buf, "%d", i);
651           s = buf;
652           break;
653         case pref_bool:
654           s = b ? "True" : "False";
655           break;
656         case pref_time:
657           {
658             unsigned int hour = 0, min = 0, sec = (unsigned int) (t/1000);
659             if (sec >= 60)
660               {
661                 min += (sec / 60);
662                 sec %= 60;
663               }
664             if (min >= 60)
665               {
666                 hour += (min / 60);
667                 min %= 60;
668               }
669             sprintf (buf, "%u:%02u:%02u", hour, min, sec);
670             s = buf;
671           }
672           break;
673         default:
674           abort();
675           break;
676         }
677       write_entry (out, pr, s);
678     }
679
680   fprintf(out, "\n");
681
682   if (visual_name) free(visual_name);
683   if (stderr_font) free(stderr_font);
684   if (programs) free(programs);
685
686   if (fclose(out) == 0)
687     {
688       time_t write_date = 0;
689
690       if (stat(tmp_name, &st) == 0)
691         {
692           write_date = st.st_mtime;
693         }
694       else
695         {
696           char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
697           sprintf(buf, "%s: couldn't stat \"%s\"", blurb(), tmp_name);
698           perror(buf);
699           unlink (tmp_name);
700           free(buf);
701           return;
702         }
703
704       if (rename (tmp_name, name) != 0)
705         {
706           char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
707           sprintf(buf, "%s: error renaming \"%s\" to \"%s\"",
708                   blurb(), tmp_name, name);
709           perror(buf);
710           unlink (tmp_name);
711           free(buf);
712           return;
713         }
714       else
715         {
716           p->init_file_date = write_date;
717
718           /* Since the .xscreensaver file is used for IPC, let's try and make
719              sure that the bits actually land on the disk right away. */
720           sync ();
721         }
722     }
723   else
724     {
725       char *buf = (char *) malloc(1024 + strlen(name));
726       sprintf(buf, "%s: error closing \"%s\"", blurb(), name);
727       perror(buf);
728       free(buf);
729       unlink (tmp_name);
730       return;
731     }
732 }
733
734 \f
735 /* Parsing the resource database
736  */
737
738
739 /* Populate `saver_preferences' with the contents of the resource database.
740    Note that this may be called multiple times -- it is re-run each time
741    the ~/.xscreensaver file is reloaded.
742
743    This function can be very noisy, since it issues resource syntax errors
744    and so on.
745  */
746 void
747 load_init_file (saver_preferences *p)
748 {
749   static Bool first_time = True;
750   
751   if (parse_init_file (p) != 0)         /* file might have gone away */
752     if (!first_time) return;
753
754   first_time = False;
755
756   p->xsync_p        = get_boolean_resource ("synchronous", "Synchronous");
757   p->verbose_p      = get_boolean_resource ("verbose", "Boolean");
758   p->timestamp_p    = get_boolean_resource ("timestamp", "Boolean");
759   p->lock_p         = get_boolean_resource ("lock", "Boolean");
760   p->lock_vt_p      = get_boolean_resource ("lockVTs", "Boolean");
761   p->fade_p         = get_boolean_resource ("fade", "Boolean");
762   p->unfade_p       = get_boolean_resource ("unfade", "Boolean");
763   p->fade_seconds   = 1000 * get_seconds_resource ("fadeSeconds", "Time");
764   p->fade_ticks     = get_integer_resource ("fadeTicks", "Integer");
765   p->install_cmap_p = get_boolean_resource ("installColormap", "Boolean");
766   p->nice_inferior  = get_integer_resource ("nice", "Nice");
767
768   p->initial_delay   = 1000 * get_seconds_resource ("initialDelay", "Time");
769   p->splash_duration = 1000 * get_seconds_resource ("splashDuration", "Time");
770   p->timeout         = 1000 * get_minutes_resource ("timeout", "Time");
771   p->lock_timeout    = 1000 * get_minutes_resource ("lockTimeout", "Time");
772   p->cycle           = 1000 * get_minutes_resource ("cycle", "Time");
773   p->passwd_timeout  = 1000 * get_seconds_resource ("passwdTimeout", "Time");
774   p->pointer_timeout = 1000 * get_seconds_resource ("pointerPollTime", "Time");
775   p->notice_events_timeout = 1000*get_seconds_resource("windowCreationTimeout",
776                                                        "Time");
777   p->shell = get_string_resource ("bourneShell", "BourneShell");
778
779   p->demo_command = get_string_resource("demoCommand", "URL");
780   p->prefs_command = get_string_resource("prefsCommand", "URL");
781   p->help_url = get_string_resource("helpURL", "URL");
782   p->load_url_command = get_string_resource("loadURL", "LoadURL");
783
784   {
785     char *s;
786     if ((s = get_string_resource ("splash", "Boolean")))
787       if (!get_boolean_resource("splash", "Boolean"))
788         p->splash_duration = 0;
789     if (s) free (s);
790   }
791
792   p->use_xidle_extension = get_boolean_resource ("xidleExtension","Boolean");
793   p->use_mit_saver_extension = get_boolean_resource ("mitSaverExtension",
794                                                      "Boolean");
795   p->use_sgi_saver_extension = get_boolean_resource ("sgiSaverExtension",
796                                                      "Boolean");
797   p->use_proc_interrupts = get_boolean_resource ("procInterrupts", "Boolean");
798
799   /* Throttle the various timeouts to reasonable values.
800    */
801   if (p->passwd_timeout <= 0) p->passwd_timeout = 30000;         /* 30 secs */
802   if (p->timeout < 10000) p->timeout = 10000;                    /* 10 secs */
803   if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000;         /*  2 secs */
804   if (p->pointer_timeout <= 0) p->pointer_timeout = 5000;        /*  5 secs */
805   if (p->notice_events_timeout <= 0)
806     p->notice_events_timeout = 10000;                            /* 10 secs */
807   if (p->fade_seconds <= 0 || p->fade_ticks <= 0)
808     p->fade_p = False;
809   if (! p->fade_p) p->unfade_p = False;
810
811   if (p->verbose_p && !p->fading_possible_p && (p->fade_p || p->unfade_p))
812     {
813       fprintf (stderr, "%s: there are no PseudoColor or GrayScale visuals.\n",
814                blurb());
815       fprintf (stderr, "%s: ignoring the request for fading/unfading.\n",
816                blurb());
817     }
818
819   p->watchdog_timeout = p->cycle;
820   if (p->watchdog_timeout < 30000) p->watchdog_timeout = 30000;   /* 30 secs */
821   if (p->watchdog_timeout > 3600000) p->watchdog_timeout = 3600000; /*  1 hr */
822
823   get_screenhacks (p);
824
825   if (p->debug_p)
826     {
827       p->xsync_p = True;
828       p->verbose_p = True;
829       p->timestamp_p = True;
830       p->initial_delay = 0;
831     }
832 }
833
834 \f
835 /* Parsing the programs resource.
836  */
837
838 static char *
839 reformat_hack (const char *hack)
840 {
841   int i;
842   const char *in = hack;
843   int indent = 15;
844   char *h2 = (char *) malloc(strlen(in) + indent + 2);
845   char *out = h2;
846   Bool disabled_p = False;
847
848   while (isspace(*in)) in++;            /* skip whitespace */
849
850   if (*in == '-')                       /* Handle a leading "-". */
851     {
852       in++;
853       hack = in;
854       *out++ = '-';
855       *out++ = ' ';
856       disabled_p = True;
857       while (isspace(*in)) in++;
858     }
859   else
860     {
861       *out++ = ' ';
862       *out++ = ' ';
863     }
864
865   while (*in && !isspace(*in) && *in != ':')
866     *out++ = *in++;                     /* snarf first token */
867   while (isspace(*in)) in++;            /* skip whitespace */
868
869   if (*in == ':')
870     *out++ = *in++;                     /* copy colon */
871   else
872     {
873       in = hack;
874       out = h2 + 2;                     /* reset to beginning */
875     }
876
877   *out = 0;
878
879   while (isspace(*in)) in++;            /* skip whitespace */
880   for (i = strlen(h2); i < indent; i++) /* indent */
881     *out++ = ' ';
882
883   /* copy the rest of the line. */
884   while (*in)
885     {
886       /* shrink all whitespace to one space, for the benefit of the "demo"
887          mode display.  We only do this when we can easily tell that the
888          whitespace is not significant (no shell metachars).
889        */
890       switch (*in)
891         {
892         case '\'': case '"': case '`': case '\\':
893           {
894             /* Metachars are scary.  Copy the rest of the line unchanged. */
895             while (*in)
896               *out++ = *in++;
897           }
898           break;
899         case ' ': case '\t':
900           {
901             while (*in == ' ' || *in == '\t')
902               in++;
903             *out++ = ' ';
904           }
905           break;
906         default:
907           *out++ = *in++;
908           break;
909         }
910     }
911   *out = 0;
912
913   /* strip trailing whitespace. */
914   out = out-1;
915   while (out > h2 && (*out == ' ' || *out == '\t' || *out == '\n'))
916     *out-- = 0;
917
918   return h2;
919 }
920
921
922 static void
923 get_screenhacks (saver_preferences *p)
924 {
925   int i = 0;
926   int start = 0;
927   int end = 0;
928   int size;
929   char *d;
930
931   d = get_string_resource ("monoPrograms", "MonoPrograms");
932   if (d && !*d) { free(d); d = 0; }
933   if (!d)
934     d = get_string_resource ("colorPrograms", "ColorPrograms");
935   if (d && !*d) { free(d); d = 0; }
936
937   if (d)
938     {
939       fprintf (stderr,
940        "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
941         see the manual for details.\n", blurb());
942       free(d);
943     }
944
945   d = get_string_resource ("programs", "Programs");
946
947   if (p->screenhacks)
948     {
949       for (i = 0; i < p->screenhacks_count; i++)
950         if (p->screenhacks[i])
951           free (p->screenhacks[i]);
952       free(p->screenhacks);
953       p->screenhacks = 0;
954     }
955
956   if (!d || !*d)
957     {
958       p->screenhacks_count = 0;
959       p->screenhacks = 0;
960       return;
961     }
962
963   size = strlen (d);
964
965
966   /* Count up the number of newlines (which will be equal to or larger than
967      the number of hacks.)
968    */
969   i = 0;
970   for (i = 0; d[i]; i++)
971     if (d[i] == '\n')
972       i++;
973   i++;
974
975   p->screenhacks = (char **) calloc (sizeof (char *), i+1);
976
977   /* Iterate over the lines in `d' (the string with newlines)
978      and make new strings to stuff into the `screenhacks' array.
979    */
980   p->screenhacks_count = 0;
981   while (start < size)
982     {
983       /* skip forward over whitespace. */
984       while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n')
985         start++;
986
987       /* skip forward to newline or end of string. */
988       end = start;
989       while (d[end] != 0 && d[end] != '\n')
990         end++;
991
992       /* null terminate. */
993       d[end] = 0;
994
995       p->screenhacks[p->screenhacks_count++] = reformat_hack (d + start);
996       if (p->screenhacks_count >= i)
997         abort();
998
999       start = end+1;
1000     }
1001
1002   if (p->screenhacks_count == 0)
1003     {
1004       free (p->screenhacks);
1005       p->screenhacks = 0;
1006     }
1007 }