http://ftp.x.org/contrib/applications/xscreensaver-2.34.tar.gz
[xscreensaver] / driver / dotfile.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 <sys/types.h>
18 #include <sys/stat.h>
19
20 #include <X11/Xlib.h>
21 #include <X11/Xresource.h>
22
23 #ifndef VMS
24 # include <pwd.h>
25 #else /* VMS */
26 # include "vms-pwd.h"
27 #endif /* VMS */
28
29
30 /* This file doesn't need the Xt headers, so stub these types out... */
31 #undef XtPointer
32 #define XtAppContext void*
33 #define XtIntervalId void*
34 #define XtPointer    void*
35 #define Widget       void*
36
37 #include "xscreensaver.h"
38 #include "resources.h"
39
40 /* Just in case there's something pathological about stat.h... */
41 #ifndef  S_IRUSR
42 # define S_IRUSR 00400
43 #endif
44 #ifndef  S_IWUSR
45 # define S_IWUSR 00200
46 #endif
47 #ifndef  S_IXUSR
48 # define S_IXUSR 00100
49 #endif
50 #ifndef  S_IXGRP
51 # define S_IXGRP 00010
52 #endif
53 #ifndef  S_IXOTH
54 # define S_IXOTH 00001
55 #endif
56
57
58 static const char *
59 init_file_name (void)
60 {
61   static char *file = 0;
62
63   if (!file)
64     {
65       struct passwd *p = getpwuid (getuid ());
66
67       if (!p || !p->pw_name || !*p->pw_name)
68         {
69           fprintf (stderr, "%s: couldn't get user info of uid %d\n",
70                    blurb(), getuid ());
71           file = "";
72         }
73       else if (!p->pw_dir || !*p->pw_dir)
74         {
75           fprintf (stderr, "%s: couldn't get home directory of %s\n",
76                    blurb(), (p->pw_name ? p->pw_name : "???"));
77           file = "";
78         }
79       else
80         {
81           const char *home = p->pw_dir;
82           const char *name = ".xscreensaver";
83           file = (char *) malloc(strlen(home) + strlen(name) + 2);
84           strcpy(file, home);
85           if (!*home || home[strlen(home)-1] != '/')
86             strcat(file, "/");
87           strcat(file, name);
88         }
89     }
90
91   if (file && *file)
92     return file;
93   else
94     return 0;
95 }
96
97
98 static const char *
99 init_file_tmp_name (void)
100 {
101   static char *file = 0;
102   if (!file)
103     {
104       const char *name = init_file_name();
105       const char *suffix = ".tmp";
106       if (!name || !*name)
107         file = "";
108       else
109         {
110           file = (char *) malloc(strlen(name) + strlen(suffix) + 2);
111           strcpy(file, name);
112           strcat(file, suffix);
113         }
114     }
115
116   if (file && *file)
117     return file;
118   else
119     return 0;
120 }
121
122
123 static const char * const prefs[] = {
124   "timeout",
125   "cycle",
126   "lock",
127   "lockVTs",
128   "lockTimeout",
129   "passwdTimeout",
130   "visualID",
131   "installColormap",
132   "verbose",
133   "timestamp",
134   "splash",                     /* not saved -- same as "splashDuration: 0" */
135   "splashDuration",
136   "helpURL",
137   "loadURL",
138   "nice",
139   "fade",
140   "unfade",
141   "fadeSeconds",
142   "fadeTicks",
143   "captureStderr",
144   "captureStdout",              /* not saved -- obsolete */
145   "font",
146   "",
147   "programs",
148   "",
149   "pointerPollTime",
150   "windowCreationTimeout",
151   "initialDelay",
152   "sgiSaverExtension",
153   "mitSaverExtension",
154   "xidleExtension",
155   "overlayStderr",
156   "overlayTextBackground",      /* not saved -- X resources only */
157   "overlayTextForeground",      /* not saved -- X resources only */
158   "bourneShell",                /* not saved -- X resources only */
159   0
160 };
161
162 static char *strip(char *s)
163 {
164   char *s2;
165   while (*s == '\t' || *s == ' ' || *s == '\r' || *s == '\n')
166     s++;
167   for (s2 = s; *s2; s2++)
168     ;
169   for (s2--; s2 >= s; s2--) 
170     if (*s2 == '\t' || *s2 == ' ' || *s2 == '\r' || *s2 =='\n') 
171       *s2 = 0;
172     else
173       break;
174   return s;
175 }
176
177 \f
178 /* Reading
179  */
180
181 static int
182 handle_entry (saver_info *si, const char *key, const char *value,
183               const char *filename, int line)
184 {
185   int i;
186   for (i = 0; prefs[i]; i++)
187     if (*prefs[i] && !strcasecmp(key, prefs[i]))
188       {
189         char *val = strdup(value);
190         char *spec = (char *) malloc(strlen(progclass) + strlen(prefs[i]) +10);
191         strcpy(spec, progclass);
192         strcat(spec, ".");
193         strcat(spec, prefs[i]);
194
195         XrmPutStringResource (&si->db, spec, val);
196
197         free(spec);
198         free(val);
199         return 0;
200       }
201
202   fprintf(stderr, "%s: %s:%d: unknown option \"%s\"\n",
203           blurb(), filename, line, key);
204   return 1;
205 }
206
207 int
208 read_init_file (saver_info *si)
209 {
210   time_t write_date = 0;
211   const char *name = init_file_name();
212   int line = 0;
213   struct stat st;
214   FILE *in;
215   int buf_size = 1024;
216   char *buf;
217
218   if (!name) return 0;
219
220   if (stat(name, &st) != 0)
221     {
222       si->init_file_date = 0;
223       return 0;
224     }
225
226   in = fopen(name, "r");
227   if (!in)
228     {
229       char *buf = (char *) malloc(1024 + strlen(name));
230       sprintf(buf, "%s: error reading %s", blurb(), name);
231       perror(buf);
232       free(buf);
233       return -1;
234     }
235
236   if (fstat (fileno(in), &st) == 0)
237     {
238       write_date = st.st_mtime;
239     }
240   else
241     {
242       char *buf = (char *) malloc(1024 + strlen(name));
243       sprintf(buf, "%s: couldn't re-stat %s", blurb(), name);
244       perror(buf);
245       free(buf);
246       return -1;
247     }
248
249   buf = (char *) malloc(buf_size);
250
251   while (fgets (buf, buf_size-1, in))
252     {
253       char *key, *value;
254       int L = strlen(buf);
255
256       line++;
257       while (L > 2 &&
258              (buf[L-1] != '\n' ||       /* whole line didn't fit in buffer */
259               buf[L-2] == '\\'))        /* or line ended with backslash */
260         {
261           if (buf[L-2] == '\\')         /* backslash-newline gets swallowed */
262             {
263               buf[L-2] = 0;
264               L -= 2;
265             }
266           buf_size += 1024;
267           buf = (char *) realloc(buf, buf_size);
268           if (!buf) exit(1);
269
270           line++;
271           if (!fgets (buf + L, buf_size-L-1, in))
272             break;
273           L = strlen(buf);
274         }
275
276       /* Now handle other backslash escapes. */
277       {
278         int i, j;
279         for (i = 0; buf[i]; i++)
280           if (buf[i] == '\\')
281             {
282               switch (buf[i+1])
283                 {
284                 case 'n': buf[i] = '\n'; break;
285                 case 'r': buf[i] = '\r'; break;
286                 case 't': buf[i] = '\t'; break;
287                 default:  buf[i] = buf[i+1]; break;
288                 }
289               for (j = i+2; buf[j]; j++)
290                 buf[j-1] = buf[j];
291               buf[j-1] = 0;
292             }
293       }
294
295       key = strip(buf);
296
297       if (*key == '#' || *key == '!' || *key == ';' ||
298           *key == '\n' || *key == 0)
299         continue;
300
301       value = strchr (key, ':');
302       if (!value)
303         {
304           fprintf(stderr, "%s: %s:%d: unparsable line: %s\n", blurb(),
305                   name, line, key);
306           continue;
307         }
308       else
309         {
310           *value++ = 0;
311           value = strip(value);
312         }
313
314       handle_entry (si, key, value, name, line);
315     }
316   free(buf);
317
318   si->init_file_date = write_date;
319   return 0;
320 }
321
322
323 int
324 maybe_reload_init_file (saver_info *si)
325 {
326   saver_preferences *p = &si->prefs;
327   const char *name = init_file_name();
328   struct stat st;
329   int status = 0;
330
331   if (!name) return 0;
332
333   if (stat(name, &st) != 0)
334     return 0;
335
336   if (si->init_file_date == st.st_mtime)
337     return 0;
338
339   if (p->verbose_p)
340     fprintf (stderr, "%s: file %s has changed, reloading.\n", blurb(), name);
341
342   status = read_init_file (si);
343   if (status == 0)
344     get_resources (si);
345   return status;
346 }
347
348 \f
349 /* Writing
350  */
351
352 static int
353 tab_to (FILE *out, int from, int to)
354 {
355   int tab_width = 8;
356   int to_mod = (to / tab_width) * tab_width;
357   while (from < to_mod)
358     {
359       fprintf(out, "\t");
360       from = (((from / tab_width) + 1) * tab_width);
361     }
362   while (from < to)
363     {
364       fprintf(out, " ");
365       from++;
366     }
367   return from;
368 }
369
370 static void
371 write_entry (FILE *out, const char *key, const char *value)
372 {
373   char *v = strdup(value ? value : "");
374   char *v2 = v;
375   char *nl = 0;
376   int col;
377   Bool do_visual_kludge = (!strcmp(key, "programs"));
378   Bool do_wrap = do_visual_kludge;
379   int tab = (do_visual_kludge ? 16 : 23);
380   int tab2 = 3;
381   Bool first = True;
382
383   fprintf(out, "%s:", key);
384   col = strlen(key) + 1;
385
386   while (1)
387     {
388       char *s;
389       if (first)
390         first = False;
391       else
392         {
393           col = tab_to(out, col, 75);
394           fprintf(out, " \\n\\\n");
395           col = 0;
396         }
397
398       v2 = strip(v2);
399       nl = strchr(v2, '\n');
400       if (nl)
401         *nl = 0;
402
403       s = (do_visual_kludge ? strpbrk(v2, " \t\n:") : 0);
404       if (s && *s == ':')
405         col = tab_to (out, col, tab2);
406       else
407         col = tab_to (out, col, tab);
408
409       if (do_wrap &&
410           strlen(v2) + col > 75)
411         {
412           int L = strlen(v2);
413           int start = 0;
414           int end = start;
415           while (start < L)
416             {
417               while (v2[end] == ' ' || v2[end] == '\t')
418                 end++;
419               while (v2[end] != ' ' && v2[end] != '\t' &&
420                      v2[end] != '\n' && v2[end] != 0)
421                 end++;
422               if (col + (end - start) >= 74)
423                 {
424                   col = tab_to (out, col, 75);
425                   fprintf(out, "   \\\n");
426                   col = tab_to (out, 0, tab + 2);
427                   while (v2[start] == ' ' || v2[start] == '\t')
428                     start++;
429                 }
430
431               while (start < end)
432                 {
433                   fputc(v2[start++], out);
434                   col++;
435                 }
436             }
437         }
438       else
439         {
440           fprintf (out, "%s", v2);
441           col += strlen(v2);
442         }
443
444       if (nl)
445         v2 = nl + 1;
446       else
447         break;
448     }
449
450   fprintf(out, "\n");
451   free(v);
452 }
453
454 int
455 write_init_file (saver_info *si)
456 {
457   const char *name = init_file_name();
458   const char *tmp_name = init_file_tmp_name();
459   struct stat st;
460   saver_preferences *p = &si->prefs;
461   int i, j;
462
463   /* Kludge, since these aren't in the saver_preferences struct as strings...
464    */
465   char *visual_name;
466   char *programs;
467   Bool capture_stderr_p;
468   Bool overlay_stderr_p;
469   char *stderr_font;
470   FILE *out;
471
472   if (!name) return 0;
473
474   if (si->dangerous_uid_p)
475     {
476       if (p->verbose_p)
477         {
478           fprintf (stderr, "%s: not writing \"%s\":\n", blurb(), name);
479           describe_uids (si, stderr);
480         }
481       return 0;
482     }
483
484   if (p->verbose_p)
485     fprintf (stderr, "%s: writing \"%s\".\n", blurb(), name);
486
487   unlink (tmp_name);
488   out = fopen(tmp_name, "w");
489   if (!out)
490     {
491       char *buf = (char *) malloc(1024 + strlen(name));
492       sprintf(buf, "%s: error writing %s", blurb(), name);
493       perror(buf);
494       free(buf);
495       return -1;
496     }
497
498   /* Give the new .xscreensaver file the same permissions as the old one;
499      except ensure that it is readable and writable by owner, and not
500      executable.
501    */
502   if (stat(name, &st) == 0)
503     {
504       mode_t mode = st.st_mode;
505       mode |= S_IRUSR | S_IWUSR;
506       mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH);
507       if (fchmod (fileno(out), mode) != 0)
508         {
509           char *buf = (char *) malloc(1024 + strlen(name));
510           sprintf (buf, "%s: error fchmodding %s to 0%o", blurb(),
511                    tmp_name, (unsigned int) mode);
512           perror(buf);
513           free(buf);
514           return -1;
515         }
516     }
517
518   /* Kludge, since these aren't in the saver_preferences struct... */
519   visual_name = get_string_resource ("visualID", "VisualID");
520   programs = 0;
521   capture_stderr_p = get_boolean_resource ("captureStderr", "Boolean");
522   overlay_stderr_p = get_boolean_resource ("overlayStderr", "Boolean");
523   stderr_font = get_string_resource ("font", "Font");
524
525   i = 0;
526   for (j = 0; j < p->screenhacks_count; j++)
527     i += strlen(p->screenhacks[j]) + 2;
528   {
529     char *ss = programs = (char *) malloc(i + 10);
530     *ss = 0;
531     for (j = 0; j < p->screenhacks_count; j++)
532       {
533         strcat(ss, p->screenhacks[j]);
534         ss += strlen(ss);
535         *ss++ = '\n';
536         *ss = 0;
537       }
538   }
539
540   {
541     struct passwd *p = getpwuid (getuid ());
542     char *whoami = (p && p->pw_name && *p->pw_name
543                     ? p->pw_name : "<unknown>");
544     fprintf (out, 
545              "# %s Preferences File\n"
546              "# Written by %s %s for %s on %s.\n"
547              "# http://www.jwz.org/xscreensaver/\n"
548              "\n",
549              progclass, progname, si->version,
550              (whoami ? whoami : "<unknown>"),
551              timestring());
552   }
553
554   for (j = 0; prefs[j]; j++)
555     {
556       char buf[255];
557       const char *pr = prefs[j];
558       enum pref_type { pref_str, pref_int, pref_bool, pref_time
559       } type = pref_str;
560       const char *s = 0;
561       int i = 0;
562       Bool b = False;
563       Time t = 0;
564
565       if (pr && !*pr)
566         {
567           fprintf(out, "\n");
568           continue;
569         }
570
571 # undef CHECK
572 # define CHECK(X) else if (!strcmp(pr, X))
573       if (!pr || !*pr)          ;
574       CHECK("timeout")          type = pref_time, t = p->timeout;
575       CHECK("cycle")            type = pref_time, t = p->cycle;
576       CHECK("lock")             type = pref_bool, b = p->lock_p;
577 # if 0 /* #### not ready yet */
578       CHECK("lockVTs")          type = pref_bool, b = p->lock_vt_p;
579 # else
580       CHECK("lockVTs")          continue;  /* don't save */
581 # endif
582       CHECK("lockTimeout")      type = pref_time, t = p->lock_timeout;
583       CHECK("passwdTimeout")    type = pref_time, t = p->passwd_timeout;
584       CHECK("visualID")         type = pref_str,  s =    visual_name;
585       CHECK("installColormap")  type = pref_bool, b = p->install_cmap_p;
586       CHECK("verbose")          type = pref_bool, b = p->verbose_p;
587       CHECK("timestamp")        type = pref_bool, b = p->timestamp_p;
588       CHECK("splash")           continue;  /* don't save */
589       CHECK("splashDuration")   type = pref_time, t = p->splash_duration;
590       CHECK("helpURL")          type = pref_str,  s = p->help_url;
591       CHECK("loadURL")          type = pref_str,  s = p->load_url_command;
592       CHECK("nice")             type = pref_int,  i = p->nice_inferior;
593       CHECK("fade")             type = pref_bool, b = p->fade_p;
594       CHECK("unfade")           type = pref_bool, b = p->unfade_p;
595       CHECK("fadeSeconds")      type = pref_time, t = p->fade_seconds;
596       CHECK("fadeTicks")        type = pref_int,  i = p->fade_ticks;
597       CHECK("captureStderr")    type = pref_bool, b =    capture_stderr_p;
598       CHECK("captureStdout")    continue;  /* don't save */
599       CHECK("font")             type = pref_str,  s =    stderr_font;
600       CHECK("programs")         type = pref_str,  s =    programs;
601       CHECK("pointerPollTime")  type = pref_time, t = p->pointer_timeout;
602       CHECK("windowCreationTimeout")type=pref_time,t= p->notice_events_timeout;
603       CHECK("initialDelay")     type = pref_time, t = p->initial_delay;
604       CHECK("sgiSaverExtension")type = pref_bool, b=p->use_sgi_saver_extension;
605       CHECK("mitSaverExtension")type = pref_bool, b=p->use_mit_saver_extension;
606       CHECK("xidleExtension")   type = pref_bool, b = p->use_xidle_extension;
607       CHECK("overlayStderr")    type = pref_bool, b = overlay_stderr_p;
608       CHECK("overlayTextBackground") continue;  /* don't save */
609       CHECK("overlayTextForeground") continue;  /* don't save */
610       CHECK("bourneShell")      continue;
611       else                      abort();
612 # undef CHECK
613
614       switch (type)
615         {
616         case pref_str:
617           break;
618         case pref_int:
619           sprintf(buf, "%d", i);
620           s = buf;
621           break;
622         case pref_bool:
623           s = b ? "True" : "False";
624           break;
625         case pref_time:
626           {
627             unsigned int hour = 0, min = 0, sec = (unsigned int) (t/1000);
628             if (sec >= 60)
629               {
630                 min += (sec / 60);
631                 sec %= 60;
632               }
633             if (min >= 60)
634               {
635                 hour += (min / 60);
636                 min %= 60;
637               }
638             sprintf (buf, "%u:%02u:%02u", hour, min, sec);
639             s = buf;
640           }
641           break;
642         default:
643           abort();
644           break;
645         }
646       write_entry (out, pr, s);
647     }
648
649   fprintf(out, "\n");
650
651   if (visual_name) free(visual_name);
652   if (stderr_font) free(stderr_font);
653   if (programs) free(programs);
654
655   if (fclose(out) == 0)
656     {
657       time_t write_date = 0;
658
659       if (stat(tmp_name, &st) == 0)
660         {
661           write_date = st.st_mtime;
662         }
663       else
664         {
665           char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
666           sprintf(buf, "%s: couldn't stat %s", blurb(), tmp_name);
667           perror(buf);
668           unlink (tmp_name);
669           free(buf);
670           return -1;
671         }
672
673       if (rename (tmp_name, name) != 0)
674         {
675           char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
676           sprintf(buf, "%s: error renaming %s to %s", blurb(), tmp_name, name);
677           perror(buf);
678           unlink (tmp_name);
679           free(buf);
680           return -1;
681         }
682       else
683         {
684           si->init_file_date = write_date;
685         }
686     }
687   else
688     {
689       char *buf = (char *) malloc(1024 + strlen(name));
690       sprintf(buf, "%s: error closing %s", blurb(), name);
691       perror(buf);
692       free(buf);
693       unlink (tmp_name);
694       return -1;
695     }
696
697   return 0;
698 }