1 /* dotfile.c --- management of the ~/.xscreensaver file.
2 * xscreensaver, Copyright (c) 1998-2016 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
29 #include <sys/param.h> /* for PATH_MAX */
32 #include <X11/Xresource.h>
41 /* This file doesn't need the Xt headers, so stub these types out... */
43 #define XtAppContext void*
44 #define XtIntervalId void*
45 #define XtPointer void*
49 /* Just in case there's something pathological about stat.h... */
51 # define S_IRUSR 00400
54 # define S_IWUSR 00200
57 # define S_IXUSR 00100
60 # define S_IXGRP 00010
63 # define S_IXOTH 00001
69 #include "resources.h"
71 /* don't use realpath() on fedora system */
72 #ifdef _FORTIFY_SOURCE
77 extern char *progname;
78 extern char *progclass;
79 extern const char *blurb (void);
83 static void get_screenhacks (Display *, saver_preferences *);
84 static char *format_command (const char *cmd, Bool wrap_p);
85 static void merge_system_screenhacks (Display *, saver_preferences *,
86 screenhack **system_list, int count);
87 static void stop_the_insanity (saver_preferences *p);
91 chase_symlinks (const char *file)
98 # define PATH_MAX MAXPATHLEN
100 # define PATH_MAX 2048
104 if (realpath (file, buf))
107 /* sprintf (buf, "%.100s: realpath %.200s", blurb(), file);
110 # endif /* HAVE_REALPATH */
116 i_am_a_nobody (uid_t uid)
120 p = getpwnam ("nobody");
121 if (! p) p = getpwnam ("noaccess");
122 if (! p) p = getpwnam ("daemon");
124 if (! p) /* There is no nobody? */
127 return (uid == p->pw_uid);
132 init_file_name (void)
134 static char *file = 0;
138 uid_t uid = getuid ();
139 struct passwd *p = getpwuid (uid);
141 if (i_am_a_nobody (uid))
142 /* If we're running as nobody, then use root's .xscreensaver file
143 (since ~root/.xscreensaver and ~nobody/.xscreensaver are likely
144 to be different -- if we didn't do this, then xscreensaver-demo
145 would appear to have no effect when the luser is running as root.)
151 if (!p || !p->pw_name || !*p->pw_name)
153 fprintf (stderr, "%s: couldn't get user info of uid %d\n",
157 else if (!p->pw_dir || !*p->pw_dir)
159 fprintf (stderr, "%s: couldn't get home directory of \"%s\"\n",
160 blurb(), (p->pw_name ? p->pw_name : "???"));
165 const char *home = p->pw_dir;
166 const char *name = ".xscreensaver";
167 file = (char *) malloc(strlen(home) + strlen(name) + 2);
169 if (!*home || home[strlen(home)-1] != '/')
183 init_file_tmp_name (void)
185 static char *file = 0;
188 const char *name = init_file_name();
189 const char *suffix = ".tmp";
191 char *n2 = chase_symlinks (name);
198 file = (char *) malloc(strlen(name) + strlen(suffix) + 2);
200 strcat(file, suffix);
213 get_byte_resource (Display *dpy, char *name, char *class)
215 char *s = get_string_resource (dpy, name, class);
220 while (isspace(*s2)) s2++;
221 while (*s2 >= '0' && *s2 <= '9')
223 n = (n * 10) + (*s2 - '0');
226 while (isspace(*s2)) s2++;
227 if (*s2 == 'k' || *s2 == 'K') n <<= 10;
228 else if (*s2 == 'm' || *s2 == 'M') n <<= 20;
229 else if (*s2 == 'g' || *s2 == 'G') n <<= 30;
233 fprintf (stderr, "%s: %s must be a number of bytes, not \"%s\".\n",
239 if (*s2 == 'b' || *s2 == 'B') s2++;
240 while (isspace(*s2)) s2++;
248 static const char * const prefs[] = {
252 "lockVTs", /* not saved */
265 "helpURL", /* not saved */
266 "loadURL", /* not saved */
267 "newLoginCommand", /* not saved */
275 "captureStdout", /* not saved -- obsolete */
276 "logFile", /* not saved */
277 "ignoreUninstalledPrograms",
286 "chooseRandomImages",
300 "windowCreationTimeout",
302 "sgiSaverExtension", /* not saved -- obsolete */
303 "mitSaverExtension", /* not saved -- obsolete */
304 "xidleExtension", /* not saved -- obsolete */
305 "GetViewPortIsFullOfLies",
307 "xinputExtensionDev",
309 "overlayTextBackground", /* not saved -- X resources only */
310 "overlayTextForeground", /* not saved -- X resources only */
311 "bourneShell", /* not saved -- X resources only */
320 while (*s == '\t' || *s == ' ' || *s == '\r' || *s == '\n')
322 for (s2 = s; *s2; s2++)
324 for (s2--; s2 >= s; s2--)
325 if (*s2 == '\t' || *s2 == ' ' || *s2 == '\r' || *s2 =='\n')
337 handle_entry (XrmDatabase *db, const char *key, const char *value,
338 const char *filename, int line)
341 for (i = 0; prefs[i]; i++)
342 if (*prefs[i] && !strcasecmp(key, prefs[i]))
344 char *val = strdup(value);
345 char *spec = (char *) malloc(strlen(progclass) + strlen(prefs[i]) +10);
346 strcpy(spec, progclass);
348 strcat(spec, prefs[i]);
350 XrmPutStringResource (db, spec, val);
357 fprintf(stderr, "%s: %s:%d: unknown option \"%s\"\n",
358 blurb(), filename, line, key);
364 parse_init_file (saver_preferences *p)
366 time_t write_date = 0;
367 const char *name = init_file_name();
376 if (stat(name, &st) != 0)
378 p->init_file_date = 0;
382 in = fopen(name, "r");
385 char *buf = (char *) malloc(1024 + strlen(name));
386 sprintf(buf, "%s: error reading \"%s\"", blurb(), name);
392 if (fstat (fileno(in), &st) == 0)
394 write_date = st.st_mtime;
398 char *buf = (char *) malloc(1024 + strlen(name));
399 sprintf(buf, "%s: couldn't re-stat \"%s\"", blurb(), name);
405 buf = (char *) malloc(buf_size);
407 while (fgets (buf, buf_size-1, in))
414 (buf[L-1] != '\n' || /* whole line didn't fit in buffer */
415 buf[L-2] == '\\')) /* or line ended with backslash */
417 if (buf[L-2] == '\\') /* backslash-newline gets swallowed */
423 buf = (char *) realloc(buf, buf_size);
427 if (!fgets (buf + L, buf_size-L-1, in))
432 /* Now handle other backslash escapes. */
435 for (i = 0; buf[i]; i++)
440 case 'n': buf[i] = '\n'; break;
441 case 'r': buf[i] = '\r'; break;
442 case 't': buf[i] = '\t'; break;
443 default: buf[i] = buf[i+1]; break;
445 for (j = i+2; buf[j]; j++)
453 if (*key == '#' || *key == '!' || *key == ';' ||
454 *key == '\n' || *key == 0)
457 value = strchr (key, ':');
460 fprintf(stderr, "%s: %s:%d: unparsable line: %s\n", blurb(),
467 value = strip(value);
471 handle_entry (&p->db, key, value, name, line);
476 p->init_file_date = write_date;
482 init_file_changed_p (saver_preferences *p)
484 const char *name = init_file_name();
487 if (!name) return False;
489 if (stat(name, &st) != 0)
492 if (p->init_file_date == st.st_mtime)
503 tab_to (FILE *out, int from, int to)
506 int to_mod = (to / tab_width) * tab_width;
507 while (from < to_mod)
510 from = (((from / tab_width) + 1) * tab_width);
521 stab_to (char *out, int from, int to)
524 int to_mod = (to / tab_width) * tab_width;
525 while (from < to_mod)
528 from = (((from / tab_width) + 1) * tab_width);
539 string_columns (const char *string, int length, int start)
543 const char *end = string + length;
548 else if (*string == '\t')
549 col = (((col / tab_width) + 1) * tab_width);
559 write_entry (FILE *out, const char *key, const char *value)
561 char *v = strdup(value ? value : "");
565 Bool programs_p = (!strcmp(key, "programs"));
566 int tab = (programs_p ? 32 : 16);
569 fprintf(out, "%s:", key);
570 col = strlen(key) + 1;
572 if (strlen(key) > 14)
573 col = tab_to (out, col, 20);
579 nl = strchr(v2, '\n');
583 if (first && programs_p)
585 col = tab_to (out, col, 77);
586 fprintf (out, " \\\n");
594 col = tab_to (out, col, 75);
595 fprintf (out, " \\n\\\n");
600 col = tab_to (out, col, tab);
603 string_columns(v2, strlen (v2), col) + col > 75)
610 while (v2[end] == ' ' || v2[end] == '\t')
612 while (v2[end] != ' ' && v2[end] != '\t' &&
613 v2[end] != '\n' && v2[end] != 0)
615 if (string_columns (v2 + start, (end - start), col) >= 74)
617 col = tab_to (out, col, 75);
618 fprintf(out, " \\\n");
619 col = tab_to (out, 0, tab + 2);
620 while (v2[start] == ' ' || v2[start] == '\t')
624 col = string_columns (v2 + start, (end - start), col);
626 fputc(v2[start++], out);
631 fprintf (out, "%s", v2);
632 col += string_columns(v2, strlen (v2), col);
646 write_init_file (Display *dpy,
647 saver_preferences *p, const char *version_string,
651 const char *name = init_file_name();
652 const char *tmp_name = init_file_tmp_name();
653 char *n2 = chase_symlinks (name);
657 /* Kludge, since these aren't in the saver_preferences struct as strings...
661 Bool overlay_stderr_p;
669 /* Throttle the various timeouts to reasonable values before writing
671 stop_the_insanity (p);
675 fprintf (stderr, "%s: writing \"%s\".\n", blurb(), name);
678 out = fopen(tmp_name, "w");
681 char *buf = (char *) malloc(1024 + strlen(name));
682 sprintf(buf, "%s: error writing \"%s\"", blurb(), name);
688 /* Give the new .xscreensaver file the same permissions as the old one;
689 except ensure that it is readable and writable by owner, and not
690 executable. Extra hack: if we're running as root, make the file
691 be world-readable (so that the daemon, running as "nobody", will
692 still be able to read it.)
694 if (stat(name, &st) == 0)
696 mode_t mode = st.st_mode;
697 mode |= S_IRUSR | S_IWUSR; /* read/write by user */
698 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); /* executable by none */
700 if (getuid() == (uid_t) 0) /* read by group/other */
701 mode |= S_IRGRP | S_IROTH;
703 if (fchmod (fileno(out), mode) != 0)
705 char *buf = (char *) malloc(1024 + strlen(name));
706 sprintf (buf, "%s: error fchmodding \"%s\" to 0%o", blurb(),
707 tmp_name, (unsigned int) mode);
714 /* Kludge, since these aren't in the saver_preferences struct... */
715 visual_name = get_string_resource (dpy, "visualID", "VisualID");
717 overlay_stderr_p = get_boolean_resource (dpy, "overlayStderr", "Boolean");
718 stderr_font = get_string_resource (dpy, "font", "Font");
723 char **hack_strings = (char **)
724 calloc (p->screenhacks_count, sizeof(char *));
726 for (j = 0; j < p->screenhacks_count; j++)
728 hack_strings[j] = format_hack (dpy, p->screenhacks[j], True);
729 i += strlen (hack_strings[j]);
733 ss = programs = (char *) malloc(i + 10);
735 for (j = 0; j < p->screenhacks_count; j++)
737 strcat (ss, hack_strings[j]);
738 free (hack_strings[j]);
747 struct passwd *pw = getpwuid (getuid ());
748 char *whoami = (pw && pw->pw_name && *pw->pw_name
751 time_t now = time ((time_t *) 0);
752 char *timestr = (char *) ctime (&now);
753 char *nl = (char *) strchr (timestr, '\n');
756 "# %s Preferences File\n"
757 "# Written by %s %s for %s on %s.\n"
758 "# https://www.jwz.org/xscreensaver/\n"
760 progclass, progname, version_string, whoami, timestr);
763 for (j = 0; prefs[j]; j++)
766 const char *pr = prefs[j];
767 enum pref_type { pref_str, pref_int, pref_bool, pref_byte, pref_time
781 # define CHECK(X) else if (!strcmp(pr, X))
783 CHECK("timeout") type = pref_time, t = p->timeout;
784 CHECK("cycle") type = pref_time, t = p->cycle;
785 CHECK("lock") type = pref_bool, b = p->lock_p;
786 CHECK("lockVTs") continue; /* don't save, unused */
787 CHECK("lockTimeout") type = pref_time, t = p->lock_timeout;
788 CHECK("passwdTimeout") type = pref_time, t = p->passwd_timeout;
789 CHECK("visualID") type = pref_str, s = visual_name;
790 CHECK("installColormap") type = pref_bool, b = p->install_cmap_p;
791 CHECK("verbose") type = pref_bool, b = p->verbose_p;
792 CHECK("timestamp") type = pref_bool, b = p->timestamp_p;
793 CHECK("splash") type = pref_bool, b = p->splash_p;
794 CHECK("splashDuration") type = pref_time, t = p->splash_duration;
796 CHECK("quad") type = pref_bool, b = p->quad_p;
797 # else /* !QUAD_MODE */
798 CHECK("quad") continue; /* don't save */
799 # endif /* !QUAD_MODE */
800 CHECK("demoCommand") type = pref_str, s = p->demo_command;
801 CHECK("prefsCommand") type = pref_str, s = p->prefs_command;
802 /* CHECK("helpURL") type = pref_str, s = p->help_url; */
803 CHECK("helpURL") continue; /* don't save */
804 /* CHECK("loadURL") type = pref_str, s = p->load_url_command; */
805 CHECK("loadURL") continue; /* don't save */
806 /* CHECK("newLoginCommand") type = pref_str, s = p->new_login_command; */
807 CHECK("newLoginCommand") continue; /* don't save */
808 CHECK("nice") type = pref_int, i = p->nice_inferior;
809 CHECK("memoryLimit") type = pref_byte, i = p->inferior_memory_limit;
810 CHECK("fade") type = pref_bool, b = p->fade_p;
811 CHECK("unfade") type = pref_bool, b = p->unfade_p;
812 CHECK("fadeSeconds") type = pref_time, t = p->fade_seconds;
813 CHECK("fadeTicks") type = pref_int, i = p->fade_ticks;
814 CHECK("captureStderr") type = pref_bool, b = p->capture_stderr_p;
815 CHECK("captureStdout") continue; /* don't save */
816 CHECK("logFile") continue; /* don't save */
817 CHECK("ignoreUninstalledPrograms")
818 type = pref_bool, b = p->ignore_uninstalled_p;
820 CHECK("font") type = pref_str, s = stderr_font;
822 CHECK("dpmsEnabled") type = pref_bool, b = p->dpms_enabled_p;
823 CHECK("dpmsQuickOff") type = pref_bool, b = p->dpms_quickoff_p;
824 CHECK("dpmsStandby") type = pref_time, t = p->dpms_standby;
825 CHECK("dpmsSuspend") type = pref_time, t = p->dpms_suspend;
826 CHECK("dpmsOff") type = pref_time, t = p->dpms_off;
828 CHECK("grabDesktopImages") type =pref_bool, b = p->grab_desktop_p;
829 CHECK("grabVideoFrames") type =pref_bool, b = p->grab_video_p;
830 CHECK("chooseRandomImages")type =pref_bool, b = p->random_image_p;
831 CHECK("imageDirectory") type =pref_str, s = p->image_directory;
833 CHECK("mode") type = pref_str,
834 s = (p->mode == ONE_HACK ? "one" :
835 p->mode == BLANK_ONLY ? "blank" :
836 p->mode == DONT_BLANK ? "off" :
837 p->mode == RANDOM_HACKS_SAME
840 CHECK("selected") type = pref_int, i = p->selected_hack;
842 CHECK("textMode") type = pref_str,
843 s = (p->tmode == TEXT_URL ? "url" :
844 p->tmode == TEXT_LITERAL ? "literal" :
845 p->tmode == TEXT_FILE ? "file" :
846 p->tmode == TEXT_PROGRAM ? "program" :
848 CHECK("textLiteral") type = pref_str, s = p->text_literal;
849 CHECK("textFile") type = pref_str, s = p->text_file;
850 CHECK("textProgram") type = pref_str, s = p->text_program;
851 CHECK("textURL") type = pref_str, s = p->text_url;
853 CHECK("programs") type = pref_str, s = programs;
854 CHECK("pointerPollTime") type = pref_time, t = p->pointer_timeout;
855 CHECK("pointerHysteresis")type = pref_int, i = p->pointer_hysteresis;
856 CHECK("windowCreationTimeout")type=pref_time,t= p->notice_events_timeout;
857 CHECK("initialDelay") type = pref_time, t = p->initial_delay;
858 CHECK("sgiSaverExtension") continue; /* don't save */
859 CHECK("mitSaverExtension") continue; /* don't save */
860 CHECK("xidleExtension") continue; /* don't save */
861 CHECK("procInterrupts") type = pref_bool, b = p->use_proc_interrupts;
862 CHECK("xinputExtensionDev") type = pref_bool, b = p->use_xinput_extension;
863 CHECK("GetViewPortIsFullOfLies") type = pref_bool,
864 b = p->getviewport_full_of_lies_p;
865 CHECK("overlayStderr") type = pref_bool, b = overlay_stderr_p;
866 CHECK("overlayTextBackground") continue; /* don't save */
867 CHECK("overlayTextForeground") continue; /* don't save */
868 CHECK("bourneShell") continue; /* don't save */
869 CHECK("authWarningSlack") type = pref_int, i = p->auth_warning_slack;
878 sprintf(buf, "%d", i);
882 s = b ? "True" : "False";
886 unsigned int hour = 0, min = 0, sec = (unsigned int) (t/1000);
897 sprintf (buf, "%u:%02u:%02u", hour, min, sec);
903 if (i >= (1<<30) && i == ((i >> 30) << 30))
904 sprintf(buf, "%dG", i >> 30);
905 else if (i >= (1<<20) && i == ((i >> 20) << 20))
906 sprintf(buf, "%dM", i >> 20);
907 else if (i >= (1<<10) && i == ((i >> 10) << 10))
908 sprintf(buf, "%dK", i >> 10);
910 sprintf(buf, "%d", i);
919 if (pr && (!strcmp(pr, "mode") || !strcmp(pr, "textMode")))
922 write_entry (out, pr, s);
927 if (visual_name) free(visual_name);
928 if (stderr_font) free(stderr_font);
929 if (programs) free(programs);
931 if (fclose(out) == 0)
933 time_t write_date = 0;
935 if (stat(tmp_name, &st) == 0)
937 write_date = st.st_mtime;
941 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
942 sprintf(buf, "%s: couldn't stat \"%s\"", blurb(), tmp_name);
949 if (rename (tmp_name, name) != 0)
951 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
952 sprintf(buf, "%s: error renaming \"%s\" to \"%s\"",
953 blurb(), tmp_name, name);
961 p->init_file_date = write_date;
963 /* Since the .xscreensaver file is used for IPC, let's try and make
964 sure that the bits actually land on the disk right away. */
967 status = 0; /* wrote and renamed successfully! */
972 char *buf = (char *) malloc(1024 + strlen(name));
973 sprintf(buf, "%s: error closing \"%s\"", blurb(), name);
986 /* Parsing the resource database
990 free_screenhack (screenhack *hack)
992 if (hack->visual) free (hack->visual);
993 if (hack->name) free (hack->name);
994 free (hack->command);
995 memset (hack, 0, sizeof(*hack));
1000 free_screenhack_list (screenhack **list, int count)
1004 for (i = 0; i < count; i++)
1006 free_screenhack (list[i]);
1012 /* Populate `saver_preferences' with the contents of the resource database.
1013 Note that this may be called multiple times -- it is re-run each time
1014 the ~/.xscreensaver file is reloaded.
1016 This function can be very noisy, since it issues resource syntax errors
1020 load_init_file (Display *dpy, saver_preferences *p)
1022 static Bool first_time = True;
1024 screenhack **system_default_screenhacks = 0;
1025 int system_default_screenhack_count = 0;
1029 /* Get the programs resource before the .xscreensaver file has been
1030 parsed and merged into the resource database for the first time:
1031 this is the value of *programs from the app-defaults file.
1032 Then clear it out so that it will be parsed again later, after
1033 the init file has been read.
1035 get_screenhacks (dpy, p);
1036 system_default_screenhacks = p->screenhacks;
1037 system_default_screenhack_count = p->screenhacks_count;
1039 p->screenhacks_count = 0;
1042 if (parse_init_file (p) != 0) /* file might have gone away */
1043 if (!first_time) return;
1047 p->xsync_p = get_boolean_resource (dpy, "synchronous", "Synchronous");
1048 p->verbose_p = get_boolean_resource (dpy, "verbose", "Boolean");
1049 p->timestamp_p = get_boolean_resource (dpy, "timestamp", "Boolean");
1050 p->lock_p = get_boolean_resource (dpy, "lock", "Boolean");
1051 p->fade_p = get_boolean_resource (dpy, "fade", "Boolean");
1052 p->unfade_p = get_boolean_resource (dpy, "unfade", "Boolean");
1053 p->fade_seconds = 1000 * get_seconds_resource (dpy, "fadeSeconds", "Time");
1054 p->fade_ticks = get_integer_resource (dpy, "fadeTicks", "Integer");
1055 p->install_cmap_p = get_boolean_resource (dpy, "installColormap", "Boolean");
1056 p->nice_inferior = get_integer_resource (dpy, "nice", "Nice");
1057 p->inferior_memory_limit = get_byte_resource (dpy, "memoryLimit",
1059 p->splash_p = get_boolean_resource (dpy, "splash", "Boolean");
1061 p->quad_p = get_boolean_resource (dpy, "quad", "Boolean");
1063 p->capture_stderr_p = get_boolean_resource (dpy, "captureStderr", "Boolean");
1064 p->ignore_uninstalled_p = get_boolean_resource (dpy,
1065 "ignoreUninstalledPrograms",
1068 p->initial_delay = 1000 * get_seconds_resource (dpy, "initialDelay", "Time");
1069 p->splash_duration = 1000 * get_seconds_resource (dpy, "splashDuration", "Time");
1070 p->timeout = 1000 * get_minutes_resource (dpy, "timeout", "Time");
1071 p->lock_timeout = 1000 * get_minutes_resource (dpy, "lockTimeout", "Time");
1072 p->cycle = 1000 * get_minutes_resource (dpy, "cycle", "Time");
1073 p->passwd_timeout = 1000 * get_seconds_resource (dpy, "passwdTimeout", "Time");
1074 p->pointer_timeout = 1000 * get_seconds_resource (dpy, "pointerPollTime", "Time");
1075 p->pointer_hysteresis = get_integer_resource (dpy, "pointerHysteresis","Integer");
1076 p->notice_events_timeout = 1000*get_seconds_resource(dpy,
1077 "windowCreationTimeout",
1080 p->dpms_enabled_p = get_boolean_resource (dpy, "dpmsEnabled", "Boolean");
1081 p->dpms_quickoff_p = get_boolean_resource (dpy, "dpmsQuickOff", "Boolean");
1082 p->dpms_standby = 1000 * get_minutes_resource (dpy, "dpmsStandby", "Time");
1083 p->dpms_suspend = 1000 * get_minutes_resource (dpy, "dpmsSuspend", "Time");
1084 p->dpms_off = 1000 * get_minutes_resource (dpy, "dpmsOff", "Time");
1086 p->grab_desktop_p = get_boolean_resource (dpy, "grabDesktopImages", "Boolean");
1087 p->grab_video_p = get_boolean_resource (dpy, "grabVideoFrames", "Boolean");
1088 p->random_image_p = get_boolean_resource (dpy, "chooseRandomImages", "Boolean");
1089 p->image_directory = get_string_resource (dpy,
1093 p->text_literal = get_string_resource (dpy, "textLiteral", "TextLiteral");
1094 p->text_file = get_string_resource (dpy, "textFile", "TextFile");
1095 p->text_program = get_string_resource (dpy, "textProgram", "TextProgram");
1096 p->text_url = get_string_resource (dpy, "textURL", "TextURL");
1098 p->shell = get_string_resource (dpy, "bourneShell", "BourneShell");
1100 p->demo_command = get_string_resource(dpy, "demoCommand", "URL");
1101 p->prefs_command = get_string_resource(dpy, "prefsCommand", "URL");
1102 p->help_url = get_string_resource(dpy, "helpURL", "URL");
1103 p->load_url_command = get_string_resource(dpy, "loadURL", "LoadURL");
1104 p->new_login_command = get_string_resource(dpy,
1107 p->auth_warning_slack = get_integer_resource(dpy, "authWarningSlack",
1110 /* If "*splash" is unset, default to true. */
1112 char *s = get_string_resource (dpy, "splash", "Boolean");
1119 /* If "*grabDesktopImages" is unset, default to true. */
1121 char *s = get_string_resource (dpy, "grabDesktopImages", "Boolean");
1125 p->grab_desktop_p = True;
1128 p->use_xidle_extension = get_boolean_resource (dpy, "xidleExtension","Boolean");
1129 #if 0 /* obsolete. */
1130 p->use_sgi_saver_extension = get_boolean_resource (dpy,
1131 "sgiSaverExtension",
1135 p->use_xinput_extension = get_boolean_resource (dpy, "xinputExtensionDev",
1138 #if 0 /* broken and evil. */
1139 p->use_mit_saver_extension = get_boolean_resource (dpy,
1140 "mitSaverExtension",
1144 p->use_proc_interrupts = get_boolean_resource (dpy,
1145 "procInterrupts", "Boolean");
1147 p->getviewport_full_of_lies_p =
1148 get_boolean_resource (dpy, "GetViewPortIsFullOfLies", "Boolean");
1150 get_screenhacks (dpy, p); /* Parse the "programs" resource. */
1153 char *s = get_string_resource (dpy, "selected", "Integer");
1155 p->selected_hack = -1;
1157 p->selected_hack = get_integer_resource (dpy, "selected", "Integer");
1159 if (p->selected_hack < 0 || p->selected_hack >= p->screenhacks_count)
1160 p->selected_hack = -1;
1164 char *s = get_string_resource (dpy, "mode", "Mode");
1165 if (s && !strcasecmp (s, "one")) p->mode = ONE_HACK;
1166 else if (s && !strcasecmp (s, "blank")) p->mode = BLANK_ONLY;
1167 else if (s && !strcasecmp (s, "off")) p->mode = DONT_BLANK;
1168 else if (s && !strcasecmp (s, "random-same")) p->mode = RANDOM_HACKS_SAME;
1169 else p->mode = RANDOM_HACKS;
1174 char *s = get_string_resource (dpy, "textMode", "TextMode");
1175 if (s && !strcasecmp (s, "url")) p->tmode = TEXT_URL;
1176 else if (s && !strcasecmp (s, "literal")) p->tmode = TEXT_LITERAL;
1177 else if (s && !strcasecmp (s, "file")) p->tmode = TEXT_FILE;
1178 else if (s && !strcasecmp (s, "program")) p->tmode = TEXT_PROGRAM;
1179 else p->tmode = TEXT_DATE;
1183 if (system_default_screenhack_count) /* note: first_time is also true */
1185 merge_system_screenhacks (dpy, p, system_default_screenhacks,
1186 system_default_screenhack_count);
1187 free_screenhack_list (system_default_screenhacks,
1188 system_default_screenhack_count);
1189 system_default_screenhacks = 0;
1190 system_default_screenhack_count = 0;
1196 p->verbose_p = True;
1197 p->timestamp_p = True;
1198 p->initial_delay = 0;
1201 /* Throttle the various timeouts to reasonable values after reading the
1203 stop_the_insanity (p);
1207 /* If there are any hacks in the system-wide defaults that are not in
1208 the ~/.xscreensaver file, add the new ones to the end of the list.
1209 This does *not* actually save the file.
1212 merge_system_screenhacks (Display *dpy, saver_preferences *p,
1213 screenhack **system_list, int system_count)
1215 /* Yeah yeah, this is an N^2 operation, but I don't have hashtables handy,
1220 for (i = 0; i < system_count; i++)
1223 Bool matched_p = False;
1225 for (j = 0; j < p->screenhacks_count; j++)
1228 if (!system_list[i]->name)
1229 system_list[i]->name = make_hack_name (dpy,
1230 system_list[i]->command);
1232 name = p->screenhacks[j]->name;
1234 name = make_hack_name (dpy, p->screenhacks[j]->command);
1236 matched_p = !strcasecmp (name, system_list[i]->name);
1238 if (name != p->screenhacks[j]->name)
1247 /* We have an entry in the system-wide list that is not in the
1248 user's .xscreensaver file. Add it to the end.
1249 Note that p->screenhacks is a single malloc block, not a
1250 linked list, so we have to realloc it.
1252 screenhack *oh = system_list[i];
1253 screenhack *nh = (screenhack *) malloc (sizeof(screenhack));
1255 if (made_space == 0)
1258 p->screenhacks = (screenhack **)
1259 realloc (p->screenhacks,
1260 (p->screenhacks_count + made_space + 1)
1261 * sizeof(screenhack));
1262 if (!p->screenhacks) abort();
1265 nh->enabled_p = oh->enabled_p;
1266 nh->visual = oh->visual ? strdup(oh->visual) : 0;
1267 nh->name = oh->name ? strdup(oh->name) : 0;
1268 nh->command = oh->command ? strdup(oh->command) : 0;
1270 p->screenhacks[p->screenhacks_count++] = nh;
1271 p->screenhacks[p->screenhacks_count] = 0;
1275 fprintf (stderr, "%s: noticed new hack: %s\n", blurb(),
1276 (nh->name ? nh->name : make_hack_name (dpy, nh->command)));
1284 /* Parsing the programs resource.
1288 parse_screenhack (const char *line)
1290 screenhack *h = (screenhack *) calloc (1, sizeof(*h));
1293 h->enabled_p = True;
1295 while (isspace(*line)) line++; /* skip whitespace */
1296 if (*line == '-') /* handle "-" */
1298 h->enabled_p = False;
1300 while (isspace(*line)) line++; /* skip whitespace */
1303 s = line; /* handle "visual:" */
1304 while (*line && *line != ':' && *line != '"' && !isspace(*line))
1310 h->visual = (char *) malloc (line-s+1);
1311 strncpy (h->visual, s, line-s);
1312 h->visual[line-s] = 0;
1313 if (*line == ':') line++; /* skip ":" */
1314 while (isspace(*line)) line++; /* skip whitespace */
1317 if (*line == '"') /* handle "name" */
1321 while (*line && *line != '"')
1323 h->name = (char *) malloc (line-s+1);
1324 strncpy (h->name, s, line-s);
1325 h->name[line-s] = 0;
1326 if (*line == '"') line++; /* skip "\"" */
1327 while (isspace(*line)) line++; /* skip whitespace */
1330 h->command = format_command (line, False); /* handle command */
1336 format_command (const char *cmd, Bool wrap_p)
1340 char *cmd2 = (char *) calloc (1, 2 * (strlen (cmd) + 1));
1341 const char *in = cmd;
1345 /* shrink all whitespace to one space, for the benefit of the "demo"
1346 mode display. We only do this when we can easily tell that the
1347 whitespace is not significant (no shell metachars).
1351 case '\'': case '"': case '`': case '\\':
1352 /* Metachars are scary. Copy the rest of the line unchanged. */
1354 *out++ = *in++, col++;
1357 case ' ': case '\t':
1358 /* Squeeze all other whitespace down to one space. */
1359 while (*in == ' ' || *in == '\t')
1361 *out++ = ' ', col++;
1365 /* Copy other chars unchanged. */
1366 *out++ = *in++, col++;
1373 /* Strip trailing whitespace */
1374 while (out > cmd2 && isspace (out[-1]))
1381 /* Returns a new string describing the shell command.
1382 This may be just the name of the program, capitalized.
1383 It also may be something from the resource database (gotten
1384 by looking for "hacks.XYZ.name", where XYZ is the program.)
1387 make_hack_name (Display *dpy, const char *shell_command)
1389 char *s = strdup (shell_command);
1393 for (s2 = s; *s2; s2++) /* truncate at first whitespace */
1400 s2 = strrchr (s, '/'); /* if pathname, take last component */
1408 if (strlen (s) > 50) /* 51 is hereby defined as "unreasonable" */
1411 sprintf (res_name, "hacks.%s.name", s); /* resource? */
1412 s2 = get_string_resource (dpy, res_name, res_name);
1419 for (s2 = s; *s2; s2++) /* if it has any capitals, return it */
1420 if (*s2 >= 'A' && *s2 <= 'Z')
1423 if (s[0] >= 'a' && s[0] <= 'z') /* else cap it */
1425 if (s[0] == 'X' && s[1] >= 'a' && s[1] <= 'z') /* (magic leading X) */
1427 if (s[0] == 'G' && s[1] == 'l' &&
1428 s[2] >= 'a' && s[2] <= 'z') /* (magic leading GL) */
1436 format_hack (Display *dpy, screenhack *hack, Bool wrap_p)
1443 char *def_name = make_hack_name (dpy, hack->command);
1445 /* Don't ever write out a name for a hack if it's the same as the default.
1447 if (hack->name && !strcmp (hack->name, def_name))
1454 size = (2 * (strlen(hack->command) +
1455 (hack->visual ? strlen(hack->visual) : 0) +
1456 (hack->name ? strlen(hack->name) : 0) +
1458 h2 = (char *) malloc (size);
1461 if (!hack->enabled_p) *out++ = '-'; /* write disabled flag */
1463 if (hack->visual && *hack->visual) /* write visual name */
1465 if (hack->enabled_p) *out++ = ' ';
1467 strcpy (out, hack->visual);
1468 out += strlen (hack->visual);
1474 col = string_columns (h2, strlen (h2), 0);
1476 if (hack->name && *hack->name) /* write pretty name */
1478 int L = (strlen (hack->name) + 2);
1480 out = stab_to (out, col, tab - L - 2);
1484 strcpy (out, hack->name);
1485 out += strlen (hack->name);
1489 col = string_columns (h2, strlen (h2), 0);
1490 if (wrap_p && col >= tab)
1491 out = stab_to (out, col, 77);
1495 if (out >= h2+size) abort();
1499 col = string_columns (h2, strlen (h2), 0);
1500 out = stab_to (out, col, tab); /* indent */
1502 if (out >= h2+size) abort();
1503 s = format_command (hack->command, wrap_p);
1514 get_screenhacks (Display *dpy, saver_preferences *p)
1522 d = get_string_resource (dpy, "monoPrograms", "MonoPrograms");
1523 if (d && !*d) { free(d); d = 0; }
1525 d = get_string_resource (dpy, "colorPrograms", "ColorPrograms");
1526 if (d && !*d) { free(d); d = 0; }
1531 "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
1532 see the manual for details.\n", blurb());
1536 d = get_string_resource (dpy, "programs", "Programs");
1538 free_screenhack_list (p->screenhacks, p->screenhacks_count);
1540 p->screenhacks_count = 0;
1548 /* Count up the number of newlines (which will be equal to or larger than
1549 one less than the number of hacks.)
1551 for (i = j = 0; d[i]; i++)
1556 p->screenhacks = (screenhack **) calloc (j + 1, sizeof (screenhack *));
1558 /* Iterate over the lines in `d' (the string with newlines)
1559 and make new strings to stuff into the `screenhacks' array.
1561 p->screenhacks_count = 0;
1562 while (start < size)
1564 /* skip forward over whitespace. */
1565 while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n')
1568 /* skip forward to newline or end of string. */
1570 while (d[end] != 0 && d[end] != '\n')
1573 /* null terminate. */
1576 p->screenhacks[p->screenhacks_count++] = parse_screenhack (d + start);
1577 if (p->screenhacks_count >= i)
1585 if (p->screenhacks_count == 0)
1587 free (p->screenhacks);
1593 /* Make sure all the values in the preferences struct are sane.
1596 stop_the_insanity (saver_preferences *p)
1598 if (p->passwd_timeout <= 0) p->passwd_timeout = 30000; /* 30 secs */
1599 if (p->timeout < 15000) p->timeout = 15000; /* 15 secs */
1600 if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */
1601 if (p->pointer_timeout <= 0) p->pointer_timeout = 5000; /* 5 secs */
1602 if (p->notice_events_timeout <= 0)
1603 p->notice_events_timeout = 10000; /* 10 secs */
1604 if (p->fade_seconds <= 0 || p->fade_ticks <= 0)
1606 if (! p->fade_p) p->unfade_p = False;
1608 /* The DPMS settings may have the value 0.
1609 But if they are negative, or are a range less than 10 seconds,
1610 reset them to sensible defaults. (Since that must be a mistake.)
1612 if (p->dpms_standby != 0 &&
1613 p->dpms_standby < 10 * 1000)
1614 p->dpms_standby = 2 * 60 * 60 * 1000; /* 2 hours */
1615 if (p->dpms_suspend != 0 &&
1616 p->dpms_suspend < 10 * 1000)
1617 p->dpms_suspend = 2 * 60 * 60 * 1000; /* 2 hours */
1618 if (p->dpms_off != 0 &&
1619 p->dpms_off < 10 * 1000)
1620 p->dpms_off = 4 * 60 * 60 * 1000; /* 4 hours */
1622 /* suspend may not be greater than off, unless off is 0.
1623 standby may not be greater than suspend, unless suspend is 0.
1625 if (p->dpms_off != 0 &&
1626 p->dpms_suspend > p->dpms_off)
1627 p->dpms_suspend = p->dpms_off;
1628 if (p->dpms_suspend != 0 &&
1629 p->dpms_standby > p->dpms_suspend)
1630 p->dpms_standby = p->dpms_suspend;
1632 /* These fixes above ignores the case
1633 suspend = 0 and standby > off ...
1635 if (p->dpms_off != 0 &&
1636 p->dpms_standby > p->dpms_off)
1637 p->dpms_standby = p->dpms_off;
1640 if (p->dpms_standby == 0 && /* if *all* are 0, then DPMS is disabled */
1641 p->dpms_suspend == 0 &&
1643 !(p->dpms_quickoff_p) /* ... but we want to do DPMS quick off */
1645 p->dpms_enabled_p = False;
1648 /* Set watchdog timeout to about half of the cycle timeout, but
1649 don't let it be faster than 1/2 minute or slower than 1 minute.
1651 p->watchdog_timeout = p->cycle * 0.6;
1652 if (p->watchdog_timeout < 27000) p->watchdog_timeout = 27000; /* 27 secs */
1653 if (p->watchdog_timeout > 57000) p->watchdog_timeout = 57000; /* 57 secs */
1655 if (p->pointer_hysteresis < 0) p->pointer_hysteresis = 0;
1656 if (p->pointer_hysteresis > 100) p->pointer_hysteresis = 100;
1658 if (p->auth_warning_slack < 0) p->auth_warning_slack = 0;
1659 if (p->auth_warning_slack > 300) p->auth_warning_slack = 300;
1666 /* If you are in here because you're planning on disabling this warning
1667 before redistributing my software, please don't.
1669 I sincerely request that you do one of the following:
1671 1: leave this code intact and this warning in place, -OR-
1673 2: Remove xscreensaver from your distribution.
1675 I would seriously prefer that you not distribute my software at all
1676 than that you distribute one version and then never update it for
1679 I am *constantly* getting email from users reporting bugs that have
1680 been fixed for literally years who have no idea that the software
1681 they are running is years out of date. Yes, it would be great if we
1682 lived in the ideal world where people checked that they were running
1683 the latest release before they report a bug, but we don't. To most
1684 people, "running the latest release" is synonymous with "running the
1685 latest release that my distro packages for me."
1687 When they even bother to tell me what version they're running, I
1688 say, "That version is three years old!", and they say "But this is
1689 the latest version my distro ships". Then I say, "your distro
1690 sucks", and they say "but I don't know how to compile from source,
1691 herp derp I eat paste", and *everybody* goes away unhappy.
1693 It wastes an enormous amount of my time, but worse than that, it
1694 does a grave disservice to the users, who are stuck experiencing
1695 bugs that are already fixed! These users think they are running the
1696 latest release, and they are not. They would like to be running the
1697 actual latest release, but they don't know how, because their distro
1698 makes that very difficult for them. It's terrible for everyone, and
1699 kind of makes me regret ever having released this software in the
1702 So seriously. I ask that if you're planning on disabling this
1703 obsolescence warning, that you instead just remove xscreensaver from
1704 your distro entirely. Everybody will be happier that way. Check
1705 out gnome-screensaver instead, I understand it's really nice.
1707 Of course, my license allows you to ignore me and do whatever the
1708 fuck you want, but as the author, I hope you will have the common
1709 courtesy of complying with my request.
1715 PS: In particular, since Debian refuses to upgrade software on any
1716 kind of rational timeline, I have asked that they stop shipping
1717 xscreensaver at all. They have refused. Instead of upgrading the
1718 software, they simply patched out this warning.
1720 If you want to witness the sad state of the open source peanut
1721 gallery, look no farther than the comments on my blog:
1722 http://jwz.org/b/yiYo
1724 Many of these people fall back on their go-to argument of, "If it is
1725 legal, it must be right." If you believe in that rhetorical device
1726 then you are a terrible person, and possibly a sociopath.
1728 There are also the armchair lawyers who say "Well, instead of
1729 *asking* people to do the right thing out of common courtesy, you
1730 should just change your license to prohibit them from acting
1731 amorally." Again, this is the answer of a sociopath, but that aside,
1732 if you devote even a second's thought to this you will realize that
1733 the end result of this would be for distros like Debian to just keep
1734 shipping the last version with the old license and then never
1735 upgrading it again -- which would be the worst possible outcome for
1736 everyone involved, most especially the users.
1739 time_t now = time ((time_t *) 0); /* */
1740 struct tm *tm = localtime (&now); /* d */
1741 const char *s = screensaver_id; /* o */
1742 char mon[4], year[5]; /* n */
1743 int m, y, months; /* ' */
1744 s = strchr (s, ' '); if (!s) abort(); s++; /* t */
1745 s = strchr (s, '('); if (!s) abort(); s++; /* */
1746 s = strchr (s, '-'); if (!s) abort(); s++; /* d */
1747 strncpy (mon, s, 3); /* o */
1749 s = strchr (s, '-'); if (!s) abort(); s++; /* i */
1750 strncpy (year, s, 4); /* t */
1751 year[4] = 0; /* , */
1752 y = atoi (year); /* */
1753 if (!strcmp(mon, "Jan")) m = 0; /* s */
1754 else if (!strcmp(mon, "Feb")) m = 1; /* t */
1755 else if (!strcmp(mon, "Mar")) m = 2; /* o */
1756 else if (!strcmp(mon, "Apr")) m = 3; /* p */
1757 else if (!strcmp(mon, "May")) m = 4; /* , */
1758 else if (!strcmp(mon, "Jun")) m = 5; /* */
1759 else if (!strcmp(mon, "Jul")) m = 6; /* s */
1760 else if (!strcmp(mon, "Aug")) m = 7; /* t */
1761 else if (!strcmp(mon, "Sep")) m = 8; /* a */
1762 else if (!strcmp(mon, "Oct")) m = 9; /* a */
1763 else if (!strcmp(mon, "Nov")) m = 10; /* a */
1764 else if (!strcmp(mon, "Dec")) m = 11; /* h */
1765 else abort(); /* h */
1766 months = ((((tm->tm_year + 1900) * 12) + tm->tm_mon) - /* h */
1767 (y * 12 + m)); /* p */
1769 return (months >= 17); /* */