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);
76 static char *format_command (const char *cmd, Bool wrap_p);
80 chase_symlinks (const char *file)
86 if (realpath (file, buf))
89 sprintf (buf, "%s: realpath", blurb());
92 # endif /* HAVE_REALPATH */
100 static char *file = 0;
104 struct passwd *p = getpwuid (getuid ());
106 if (!p || !p->pw_name || !*p->pw_name)
108 fprintf (stderr, "%s: couldn't get user info of uid %d\n",
112 else if (!p->pw_dir || !*p->pw_dir)
114 fprintf (stderr, "%s: couldn't get home directory of \"%s\"\n",
115 blurb(), (p->pw_name ? p->pw_name : "???"));
120 const char *home = p->pw_dir;
121 const char *name = ".xscreensaver";
122 file = (char *) malloc(strlen(home) + strlen(name) + 2);
124 if (!*home || home[strlen(home)-1] != '/')
138 init_file_tmp_name (void)
140 static char *file = 0;
143 const char *name = init_file_name();
144 const char *suffix = ".tmp";
146 char *n2 = chase_symlinks (name);
153 file = (char *) malloc(strlen(name) + strlen(suffix) + 2);
155 strcat(file, suffix);
168 static const char * const prefs[] = {
179 "splash", /* not saved -- same as "splashDuration: 0" */
191 "captureStdout", /* not saved -- obsolete */
197 "windowCreationTimeout",
204 "overlayTextBackground", /* not saved -- X resources only */
205 "overlayTextForeground", /* not saved -- X resources only */
206 "bourneShell", /* not saved -- X resources only */
214 while (*s == '\t' || *s == ' ' || *s == '\r' || *s == '\n')
216 for (s2 = s; *s2; s2++)
218 for (s2--; s2 >= s; s2--)
219 if (*s2 == '\t' || *s2 == ' ' || *s2 == '\r' || *s2 =='\n')
231 handle_entry (XrmDatabase *db, const char *key, const char *value,
232 const char *filename, int line)
235 for (i = 0; prefs[i]; i++)
236 if (*prefs[i] && !strcasecmp(key, prefs[i]))
238 char *val = strdup(value);
239 char *spec = (char *) malloc(strlen(progclass) + strlen(prefs[i]) +10);
240 strcpy(spec, progclass);
242 strcat(spec, prefs[i]);
244 XrmPutStringResource (db, spec, val);
251 fprintf(stderr, "%s: %s:%d: unknown option \"%s\"\n",
252 blurb(), filename, line, key);
258 parse_init_file (saver_preferences *p)
260 time_t write_date = 0;
261 const char *name = init_file_name();
270 if (stat(name, &st) != 0)
272 p->init_file_date = 0;
276 in = fopen(name, "r");
279 char *buf = (char *) malloc(1024 + strlen(name));
280 sprintf(buf, "%s: error reading \"%s\"", blurb(), name);
286 if (fstat (fileno(in), &st) == 0)
288 write_date = st.st_mtime;
292 char *buf = (char *) malloc(1024 + strlen(name));
293 sprintf(buf, "%s: couldn't re-stat \"%s\"", blurb(), name);
299 buf = (char *) malloc(buf_size);
301 while (fgets (buf, buf_size-1, in))
308 (buf[L-1] != '\n' || /* whole line didn't fit in buffer */
309 buf[L-2] == '\\')) /* or line ended with backslash */
311 if (buf[L-2] == '\\') /* backslash-newline gets swallowed */
317 buf = (char *) realloc(buf, buf_size);
321 if (!fgets (buf + L, buf_size-L-1, in))
326 /* Now handle other backslash escapes. */
329 for (i = 0; buf[i]; i++)
334 case 'n': buf[i] = '\n'; break;
335 case 'r': buf[i] = '\r'; break;
336 case 't': buf[i] = '\t'; break;
337 default: buf[i] = buf[i+1]; break;
339 for (j = i+2; buf[j]; j++)
347 if (*key == '#' || *key == '!' || *key == ';' ||
348 *key == '\n' || *key == 0)
351 value = strchr (key, ':');
354 fprintf(stderr, "%s: %s:%d: unparsable line: %s\n", blurb(),
361 value = strip(value);
365 handle_entry (&p->db, key, value, name, line);
370 p->init_file_date = write_date;
376 init_file_changed_p (saver_preferences *p)
378 const char *name = init_file_name();
381 if (!name) return False;
383 if (stat(name, &st) != 0)
386 if (p->init_file_date == st.st_mtime)
397 tab_to (FILE *out, int from, int to)
400 int to_mod = (to / tab_width) * tab_width;
401 while (from < to_mod)
404 from = (((from / tab_width) + 1) * tab_width);
415 stab_to (char *out, int from, int to)
418 int to_mod = (to / tab_width) * tab_width;
419 while (from < to_mod)
422 from = (((from / tab_width) + 1) * tab_width);
433 string_columns (const char *string, int length, int start)
437 const char *end = string + length;
442 else if (*string == '\t')
443 col = (((col / tab_width) + 1) * tab_width);
453 write_entry (FILE *out, const char *key, const char *value)
455 char *v = strdup(value ? value : "");
459 Bool programs_p = (!strcmp(key, "programs"));
460 int tab = (programs_p ? 32 : 16);
463 fprintf(out, "%s:", key);
464 col = strlen(key) + 1;
470 nl = strchr(v2, '\n');
474 if (first && programs_p)
476 col = tab_to (out, col, 77);
477 fprintf (out, " \\\n");
485 col = tab_to (out, col, 75);
486 fprintf (out, " \\n\\\n");
491 col = tab_to (out, col, tab);
494 string_columns(v2, strlen (v2), col) + col > 75)
501 while (v2[end] == ' ' || v2[end] == '\t')
503 while (v2[end] != ' ' && v2[end] != '\t' &&
504 v2[end] != '\n' && v2[end] != 0)
506 if (string_columns (v2 + start, (end - start), col) >= 74)
508 col = tab_to (out, col, 75);
509 fprintf(out, " \\\n");
510 col = tab_to (out, 0, tab + 2);
511 while (v2[start] == ' ' || v2[start] == '\t')
515 col = string_columns (v2 + start, (end - start), col);
517 fputc(v2[start++], out);
522 fprintf (out, "%s", v2);
523 col += string_columns(v2, strlen (v2), col);
537 write_init_file (saver_preferences *p, const char *version_string)
539 const char *name = init_file_name();
540 const char *tmp_name = init_file_tmp_name();
541 char *n2 = chase_symlinks (name);
545 /* Kludge, since these aren't in the saver_preferences struct as strings...
549 Bool capture_stderr_p;
550 Bool overlay_stderr_p;
559 fprintf (stderr, "%s: writing \"%s\".\n", blurb(), name);
562 out = fopen(tmp_name, "w");
565 char *buf = (char *) malloc(1024 + strlen(name));
566 sprintf(buf, "%s: error writing \"%s\"", blurb(), name);
572 /* Give the new .xscreensaver file the same permissions as the old one;
573 except ensure that it is readable and writable by owner, and not
576 if (stat(name, &st) == 0)
578 mode_t mode = st.st_mode;
579 mode |= S_IRUSR | S_IWUSR;
580 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH);
581 if (fchmod (fileno(out), mode) != 0)
583 char *buf = (char *) malloc(1024 + strlen(name));
584 sprintf (buf, "%s: error fchmodding \"%s\" to 0%o", blurb(),
585 tmp_name, (unsigned int) mode);
592 /* Kludge, since these aren't in the saver_preferences struct... */
593 visual_name = get_string_resource ("visualID", "VisualID");
595 capture_stderr_p = get_boolean_resource ("captureStderr", "Boolean");
596 overlay_stderr_p = get_boolean_resource ("overlayStderr", "Boolean");
597 stderr_font = get_string_resource ("font", "Font");
602 char **hack_strings = (char **)
603 calloc (p->screenhacks_count, sizeof(char *));
605 for (j = 0; j < p->screenhacks_count; j++)
607 hack_strings[j] = format_hack (p->screenhacks[j], True);
608 i += strlen (hack_strings[j]);
612 ss = programs = (char *) malloc(i + 10);
614 for (j = 0; j < p->screenhacks_count; j++)
616 strcat (ss, hack_strings[j]);
617 free (hack_strings[j]);
625 struct passwd *pw = getpwuid (getuid ());
626 char *whoami = (pw && pw->pw_name && *pw->pw_name
629 time_t now = time ((time_t *) 0);
630 char *timestr = (char *) ctime (&now);
631 char *nl = (char *) strchr (timestr, '\n');
634 "# %s Preferences File\n"
635 "# Written by %s %s for %s on %s.\n"
636 "# http://www.jwz.org/xscreensaver/\n"
638 progclass, progname, version_string, whoami, timestr);
641 for (j = 0; prefs[j]; j++)
644 const char *pr = prefs[j];
645 enum pref_type { pref_str, pref_int, pref_bool, pref_time
659 # define CHECK(X) else if (!strcmp(pr, X))
661 CHECK("timeout") type = pref_time, t = p->timeout;
662 CHECK("cycle") type = pref_time, t = p->cycle;
663 CHECK("lock") type = pref_bool, b = p->lock_p;
664 # if 0 /* #### not ready yet */
665 CHECK("lockVTs") type = pref_bool, b = p->lock_vt_p;
667 CHECK("lockVTs") continue; /* don't save */
669 CHECK("lockTimeout") type = pref_time, t = p->lock_timeout;
670 CHECK("passwdTimeout") type = pref_time, t = p->passwd_timeout;
671 CHECK("visualID") type = pref_str, s = visual_name;
672 CHECK("installColormap") type = pref_bool, b = p->install_cmap_p;
673 CHECK("verbose") type = pref_bool, b = p->verbose_p;
674 CHECK("timestamp") type = pref_bool, b = p->timestamp_p;
675 CHECK("splash") continue; /* don't save */
676 CHECK("splashDuration") type = pref_time, t = p->splash_duration;
677 CHECK("demoCommand") type = pref_str, s = p->demo_command;
678 CHECK("prefsCommand") type = pref_str, s = p->prefs_command;
679 CHECK("helpURL") type = pref_str, s = p->help_url;
680 CHECK("loadURL") type = pref_str, s = p->load_url_command;
681 CHECK("nice") type = pref_int, i = p->nice_inferior;
682 CHECK("fade") type = pref_bool, b = p->fade_p;
683 CHECK("unfade") type = pref_bool, b = p->unfade_p;
684 CHECK("fadeSeconds") type = pref_time, t = p->fade_seconds;
685 CHECK("fadeTicks") type = pref_int, i = p->fade_ticks;
686 CHECK("captureStderr") type = pref_bool, b = capture_stderr_p;
687 CHECK("captureStdout") continue; /* don't save */
688 CHECK("font") type = pref_str, s = stderr_font;
689 CHECK("programs") type = pref_str, s = programs;
690 CHECK("pointerPollTime") type = pref_time, t = p->pointer_timeout;
691 CHECK("windowCreationTimeout")type=pref_time,t= p->notice_events_timeout;
692 CHECK("initialDelay") type = pref_time, t = p->initial_delay;
693 CHECK("sgiSaverExtension")type = pref_bool, b=p->use_sgi_saver_extension;
694 CHECK("mitSaverExtension")type = pref_bool, b=p->use_mit_saver_extension;
695 CHECK("xidleExtension") type = pref_bool, b = p->use_xidle_extension;
696 CHECK("procInterrupts") type = pref_bool, b = p->use_proc_interrupts;
697 CHECK("overlayStderr") type = pref_bool, b = overlay_stderr_p;
698 CHECK("overlayTextBackground") continue; /* don't save */
699 CHECK("overlayTextForeground") continue; /* don't save */
700 CHECK("bourneShell") continue;
709 sprintf(buf, "%d", i);
713 s = b ? "True" : "False";
717 unsigned int hour = 0, min = 0, sec = (unsigned int) (t/1000);
728 sprintf (buf, "%u:%02u:%02u", hour, min, sec);
736 write_entry (out, pr, s);
741 if (visual_name) free(visual_name);
742 if (stderr_font) free(stderr_font);
743 if (programs) free(programs);
745 if (fclose(out) == 0)
747 time_t write_date = 0;
749 if (stat(tmp_name, &st) == 0)
751 write_date = st.st_mtime;
755 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
756 sprintf(buf, "%s: couldn't stat \"%s\"", blurb(), tmp_name);
763 if (rename (tmp_name, name) != 0)
765 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
766 sprintf(buf, "%s: error renaming \"%s\" to \"%s\"",
767 blurb(), tmp_name, name);
775 p->init_file_date = write_date;
777 /* Since the .xscreensaver file is used for IPC, let's try and make
778 sure that the bits actually land on the disk right away. */
784 char *buf = (char *) malloc(1024 + strlen(name));
785 sprintf(buf, "%s: error closing \"%s\"", blurb(), name);
797 /* Parsing the resource database
801 /* Populate `saver_preferences' with the contents of the resource database.
802 Note that this may be called multiple times -- it is re-run each time
803 the ~/.xscreensaver file is reloaded.
805 This function can be very noisy, since it issues resource syntax errors
809 load_init_file (saver_preferences *p)
811 static Bool first_time = True;
813 if (parse_init_file (p) != 0) /* file might have gone away */
814 if (!first_time) return;
818 p->xsync_p = get_boolean_resource ("synchronous", "Synchronous");
819 p->verbose_p = get_boolean_resource ("verbose", "Boolean");
820 p->timestamp_p = get_boolean_resource ("timestamp", "Boolean");
821 p->lock_p = get_boolean_resource ("lock", "Boolean");
822 p->lock_vt_p = get_boolean_resource ("lockVTs", "Boolean");
823 p->fade_p = get_boolean_resource ("fade", "Boolean");
824 p->unfade_p = get_boolean_resource ("unfade", "Boolean");
825 p->fade_seconds = 1000 * get_seconds_resource ("fadeSeconds", "Time");
826 p->fade_ticks = get_integer_resource ("fadeTicks", "Integer");
827 p->install_cmap_p = get_boolean_resource ("installColormap", "Boolean");
828 p->nice_inferior = get_integer_resource ("nice", "Nice");
830 p->initial_delay = 1000 * get_seconds_resource ("initialDelay", "Time");
831 p->splash_duration = 1000 * get_seconds_resource ("splashDuration", "Time");
832 p->timeout = 1000 * get_minutes_resource ("timeout", "Time");
833 p->lock_timeout = 1000 * get_minutes_resource ("lockTimeout", "Time");
834 p->cycle = 1000 * get_minutes_resource ("cycle", "Time");
835 p->passwd_timeout = 1000 * get_seconds_resource ("passwdTimeout", "Time");
836 p->pointer_timeout = 1000 * get_seconds_resource ("pointerPollTime", "Time");
837 p->notice_events_timeout = 1000*get_seconds_resource("windowCreationTimeout",
839 p->shell = get_string_resource ("bourneShell", "BourneShell");
841 p->demo_command = get_string_resource("demoCommand", "URL");
842 p->prefs_command = get_string_resource("prefsCommand", "URL");
843 p->help_url = get_string_resource("helpURL", "URL");
844 p->load_url_command = get_string_resource("loadURL", "LoadURL");
848 if ((s = get_string_resource ("splash", "Boolean")))
849 if (!get_boolean_resource("splash", "Boolean"))
850 p->splash_duration = 0;
854 p->use_xidle_extension = get_boolean_resource ("xidleExtension","Boolean");
855 p->use_mit_saver_extension = get_boolean_resource ("mitSaverExtension",
857 p->use_sgi_saver_extension = get_boolean_resource ("sgiSaverExtension",
859 p->use_proc_interrupts = get_boolean_resource ("procInterrupts", "Boolean");
861 /* Throttle the various timeouts to reasonable values.
863 if (p->passwd_timeout <= 0) p->passwd_timeout = 30000; /* 30 secs */
864 if (p->timeout < 10000) p->timeout = 10000; /* 10 secs */
865 if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */
866 if (p->pointer_timeout <= 0) p->pointer_timeout = 5000; /* 5 secs */
867 if (p->notice_events_timeout <= 0)
868 p->notice_events_timeout = 10000; /* 10 secs */
869 if (p->fade_seconds <= 0 || p->fade_ticks <= 0)
871 if (! p->fade_p) p->unfade_p = False;
873 p->watchdog_timeout = p->cycle * 0.6;
874 if (p->watchdog_timeout < 30000) p->watchdog_timeout = 30000; /* 30 secs */
875 if (p->watchdog_timeout > 3600000) p->watchdog_timeout = 3600000; /* 1 hr */
883 p->timestamp_p = True;
884 p->initial_delay = 0;
889 /* Parsing the programs resource.
893 parse_screenhack (const char *line)
895 screenhack *h = (screenhack *) calloc (1, sizeof(*h));
900 while (isspace(*line)) line++; /* skip whitespace */
901 if (*line == '-') /* handle "-" */
903 h->enabled_p = False;
905 while (isspace(*line)) line++; /* skip whitespace */
908 s = line; /* handle "visual:" */
909 while (*line && *line != ':' && *line != '"' && !isspace(*line))
915 h->visual = (char *) malloc (line-s+1);
916 strncpy (h->visual, s, line-s);
917 h->visual[line-s] = 0;
918 if (*line == ':') line++; /* skip ":" */
919 while (isspace(*line)) line++; /* skip whitespace */
922 if (*line == '"') /* handle "name" */
926 while (*line && *line != '"')
928 h->name = (char *) malloc (line-s+1);
929 strncpy (h->name, s, line-s);
931 if (*line == '"') line++; /* skip "\"" */
932 while (isspace(*line)) line++; /* skip whitespace */
935 h->command = format_command (line, False); /* handle command */
941 free_screenhack (screenhack *hack)
943 if (hack->visual) free (hack->visual);
944 if (hack->name) free (hack->name);
945 free (hack->command);
946 memset (hack, 0, sizeof(*hack));
952 format_command (const char *cmd, Bool wrap_p)
956 char *cmd2 = (char *) calloc (1, 2 * (strlen (cmd) + 1));
957 const char *in = cmd;
961 /* shrink all whitespace to one space, for the benefit of the "demo"
962 mode display. We only do this when we can easily tell that the
963 whitespace is not significant (no shell metachars).
967 case '\'': case '"': case '`': case '\\':
968 /* Metachars are scary. Copy the rest of the line unchanged. */
970 *out++ = *in++, col++;
974 /* Squeeze all other whitespace down to one space. */
975 while (*in == ' ' || *in == '\t')
981 /* Copy other chars unchanged. */
982 *out++ = *in++, col++;
989 /* Strip trailing whitespace */
990 while (out > cmd2 && isspace (out[-1]))
998 format_hack (screenhack *hack, Bool wrap_p)
1001 int size = (2 * (strlen(hack->command) +
1002 (hack->visual ? strlen(hack->visual) : 0) +
1003 (hack->name ? strlen(hack->name) : 0) +
1005 char *h2 = (char *) malloc (size);
1010 if (!hack->enabled_p) *out++ = '-'; /* write disabled flag */
1012 if (hack->visual && *hack->visual) /* write visual name */
1014 if (hack->enabled_p) *out++ = ' ';
1016 strcpy (out, hack->visual);
1017 out += strlen (hack->visual);
1023 col = string_columns (h2, strlen (h2), 0);
1025 if (hack->name && *hack->name) /* write pretty name */
1027 int L = (strlen (hack->name) + 2);
1029 out = stab_to (out, col, tab - L - 2);
1033 strcpy (out, hack->name);
1034 out += strlen (hack->name);
1038 col = string_columns (h2, strlen (h2), 0);
1039 if (wrap_p && col >= tab)
1041 out = stab_to (out, col, 77);
1042 *out += strlen(out);
1047 if (out >= h2+size) abort();
1051 col = string_columns (h2, strlen (h2), 0);
1052 out = stab_to (out, col, tab); /* indent */
1054 if (out >= h2+size) abort();
1055 s = format_command (hack->command, wrap_p);
1066 get_screenhacks (saver_preferences *p)
1074 d = get_string_resource ("monoPrograms", "MonoPrograms");
1075 if (d && !*d) { free(d); d = 0; }
1077 d = get_string_resource ("colorPrograms", "ColorPrograms");
1078 if (d && !*d) { free(d); d = 0; }
1083 "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
1084 see the manual for details.\n", blurb());
1088 d = get_string_resource ("programs", "Programs");
1092 for (i = 0; i < p->screenhacks_count; i++)
1093 if (p->screenhacks[i])
1094 free_screenhack (p->screenhacks[i]);
1095 free(p->screenhacks);
1101 p->screenhacks_count = 0;
1109 /* Count up the number of newlines (which will be equal to or larger than
1110 the number of hacks.)
1113 for (i = 0; d[i]; i++)
1118 p->screenhacks = (screenhack **) calloc (sizeof (screenhack *), i+1);
1120 /* Iterate over the lines in `d' (the string with newlines)
1121 and make new strings to stuff into the `screenhacks' array.
1123 p->screenhacks_count = 0;
1124 while (start < size)
1126 /* skip forward over whitespace. */
1127 while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n')
1130 /* skip forward to newline or end of string. */
1132 while (d[end] != 0 && d[end] != '\n')
1135 /* null terminate. */
1138 p->screenhacks[p->screenhacks_count++] = parse_screenhack (d + start);
1139 if (p->screenhacks_count >= i)
1145 if (p->screenhacks_count == 0)
1147 free (p->screenhacks);