1 /* dotfile.c --- management of the ~/.xscreensaver file.
2 * xscreensaver, Copyright (c) 1998, 2003 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[] = {
259 "captureStdout", /* not saved -- obsolete */
260 "ignoreUninstalledPrograms",
268 "chooseRandomImages",
276 "windowCreationTimeout",
283 "overlayTextBackground", /* not saved -- X resources only */
284 "overlayTextForeground", /* not saved -- X resources only */
285 "bourneShell", /* not saved -- X resources only */
293 while (*s == '\t' || *s == ' ' || *s == '\r' || *s == '\n')
295 for (s2 = s; *s2; s2++)
297 for (s2--; s2 >= s; s2--)
298 if (*s2 == '\t' || *s2 == ' ' || *s2 == '\r' || *s2 =='\n')
310 handle_entry (XrmDatabase *db, const char *key, const char *value,
311 const char *filename, int line)
314 for (i = 0; prefs[i]; i++)
315 if (*prefs[i] && !strcasecmp(key, prefs[i]))
317 char *val = strdup(value);
318 char *spec = (char *) malloc(strlen(progclass) + strlen(prefs[i]) +10);
319 strcpy(spec, progclass);
321 strcat(spec, prefs[i]);
323 XrmPutStringResource (db, spec, val);
330 fprintf(stderr, "%s: %s:%d: unknown option \"%s\"\n",
331 blurb(), filename, line, key);
337 parse_init_file (saver_preferences *p)
339 time_t write_date = 0;
340 const char *name = init_file_name();
349 if (stat(name, &st) != 0)
351 p->init_file_date = 0;
355 in = fopen(name, "r");
358 char *buf = (char *) malloc(1024 + strlen(name));
359 sprintf(buf, "%s: error reading \"%s\"", blurb(), name);
365 if (fstat (fileno(in), &st) == 0)
367 write_date = st.st_mtime;
371 char *buf = (char *) malloc(1024 + strlen(name));
372 sprintf(buf, "%s: couldn't re-stat \"%s\"", blurb(), name);
378 buf = (char *) malloc(buf_size);
380 while (fgets (buf, buf_size-1, in))
387 (buf[L-1] != '\n' || /* whole line didn't fit in buffer */
388 buf[L-2] == '\\')) /* or line ended with backslash */
390 if (buf[L-2] == '\\') /* backslash-newline gets swallowed */
396 buf = (char *) realloc(buf, buf_size);
400 if (!fgets (buf + L, buf_size-L-1, in))
405 /* Now handle other backslash escapes. */
408 for (i = 0; buf[i]; i++)
413 case 'n': buf[i] = '\n'; break;
414 case 'r': buf[i] = '\r'; break;
415 case 't': buf[i] = '\t'; break;
416 default: buf[i] = buf[i+1]; break;
418 for (j = i+2; buf[j]; j++)
426 if (*key == '#' || *key == '!' || *key == ';' ||
427 *key == '\n' || *key == 0)
430 value = strchr (key, ':');
433 fprintf(stderr, "%s: %s:%d: unparsable line: %s\n", blurb(),
440 value = strip(value);
444 handle_entry (&p->db, key, value, name, line);
449 p->init_file_date = write_date;
455 init_file_changed_p (saver_preferences *p)
457 const char *name = init_file_name();
460 if (!name) return False;
462 if (stat(name, &st) != 0)
465 if (p->init_file_date == st.st_mtime)
476 tab_to (FILE *out, int from, int to)
479 int to_mod = (to / tab_width) * tab_width;
480 while (from < to_mod)
483 from = (((from / tab_width) + 1) * tab_width);
494 stab_to (char *out, int from, int to)
497 int to_mod = (to / tab_width) * tab_width;
498 while (from < to_mod)
501 from = (((from / tab_width) + 1) * tab_width);
512 string_columns (const char *string, int length, int start)
516 const char *end = string + length;
521 else if (*string == '\t')
522 col = (((col / tab_width) + 1) * tab_width);
532 write_entry (FILE *out, const char *key, const char *value)
534 char *v = strdup(value ? value : "");
538 Bool programs_p = (!strcmp(key, "programs"));
539 int tab = (programs_p ? 32 : 16);
542 fprintf(out, "%s:", key);
543 col = strlen(key) + 1;
545 if (strlen(key) > 14)
546 col = tab_to (out, col, 20);
552 nl = strchr(v2, '\n');
556 if (first && programs_p)
558 col = tab_to (out, col, 77);
559 fprintf (out, " \\\n");
567 col = tab_to (out, col, 75);
568 fprintf (out, " \\n\\\n");
573 col = tab_to (out, col, tab);
576 string_columns(v2, strlen (v2), col) + col > 75)
583 while (v2[end] == ' ' || v2[end] == '\t')
585 while (v2[end] != ' ' && v2[end] != '\t' &&
586 v2[end] != '\n' && v2[end] != 0)
588 if (string_columns (v2 + start, (end - start), col) >= 74)
590 col = tab_to (out, col, 75);
591 fprintf(out, " \\\n");
592 col = tab_to (out, 0, tab + 2);
593 while (v2[start] == ' ' || v2[start] == '\t')
597 col = string_columns (v2 + start, (end - start), col);
599 fputc(v2[start++], out);
604 fprintf (out, "%s", v2);
605 col += string_columns(v2, strlen (v2), col);
619 write_init_file (saver_preferences *p, const char *version_string,
623 const char *name = init_file_name();
624 const char *tmp_name = init_file_tmp_name();
625 char *n2 = chase_symlinks (name);
629 /* Kludge, since these aren't in the saver_preferences struct as strings...
633 Bool overlay_stderr_p;
641 /* Throttle the various timeouts to reasonable values before writing
643 stop_the_insanity (p);
647 fprintf (stderr, "%s: writing \"%s\".\n", blurb(), name);
650 out = fopen(tmp_name, "w");
653 char *buf = (char *) malloc(1024 + strlen(name));
654 sprintf(buf, "%s: error writing \"%s\"", blurb(), name);
660 /* Give the new .xscreensaver file the same permissions as the old one;
661 except ensure that it is readable and writable by owner, and not
662 executable. Extra hack: if we're running as root, make the file
663 be world-readable (so that the daemon, running as "nobody", will
664 still be able to read it.)
666 if (stat(name, &st) == 0)
668 mode_t mode = st.st_mode;
669 mode |= S_IRUSR | S_IWUSR; /* read/write by user */
670 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); /* executable by none */
672 if (getuid() == (uid_t) 0) /* read by group/other */
673 mode |= S_IRGRP | S_IROTH;
675 if (fchmod (fileno(out), mode) != 0)
677 char *buf = (char *) malloc(1024 + strlen(name));
678 sprintf (buf, "%s: error fchmodding \"%s\" to 0%o", blurb(),
679 tmp_name, (unsigned int) mode);
686 /* Kludge, since these aren't in the saver_preferences struct... */
687 visual_name = get_string_resource ("visualID", "VisualID");
689 overlay_stderr_p = get_boolean_resource ("overlayStderr", "Boolean");
690 stderr_font = get_string_resource ("font", "Font");
695 char **hack_strings = (char **)
696 calloc (p->screenhacks_count, sizeof(char *));
698 for (j = 0; j < p->screenhacks_count; j++)
700 hack_strings[j] = format_hack (p->screenhacks[j], True);
701 i += strlen (hack_strings[j]);
705 ss = programs = (char *) malloc(i + 10);
707 for (j = 0; j < p->screenhacks_count; j++)
709 strcat (ss, hack_strings[j]);
710 free (hack_strings[j]);
719 struct passwd *pw = getpwuid (getuid ());
720 char *whoami = (pw && pw->pw_name && *pw->pw_name
723 time_t now = time ((time_t *) 0);
724 char *timestr = (char *) ctime (&now);
725 char *nl = (char *) strchr (timestr, '\n');
728 "# %s Preferences File\n"
729 "# Written by %s %s for %s on %s.\n"
730 "# http://www.jwz.org/xscreensaver/\n"
732 progclass, progname, version_string, whoami, timestr);
735 for (j = 0; prefs[j]; j++)
738 const char *pr = prefs[j];
739 enum pref_type { pref_str, pref_int, pref_bool, pref_byte, pref_time
753 # define CHECK(X) else if (!strcmp(pr, X))
755 CHECK("timeout") type = pref_time, t = p->timeout;
756 CHECK("cycle") type = pref_time, t = p->cycle;
757 CHECK("lock") type = pref_bool, b = p->lock_p;
758 # if 0 /* #### not ready yet */
759 CHECK("lockVTs") type = pref_bool, b = p->lock_vt_p;
761 CHECK("lockVTs") continue; /* don't save */
763 CHECK("lockTimeout") type = pref_time, t = p->lock_timeout;
764 CHECK("passwdTimeout") type = pref_time, t = p->passwd_timeout;
765 CHECK("visualID") type = pref_str, s = visual_name;
766 CHECK("installColormap") type = pref_bool, b = p->install_cmap_p;
767 CHECK("verbose") type = pref_bool, b = p->verbose_p;
768 CHECK("timestamp") type = pref_bool, b = p->timestamp_p;
769 CHECK("splash") type = pref_bool, b = p->splash_p;
770 CHECK("splashDuration") type = pref_time, t = p->splash_duration;
771 CHECK("quad") type = pref_bool, b = p->quad_p;
772 CHECK("demoCommand") type = pref_str, s = p->demo_command;
773 CHECK("prefsCommand") type = pref_str, s = p->prefs_command;
774 /* CHECK("helpURL") type = pref_str, s = p->help_url; */
775 CHECK("helpURL") continue; /* don't save */
776 /* CHECK("loadURL") type = pref_str, s = p->load_url_command; */
777 CHECK("loadURL") continue; /* don't save */
778 CHECK("nice") type = pref_int, i = p->nice_inferior;
779 CHECK("memoryLimit") type = pref_byte, i = p->inferior_memory_limit;
780 CHECK("fade") type = pref_bool, b = p->fade_p;
781 CHECK("unfade") type = pref_bool, b = p->unfade_p;
782 CHECK("fadeSeconds") type = pref_time, t = p->fade_seconds;
783 CHECK("fadeTicks") type = pref_int, i = p->fade_ticks;
784 CHECK("captureStderr") type = pref_bool, b = p->capture_stderr_p;
785 CHECK("captureStdout") continue; /* don't save */
786 CHECK("ignoreUninstalledPrograms")
787 type = pref_bool, b = p->ignore_uninstalled_p;
789 CHECK("font") type = pref_str, s = stderr_font;
791 CHECK("dpmsEnabled") type = pref_bool, b = p->dpms_enabled_p;
792 CHECK("dpmsStandby") type = pref_time, t = p->dpms_standby;
793 CHECK("dpmsSuspend") type = pref_time, t = p->dpms_suspend;
794 CHECK("dpmsOff") type = pref_time, t = p->dpms_off;
796 CHECK("grabDesktopImages") type =pref_bool, b = p->grab_desktop_p;
797 CHECK("grabVideoFrames") type =pref_bool, b = p->grab_video_p;
798 CHECK("chooseRandomImages")type =pref_bool, b = p->random_image_p;
799 CHECK("imageDirectory") type =pref_str, s = p->image_directory;
801 CHECK("mode") type = pref_str,
802 s = (p->mode == ONE_HACK ? "one" :
803 p->mode == BLANK_ONLY ? "blank" :
804 p->mode == DONT_BLANK ? "off" :
806 CHECK("selected") type = pref_int, i = p->selected_hack;
808 CHECK("programs") type = pref_str, s = programs;
809 CHECK("pointerPollTime") type = pref_time, t = p->pointer_timeout;
810 CHECK("windowCreationTimeout")type=pref_time,t= p->notice_events_timeout;
811 CHECK("initialDelay") type = pref_time, t = p->initial_delay;
812 CHECK("sgiSaverExtension")type = pref_bool, b=p->use_sgi_saver_extension;
813 CHECK("mitSaverExtension")type = pref_bool, b=p->use_mit_saver_extension;
814 CHECK("xidleExtension") type = pref_bool, b = p->use_xidle_extension;
815 CHECK("procInterrupts") type = pref_bool, b = p->use_proc_interrupts;
816 CHECK("overlayStderr") type = pref_bool, b = overlay_stderr_p;
817 CHECK("overlayTextBackground") continue; /* don't save */
818 CHECK("overlayTextForeground") continue; /* don't save */
819 CHECK("bourneShell") continue;
828 sprintf(buf, "%d", i);
832 s = b ? "True" : "False";
836 unsigned int hour = 0, min = 0, sec = (unsigned int) (t/1000);
847 sprintf (buf, "%u:%02u:%02u", hour, min, sec);
853 if (i >= (1<<30) && i == ((i >> 30) << 30))
854 sprintf(buf, "%dG", i >> 30);
855 else if (i >= (1<<20) && i == ((i >> 20) << 20))
856 sprintf(buf, "%dM", i >> 20);
857 else if (i >= (1<<10) && i == ((i >> 10) << 10))
858 sprintf(buf, "%dK", i >> 10);
860 sprintf(buf, "%d", i);
869 if (pr && !strcmp(pr, "mode")) fprintf(out, "\n");
871 write_entry (out, pr, s);
876 if (visual_name) free(visual_name);
877 if (stderr_font) free(stderr_font);
878 if (programs) free(programs);
880 if (fclose(out) == 0)
882 time_t write_date = 0;
884 if (stat(tmp_name, &st) == 0)
886 write_date = st.st_mtime;
890 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
891 sprintf(buf, "%s: couldn't stat \"%s\"", blurb(), tmp_name);
898 if (rename (tmp_name, name) != 0)
900 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
901 sprintf(buf, "%s: error renaming \"%s\" to \"%s\"",
902 blurb(), tmp_name, name);
910 p->init_file_date = write_date;
912 /* Since the .xscreensaver file is used for IPC, let's try and make
913 sure that the bits actually land on the disk right away. */
916 status = 0; /* wrote and renamed successfully! */
921 char *buf = (char *) malloc(1024 + strlen(name));
922 sprintf(buf, "%s: error closing \"%s\"", blurb(), name);
935 /* Parsing the resource database
939 free_screenhack (screenhack *hack)
941 if (hack->visual) free (hack->visual);
942 if (hack->name) free (hack->name);
943 free (hack->command);
944 memset (hack, 0, sizeof(*hack));
949 free_screenhack_list (screenhack **list, int count)
953 for (i = 0; i < count; i++)
955 free_screenhack (list[i]);
961 /* Populate `saver_preferences' with the contents of the resource database.
962 Note that this may be called multiple times -- it is re-run each time
963 the ~/.xscreensaver file is reloaded.
965 This function can be very noisy, since it issues resource syntax errors
969 load_init_file (saver_preferences *p)
971 static Bool first_time = True;
973 screenhack **system_default_screenhacks = 0;
974 int system_default_screenhack_count = 0;
978 /* Get the programs resource before the .xscreensaver file has been
979 parsed and merged into the resource database for the first time:
980 this is the value of *programs from the app-defaults file.
981 Then clear it out so that it will be parsed again later, after
982 the init file has been read.
985 system_default_screenhacks = p->screenhacks;
986 system_default_screenhack_count = p->screenhacks_count;
988 p->screenhacks_count = 0;
991 if (parse_init_file (p) != 0) /* file might have gone away */
992 if (!first_time) return;
996 p->xsync_p = get_boolean_resource ("synchronous", "Synchronous");
997 p->verbose_p = get_boolean_resource ("verbose", "Boolean");
998 p->timestamp_p = get_boolean_resource ("timestamp", "Boolean");
999 p->lock_p = get_boolean_resource ("lock", "Boolean");
1000 p->lock_vt_p = get_boolean_resource ("lockVTs", "Boolean");
1001 p->fade_p = get_boolean_resource ("fade", "Boolean");
1002 p->unfade_p = get_boolean_resource ("unfade", "Boolean");
1003 p->fade_seconds = 1000 * get_seconds_resource ("fadeSeconds", "Time");
1004 p->fade_ticks = get_integer_resource ("fadeTicks", "Integer");
1005 p->install_cmap_p = get_boolean_resource ("installColormap", "Boolean");
1006 p->nice_inferior = get_integer_resource ("nice", "Nice");
1007 p->inferior_memory_limit = get_byte_resource ("memoryLimit", "MemoryLimit");
1008 p->splash_p = get_boolean_resource ("splash", "Boolean");
1009 p->quad_p = get_boolean_resource ("quad", "Boolean");
1010 p->capture_stderr_p = get_boolean_resource ("captureStderr", "Boolean");
1011 p->ignore_uninstalled_p = get_boolean_resource ("ignoreUninstalledPrograms",
1014 p->initial_delay = 1000 * get_seconds_resource ("initialDelay", "Time");
1015 p->splash_duration = 1000 * get_seconds_resource ("splashDuration", "Time");
1016 p->timeout = 1000 * get_minutes_resource ("timeout", "Time");
1017 p->lock_timeout = 1000 * get_minutes_resource ("lockTimeout", "Time");
1018 p->cycle = 1000 * get_minutes_resource ("cycle", "Time");
1019 p->passwd_timeout = 1000 * get_seconds_resource ("passwdTimeout", "Time");
1020 p->pointer_timeout = 1000 * get_seconds_resource ("pointerPollTime", "Time");
1021 p->notice_events_timeout = 1000*get_seconds_resource("windowCreationTimeout",
1024 p->dpms_enabled_p = get_boolean_resource ("dpmsEnabled", "Boolean");
1025 p->dpms_standby = 1000 * get_minutes_resource ("dpmsStandby", "Time");
1026 p->dpms_suspend = 1000 * get_minutes_resource ("dpmsSuspend", "Time");
1027 p->dpms_off = 1000 * get_minutes_resource ("dpmsOff", "Time");
1029 p->grab_desktop_p = get_boolean_resource ("grabDesktopImages", "Boolean");
1030 p->grab_video_p = get_boolean_resource ("grabVideoFrames", "Boolean");
1031 p->random_image_p = get_boolean_resource ("chooseRandomImages", "Boolean");
1032 p->image_directory = get_string_resource ("imageDirectory",
1035 p->shell = get_string_resource ("bourneShell", "BourneShell");
1037 p->demo_command = get_string_resource("demoCommand", "URL");
1038 p->prefs_command = get_string_resource("prefsCommand", "URL");
1039 p->help_url = get_string_resource("helpURL", "URL");
1040 p->load_url_command = get_string_resource("loadURL", "LoadURL");
1043 /* If "*splash" is unset, default to true. */
1045 char *s = get_string_resource ("splash", "Boolean");
1052 /* If "*grabDesktopImages" is unset, default to true. */
1054 char *s = get_string_resource ("grabDesktopImages", "Boolean");
1058 p->grab_desktop_p = True;
1061 p->use_xidle_extension = get_boolean_resource ("xidleExtension","Boolean");
1062 p->use_mit_saver_extension = get_boolean_resource ("mitSaverExtension",
1064 p->use_sgi_saver_extension = get_boolean_resource ("sgiSaverExtension",
1066 p->use_proc_interrupts = get_boolean_resource ("procInterrupts", "Boolean");
1068 get_screenhacks (p); /* Parse the "programs" resource. */
1070 p->selected_hack = get_integer_resource ("selected", "Integer");
1071 if (p->selected_hack < 0 || p->selected_hack >= p->screenhacks_count)
1072 p->selected_hack = -1;
1075 char *s = get_string_resource ("mode", "Mode");
1076 if (s && !strcasecmp (s, "one")) p->mode = ONE_HACK;
1077 else if (s && !strcasecmp (s, "blank")) p->mode = BLANK_ONLY;
1078 else if (s && !strcasecmp (s, "off")) p->mode = DONT_BLANK;
1079 else p->mode = RANDOM_HACKS;
1083 if (system_default_screenhack_count) /* note: first_time is also true */
1085 merge_system_screenhacks (p, system_default_screenhacks,
1086 system_default_screenhack_count);
1087 free_screenhack_list (system_default_screenhacks,
1088 system_default_screenhack_count);
1089 system_default_screenhacks = 0;
1090 system_default_screenhack_count = 0;
1096 p->verbose_p = True;
1097 p->timestamp_p = True;
1098 p->initial_delay = 0;
1101 /* Throttle the various timeouts to reasonable values after reading the
1103 stop_the_insanity (p);
1107 /* If there are any hacks in the system-wide defaults that are not in
1108 the ~/.xscreensaver file, add the new ones to the end of the list.
1109 This does *not* actually save the file.
1112 merge_system_screenhacks (saver_preferences *p,
1113 screenhack **system_list, int system_count)
1115 /* Yeah yeah, this is an N^2 operation, but I don't have hashtables handy,
1120 for (i = 0; i < system_count; i++)
1123 Bool matched_p = False;
1125 for (j = 0; j < p->screenhacks_count; j++)
1128 if (!system_list[i]->name)
1129 system_list[i]->name = make_hack_name (system_list[i]->command);
1131 name = p->screenhacks[j]->name;
1133 name = make_hack_name (p->screenhacks[j]->command);
1135 matched_p = !strcasecmp (name, system_list[i]->name);
1137 if (name != p->screenhacks[j]->name)
1146 /* We have an entry in the system-wide list that is not in the
1147 user's .xscreensaver file. Add it to the end.
1148 Note that p->screenhacks is a single malloc block, not a
1149 linked list, so we have to realloc it.
1151 screenhack *oh = system_list[i];
1152 screenhack *nh = (screenhack *) malloc (sizeof(screenhack));
1154 if (made_space == 0)
1157 p->screenhacks = (screenhack **)
1158 realloc (p->screenhacks,
1159 (p->screenhacks_count + made_space + 1)
1160 * sizeof(screenhack));
1161 if (!p->screenhacks) abort();
1164 nh->enabled_p = oh->enabled_p;
1165 nh->visual = oh->visual ? strdup(oh->visual) : 0;
1166 nh->name = oh->name ? strdup(oh->name) : 0;
1167 nh->command = oh->command ? strdup(oh->command) : 0;
1169 p->screenhacks[p->screenhacks_count++] = nh;
1170 p->screenhacks[p->screenhacks_count] = 0;
1174 fprintf (stderr, "%s: noticed new hack: %s\n", blurb(),
1175 (nh->name ? nh->name : make_hack_name (nh->command)));
1183 /* Parsing the programs resource.
1187 parse_screenhack (const char *line)
1189 screenhack *h = (screenhack *) calloc (1, sizeof(*h));
1192 h->enabled_p = True;
1194 while (isspace(*line)) line++; /* skip whitespace */
1195 if (*line == '-') /* handle "-" */
1197 h->enabled_p = False;
1199 while (isspace(*line)) line++; /* skip whitespace */
1202 s = line; /* handle "visual:" */
1203 while (*line && *line != ':' && *line != '"' && !isspace(*line))
1209 h->visual = (char *) malloc (line-s+1);
1210 strncpy (h->visual, s, line-s);
1211 h->visual[line-s] = 0;
1212 if (*line == ':') line++; /* skip ":" */
1213 while (isspace(*line)) line++; /* skip whitespace */
1216 if (*line == '"') /* handle "name" */
1220 while (*line && *line != '"')
1222 h->name = (char *) malloc (line-s+1);
1223 strncpy (h->name, s, line-s);
1224 h->name[line-s] = 0;
1225 if (*line == '"') line++; /* skip "\"" */
1226 while (isspace(*line)) line++; /* skip whitespace */
1229 h->command = format_command (line, False); /* handle command */
1235 format_command (const char *cmd, Bool wrap_p)
1239 char *cmd2 = (char *) calloc (1, 2 * (strlen (cmd) + 1));
1240 const char *in = cmd;
1244 /* shrink all whitespace to one space, for the benefit of the "demo"
1245 mode display. We only do this when we can easily tell that the
1246 whitespace is not significant (no shell metachars).
1250 case '\'': case '"': case '`': case '\\':
1251 /* Metachars are scary. Copy the rest of the line unchanged. */
1253 *out++ = *in++, col++;
1256 case ' ': case '\t':
1257 /* Squeeze all other whitespace down to one space. */
1258 while (*in == ' ' || *in == '\t')
1260 *out++ = ' ', col++;
1264 /* Copy other chars unchanged. */
1265 *out++ = *in++, col++;
1272 /* Strip trailing whitespace */
1273 while (out > cmd2 && isspace (out[-1]))
1280 /* Returns a new string describing the shell command.
1281 This may be just the name of the program, capitalized.
1282 It also may be something from the resource database (gotten
1283 by looking for "hacks.XYZ.name", where XYZ is the program.)
1286 make_hack_name (const char *shell_command)
1288 char *s = strdup (shell_command);
1292 for (s2 = s; *s2; s2++) /* truncate at first whitespace */
1299 s2 = strrchr (s, '/'); /* if pathname, take last component */
1307 if (strlen (s) > 50) /* 51 is hereby defined as "unreasonable" */
1310 sprintf (res_name, "hacks.%s.name", s); /* resource? */
1311 s2 = get_string_resource (res_name, res_name);
1318 for (s2 = s; *s2; s2++) /* if it has any capitals, return it */
1319 if (*s2 >= 'A' && *s2 <= 'Z')
1322 if (s[0] >= 'a' && s[0] <= 'z') /* else cap it */
1324 if (s[0] == 'X' && s[1] >= 'a' && s[1] <= 'z') /* (magic leading X) */
1326 if (s[0] == 'G' && s[1] == 'l' &&
1327 s[2] >= 'a' && s[2] <= 'z') /* (magic leading GL) */
1335 format_hack (screenhack *hack, Bool wrap_p)
1342 char *def_name = make_hack_name (hack->command);
1344 /* Don't ever write out a name for a hack if it's the same as the default.
1346 if (hack->name && !strcmp (hack->name, def_name))
1353 size = (2 * (strlen(hack->command) +
1354 (hack->visual ? strlen(hack->visual) : 0) +
1355 (hack->name ? strlen(hack->name) : 0) +
1357 h2 = (char *) malloc (size);
1360 if (!hack->enabled_p) *out++ = '-'; /* write disabled flag */
1362 if (hack->visual && *hack->visual) /* write visual name */
1364 if (hack->enabled_p) *out++ = ' ';
1366 strcpy (out, hack->visual);
1367 out += strlen (hack->visual);
1373 col = string_columns (h2, strlen (h2), 0);
1375 if (hack->name && *hack->name) /* write pretty name */
1377 int L = (strlen (hack->name) + 2);
1379 out = stab_to (out, col, tab - L - 2);
1383 strcpy (out, hack->name);
1384 out += strlen (hack->name);
1388 col = string_columns (h2, strlen (h2), 0);
1389 if (wrap_p && col >= tab)
1390 out = stab_to (out, col, 77);
1394 if (out >= h2+size) abort();
1398 col = string_columns (h2, strlen (h2), 0);
1399 out = stab_to (out, col, tab); /* indent */
1401 if (out >= h2+size) abort();
1402 s = format_command (hack->command, wrap_p);
1413 get_screenhacks (saver_preferences *p)
1421 d = get_string_resource ("monoPrograms", "MonoPrograms");
1422 if (d && !*d) { free(d); d = 0; }
1424 d = get_string_resource ("colorPrograms", "ColorPrograms");
1425 if (d && !*d) { free(d); d = 0; }
1430 "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
1431 see the manual for details.\n", blurb());
1435 d = get_string_resource ("programs", "Programs");
1437 free_screenhack_list (p->screenhacks, p->screenhacks_count);
1439 p->screenhacks_count = 0;
1447 /* Count up the number of newlines (which will be equal to or larger than
1448 one less than the number of hacks.)
1450 for (i = j = 0; d[i]; i++)
1455 p->screenhacks = (screenhack **) calloc (j + 1, sizeof (screenhack *));
1457 /* Iterate over the lines in `d' (the string with newlines)
1458 and make new strings to stuff into the `screenhacks' array.
1460 p->screenhacks_count = 0;
1461 while (start < size)
1463 /* skip forward over whitespace. */
1464 while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n')
1467 /* skip forward to newline or end of string. */
1469 while (d[end] != 0 && d[end] != '\n')
1472 /* null terminate. */
1475 p->screenhacks[p->screenhacks_count++] = parse_screenhack (d + start);
1476 if (p->screenhacks_count >= i)
1484 if (p->screenhacks_count == 0)
1486 free (p->screenhacks);
1492 /* Make sure all the values in the preferences struct are sane.
1495 stop_the_insanity (saver_preferences *p)
1497 if (p->passwd_timeout <= 0) p->passwd_timeout = 30000; /* 30 secs */
1498 if (p->timeout < 15000) p->timeout = 15000; /* 15 secs */
1499 if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */
1500 if (p->pointer_timeout <= 0) p->pointer_timeout = 5000; /* 5 secs */
1501 if (p->notice_events_timeout <= 0)
1502 p->notice_events_timeout = 10000; /* 10 secs */
1503 if (p->fade_seconds <= 0 || p->fade_ticks <= 0)
1505 if (! p->fade_p) p->unfade_p = False;
1507 /* The DPMS settings may have the value 0.
1508 But if they are negative, or are a range less than 10 seconds,
1509 reset them to sensible defaults. (Since that must be a mistake.)
1511 if (p->dpms_standby != 0 &&
1512 p->dpms_standby < 10 * 1000)
1513 p->dpms_standby = 2 * 60 * 60 * 1000; /* 2 hours */
1514 if (p->dpms_suspend != 0 &&
1515 p->dpms_suspend < 10 * 1000)
1516 p->dpms_suspend = 2 * 60 * 60 * 1000; /* 2 hours */
1517 if (p->dpms_off != 0 &&
1518 p->dpms_off < 10 * 1000)
1519 p->dpms_off = 4 * 60 * 60 * 1000; /* 4 hours */
1521 if (p->dpms_standby == 0 && /* if *all* are 0, then DPMS is disabled */
1522 p->dpms_suspend == 0 &&
1524 p->dpms_enabled_p = False;
1526 p->watchdog_timeout = p->cycle * 0.6;
1527 if (p->watchdog_timeout < 30000) p->watchdog_timeout = 30000; /* 30 secs */
1528 if (p->watchdog_timeout > 3600000) p->watchdog_timeout = 3600000; /* 1 hr */