1 /* dotfile.c --- management of the ~/.xscreensaver file.
2 * xscreensaver, Copyright (c) 1998-2004 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",
281 "GetViewPortIsFullOfLies",
284 "overlayTextBackground", /* not saved -- X resources only */
285 "overlayTextForeground", /* not saved -- X resources only */
286 "bourneShell", /* not saved -- X resources only */
294 while (*s == '\t' || *s == ' ' || *s == '\r' || *s == '\n')
296 for (s2 = s; *s2; s2++)
298 for (s2--; s2 >= s; s2--)
299 if (*s2 == '\t' || *s2 == ' ' || *s2 == '\r' || *s2 =='\n')
311 handle_entry (XrmDatabase *db, const char *key, const char *value,
312 const char *filename, int line)
315 for (i = 0; prefs[i]; i++)
316 if (*prefs[i] && !strcasecmp(key, prefs[i]))
318 char *val = strdup(value);
319 char *spec = (char *) malloc(strlen(progclass) + strlen(prefs[i]) +10);
320 strcpy(spec, progclass);
322 strcat(spec, prefs[i]);
324 XrmPutStringResource (db, spec, val);
331 fprintf(stderr, "%s: %s:%d: unknown option \"%s\"\n",
332 blurb(), filename, line, key);
338 parse_init_file (saver_preferences *p)
340 time_t write_date = 0;
341 const char *name = init_file_name();
350 if (stat(name, &st) != 0)
352 p->init_file_date = 0;
356 in = fopen(name, "r");
359 char *buf = (char *) malloc(1024 + strlen(name));
360 sprintf(buf, "%s: error reading \"%s\"", blurb(), name);
366 if (fstat (fileno(in), &st) == 0)
368 write_date = st.st_mtime;
372 char *buf = (char *) malloc(1024 + strlen(name));
373 sprintf(buf, "%s: couldn't re-stat \"%s\"", blurb(), name);
379 buf = (char *) malloc(buf_size);
381 while (fgets (buf, buf_size-1, in))
388 (buf[L-1] != '\n' || /* whole line didn't fit in buffer */
389 buf[L-2] == '\\')) /* or line ended with backslash */
391 if (buf[L-2] == '\\') /* backslash-newline gets swallowed */
397 buf = (char *) realloc(buf, buf_size);
401 if (!fgets (buf + L, buf_size-L-1, in))
406 /* Now handle other backslash escapes. */
409 for (i = 0; buf[i]; i++)
414 case 'n': buf[i] = '\n'; break;
415 case 'r': buf[i] = '\r'; break;
416 case 't': buf[i] = '\t'; break;
417 default: buf[i] = buf[i+1]; break;
419 for (j = i+2; buf[j]; j++)
427 if (*key == '#' || *key == '!' || *key == ';' ||
428 *key == '\n' || *key == 0)
431 value = strchr (key, ':');
434 fprintf(stderr, "%s: %s:%d: unparsable line: %s\n", blurb(),
441 value = strip(value);
445 handle_entry (&p->db, key, value, name, line);
450 p->init_file_date = write_date;
456 init_file_changed_p (saver_preferences *p)
458 const char *name = init_file_name();
461 if (!name) return False;
463 if (stat(name, &st) != 0)
466 if (p->init_file_date == st.st_mtime)
477 tab_to (FILE *out, int from, int to)
480 int to_mod = (to / tab_width) * tab_width;
481 while (from < to_mod)
484 from = (((from / tab_width) + 1) * tab_width);
495 stab_to (char *out, int from, int to)
498 int to_mod = (to / tab_width) * tab_width;
499 while (from < to_mod)
502 from = (((from / tab_width) + 1) * tab_width);
513 string_columns (const char *string, int length, int start)
517 const char *end = string + length;
522 else if (*string == '\t')
523 col = (((col / tab_width) + 1) * tab_width);
533 write_entry (FILE *out, const char *key, const char *value)
535 char *v = strdup(value ? value : "");
539 Bool programs_p = (!strcmp(key, "programs"));
540 int tab = (programs_p ? 32 : 16);
543 fprintf(out, "%s:", key);
544 col = strlen(key) + 1;
546 if (strlen(key) > 14)
547 col = tab_to (out, col, 20);
553 nl = strchr(v2, '\n');
557 if (first && programs_p)
559 col = tab_to (out, col, 77);
560 fprintf (out, " \\\n");
568 col = tab_to (out, col, 75);
569 fprintf (out, " \\n\\\n");
574 col = tab_to (out, col, tab);
577 string_columns(v2, strlen (v2), col) + col > 75)
584 while (v2[end] == ' ' || v2[end] == '\t')
586 while (v2[end] != ' ' && v2[end] != '\t' &&
587 v2[end] != '\n' && v2[end] != 0)
589 if (string_columns (v2 + start, (end - start), col) >= 74)
591 col = tab_to (out, col, 75);
592 fprintf(out, " \\\n");
593 col = tab_to (out, 0, tab + 2);
594 while (v2[start] == ' ' || v2[start] == '\t')
598 col = string_columns (v2 + start, (end - start), col);
600 fputc(v2[start++], out);
605 fprintf (out, "%s", v2);
606 col += string_columns(v2, strlen (v2), col);
620 write_init_file (saver_preferences *p, const char *version_string,
624 const char *name = init_file_name();
625 const char *tmp_name = init_file_tmp_name();
626 char *n2 = chase_symlinks (name);
630 /* Kludge, since these aren't in the saver_preferences struct as strings...
634 Bool overlay_stderr_p;
642 /* Throttle the various timeouts to reasonable values before writing
644 stop_the_insanity (p);
648 fprintf (stderr, "%s: writing \"%s\".\n", blurb(), name);
651 out = fopen(tmp_name, "w");
654 char *buf = (char *) malloc(1024 + strlen(name));
655 sprintf(buf, "%s: error writing \"%s\"", blurb(), name);
661 /* Give the new .xscreensaver file the same permissions as the old one;
662 except ensure that it is readable and writable by owner, and not
663 executable. Extra hack: if we're running as root, make the file
664 be world-readable (so that the daemon, running as "nobody", will
665 still be able to read it.)
667 if (stat(name, &st) == 0)
669 mode_t mode = st.st_mode;
670 mode |= S_IRUSR | S_IWUSR; /* read/write by user */
671 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); /* executable by none */
673 if (getuid() == (uid_t) 0) /* read by group/other */
674 mode |= S_IRGRP | S_IROTH;
676 if (fchmod (fileno(out), mode) != 0)
678 char *buf = (char *) malloc(1024 + strlen(name));
679 sprintf (buf, "%s: error fchmodding \"%s\" to 0%o", blurb(),
680 tmp_name, (unsigned int) mode);
687 /* Kludge, since these aren't in the saver_preferences struct... */
688 visual_name = get_string_resource ("visualID", "VisualID");
690 overlay_stderr_p = get_boolean_resource ("overlayStderr", "Boolean");
691 stderr_font = get_string_resource ("font", "Font");
696 char **hack_strings = (char **)
697 calloc (p->screenhacks_count, sizeof(char *));
699 for (j = 0; j < p->screenhacks_count; j++)
701 hack_strings[j] = format_hack (p->screenhacks[j], True);
702 i += strlen (hack_strings[j]);
706 ss = programs = (char *) malloc(i + 10);
708 for (j = 0; j < p->screenhacks_count; j++)
710 strcat (ss, hack_strings[j]);
711 free (hack_strings[j]);
720 struct passwd *pw = getpwuid (getuid ());
721 char *whoami = (pw && pw->pw_name && *pw->pw_name
724 time_t now = time ((time_t *) 0);
725 char *timestr = (char *) ctime (&now);
726 char *nl = (char *) strchr (timestr, '\n');
729 "# %s Preferences File\n"
730 "# Written by %s %s for %s on %s.\n"
731 "# http://www.jwz.org/xscreensaver/\n"
733 progclass, progname, version_string, whoami, timestr);
736 for (j = 0; prefs[j]; j++)
739 const char *pr = prefs[j];
740 enum pref_type { pref_str, pref_int, pref_bool, pref_byte, pref_time
754 # define CHECK(X) else if (!strcmp(pr, X))
756 CHECK("timeout") type = pref_time, t = p->timeout;
757 CHECK("cycle") type = pref_time, t = p->cycle;
758 CHECK("lock") type = pref_bool, b = p->lock_p;
759 # if 0 /* #### not ready yet */
760 CHECK("lockVTs") type = pref_bool, b = p->lock_vt_p;
762 CHECK("lockVTs") continue; /* don't save */
764 CHECK("lockTimeout") type = pref_time, t = p->lock_timeout;
765 CHECK("passwdTimeout") type = pref_time, t = p->passwd_timeout;
766 CHECK("visualID") type = pref_str, s = visual_name;
767 CHECK("installColormap") type = pref_bool, b = p->install_cmap_p;
768 CHECK("verbose") type = pref_bool, b = p->verbose_p;
769 CHECK("timestamp") type = pref_bool, b = p->timestamp_p;
770 CHECK("splash") type = pref_bool, b = p->splash_p;
771 CHECK("splashDuration") type = pref_time, t = p->splash_duration;
772 CHECK("quad") type = pref_bool, b = p->quad_p;
773 CHECK("demoCommand") type = pref_str, s = p->demo_command;
774 CHECK("prefsCommand") type = pref_str, s = p->prefs_command;
775 /* CHECK("helpURL") type = pref_str, s = p->help_url; */
776 CHECK("helpURL") continue; /* don't save */
777 /* CHECK("loadURL") type = pref_str, s = p->load_url_command; */
778 CHECK("loadURL") continue; /* don't save */
779 CHECK("nice") type = pref_int, i = p->nice_inferior;
780 CHECK("memoryLimit") type = pref_byte, i = p->inferior_memory_limit;
781 CHECK("fade") type = pref_bool, b = p->fade_p;
782 CHECK("unfade") type = pref_bool, b = p->unfade_p;
783 CHECK("fadeSeconds") type = pref_time, t = p->fade_seconds;
784 CHECK("fadeTicks") type = pref_int, i = p->fade_ticks;
785 CHECK("captureStderr") type = pref_bool, b = p->capture_stderr_p;
786 CHECK("captureStdout") continue; /* don't save */
787 CHECK("ignoreUninstalledPrograms")
788 type = pref_bool, b = p->ignore_uninstalled_p;
790 CHECK("font") type = pref_str, s = stderr_font;
792 CHECK("dpmsEnabled") type = pref_bool, b = p->dpms_enabled_p;
793 CHECK("dpmsStandby") type = pref_time, t = p->dpms_standby;
794 CHECK("dpmsSuspend") type = pref_time, t = p->dpms_suspend;
795 CHECK("dpmsOff") type = pref_time, t = p->dpms_off;
797 CHECK("grabDesktopImages") type =pref_bool, b = p->grab_desktop_p;
798 CHECK("grabVideoFrames") type =pref_bool, b = p->grab_video_p;
799 CHECK("chooseRandomImages")type =pref_bool, b = p->random_image_p;
800 CHECK("imageDirectory") type =pref_str, s = p->image_directory;
802 CHECK("mode") type = pref_str,
803 s = (p->mode == ONE_HACK ? "one" :
804 p->mode == BLANK_ONLY ? "blank" :
805 p->mode == DONT_BLANK ? "off" :
807 CHECK("selected") type = pref_int, i = p->selected_hack;
809 CHECK("programs") type = pref_str, s = programs;
810 CHECK("pointerPollTime") type = pref_time, t = p->pointer_timeout;
811 CHECK("windowCreationTimeout")type=pref_time,t= p->notice_events_timeout;
812 CHECK("initialDelay") type = pref_time, t = p->initial_delay;
813 CHECK("sgiSaverExtension")type = pref_bool, b=p->use_sgi_saver_extension;
814 CHECK("mitSaverExtension")type = pref_bool, b=p->use_mit_saver_extension;
815 CHECK("xidleExtension") type = pref_bool, b = p->use_xidle_extension;
816 CHECK("procInterrupts") type = pref_bool, b = p->use_proc_interrupts;
817 CHECK("GetViewPortIsFullOfLies") type = pref_bool,
818 b = p->getviewport_full_of_lies_p;
819 CHECK("overlayStderr") type = pref_bool, b = overlay_stderr_p;
820 CHECK("overlayTextBackground") continue; /* don't save */
821 CHECK("overlayTextForeground") continue; /* don't save */
822 CHECK("bourneShell") continue;
831 sprintf(buf, "%d", i);
835 s = b ? "True" : "False";
839 unsigned int hour = 0, min = 0, sec = (unsigned int) (t/1000);
850 sprintf (buf, "%u:%02u:%02u", hour, min, sec);
856 if (i >= (1<<30) && i == ((i >> 30) << 30))
857 sprintf(buf, "%dG", i >> 30);
858 else if (i >= (1<<20) && i == ((i >> 20) << 20))
859 sprintf(buf, "%dM", i >> 20);
860 else if (i >= (1<<10) && i == ((i >> 10) << 10))
861 sprintf(buf, "%dK", i >> 10);
863 sprintf(buf, "%d", i);
872 if (pr && !strcmp(pr, "mode")) fprintf(out, "\n");
874 write_entry (out, pr, s);
879 if (visual_name) free(visual_name);
880 if (stderr_font) free(stderr_font);
881 if (programs) free(programs);
883 if (fclose(out) == 0)
885 time_t write_date = 0;
887 if (stat(tmp_name, &st) == 0)
889 write_date = st.st_mtime;
893 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
894 sprintf(buf, "%s: couldn't stat \"%s\"", blurb(), tmp_name);
901 if (rename (tmp_name, name) != 0)
903 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
904 sprintf(buf, "%s: error renaming \"%s\" to \"%s\"",
905 blurb(), tmp_name, name);
913 p->init_file_date = write_date;
915 /* Since the .xscreensaver file is used for IPC, let's try and make
916 sure that the bits actually land on the disk right away. */
919 status = 0; /* wrote and renamed successfully! */
924 char *buf = (char *) malloc(1024 + strlen(name));
925 sprintf(buf, "%s: error closing \"%s\"", blurb(), name);
938 /* Parsing the resource database
942 free_screenhack (screenhack *hack)
944 if (hack->visual) free (hack->visual);
945 if (hack->name) free (hack->name);
946 free (hack->command);
947 memset (hack, 0, sizeof(*hack));
952 free_screenhack_list (screenhack **list, int count)
956 for (i = 0; i < count; i++)
958 free_screenhack (list[i]);
964 /* Populate `saver_preferences' with the contents of the resource database.
965 Note that this may be called multiple times -- it is re-run each time
966 the ~/.xscreensaver file is reloaded.
968 This function can be very noisy, since it issues resource syntax errors
972 load_init_file (saver_preferences *p)
974 static Bool first_time = True;
976 screenhack **system_default_screenhacks = 0;
977 int system_default_screenhack_count = 0;
981 /* Get the programs resource before the .xscreensaver file has been
982 parsed and merged into the resource database for the first time:
983 this is the value of *programs from the app-defaults file.
984 Then clear it out so that it will be parsed again later, after
985 the init file has been read.
988 system_default_screenhacks = p->screenhacks;
989 system_default_screenhack_count = p->screenhacks_count;
991 p->screenhacks_count = 0;
994 if (parse_init_file (p) != 0) /* file might have gone away */
995 if (!first_time) return;
999 p->xsync_p = get_boolean_resource ("synchronous", "Synchronous");
1000 p->verbose_p = get_boolean_resource ("verbose", "Boolean");
1001 p->timestamp_p = get_boolean_resource ("timestamp", "Boolean");
1002 p->lock_p = get_boolean_resource ("lock", "Boolean");
1003 p->lock_vt_p = get_boolean_resource ("lockVTs", "Boolean");
1004 p->fade_p = get_boolean_resource ("fade", "Boolean");
1005 p->unfade_p = get_boolean_resource ("unfade", "Boolean");
1006 p->fade_seconds = 1000 * get_seconds_resource ("fadeSeconds", "Time");
1007 p->fade_ticks = get_integer_resource ("fadeTicks", "Integer");
1008 p->install_cmap_p = get_boolean_resource ("installColormap", "Boolean");
1009 p->nice_inferior = get_integer_resource ("nice", "Nice");
1010 p->inferior_memory_limit = get_byte_resource ("memoryLimit", "MemoryLimit");
1011 p->splash_p = get_boolean_resource ("splash", "Boolean");
1012 p->quad_p = get_boolean_resource ("quad", "Boolean");
1013 p->capture_stderr_p = get_boolean_resource ("captureStderr", "Boolean");
1014 p->ignore_uninstalled_p = get_boolean_resource ("ignoreUninstalledPrograms",
1017 p->initial_delay = 1000 * get_seconds_resource ("initialDelay", "Time");
1018 p->splash_duration = 1000 * get_seconds_resource ("splashDuration", "Time");
1019 p->timeout = 1000 * get_minutes_resource ("timeout", "Time");
1020 p->lock_timeout = 1000 * get_minutes_resource ("lockTimeout", "Time");
1021 p->cycle = 1000 * get_minutes_resource ("cycle", "Time");
1022 p->passwd_timeout = 1000 * get_seconds_resource ("passwdTimeout", "Time");
1023 p->pointer_timeout = 1000 * get_seconds_resource ("pointerPollTime", "Time");
1024 p->notice_events_timeout = 1000*get_seconds_resource("windowCreationTimeout",
1027 p->dpms_enabled_p = get_boolean_resource ("dpmsEnabled", "Boolean");
1028 p->dpms_standby = 1000 * get_minutes_resource ("dpmsStandby", "Time");
1029 p->dpms_suspend = 1000 * get_minutes_resource ("dpmsSuspend", "Time");
1030 p->dpms_off = 1000 * get_minutes_resource ("dpmsOff", "Time");
1032 p->grab_desktop_p = get_boolean_resource ("grabDesktopImages", "Boolean");
1033 p->grab_video_p = get_boolean_resource ("grabVideoFrames", "Boolean");
1034 p->random_image_p = get_boolean_resource ("chooseRandomImages", "Boolean");
1035 p->image_directory = get_string_resource ("imageDirectory",
1038 p->shell = get_string_resource ("bourneShell", "BourneShell");
1040 p->demo_command = get_string_resource("demoCommand", "URL");
1041 p->prefs_command = get_string_resource("prefsCommand", "URL");
1042 p->help_url = get_string_resource("helpURL", "URL");
1043 p->load_url_command = get_string_resource("loadURL", "LoadURL");
1046 /* If "*splash" is unset, default to true. */
1048 char *s = get_string_resource ("splash", "Boolean");
1055 /* If "*grabDesktopImages" is unset, default to true. */
1057 char *s = get_string_resource ("grabDesktopImages", "Boolean");
1061 p->grab_desktop_p = True;
1064 p->use_xidle_extension = get_boolean_resource ("xidleExtension","Boolean");
1065 p->use_mit_saver_extension = get_boolean_resource ("mitSaverExtension",
1067 p->use_sgi_saver_extension = get_boolean_resource ("sgiSaverExtension",
1069 p->use_proc_interrupts = get_boolean_resource ("procInterrupts", "Boolean");
1071 p->getviewport_full_of_lies_p =
1072 get_boolean_resource ("GetViewPortIsFullOfLies", "Boolean");
1074 get_screenhacks (p); /* Parse the "programs" resource. */
1076 p->selected_hack = get_integer_resource ("selected", "Integer");
1077 if (p->selected_hack < 0 || p->selected_hack >= p->screenhacks_count)
1078 p->selected_hack = -1;
1081 char *s = get_string_resource ("mode", "Mode");
1082 if (s && !strcasecmp (s, "one")) p->mode = ONE_HACK;
1083 else if (s && !strcasecmp (s, "blank")) p->mode = BLANK_ONLY;
1084 else if (s && !strcasecmp (s, "off")) p->mode = DONT_BLANK;
1085 else p->mode = RANDOM_HACKS;
1089 if (system_default_screenhack_count) /* note: first_time is also true */
1091 merge_system_screenhacks (p, system_default_screenhacks,
1092 system_default_screenhack_count);
1093 free_screenhack_list (system_default_screenhacks,
1094 system_default_screenhack_count);
1095 system_default_screenhacks = 0;
1096 system_default_screenhack_count = 0;
1102 p->verbose_p = True;
1103 p->timestamp_p = True;
1104 p->initial_delay = 0;
1107 /* Throttle the various timeouts to reasonable values after reading the
1109 stop_the_insanity (p);
1113 /* If there are any hacks in the system-wide defaults that are not in
1114 the ~/.xscreensaver file, add the new ones to the end of the list.
1115 This does *not* actually save the file.
1118 merge_system_screenhacks (saver_preferences *p,
1119 screenhack **system_list, int system_count)
1121 /* Yeah yeah, this is an N^2 operation, but I don't have hashtables handy,
1126 for (i = 0; i < system_count; i++)
1129 Bool matched_p = False;
1131 for (j = 0; j < p->screenhacks_count; j++)
1134 if (!system_list[i]->name)
1135 system_list[i]->name = make_hack_name (system_list[i]->command);
1137 name = p->screenhacks[j]->name;
1139 name = make_hack_name (p->screenhacks[j]->command);
1141 matched_p = !strcasecmp (name, system_list[i]->name);
1143 if (name != p->screenhacks[j]->name)
1152 /* We have an entry in the system-wide list that is not in the
1153 user's .xscreensaver file. Add it to the end.
1154 Note that p->screenhacks is a single malloc block, not a
1155 linked list, so we have to realloc it.
1157 screenhack *oh = system_list[i];
1158 screenhack *nh = (screenhack *) malloc (sizeof(screenhack));
1160 if (made_space == 0)
1163 p->screenhacks = (screenhack **)
1164 realloc (p->screenhacks,
1165 (p->screenhacks_count + made_space + 1)
1166 * sizeof(screenhack));
1167 if (!p->screenhacks) abort();
1170 nh->enabled_p = oh->enabled_p;
1171 nh->visual = oh->visual ? strdup(oh->visual) : 0;
1172 nh->name = oh->name ? strdup(oh->name) : 0;
1173 nh->command = oh->command ? strdup(oh->command) : 0;
1175 p->screenhacks[p->screenhacks_count++] = nh;
1176 p->screenhacks[p->screenhacks_count] = 0;
1180 fprintf (stderr, "%s: noticed new hack: %s\n", blurb(),
1181 (nh->name ? nh->name : make_hack_name (nh->command)));
1189 /* Parsing the programs resource.
1193 parse_screenhack (const char *line)
1195 screenhack *h = (screenhack *) calloc (1, sizeof(*h));
1198 h->enabled_p = True;
1200 while (isspace(*line)) line++; /* skip whitespace */
1201 if (*line == '-') /* handle "-" */
1203 h->enabled_p = False;
1205 while (isspace(*line)) line++; /* skip whitespace */
1208 s = line; /* handle "visual:" */
1209 while (*line && *line != ':' && *line != '"' && !isspace(*line))
1215 h->visual = (char *) malloc (line-s+1);
1216 strncpy (h->visual, s, line-s);
1217 h->visual[line-s] = 0;
1218 if (*line == ':') line++; /* skip ":" */
1219 while (isspace(*line)) line++; /* skip whitespace */
1222 if (*line == '"') /* handle "name" */
1226 while (*line && *line != '"')
1228 h->name = (char *) malloc (line-s+1);
1229 strncpy (h->name, s, line-s);
1230 h->name[line-s] = 0;
1231 if (*line == '"') line++; /* skip "\"" */
1232 while (isspace(*line)) line++; /* skip whitespace */
1235 h->command = format_command (line, False); /* handle command */
1241 format_command (const char *cmd, Bool wrap_p)
1245 char *cmd2 = (char *) calloc (1, 2 * (strlen (cmd) + 1));
1246 const char *in = cmd;
1250 /* shrink all whitespace to one space, for the benefit of the "demo"
1251 mode display. We only do this when we can easily tell that the
1252 whitespace is not significant (no shell metachars).
1256 case '\'': case '"': case '`': case '\\':
1257 /* Metachars are scary. Copy the rest of the line unchanged. */
1259 *out++ = *in++, col++;
1262 case ' ': case '\t':
1263 /* Squeeze all other whitespace down to one space. */
1264 while (*in == ' ' || *in == '\t')
1266 *out++ = ' ', col++;
1270 /* Copy other chars unchanged. */
1271 *out++ = *in++, col++;
1278 /* Strip trailing whitespace */
1279 while (out > cmd2 && isspace (out[-1]))
1286 /* Returns a new string describing the shell command.
1287 This may be just the name of the program, capitalized.
1288 It also may be something from the resource database (gotten
1289 by looking for "hacks.XYZ.name", where XYZ is the program.)
1292 make_hack_name (const char *shell_command)
1294 char *s = strdup (shell_command);
1298 for (s2 = s; *s2; s2++) /* truncate at first whitespace */
1305 s2 = strrchr (s, '/'); /* if pathname, take last component */
1313 if (strlen (s) > 50) /* 51 is hereby defined as "unreasonable" */
1316 sprintf (res_name, "hacks.%s.name", s); /* resource? */
1317 s2 = get_string_resource (res_name, res_name);
1324 for (s2 = s; *s2; s2++) /* if it has any capitals, return it */
1325 if (*s2 >= 'A' && *s2 <= 'Z')
1328 if (s[0] >= 'a' && s[0] <= 'z') /* else cap it */
1330 if (s[0] == 'X' && s[1] >= 'a' && s[1] <= 'z') /* (magic leading X) */
1332 if (s[0] == 'G' && s[1] == 'l' &&
1333 s[2] >= 'a' && s[2] <= 'z') /* (magic leading GL) */
1341 format_hack (screenhack *hack, Bool wrap_p)
1348 char *def_name = make_hack_name (hack->command);
1350 /* Don't ever write out a name for a hack if it's the same as the default.
1352 if (hack->name && !strcmp (hack->name, def_name))
1359 size = (2 * (strlen(hack->command) +
1360 (hack->visual ? strlen(hack->visual) : 0) +
1361 (hack->name ? strlen(hack->name) : 0) +
1363 h2 = (char *) malloc (size);
1366 if (!hack->enabled_p) *out++ = '-'; /* write disabled flag */
1368 if (hack->visual && *hack->visual) /* write visual name */
1370 if (hack->enabled_p) *out++ = ' ';
1372 strcpy (out, hack->visual);
1373 out += strlen (hack->visual);
1379 col = string_columns (h2, strlen (h2), 0);
1381 if (hack->name && *hack->name) /* write pretty name */
1383 int L = (strlen (hack->name) + 2);
1385 out = stab_to (out, col, tab - L - 2);
1389 strcpy (out, hack->name);
1390 out += strlen (hack->name);
1394 col = string_columns (h2, strlen (h2), 0);
1395 if (wrap_p && col >= tab)
1396 out = stab_to (out, col, 77);
1400 if (out >= h2+size) abort();
1404 col = string_columns (h2, strlen (h2), 0);
1405 out = stab_to (out, col, tab); /* indent */
1407 if (out >= h2+size) abort();
1408 s = format_command (hack->command, wrap_p);
1419 get_screenhacks (saver_preferences *p)
1427 d = get_string_resource ("monoPrograms", "MonoPrograms");
1428 if (d && !*d) { free(d); d = 0; }
1430 d = get_string_resource ("colorPrograms", "ColorPrograms");
1431 if (d && !*d) { free(d); d = 0; }
1436 "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
1437 see the manual for details.\n", blurb());
1441 d = get_string_resource ("programs", "Programs");
1443 free_screenhack_list (p->screenhacks, p->screenhacks_count);
1445 p->screenhacks_count = 0;
1453 /* Count up the number of newlines (which will be equal to or larger than
1454 one less than the number of hacks.)
1456 for (i = j = 0; d[i]; i++)
1461 p->screenhacks = (screenhack **) calloc (j + 1, sizeof (screenhack *));
1463 /* Iterate over the lines in `d' (the string with newlines)
1464 and make new strings to stuff into the `screenhacks' array.
1466 p->screenhacks_count = 0;
1467 while (start < size)
1469 /* skip forward over whitespace. */
1470 while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n')
1473 /* skip forward to newline or end of string. */
1475 while (d[end] != 0 && d[end] != '\n')
1478 /* null terminate. */
1481 p->screenhacks[p->screenhacks_count++] = parse_screenhack (d + start);
1482 if (p->screenhacks_count >= i)
1490 if (p->screenhacks_count == 0)
1492 free (p->screenhacks);
1498 /* Make sure all the values in the preferences struct are sane.
1501 stop_the_insanity (saver_preferences *p)
1503 if (p->passwd_timeout <= 0) p->passwd_timeout = 30000; /* 30 secs */
1504 if (p->timeout < 15000) p->timeout = 15000; /* 15 secs */
1505 if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */
1506 if (p->pointer_timeout <= 0) p->pointer_timeout = 5000; /* 5 secs */
1507 if (p->notice_events_timeout <= 0)
1508 p->notice_events_timeout = 10000; /* 10 secs */
1509 if (p->fade_seconds <= 0 || p->fade_ticks <= 0)
1511 if (! p->fade_p) p->unfade_p = False;
1513 /* The DPMS settings may have the value 0.
1514 But if they are negative, or are a range less than 10 seconds,
1515 reset them to sensible defaults. (Since that must be a mistake.)
1517 if (p->dpms_standby != 0 &&
1518 p->dpms_standby < 10 * 1000)
1519 p->dpms_standby = 2 * 60 * 60 * 1000; /* 2 hours */
1520 if (p->dpms_suspend != 0 &&
1521 p->dpms_suspend < 10 * 1000)
1522 p->dpms_suspend = 2 * 60 * 60 * 1000; /* 2 hours */
1523 if (p->dpms_off != 0 &&
1524 p->dpms_off < 10 * 1000)
1525 p->dpms_off = 4 * 60 * 60 * 1000; /* 4 hours */
1527 if (p->dpms_standby == 0 && /* if *all* are 0, then DPMS is disabled */
1528 p->dpms_suspend == 0 &&
1530 p->dpms_enabled_p = False;
1532 p->watchdog_timeout = p->cycle * 0.6;
1533 if (p->watchdog_timeout < 30000) p->watchdog_timeout = 30000; /* 30 secs */
1534 if (p->watchdog_timeout > 3600000) p->watchdog_timeout = 3600000; /* 1 hr */