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
30 #include <X11/Xresource.h>
39 /* This file doesn't need the Xt headers, so stub these types out... */
41 #define XtAppContext void*
42 #define XtIntervalId void*
43 #define XtPointer void*
47 /* Just in case there's something pathological about stat.h... */
49 # define S_IRUSR 00400
52 # define S_IWUSR 00200
55 # define S_IXUSR 00100
58 # define S_IXGRP 00010
61 # define S_IXOTH 00001
66 #include "resources.h"
69 extern char *progname;
70 extern char *progclass;
71 extern const char *blurb (void);
75 static void get_screenhacks (saver_preferences *p);
76 static char *format_command (const char *cmd, Bool wrap_p);
77 static void merge_system_screenhacks (saver_preferences *p,
78 screenhack **system_list, int count);
81 chase_symlinks (const char *file)
87 if (realpath (file, buf))
90 sprintf (buf, "%s: realpath", blurb());
93 # endif /* HAVE_REALPATH */
99 i_am_a_nobody (uid_t uid)
103 p = getpwnam ("nobody");
104 if (! p) p = getpwnam ("noaccess");
105 if (! p) p = getpwnam ("daemon");
107 if (! p) /* There is no nobody? */
110 return (uid == p->pw_uid);
115 init_file_name (void)
117 static char *file = 0;
121 uid_t uid = getuid ();
122 struct passwd *p = getpwuid (uid);
124 if (i_am_a_nobody (uid))
125 /* If we're running as nobody, then use root's .xscreensaver file
126 (since ~root/.xscreensaver and ~nobody/.xscreensaver are likely
127 to be different -- if we didn't do this, then xscreensaver-demo
128 would appear to have no effect when the luser is running as root.)
134 if (!p || !p->pw_name || !*p->pw_name)
136 fprintf (stderr, "%s: couldn't get user info of uid %d\n",
140 else if (!p->pw_dir || !*p->pw_dir)
142 fprintf (stderr, "%s: couldn't get home directory of \"%s\"\n",
143 blurb(), (p->pw_name ? p->pw_name : "???"));
148 const char *home = p->pw_dir;
149 const char *name = ".xscreensaver";
150 file = (char *) malloc(strlen(home) + strlen(name) + 2);
152 if (!*home || home[strlen(home)-1] != '/')
166 init_file_tmp_name (void)
168 static char *file = 0;
171 const char *name = init_file_name();
172 const char *suffix = ".tmp";
174 char *n2 = chase_symlinks (name);
181 file = (char *) malloc(strlen(name) + strlen(suffix) + 2);
183 strcat(file, suffix);
196 get_byte_resource (char *name, char *class)
198 char *s = get_string_resource (name, class);
203 while (isspace(*s2)) s2++;
204 while (*s2 >= '0' && *s2 <= '9')
206 n = (n * 10) + (*s2 - '0');
209 while (isspace(*s2)) s2++;
210 if (*s2 == 'k' || *s2 == 'K') n <<= 10;
211 else if (*s2 == 'm' || *s2 == 'M') n <<= 20;
212 else if (*s2 == 'g' || *s2 == 'G') n <<= 30;
216 fprintf (stderr, "%s: %s must be a number of bytes, not \"%s\".\n",
221 if (*s2 == 'b' || *s2 == 'B') s2++;
222 while (isspace(*s2)) s2++;
229 static const char * const prefs[] = {
253 "captureStdout", /* not saved -- obsolete */
261 "chooseRandomImages",
267 "windowCreationTimeout",
274 "overlayTextBackground", /* not saved -- X resources only */
275 "overlayTextForeground", /* not saved -- X resources only */
276 "bourneShell", /* not saved -- X resources only */
284 while (*s == '\t' || *s == ' ' || *s == '\r' || *s == '\n')
286 for (s2 = s; *s2; s2++)
288 for (s2--; s2 >= s; s2--)
289 if (*s2 == '\t' || *s2 == ' ' || *s2 == '\r' || *s2 =='\n')
301 handle_entry (XrmDatabase *db, const char *key, const char *value,
302 const char *filename, int line)
305 for (i = 0; prefs[i]; i++)
306 if (*prefs[i] && !strcasecmp(key, prefs[i]))
308 char *val = strdup(value);
309 char *spec = (char *) malloc(strlen(progclass) + strlen(prefs[i]) +10);
310 strcpy(spec, progclass);
312 strcat(spec, prefs[i]);
314 XrmPutStringResource (db, spec, val);
321 fprintf(stderr, "%s: %s:%d: unknown option \"%s\"\n",
322 blurb(), filename, line, key);
328 parse_init_file (saver_preferences *p)
330 time_t write_date = 0;
331 const char *name = init_file_name();
340 if (stat(name, &st) != 0)
342 p->init_file_date = 0;
346 in = fopen(name, "r");
349 char *buf = (char *) malloc(1024 + strlen(name));
350 sprintf(buf, "%s: error reading \"%s\"", blurb(), name);
356 if (fstat (fileno(in), &st) == 0)
358 write_date = st.st_mtime;
362 char *buf = (char *) malloc(1024 + strlen(name));
363 sprintf(buf, "%s: couldn't re-stat \"%s\"", blurb(), name);
369 buf = (char *) malloc(buf_size);
371 while (fgets (buf, buf_size-1, in))
378 (buf[L-1] != '\n' || /* whole line didn't fit in buffer */
379 buf[L-2] == '\\')) /* or line ended with backslash */
381 if (buf[L-2] == '\\') /* backslash-newline gets swallowed */
387 buf = (char *) realloc(buf, buf_size);
391 if (!fgets (buf + L, buf_size-L-1, in))
396 /* Now handle other backslash escapes. */
399 for (i = 0; buf[i]; i++)
404 case 'n': buf[i] = '\n'; break;
405 case 'r': buf[i] = '\r'; break;
406 case 't': buf[i] = '\t'; break;
407 default: buf[i] = buf[i+1]; break;
409 for (j = i+2; buf[j]; j++)
417 if (*key == '#' || *key == '!' || *key == ';' ||
418 *key == '\n' || *key == 0)
421 value = strchr (key, ':');
424 fprintf(stderr, "%s: %s:%d: unparsable line: %s\n", blurb(),
431 value = strip(value);
435 handle_entry (&p->db, key, value, name, line);
440 p->init_file_date = write_date;
446 init_file_changed_p (saver_preferences *p)
448 const char *name = init_file_name();
451 if (!name) return False;
453 if (stat(name, &st) != 0)
456 if (p->init_file_date == st.st_mtime)
467 tab_to (FILE *out, int from, int to)
470 int to_mod = (to / tab_width) * tab_width;
471 while (from < to_mod)
474 from = (((from / tab_width) + 1) * tab_width);
485 stab_to (char *out, int from, int to)
488 int to_mod = (to / tab_width) * tab_width;
489 while (from < to_mod)
492 from = (((from / tab_width) + 1) * tab_width);
503 string_columns (const char *string, int length, int start)
507 const char *end = string + length;
512 else if (*string == '\t')
513 col = (((col / tab_width) + 1) * tab_width);
523 write_entry (FILE *out, const char *key, const char *value)
525 char *v = strdup(value ? value : "");
529 Bool programs_p = (!strcmp(key, "programs"));
530 int tab = (programs_p ? 32 : 16);
533 fprintf(out, "%s:", key);
534 col = strlen(key) + 1;
536 if (strlen(key) > 14)
537 col = tab_to (out, col, 20);
543 nl = strchr(v2, '\n');
547 if (first && programs_p)
549 col = tab_to (out, col, 77);
550 fprintf (out, " \\\n");
558 col = tab_to (out, col, 75);
559 fprintf (out, " \\n\\\n");
564 col = tab_to (out, col, tab);
567 string_columns(v2, strlen (v2), col) + col > 75)
574 while (v2[end] == ' ' || v2[end] == '\t')
576 while (v2[end] != ' ' && v2[end] != '\t' &&
577 v2[end] != '\n' && v2[end] != 0)
579 if (string_columns (v2 + start, (end - start), col) >= 74)
581 col = tab_to (out, col, 75);
582 fprintf(out, " \\\n");
583 col = tab_to (out, 0, tab + 2);
584 while (v2[start] == ' ' || v2[start] == '\t')
588 col = string_columns (v2 + start, (end - start), col);
590 fputc(v2[start++], out);
595 fprintf (out, "%s", v2);
596 col += string_columns(v2, strlen (v2), col);
610 write_init_file (saver_preferences *p, const char *version_string,
614 const char *name = init_file_name();
615 const char *tmp_name = init_file_tmp_name();
616 char *n2 = chase_symlinks (name);
620 /* Kludge, since these aren't in the saver_preferences struct as strings...
624 Bool overlay_stderr_p;
633 fprintf (stderr, "%s: writing \"%s\".\n", blurb(), name);
636 out = fopen(tmp_name, "w");
639 char *buf = (char *) malloc(1024 + strlen(name));
640 sprintf(buf, "%s: error writing \"%s\"", blurb(), name);
646 /* Give the new .xscreensaver file the same permissions as the old one;
647 except ensure that it is readable and writable by owner, and not
648 executable. Extra hack: if we're running as root, make the file
649 be world-readable (so that the daemon, running as "nobody", will
650 still be able to read it.)
652 if (stat(name, &st) == 0)
654 mode_t mode = st.st_mode;
655 mode |= S_IRUSR | S_IWUSR; /* read/write by user */
656 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); /* executable by none */
658 if (getuid() == (uid_t) 0) /* read by group/other */
659 mode |= S_IRGRP | S_IROTH;
661 if (fchmod (fileno(out), mode) != 0)
663 char *buf = (char *) malloc(1024 + strlen(name));
664 sprintf (buf, "%s: error fchmodding \"%s\" to 0%o", blurb(),
665 tmp_name, (unsigned int) mode);
672 /* Kludge, since these aren't in the saver_preferences struct... */
673 visual_name = get_string_resource ("visualID", "VisualID");
675 overlay_stderr_p = get_boolean_resource ("overlayStderr", "Boolean");
676 stderr_font = get_string_resource ("font", "Font");
681 char **hack_strings = (char **)
682 calloc (p->screenhacks_count, sizeof(char *));
684 for (j = 0; j < p->screenhacks_count; j++)
686 hack_strings[j] = format_hack (p->screenhacks[j], True);
687 i += strlen (hack_strings[j]);
691 ss = programs = (char *) malloc(i + 10);
693 for (j = 0; j < p->screenhacks_count; j++)
695 strcat (ss, hack_strings[j]);
696 free (hack_strings[j]);
704 struct passwd *pw = getpwuid (getuid ());
705 char *whoami = (pw && pw->pw_name && *pw->pw_name
708 time_t now = time ((time_t *) 0);
709 char *timestr = (char *) ctime (&now);
710 char *nl = (char *) strchr (timestr, '\n');
713 "# %s Preferences File\n"
714 "# Written by %s %s for %s on %s.\n"
715 "# http://www.jwz.org/xscreensaver/\n"
717 progclass, progname, version_string, whoami, timestr);
720 for (j = 0; prefs[j]; j++)
723 const char *pr = prefs[j];
724 enum pref_type { pref_str, pref_int, pref_bool, pref_byte, pref_time
738 # define CHECK(X) else if (!strcmp(pr, X))
740 CHECK("timeout") type = pref_time, t = p->timeout;
741 CHECK("cycle") type = pref_time, t = p->cycle;
742 CHECK("lock") type = pref_bool, b = p->lock_p;
743 # if 0 /* #### not ready yet */
744 CHECK("lockVTs") type = pref_bool, b = p->lock_vt_p;
746 CHECK("lockVTs") continue; /* don't save */
748 CHECK("lockTimeout") type = pref_time, t = p->lock_timeout;
749 CHECK("passwdTimeout") type = pref_time, t = p->passwd_timeout;
750 CHECK("visualID") type = pref_str, s = visual_name;
751 CHECK("installColormap") type = pref_bool, b = p->install_cmap_p;
752 CHECK("verbose") type = pref_bool, b = p->verbose_p;
753 CHECK("timestamp") type = pref_bool, b = p->timestamp_p;
754 CHECK("splash") type = pref_bool, b = p->splash_p;
755 CHECK("splashDuration") type = pref_time, t = p->splash_duration;
756 CHECK("demoCommand") type = pref_str, s = p->demo_command;
757 CHECK("prefsCommand") type = pref_str, s = p->prefs_command;
758 CHECK("helpURL") type = pref_str, s = p->help_url;
759 CHECK("loadURL") type = pref_str, s = p->load_url_command;
760 CHECK("nice") type = pref_int, i = p->nice_inferior;
761 CHECK("memoryLimit") type = pref_byte, i = p->inferior_memory_limit;
762 CHECK("fade") type = pref_bool, b = p->fade_p;
763 CHECK("unfade") type = pref_bool, b = p->unfade_p;
764 CHECK("fadeSeconds") type = pref_time, t = p->fade_seconds;
765 CHECK("fadeTicks") type = pref_int, i = p->fade_ticks;
766 CHECK("captureStderr") type = pref_bool, b = p->capture_stderr_p;
767 CHECK("captureStdout") continue; /* don't save */
768 CHECK("font") type = pref_str, s = stderr_font;
770 CHECK("dpmsEnabled") type = pref_bool, b = p->dpms_enabled_p;
771 CHECK("dpmsStandby") type = pref_time, t = p->dpms_standby;
772 CHECK("dpmsSuspend") type = pref_time, t = p->dpms_suspend;
773 CHECK("dpmsOff") type = pref_time, t = p->dpms_off;
775 CHECK("grabDesktopImages") type =pref_bool, b = p->grab_desktop_p;
776 CHECK("grabVideoFrames") type =pref_bool, b = p->grab_video_p;
777 CHECK("chooseRandomImages")type =pref_bool, b = p->random_image_p;
778 CHECK("imageDirectory") type =pref_str, s = p->image_directory;
780 CHECK("programs") type = pref_str, s = programs;
781 CHECK("pointerPollTime") type = pref_time, t = p->pointer_timeout;
782 CHECK("windowCreationTimeout")type=pref_time,t= p->notice_events_timeout;
783 CHECK("initialDelay") type = pref_time, t = p->initial_delay;
784 CHECK("sgiSaverExtension")type = pref_bool, b=p->use_sgi_saver_extension;
785 CHECK("mitSaverExtension")type = pref_bool, b=p->use_mit_saver_extension;
786 CHECK("xidleExtension") type = pref_bool, b = p->use_xidle_extension;
787 CHECK("procInterrupts") type = pref_bool, b = p->use_proc_interrupts;
788 CHECK("overlayStderr") type = pref_bool, b = overlay_stderr_p;
789 CHECK("overlayTextBackground") continue; /* don't save */
790 CHECK("overlayTextForeground") continue; /* don't save */
791 CHECK("bourneShell") continue;
800 sprintf(buf, "%d", i);
804 s = b ? "True" : "False";
808 unsigned int hour = 0, min = 0, sec = (unsigned int) (t/1000);
819 sprintf (buf, "%u:%02u:%02u", hour, min, sec);
825 if (i >= (1<<30) && i == ((i >> 30) << 30))
826 sprintf(buf, "%dG", i >> 30);
827 else if (i >= (1<<20) && i == ((i >> 20) << 20))
828 sprintf(buf, "%dM", i >> 20);
829 else if (i >= (1<<10) && i == ((i >> 10) << 10))
830 sprintf(buf, "%dK", i >> 10);
832 sprintf(buf, "%d", i);
840 write_entry (out, pr, s);
845 if (visual_name) free(visual_name);
846 if (stderr_font) free(stderr_font);
847 if (programs) free(programs);
849 if (fclose(out) == 0)
851 time_t write_date = 0;
853 if (stat(tmp_name, &st) == 0)
855 write_date = st.st_mtime;
859 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
860 sprintf(buf, "%s: couldn't stat \"%s\"", blurb(), tmp_name);
867 if (rename (tmp_name, name) != 0)
869 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
870 sprintf(buf, "%s: error renaming \"%s\" to \"%s\"",
871 blurb(), tmp_name, name);
879 p->init_file_date = write_date;
881 /* Since the .xscreensaver file is used for IPC, let's try and make
882 sure that the bits actually land on the disk right away. */
885 status = 0; /* wrote and renamed successfully! */
890 char *buf = (char *) malloc(1024 + strlen(name));
891 sprintf(buf, "%s: error closing \"%s\"", blurb(), name);
904 /* Parsing the resource database
908 free_screenhack (screenhack *hack)
910 if (hack->visual) free (hack->visual);
911 if (hack->name) free (hack->name);
912 free (hack->command);
913 memset (hack, 0, sizeof(*hack));
918 free_screenhack_list (screenhack **list, int count)
922 for (i = 0; i < count; i++)
924 free_screenhack (list[i]);
929 /* Populate `saver_preferences' with the contents of the resource database.
930 Note that this may be called multiple times -- it is re-run each time
931 the ~/.xscreensaver file is reloaded.
933 This function can be very noisy, since it issues resource syntax errors
937 load_init_file (saver_preferences *p)
939 static Bool first_time = True;
941 screenhack **system_default_screenhacks = 0;
942 int system_default_screenhack_count = 0;
946 /* Get the programs resource before the .xscreensaver file has been
947 parsed and merged into the resource database for the first time:
948 this is the value of *programs from the app-defaults file.
949 Then clear it out so that it will be parsed again later, after
950 the init file has been read.
953 system_default_screenhacks = p->screenhacks;
954 system_default_screenhack_count = p->screenhacks_count;
956 p->screenhacks_count = 0;
959 if (parse_init_file (p) != 0) /* file might have gone away */
960 if (!first_time) return;
964 p->xsync_p = get_boolean_resource ("synchronous", "Synchronous");
965 p->verbose_p = get_boolean_resource ("verbose", "Boolean");
966 p->timestamp_p = get_boolean_resource ("timestamp", "Boolean");
967 p->lock_p = get_boolean_resource ("lock", "Boolean");
968 p->lock_vt_p = get_boolean_resource ("lockVTs", "Boolean");
969 p->fade_p = get_boolean_resource ("fade", "Boolean");
970 p->unfade_p = get_boolean_resource ("unfade", "Boolean");
971 p->fade_seconds = 1000 * get_seconds_resource ("fadeSeconds", "Time");
972 p->fade_ticks = get_integer_resource ("fadeTicks", "Integer");
973 p->install_cmap_p = get_boolean_resource ("installColormap", "Boolean");
974 p->nice_inferior = get_integer_resource ("nice", "Nice");
975 p->inferior_memory_limit = get_byte_resource ("memoryLimit", "MemoryLimit");
976 p->splash_p = get_boolean_resource ("splash", "Boolean");
977 p->capture_stderr_p = get_boolean_resource ("captureStderr", "Boolean");
979 p->initial_delay = 1000 * get_seconds_resource ("initialDelay", "Time");
980 p->splash_duration = 1000 * get_seconds_resource ("splashDuration", "Time");
981 p->timeout = 1000 * get_minutes_resource ("timeout", "Time");
982 p->lock_timeout = 1000 * get_minutes_resource ("lockTimeout", "Time");
983 p->cycle = 1000 * get_minutes_resource ("cycle", "Time");
984 p->passwd_timeout = 1000 * get_seconds_resource ("passwdTimeout", "Time");
985 p->pointer_timeout = 1000 * get_seconds_resource ("pointerPollTime", "Time");
986 p->notice_events_timeout = 1000*get_seconds_resource("windowCreationTimeout",
989 p->dpms_enabled_p = get_boolean_resource ("dpmsEnabled", "Boolean");
990 p->dpms_standby = 1000 * get_minutes_resource ("dpmsStandby", "Time");
991 p->dpms_suspend = 1000 * get_minutes_resource ("dpmsSuspend", "Time");
992 p->dpms_off = 1000 * get_minutes_resource ("dpmsOff", "Time");
994 p->grab_desktop_p = get_boolean_resource ("grabDesktopImages", "Boolean");
995 p->grab_video_p = get_boolean_resource ("grabVideoFrames", "Boolean");
996 p->random_image_p = get_boolean_resource ("chooseRandomImages", "Boolean");
997 p->image_directory = get_string_resource ("imageDirectory",
1000 p->shell = get_string_resource ("bourneShell", "BourneShell");
1002 p->demo_command = get_string_resource("demoCommand", "URL");
1003 p->prefs_command = get_string_resource("prefsCommand", "URL");
1004 p->help_url = get_string_resource("helpURL", "URL");
1005 p->load_url_command = get_string_resource("loadURL", "LoadURL");
1008 /* If "*splash" is unset, default to true. */
1010 char *s = get_string_resource ("splash", "Boolean");
1017 /* If "*grabDesktopImages" is unset, default to true. */
1019 char *s = get_string_resource ("grabDesktopImages", "Boolean");
1023 p->grab_desktop_p = True;
1026 p->use_xidle_extension = get_boolean_resource ("xidleExtension","Boolean");
1027 p->use_mit_saver_extension = get_boolean_resource ("mitSaverExtension",
1029 p->use_sgi_saver_extension = get_boolean_resource ("sgiSaverExtension",
1031 p->use_proc_interrupts = get_boolean_resource ("procInterrupts", "Boolean");
1033 /* Throttle the various timeouts to reasonable values.
1035 if (p->passwd_timeout <= 0) p->passwd_timeout = 30000; /* 30 secs */
1036 if (p->timeout < 10000) p->timeout = 10000; /* 10 secs */
1037 if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */
1038 if (p->pointer_timeout <= 0) p->pointer_timeout = 5000; /* 5 secs */
1039 if (p->notice_events_timeout <= 0)
1040 p->notice_events_timeout = 10000; /* 10 secs */
1041 if (p->fade_seconds <= 0 || p->fade_ticks <= 0)
1043 if (! p->fade_p) p->unfade_p = False;
1045 /* The DPMS settings may have the value 0.
1046 But if they are negative, or are a range less than 10 seconds,
1047 reset them to sensible defaults. (Since that must be a mistake.)
1049 if (p->dpms_standby != 0 &&
1050 p->dpms_standby < 10 * 1000)
1051 p->dpms_standby = 2 * 60 * 60 * 1000; /* 2 hours */
1052 if (p->dpms_suspend != 0 &&
1053 p->dpms_suspend < 10 * 1000)
1054 p->dpms_suspend = 2 * 60 * 60 * 1000; /* 2 hours */
1055 if (p->dpms_off != 0 &&
1056 p->dpms_off < 10 * 1000)
1057 p->dpms_off = 4 * 60 * 60 * 1000; /* 4 hours */
1059 if (p->dpms_standby == 0 && /* if *all* are 0, then DPMS is disabled */
1060 p->dpms_suspend == 0 &&
1062 p->dpms_enabled_p = False;
1065 p->watchdog_timeout = p->cycle * 0.6;
1066 if (p->watchdog_timeout < 30000) p->watchdog_timeout = 30000; /* 30 secs */
1067 if (p->watchdog_timeout > 3600000) p->watchdog_timeout = 3600000; /* 1 hr */
1069 get_screenhacks (p);
1071 if (system_default_screenhack_count) /* note: first_time is also true */
1073 merge_system_screenhacks (p, system_default_screenhacks,
1074 system_default_screenhack_count);
1075 free_screenhack_list (system_default_screenhacks,
1076 system_default_screenhack_count);
1077 system_default_screenhacks = 0;
1078 system_default_screenhack_count = 0;
1084 p->verbose_p = True;
1085 p->timestamp_p = True;
1086 p->initial_delay = 0;
1091 /* If there are any hacks in the system-wide defaults that are not in
1092 the ~/.xscreensaver file, add the new ones to the end of the list.
1093 This does *not* actually save the file.
1096 merge_system_screenhacks (saver_preferences *p,
1097 screenhack **system_list, int system_count)
1099 /* Yeah yeah, this is an N^2 operation, but I don't have hashtables handy,
1104 for (i = 0; i < system_count; i++)
1107 Bool matched_p = False;
1109 for (j = 0; j < p->screenhacks_count; j++)
1112 if (!system_list[i]->name)
1113 system_list[i]->name = make_hack_name (system_list[i]->command);
1115 name = p->screenhacks[j]->name;
1117 name = make_hack_name (p->screenhacks[j]->command);
1119 matched_p = !strcasecmp (name, system_list[i]->name);
1121 if (name != p->screenhacks[j]->name)
1130 /* We have an entry in the system-wide list that is not in the
1131 user's .xscreensaver file. Add it to the end.
1132 Note that p->screenhacks is a single malloc block, not a
1133 linked list, so we have to realloc it.
1135 screenhack *oh = system_list[i];
1136 screenhack *nh = (screenhack *) malloc (sizeof(screenhack));
1138 if (made_space == 0)
1141 p->screenhacks = (screenhack **)
1142 realloc (p->screenhacks,
1143 (p->screenhacks_count + made_space + 1)
1144 * sizeof(screenhack));
1145 if (!p->screenhacks) abort();
1148 nh->enabled_p = oh->enabled_p;
1149 nh->visual = oh->visual ? strdup(oh->visual) : 0;
1150 nh->name = oh->name ? strdup(oh->name) : 0;
1151 nh->command = oh->command ? strdup(oh->command) : 0;
1153 p->screenhacks[p->screenhacks_count++] = nh;
1154 p->screenhacks[p->screenhacks_count] = 0;
1158 fprintf (stderr, "%s: noticed new hack: %s\n", blurb(),
1159 (nh->name ? nh->name : make_hack_name (nh->command)));
1167 /* Parsing the programs resource.
1171 parse_screenhack (const char *line)
1173 screenhack *h = (screenhack *) calloc (1, sizeof(*h));
1176 h->enabled_p = True;
1178 while (isspace(*line)) line++; /* skip whitespace */
1179 if (*line == '-') /* handle "-" */
1181 h->enabled_p = False;
1183 while (isspace(*line)) line++; /* skip whitespace */
1186 s = line; /* handle "visual:" */
1187 while (*line && *line != ':' && *line != '"' && !isspace(*line))
1193 h->visual = (char *) malloc (line-s+1);
1194 strncpy (h->visual, s, line-s);
1195 h->visual[line-s] = 0;
1196 if (*line == ':') line++; /* skip ":" */
1197 while (isspace(*line)) line++; /* skip whitespace */
1200 if (*line == '"') /* handle "name" */
1204 while (*line && *line != '"')
1206 h->name = (char *) malloc (line-s+1);
1207 strncpy (h->name, s, line-s);
1208 h->name[line-s] = 0;
1209 if (*line == '"') line++; /* skip "\"" */
1210 while (isspace(*line)) line++; /* skip whitespace */
1213 h->command = format_command (line, False); /* handle command */
1219 format_command (const char *cmd, Bool wrap_p)
1223 char *cmd2 = (char *) calloc (1, 2 * (strlen (cmd) + 1));
1224 const char *in = cmd;
1228 /* shrink all whitespace to one space, for the benefit of the "demo"
1229 mode display. We only do this when we can easily tell that the
1230 whitespace is not significant (no shell metachars).
1234 case '\'': case '"': case '`': case '\\':
1235 /* Metachars are scary. Copy the rest of the line unchanged. */
1237 *out++ = *in++, col++;
1240 case ' ': case '\t':
1241 /* Squeeze all other whitespace down to one space. */
1242 while (*in == ' ' || *in == '\t')
1244 *out++ = ' ', col++;
1248 /* Copy other chars unchanged. */
1249 *out++ = *in++, col++;
1256 /* Strip trailing whitespace */
1257 while (out > cmd2 && isspace (out[-1]))
1264 /* Returns a new string describing the shell command.
1265 This may be just the name of the program, capitalized.
1266 It also may be something from the resource database (gotten
1267 by looking for "hacks.XYZ.name", where XYZ is the program.)
1270 make_hack_name (const char *shell_command)
1272 char *s = strdup (shell_command);
1276 for (s2 = s; *s2; s2++) /* truncate at first whitespace */
1283 s2 = strrchr (s, '/'); /* if pathname, take last component */
1291 if (strlen (s) > 50) /* 51 is hereby defined as "unreasonable" */
1294 sprintf (res_name, "hacks.%s.name", s); /* resource? */
1295 s2 = get_string_resource (res_name, res_name);
1299 for (s2 = s; *s2; s2++) /* if it has any capitals, return it */
1300 if (*s2 >= 'A' && *s2 <= 'Z')
1303 if (s[0] >= 'a' && s[0] <= 'z') /* else cap it */
1305 if (s[0] == 'X' && s[1] >= 'a' && s[1] <= 'z') /* (magic leading X) */
1312 format_hack (screenhack *hack, Bool wrap_p)
1315 int size = (2 * (strlen(hack->command) +
1316 (hack->visual ? strlen(hack->visual) : 0) +
1317 (hack->name ? strlen(hack->name) : 0) +
1319 char *h2 = (char *) malloc (size);
1324 if (!hack->enabled_p) *out++ = '-'; /* write disabled flag */
1326 if (hack->visual && *hack->visual) /* write visual name */
1328 if (hack->enabled_p) *out++ = ' ';
1330 strcpy (out, hack->visual);
1331 out += strlen (hack->visual);
1337 col = string_columns (h2, strlen (h2), 0);
1339 if (hack->name && *hack->name) /* write pretty name */
1341 int L = (strlen (hack->name) + 2);
1343 out = stab_to (out, col, tab - L - 2);
1347 strcpy (out, hack->name);
1348 out += strlen (hack->name);
1352 col = string_columns (h2, strlen (h2), 0);
1353 if (wrap_p && col >= tab)
1355 out = stab_to (out, col, 77);
1356 *out += strlen(out);
1361 if (out >= h2+size) abort();
1365 col = string_columns (h2, strlen (h2), 0);
1366 out = stab_to (out, col, tab); /* indent */
1368 if (out >= h2+size) abort();
1369 s = format_command (hack->command, wrap_p);
1380 get_screenhacks (saver_preferences *p)
1388 d = get_string_resource ("monoPrograms", "MonoPrograms");
1389 if (d && !*d) { free(d); d = 0; }
1391 d = get_string_resource ("colorPrograms", "ColorPrograms");
1392 if (d && !*d) { free(d); d = 0; }
1397 "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
1398 see the manual for details.\n", blurb());
1402 d = get_string_resource ("programs", "Programs");
1404 free_screenhack_list (p->screenhacks, p->screenhacks_count);
1406 p->screenhacks_count = 0;
1414 /* Count up the number of newlines (which will be equal to or larger than
1415 one less than the number of hacks.)
1417 for (i = j = 0; d[i]; i++)
1422 p->screenhacks = (screenhack **) calloc (j + 1, sizeof (screenhack *));
1424 /* Iterate over the lines in `d' (the string with newlines)
1425 and make new strings to stuff into the `screenhacks' array.
1427 p->screenhacks_count = 0;
1428 while (start < size)
1430 /* skip forward over whitespace. */
1431 while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n')
1434 /* skip forward to newline or end of string. */
1436 while (d[end] != 0 && d[end] != '\n')
1439 /* null terminate. */
1442 p->screenhacks[p->screenhacks_count++] = parse_screenhack (d + start);
1443 if (p->screenhacks_count >= i)
1449 if (p->screenhacks_count == 0)
1451 free (p->screenhacks);