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[] = {
258 "captureStdout", /* not saved -- obsolete */
259 "ignoreUninstalledPrograms",
267 "chooseRandomImages",
275 "windowCreationTimeout",
282 "overlayTextBackground", /* not saved -- X resources only */
283 "overlayTextForeground", /* not saved -- X resources only */
284 "bourneShell", /* not saved -- X resources only */
292 while (*s == '\t' || *s == ' ' || *s == '\r' || *s == '\n')
294 for (s2 = s; *s2; s2++)
296 for (s2--; s2 >= s; s2--)
297 if (*s2 == '\t' || *s2 == ' ' || *s2 == '\r' || *s2 =='\n')
309 handle_entry (XrmDatabase *db, const char *key, const char *value,
310 const char *filename, int line)
313 for (i = 0; prefs[i]; i++)
314 if (*prefs[i] && !strcasecmp(key, prefs[i]))
316 char *val = strdup(value);
317 char *spec = (char *) malloc(strlen(progclass) + strlen(prefs[i]) +10);
318 strcpy(spec, progclass);
320 strcat(spec, prefs[i]);
322 XrmPutStringResource (db, spec, val);
329 fprintf(stderr, "%s: %s:%d: unknown option \"%s\"\n",
330 blurb(), filename, line, key);
336 parse_init_file (saver_preferences *p)
338 time_t write_date = 0;
339 const char *name = init_file_name();
348 if (stat(name, &st) != 0)
350 p->init_file_date = 0;
354 in = fopen(name, "r");
357 char *buf = (char *) malloc(1024 + strlen(name));
358 sprintf(buf, "%s: error reading \"%s\"", blurb(), name);
364 if (fstat (fileno(in), &st) == 0)
366 write_date = st.st_mtime;
370 char *buf = (char *) malloc(1024 + strlen(name));
371 sprintf(buf, "%s: couldn't re-stat \"%s\"", blurb(), name);
377 buf = (char *) malloc(buf_size);
379 while (fgets (buf, buf_size-1, in))
386 (buf[L-1] != '\n' || /* whole line didn't fit in buffer */
387 buf[L-2] == '\\')) /* or line ended with backslash */
389 if (buf[L-2] == '\\') /* backslash-newline gets swallowed */
395 buf = (char *) realloc(buf, buf_size);
399 if (!fgets (buf + L, buf_size-L-1, in))
404 /* Now handle other backslash escapes. */
407 for (i = 0; buf[i]; i++)
412 case 'n': buf[i] = '\n'; break;
413 case 'r': buf[i] = '\r'; break;
414 case 't': buf[i] = '\t'; break;
415 default: buf[i] = buf[i+1]; break;
417 for (j = i+2; buf[j]; j++)
425 if (*key == '#' || *key == '!' || *key == ';' ||
426 *key == '\n' || *key == 0)
429 value = strchr (key, ':');
432 fprintf(stderr, "%s: %s:%d: unparsable line: %s\n", blurb(),
439 value = strip(value);
443 handle_entry (&p->db, key, value, name, line);
448 p->init_file_date = write_date;
454 init_file_changed_p (saver_preferences *p)
456 const char *name = init_file_name();
459 if (!name) return False;
461 if (stat(name, &st) != 0)
464 if (p->init_file_date == st.st_mtime)
475 tab_to (FILE *out, int from, int to)
478 int to_mod = (to / tab_width) * tab_width;
479 while (from < to_mod)
482 from = (((from / tab_width) + 1) * tab_width);
493 stab_to (char *out, int from, int to)
496 int to_mod = (to / tab_width) * tab_width;
497 while (from < to_mod)
500 from = (((from / tab_width) + 1) * tab_width);
511 string_columns (const char *string, int length, int start)
515 const char *end = string + length;
520 else if (*string == '\t')
521 col = (((col / tab_width) + 1) * tab_width);
531 write_entry (FILE *out, const char *key, const char *value)
533 char *v = strdup(value ? value : "");
537 Bool programs_p = (!strcmp(key, "programs"));
538 int tab = (programs_p ? 32 : 16);
541 fprintf(out, "%s:", key);
542 col = strlen(key) + 1;
544 if (strlen(key) > 14)
545 col = tab_to (out, col, 20);
551 nl = strchr(v2, '\n');
555 if (first && programs_p)
557 col = tab_to (out, col, 77);
558 fprintf (out, " \\\n");
566 col = tab_to (out, col, 75);
567 fprintf (out, " \\n\\\n");
572 col = tab_to (out, col, tab);
575 string_columns(v2, strlen (v2), col) + col > 75)
582 while (v2[end] == ' ' || v2[end] == '\t')
584 while (v2[end] != ' ' && v2[end] != '\t' &&
585 v2[end] != '\n' && v2[end] != 0)
587 if (string_columns (v2 + start, (end - start), col) >= 74)
589 col = tab_to (out, col, 75);
590 fprintf(out, " \\\n");
591 col = tab_to (out, 0, tab + 2);
592 while (v2[start] == ' ' || v2[start] == '\t')
596 col = string_columns (v2 + start, (end - start), col);
598 fputc(v2[start++], out);
603 fprintf (out, "%s", v2);
604 col += string_columns(v2, strlen (v2), col);
618 write_init_file (saver_preferences *p, const char *version_string,
622 const char *name = init_file_name();
623 const char *tmp_name = init_file_tmp_name();
624 char *n2 = chase_symlinks (name);
628 /* Kludge, since these aren't in the saver_preferences struct as strings...
632 Bool overlay_stderr_p;
640 /* Throttle the various timeouts to reasonable values before writing
642 stop_the_insanity (p);
646 fprintf (stderr, "%s: writing \"%s\".\n", blurb(), name);
649 out = fopen(tmp_name, "w");
652 char *buf = (char *) malloc(1024 + strlen(name));
653 sprintf(buf, "%s: error writing \"%s\"", blurb(), name);
659 /* Give the new .xscreensaver file the same permissions as the old one;
660 except ensure that it is readable and writable by owner, and not
661 executable. Extra hack: if we're running as root, make the file
662 be world-readable (so that the daemon, running as "nobody", will
663 still be able to read it.)
665 if (stat(name, &st) == 0)
667 mode_t mode = st.st_mode;
668 mode |= S_IRUSR | S_IWUSR; /* read/write by user */
669 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); /* executable by none */
671 if (getuid() == (uid_t) 0) /* read by group/other */
672 mode |= S_IRGRP | S_IROTH;
674 if (fchmod (fileno(out), mode) != 0)
676 char *buf = (char *) malloc(1024 + strlen(name));
677 sprintf (buf, "%s: error fchmodding \"%s\" to 0%o", blurb(),
678 tmp_name, (unsigned int) mode);
685 /* Kludge, since these aren't in the saver_preferences struct... */
686 visual_name = get_string_resource ("visualID", "VisualID");
688 overlay_stderr_p = get_boolean_resource ("overlayStderr", "Boolean");
689 stderr_font = get_string_resource ("font", "Font");
694 char **hack_strings = (char **)
695 calloc (p->screenhacks_count, sizeof(char *));
697 for (j = 0; j < p->screenhacks_count; j++)
699 hack_strings[j] = format_hack (p->screenhacks[j], True);
700 i += strlen (hack_strings[j]);
704 ss = programs = (char *) malloc(i + 10);
706 for (j = 0; j < p->screenhacks_count; j++)
708 strcat (ss, hack_strings[j]);
709 free (hack_strings[j]);
718 struct passwd *pw = getpwuid (getuid ());
719 char *whoami = (pw && pw->pw_name && *pw->pw_name
722 time_t now = time ((time_t *) 0);
723 char *timestr = (char *) ctime (&now);
724 char *nl = (char *) strchr (timestr, '\n');
727 "# %s Preferences File\n"
728 "# Written by %s %s for %s on %s.\n"
729 "# http://www.jwz.org/xscreensaver/\n"
731 progclass, progname, version_string, whoami, timestr);
734 for (j = 0; prefs[j]; j++)
737 const char *pr = prefs[j];
738 enum pref_type { pref_str, pref_int, pref_bool, pref_byte, pref_time
752 # define CHECK(X) else if (!strcmp(pr, X))
754 CHECK("timeout") type = pref_time, t = p->timeout;
755 CHECK("cycle") type = pref_time, t = p->cycle;
756 CHECK("lock") type = pref_bool, b = p->lock_p;
757 # if 0 /* #### not ready yet */
758 CHECK("lockVTs") type = pref_bool, b = p->lock_vt_p;
760 CHECK("lockVTs") continue; /* don't save */
762 CHECK("lockTimeout") type = pref_time, t = p->lock_timeout;
763 CHECK("passwdTimeout") type = pref_time, t = p->passwd_timeout;
764 CHECK("visualID") type = pref_str, s = visual_name;
765 CHECK("installColormap") type = pref_bool, b = p->install_cmap_p;
766 CHECK("verbose") type = pref_bool, b = p->verbose_p;
767 CHECK("timestamp") type = pref_bool, b = p->timestamp_p;
768 CHECK("splash") type = pref_bool, b = p->splash_p;
769 CHECK("splashDuration") type = pref_time, t = p->splash_duration;
770 CHECK("demoCommand") type = pref_str, s = p->demo_command;
771 CHECK("prefsCommand") type = pref_str, s = p->prefs_command;
772 /* CHECK("helpURL") type = pref_str, s = p->help_url; */
773 CHECK("helpURL") continue; /* don't save */
774 /* CHECK("loadURL") type = pref_str, s = p->load_url_command; */
775 CHECK("loadURL") continue; /* don't save */
776 CHECK("nice") type = pref_int, i = p->nice_inferior;
777 CHECK("memoryLimit") type = pref_byte, i = p->inferior_memory_limit;
778 CHECK("fade") type = pref_bool, b = p->fade_p;
779 CHECK("unfade") type = pref_bool, b = p->unfade_p;
780 CHECK("fadeSeconds") type = pref_time, t = p->fade_seconds;
781 CHECK("fadeTicks") type = pref_int, i = p->fade_ticks;
782 CHECK("captureStderr") type = pref_bool, b = p->capture_stderr_p;
783 CHECK("captureStdout") continue; /* don't save */
784 CHECK("ignoreUninstalledPrograms")
785 type = pref_bool, b = p->ignore_uninstalled_p;
787 CHECK("font") type = pref_str, s = stderr_font;
789 CHECK("dpmsEnabled") type = pref_bool, b = p->dpms_enabled_p;
790 CHECK("dpmsStandby") type = pref_time, t = p->dpms_standby;
791 CHECK("dpmsSuspend") type = pref_time, t = p->dpms_suspend;
792 CHECK("dpmsOff") type = pref_time, t = p->dpms_off;
794 CHECK("grabDesktopImages") type =pref_bool, b = p->grab_desktop_p;
795 CHECK("grabVideoFrames") type =pref_bool, b = p->grab_video_p;
796 CHECK("chooseRandomImages")type =pref_bool, b = p->random_image_p;
797 CHECK("imageDirectory") type =pref_str, s = p->image_directory;
799 CHECK("mode") type = pref_str,
800 s = (p->mode == ONE_HACK ? "one" :
801 p->mode == BLANK_ONLY ? "blank" :
802 p->mode == DONT_BLANK ? "off" :
804 CHECK("selected") type = pref_int, i = p->selected_hack;
806 CHECK("programs") type = pref_str, s = programs;
807 CHECK("pointerPollTime") type = pref_time, t = p->pointer_timeout;
808 CHECK("windowCreationTimeout")type=pref_time,t= p->notice_events_timeout;
809 CHECK("initialDelay") type = pref_time, t = p->initial_delay;
810 CHECK("sgiSaverExtension")type = pref_bool, b=p->use_sgi_saver_extension;
811 CHECK("mitSaverExtension")type = pref_bool, b=p->use_mit_saver_extension;
812 CHECK("xidleExtension") type = pref_bool, b = p->use_xidle_extension;
813 CHECK("procInterrupts") type = pref_bool, b = p->use_proc_interrupts;
814 CHECK("overlayStderr") type = pref_bool, b = overlay_stderr_p;
815 CHECK("overlayTextBackground") continue; /* don't save */
816 CHECK("overlayTextForeground") continue; /* don't save */
817 CHECK("bourneShell") continue;
826 sprintf(buf, "%d", i);
830 s = b ? "True" : "False";
834 unsigned int hour = 0, min = 0, sec = (unsigned int) (t/1000);
845 sprintf (buf, "%u:%02u:%02u", hour, min, sec);
851 if (i >= (1<<30) && i == ((i >> 30) << 30))
852 sprintf(buf, "%dG", i >> 30);
853 else if (i >= (1<<20) && i == ((i >> 20) << 20))
854 sprintf(buf, "%dM", i >> 20);
855 else if (i >= (1<<10) && i == ((i >> 10) << 10))
856 sprintf(buf, "%dK", i >> 10);
858 sprintf(buf, "%d", i);
867 if (pr && !strcmp(pr, "mode")) fprintf(out, "\n");
869 write_entry (out, pr, s);
874 if (visual_name) free(visual_name);
875 if (stderr_font) free(stderr_font);
876 if (programs) free(programs);
878 if (fclose(out) == 0)
880 time_t write_date = 0;
882 if (stat(tmp_name, &st) == 0)
884 write_date = st.st_mtime;
888 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
889 sprintf(buf, "%s: couldn't stat \"%s\"", blurb(), tmp_name);
896 if (rename (tmp_name, name) != 0)
898 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
899 sprintf(buf, "%s: error renaming \"%s\" to \"%s\"",
900 blurb(), tmp_name, name);
908 p->init_file_date = write_date;
910 /* Since the .xscreensaver file is used for IPC, let's try and make
911 sure that the bits actually land on the disk right away. */
914 status = 0; /* wrote and renamed successfully! */
919 char *buf = (char *) malloc(1024 + strlen(name));
920 sprintf(buf, "%s: error closing \"%s\"", blurb(), name);
933 /* Parsing the resource database
937 free_screenhack (screenhack *hack)
939 if (hack->visual) free (hack->visual);
940 if (hack->name) free (hack->name);
941 free (hack->command);
942 memset (hack, 0, sizeof(*hack));
947 free_screenhack_list (screenhack **list, int count)
951 for (i = 0; i < count; i++)
953 free_screenhack (list[i]);
959 /* Populate `saver_preferences' with the contents of the resource database.
960 Note that this may be called multiple times -- it is re-run each time
961 the ~/.xscreensaver file is reloaded.
963 This function can be very noisy, since it issues resource syntax errors
967 load_init_file (saver_preferences *p)
969 static Bool first_time = True;
971 screenhack **system_default_screenhacks = 0;
972 int system_default_screenhack_count = 0;
976 /* Get the programs resource before the .xscreensaver file has been
977 parsed and merged into the resource database for the first time:
978 this is the value of *programs from the app-defaults file.
979 Then clear it out so that it will be parsed again later, after
980 the init file has been read.
983 system_default_screenhacks = p->screenhacks;
984 system_default_screenhack_count = p->screenhacks_count;
986 p->screenhacks_count = 0;
989 if (parse_init_file (p) != 0) /* file might have gone away */
990 if (!first_time) return;
994 p->xsync_p = get_boolean_resource ("synchronous", "Synchronous");
995 p->verbose_p = get_boolean_resource ("verbose", "Boolean");
996 p->timestamp_p = get_boolean_resource ("timestamp", "Boolean");
997 p->lock_p = get_boolean_resource ("lock", "Boolean");
998 p->lock_vt_p = get_boolean_resource ("lockVTs", "Boolean");
999 p->fade_p = get_boolean_resource ("fade", "Boolean");
1000 p->unfade_p = get_boolean_resource ("unfade", "Boolean");
1001 p->fade_seconds = 1000 * get_seconds_resource ("fadeSeconds", "Time");
1002 p->fade_ticks = get_integer_resource ("fadeTicks", "Integer");
1003 p->install_cmap_p = get_boolean_resource ("installColormap", "Boolean");
1004 p->nice_inferior = get_integer_resource ("nice", "Nice");
1005 p->inferior_memory_limit = get_byte_resource ("memoryLimit", "MemoryLimit");
1006 p->splash_p = get_boolean_resource ("splash", "Boolean");
1007 p->capture_stderr_p = get_boolean_resource ("captureStderr", "Boolean");
1008 p->ignore_uninstalled_p = get_boolean_resource ("ignoreUninstalledPrograms",
1011 p->initial_delay = 1000 * get_seconds_resource ("initialDelay", "Time");
1012 p->splash_duration = 1000 * get_seconds_resource ("splashDuration", "Time");
1013 p->timeout = 1000 * get_minutes_resource ("timeout", "Time");
1014 p->lock_timeout = 1000 * get_minutes_resource ("lockTimeout", "Time");
1015 p->cycle = 1000 * get_minutes_resource ("cycle", "Time");
1016 p->passwd_timeout = 1000 * get_seconds_resource ("passwdTimeout", "Time");
1017 p->pointer_timeout = 1000 * get_seconds_resource ("pointerPollTime", "Time");
1018 p->notice_events_timeout = 1000*get_seconds_resource("windowCreationTimeout",
1021 p->dpms_enabled_p = get_boolean_resource ("dpmsEnabled", "Boolean");
1022 p->dpms_standby = 1000 * get_minutes_resource ("dpmsStandby", "Time");
1023 p->dpms_suspend = 1000 * get_minutes_resource ("dpmsSuspend", "Time");
1024 p->dpms_off = 1000 * get_minutes_resource ("dpmsOff", "Time");
1026 p->grab_desktop_p = get_boolean_resource ("grabDesktopImages", "Boolean");
1027 p->grab_video_p = get_boolean_resource ("grabVideoFrames", "Boolean");
1028 p->random_image_p = get_boolean_resource ("chooseRandomImages", "Boolean");
1029 p->image_directory = get_string_resource ("imageDirectory",
1032 p->shell = get_string_resource ("bourneShell", "BourneShell");
1034 p->demo_command = get_string_resource("demoCommand", "URL");
1035 p->prefs_command = get_string_resource("prefsCommand", "URL");
1036 p->help_url = get_string_resource("helpURL", "URL");
1037 p->load_url_command = get_string_resource("loadURL", "LoadURL");
1040 /* If "*splash" is unset, default to true. */
1042 char *s = get_string_resource ("splash", "Boolean");
1049 /* If "*grabDesktopImages" is unset, default to true. */
1051 char *s = get_string_resource ("grabDesktopImages", "Boolean");
1055 p->grab_desktop_p = True;
1058 p->use_xidle_extension = get_boolean_resource ("xidleExtension","Boolean");
1059 p->use_mit_saver_extension = get_boolean_resource ("mitSaverExtension",
1061 p->use_sgi_saver_extension = get_boolean_resource ("sgiSaverExtension",
1063 p->use_proc_interrupts = get_boolean_resource ("procInterrupts", "Boolean");
1065 get_screenhacks (p); /* Parse the "programs" resource. */
1067 p->selected_hack = get_integer_resource ("selected", "Integer");
1068 if (p->selected_hack < 0 || p->selected_hack >= p->screenhacks_count)
1069 p->selected_hack = -1;
1072 char *s = get_string_resource ("mode", "Mode");
1073 if (s && !strcasecmp (s, "one")) p->mode = ONE_HACK;
1074 else if (s && !strcasecmp (s, "blank")) p->mode = BLANK_ONLY;
1075 else if (s && !strcasecmp (s, "off")) p->mode = DONT_BLANK;
1076 else p->mode = RANDOM_HACKS;
1080 if (system_default_screenhack_count) /* note: first_time is also true */
1082 merge_system_screenhacks (p, system_default_screenhacks,
1083 system_default_screenhack_count);
1084 free_screenhack_list (system_default_screenhacks,
1085 system_default_screenhack_count);
1086 system_default_screenhacks = 0;
1087 system_default_screenhack_count = 0;
1093 p->verbose_p = True;
1094 p->timestamp_p = True;
1095 p->initial_delay = 0;
1098 /* Throttle the various timeouts to reasonable values after reading the
1100 stop_the_insanity (p);
1104 /* If there are any hacks in the system-wide defaults that are not in
1105 the ~/.xscreensaver file, add the new ones to the end of the list.
1106 This does *not* actually save the file.
1109 merge_system_screenhacks (saver_preferences *p,
1110 screenhack **system_list, int system_count)
1112 /* Yeah yeah, this is an N^2 operation, but I don't have hashtables handy,
1117 for (i = 0; i < system_count; i++)
1120 Bool matched_p = False;
1122 for (j = 0; j < p->screenhacks_count; j++)
1125 if (!system_list[i]->name)
1126 system_list[i]->name = make_hack_name (system_list[i]->command);
1128 name = p->screenhacks[j]->name;
1130 name = make_hack_name (p->screenhacks[j]->command);
1132 matched_p = !strcasecmp (name, system_list[i]->name);
1134 if (name != p->screenhacks[j]->name)
1143 /* We have an entry in the system-wide list that is not in the
1144 user's .xscreensaver file. Add it to the end.
1145 Note that p->screenhacks is a single malloc block, not a
1146 linked list, so we have to realloc it.
1148 screenhack *oh = system_list[i];
1149 screenhack *nh = (screenhack *) malloc (sizeof(screenhack));
1151 if (made_space == 0)
1154 p->screenhacks = (screenhack **)
1155 realloc (p->screenhacks,
1156 (p->screenhacks_count + made_space + 1)
1157 * sizeof(screenhack));
1158 if (!p->screenhacks) abort();
1161 nh->enabled_p = oh->enabled_p;
1162 nh->visual = oh->visual ? strdup(oh->visual) : 0;
1163 nh->name = oh->name ? strdup(oh->name) : 0;
1164 nh->command = oh->command ? strdup(oh->command) : 0;
1166 p->screenhacks[p->screenhacks_count++] = nh;
1167 p->screenhacks[p->screenhacks_count] = 0;
1171 fprintf (stderr, "%s: noticed new hack: %s\n", blurb(),
1172 (nh->name ? nh->name : make_hack_name (nh->command)));
1180 /* Parsing the programs resource.
1184 parse_screenhack (const char *line)
1186 screenhack *h = (screenhack *) calloc (1, sizeof(*h));
1189 h->enabled_p = True;
1191 while (isspace(*line)) line++; /* skip whitespace */
1192 if (*line == '-') /* handle "-" */
1194 h->enabled_p = False;
1196 while (isspace(*line)) line++; /* skip whitespace */
1199 s = line; /* handle "visual:" */
1200 while (*line && *line != ':' && *line != '"' && !isspace(*line))
1206 h->visual = (char *) malloc (line-s+1);
1207 strncpy (h->visual, s, line-s);
1208 h->visual[line-s] = 0;
1209 if (*line == ':') line++; /* skip ":" */
1210 while (isspace(*line)) line++; /* skip whitespace */
1213 if (*line == '"') /* handle "name" */
1217 while (*line && *line != '"')
1219 h->name = (char *) malloc (line-s+1);
1220 strncpy (h->name, s, line-s);
1221 h->name[line-s] = 0;
1222 if (*line == '"') line++; /* skip "\"" */
1223 while (isspace(*line)) line++; /* skip whitespace */
1226 h->command = format_command (line, False); /* handle command */
1232 format_command (const char *cmd, Bool wrap_p)
1236 char *cmd2 = (char *) calloc (1, 2 * (strlen (cmd) + 1));
1237 const char *in = cmd;
1241 /* shrink all whitespace to one space, for the benefit of the "demo"
1242 mode display. We only do this when we can easily tell that the
1243 whitespace is not significant (no shell metachars).
1247 case '\'': case '"': case '`': case '\\':
1248 /* Metachars are scary. Copy the rest of the line unchanged. */
1250 *out++ = *in++, col++;
1253 case ' ': case '\t':
1254 /* Squeeze all other whitespace down to one space. */
1255 while (*in == ' ' || *in == '\t')
1257 *out++ = ' ', col++;
1261 /* Copy other chars unchanged. */
1262 *out++ = *in++, col++;
1269 /* Strip trailing whitespace */
1270 while (out > cmd2 && isspace (out[-1]))
1277 /* Returns a new string describing the shell command.
1278 This may be just the name of the program, capitalized.
1279 It also may be something from the resource database (gotten
1280 by looking for "hacks.XYZ.name", where XYZ is the program.)
1283 make_hack_name (const char *shell_command)
1285 char *s = strdup (shell_command);
1289 for (s2 = s; *s2; s2++) /* truncate at first whitespace */
1296 s2 = strrchr (s, '/'); /* if pathname, take last component */
1304 if (strlen (s) > 50) /* 51 is hereby defined as "unreasonable" */
1307 sprintf (res_name, "hacks.%s.name", s); /* resource? */
1308 s2 = get_string_resource (res_name, res_name);
1315 for (s2 = s; *s2; s2++) /* if it has any capitals, return it */
1316 if (*s2 >= 'A' && *s2 <= 'Z')
1319 if (s[0] >= 'a' && s[0] <= 'z') /* else cap it */
1321 if (s[0] == 'X' && s[1] >= 'a' && s[1] <= 'z') /* (magic leading X) */
1323 if (s[0] == 'G' && s[1] == 'l' &&
1324 s[2] >= 'a' && s[2] <= 'z') /* (magic leading GL) */
1332 format_hack (screenhack *hack, Bool wrap_p)
1339 char *def_name = make_hack_name (hack->command);
1341 /* Don't ever write out a name for a hack if it's the same as the default.
1343 if (hack->name && !strcmp (hack->name, def_name))
1350 size = (2 * (strlen(hack->command) +
1351 (hack->visual ? strlen(hack->visual) : 0) +
1352 (hack->name ? strlen(hack->name) : 0) +
1354 h2 = (char *) malloc (size);
1357 if (!hack->enabled_p) *out++ = '-'; /* write disabled flag */
1359 if (hack->visual && *hack->visual) /* write visual name */
1361 if (hack->enabled_p) *out++ = ' ';
1363 strcpy (out, hack->visual);
1364 out += strlen (hack->visual);
1370 col = string_columns (h2, strlen (h2), 0);
1372 if (hack->name && *hack->name) /* write pretty name */
1374 int L = (strlen (hack->name) + 2);
1376 out = stab_to (out, col, tab - L - 2);
1380 strcpy (out, hack->name);
1381 out += strlen (hack->name);
1385 col = string_columns (h2, strlen (h2), 0);
1386 if (wrap_p && col >= tab)
1387 out = stab_to (out, col, 77);
1391 if (out >= h2+size) abort();
1395 col = string_columns (h2, strlen (h2), 0);
1396 out = stab_to (out, col, tab); /* indent */
1398 if (out >= h2+size) abort();
1399 s = format_command (hack->command, wrap_p);
1410 get_screenhacks (saver_preferences *p)
1418 d = get_string_resource ("monoPrograms", "MonoPrograms");
1419 if (d && !*d) { free(d); d = 0; }
1421 d = get_string_resource ("colorPrograms", "ColorPrograms");
1422 if (d && !*d) { free(d); d = 0; }
1427 "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
1428 see the manual for details.\n", blurb());
1432 d = get_string_resource ("programs", "Programs");
1434 free_screenhack_list (p->screenhacks, p->screenhacks_count);
1436 p->screenhacks_count = 0;
1444 /* Count up the number of newlines (which will be equal to or larger than
1445 one less than the number of hacks.)
1447 for (i = j = 0; d[i]; i++)
1452 p->screenhacks = (screenhack **) calloc (j + 1, sizeof (screenhack *));
1454 /* Iterate over the lines in `d' (the string with newlines)
1455 and make new strings to stuff into the `screenhacks' array.
1457 p->screenhacks_count = 0;
1458 while (start < size)
1460 /* skip forward over whitespace. */
1461 while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n')
1464 /* skip forward to newline or end of string. */
1466 while (d[end] != 0 && d[end] != '\n')
1469 /* null terminate. */
1472 p->screenhacks[p->screenhacks_count++] = parse_screenhack (d + start);
1473 if (p->screenhacks_count >= i)
1481 if (p->screenhacks_count == 0)
1483 free (p->screenhacks);
1489 /* Make sure all the values in the preferences struct are sane.
1492 stop_the_insanity (saver_preferences *p)
1494 if (p->passwd_timeout <= 0) p->passwd_timeout = 30000; /* 30 secs */
1495 if (p->timeout < 15000) p->timeout = 15000; /* 15 secs */
1496 if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */
1497 if (p->pointer_timeout <= 0) p->pointer_timeout = 5000; /* 5 secs */
1498 if (p->notice_events_timeout <= 0)
1499 p->notice_events_timeout = 10000; /* 10 secs */
1500 if (p->fade_seconds <= 0 || p->fade_ticks <= 0)
1502 if (! p->fade_p) p->unfade_p = False;
1504 /* The DPMS settings may have the value 0.
1505 But if they are negative, or are a range less than 10 seconds,
1506 reset them to sensible defaults. (Since that must be a mistake.)
1508 if (p->dpms_standby != 0 &&
1509 p->dpms_standby < 10 * 1000)
1510 p->dpms_standby = 2 * 60 * 60 * 1000; /* 2 hours */
1511 if (p->dpms_suspend != 0 &&
1512 p->dpms_suspend < 10 * 1000)
1513 p->dpms_suspend = 2 * 60 * 60 * 1000; /* 2 hours */
1514 if (p->dpms_off != 0 &&
1515 p->dpms_off < 10 * 1000)
1516 p->dpms_off = 4 * 60 * 60 * 1000; /* 4 hours */
1518 if (p->dpms_standby == 0 && /* if *all* are 0, then DPMS is disabled */
1519 p->dpms_suspend == 0 &&
1521 p->dpms_enabled_p = False;
1523 p->watchdog_timeout = p->cycle * 0.6;
1524 if (p->watchdog_timeout < 30000) p->watchdog_timeout = 30000; /* 30 secs */
1525 if (p->watchdog_timeout > 3600000) p->watchdog_timeout = 3600000; /* 1 hr */