http://ftp.x.org/contrib/applications/xscreensaver-3.17.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   fclose (in);
367   free(buf);
368
369   p->init_file_date = write_date;
370   return 0;
371 }
372
373
374 Bool
375 init_file_changed_p (saver_preferences *p)
376 {
377   const char *name = init_file_name();
378   struct stat st;
379
380   if (!name) return False;
381
382   if (stat(name, &st) != 0)
383     return False;
384
385   if (p->init_file_date == st.st_mtime)
386     return False;
387
388   return True;
389 }
390
391 \f
392 /* Writing
393  */
394
395 static int
396 tab_to (FILE *out, int from, int to)
397 {
398   int tab_width = 8;
399   int to_mod = (to / tab_width) * tab_width;
400   while (from < to_mod)
401     {
402       fprintf(out, "\t");
403       from = (((from / tab_width) + 1) * tab_width);
404     }
405   while (from < to)
406     {
407       fprintf(out, " ");
408       from++;
409     }
410   return from;
411 }
412
413 static void
414 write_entry (FILE *out, const char *key, const char *value)
415 {
416   char *v = strdup(value ? value : "");
417   char *v2 = v;
418   char *nl = 0;
419   int col;
420   Bool do_visual_kludge = (!strcmp(key, "programs"));
421   Bool do_wrap = do_visual_kludge;
422   int tab = (do_visual_kludge ? 16 : 23);
423   int tab2 = 3;
424   Bool first = True;
425
426   fprintf(out, "%s:", key);
427   col = strlen(key) + 1;
428
429   while (1)
430     {
431       char *s;
432       Bool disabled_p = False;
433
434       v2 = strip(v2);
435       nl = strchr(v2, '\n');
436       if (nl)
437         *nl = 0;
438
439       if (do_visual_kludge && *v2 == '-')
440         {
441           disabled_p = True;
442           v2++;
443           v2 = strip(v2);
444         }
445
446       if (first && disabled_p)
447         first = False;
448
449       if (first)
450         first = False;
451       else
452         {
453           col = tab_to(out, col, 75);
454           fprintf(out, " \\n\\\n");
455           col = 0;
456         }
457
458       if (disabled_p)
459         {
460           fprintf(out, "-");
461           col++;
462         }
463
464       s = (do_visual_kludge ? strpbrk(v2, " \t\n:") : 0);
465       if (s && *s == ':')
466         col = tab_to (out, col, tab2);
467       else
468         col = tab_to (out, col, tab);
469
470       if (do_wrap &&
471           strlen(v2) + col > 75)
472         {
473           int L = strlen(v2);
474           int start = 0;
475           int end = start;
476           while (start < L)
477             {
478               while (v2[end] == ' ' || v2[end] == '\t')
479                 end++;
480               while (v2[end] != ' ' && v2[end] != '\t' &&
481                      v2[end] != '\n' && v2[end] != 0)
482                 end++;
483               if (col + (end - start) >= 74)
484                 {
485                   col = tab_to (out, col, 75);
486                   fprintf(out, "   \\\n");
487                   col = tab_to (out, 0, tab + 2);
488                   while (v2[start] == ' ' || v2[start] == '\t')
489                     start++;
490                 }
491
492               while (start < end)
493                 {
494                   fputc(v2[start++], out);
495                   col++;
496                 }
497             }
498         }
499       else
500         {
501           fprintf (out, "%s", v2);
502           col += strlen(v2);
503         }
504
505       if (nl)
506         v2 = nl + 1;
507       else
508         break;
509     }
510
511   fprintf(out, "\n");
512   free(v);
513 }
514
515 void
516 write_init_file (saver_preferences *p, const char *version_string)
517 {
518   const char *name = init_file_name();
519   const char *tmp_name = init_file_tmp_name();
520   char *n2 = chase_symlinks (name);
521   struct stat st;
522   int i, j;
523
524   /* Kludge, since these aren't in the saver_preferences struct as strings...
525    */
526   char *visual_name;
527   char *programs;
528   Bool capture_stderr_p;
529   Bool overlay_stderr_p;
530   char *stderr_font;
531   FILE *out;
532
533   if (!name) goto END;
534
535   if (n2) name = n2;
536
537   if (p->verbose_p)
538     fprintf (stderr, "%s: writing \"%s\".\n", blurb(), name);
539
540   unlink (tmp_name);
541   out = fopen(tmp_name, "w");
542   if (!out)
543     {
544       char *buf = (char *) malloc(1024 + strlen(name));
545       sprintf(buf, "%s: error writing \"%s\"", blurb(), name);
546       perror(buf);
547       free(buf);
548       goto END;
549     }
550
551   /* Give the new .xscreensaver file the same permissions as the old one;
552      except ensure that it is readable and writable by owner, and not
553      executable.
554    */
555   if (stat(name, &st) == 0)
556     {
557       mode_t mode = st.st_mode;
558       mode |= S_IRUSR | S_IWUSR;
559       mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH);
560       if (fchmod (fileno(out), mode) != 0)
561         {
562           char *buf = (char *) malloc(1024 + strlen(name));
563           sprintf (buf, "%s: error fchmodding \"%s\" to 0%o", blurb(),
564                    tmp_name, (unsigned int) mode);
565           perror(buf);
566           free(buf);
567           goto END;
568         }
569     }
570
571   /* Kludge, since these aren't in the saver_preferences struct... */
572   visual_name = get_string_resource ("visualID", "VisualID");
573   programs = 0;
574   capture_stderr_p = get_boolean_resource ("captureStderr", "Boolean");
575   overlay_stderr_p = get_boolean_resource ("overlayStderr", "Boolean");
576   stderr_font = get_string_resource ("font", "Font");
577
578   i = 0;
579   for (j = 0; j < p->screenhacks_count; j++)
580     i += strlen(p->screenhacks[j]) + 2;
581   {
582     char *ss = programs = (char *) malloc(i + 10);
583     *ss = 0;
584     for (j = 0; j < p->screenhacks_count; j++)
585       {
586         strcat(ss, p->screenhacks[j]);
587         ss += strlen(ss);
588         *ss++ = '\n';
589         *ss = 0;
590       }
591   }
592
593   {
594     struct passwd *pw = getpwuid (getuid ());
595     char *whoami = (pw && pw->pw_name && *pw->pw_name
596                     ? pw->pw_name
597                     : "<unknown>");
598     time_t now = time ((time_t *) 0);
599     char *timestr = (char *) ctime (&now);
600     char *nl = (char *) strchr (timestr, '\n');
601     if (nl) *nl = 0;
602     fprintf (out,
603              "# %s Preferences File\n"
604              "# Written by %s %s for %s on %s.\n"
605              "# http://www.jwz.org/xscreensaver/\n"
606              "\n",
607              progclass, progname, version_string, whoami, timestr);
608   }
609
610   for (j = 0; prefs[j]; j++)
611     {
612       char buf[255];
613       const char *pr = prefs[j];
614       enum pref_type { pref_str, pref_int, pref_bool, pref_time
615       } type = pref_str;
616       const char *s = 0;
617       int i = 0;
618       Bool b = False;
619       Time t = 0;
620
621       if (pr && !*pr)
622         {
623           fprintf(out, "\n");
624           continue;
625         }
626
627 # undef CHECK
628 # define CHECK(X) else if (!strcmp(pr, X))
629       if (!pr || !*pr)          ;
630       CHECK("timeout")          type = pref_time, t = p->timeout;
631       CHECK("cycle")            type = pref_time, t = p->cycle;
632       CHECK("lock")             type = pref_bool, b = p->lock_p;
633 # if 0 /* #### not ready yet */
634       CHECK("lockVTs")          type = pref_bool, b = p->lock_vt_p;
635 # else
636       CHECK("lockVTs")          continue;  /* don't save */
637 # endif
638       CHECK("lockTimeout")      type = pref_time, t = p->lock_timeout;
639       CHECK("passwdTimeout")    type = pref_time, t = p->passwd_timeout;
640       CHECK("visualID")         type = pref_str,  s =    visual_name;
641       CHECK("installColormap")  type = pref_bool, b = p->install_cmap_p;
642       CHECK("verbose")          type = pref_bool, b = p->verbose_p;
643       CHECK("timestamp")        type = pref_bool, b = p->timestamp_p;
644       CHECK("splash")           continue;  /* don't save */
645       CHECK("splashDuration")   type = pref_time, t = p->splash_duration;
646       CHECK("demoCommand")      type = pref_str,  s = p->demo_command;
647       CHECK("prefsCommand")     type = pref_str,  s = p->prefs_command;
648       CHECK("helpURL")          type = pref_str,  s = p->help_url;
649       CHECK("loadURL")          type = pref_str,  s = p->load_url_command;
650       CHECK("nice")             type = pref_int,  i = p->nice_inferior;
651       CHECK("fade")             type = pref_bool, b = p->fade_p;
652       CHECK("unfade")           type = pref_bool, b = p->unfade_p;
653       CHECK("fadeSeconds")      type = pref_time, t = p->fade_seconds;
654       CHECK("fadeTicks")        type = pref_int,  i = p->fade_ticks;
655       CHECK("captureStderr")    type = pref_bool, b =    capture_stderr_p;
656       CHECK("captureStdout")    continue;  /* don't save */
657       CHECK("font")             type = pref_str,  s =    stderr_font;
658       CHECK("programs")         type = pref_str,  s =    programs;
659       CHECK("pointerPollTime")  type = pref_time, t = p->pointer_timeout;
660       CHECK("windowCreationTimeout")type=pref_time,t= p->notice_events_timeout;
661       CHECK("initialDelay")     type = pref_time, t = p->initial_delay;
662       CHECK("sgiSaverExtension")type = pref_bool, b=p->use_sgi_saver_extension;
663       CHECK("mitSaverExtension")type = pref_bool, b=p->use_mit_saver_extension;
664       CHECK("xidleExtension")   type = pref_bool, b = p->use_xidle_extension;
665       CHECK("procInterrupts")   type = pref_bool, b = p->use_proc_interrupts;
666       CHECK("overlayStderr")    type = pref_bool, b = overlay_stderr_p;
667       CHECK("overlayTextBackground") continue;  /* don't save */
668       CHECK("overlayTextForeground") continue;  /* don't save */
669       CHECK("bourneShell")      continue;
670       else                      abort();
671 # undef CHECK
672
673       switch (type)
674         {
675         case pref_str:
676           break;
677         case pref_int:
678           sprintf(buf, "%d", i);
679           s = buf;
680           break;
681         case pref_bool:
682           s = b ? "True" : "False";
683           break;
684         case pref_time:
685           {
686             unsigned int hour = 0, min = 0, sec = (unsigned int) (t/1000);
687             if (sec >= 60)
688               {
689                 min += (sec / 60);
690                 sec %= 60;
691               }
692             if (min >= 60)
693               {
694                 hour += (min / 60);
695                 min %= 60;
696               }
697             sprintf (buf, "%u:%02u:%02u", hour, min, sec);
698             s = buf;
699           }
700           break;
701         default:
702           abort();
703           break;
704         }
705       write_entry (out, pr, s);
706     }
707
708   fprintf(out, "\n");
709
710   if (visual_name) free(visual_name);
711   if (stderr_font) free(stderr_font);
712   if (programs) free(programs);
713
714   if (fclose(out) == 0)
715     {
716       time_t write_date = 0;
717
718       if (stat(tmp_name, &st) == 0)
719         {
720           write_date = st.st_mtime;
721         }
722       else
723         {
724           char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
725           sprintf(buf, "%s: couldn't stat \"%s\"", blurb(), tmp_name);
726           perror(buf);
727           unlink (tmp_name);
728           free(buf);
729           goto END;
730         }
731
732       if (rename (tmp_name, name) != 0)
733         {
734           char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
735           sprintf(buf, "%s: error renaming \"%s\" to \"%s\"",
736                   blurb(), tmp_name, name);
737           perror(buf);
738           unlink (tmp_name);
739           free(buf);
740           goto END;
741         }
742       else
743         {
744           p->init_file_date = write_date;
745
746           /* Since the .xscreensaver file is used for IPC, let's try and make
747              sure that the bits actually land on the disk right away. */
748           sync ();
749         }
750     }
751   else
752     {
753       char *buf = (char *) malloc(1024 + strlen(name));
754       sprintf(buf, "%s: error closing \"%s\"", blurb(), name);
755       perror(buf);
756       free(buf);
757       unlink (tmp_name);
758       goto END;
759     }
760
761  END:
762   if (n2) free (n2);
763 }
764
765 \f
766 /* Parsing the resource database
767  */
768
769
770 /* Populate `saver_preferences' with the contents of the resource database.
771    Note that this may be called multiple times -- it is re-run each time
772    the ~/.xscreensaver file is reloaded.
773
774    This function can be very noisy, since it issues resource syntax errors
775    and so on.
776  */
777 void
778 load_init_file (saver_preferences *p)
779 {
780   static Bool first_time = True;
781   
782   if (parse_init_file (p) != 0)         /* file might have gone away */
783     if (!first_time) return;
784
785   first_time = False;
786
787   p->xsync_p        = get_boolean_resource ("synchronous", "Synchronous");
788   p->verbose_p      = get_boolean_resource ("verbose", "Boolean");
789   p->timestamp_p    = get_boolean_resource ("timestamp", "Boolean");
790   p->lock_p         = get_boolean_resource ("lock", "Boolean");
791   p->lock_vt_p      = get_boolean_resource ("lockVTs", "Boolean");
792   p->fade_p         = get_boolean_resource ("fade", "Boolean");
793   p->unfade_p       = get_boolean_resource ("unfade", "Boolean");
794   p->fade_seconds   = 1000 * get_seconds_resource ("fadeSeconds", "Time");
795   p->fade_ticks     = get_integer_resource ("fadeTicks", "Integer");
796   p->install_cmap_p = get_boolean_resource ("installColormap", "Boolean");
797   p->nice_inferior  = get_integer_resource ("nice", "Nice");
798
799   p->initial_delay   = 1000 * get_seconds_resource ("initialDelay", "Time");
800   p->splash_duration = 1000 * get_seconds_resource ("splashDuration", "Time");
801   p->timeout         = 1000 * get_minutes_resource ("timeout", "Time");
802   p->lock_timeout    = 1000 * get_minutes_resource ("lockTimeout", "Time");
803   p->cycle           = 1000 * get_minutes_resource ("cycle", "Time");
804   p->passwd_timeout  = 1000 * get_seconds_resource ("passwdTimeout", "Time");
805   p->pointer_timeout = 1000 * get_seconds_resource ("pointerPollTime", "Time");
806   p->notice_events_timeout = 1000*get_seconds_resource("windowCreationTimeout",
807                                                        "Time");
808   p->shell = get_string_resource ("bourneShell", "BourneShell");
809
810   p->demo_command = get_string_resource("demoCommand", "URL");
811   p->prefs_command = get_string_resource("prefsCommand", "URL");
812   p->help_url = get_string_resource("helpURL", "URL");
813   p->load_url_command = get_string_resource("loadURL", "LoadURL");
814
815   {
816     char *s;
817     if ((s = get_string_resource ("splash", "Boolean")))
818       if (!get_boolean_resource("splash", "Boolean"))
819         p->splash_duration = 0;
820     if (s) free (s);
821   }
822
823   p->use_xidle_extension = get_boolean_resource ("xidleExtension","Boolean");
824   p->use_mit_saver_extension = get_boolean_resource ("mitSaverExtension",
825                                                      "Boolean");
826   p->use_sgi_saver_extension = get_boolean_resource ("sgiSaverExtension",
827                                                      "Boolean");
828   p->use_proc_interrupts = get_boolean_resource ("procInterrupts", "Boolean");
829
830   /* Throttle the various timeouts to reasonable values.
831    */
832   if (p->passwd_timeout <= 0) p->passwd_timeout = 30000;         /* 30 secs */
833   if (p->timeout < 10000) p->timeout = 10000;                    /* 10 secs */
834   if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000;         /*  2 secs */
835   if (p->pointer_timeout <= 0) p->pointer_timeout = 5000;        /*  5 secs */
836   if (p->notice_events_timeout <= 0)
837     p->notice_events_timeout = 10000;                            /* 10 secs */
838   if (p->fade_seconds <= 0 || p->fade_ticks <= 0)
839     p->fade_p = False;
840   if (! p->fade_p) p->unfade_p = False;
841
842   p->watchdog_timeout = p->cycle * 0.6;
843   if (p->watchdog_timeout < 30000) p->watchdog_timeout = 30000;   /* 30 secs */
844   if (p->watchdog_timeout > 3600000) p->watchdog_timeout = 3600000; /*  1 hr */
845
846   get_screenhacks (p);
847
848   if (p->debug_p)
849     {
850       p->xsync_p = True;
851       p->verbose_p = True;
852       p->timestamp_p = True;
853       p->initial_delay = 0;
854     }
855 }
856
857 \f
858 /* Parsing the programs resource.
859  */
860
861 static char *
862 reformat_hack (const char *hack)
863 {
864   int i;
865   const char *in = hack;
866   int indent = 15;
867   char *h2 = (char *) malloc(strlen(in) + indent + 2);
868   char *out = h2;
869   Bool disabled_p = False;
870
871   while (isspace(*in)) in++;            /* skip whitespace */
872
873   if (*in == '-')                       /* Handle a leading "-". */
874     {
875       in++;
876       hack = in;
877       *out++ = '-';
878       *out++ = ' ';
879       disabled_p = True;
880       while (isspace(*in)) in++;
881     }
882   else
883     {
884       *out++ = ' ';
885       *out++ = ' ';
886     }
887
888   while (*in && !isspace(*in) && *in != ':')
889     *out++ = *in++;                     /* snarf first token */
890   while (isspace(*in)) in++;            /* skip whitespace */
891
892   if (*in == ':')
893     *out++ = *in++;                     /* copy colon */
894   else
895     {
896       in = hack;
897       out = h2 + 2;                     /* reset to beginning */
898     }
899
900   *out = 0;
901
902   while (isspace(*in)) in++;            /* skip whitespace */
903   for (i = strlen(h2); i < indent; i++) /* indent */
904     *out++ = ' ';
905
906   /* copy the rest of the line. */
907   while (*in)
908     {
909       /* shrink all whitespace to one space, for the benefit of the "demo"
910          mode display.  We only do this when we can easily tell that the
911          whitespace is not significant (no shell metachars).
912        */
913       switch (*in)
914         {
915         case '\'': case '"': case '`': case '\\':
916           {
917             /* Metachars are scary.  Copy the rest of the line unchanged. */
918             while (*in)
919               *out++ = *in++;
920           }
921           break;
922         case ' ': case '\t':
923           {
924             while (*in == ' ' || *in == '\t')
925               in++;
926             *out++ = ' ';
927           }
928           break;
929         default:
930           *out++ = *in++;
931           break;
932         }
933     }
934   *out = 0;
935
936   /* strip trailing whitespace. */
937   out = out-1;
938   while (out > h2 && (*out == ' ' || *out == '\t' || *out == '\n'))
939     *out-- = 0;
940
941   return h2;
942 }
943
944
945 static void
946 get_screenhacks (saver_preferences *p)
947 {
948   int i = 0;
949   int start = 0;
950   int end = 0;
951   int size;
952   char *d;
953
954   d = get_string_resource ("monoPrograms", "MonoPrograms");
955   if (d && !*d) { free(d); d = 0; }
956   if (!d)
957     d = get_string_resource ("colorPrograms", "ColorPrograms");
958   if (d && !*d) { free(d); d = 0; }
959
960   if (d)
961     {
962       fprintf (stderr,
963        "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
964         see the manual for details.\n", blurb());
965       free(d);
966     }
967
968   d = get_string_resource ("programs", "Programs");
969
970   if (p->screenhacks)
971     {
972       for (i = 0; i < p->screenhacks_count; i++)
973         if (p->screenhacks[i])
974           free (p->screenhacks[i]);
975       free(p->screenhacks);
976       p->screenhacks = 0;
977     }
978
979   if (!d || !*d)
980     {
981       p->screenhacks_count = 0;
982       p->screenhacks = 0;
983       return;
984     }
985
986   size = strlen (d);
987
988
989   /* Count up the number of newlines (which will be equal to or larger than
990      the number of hacks.)
991    */
992   i = 0;
993   for (i = 0; d[i]; i++)
994     if (d[i] == '\n')
995       i++;
996   i++;
997
998   p->screenhacks = (char **) calloc (sizeof (char *), i+1);
999
1000   /* Iterate over the lines in `d' (the string with newlines)
1001      and make new strings to stuff into the `screenhacks' array.
1002    */
1003   p->screenhacks_count = 0;
1004   while (start < size)
1005     {
1006       /* skip forward over whitespace. */
1007       while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n')
1008         start++;
1009
1010       /* skip forward to newline or end of string. */
1011       end = start;
1012       while (d[end] != 0 && d[end] != '\n')
1013         end++;
1014
1015       /* null terminate. */
1016       d[end] = 0;
1017
1018       p->screenhacks[p->screenhacks_count++] = reformat_hack (d + start);
1019       if (p->screenhacks_count >= i)
1020         abort();
1021
1022       start = end+1;
1023     }
1024
1025   if (p->screenhacks_count == 0)
1026     {
1027       free (p->screenhacks);
1028       p->screenhacks = 0;
1029     }
1030 }