1 /* dotfile.c --- management of the ~/.xscreensaver file.
2 * xscreensaver, Copyright (c) 1998-2005 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
31 #include <X11/Xresource.h>
40 /* This file doesn't need the Xt headers, so stub these types out... */
42 #define XtAppContext void*
43 #define XtIntervalId void*
44 #define XtPointer void*
48 /* Just in case there's something pathological about stat.h... */
50 # define S_IRUSR 00400
53 # define S_IWUSR 00200
56 # define S_IXUSR 00100
59 # define S_IXGRP 00010
62 # define S_IXOTH 00001
67 #include "resources.h"
70 extern char *progname;
71 extern char *progclass;
72 extern const char *blurb (void);
76 static void get_screenhacks (saver_preferences *p);
77 static char *format_command (const char *cmd, Bool wrap_p);
78 static void merge_system_screenhacks (saver_preferences *p,
79 screenhack **system_list, int count);
80 static void stop_the_insanity (saver_preferences *p);
84 chase_symlinks (const char *file)
90 if (realpath (file, buf))
93 sprintf (buf, "%.100s: realpath %.200s", blurb(), file);
96 # endif /* HAVE_REALPATH */
102 i_am_a_nobody (uid_t uid)
106 p = getpwnam ("nobody");
107 if (! p) p = getpwnam ("noaccess");
108 if (! p) p = getpwnam ("daemon");
110 if (! p) /* There is no nobody? */
113 return (uid == p->pw_uid);
118 init_file_name (void)
120 static char *file = 0;
124 uid_t uid = getuid ();
125 struct passwd *p = getpwuid (uid);
127 if (i_am_a_nobody (uid))
128 /* If we're running as nobody, then use root's .xscreensaver file
129 (since ~root/.xscreensaver and ~nobody/.xscreensaver are likely
130 to be different -- if we didn't do this, then xscreensaver-demo
131 would appear to have no effect when the luser is running as root.)
137 if (!p || !p->pw_name || !*p->pw_name)
139 fprintf (stderr, "%s: couldn't get user info of uid %d\n",
143 else if (!p->pw_dir || !*p->pw_dir)
145 fprintf (stderr, "%s: couldn't get home directory of \"%s\"\n",
146 blurb(), (p->pw_name ? p->pw_name : "???"));
151 const char *home = p->pw_dir;
152 const char *name = ".xscreensaver";
153 file = (char *) malloc(strlen(home) + strlen(name) + 2);
155 if (!*home || home[strlen(home)-1] != '/')
169 init_file_tmp_name (void)
171 static char *file = 0;
174 const char *name = init_file_name();
175 const char *suffix = ".tmp";
177 char *n2 = chase_symlinks (name);
184 file = (char *) malloc(strlen(name) + strlen(suffix) + 2);
186 strcat(file, suffix);
199 get_byte_resource (char *name, char *class)
201 char *s = get_string_resource (name, class);
206 while (isspace(*s2)) s2++;
207 while (*s2 >= '0' && *s2 <= '9')
209 n = (n * 10) + (*s2 - '0');
212 while (isspace(*s2)) s2++;
213 if (*s2 == 'k' || *s2 == 'K') n <<= 10;
214 else if (*s2 == 'm' || *s2 == 'M') n <<= 20;
215 else if (*s2 == 'g' || *s2 == 'G') n <<= 30;
219 fprintf (stderr, "%s: %s must be a number of bytes, not \"%s\".\n",
225 if (*s2 == 'b' || *s2 == 'B') s2++;
226 while (isspace(*s2)) s2++;
234 static const char * const prefs[] = {
238 "lockVTs", /* not saved */
251 "helpURL", /* not saved */
252 "loadURL", /* not saved */
253 "newLoginCommand", /* not saved */
261 "captureStdout", /* not saved -- obsolete */
262 "ignoreUninstalledPrograms",
270 "chooseRandomImages",
284 "windowCreationTimeout",
287 "mitSaverExtension", /* not saved -- obsolete */
289 "GetViewPortIsFullOfLies",
292 "overlayTextBackground", /* not saved -- X resources only */
293 "overlayTextForeground", /* not saved -- X resources only */
294 "bourneShell", /* not saved -- X resources only */
302 while (*s == '\t' || *s == ' ' || *s == '\r' || *s == '\n')
304 for (s2 = s; *s2; s2++)
306 for (s2--; s2 >= s; s2--)
307 if (*s2 == '\t' || *s2 == ' ' || *s2 == '\r' || *s2 =='\n')
319 handle_entry (XrmDatabase *db, const char *key, const char *value,
320 const char *filename, int line)
323 for (i = 0; prefs[i]; i++)
324 if (*prefs[i] && !strcasecmp(key, prefs[i]))
326 char *val = strdup(value);
327 char *spec = (char *) malloc(strlen(progclass) + strlen(prefs[i]) +10);
328 strcpy(spec, progclass);
330 strcat(spec, prefs[i]);
332 XrmPutStringResource (db, spec, val);
339 fprintf(stderr, "%s: %s:%d: unknown option \"%s\"\n",
340 blurb(), filename, line, key);
346 parse_init_file (saver_preferences *p)
348 time_t write_date = 0;
349 const char *name = init_file_name();
358 if (stat(name, &st) != 0)
360 p->init_file_date = 0;
364 in = fopen(name, "r");
367 char *buf = (char *) malloc(1024 + strlen(name));
368 sprintf(buf, "%s: error reading \"%s\"", blurb(), name);
374 if (fstat (fileno(in), &st) == 0)
376 write_date = st.st_mtime;
380 char *buf = (char *) malloc(1024 + strlen(name));
381 sprintf(buf, "%s: couldn't re-stat \"%s\"", blurb(), name);
387 buf = (char *) malloc(buf_size);
389 while (fgets (buf, buf_size-1, in))
396 (buf[L-1] != '\n' || /* whole line didn't fit in buffer */
397 buf[L-2] == '\\')) /* or line ended with backslash */
399 if (buf[L-2] == '\\') /* backslash-newline gets swallowed */
405 buf = (char *) realloc(buf, buf_size);
409 if (!fgets (buf + L, buf_size-L-1, in))
414 /* Now handle other backslash escapes. */
417 for (i = 0; buf[i]; i++)
422 case 'n': buf[i] = '\n'; break;
423 case 'r': buf[i] = '\r'; break;
424 case 't': buf[i] = '\t'; break;
425 default: buf[i] = buf[i+1]; break;
427 for (j = i+2; buf[j]; j++)
435 if (*key == '#' || *key == '!' || *key == ';' ||
436 *key == '\n' || *key == 0)
439 value = strchr (key, ':');
442 fprintf(stderr, "%s: %s:%d: unparsable line: %s\n", blurb(),
449 value = strip(value);
453 handle_entry (&p->db, key, value, name, line);
458 p->init_file_date = write_date;
464 init_file_changed_p (saver_preferences *p)
466 const char *name = init_file_name();
469 if (!name) return False;
471 if (stat(name, &st) != 0)
474 if (p->init_file_date == st.st_mtime)
485 tab_to (FILE *out, int from, int to)
488 int to_mod = (to / tab_width) * tab_width;
489 while (from < to_mod)
492 from = (((from / tab_width) + 1) * tab_width);
503 stab_to (char *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 string_columns (const char *string, int length, int start)
525 const char *end = string + length;
530 else if (*string == '\t')
531 col = (((col / tab_width) + 1) * tab_width);
541 write_entry (FILE *out, const char *key, const char *value)
543 char *v = strdup(value ? value : "");
547 Bool programs_p = (!strcmp(key, "programs"));
548 int tab = (programs_p ? 32 : 16);
551 fprintf(out, "%s:", key);
552 col = strlen(key) + 1;
554 if (strlen(key) > 14)
555 col = tab_to (out, col, 20);
561 nl = strchr(v2, '\n');
565 if (first && programs_p)
567 col = tab_to (out, col, 77);
568 fprintf (out, " \\\n");
576 col = tab_to (out, col, 75);
577 fprintf (out, " \\n\\\n");
582 col = tab_to (out, col, tab);
585 string_columns(v2, strlen (v2), col) + col > 75)
592 while (v2[end] == ' ' || v2[end] == '\t')
594 while (v2[end] != ' ' && v2[end] != '\t' &&
595 v2[end] != '\n' && v2[end] != 0)
597 if (string_columns (v2 + start, (end - start), col) >= 74)
599 col = tab_to (out, col, 75);
600 fprintf(out, " \\\n");
601 col = tab_to (out, 0, tab + 2);
602 while (v2[start] == ' ' || v2[start] == '\t')
606 col = string_columns (v2 + start, (end - start), col);
608 fputc(v2[start++], out);
613 fprintf (out, "%s", v2);
614 col += string_columns(v2, strlen (v2), col);
628 write_init_file (saver_preferences *p, const char *version_string,
632 const char *name = init_file_name();
633 const char *tmp_name = init_file_tmp_name();
634 char *n2 = chase_symlinks (name);
638 /* Kludge, since these aren't in the saver_preferences struct as strings...
642 Bool overlay_stderr_p;
650 /* Throttle the various timeouts to reasonable values before writing
652 stop_the_insanity (p);
656 fprintf (stderr, "%s: writing \"%s\".\n", blurb(), name);
659 out = fopen(tmp_name, "w");
662 char *buf = (char *) malloc(1024 + strlen(name));
663 sprintf(buf, "%s: error writing \"%s\"", blurb(), name);
669 /* Give the new .xscreensaver file the same permissions as the old one;
670 except ensure that it is readable and writable by owner, and not
671 executable. Extra hack: if we're running as root, make the file
672 be world-readable (so that the daemon, running as "nobody", will
673 still be able to read it.)
675 if (stat(name, &st) == 0)
677 mode_t mode = st.st_mode;
678 mode |= S_IRUSR | S_IWUSR; /* read/write by user */
679 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); /* executable by none */
681 if (getuid() == (uid_t) 0) /* read by group/other */
682 mode |= S_IRGRP | S_IROTH;
684 if (fchmod (fileno(out), mode) != 0)
686 char *buf = (char *) malloc(1024 + strlen(name));
687 sprintf (buf, "%s: error fchmodding \"%s\" to 0%o", blurb(),
688 tmp_name, (unsigned int) mode);
695 /* Kludge, since these aren't in the saver_preferences struct... */
696 visual_name = get_string_resource ("visualID", "VisualID");
698 overlay_stderr_p = get_boolean_resource ("overlayStderr", "Boolean");
699 stderr_font = get_string_resource ("font", "Font");
704 char **hack_strings = (char **)
705 calloc (p->screenhacks_count, sizeof(char *));
707 for (j = 0; j < p->screenhacks_count; j++)
709 hack_strings[j] = format_hack (p->screenhacks[j], True);
710 i += strlen (hack_strings[j]);
714 ss = programs = (char *) malloc(i + 10);
716 for (j = 0; j < p->screenhacks_count; j++)
718 strcat (ss, hack_strings[j]);
719 free (hack_strings[j]);
728 struct passwd *pw = getpwuid (getuid ());
729 char *whoami = (pw && pw->pw_name && *pw->pw_name
732 time_t now = time ((time_t *) 0);
733 char *timestr = (char *) ctime (&now);
734 char *nl = (char *) strchr (timestr, '\n');
737 "# %s Preferences File\n"
738 "# Written by %s %s for %s on %s.\n"
739 "# http://www.jwz.org/xscreensaver/\n"
741 progclass, progname, version_string, whoami, timestr);
744 for (j = 0; prefs[j]; j++)
747 const char *pr = prefs[j];
748 enum pref_type { pref_str, pref_int, pref_bool, pref_byte, pref_time
762 # define CHECK(X) else if (!strcmp(pr, X))
764 CHECK("timeout") type = pref_time, t = p->timeout;
765 CHECK("cycle") type = pref_time, t = p->cycle;
766 CHECK("lock") type = pref_bool, b = p->lock_p;
767 # if 0 /* #### not ready yet */
768 CHECK("lockVTs") type = pref_bool, b = p->lock_vt_p;
770 CHECK("lockVTs") continue; /* don't save */
772 CHECK("lockTimeout") type = pref_time, t = p->lock_timeout;
773 CHECK("passwdTimeout") type = pref_time, t = p->passwd_timeout;
774 CHECK("visualID") type = pref_str, s = visual_name;
775 CHECK("installColormap") type = pref_bool, b = p->install_cmap_p;
776 CHECK("verbose") type = pref_bool, b = p->verbose_p;
777 CHECK("timestamp") type = pref_bool, b = p->timestamp_p;
778 CHECK("splash") type = pref_bool, b = p->splash_p;
779 CHECK("splashDuration") type = pref_time, t = p->splash_duration;
780 CHECK("quad") type = pref_bool, b = p->quad_p;
781 CHECK("demoCommand") type = pref_str, s = p->demo_command;
782 CHECK("prefsCommand") type = pref_str, s = p->prefs_command;
783 /* CHECK("helpURL") type = pref_str, s = p->help_url; */
784 CHECK("helpURL") continue; /* don't save */
785 /* CHECK("loadURL") type = pref_str, s = p->load_url_command; */
786 CHECK("loadURL") continue; /* don't save */
787 /* CHECK("newLoginCommand") type = pref_str, s = p->new_login_command; */
788 CHECK("newLoginCommand") continue; /* don't save */
789 CHECK("nice") type = pref_int, i = p->nice_inferior;
790 CHECK("memoryLimit") type = pref_byte, i = p->inferior_memory_limit;
791 CHECK("fade") type = pref_bool, b = p->fade_p;
792 CHECK("unfade") type = pref_bool, b = p->unfade_p;
793 CHECK("fadeSeconds") type = pref_time, t = p->fade_seconds;
794 CHECK("fadeTicks") type = pref_int, i = p->fade_ticks;
795 CHECK("captureStderr") type = pref_bool, b = p->capture_stderr_p;
796 CHECK("captureStdout") continue; /* don't save */
797 CHECK("ignoreUninstalledPrograms")
798 type = pref_bool, b = p->ignore_uninstalled_p;
800 CHECK("font") type = pref_str, s = stderr_font;
802 CHECK("dpmsEnabled") type = pref_bool, b = p->dpms_enabled_p;
803 CHECK("dpmsStandby") type = pref_time, t = p->dpms_standby;
804 CHECK("dpmsSuspend") type = pref_time, t = p->dpms_suspend;
805 CHECK("dpmsOff") type = pref_time, t = p->dpms_off;
807 CHECK("grabDesktopImages") type =pref_bool, b = p->grab_desktop_p;
808 CHECK("grabVideoFrames") type =pref_bool, b = p->grab_video_p;
809 CHECK("chooseRandomImages")type =pref_bool, b = p->random_image_p;
810 CHECK("imageDirectory") type =pref_str, s = p->image_directory;
812 CHECK("mode") type = pref_str,
813 s = (p->mode == ONE_HACK ? "one" :
814 p->mode == BLANK_ONLY ? "blank" :
815 p->mode == DONT_BLANK ? "off" :
816 p->mode == RANDOM_HACKS_SAME
819 CHECK("selected") type = pref_int, i = p->selected_hack;
821 CHECK("textMode") type = pref_str,
822 s = (p->tmode == TEXT_URL ? "url" :
823 p->tmode == TEXT_LITERAL ? "literal" :
824 p->tmode == TEXT_FILE ? "file" :
825 p->tmode == TEXT_PROGRAM ? "program" :
827 CHECK("textLiteral") type = pref_str, s = p->text_literal;
828 CHECK("textFile") type = pref_str, s = p->text_file;
829 CHECK("textProgram") type = pref_str, s = p->text_program;
830 CHECK("textURL") type = pref_str, s = p->text_url;
832 CHECK("programs") type = pref_str, s = programs;
833 CHECK("pointerPollTime") type = pref_time, t = p->pointer_timeout;
834 CHECK("pointerHysteresis")type = pref_int, i = p->pointer_hysteresis;
835 CHECK("windowCreationTimeout")type=pref_time,t= p->notice_events_timeout;
836 CHECK("initialDelay") type = pref_time, t = p->initial_delay;
837 CHECK("sgiSaverExtension")type = pref_bool, b=p->use_sgi_saver_extension;
838 CHECK("mitSaverExtension") continue; /* don't save */
839 CHECK("xidleExtension") type = pref_bool, b = p->use_xidle_extension;
840 CHECK("procInterrupts") type = pref_bool, b = p->use_proc_interrupts;
841 CHECK("GetViewPortIsFullOfLies") type = pref_bool,
842 b = p->getviewport_full_of_lies_p;
843 CHECK("overlayStderr") type = pref_bool, b = overlay_stderr_p;
844 CHECK("overlayTextBackground") continue; /* don't save */
845 CHECK("overlayTextForeground") continue; /* don't save */
846 CHECK("bourneShell") continue;
855 sprintf(buf, "%d", i);
859 s = b ? "True" : "False";
863 unsigned int hour = 0, min = 0, sec = (unsigned int) (t/1000);
874 sprintf (buf, "%u:%02u:%02u", hour, min, sec);
880 if (i >= (1<<30) && i == ((i >> 30) << 30))
881 sprintf(buf, "%dG", i >> 30);
882 else if (i >= (1<<20) && i == ((i >> 20) << 20))
883 sprintf(buf, "%dM", i >> 20);
884 else if (i >= (1<<10) && i == ((i >> 10) << 10))
885 sprintf(buf, "%dK", i >> 10);
887 sprintf(buf, "%d", i);
896 if (pr && (!strcmp(pr, "mode") || !strcmp(pr, "textMode")))
899 write_entry (out, pr, s);
904 if (visual_name) free(visual_name);
905 if (stderr_font) free(stderr_font);
906 if (programs) free(programs);
908 if (fclose(out) == 0)
910 time_t write_date = 0;
912 if (stat(tmp_name, &st) == 0)
914 write_date = st.st_mtime;
918 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
919 sprintf(buf, "%s: couldn't stat \"%s\"", blurb(), tmp_name);
926 if (rename (tmp_name, name) != 0)
928 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
929 sprintf(buf, "%s: error renaming \"%s\" to \"%s\"",
930 blurb(), tmp_name, name);
938 p->init_file_date = write_date;
940 /* Since the .xscreensaver file is used for IPC, let's try and make
941 sure that the bits actually land on the disk right away. */
944 status = 0; /* wrote and renamed successfully! */
949 char *buf = (char *) malloc(1024 + strlen(name));
950 sprintf(buf, "%s: error closing \"%s\"", blurb(), name);
963 /* Parsing the resource database
967 free_screenhack (screenhack *hack)
969 if (hack->visual) free (hack->visual);
970 if (hack->name) free (hack->name);
971 free (hack->command);
972 memset (hack, 0, sizeof(*hack));
977 free_screenhack_list (screenhack **list, int count)
981 for (i = 0; i < count; i++)
983 free_screenhack (list[i]);
989 /* Populate `saver_preferences' with the contents of the resource database.
990 Note that this may be called multiple times -- it is re-run each time
991 the ~/.xscreensaver file is reloaded.
993 This function can be very noisy, since it issues resource syntax errors
997 load_init_file (saver_preferences *p)
999 static Bool first_time = True;
1001 screenhack **system_default_screenhacks = 0;
1002 int system_default_screenhack_count = 0;
1006 /* Get the programs resource before the .xscreensaver file has been
1007 parsed and merged into the resource database for the first time:
1008 this is the value of *programs from the app-defaults file.
1009 Then clear it out so that it will be parsed again later, after
1010 the init file has been read.
1012 get_screenhacks (p);
1013 system_default_screenhacks = p->screenhacks;
1014 system_default_screenhack_count = p->screenhacks_count;
1016 p->screenhacks_count = 0;
1019 if (parse_init_file (p) != 0) /* file might have gone away */
1020 if (!first_time) return;
1024 p->xsync_p = get_boolean_resource ("synchronous", "Synchronous");
1025 p->verbose_p = get_boolean_resource ("verbose", "Boolean");
1026 p->timestamp_p = get_boolean_resource ("timestamp", "Boolean");
1027 p->lock_p = get_boolean_resource ("lock", "Boolean");
1028 p->lock_vt_p = get_boolean_resource ("lockVTs", "Boolean");
1029 p->fade_p = get_boolean_resource ("fade", "Boolean");
1030 p->unfade_p = get_boolean_resource ("unfade", "Boolean");
1031 p->fade_seconds = 1000 * get_seconds_resource ("fadeSeconds", "Time");
1032 p->fade_ticks = get_integer_resource ("fadeTicks", "Integer");
1033 p->install_cmap_p = get_boolean_resource ("installColormap", "Boolean");
1034 p->nice_inferior = get_integer_resource ("nice", "Nice");
1035 p->inferior_memory_limit = get_byte_resource ("memoryLimit", "MemoryLimit");
1036 p->splash_p = get_boolean_resource ("splash", "Boolean");
1037 p->quad_p = get_boolean_resource ("quad", "Boolean");
1038 p->capture_stderr_p = get_boolean_resource ("captureStderr", "Boolean");
1039 p->ignore_uninstalled_p = get_boolean_resource ("ignoreUninstalledPrograms",
1042 p->initial_delay = 1000 * get_seconds_resource ("initialDelay", "Time");
1043 p->splash_duration = 1000 * get_seconds_resource ("splashDuration", "Time");
1044 p->timeout = 1000 * get_minutes_resource ("timeout", "Time");
1045 p->lock_timeout = 1000 * get_minutes_resource ("lockTimeout", "Time");
1046 p->cycle = 1000 * get_minutes_resource ("cycle", "Time");
1047 p->passwd_timeout = 1000 * get_seconds_resource ("passwdTimeout", "Time");
1048 p->pointer_timeout = 1000 * get_seconds_resource ("pointerPollTime", "Time");
1049 p->pointer_hysteresis = get_integer_resource ("pointerHysteresis","Integer");
1050 p->notice_events_timeout = 1000*get_seconds_resource("windowCreationTimeout",
1053 p->dpms_enabled_p = get_boolean_resource ("dpmsEnabled", "Boolean");
1054 p->dpms_standby = 1000 * get_minutes_resource ("dpmsStandby", "Time");
1055 p->dpms_suspend = 1000 * get_minutes_resource ("dpmsSuspend", "Time");
1056 p->dpms_off = 1000 * get_minutes_resource ("dpmsOff", "Time");
1058 p->grab_desktop_p = get_boolean_resource ("grabDesktopImages", "Boolean");
1059 p->grab_video_p = get_boolean_resource ("grabVideoFrames", "Boolean");
1060 p->random_image_p = get_boolean_resource ("chooseRandomImages", "Boolean");
1061 p->image_directory = get_string_resource ("imageDirectory",
1064 p->text_literal = get_string_resource ("textLiteral", "TextLiteral");
1065 p->text_file = get_string_resource ("textFile", "TextFile");
1066 p->text_program = get_string_resource ("textProgram", "TextProgram");
1067 p->text_url = get_string_resource ("textURL", "TextURL");
1069 p->shell = get_string_resource ("bourneShell", "BourneShell");
1071 p->demo_command = get_string_resource("demoCommand", "URL");
1072 p->prefs_command = get_string_resource("prefsCommand", "URL");
1073 p->help_url = get_string_resource("helpURL", "URL");
1074 p->load_url_command = get_string_resource("loadURL", "LoadURL");
1075 p->new_login_command = get_string_resource("newLoginCommand",
1078 /* If "*splash" is unset, default to true. */
1080 char *s = get_string_resource ("splash", "Boolean");
1087 /* If "*grabDesktopImages" is unset, default to true. */
1089 char *s = get_string_resource ("grabDesktopImages", "Boolean");
1093 p->grab_desktop_p = True;
1096 p->use_xidle_extension = get_boolean_resource ("xidleExtension","Boolean");
1097 #if 0 /* ignore this, it is evil. */
1098 p->use_mit_saver_extension = get_boolean_resource ("mitSaverExtension",
1101 p->use_sgi_saver_extension = get_boolean_resource ("sgiSaverExtension",
1103 p->use_proc_interrupts = get_boolean_resource ("procInterrupts", "Boolean");
1105 p->getviewport_full_of_lies_p =
1106 get_boolean_resource ("GetViewPortIsFullOfLies", "Boolean");
1108 get_screenhacks (p); /* Parse the "programs" resource. */
1111 char *s = get_string_resource ("selected", "Integer");
1113 p->selected_hack = -1;
1115 p->selected_hack = get_integer_resource ("selected", "Integer");
1117 if (p->selected_hack < 0 || p->selected_hack >= p->screenhacks_count)
1118 p->selected_hack = -1;
1122 char *s = get_string_resource ("mode", "Mode");
1123 if (s && !strcasecmp (s, "one")) p->mode = ONE_HACK;
1124 else if (s && !strcasecmp (s, "blank")) p->mode = BLANK_ONLY;
1125 else if (s && !strcasecmp (s, "off")) p->mode = DONT_BLANK;
1126 else if (s && !strcasecmp (s, "random-same")) p->mode = RANDOM_HACKS_SAME;
1127 else p->mode = RANDOM_HACKS;
1132 char *s = get_string_resource ("textMode", "TextMode");
1133 if (s && !strcasecmp (s, "url")) p->tmode = TEXT_URL;
1134 else if (s && !strcasecmp (s, "literal")) p->tmode = TEXT_LITERAL;
1135 else if (s && !strcasecmp (s, "file")) p->tmode = TEXT_FILE;
1136 else if (s && !strcasecmp (s, "program")) p->tmode = TEXT_PROGRAM;
1137 else p->tmode = TEXT_DATE;
1141 if (system_default_screenhack_count) /* note: first_time is also true */
1143 merge_system_screenhacks (p, system_default_screenhacks,
1144 system_default_screenhack_count);
1145 free_screenhack_list (system_default_screenhacks,
1146 system_default_screenhack_count);
1147 system_default_screenhacks = 0;
1148 system_default_screenhack_count = 0;
1154 p->verbose_p = True;
1155 p->timestamp_p = True;
1156 p->initial_delay = 0;
1159 /* Throttle the various timeouts to reasonable values after reading the
1161 stop_the_insanity (p);
1165 /* If there are any hacks in the system-wide defaults that are not in
1166 the ~/.xscreensaver file, add the new ones to the end of the list.
1167 This does *not* actually save the file.
1170 merge_system_screenhacks (saver_preferences *p,
1171 screenhack **system_list, int system_count)
1173 /* Yeah yeah, this is an N^2 operation, but I don't have hashtables handy,
1178 for (i = 0; i < system_count; i++)
1181 Bool matched_p = False;
1183 for (j = 0; j < p->screenhacks_count; j++)
1186 if (!system_list[i]->name)
1187 system_list[i]->name = make_hack_name (system_list[i]->command);
1189 name = p->screenhacks[j]->name;
1191 name = make_hack_name (p->screenhacks[j]->command);
1193 matched_p = !strcasecmp (name, system_list[i]->name);
1195 if (name != p->screenhacks[j]->name)
1204 /* We have an entry in the system-wide list that is not in the
1205 user's .xscreensaver file. Add it to the end.
1206 Note that p->screenhacks is a single malloc block, not a
1207 linked list, so we have to realloc it.
1209 screenhack *oh = system_list[i];
1210 screenhack *nh = (screenhack *) malloc (sizeof(screenhack));
1212 if (made_space == 0)
1215 p->screenhacks = (screenhack **)
1216 realloc (p->screenhacks,
1217 (p->screenhacks_count + made_space + 1)
1218 * sizeof(screenhack));
1219 if (!p->screenhacks) abort();
1222 nh->enabled_p = oh->enabled_p;
1223 nh->visual = oh->visual ? strdup(oh->visual) : 0;
1224 nh->name = oh->name ? strdup(oh->name) : 0;
1225 nh->command = oh->command ? strdup(oh->command) : 0;
1227 p->screenhacks[p->screenhacks_count++] = nh;
1228 p->screenhacks[p->screenhacks_count] = 0;
1232 fprintf (stderr, "%s: noticed new hack: %s\n", blurb(),
1233 (nh->name ? nh->name : make_hack_name (nh->command)));
1241 /* Parsing the programs resource.
1245 parse_screenhack (const char *line)
1247 screenhack *h = (screenhack *) calloc (1, sizeof(*h));
1250 h->enabled_p = True;
1252 while (isspace(*line)) line++; /* skip whitespace */
1253 if (*line == '-') /* handle "-" */
1255 h->enabled_p = False;
1257 while (isspace(*line)) line++; /* skip whitespace */
1260 s = line; /* handle "visual:" */
1261 while (*line && *line != ':' && *line != '"' && !isspace(*line))
1267 h->visual = (char *) malloc (line-s+1);
1268 strncpy (h->visual, s, line-s);
1269 h->visual[line-s] = 0;
1270 if (*line == ':') line++; /* skip ":" */
1271 while (isspace(*line)) line++; /* skip whitespace */
1274 if (*line == '"') /* handle "name" */
1278 while (*line && *line != '"')
1280 h->name = (char *) malloc (line-s+1);
1281 strncpy (h->name, s, line-s);
1282 h->name[line-s] = 0;
1283 if (*line == '"') line++; /* skip "\"" */
1284 while (isspace(*line)) line++; /* skip whitespace */
1287 h->command = format_command (line, False); /* handle command */
1293 format_command (const char *cmd, Bool wrap_p)
1297 char *cmd2 = (char *) calloc (1, 2 * (strlen (cmd) + 1));
1298 const char *in = cmd;
1302 /* shrink all whitespace to one space, for the benefit of the "demo"
1303 mode display. We only do this when we can easily tell that the
1304 whitespace is not significant (no shell metachars).
1308 case '\'': case '"': case '`': case '\\':
1309 /* Metachars are scary. Copy the rest of the line unchanged. */
1311 *out++ = *in++, col++;
1314 case ' ': case '\t':
1315 /* Squeeze all other whitespace down to one space. */
1316 while (*in == ' ' || *in == '\t')
1318 *out++ = ' ', col++;
1322 /* Copy other chars unchanged. */
1323 *out++ = *in++, col++;
1330 /* Strip trailing whitespace */
1331 while (out > cmd2 && isspace (out[-1]))
1338 /* Returns a new string describing the shell command.
1339 This may be just the name of the program, capitalized.
1340 It also may be something from the resource database (gotten
1341 by looking for "hacks.XYZ.name", where XYZ is the program.)
1344 make_hack_name (const char *shell_command)
1346 char *s = strdup (shell_command);
1350 for (s2 = s; *s2; s2++) /* truncate at first whitespace */
1357 s2 = strrchr (s, '/'); /* if pathname, take last component */
1365 if (strlen (s) > 50) /* 51 is hereby defined as "unreasonable" */
1368 sprintf (res_name, "hacks.%s.name", s); /* resource? */
1369 s2 = get_string_resource (res_name, res_name);
1376 for (s2 = s; *s2; s2++) /* if it has any capitals, return it */
1377 if (*s2 >= 'A' && *s2 <= 'Z')
1380 if (s[0] >= 'a' && s[0] <= 'z') /* else cap it */
1382 if (s[0] == 'X' && s[1] >= 'a' && s[1] <= 'z') /* (magic leading X) */
1384 if (s[0] == 'G' && s[1] == 'l' &&
1385 s[2] >= 'a' && s[2] <= 'z') /* (magic leading GL) */
1393 format_hack (screenhack *hack, Bool wrap_p)
1400 char *def_name = make_hack_name (hack->command);
1402 /* Don't ever write out a name for a hack if it's the same as the default.
1404 if (hack->name && !strcmp (hack->name, def_name))
1411 size = (2 * (strlen(hack->command) +
1412 (hack->visual ? strlen(hack->visual) : 0) +
1413 (hack->name ? strlen(hack->name) : 0) +
1415 h2 = (char *) malloc (size);
1418 if (!hack->enabled_p) *out++ = '-'; /* write disabled flag */
1420 if (hack->visual && *hack->visual) /* write visual name */
1422 if (hack->enabled_p) *out++ = ' ';
1424 strcpy (out, hack->visual);
1425 out += strlen (hack->visual);
1431 col = string_columns (h2, strlen (h2), 0);
1433 if (hack->name && *hack->name) /* write pretty name */
1435 int L = (strlen (hack->name) + 2);
1437 out = stab_to (out, col, tab - L - 2);
1441 strcpy (out, hack->name);
1442 out += strlen (hack->name);
1446 col = string_columns (h2, strlen (h2), 0);
1447 if (wrap_p && col >= tab)
1448 out = stab_to (out, col, 77);
1452 if (out >= h2+size) abort();
1456 col = string_columns (h2, strlen (h2), 0);
1457 out = stab_to (out, col, tab); /* indent */
1459 if (out >= h2+size) abort();
1460 s = format_command (hack->command, wrap_p);
1471 get_screenhacks (saver_preferences *p)
1479 d = get_string_resource ("monoPrograms", "MonoPrograms");
1480 if (d && !*d) { free(d); d = 0; }
1482 d = get_string_resource ("colorPrograms", "ColorPrograms");
1483 if (d && !*d) { free(d); d = 0; }
1488 "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
1489 see the manual for details.\n", blurb());
1493 d = get_string_resource ("programs", "Programs");
1495 free_screenhack_list (p->screenhacks, p->screenhacks_count);
1497 p->screenhacks_count = 0;
1505 /* Count up the number of newlines (which will be equal to or larger than
1506 one less than the number of hacks.)
1508 for (i = j = 0; d[i]; i++)
1513 p->screenhacks = (screenhack **) calloc (j + 1, sizeof (screenhack *));
1515 /* Iterate over the lines in `d' (the string with newlines)
1516 and make new strings to stuff into the `screenhacks' array.
1518 p->screenhacks_count = 0;
1519 while (start < size)
1521 /* skip forward over whitespace. */
1522 while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n')
1525 /* skip forward to newline or end of string. */
1527 while (d[end] != 0 && d[end] != '\n')
1530 /* null terminate. */
1533 p->screenhacks[p->screenhacks_count++] = parse_screenhack (d + start);
1534 if (p->screenhacks_count >= i)
1542 if (p->screenhacks_count == 0)
1544 free (p->screenhacks);
1550 /* Make sure all the values in the preferences struct are sane.
1553 stop_the_insanity (saver_preferences *p)
1555 if (p->passwd_timeout <= 0) p->passwd_timeout = 30000; /* 30 secs */
1556 if (p->timeout < 15000) p->timeout = 15000; /* 15 secs */
1557 if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */
1558 if (p->pointer_timeout <= 0) p->pointer_timeout = 5000; /* 5 secs */
1559 if (p->notice_events_timeout <= 0)
1560 p->notice_events_timeout = 10000; /* 10 secs */
1561 if (p->fade_seconds <= 0 || p->fade_ticks <= 0)
1563 if (! p->fade_p) p->unfade_p = False;
1565 /* The DPMS settings may have the value 0.
1566 But if they are negative, or are a range less than 10 seconds,
1567 reset them to sensible defaults. (Since that must be a mistake.)
1569 if (p->dpms_standby != 0 &&
1570 p->dpms_standby < 10 * 1000)
1571 p->dpms_standby = 2 * 60 * 60 * 1000; /* 2 hours */
1572 if (p->dpms_suspend != 0 &&
1573 p->dpms_suspend < 10 * 1000)
1574 p->dpms_suspend = 2 * 60 * 60 * 1000; /* 2 hours */
1575 if (p->dpms_off != 0 &&
1576 p->dpms_off < 10 * 1000)
1577 p->dpms_off = 4 * 60 * 60 * 1000; /* 4 hours */
1579 /* standby may not be greater than suspend.
1580 suspend may not be greater than off.
1582 if (p->dpms_standby > p->dpms_suspend) p->dpms_standby = p->dpms_suspend;
1583 if (p->dpms_suspend > p->dpms_off) p->dpms_suspend = p->dpms_off;
1586 if (p->dpms_standby == 0 && /* if *all* are 0, then DPMS is disabled */
1587 p->dpms_suspend == 0 &&
1589 p->dpms_enabled_p = False;
1592 p->watchdog_timeout = p->cycle * 0.6;
1593 if (p->watchdog_timeout < 30000) p->watchdog_timeout = 30000; /* 30 secs */
1594 if (p->watchdog_timeout > 3600000) p->watchdog_timeout = 3600000; /* 1 hr */
1596 if (p->pointer_hysteresis < 0) p->pointer_hysteresis = 0;
1597 if (p->pointer_hysteresis > 100) p->pointer_hysteresis = 100;