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