1 /* dotfile.c --- management of the ~/.xscreensaver file.
2 * xscreensaver, Copyright (c) 1998 Jamie Zawinski <jwz@jwz.org>
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
30 #include <X11/Xresource.h>
39 /* This file doesn't need the Xt headers, so stub these types out... */
41 #define XtAppContext void*
42 #define XtIntervalId void*
43 #define XtPointer void*
47 /* Just in case there's something pathological about stat.h... */
49 # define S_IRUSR 00400
52 # define S_IWUSR 00200
55 # define S_IXUSR 00100
58 # define S_IXGRP 00010
61 # define S_IXOTH 00001
66 #include "resources.h"
69 extern char *progname;
70 extern char *progclass;
71 extern const char *blurb (void);
75 static void get_screenhacks (saver_preferences *p);
79 chase_symlinks (const char *file)
85 if (realpath (file, buf))
88 sprintf (buf, "%s: realpath", blurb());
91 # endif /* HAVE_REALPATH */
99 static char *file = 0;
103 struct passwd *p = getpwuid (getuid ());
105 if (!p || !p->pw_name || !*p->pw_name)
107 fprintf (stderr, "%s: couldn't get user info of uid %d\n",
111 else if (!p->pw_dir || !*p->pw_dir)
113 fprintf (stderr, "%s: couldn't get home directory of \"%s\"\n",
114 blurb(), (p->pw_name ? p->pw_name : "???"));
119 const char *home = p->pw_dir;
120 const char *name = ".xscreensaver";
121 file = (char *) malloc(strlen(home) + strlen(name) + 2);
123 if (!*home || home[strlen(home)-1] != '/')
137 init_file_tmp_name (void)
139 static char *file = 0;
142 const char *name = init_file_name();
143 const char *suffix = ".tmp";
145 char *n2 = chase_symlinks (name);
152 file = (char *) malloc(strlen(name) + strlen(suffix) + 2);
154 strcat(file, suffix);
167 static const char * const prefs[] = {
178 "splash", /* not saved -- same as "splashDuration: 0" */
190 "captureStdout", /* not saved -- obsolete */
196 "windowCreationTimeout",
203 "overlayTextBackground", /* not saved -- X resources only */
204 "overlayTextForeground", /* not saved -- X resources only */
205 "bourneShell", /* not saved -- X resources only */
213 while (*s == '\t' || *s == ' ' || *s == '\r' || *s == '\n')
215 for (s2 = s; *s2; s2++)
217 for (s2--; s2 >= s; s2--)
218 if (*s2 == '\t' || *s2 == ' ' || *s2 == '\r' || *s2 =='\n')
230 handle_entry (XrmDatabase *db, const char *key, const char *value,
231 const char *filename, int line)
234 for (i = 0; prefs[i]; i++)
235 if (*prefs[i] && !strcasecmp(key, prefs[i]))
237 char *val = strdup(value);
238 char *spec = (char *) malloc(strlen(progclass) + strlen(prefs[i]) +10);
239 strcpy(spec, progclass);
241 strcat(spec, prefs[i]);
243 XrmPutStringResource (db, spec, val);
250 fprintf(stderr, "%s: %s:%d: unknown option \"%s\"\n",
251 blurb(), filename, line, key);
257 parse_init_file (saver_preferences *p)
259 time_t write_date = 0;
260 const char *name = init_file_name();
269 if (stat(name, &st) != 0)
271 p->init_file_date = 0;
275 in = fopen(name, "r");
278 char *buf = (char *) malloc(1024 + strlen(name));
279 sprintf(buf, "%s: error reading \"%s\"", blurb(), name);
285 if (fstat (fileno(in), &st) == 0)
287 write_date = st.st_mtime;
291 char *buf = (char *) malloc(1024 + strlen(name));
292 sprintf(buf, "%s: couldn't re-stat \"%s\"", blurb(), name);
298 buf = (char *) malloc(buf_size);
300 while (fgets (buf, buf_size-1, in))
307 (buf[L-1] != '\n' || /* whole line didn't fit in buffer */
308 buf[L-2] == '\\')) /* or line ended with backslash */
310 if (buf[L-2] == '\\') /* backslash-newline gets swallowed */
316 buf = (char *) realloc(buf, buf_size);
320 if (!fgets (buf + L, buf_size-L-1, in))
325 /* Now handle other backslash escapes. */
328 for (i = 0; buf[i]; i++)
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;
338 for (j = i+2; buf[j]; j++)
346 if (*key == '#' || *key == '!' || *key == ';' ||
347 *key == '\n' || *key == 0)
350 value = strchr (key, ':');
353 fprintf(stderr, "%s: %s:%d: unparsable line: %s\n", blurb(),
360 value = strip(value);
364 handle_entry (&p->db, key, value, name, line);
369 p->init_file_date = write_date;
375 init_file_changed_p (saver_preferences *p)
377 const char *name = init_file_name();
380 if (!name) return False;
382 if (stat(name, &st) != 0)
385 if (p->init_file_date == st.st_mtime)
396 tab_to (FILE *out, int from, int to)
399 int to_mod = (to / tab_width) * tab_width;
400 while (from < to_mod)
403 from = (((from / tab_width) + 1) * tab_width);
414 write_entry (FILE *out, const char *key, const char *value)
416 char *v = strdup(value ? value : "");
420 Bool do_visual_kludge = (!strcmp(key, "programs"));
421 Bool do_wrap = do_visual_kludge;
422 int tab = (do_visual_kludge ? 16 : 23);
426 fprintf(out, "%s:", key);
427 col = strlen(key) + 1;
432 Bool disabled_p = False;
435 nl = strchr(v2, '\n');
439 if (do_visual_kludge && *v2 == '-')
446 if (first && disabled_p)
453 col = tab_to(out, col, 75);
454 fprintf(out, " \\n\\\n");
464 s = (do_visual_kludge ? strpbrk(v2, " \t\n:") : 0);
466 col = tab_to (out, col, tab2);
468 col = tab_to (out, col, tab);
471 strlen(v2) + col > 75)
478 while (v2[end] == ' ' || v2[end] == '\t')
480 while (v2[end] != ' ' && v2[end] != '\t' &&
481 v2[end] != '\n' && v2[end] != 0)
483 if (col + (end - start) >= 74)
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')
494 fputc(v2[start++], out);
501 fprintf (out, "%s", v2);
516 write_init_file (saver_preferences *p, const char *version_string)
518 const char *name = init_file_name();
519 const char *tmp_name = init_file_tmp_name();
520 char *n2 = chase_symlinks (name);
524 /* Kludge, since these aren't in the saver_preferences struct as strings...
528 Bool capture_stderr_p;
529 Bool overlay_stderr_p;
538 fprintf (stderr, "%s: writing \"%s\".\n", blurb(), name);
541 out = fopen(tmp_name, "w");
544 char *buf = (char *) malloc(1024 + strlen(name));
545 sprintf(buf, "%s: error writing \"%s\"", blurb(), name);
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
555 if (stat(name, &st) == 0)
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)
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);
571 /* Kludge, since these aren't in the saver_preferences struct... */
572 visual_name = get_string_resource ("visualID", "VisualID");
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");
579 for (j = 0; j < p->screenhacks_count; j++)
580 i += strlen(p->screenhacks[j]) + 2;
582 char *ss = programs = (char *) malloc(i + 10);
584 for (j = 0; j < p->screenhacks_count; j++)
586 strcat(ss, p->screenhacks[j]);
594 struct passwd *pw = getpwuid (getuid ());
595 char *whoami = (pw && pw->pw_name && *pw->pw_name
598 time_t now = time ((time_t *) 0);
599 char *timestr = (char *) ctime (&now);
600 char *nl = (char *) strchr (timestr, '\n');
603 "# %s Preferences File\n"
604 "# Written by %s %s for %s on %s.\n"
605 "# http://www.jwz.org/xscreensaver/\n"
607 progclass, progname, version_string, whoami, timestr);
610 for (j = 0; prefs[j]; j++)
613 const char *pr = prefs[j];
614 enum pref_type { pref_str, pref_int, pref_bool, pref_time
628 # define CHECK(X) else if (!strcmp(pr, X))
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;
636 CHECK("lockVTs") continue; /* don't save */
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;
678 sprintf(buf, "%d", i);
682 s = b ? "True" : "False";
686 unsigned int hour = 0, min = 0, sec = (unsigned int) (t/1000);
697 sprintf (buf, "%u:%02u:%02u", hour, min, sec);
705 write_entry (out, pr, s);
710 if (visual_name) free(visual_name);
711 if (stderr_font) free(stderr_font);
712 if (programs) free(programs);
714 if (fclose(out) == 0)
716 time_t write_date = 0;
718 if (stat(tmp_name, &st) == 0)
720 write_date = st.st_mtime;
724 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
725 sprintf(buf, "%s: couldn't stat \"%s\"", blurb(), tmp_name);
732 if (rename (tmp_name, name) != 0)
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);
744 p->init_file_date = write_date;
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. */
753 char *buf = (char *) malloc(1024 + strlen(name));
754 sprintf(buf, "%s: error closing \"%s\"", blurb(), name);
766 /* Parsing the resource database
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.
774 This function can be very noisy, since it issues resource syntax errors
778 load_init_file (saver_preferences *p)
780 static Bool first_time = True;
782 if (parse_init_file (p) != 0) /* file might have gone away */
783 if (!first_time) return;
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");
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",
808 p->shell = get_string_resource ("bourneShell", "BourneShell");
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");
817 if ((s = get_string_resource ("splash", "Boolean")))
818 if (!get_boolean_resource("splash", "Boolean"))
819 p->splash_duration = 0;
823 p->use_xidle_extension = get_boolean_resource ("xidleExtension","Boolean");
824 p->use_mit_saver_extension = get_boolean_resource ("mitSaverExtension",
826 p->use_sgi_saver_extension = get_boolean_resource ("sgiSaverExtension",
828 p->use_proc_interrupts = get_boolean_resource ("procInterrupts", "Boolean");
830 /* Throttle the various timeouts to reasonable values.
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)
840 if (! p->fade_p) p->unfade_p = False;
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 */
852 p->timestamp_p = True;
853 p->initial_delay = 0;
858 /* Parsing the programs resource.
862 reformat_hack (const char *hack)
865 const char *in = hack;
867 char *h2 = (char *) malloc(strlen(in) + indent + 2);
869 Bool disabled_p = False;
871 while (isspace(*in)) in++; /* skip whitespace */
873 if (*in == '-') /* Handle a leading "-". */
880 while (isspace(*in)) in++;
888 while (*in && !isspace(*in) && *in != ':')
889 *out++ = *in++; /* snarf first token */
890 while (isspace(*in)) in++; /* skip whitespace */
893 *out++ = *in++; /* copy colon */
897 out = h2 + 2; /* reset to beginning */
902 while (isspace(*in)) in++; /* skip whitespace */
903 for (i = strlen(h2); i < indent; i++) /* indent */
906 /* copy the rest of the line. */
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).
915 case '\'': case '"': case '`': case '\\':
917 /* Metachars are scary. Copy the rest of the line unchanged. */
924 while (*in == ' ' || *in == '\t')
936 /* strip trailing whitespace. */
938 while (out > h2 && (*out == ' ' || *out == '\t' || *out == '\n'))
946 get_screenhacks (saver_preferences *p)
954 d = get_string_resource ("monoPrograms", "MonoPrograms");
955 if (d && !*d) { free(d); d = 0; }
957 d = get_string_resource ("colorPrograms", "ColorPrograms");
958 if (d && !*d) { free(d); d = 0; }
963 "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
964 see the manual for details.\n", blurb());
968 d = get_string_resource ("programs", "Programs");
972 for (i = 0; i < p->screenhacks_count; i++)
973 if (p->screenhacks[i])
974 free (p->screenhacks[i]);
975 free(p->screenhacks);
981 p->screenhacks_count = 0;
989 /* Count up the number of newlines (which will be equal to or larger than
990 the number of hacks.)
993 for (i = 0; d[i]; i++)
998 p->screenhacks = (char **) calloc (sizeof (char *), i+1);
1000 /* Iterate over the lines in `d' (the string with newlines)
1001 and make new strings to stuff into the `screenhacks' array.
1003 p->screenhacks_count = 0;
1004 while (start < size)
1006 /* skip forward over whitespace. */
1007 while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n')
1010 /* skip forward to newline or end of string. */
1012 while (d[end] != 0 && d[end] != '\n')
1015 /* null terminate. */
1018 p->screenhacks[p->screenhacks_count++] = reformat_hack (d + start);
1019 if (p->screenhacks_count >= i)
1025 if (p->screenhacks_count == 0)
1027 free (p->screenhacks);