1 /* dotfile.c --- management of the ~/.xscreensaver file.
2 * xscreensaver, Copyright (c) 1998 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, "%s: realpath", blurb());
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 */
266 "chooseRandomImages",
274 "windowCreationTimeout",
281 "overlayTextBackground", /* not saved -- X resources only */
282 "overlayTextForeground", /* not saved -- X resources only */
283 "bourneShell", /* not saved -- X resources only */
291 while (*s == '\t' || *s == ' ' || *s == '\r' || *s == '\n')
293 for (s2 = s; *s2; s2++)
295 for (s2--; s2 >= s; s2--)
296 if (*s2 == '\t' || *s2 == ' ' || *s2 == '\r' || *s2 =='\n')
308 handle_entry (XrmDatabase *db, const char *key, const char *value,
309 const char *filename, int line)
312 for (i = 0; prefs[i]; i++)
313 if (*prefs[i] && !strcasecmp(key, prefs[i]))
315 char *val = strdup(value);
316 char *spec = (char *) malloc(strlen(progclass) + strlen(prefs[i]) +10);
317 strcpy(spec, progclass);
319 strcat(spec, prefs[i]);
321 XrmPutStringResource (db, spec, val);
328 fprintf(stderr, "%s: %s:%d: unknown option \"%s\"\n",
329 blurb(), filename, line, key);
335 parse_init_file (saver_preferences *p)
337 time_t write_date = 0;
338 const char *name = init_file_name();
347 if (stat(name, &st) != 0)
349 p->init_file_date = 0;
353 in = fopen(name, "r");
356 char *buf = (char *) malloc(1024 + strlen(name));
357 sprintf(buf, "%s: error reading \"%s\"", blurb(), name);
363 if (fstat (fileno(in), &st) == 0)
365 write_date = st.st_mtime;
369 char *buf = (char *) malloc(1024 + strlen(name));
370 sprintf(buf, "%s: couldn't re-stat \"%s\"", blurb(), name);
376 buf = (char *) malloc(buf_size);
378 while (fgets (buf, buf_size-1, in))
385 (buf[L-1] != '\n' || /* whole line didn't fit in buffer */
386 buf[L-2] == '\\')) /* or line ended with backslash */
388 if (buf[L-2] == '\\') /* backslash-newline gets swallowed */
394 buf = (char *) realloc(buf, buf_size);
398 if (!fgets (buf + L, buf_size-L-1, in))
403 /* Now handle other backslash escapes. */
406 for (i = 0; buf[i]; i++)
411 case 'n': buf[i] = '\n'; break;
412 case 'r': buf[i] = '\r'; break;
413 case 't': buf[i] = '\t'; break;
414 default: buf[i] = buf[i+1]; break;
416 for (j = i+2; buf[j]; j++)
424 if (*key == '#' || *key == '!' || *key == ';' ||
425 *key == '\n' || *key == 0)
428 value = strchr (key, ':');
431 fprintf(stderr, "%s: %s:%d: unparsable line: %s\n", blurb(),
438 value = strip(value);
442 handle_entry (&p->db, key, value, name, line);
447 p->init_file_date = write_date;
453 init_file_changed_p (saver_preferences *p)
455 const char *name = init_file_name();
458 if (!name) return False;
460 if (stat(name, &st) != 0)
463 if (p->init_file_date == st.st_mtime)
474 tab_to (FILE *out, int from, int to)
477 int to_mod = (to / tab_width) * tab_width;
478 while (from < to_mod)
481 from = (((from / tab_width) + 1) * tab_width);
492 stab_to (char *out, int from, int to)
495 int to_mod = (to / tab_width) * tab_width;
496 while (from < to_mod)
499 from = (((from / tab_width) + 1) * tab_width);
510 string_columns (const char *string, int length, int start)
514 const char *end = string + length;
519 else if (*string == '\t')
520 col = (((col / tab_width) + 1) * tab_width);
530 write_entry (FILE *out, const char *key, const char *value)
532 char *v = strdup(value ? value : "");
536 Bool programs_p = (!strcmp(key, "programs"));
537 int tab = (programs_p ? 32 : 16);
540 fprintf(out, "%s:", key);
541 col = strlen(key) + 1;
543 if (strlen(key) > 14)
544 col = tab_to (out, col, 20);
550 nl = strchr(v2, '\n');
554 if (first && programs_p)
556 col = tab_to (out, col, 77);
557 fprintf (out, " \\\n");
565 col = tab_to (out, col, 75);
566 fprintf (out, " \\n\\\n");
571 col = tab_to (out, col, tab);
574 string_columns(v2, strlen (v2), col) + col > 75)
581 while (v2[end] == ' ' || v2[end] == '\t')
583 while (v2[end] != ' ' && v2[end] != '\t' &&
584 v2[end] != '\n' && v2[end] != 0)
586 if (string_columns (v2 + start, (end - start), col) >= 74)
588 col = tab_to (out, col, 75);
589 fprintf(out, " \\\n");
590 col = tab_to (out, 0, tab + 2);
591 while (v2[start] == ' ' || v2[start] == '\t')
595 col = string_columns (v2 + start, (end - start), col);
597 fputc(v2[start++], out);
602 fprintf (out, "%s", v2);
603 col += string_columns(v2, strlen (v2), col);
617 write_init_file (saver_preferences *p, const char *version_string,
621 const char *name = init_file_name();
622 const char *tmp_name = init_file_tmp_name();
623 char *n2 = chase_symlinks (name);
627 /* Kludge, since these aren't in the saver_preferences struct as strings...
631 Bool overlay_stderr_p;
639 /* Throttle the various timeouts to reasonable values before writing
641 stop_the_insanity (p);
645 fprintf (stderr, "%s: writing \"%s\".\n", blurb(), name);
648 out = fopen(tmp_name, "w");
651 char *buf = (char *) malloc(1024 + strlen(name));
652 sprintf(buf, "%s: error writing \"%s\"", blurb(), name);
658 /* Give the new .xscreensaver file the same permissions as the old one;
659 except ensure that it is readable and writable by owner, and not
660 executable. Extra hack: if we're running as root, make the file
661 be world-readable (so that the daemon, running as "nobody", will
662 still be able to read it.)
664 if (stat(name, &st) == 0)
666 mode_t mode = st.st_mode;
667 mode |= S_IRUSR | S_IWUSR; /* read/write by user */
668 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); /* executable by none */
670 if (getuid() == (uid_t) 0) /* read by group/other */
671 mode |= S_IRGRP | S_IROTH;
673 if (fchmod (fileno(out), mode) != 0)
675 char *buf = (char *) malloc(1024 + strlen(name));
676 sprintf (buf, "%s: error fchmodding \"%s\" to 0%o", blurb(),
677 tmp_name, (unsigned int) mode);
684 /* Kludge, since these aren't in the saver_preferences struct... */
685 visual_name = get_string_resource ("visualID", "VisualID");
687 overlay_stderr_p = get_boolean_resource ("overlayStderr", "Boolean");
688 stderr_font = get_string_resource ("font", "Font");
693 char **hack_strings = (char **)
694 calloc (p->screenhacks_count, sizeof(char *));
696 for (j = 0; j < p->screenhacks_count; j++)
698 hack_strings[j] = format_hack (p->screenhacks[j], True);
699 i += strlen (hack_strings[j]);
703 ss = programs = (char *) malloc(i + 10);
705 for (j = 0; j < p->screenhacks_count; j++)
707 strcat (ss, hack_strings[j]);
708 free (hack_strings[j]);
717 struct passwd *pw = getpwuid (getuid ());
718 char *whoami = (pw && pw->pw_name && *pw->pw_name
721 time_t now = time ((time_t *) 0);
722 char *timestr = (char *) ctime (&now);
723 char *nl = (char *) strchr (timestr, '\n');
726 "# %s Preferences File\n"
727 "# Written by %s %s for %s on %s.\n"
728 "# http://www.jwz.org/xscreensaver/\n"
730 progclass, progname, version_string, whoami, timestr);
733 for (j = 0; prefs[j]; j++)
736 const char *pr = prefs[j];
737 enum pref_type { pref_str, pref_int, pref_bool, pref_byte, pref_time
751 # define CHECK(X) else if (!strcmp(pr, X))
753 CHECK("timeout") type = pref_time, t = p->timeout;
754 CHECK("cycle") type = pref_time, t = p->cycle;
755 CHECK("lock") type = pref_bool, b = p->lock_p;
756 # if 0 /* #### not ready yet */
757 CHECK("lockVTs") type = pref_bool, b = p->lock_vt_p;
759 CHECK("lockVTs") continue; /* don't save */
761 CHECK("lockTimeout") type = pref_time, t = p->lock_timeout;
762 CHECK("passwdTimeout") type = pref_time, t = p->passwd_timeout;
763 CHECK("visualID") type = pref_str, s = visual_name;
764 CHECK("installColormap") type = pref_bool, b = p->install_cmap_p;
765 CHECK("verbose") type = pref_bool, b = p->verbose_p;
766 CHECK("timestamp") type = pref_bool, b = p->timestamp_p;
767 CHECK("splash") type = pref_bool, b = p->splash_p;
768 CHECK("splashDuration") type = pref_time, t = p->splash_duration;
769 CHECK("demoCommand") type = pref_str, s = p->demo_command;
770 CHECK("prefsCommand") type = pref_str, s = p->prefs_command;
771 CHECK("helpURL") type = pref_str, s = p->help_url;
772 CHECK("loadURL") type = pref_str, s = p->load_url_command;
773 CHECK("nice") type = pref_int, i = p->nice_inferior;
774 CHECK("memoryLimit") type = pref_byte, i = p->inferior_memory_limit;
775 CHECK("fade") type = pref_bool, b = p->fade_p;
776 CHECK("unfade") type = pref_bool, b = p->unfade_p;
777 CHECK("fadeSeconds") type = pref_time, t = p->fade_seconds;
778 CHECK("fadeTicks") type = pref_int, i = p->fade_ticks;
779 CHECK("captureStderr") type = pref_bool, b = p->capture_stderr_p;
780 CHECK("captureStdout") continue; /* don't save */
781 CHECK("font") type = pref_str, s = stderr_font;
783 CHECK("dpmsEnabled") type = pref_bool, b = p->dpms_enabled_p;
784 CHECK("dpmsStandby") type = pref_time, t = p->dpms_standby;
785 CHECK("dpmsSuspend") type = pref_time, t = p->dpms_suspend;
786 CHECK("dpmsOff") type = pref_time, t = p->dpms_off;
788 CHECK("grabDesktopImages") type =pref_bool, b = p->grab_desktop_p;
789 CHECK("grabVideoFrames") type =pref_bool, b = p->grab_video_p;
790 CHECK("chooseRandomImages")type =pref_bool, b = p->random_image_p;
791 CHECK("imageDirectory") type =pref_str, s = p->image_directory;
793 CHECK("mode") type = pref_str,
794 s = (p->mode == ONE_HACK ? "one" :
795 p->mode == BLANK_ONLY ? "blank" :
796 p->mode == DONT_BLANK ? "off" :
798 CHECK("selected") type = pref_int, i = p->selected_hack;
800 CHECK("programs") type = pref_str, s = programs;
801 CHECK("pointerPollTime") type = pref_time, t = p->pointer_timeout;
802 CHECK("windowCreationTimeout")type=pref_time,t= p->notice_events_timeout;
803 CHECK("initialDelay") type = pref_time, t = p->initial_delay;
804 CHECK("sgiSaverExtension")type = pref_bool, b=p->use_sgi_saver_extension;
805 CHECK("mitSaverExtension")type = pref_bool, b=p->use_mit_saver_extension;
806 CHECK("xidleExtension") type = pref_bool, b = p->use_xidle_extension;
807 CHECK("procInterrupts") type = pref_bool, b = p->use_proc_interrupts;
808 CHECK("overlayStderr") type = pref_bool, b = overlay_stderr_p;
809 CHECK("overlayTextBackground") continue; /* don't save */
810 CHECK("overlayTextForeground") continue; /* don't save */
811 CHECK("bourneShell") continue;
820 sprintf(buf, "%d", i);
824 s = b ? "True" : "False";
828 unsigned int hour = 0, min = 0, sec = (unsigned int) (t/1000);
839 sprintf (buf, "%u:%02u:%02u", hour, min, sec);
845 if (i >= (1<<30) && i == ((i >> 30) << 30))
846 sprintf(buf, "%dG", i >> 30);
847 else if (i >= (1<<20) && i == ((i >> 20) << 20))
848 sprintf(buf, "%dM", i >> 20);
849 else if (i >= (1<<10) && i == ((i >> 10) << 10))
850 sprintf(buf, "%dK", i >> 10);
852 sprintf(buf, "%d", i);
861 if (pr && !strcmp(pr, "mode")) fprintf(out, "\n");
863 write_entry (out, pr, s);
868 if (visual_name) free(visual_name);
869 if (stderr_font) free(stderr_font);
870 if (programs) free(programs);
872 if (fclose(out) == 0)
874 time_t write_date = 0;
876 if (stat(tmp_name, &st) == 0)
878 write_date = st.st_mtime;
882 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
883 sprintf(buf, "%s: couldn't stat \"%s\"", blurb(), tmp_name);
890 if (rename (tmp_name, name) != 0)
892 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
893 sprintf(buf, "%s: error renaming \"%s\" to \"%s\"",
894 blurb(), tmp_name, name);
902 p->init_file_date = write_date;
904 /* Since the .xscreensaver file is used for IPC, let's try and make
905 sure that the bits actually land on the disk right away. */
908 status = 0; /* wrote and renamed successfully! */
913 char *buf = (char *) malloc(1024 + strlen(name));
914 sprintf(buf, "%s: error closing \"%s\"", blurb(), name);
927 /* Parsing the resource database
931 free_screenhack (screenhack *hack)
933 if (hack->visual) free (hack->visual);
934 if (hack->name) free (hack->name);
935 free (hack->command);
936 memset (hack, 0, sizeof(*hack));
941 free_screenhack_list (screenhack **list, int count)
945 for (i = 0; i < count; i++)
947 free_screenhack (list[i]);
953 /* Populate `saver_preferences' with the contents of the resource database.
954 Note that this may be called multiple times -- it is re-run each time
955 the ~/.xscreensaver file is reloaded.
957 This function can be very noisy, since it issues resource syntax errors
961 load_init_file (saver_preferences *p)
963 static Bool first_time = True;
965 screenhack **system_default_screenhacks = 0;
966 int system_default_screenhack_count = 0;
970 /* Get the programs resource before the .xscreensaver file has been
971 parsed and merged into the resource database for the first time:
972 this is the value of *programs from the app-defaults file.
973 Then clear it out so that it will be parsed again later, after
974 the init file has been read.
977 system_default_screenhacks = p->screenhacks;
978 system_default_screenhack_count = p->screenhacks_count;
980 p->screenhacks_count = 0;
983 if (parse_init_file (p) != 0) /* file might have gone away */
984 if (!first_time) return;
988 p->xsync_p = get_boolean_resource ("synchronous", "Synchronous");
989 p->verbose_p = get_boolean_resource ("verbose", "Boolean");
990 p->timestamp_p = get_boolean_resource ("timestamp", "Boolean");
991 p->lock_p = get_boolean_resource ("lock", "Boolean");
992 p->lock_vt_p = get_boolean_resource ("lockVTs", "Boolean");
993 p->fade_p = get_boolean_resource ("fade", "Boolean");
994 p->unfade_p = get_boolean_resource ("unfade", "Boolean");
995 p->fade_seconds = 1000 * get_seconds_resource ("fadeSeconds", "Time");
996 p->fade_ticks = get_integer_resource ("fadeTicks", "Integer");
997 p->install_cmap_p = get_boolean_resource ("installColormap", "Boolean");
998 p->nice_inferior = get_integer_resource ("nice", "Nice");
999 p->inferior_memory_limit = get_byte_resource ("memoryLimit", "MemoryLimit");
1000 p->splash_p = get_boolean_resource ("splash", "Boolean");
1001 p->capture_stderr_p = get_boolean_resource ("captureStderr", "Boolean");
1003 p->initial_delay = 1000 * get_seconds_resource ("initialDelay", "Time");
1004 p->splash_duration = 1000 * get_seconds_resource ("splashDuration", "Time");
1005 p->timeout = 1000 * get_minutes_resource ("timeout", "Time");
1006 p->lock_timeout = 1000 * get_minutes_resource ("lockTimeout", "Time");
1007 p->cycle = 1000 * get_minutes_resource ("cycle", "Time");
1008 p->passwd_timeout = 1000 * get_seconds_resource ("passwdTimeout", "Time");
1009 p->pointer_timeout = 1000 * get_seconds_resource ("pointerPollTime", "Time");
1010 p->notice_events_timeout = 1000*get_seconds_resource("windowCreationTimeout",
1013 p->dpms_enabled_p = get_boolean_resource ("dpmsEnabled", "Boolean");
1014 p->dpms_standby = 1000 * get_minutes_resource ("dpmsStandby", "Time");
1015 p->dpms_suspend = 1000 * get_minutes_resource ("dpmsSuspend", "Time");
1016 p->dpms_off = 1000 * get_minutes_resource ("dpmsOff", "Time");
1018 p->grab_desktop_p = get_boolean_resource ("grabDesktopImages", "Boolean");
1019 p->grab_video_p = get_boolean_resource ("grabVideoFrames", "Boolean");
1020 p->random_image_p = get_boolean_resource ("chooseRandomImages", "Boolean");
1021 p->image_directory = get_string_resource ("imageDirectory",
1024 p->shell = get_string_resource ("bourneShell", "BourneShell");
1026 p->demo_command = get_string_resource("demoCommand", "URL");
1027 p->prefs_command = get_string_resource("prefsCommand", "URL");
1028 p->help_url = get_string_resource("helpURL", "URL");
1029 p->load_url_command = get_string_resource("loadURL", "LoadURL");
1032 /* If "*splash" is unset, default to true. */
1034 char *s = get_string_resource ("splash", "Boolean");
1041 /* If "*grabDesktopImages" is unset, default to true. */
1043 char *s = get_string_resource ("grabDesktopImages", "Boolean");
1047 p->grab_desktop_p = True;
1050 p->use_xidle_extension = get_boolean_resource ("xidleExtension","Boolean");
1051 p->use_mit_saver_extension = get_boolean_resource ("mitSaverExtension",
1053 p->use_sgi_saver_extension = get_boolean_resource ("sgiSaverExtension",
1055 p->use_proc_interrupts = get_boolean_resource ("procInterrupts", "Boolean");
1057 get_screenhacks (p); /* Parse the "programs" resource. */
1059 p->selected_hack = get_integer_resource ("selected", "Integer");
1060 if (p->selected_hack < 0 || p->selected_hack >= p->screenhacks_count)
1061 p->selected_hack = -1;
1064 char *s = get_string_resource ("mode", "Mode");
1065 if (s && !strcasecmp (s, "one")) p->mode = ONE_HACK;
1066 else if (s && !strcasecmp (s, "blank")) p->mode = BLANK_ONLY;
1067 else if (s && !strcasecmp (s, "off")) p->mode = DONT_BLANK;
1068 else p->mode = RANDOM_HACKS;
1072 if (system_default_screenhack_count) /* note: first_time is also true */
1074 merge_system_screenhacks (p, system_default_screenhacks,
1075 system_default_screenhack_count);
1076 free_screenhack_list (system_default_screenhacks,
1077 system_default_screenhack_count);
1078 system_default_screenhacks = 0;
1079 system_default_screenhack_count = 0;
1085 p->verbose_p = True;
1086 p->timestamp_p = True;
1087 p->initial_delay = 0;
1090 /* Throttle the various timeouts to reasonable values after reading the
1092 stop_the_insanity (p);
1096 /* If there are any hacks in the system-wide defaults that are not in
1097 the ~/.xscreensaver file, add the new ones to the end of the list.
1098 This does *not* actually save the file.
1101 merge_system_screenhacks (saver_preferences *p,
1102 screenhack **system_list, int system_count)
1104 /* Yeah yeah, this is an N^2 operation, but I don't have hashtables handy,
1109 for (i = 0; i < system_count; i++)
1112 Bool matched_p = False;
1114 for (j = 0; j < p->screenhacks_count; j++)
1117 if (!system_list[i]->name)
1118 system_list[i]->name = make_hack_name (system_list[i]->command);
1120 name = p->screenhacks[j]->name;
1122 name = make_hack_name (p->screenhacks[j]->command);
1124 matched_p = !strcasecmp (name, system_list[i]->name);
1126 if (name != p->screenhacks[j]->name)
1135 /* We have an entry in the system-wide list that is not in the
1136 user's .xscreensaver file. Add it to the end.
1137 Note that p->screenhacks is a single malloc block, not a
1138 linked list, so we have to realloc it.
1140 screenhack *oh = system_list[i];
1141 screenhack *nh = (screenhack *) malloc (sizeof(screenhack));
1143 if (made_space == 0)
1146 p->screenhacks = (screenhack **)
1147 realloc (p->screenhacks,
1148 (p->screenhacks_count + made_space + 1)
1149 * sizeof(screenhack));
1150 if (!p->screenhacks) abort();
1153 nh->enabled_p = oh->enabled_p;
1154 nh->visual = oh->visual ? strdup(oh->visual) : 0;
1155 nh->name = oh->name ? strdup(oh->name) : 0;
1156 nh->command = oh->command ? strdup(oh->command) : 0;
1158 p->screenhacks[p->screenhacks_count++] = nh;
1159 p->screenhacks[p->screenhacks_count] = 0;
1163 fprintf (stderr, "%s: noticed new hack: %s\n", blurb(),
1164 (nh->name ? nh->name : make_hack_name (nh->command)));
1172 /* Parsing the programs resource.
1176 parse_screenhack (const char *line)
1178 screenhack *h = (screenhack *) calloc (1, sizeof(*h));
1181 h->enabled_p = True;
1183 while (isspace(*line)) line++; /* skip whitespace */
1184 if (*line == '-') /* handle "-" */
1186 h->enabled_p = False;
1188 while (isspace(*line)) line++; /* skip whitespace */
1191 s = line; /* handle "visual:" */
1192 while (*line && *line != ':' && *line != '"' && !isspace(*line))
1198 h->visual = (char *) malloc (line-s+1);
1199 strncpy (h->visual, s, line-s);
1200 h->visual[line-s] = 0;
1201 if (*line == ':') line++; /* skip ":" */
1202 while (isspace(*line)) line++; /* skip whitespace */
1205 if (*line == '"') /* handle "name" */
1209 while (*line && *line != '"')
1211 h->name = (char *) malloc (line-s+1);
1212 strncpy (h->name, s, line-s);
1213 h->name[line-s] = 0;
1214 if (*line == '"') line++; /* skip "\"" */
1215 while (isspace(*line)) line++; /* skip whitespace */
1218 h->command = format_command (line, False); /* handle command */
1224 format_command (const char *cmd, Bool wrap_p)
1228 char *cmd2 = (char *) calloc (1, 2 * (strlen (cmd) + 1));
1229 const char *in = cmd;
1233 /* shrink all whitespace to one space, for the benefit of the "demo"
1234 mode display. We only do this when we can easily tell that the
1235 whitespace is not significant (no shell metachars).
1239 case '\'': case '"': case '`': case '\\':
1240 /* Metachars are scary. Copy the rest of the line unchanged. */
1242 *out++ = *in++, col++;
1245 case ' ': case '\t':
1246 /* Squeeze all other whitespace down to one space. */
1247 while (*in == ' ' || *in == '\t')
1249 *out++ = ' ', col++;
1253 /* Copy other chars unchanged. */
1254 *out++ = *in++, col++;
1261 /* Strip trailing whitespace */
1262 while (out > cmd2 && isspace (out[-1]))
1269 /* Returns a new string describing the shell command.
1270 This may be just the name of the program, capitalized.
1271 It also may be something from the resource database (gotten
1272 by looking for "hacks.XYZ.name", where XYZ is the program.)
1275 make_hack_name (const char *shell_command)
1277 char *s = strdup (shell_command);
1281 for (s2 = s; *s2; s2++) /* truncate at first whitespace */
1288 s2 = strrchr (s, '/'); /* if pathname, take last component */
1296 if (strlen (s) > 50) /* 51 is hereby defined as "unreasonable" */
1299 sprintf (res_name, "hacks.%s.name", s); /* resource? */
1300 s2 = get_string_resource (res_name, res_name);
1307 for (s2 = s; *s2; s2++) /* if it has any capitals, return it */
1308 if (*s2 >= 'A' && *s2 <= 'Z')
1311 if (s[0] >= 'a' && s[0] <= 'z') /* else cap it */
1313 if (s[0] == 'X' && s[1] >= 'a' && s[1] <= 'z') /* (magic leading X) */
1320 format_hack (screenhack *hack, Bool wrap_p)
1327 char *def_name = make_hack_name (hack->command);
1329 /* Don't ever write out a name for a hack if it's the same as the default.
1331 if (hack->name && !strcmp (hack->name, def_name))
1338 size = (2 * (strlen(hack->command) +
1339 (hack->visual ? strlen(hack->visual) : 0) +
1340 (hack->name ? strlen(hack->name) : 0) +
1342 h2 = (char *) malloc (size);
1345 if (!hack->enabled_p) *out++ = '-'; /* write disabled flag */
1347 if (hack->visual && *hack->visual) /* write visual name */
1349 if (hack->enabled_p) *out++ = ' ';
1351 strcpy (out, hack->visual);
1352 out += strlen (hack->visual);
1358 col = string_columns (h2, strlen (h2), 0);
1360 if (hack->name && *hack->name) /* write pretty name */
1362 int L = (strlen (hack->name) + 2);
1364 out = stab_to (out, col, tab - L - 2);
1368 strcpy (out, hack->name);
1369 out += strlen (hack->name);
1373 col = string_columns (h2, strlen (h2), 0);
1374 if (wrap_p && col >= tab)
1375 out = stab_to (out, col, 77);
1379 if (out >= h2+size) abort();
1383 col = string_columns (h2, strlen (h2), 0);
1384 out = stab_to (out, col, tab); /* indent */
1386 if (out >= h2+size) abort();
1387 s = format_command (hack->command, wrap_p);
1398 get_screenhacks (saver_preferences *p)
1406 d = get_string_resource ("monoPrograms", "MonoPrograms");
1407 if (d && !*d) { free(d); d = 0; }
1409 d = get_string_resource ("colorPrograms", "ColorPrograms");
1410 if (d && !*d) { free(d); d = 0; }
1415 "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
1416 see the manual for details.\n", blurb());
1420 d = get_string_resource ("programs", "Programs");
1422 free_screenhack_list (p->screenhacks, p->screenhacks_count);
1424 p->screenhacks_count = 0;
1432 /* Count up the number of newlines (which will be equal to or larger than
1433 one less than the number of hacks.)
1435 for (i = j = 0; d[i]; i++)
1440 p->screenhacks = (screenhack **) calloc (j + 1, sizeof (screenhack *));
1442 /* Iterate over the lines in `d' (the string with newlines)
1443 and make new strings to stuff into the `screenhacks' array.
1445 p->screenhacks_count = 0;
1446 while (start < size)
1448 /* skip forward over whitespace. */
1449 while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n')
1452 /* skip forward to newline or end of string. */
1454 while (d[end] != 0 && d[end] != '\n')
1457 /* null terminate. */
1460 p->screenhacks[p->screenhacks_count++] = parse_screenhack (d + start);
1461 if (p->screenhacks_count >= i)
1469 if (p->screenhacks_count == 0)
1471 free (p->screenhacks);
1477 /* Make sure all the values in the preferences struct are sane.
1480 stop_the_insanity (saver_preferences *p)
1482 if (p->passwd_timeout <= 0) p->passwd_timeout = 30000; /* 30 secs */
1483 if (p->timeout < 15000) p->timeout = 15000; /* 15 secs */
1484 if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */
1485 if (p->pointer_timeout <= 0) p->pointer_timeout = 5000; /* 5 secs */
1486 if (p->notice_events_timeout <= 0)
1487 p->notice_events_timeout = 10000; /* 10 secs */
1488 if (p->fade_seconds <= 0 || p->fade_ticks <= 0)
1490 if (! p->fade_p) p->unfade_p = False;
1492 /* The DPMS settings may have the value 0.
1493 But if they are negative, or are a range less than 10 seconds,
1494 reset them to sensible defaults. (Since that must be a mistake.)
1496 if (p->dpms_standby != 0 &&
1497 p->dpms_standby < 10 * 1000)
1498 p->dpms_standby = 2 * 60 * 60 * 1000; /* 2 hours */
1499 if (p->dpms_suspend != 0 &&
1500 p->dpms_suspend < 10 * 1000)
1501 p->dpms_suspend = 2 * 60 * 60 * 1000; /* 2 hours */
1502 if (p->dpms_off != 0 &&
1503 p->dpms_off < 10 * 1000)
1504 p->dpms_off = 4 * 60 * 60 * 1000; /* 4 hours */
1506 if (p->dpms_standby == 0 && /* if *all* are 0, then DPMS is disabled */
1507 p->dpms_suspend == 0 &&
1509 p->dpms_enabled_p = False;
1511 p->watchdog_timeout = p->cycle * 0.6;
1512 if (p->watchdog_timeout < 30000) p->watchdog_timeout = 30000; /* 30 secs */
1513 if (p->watchdog_timeout > 3600000) p->watchdog_timeout = 3600000; /* 1 hr */