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