http://ftp.x.org/contrib/applications/xscreensaver-3.08.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 = 0;
804   if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000;         /*  2 secs */
805   if (p->pointer_timeout <= 0) p->pointer_timeout = 5000;        /*  5 secs */
806   if (p->notice_events_timeout <= 0)
807     p->notice_events_timeout = 10000;                            /* 10 secs */
808   if (p->fade_seconds <= 0 || p->fade_ticks <= 0)
809     p->fade_p = False;
810   if (! p->fade_p) p->unfade_p = False;
811
812   if (p->verbose_p && !p->fading_possible_p && (p->fade_p || p->unfade_p))
813     {
814       fprintf (stderr, "%s: there are no PseudoColor or GrayScale visuals.\n",
815                blurb());
816       fprintf (stderr, "%s: ignoring the request for fading/unfading.\n",
817                blurb());
818     }
819
820   p->watchdog_timeout = p->cycle;
821   if (p->watchdog_timeout < 30000) p->watchdog_timeout = 30000;   /* 30 secs */
822   if (p->watchdog_timeout > 3600000) p->watchdog_timeout = 3600000; /*  1 hr */
823
824   get_screenhacks (p);
825
826   if (p->debug_p)
827     {
828       p->xsync_p = True;
829       p->verbose_p = True;
830       p->timestamp_p = True;
831       p->initial_delay = 0;
832     }
833 }
834
835 \f
836 /* Parsing the programs resource.
837  */
838
839 static char *
840 reformat_hack (const char *hack)
841 {
842   int i;
843   const char *in = hack;
844   int indent = 15;
845   char *h2 = (char *) malloc(strlen(in) + indent + 2);
846   char *out = h2;
847   Bool disabled_p = False;
848
849   while (isspace(*in)) in++;            /* skip whitespace */
850
851   if (*in == '-')                       /* Handle a leading "-". */
852     {
853       in++;
854       hack = in;
855       *out++ = '-';
856       *out++ = ' ';
857       disabled_p = True;
858       while (isspace(*in)) in++;
859     }
860   else
861     {
862       *out++ = ' ';
863       *out++ = ' ';
864     }
865
866   while (*in && !isspace(*in) && *in != ':')
867     *out++ = *in++;                     /* snarf first token */
868   while (isspace(*in)) in++;            /* skip whitespace */
869
870   if (*in == ':')
871     *out++ = *in++;                     /* copy colon */
872   else
873     {
874       in = hack;
875       out = h2 + 2;                     /* reset to beginning */
876     }
877
878   *out = 0;
879
880   while (isspace(*in)) in++;            /* skip whitespace */
881   for (i = strlen(h2); i < indent; i++) /* indent */
882     *out++ = ' ';
883
884   /* copy the rest of the line. */
885   while (*in)
886     {
887       /* shrink all whitespace to one space, for the benefit of the "demo"
888          mode display.  We only do this when we can easily tell that the
889          whitespace is not significant (no shell metachars).
890        */
891       switch (*in)
892         {
893         case '\'': case '"': case '`': case '\\':
894           {
895             /* Metachars are scary.  Copy the rest of the line unchanged. */
896             while (*in)
897               *out++ = *in++;
898           }
899           break;
900         case ' ': case '\t':
901           {
902             while (*in == ' ' || *in == '\t')
903               in++;
904             *out++ = ' ';
905           }
906           break;
907         default:
908           *out++ = *in++;
909           break;
910         }
911     }
912   *out = 0;
913
914   /* strip trailing whitespace. */
915   out = out-1;
916   while (out > h2 && (*out == ' ' || *out == '\t' || *out == '\n'))
917     *out-- = 0;
918
919   return h2;
920 }
921
922
923 static void
924 get_screenhacks (saver_preferences *p)
925 {
926   int i = 0;
927   int start = 0;
928   int end = 0;
929   int size;
930   char *d;
931
932   d = get_string_resource ("monoPrograms", "MonoPrograms");
933   if (d && !*d) { free(d); d = 0; }
934   if (!d)
935     d = get_string_resource ("colorPrograms", "ColorPrograms");
936   if (d && !*d) { free(d); d = 0; }
937
938   if (d)
939     {
940       fprintf (stderr,
941        "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
942         see the manual for details.\n", blurb());
943       free(d);
944     }
945
946   d = get_string_resource ("programs", "Programs");
947
948   if (p->screenhacks)
949     {
950       for (i = 0; i < p->screenhacks_count; i++)
951         if (p->screenhacks[i])
952           free (p->screenhacks[i]);
953       free(p->screenhacks);
954       p->screenhacks = 0;
955     }
956
957   if (!d || !*d)
958     {
959       p->screenhacks_count = 0;
960       p->screenhacks = 0;
961       return;
962     }
963
964   size = strlen (d);
965
966
967   /* Count up the number of newlines (which will be equal to or larger than
968      the number of hacks.)
969    */
970   i = 0;
971   for (i = 0; d[i]; i++)
972     if (d[i] == '\n')
973       i++;
974   i++;
975
976   p->screenhacks = (char **) calloc (sizeof (char *), i+1);
977
978   /* Iterate over the lines in `d' (the string with newlines)
979      and make new strings to stuff into the `screenhacks' array.
980    */
981   p->screenhacks_count = 0;
982   while (start < size)
983     {
984       /* skip forward over whitespace. */
985       while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n')
986         start++;
987
988       /* skip forward to newline or end of string. */
989       end = start;
990       while (d[end] != 0 && d[end] != '\n')
991         end++;
992
993       /* null terminate. */
994       d[end] = 0;
995
996       p->screenhacks[p->screenhacks_count++] = reformat_hack (d + start);
997       if (p->screenhacks_count >= i)
998         abort();
999
1000       start = end+1;
1001     }
1002
1003   if (p->screenhacks_count == 0)
1004     {
1005       free (p->screenhacks);
1006       p->screenhacks = 0;
1007     }
1008 }