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",
179 "overlayTextBackground", /* not saved -- X resources only */
180 "overlayTextForeground", /* not saved -- X resources only */
181 "bourneShell", /* not saved -- X resources only */
189 while (*s == '\t' || *s == ' ' || *s == '\r' || *s == '\n')
191 for (s2 = s; *s2; s2++)
193 for (s2--; s2 >= s; s2--)
194 if (*s2 == '\t' || *s2 == ' ' || *s2 == '\r' || *s2 =='\n')
206 handle_entry (XrmDatabase *db, const char *key, const char *value,
207 const char *filename, int line)
210 for (i = 0; prefs[i]; i++)
211 if (*prefs[i] && !strcasecmp(key, prefs[i]))
213 char *val = strdup(value);
214 char *spec = (char *) malloc(strlen(progclass) + strlen(prefs[i]) +10);
215 strcpy(spec, progclass);
217 strcat(spec, prefs[i]);
219 XrmPutStringResource (db, spec, val);
226 fprintf(stderr, "%s: %s:%d: unknown option \"%s\"\n",
227 blurb(), filename, line, key);
233 parse_init_file (saver_preferences *p)
235 time_t write_date = 0;
236 const char *name = init_file_name();
245 if (stat(name, &st) != 0)
247 p->init_file_date = 0;
251 in = fopen(name, "r");
254 char *buf = (char *) malloc(1024 + strlen(name));
255 sprintf(buf, "%s: error reading \"%s\"", blurb(), name);
261 if (fstat (fileno(in), &st) == 0)
263 write_date = st.st_mtime;
267 char *buf = (char *) malloc(1024 + strlen(name));
268 sprintf(buf, "%s: couldn't re-stat \"%s\"", blurb(), name);
274 buf = (char *) malloc(buf_size);
276 while (fgets (buf, buf_size-1, in))
283 (buf[L-1] != '\n' || /* whole line didn't fit in buffer */
284 buf[L-2] == '\\')) /* or line ended with backslash */
286 if (buf[L-2] == '\\') /* backslash-newline gets swallowed */
292 buf = (char *) realloc(buf, buf_size);
296 if (!fgets (buf + L, buf_size-L-1, in))
301 /* Now handle other backslash escapes. */
304 for (i = 0; buf[i]; i++)
309 case 'n': buf[i] = '\n'; break;
310 case 'r': buf[i] = '\r'; break;
311 case 't': buf[i] = '\t'; break;
312 default: buf[i] = buf[i+1]; break;
314 for (j = i+2; buf[j]; j++)
322 if (*key == '#' || *key == '!' || *key == ';' ||
323 *key == '\n' || *key == 0)
326 value = strchr (key, ':');
329 fprintf(stderr, "%s: %s:%d: unparsable line: %s\n", blurb(),
336 value = strip(value);
340 handle_entry (&p->db, key, value, name, line);
344 p->init_file_date = write_date;
350 init_file_changed_p (saver_preferences *p)
352 const char *name = init_file_name();
355 if (!name) return False;
357 if (stat(name, &st) != 0)
360 if (p->init_file_date == st.st_mtime)
371 tab_to (FILE *out, int from, int to)
374 int to_mod = (to / tab_width) * tab_width;
375 while (from < to_mod)
378 from = (((from / tab_width) + 1) * tab_width);
389 write_entry (FILE *out, const char *key, const char *value)
391 char *v = strdup(value ? value : "");
395 Bool do_visual_kludge = (!strcmp(key, "programs"));
396 Bool do_wrap = do_visual_kludge;
397 int tab = (do_visual_kludge ? 16 : 23);
401 fprintf(out, "%s:", key);
402 col = strlen(key) + 1;
407 Bool disabled_p = False;
410 nl = strchr(v2, '\n');
414 if (do_visual_kludge && *v2 == '-')
421 if (first && disabled_p)
428 col = tab_to(out, col, 75);
429 fprintf(out, " \\n\\\n");
439 s = (do_visual_kludge ? strpbrk(v2, " \t\n:") : 0);
441 col = tab_to (out, col, tab2);
443 col = tab_to (out, col, tab);
446 strlen(v2) + col > 75)
453 while (v2[end] == ' ' || v2[end] == '\t')
455 while (v2[end] != ' ' && v2[end] != '\t' &&
456 v2[end] != '\n' && v2[end] != 0)
458 if (col + (end - start) >= 74)
460 col = tab_to (out, col, 75);
461 fprintf(out, " \\\n");
462 col = tab_to (out, 0, tab + 2);
463 while (v2[start] == ' ' || v2[start] == '\t')
469 fputc(v2[start++], out);
476 fprintf (out, "%s", v2);
491 write_init_file (saver_preferences *p, const char *version_string)
493 const char *name = init_file_name();
494 const char *tmp_name = init_file_tmp_name();
498 /* Kludge, since these aren't in the saver_preferences struct as strings...
502 Bool capture_stderr_p;
503 Bool overlay_stderr_p;
510 fprintf (stderr, "%s: writing \"%s\".\n", blurb(), name);
513 out = fopen(tmp_name, "w");
516 char *buf = (char *) malloc(1024 + strlen(name));
517 sprintf(buf, "%s: error writing \"%s\"", blurb(), name);
523 /* Give the new .xscreensaver file the same permissions as the old one;
524 except ensure that it is readable and writable by owner, and not
527 if (stat(name, &st) == 0)
529 mode_t mode = st.st_mode;
530 mode |= S_IRUSR | S_IWUSR;
531 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH);
532 if (fchmod (fileno(out), mode) != 0)
534 char *buf = (char *) malloc(1024 + strlen(name));
535 sprintf (buf, "%s: error fchmodding \"%s\" to 0%o", blurb(),
536 tmp_name, (unsigned int) mode);
543 /* Kludge, since these aren't in the saver_preferences struct... */
544 visual_name = get_string_resource ("visualID", "VisualID");
546 capture_stderr_p = get_boolean_resource ("captureStderr", "Boolean");
547 overlay_stderr_p = get_boolean_resource ("overlayStderr", "Boolean");
548 stderr_font = get_string_resource ("font", "Font");
551 for (j = 0; j < p->screenhacks_count; j++)
552 i += strlen(p->screenhacks[j]) + 2;
554 char *ss = programs = (char *) malloc(i + 10);
556 for (j = 0; j < p->screenhacks_count; j++)
558 strcat(ss, p->screenhacks[j]);
566 struct passwd *pw = getpwuid (getuid ());
567 char *whoami = (pw && pw->pw_name && *pw->pw_name
570 time_t now = time ((time_t *) 0);
571 char *timestr = (char *) ctime (&now);
572 char *nl = (char *) strchr (timestr, '\n');
575 "# %s Preferences File\n"
576 "# Written by %s %s for %s on %s.\n"
577 "# http://www.jwz.org/xscreensaver/\n"
579 progclass, progname, version_string, whoami, timestr);
582 for (j = 0; prefs[j]; j++)
585 const char *pr = prefs[j];
586 enum pref_type { pref_str, pref_int, pref_bool, pref_time
600 # define CHECK(X) else if (!strcmp(pr, X))
602 CHECK("timeout") type = pref_time, t = p->timeout;
603 CHECK("cycle") type = pref_time, t = p->cycle;
604 CHECK("lock") type = pref_bool, b = p->lock_p;
605 # if 0 /* #### not ready yet */
606 CHECK("lockVTs") type = pref_bool, b = p->lock_vt_p;
608 CHECK("lockVTs") continue; /* don't save */
610 CHECK("lockTimeout") type = pref_time, t = p->lock_timeout;
611 CHECK("passwdTimeout") type = pref_time, t = p->passwd_timeout;
612 CHECK("visualID") type = pref_str, s = visual_name;
613 CHECK("installColormap") type = pref_bool, b = p->install_cmap_p;
614 CHECK("verbose") type = pref_bool, b = p->verbose_p;
615 CHECK("timestamp") type = pref_bool, b = p->timestamp_p;
616 CHECK("splash") continue; /* don't save */
617 CHECK("splashDuration") type = pref_time, t = p->splash_duration;
618 CHECK("demoCommand") type = pref_str, s = p->demo_command;
619 CHECK("prefsCommand") type = pref_str, s = p->prefs_command;
620 CHECK("helpURL") type = pref_str, s = p->help_url;
621 CHECK("loadURL") type = pref_str, s = p->load_url_command;
622 CHECK("nice") type = pref_int, i = p->nice_inferior;
623 CHECK("fade") type = pref_bool, b = p->fade_p;
624 CHECK("unfade") type = pref_bool, b = p->unfade_p;
625 CHECK("fadeSeconds") type = pref_time, t = p->fade_seconds;
626 CHECK("fadeTicks") type = pref_int, i = p->fade_ticks;
627 CHECK("captureStderr") type = pref_bool, b = capture_stderr_p;
628 CHECK("captureStdout") continue; /* don't save */
629 CHECK("font") type = pref_str, s = stderr_font;
630 CHECK("programs") type = pref_str, s = programs;
631 CHECK("pointerPollTime") type = pref_time, t = p->pointer_timeout;
632 CHECK("windowCreationTimeout")type=pref_time,t= p->notice_events_timeout;
633 CHECK("initialDelay") type = pref_time, t = p->initial_delay;
634 CHECK("sgiSaverExtension")type = pref_bool, b=p->use_sgi_saver_extension;
635 CHECK("mitSaverExtension")type = pref_bool, b=p->use_mit_saver_extension;
636 CHECK("xidleExtension") type = pref_bool, b = p->use_xidle_extension;
637 CHECK("procInterrupts") type = pref_bool, b = p->use_proc_interrupts;
638 CHECK("overlayStderr") type = pref_bool, b = overlay_stderr_p;
639 CHECK("overlayTextBackground") continue; /* don't save */
640 CHECK("overlayTextForeground") continue; /* don't save */
641 CHECK("bourneShell") continue;
650 sprintf(buf, "%d", i);
654 s = b ? "True" : "False";
658 unsigned int hour = 0, min = 0, sec = (unsigned int) (t/1000);
669 sprintf (buf, "%u:%02u:%02u", hour, min, sec);
677 write_entry (out, pr, s);
682 if (visual_name) free(visual_name);
683 if (stderr_font) free(stderr_font);
684 if (programs) free(programs);
686 if (fclose(out) == 0)
688 time_t write_date = 0;
690 if (stat(tmp_name, &st) == 0)
692 write_date = st.st_mtime;
696 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
697 sprintf(buf, "%s: couldn't stat \"%s\"", blurb(), tmp_name);
704 if (rename (tmp_name, name) != 0)
706 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
707 sprintf(buf, "%s: error renaming \"%s\" to \"%s\"",
708 blurb(), tmp_name, name);
716 p->init_file_date = write_date;
718 /* Since the .xscreensaver file is used for IPC, let's try and make
719 sure that the bits actually land on the disk right away. */
725 char *buf = (char *) malloc(1024 + strlen(name));
726 sprintf(buf, "%s: error closing \"%s\"", blurb(), name);
735 /* Parsing the resource database
739 /* Populate `saver_preferences' with the contents of the resource database.
740 Note that this may be called multiple times -- it is re-run each time
741 the ~/.xscreensaver file is reloaded.
743 This function can be very noisy, since it issues resource syntax errors
747 load_init_file (saver_preferences *p)
749 static Bool first_time = True;
751 if (parse_init_file (p) != 0) /* file might have gone away */
752 if (!first_time) return;
756 p->xsync_p = get_boolean_resource ("synchronous", "Synchronous");
757 p->verbose_p = get_boolean_resource ("verbose", "Boolean");
758 p->timestamp_p = get_boolean_resource ("timestamp", "Boolean");
759 p->lock_p = get_boolean_resource ("lock", "Boolean");
760 p->lock_vt_p = get_boolean_resource ("lockVTs", "Boolean");
761 p->fade_p = get_boolean_resource ("fade", "Boolean");
762 p->unfade_p = get_boolean_resource ("unfade", "Boolean");
763 p->fade_seconds = 1000 * get_seconds_resource ("fadeSeconds", "Time");
764 p->fade_ticks = get_integer_resource ("fadeTicks", "Integer");
765 p->install_cmap_p = get_boolean_resource ("installColormap", "Boolean");
766 p->nice_inferior = get_integer_resource ("nice", "Nice");
768 p->initial_delay = 1000 * get_seconds_resource ("initialDelay", "Time");
769 p->splash_duration = 1000 * get_seconds_resource ("splashDuration", "Time");
770 p->timeout = 1000 * get_minutes_resource ("timeout", "Time");
771 p->lock_timeout = 1000 * get_minutes_resource ("lockTimeout", "Time");
772 p->cycle = 1000 * get_minutes_resource ("cycle", "Time");
773 p->passwd_timeout = 1000 * get_seconds_resource ("passwdTimeout", "Time");
774 p->pointer_timeout = 1000 * get_seconds_resource ("pointerPollTime", "Time");
775 p->notice_events_timeout = 1000*get_seconds_resource("windowCreationTimeout",
777 p->shell = get_string_resource ("bourneShell", "BourneShell");
779 p->demo_command = get_string_resource("demoCommand", "URL");
780 p->prefs_command = get_string_resource("prefsCommand", "URL");
781 p->help_url = get_string_resource("helpURL", "URL");
782 p->load_url_command = get_string_resource("loadURL", "LoadURL");
786 if ((s = get_string_resource ("splash", "Boolean")))
787 if (!get_boolean_resource("splash", "Boolean"))
788 p->splash_duration = 0;
792 p->use_xidle_extension = get_boolean_resource ("xidleExtension","Boolean");
793 p->use_mit_saver_extension = get_boolean_resource ("mitSaverExtension",
795 p->use_sgi_saver_extension = get_boolean_resource ("sgiSaverExtension",
797 p->use_proc_interrupts = get_boolean_resource ("procInterrupts", "Boolean");
799 /* Throttle the various timeouts to reasonable values.
801 if (p->passwd_timeout <= 0) p->passwd_timeout = 30000; /* 30 secs */
802 if (p->timeout < 10000) p->timeout = 10000; /* 10 secs */
803 if (p->cycle < 0) p->cycle = 0;
804 if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */
805 if (p->pointer_timeout <= 0) p->pointer_timeout = 5000; /* 5 secs */
806 if (p->notice_events_timeout <= 0)
807 p->notice_events_timeout = 10000; /* 10 secs */
808 if (p->fade_seconds <= 0 || p->fade_ticks <= 0)
810 if (! p->fade_p) p->unfade_p = False;
812 if (p->verbose_p && !p->fading_possible_p && (p->fade_p || p->unfade_p))
814 fprintf (stderr, "%s: there are no PseudoColor or GrayScale visuals.\n",
816 fprintf (stderr, "%s: ignoring the request for fading/unfading.\n",
820 p->watchdog_timeout = p->cycle;
821 if (p->watchdog_timeout < 30000) p->watchdog_timeout = 30000; /* 30 secs */
822 if (p->watchdog_timeout > 3600000) p->watchdog_timeout = 3600000; /* 1 hr */
830 p->timestamp_p = True;
831 p->initial_delay = 0;
836 /* Parsing the programs resource.
840 reformat_hack (const char *hack)
843 const char *in = hack;
845 char *h2 = (char *) malloc(strlen(in) + indent + 2);
847 Bool disabled_p = False;
849 while (isspace(*in)) in++; /* skip whitespace */
851 if (*in == '-') /* Handle a leading "-". */
858 while (isspace(*in)) in++;
866 while (*in && !isspace(*in) && *in != ':')
867 *out++ = *in++; /* snarf first token */
868 while (isspace(*in)) in++; /* skip whitespace */
871 *out++ = *in++; /* copy colon */
875 out = h2 + 2; /* reset to beginning */
880 while (isspace(*in)) in++; /* skip whitespace */
881 for (i = strlen(h2); i < indent; i++) /* indent */
884 /* copy the rest of the line. */
887 /* shrink all whitespace to one space, for the benefit of the "demo"
888 mode display. We only do this when we can easily tell that the
889 whitespace is not significant (no shell metachars).
893 case '\'': case '"': case '`': case '\\':
895 /* Metachars are scary. Copy the rest of the line unchanged. */
902 while (*in == ' ' || *in == '\t')
914 /* strip trailing whitespace. */
916 while (out > h2 && (*out == ' ' || *out == '\t' || *out == '\n'))
924 get_screenhacks (saver_preferences *p)
932 d = get_string_resource ("monoPrograms", "MonoPrograms");
933 if (d && !*d) { free(d); d = 0; }
935 d = get_string_resource ("colorPrograms", "ColorPrograms");
936 if (d && !*d) { free(d); d = 0; }
941 "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
942 see the manual for details.\n", blurb());
946 d = get_string_resource ("programs", "Programs");
950 for (i = 0; i < p->screenhacks_count; i++)
951 if (p->screenhacks[i])
952 free (p->screenhacks[i]);
953 free(p->screenhacks);
959 p->screenhacks_count = 0;
967 /* Count up the number of newlines (which will be equal to or larger than
968 the number of hacks.)
971 for (i = 0; d[i]; i++)
976 p->screenhacks = (char **) calloc (sizeof (char *), i+1);
978 /* Iterate over the lines in `d' (the string with newlines)
979 and make new strings to stuff into the `screenhacks' array.
981 p->screenhacks_count = 0;
984 /* skip forward over whitespace. */
985 while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n')
988 /* skip forward to newline or end of string. */
990 while (d[end] != 0 && d[end] != '\n')
993 /* null terminate. */
996 p->screenhacks[p->screenhacks_count++] = reformat_hack (d + start);
997 if (p->screenhacks_count >= i)
1003 if (p->screenhacks_count == 0)
1005 free (p->screenhacks);