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);
81 static char *file = 0;
85 struct passwd *p = getpwuid (getuid ());
87 if (!p || !p->pw_name || !*p->pw_name)
89 fprintf (stderr, "%s: couldn't get user info of uid %d\n",
93 else if (!p->pw_dir || !*p->pw_dir)
95 fprintf (stderr, "%s: couldn't get home directory of \"%s\"\n",
96 blurb(), (p->pw_name ? p->pw_name : "???"));
101 const char *home = p->pw_dir;
102 const char *name = ".xscreensaver";
103 file = (char *) malloc(strlen(home) + strlen(name) + 2);
105 if (!*home || home[strlen(home)-1] != '/')
119 init_file_tmp_name (void)
121 static char *file = 0;
124 const char *name = init_file_name();
125 const char *suffix = ".tmp";
130 file = (char *) malloc(strlen(name) + strlen(suffix) + 2);
132 strcat(file, suffix);
143 static const char * const prefs[] = {
154 "splash", /* not saved -- same as "splashDuration: 0" */
166 "captureStdout", /* not saved -- obsolete */
172 "windowCreationTimeout",
178 "overlayTextBackground", /* not saved -- X resources only */
179 "overlayTextForeground", /* not saved -- X resources only */
180 "bourneShell", /* not saved -- X resources only */
188 while (*s == '\t' || *s == ' ' || *s == '\r' || *s == '\n')
190 for (s2 = s; *s2; s2++)
192 for (s2--; s2 >= s; s2--)
193 if (*s2 == '\t' || *s2 == ' ' || *s2 == '\r' || *s2 =='\n')
205 handle_entry (XrmDatabase *db, const char *key, const char *value,
206 const char *filename, int line)
209 for (i = 0; prefs[i]; i++)
210 if (*prefs[i] && !strcasecmp(key, prefs[i]))
212 char *val = strdup(value);
213 char *spec = (char *) malloc(strlen(progclass) + strlen(prefs[i]) +10);
214 strcpy(spec, progclass);
216 strcat(spec, prefs[i]);
218 XrmPutStringResource (db, spec, val);
225 fprintf(stderr, "%s: %s:%d: unknown option \"%s\"\n",
226 blurb(), filename, line, key);
232 parse_init_file (saver_preferences *p)
234 time_t write_date = 0;
235 const char *name = init_file_name();
244 if (stat(name, &st) != 0)
246 p->init_file_date = 0;
250 in = fopen(name, "r");
253 char *buf = (char *) malloc(1024 + strlen(name));
254 sprintf(buf, "%s: error reading \"%s\"", blurb(), name);
260 if (fstat (fileno(in), &st) == 0)
262 write_date = st.st_mtime;
266 char *buf = (char *) malloc(1024 + strlen(name));
267 sprintf(buf, "%s: couldn't re-stat \"%s\"", blurb(), name);
273 buf = (char *) malloc(buf_size);
275 while (fgets (buf, buf_size-1, in))
282 (buf[L-1] != '\n' || /* whole line didn't fit in buffer */
283 buf[L-2] == '\\')) /* or line ended with backslash */
285 if (buf[L-2] == '\\') /* backslash-newline gets swallowed */
291 buf = (char *) realloc(buf, buf_size);
295 if (!fgets (buf + L, buf_size-L-1, in))
300 /* Now handle other backslash escapes. */
303 for (i = 0; buf[i]; i++)
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;
313 for (j = i+2; buf[j]; j++)
321 if (*key == '#' || *key == '!' || *key == ';' ||
322 *key == '\n' || *key == 0)
325 value = strchr (key, ':');
328 fprintf(stderr, "%s: %s:%d: unparsable line: %s\n", blurb(),
335 value = strip(value);
339 handle_entry (&p->db, key, value, name, line);
343 p->init_file_date = write_date;
349 init_file_changed_p (saver_preferences *p)
351 const char *name = init_file_name();
354 if (!name) return False;
356 if (stat(name, &st) != 0)
359 if (p->init_file_date == st.st_mtime)
370 tab_to (FILE *out, int from, int to)
373 int to_mod = (to / tab_width) * tab_width;
374 while (from < to_mod)
377 from = (((from / tab_width) + 1) * tab_width);
388 write_entry (FILE *out, const char *key, const char *value)
390 char *v = strdup(value ? value : "");
394 Bool do_visual_kludge = (!strcmp(key, "programs"));
395 Bool do_wrap = do_visual_kludge;
396 int tab = (do_visual_kludge ? 16 : 23);
400 fprintf(out, "%s:", key);
401 col = strlen(key) + 1;
406 Bool disabled_p = False;
409 nl = strchr(v2, '\n');
413 if (do_visual_kludge && *v2 == '-')
420 if (first && disabled_p)
427 col = tab_to(out, col, 75);
428 fprintf(out, " \\n\\\n");
438 s = (do_visual_kludge ? strpbrk(v2, " \t\n:") : 0);
440 col = tab_to (out, col, tab2);
442 col = tab_to (out, col, tab);
445 strlen(v2) + col > 75)
452 while (v2[end] == ' ' || v2[end] == '\t')
454 while (v2[end] != ' ' && v2[end] != '\t' &&
455 v2[end] != '\n' && v2[end] != 0)
457 if (col + (end - start) >= 74)
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')
468 fputc(v2[start++], out);
475 fprintf (out, "%s", v2);
490 write_init_file (saver_preferences *p, const char *version_string)
492 const char *name = init_file_name();
493 const char *tmp_name = init_file_tmp_name();
497 /* Kludge, since these aren't in the saver_preferences struct as strings...
501 Bool capture_stderr_p;
502 Bool overlay_stderr_p;
509 fprintf (stderr, "%s: writing \"%s\".\n", blurb(), name);
512 out = fopen(tmp_name, "w");
515 char *buf = (char *) malloc(1024 + strlen(name));
516 sprintf(buf, "%s: error writing \"%s\"", blurb(), name);
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
526 if (stat(name, &st) == 0)
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)
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);
542 /* Kludge, since these aren't in the saver_preferences struct... */
543 visual_name = get_string_resource ("visualID", "VisualID");
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");
550 for (j = 0; j < p->screenhacks_count; j++)
551 i += strlen(p->screenhacks[j]) + 2;
553 char *ss = programs = (char *) malloc(i + 10);
555 for (j = 0; j < p->screenhacks_count; j++)
557 strcat(ss, p->screenhacks[j]);
565 struct passwd *pw = getpwuid (getuid ());
566 char *whoami = (pw && pw->pw_name && *pw->pw_name
569 time_t now = time ((time_t *) 0);
570 char *timestr = (char *) ctime (&now);
571 char *nl = (char *) strchr (timestr, '\n');
574 "# %s Preferences File\n"
575 "# Written by %s %s for %s on %s.\n"
576 "# http://www.jwz.org/xscreensaver/\n"
578 progclass, progname, version_string, whoami, timestr);
581 for (j = 0; prefs[j]; j++)
584 const char *pr = prefs[j];
585 enum pref_type { pref_str, pref_int, pref_bool, pref_time
599 # define CHECK(X) else if (!strcmp(pr, X))
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;
607 CHECK("lockVTs") continue; /* don't save */
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;
648 sprintf(buf, "%d", i);
652 s = b ? "True" : "False";
656 unsigned int hour = 0, min = 0, sec = (unsigned int) (t/1000);
667 sprintf (buf, "%u:%02u:%02u", hour, min, sec);
675 write_entry (out, pr, s);
680 if (visual_name) free(visual_name);
681 if (stderr_font) free(stderr_font);
682 if (programs) free(programs);
684 if (fclose(out) == 0)
686 time_t write_date = 0;
688 if (stat(tmp_name, &st) == 0)
690 write_date = st.st_mtime;
694 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
695 sprintf(buf, "%s: couldn't stat \"%s\"", blurb(), tmp_name);
702 if (rename (tmp_name, name) != 0)
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);
714 p->init_file_date = write_date;
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. */
723 char *buf = (char *) malloc(1024 + strlen(name));
724 sprintf(buf, "%s: error closing \"%s\"", blurb(), name);
733 /* Parsing the resource database
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.
741 This function can be very noisy, since it issues resource syntax errors
745 load_init_file (saver_preferences *p)
747 static Bool first_time = True;
749 if (parse_init_file (p) != 0) /* file might have gone away */
750 if (!first_time) return;
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");
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",
775 p->shell = get_string_resource ("bourneShell", "BourneShell");
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");
784 if ((s = get_string_resource ("splash", "Boolean")))
785 if (!get_boolean_resource("splash", "Boolean"))
786 p->splash_duration = 0;
790 p->use_xidle_extension = get_boolean_resource ("xidleExtension","Boolean");
791 p->use_mit_saver_extension = get_boolean_resource ("mitSaverExtension",
793 p->use_sgi_saver_extension = get_boolean_resource ("sgiSaverExtension",
796 /* Throttle the various timeouts to reasonable values.
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)
807 if (! p->fade_p) p->unfade_p = False;
809 if (p->verbose_p && !p->fading_possible_p && (p->fade_p || p->unfade_p))
811 fprintf (stderr, "%s: there are no PseudoColor or GrayScale visuals.\n",
813 fprintf (stderr, "%s: ignoring the request for fading/unfading.\n",
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 */
827 p->timestamp_p = True;
828 p->initial_delay = 0;
833 /* Parsing the programs resource.
837 reformat_hack (const char *hack)
840 const char *in = hack;
842 char *h2 = (char *) malloc(strlen(in) + indent + 2);
844 Bool disabled_p = False;
846 while (isspace(*in)) in++; /* skip whitespace */
848 if (*in == '-') /* Handle a leading "-". */
855 while (isspace(*in)) in++;
863 while (*in && !isspace(*in) && *in != ':')
864 *out++ = *in++; /* snarf first token */
865 while (isspace(*in)) in++; /* skip whitespace */
868 *out++ = *in++; /* copy colon */
872 out = h2 + 2; /* reset to beginning */
877 while (isspace(*in)) in++; /* skip whitespace */
878 for (i = strlen(h2); i < indent; i++) /* indent */
881 /* copy the rest of the line. */
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).
890 case '\'': case '"': case '`': case '\\':
892 /* Metachars are scary. Copy the rest of the line unchanged. */
899 while (*in == ' ' || *in == '\t')
911 /* strip trailing whitespace. */
913 while (out > h2 && (*out == ' ' || *out == '\t' || *out == '\n'))
921 get_screenhacks (saver_preferences *p)
929 d = get_string_resource ("monoPrograms", "MonoPrograms");
930 if (d && !*d) { free(d); d = 0; }
932 d = get_string_resource ("colorPrograms", "ColorPrograms");
933 if (d && !*d) { free(d); d = 0; }
938 "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
939 see the manual for details.\n", blurb());
943 d = get_string_resource ("programs", "Programs");
947 for (i = 0; i < p->screenhacks_count; i++)
948 if (p->screenhacks[i])
949 free (p->screenhacks[i]);
950 free(p->screenhacks);
956 p->screenhacks_count = 0;
964 /* Count up the number of newlines (which will be equal to or larger than
965 the number of hacks.)
968 for (i = 0; d[i]; i++)
973 p->screenhacks = (char **) calloc (sizeof (char *), i+1);
975 /* Iterate over the lines in `d' (the string with newlines)
976 and make new strings to stuff into the `screenhacks' array.
978 p->screenhacks_count = 0;
981 /* skip forward over whitespace. */
982 while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n')
985 /* skip forward to newline or end of string. */
987 while (d[end] != 0 && d[end] != '\n')
990 /* null terminate. */
993 p->screenhacks[p->screenhacks_count++] = reformat_hack (d + start);
994 if (p->screenhacks_count >= i)
1000 if (p->screenhacks_count == 0)
1002 free (p->screenhacks);