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);
368 p->init_file_date = write_date;
374 init_file_changed_p (saver_preferences *p)
376 const char *name = init_file_name();
379 if (!name) return False;
381 if (stat(name, &st) != 0)
384 if (p->init_file_date == st.st_mtime)
395 tab_to (FILE *out, int from, int to)
398 int to_mod = (to / tab_width) * tab_width;
399 while (from < to_mod)
402 from = (((from / tab_width) + 1) * tab_width);
413 write_entry (FILE *out, const char *key, const char *value)
415 char *v = strdup(value ? value : "");
419 Bool do_visual_kludge = (!strcmp(key, "programs"));
420 Bool do_wrap = do_visual_kludge;
421 int tab = (do_visual_kludge ? 16 : 23);
425 fprintf(out, "%s:", key);
426 col = strlen(key) + 1;
431 Bool disabled_p = False;
434 nl = strchr(v2, '\n');
438 if (do_visual_kludge && *v2 == '-')
445 if (first && disabled_p)
452 col = tab_to(out, col, 75);
453 fprintf(out, " \\n\\\n");
463 s = (do_visual_kludge ? strpbrk(v2, " \t\n:") : 0);
465 col = tab_to (out, col, tab2);
467 col = tab_to (out, col, tab);
470 strlen(v2) + col > 75)
477 while (v2[end] == ' ' || v2[end] == '\t')
479 while (v2[end] != ' ' && v2[end] != '\t' &&
480 v2[end] != '\n' && v2[end] != 0)
482 if (col + (end - start) >= 74)
484 col = tab_to (out, col, 75);
485 fprintf(out, " \\\n");
486 col = tab_to (out, 0, tab + 2);
487 while (v2[start] == ' ' || v2[start] == '\t')
493 fputc(v2[start++], out);
500 fprintf (out, "%s", v2);
515 write_init_file (saver_preferences *p, const char *version_string)
517 const char *name = init_file_name();
518 const char *tmp_name = init_file_tmp_name();
519 char *n2 = chase_symlinks (name);
523 /* Kludge, since these aren't in the saver_preferences struct as strings...
527 Bool capture_stderr_p;
528 Bool overlay_stderr_p;
537 fprintf (stderr, "%s: writing \"%s\".\n", blurb(), name);
540 out = fopen(tmp_name, "w");
543 char *buf = (char *) malloc(1024 + strlen(name));
544 sprintf(buf, "%s: error writing \"%s\"", blurb(), name);
550 /* Give the new .xscreensaver file the same permissions as the old one;
551 except ensure that it is readable and writable by owner, and not
554 if (stat(name, &st) == 0)
556 mode_t mode = st.st_mode;
557 mode |= S_IRUSR | S_IWUSR;
558 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH);
559 if (fchmod (fileno(out), mode) != 0)
561 char *buf = (char *) malloc(1024 + strlen(name));
562 sprintf (buf, "%s: error fchmodding \"%s\" to 0%o", blurb(),
563 tmp_name, (unsigned int) mode);
570 /* Kludge, since these aren't in the saver_preferences struct... */
571 visual_name = get_string_resource ("visualID", "VisualID");
573 capture_stderr_p = get_boolean_resource ("captureStderr", "Boolean");
574 overlay_stderr_p = get_boolean_resource ("overlayStderr", "Boolean");
575 stderr_font = get_string_resource ("font", "Font");
578 for (j = 0; j < p->screenhacks_count; j++)
579 i += strlen(p->screenhacks[j]) + 2;
581 char *ss = programs = (char *) malloc(i + 10);
583 for (j = 0; j < p->screenhacks_count; j++)
585 strcat(ss, p->screenhacks[j]);
593 struct passwd *pw = getpwuid (getuid ());
594 char *whoami = (pw && pw->pw_name && *pw->pw_name
597 time_t now = time ((time_t *) 0);
598 char *timestr = (char *) ctime (&now);
599 char *nl = (char *) strchr (timestr, '\n');
602 "# %s Preferences File\n"
603 "# Written by %s %s for %s on %s.\n"
604 "# http://www.jwz.org/xscreensaver/\n"
606 progclass, progname, version_string, whoami, timestr);
609 for (j = 0; prefs[j]; j++)
612 const char *pr = prefs[j];
613 enum pref_type { pref_str, pref_int, pref_bool, pref_time
627 # define CHECK(X) else if (!strcmp(pr, X))
629 CHECK("timeout") type = pref_time, t = p->timeout;
630 CHECK("cycle") type = pref_time, t = p->cycle;
631 CHECK("lock") type = pref_bool, b = p->lock_p;
632 # if 0 /* #### not ready yet */
633 CHECK("lockVTs") type = pref_bool, b = p->lock_vt_p;
635 CHECK("lockVTs") continue; /* don't save */
637 CHECK("lockTimeout") type = pref_time, t = p->lock_timeout;
638 CHECK("passwdTimeout") type = pref_time, t = p->passwd_timeout;
639 CHECK("visualID") type = pref_str, s = visual_name;
640 CHECK("installColormap") type = pref_bool, b = p->install_cmap_p;
641 CHECK("verbose") type = pref_bool, b = p->verbose_p;
642 CHECK("timestamp") type = pref_bool, b = p->timestamp_p;
643 CHECK("splash") continue; /* don't save */
644 CHECK("splashDuration") type = pref_time, t = p->splash_duration;
645 CHECK("demoCommand") type = pref_str, s = p->demo_command;
646 CHECK("prefsCommand") type = pref_str, s = p->prefs_command;
647 CHECK("helpURL") type = pref_str, s = p->help_url;
648 CHECK("loadURL") type = pref_str, s = p->load_url_command;
649 CHECK("nice") type = pref_int, i = p->nice_inferior;
650 CHECK("fade") type = pref_bool, b = p->fade_p;
651 CHECK("unfade") type = pref_bool, b = p->unfade_p;
652 CHECK("fadeSeconds") type = pref_time, t = p->fade_seconds;
653 CHECK("fadeTicks") type = pref_int, i = p->fade_ticks;
654 CHECK("captureStderr") type = pref_bool, b = capture_stderr_p;
655 CHECK("captureStdout") continue; /* don't save */
656 CHECK("font") type = pref_str, s = stderr_font;
657 CHECK("programs") type = pref_str, s = programs;
658 CHECK("pointerPollTime") type = pref_time, t = p->pointer_timeout;
659 CHECK("windowCreationTimeout")type=pref_time,t= p->notice_events_timeout;
660 CHECK("initialDelay") type = pref_time, t = p->initial_delay;
661 CHECK("sgiSaverExtension")type = pref_bool, b=p->use_sgi_saver_extension;
662 CHECK("mitSaverExtension")type = pref_bool, b=p->use_mit_saver_extension;
663 CHECK("xidleExtension") type = pref_bool, b = p->use_xidle_extension;
664 CHECK("procInterrupts") type = pref_bool, b = p->use_proc_interrupts;
665 CHECK("overlayStderr") type = pref_bool, b = overlay_stderr_p;
666 CHECK("overlayTextBackground") continue; /* don't save */
667 CHECK("overlayTextForeground") continue; /* don't save */
668 CHECK("bourneShell") continue;
677 sprintf(buf, "%d", i);
681 s = b ? "True" : "False";
685 unsigned int hour = 0, min = 0, sec = (unsigned int) (t/1000);
696 sprintf (buf, "%u:%02u:%02u", hour, min, sec);
704 write_entry (out, pr, s);
709 if (visual_name) free(visual_name);
710 if (stderr_font) free(stderr_font);
711 if (programs) free(programs);
713 if (fclose(out) == 0)
715 time_t write_date = 0;
717 if (stat(tmp_name, &st) == 0)
719 write_date = st.st_mtime;
723 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
724 sprintf(buf, "%s: couldn't stat \"%s\"", blurb(), tmp_name);
731 if (rename (tmp_name, name) != 0)
733 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
734 sprintf(buf, "%s: error renaming \"%s\" to \"%s\"",
735 blurb(), tmp_name, name);
743 p->init_file_date = write_date;
745 /* Since the .xscreensaver file is used for IPC, let's try and make
746 sure that the bits actually land on the disk right away. */
752 char *buf = (char *) malloc(1024 + strlen(name));
753 sprintf(buf, "%s: error closing \"%s\"", blurb(), name);
765 /* Parsing the resource database
769 /* Populate `saver_preferences' with the contents of the resource database.
770 Note that this may be called multiple times -- it is re-run each time
771 the ~/.xscreensaver file is reloaded.
773 This function can be very noisy, since it issues resource syntax errors
777 load_init_file (saver_preferences *p)
779 static Bool first_time = True;
781 if (parse_init_file (p) != 0) /* file might have gone away */
782 if (!first_time) return;
786 p->xsync_p = get_boolean_resource ("synchronous", "Synchronous");
787 p->verbose_p = get_boolean_resource ("verbose", "Boolean");
788 p->timestamp_p = get_boolean_resource ("timestamp", "Boolean");
789 p->lock_p = get_boolean_resource ("lock", "Boolean");
790 p->lock_vt_p = get_boolean_resource ("lockVTs", "Boolean");
791 p->fade_p = get_boolean_resource ("fade", "Boolean");
792 p->unfade_p = get_boolean_resource ("unfade", "Boolean");
793 p->fade_seconds = 1000 * get_seconds_resource ("fadeSeconds", "Time");
794 p->fade_ticks = get_integer_resource ("fadeTicks", "Integer");
795 p->install_cmap_p = get_boolean_resource ("installColormap", "Boolean");
796 p->nice_inferior = get_integer_resource ("nice", "Nice");
798 p->initial_delay = 1000 * get_seconds_resource ("initialDelay", "Time");
799 p->splash_duration = 1000 * get_seconds_resource ("splashDuration", "Time");
800 p->timeout = 1000 * get_minutes_resource ("timeout", "Time");
801 p->lock_timeout = 1000 * get_minutes_resource ("lockTimeout", "Time");
802 p->cycle = 1000 * get_minutes_resource ("cycle", "Time");
803 p->passwd_timeout = 1000 * get_seconds_resource ("passwdTimeout", "Time");
804 p->pointer_timeout = 1000 * get_seconds_resource ("pointerPollTime", "Time");
805 p->notice_events_timeout = 1000*get_seconds_resource("windowCreationTimeout",
807 p->shell = get_string_resource ("bourneShell", "BourneShell");
809 p->demo_command = get_string_resource("demoCommand", "URL");
810 p->prefs_command = get_string_resource("prefsCommand", "URL");
811 p->help_url = get_string_resource("helpURL", "URL");
812 p->load_url_command = get_string_resource("loadURL", "LoadURL");
816 if ((s = get_string_resource ("splash", "Boolean")))
817 if (!get_boolean_resource("splash", "Boolean"))
818 p->splash_duration = 0;
822 p->use_xidle_extension = get_boolean_resource ("xidleExtension","Boolean");
823 p->use_mit_saver_extension = get_boolean_resource ("mitSaverExtension",
825 p->use_sgi_saver_extension = get_boolean_resource ("sgiSaverExtension",
827 p->use_proc_interrupts = get_boolean_resource ("procInterrupts", "Boolean");
829 /* Throttle the various timeouts to reasonable values.
831 if (p->passwd_timeout <= 0) p->passwd_timeout = 30000; /* 30 secs */
832 if (p->timeout < 10000) p->timeout = 10000; /* 10 secs */
833 if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */
834 if (p->pointer_timeout <= 0) p->pointer_timeout = 5000; /* 5 secs */
835 if (p->notice_events_timeout <= 0)
836 p->notice_events_timeout = 10000; /* 10 secs */
837 if (p->fade_seconds <= 0 || p->fade_ticks <= 0)
839 if (! p->fade_p) p->unfade_p = False;
841 p->watchdog_timeout = p->cycle * 0.6;
842 if (p->watchdog_timeout < 30000) p->watchdog_timeout = 30000; /* 30 secs */
843 if (p->watchdog_timeout > 3600000) p->watchdog_timeout = 3600000; /* 1 hr */
851 p->timestamp_p = True;
852 p->initial_delay = 0;
857 /* Parsing the programs resource.
861 reformat_hack (const char *hack)
864 const char *in = hack;
866 char *h2 = (char *) malloc(strlen(in) + indent + 2);
868 Bool disabled_p = False;
870 while (isspace(*in)) in++; /* skip whitespace */
872 if (*in == '-') /* Handle a leading "-". */
879 while (isspace(*in)) in++;
887 while (*in && !isspace(*in) && *in != ':')
888 *out++ = *in++; /* snarf first token */
889 while (isspace(*in)) in++; /* skip whitespace */
892 *out++ = *in++; /* copy colon */
896 out = h2 + 2; /* reset to beginning */
901 while (isspace(*in)) in++; /* skip whitespace */
902 for (i = strlen(h2); i < indent; i++) /* indent */
905 /* copy the rest of the line. */
908 /* shrink all whitespace to one space, for the benefit of the "demo"
909 mode display. We only do this when we can easily tell that the
910 whitespace is not significant (no shell metachars).
914 case '\'': case '"': case '`': case '\\':
916 /* Metachars are scary. Copy the rest of the line unchanged. */
923 while (*in == ' ' || *in == '\t')
935 /* strip trailing whitespace. */
937 while (out > h2 && (*out == ' ' || *out == '\t' || *out == '\n'))
945 get_screenhacks (saver_preferences *p)
953 d = get_string_resource ("monoPrograms", "MonoPrograms");
954 if (d && !*d) { free(d); d = 0; }
956 d = get_string_resource ("colorPrograms", "ColorPrograms");
957 if (d && !*d) { free(d); d = 0; }
962 "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
963 see the manual for details.\n", blurb());
967 d = get_string_resource ("programs", "Programs");
971 for (i = 0; i < p->screenhacks_count; i++)
972 if (p->screenhacks[i])
973 free (p->screenhacks[i]);
974 free(p->screenhacks);
980 p->screenhacks_count = 0;
988 /* Count up the number of newlines (which will be equal to or larger than
989 the number of hacks.)
992 for (i = 0; d[i]; i++)
997 p->screenhacks = (char **) calloc (sizeof (char *), i+1);
999 /* Iterate over the lines in `d' (the string with newlines)
1000 and make new strings to stuff into the `screenhacks' array.
1002 p->screenhacks_count = 0;
1003 while (start < size)
1005 /* skip forward over whitespace. */
1006 while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n')
1009 /* skip forward to newline or end of string. */
1011 while (d[end] != 0 && d[end] != '\n')
1014 /* null terminate. */
1017 p->screenhacks[p->screenhacks_count++] = reformat_hack (d + start);
1018 if (p->screenhacks_count >= i)
1024 if (p->screenhacks_count == 0)
1026 free (p->screenhacks);