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);
82 chase_symlinks (const char *file)
88 if (realpath (file, buf))
91 sprintf (buf, "%s: realpath", blurb());
94 # endif /* HAVE_REALPATH */
100 i_am_a_nobody (uid_t uid)
104 p = getpwnam ("nobody");
105 if (! p) p = getpwnam ("noaccess");
106 if (! p) p = getpwnam ("daemon");
108 if (! p) /* There is no nobody? */
111 return (uid == p->pw_uid);
116 init_file_name (void)
118 static char *file = 0;
122 uid_t uid = getuid ();
123 struct passwd *p = getpwuid (uid);
125 if (i_am_a_nobody (uid))
126 /* If we're running as nobody, then use root's .xscreensaver file
127 (since ~root/.xscreensaver and ~nobody/.xscreensaver are likely
128 to be different -- if we didn't do this, then xscreensaver-demo
129 would appear to have no effect when the luser is running as root.)
135 if (!p || !p->pw_name || !*p->pw_name)
137 fprintf (stderr, "%s: couldn't get user info of uid %d\n",
141 else if (!p->pw_dir || !*p->pw_dir)
143 fprintf (stderr, "%s: couldn't get home directory of \"%s\"\n",
144 blurb(), (p->pw_name ? p->pw_name : "???"));
149 const char *home = p->pw_dir;
150 const char *name = ".xscreensaver";
151 file = (char *) malloc(strlen(home) + strlen(name) + 2);
153 if (!*home || home[strlen(home)-1] != '/')
167 init_file_tmp_name (void)
169 static char *file = 0;
172 const char *name = init_file_name();
173 const char *suffix = ".tmp";
175 char *n2 = chase_symlinks (name);
182 file = (char *) malloc(strlen(name) + strlen(suffix) + 2);
184 strcat(file, suffix);
197 get_byte_resource (char *name, char *class)
199 char *s = get_string_resource (name, class);
204 while (isspace(*s2)) s2++;
205 while (*s2 >= '0' && *s2 <= '9')
207 n = (n * 10) + (*s2 - '0');
210 while (isspace(*s2)) s2++;
211 if (*s2 == 'k' || *s2 == 'K') n <<= 10;
212 else if (*s2 == 'm' || *s2 == 'M') n <<= 20;
213 else if (*s2 == 'g' || *s2 == 'G') n <<= 30;
217 fprintf (stderr, "%s: %s must be a number of bytes, not \"%s\".\n",
222 if (*s2 == 'b' || *s2 == 'B') s2++;
223 while (isspace(*s2)) s2++;
230 static const char * const prefs[] = {
254 "captureStdout", /* not saved -- obsolete */
262 "chooseRandomImages",
268 "windowCreationTimeout",
275 "overlayTextBackground", /* not saved -- X resources only */
276 "overlayTextForeground", /* not saved -- X resources only */
277 "bourneShell", /* not saved -- X resources only */
285 while (*s == '\t' || *s == ' ' || *s == '\r' || *s == '\n')
287 for (s2 = s; *s2; s2++)
289 for (s2--; s2 >= s; s2--)
290 if (*s2 == '\t' || *s2 == ' ' || *s2 == '\r' || *s2 =='\n')
302 handle_entry (XrmDatabase *db, const char *key, const char *value,
303 const char *filename, int line)
306 for (i = 0; prefs[i]; i++)
307 if (*prefs[i] && !strcasecmp(key, prefs[i]))
309 char *val = strdup(value);
310 char *spec = (char *) malloc(strlen(progclass) + strlen(prefs[i]) +10);
311 strcpy(spec, progclass);
313 strcat(spec, prefs[i]);
315 XrmPutStringResource (db, spec, val);
322 fprintf(stderr, "%s: %s:%d: unknown option \"%s\"\n",
323 blurb(), filename, line, key);
329 parse_init_file (saver_preferences *p)
331 time_t write_date = 0;
332 const char *name = init_file_name();
341 if (stat(name, &st) != 0)
343 p->init_file_date = 0;
347 in = fopen(name, "r");
350 char *buf = (char *) malloc(1024 + strlen(name));
351 sprintf(buf, "%s: error reading \"%s\"", blurb(), name);
357 if (fstat (fileno(in), &st) == 0)
359 write_date = st.st_mtime;
363 char *buf = (char *) malloc(1024 + strlen(name));
364 sprintf(buf, "%s: couldn't re-stat \"%s\"", blurb(), name);
370 buf = (char *) malloc(buf_size);
372 while (fgets (buf, buf_size-1, in))
379 (buf[L-1] != '\n' || /* whole line didn't fit in buffer */
380 buf[L-2] == '\\')) /* or line ended with backslash */
382 if (buf[L-2] == '\\') /* backslash-newline gets swallowed */
388 buf = (char *) realloc(buf, buf_size);
392 if (!fgets (buf + L, buf_size-L-1, in))
397 /* Now handle other backslash escapes. */
400 for (i = 0; buf[i]; i++)
405 case 'n': buf[i] = '\n'; break;
406 case 'r': buf[i] = '\r'; break;
407 case 't': buf[i] = '\t'; break;
408 default: buf[i] = buf[i+1]; break;
410 for (j = i+2; buf[j]; j++)
418 if (*key == '#' || *key == '!' || *key == ';' ||
419 *key == '\n' || *key == 0)
422 value = strchr (key, ':');
425 fprintf(stderr, "%s: %s:%d: unparsable line: %s\n", blurb(),
432 value = strip(value);
436 handle_entry (&p->db, key, value, name, line);
441 p->init_file_date = write_date;
447 init_file_changed_p (saver_preferences *p)
449 const char *name = init_file_name();
452 if (!name) return False;
454 if (stat(name, &st) != 0)
457 if (p->init_file_date == st.st_mtime)
468 tab_to (FILE *out, int from, int to)
471 int to_mod = (to / tab_width) * tab_width;
472 while (from < to_mod)
475 from = (((from / tab_width) + 1) * tab_width);
486 stab_to (char *out, int from, int to)
489 int to_mod = (to / tab_width) * tab_width;
490 while (from < to_mod)
493 from = (((from / tab_width) + 1) * tab_width);
504 string_columns (const char *string, int length, int start)
508 const char *end = string + length;
513 else if (*string == '\t')
514 col = (((col / tab_width) + 1) * tab_width);
524 write_entry (FILE *out, const char *key, const char *value)
526 char *v = strdup(value ? value : "");
530 Bool programs_p = (!strcmp(key, "programs"));
531 int tab = (programs_p ? 32 : 16);
534 fprintf(out, "%s:", key);
535 col = strlen(key) + 1;
537 if (strlen(key) > 14)
538 col = tab_to (out, col, 20);
544 nl = strchr(v2, '\n');
548 if (first && programs_p)
550 col = tab_to (out, col, 77);
551 fprintf (out, " \\\n");
559 col = tab_to (out, col, 75);
560 fprintf (out, " \\n\\\n");
565 col = tab_to (out, col, tab);
568 string_columns(v2, strlen (v2), col) + col > 75)
575 while (v2[end] == ' ' || v2[end] == '\t')
577 while (v2[end] != ' ' && v2[end] != '\t' &&
578 v2[end] != '\n' && v2[end] != 0)
580 if (string_columns (v2 + start, (end - start), col) >= 74)
582 col = tab_to (out, col, 75);
583 fprintf(out, " \\\n");
584 col = tab_to (out, 0, tab + 2);
585 while (v2[start] == ' ' || v2[start] == '\t')
589 col = string_columns (v2 + start, (end - start), col);
591 fputc(v2[start++], out);
596 fprintf (out, "%s", v2);
597 col += string_columns(v2, strlen (v2), col);
611 write_init_file (saver_preferences *p, const char *version_string,
615 const char *name = init_file_name();
616 const char *tmp_name = init_file_tmp_name();
617 char *n2 = chase_symlinks (name);
621 /* Kludge, since these aren't in the saver_preferences struct as strings...
625 Bool overlay_stderr_p;
634 fprintf (stderr, "%s: writing \"%s\".\n", blurb(), name);
637 out = fopen(tmp_name, "w");
640 char *buf = (char *) malloc(1024 + strlen(name));
641 sprintf(buf, "%s: error writing \"%s\"", blurb(), name);
647 /* Give the new .xscreensaver file the same permissions as the old one;
648 except ensure that it is readable and writable by owner, and not
649 executable. Extra hack: if we're running as root, make the file
650 be world-readable (so that the daemon, running as "nobody", will
651 still be able to read it.)
653 if (stat(name, &st) == 0)
655 mode_t mode = st.st_mode;
656 mode |= S_IRUSR | S_IWUSR; /* read/write by user */
657 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); /* executable by none */
659 if (getuid() == (uid_t) 0) /* read by group/other */
660 mode |= S_IRGRP | S_IROTH;
662 if (fchmod (fileno(out), mode) != 0)
664 char *buf = (char *) malloc(1024 + strlen(name));
665 sprintf (buf, "%s: error fchmodding \"%s\" to 0%o", blurb(),
666 tmp_name, (unsigned int) mode);
673 /* Kludge, since these aren't in the saver_preferences struct... */
674 visual_name = get_string_resource ("visualID", "VisualID");
676 overlay_stderr_p = get_boolean_resource ("overlayStderr", "Boolean");
677 stderr_font = get_string_resource ("font", "Font");
682 char **hack_strings = (char **)
683 calloc (p->screenhacks_count, sizeof(char *));
685 for (j = 0; j < p->screenhacks_count; j++)
687 hack_strings[j] = format_hack (p->screenhacks[j], True);
688 i += strlen (hack_strings[j]);
692 ss = programs = (char *) malloc(i + 10);
694 for (j = 0; j < p->screenhacks_count; j++)
696 strcat (ss, hack_strings[j]);
697 free (hack_strings[j]);
705 struct passwd *pw = getpwuid (getuid ());
706 char *whoami = (pw && pw->pw_name && *pw->pw_name
709 time_t now = time ((time_t *) 0);
710 char *timestr = (char *) ctime (&now);
711 char *nl = (char *) strchr (timestr, '\n');
714 "# %s Preferences File\n"
715 "# Written by %s %s for %s on %s.\n"
716 "# http://www.jwz.org/xscreensaver/\n"
718 progclass, progname, version_string, whoami, timestr);
721 for (j = 0; prefs[j]; j++)
724 const char *pr = prefs[j];
725 enum pref_type { pref_str, pref_int, pref_bool, pref_byte, pref_time
739 # define CHECK(X) else if (!strcmp(pr, X))
741 CHECK("timeout") type = pref_time, t = p->timeout;
742 CHECK("cycle") type = pref_time, t = p->cycle;
743 CHECK("lock") type = pref_bool, b = p->lock_p;
744 # if 0 /* #### not ready yet */
745 CHECK("lockVTs") type = pref_bool, b = p->lock_vt_p;
747 CHECK("lockVTs") continue; /* don't save */
749 CHECK("lockTimeout") type = pref_time, t = p->lock_timeout;
750 CHECK("passwdTimeout") type = pref_time, t = p->passwd_timeout;
751 CHECK("visualID") type = pref_str, s = visual_name;
752 CHECK("installColormap") type = pref_bool, b = p->install_cmap_p;
753 CHECK("verbose") type = pref_bool, b = p->verbose_p;
754 CHECK("timestamp") type = pref_bool, b = p->timestamp_p;
755 CHECK("splash") type = pref_bool, b = p->splash_p;
756 CHECK("splashDuration") type = pref_time, t = p->splash_duration;
757 CHECK("demoCommand") type = pref_str, s = p->demo_command;
758 CHECK("prefsCommand") type = pref_str, s = p->prefs_command;
759 CHECK("helpURL") type = pref_str, s = p->help_url;
760 CHECK("loadURL") type = pref_str, s = p->load_url_command;
761 CHECK("nice") type = pref_int, i = p->nice_inferior;
762 CHECK("memoryLimit") type = pref_byte, i = p->inferior_memory_limit;
763 CHECK("fade") type = pref_bool, b = p->fade_p;
764 CHECK("unfade") type = pref_bool, b = p->unfade_p;
765 CHECK("fadeSeconds") type = pref_time, t = p->fade_seconds;
766 CHECK("fadeTicks") type = pref_int, i = p->fade_ticks;
767 CHECK("captureStderr") type = pref_bool, b = p->capture_stderr_p;
768 CHECK("captureStdout") continue; /* don't save */
769 CHECK("font") type = pref_str, s = stderr_font;
771 CHECK("dpmsEnabled") type = pref_bool, b = p->dpms_enabled_p;
772 CHECK("dpmsStandby") type = pref_time, t = p->dpms_standby;
773 CHECK("dpmsSuspend") type = pref_time, t = p->dpms_suspend;
774 CHECK("dpmsOff") type = pref_time, t = p->dpms_off;
776 CHECK("grabDesktopImages") type =pref_bool, b = p->grab_desktop_p;
777 CHECK("grabVideoFrames") type =pref_bool, b = p->grab_video_p;
778 CHECK("chooseRandomImages")type =pref_bool, b = p->random_image_p;
779 CHECK("imageDirectory") type =pref_str, s = p->image_directory;
781 CHECK("programs") type = pref_str, s = programs;
782 CHECK("pointerPollTime") type = pref_time, t = p->pointer_timeout;
783 CHECK("windowCreationTimeout")type=pref_time,t= p->notice_events_timeout;
784 CHECK("initialDelay") type = pref_time, t = p->initial_delay;
785 CHECK("sgiSaverExtension")type = pref_bool, b=p->use_sgi_saver_extension;
786 CHECK("mitSaverExtension")type = pref_bool, b=p->use_mit_saver_extension;
787 CHECK("xidleExtension") type = pref_bool, b = p->use_xidle_extension;
788 CHECK("procInterrupts") type = pref_bool, b = p->use_proc_interrupts;
789 CHECK("overlayStderr") type = pref_bool, b = overlay_stderr_p;
790 CHECK("overlayTextBackground") continue; /* don't save */
791 CHECK("overlayTextForeground") continue; /* don't save */
792 CHECK("bourneShell") continue;
801 sprintf(buf, "%d", i);
805 s = b ? "True" : "False";
809 unsigned int hour = 0, min = 0, sec = (unsigned int) (t/1000);
820 sprintf (buf, "%u:%02u:%02u", hour, min, sec);
826 if (i >= (1<<30) && i == ((i >> 30) << 30))
827 sprintf(buf, "%dG", i >> 30);
828 else if (i >= (1<<20) && i == ((i >> 20) << 20))
829 sprintf(buf, "%dM", i >> 20);
830 else if (i >= (1<<10) && i == ((i >> 10) << 10))
831 sprintf(buf, "%dK", i >> 10);
833 sprintf(buf, "%d", i);
841 write_entry (out, pr, s);
846 if (visual_name) free(visual_name);
847 if (stderr_font) free(stderr_font);
848 if (programs) free(programs);
850 if (fclose(out) == 0)
852 time_t write_date = 0;
854 if (stat(tmp_name, &st) == 0)
856 write_date = st.st_mtime;
860 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
861 sprintf(buf, "%s: couldn't stat \"%s\"", blurb(), tmp_name);
868 if (rename (tmp_name, name) != 0)
870 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
871 sprintf(buf, "%s: error renaming \"%s\" to \"%s\"",
872 blurb(), tmp_name, name);
880 p->init_file_date = write_date;
882 /* Since the .xscreensaver file is used for IPC, let's try and make
883 sure that the bits actually land on the disk right away. */
886 status = 0; /* wrote and renamed successfully! */
891 char *buf = (char *) malloc(1024 + strlen(name));
892 sprintf(buf, "%s: error closing \"%s\"", blurb(), name);
905 /* Parsing the resource database
909 free_screenhack (screenhack *hack)
911 if (hack->visual) free (hack->visual);
912 if (hack->name) free (hack->name);
913 free (hack->command);
914 memset (hack, 0, sizeof(*hack));
919 free_screenhack_list (screenhack **list, int count)
923 for (i = 0; i < count; i++)
925 free_screenhack (list[i]);
930 /* Populate `saver_preferences' with the contents of the resource database.
931 Note that this may be called multiple times -- it is re-run each time
932 the ~/.xscreensaver file is reloaded.
934 This function can be very noisy, since it issues resource syntax errors
938 load_init_file (saver_preferences *p)
940 static Bool first_time = True;
942 screenhack **system_default_screenhacks = 0;
943 int system_default_screenhack_count = 0;
947 /* Get the programs resource before the .xscreensaver file has been
948 parsed and merged into the resource database for the first time:
949 this is the value of *programs from the app-defaults file.
950 Then clear it out so that it will be parsed again later, after
951 the init file has been read.
954 system_default_screenhacks = p->screenhacks;
955 system_default_screenhack_count = p->screenhacks_count;
957 p->screenhacks_count = 0;
960 if (parse_init_file (p) != 0) /* file might have gone away */
961 if (!first_time) return;
965 p->xsync_p = get_boolean_resource ("synchronous", "Synchronous");
966 p->verbose_p = get_boolean_resource ("verbose", "Boolean");
967 p->timestamp_p = get_boolean_resource ("timestamp", "Boolean");
968 p->lock_p = get_boolean_resource ("lock", "Boolean");
969 p->lock_vt_p = get_boolean_resource ("lockVTs", "Boolean");
970 p->fade_p = get_boolean_resource ("fade", "Boolean");
971 p->unfade_p = get_boolean_resource ("unfade", "Boolean");
972 p->fade_seconds = 1000 * get_seconds_resource ("fadeSeconds", "Time");
973 p->fade_ticks = get_integer_resource ("fadeTicks", "Integer");
974 p->install_cmap_p = get_boolean_resource ("installColormap", "Boolean");
975 p->nice_inferior = get_integer_resource ("nice", "Nice");
976 p->inferior_memory_limit = get_byte_resource ("memoryLimit", "MemoryLimit");
977 p->splash_p = get_boolean_resource ("splash", "Boolean");
978 p->capture_stderr_p = get_boolean_resource ("captureStderr", "Boolean");
980 p->initial_delay = 1000 * get_seconds_resource ("initialDelay", "Time");
981 p->splash_duration = 1000 * get_seconds_resource ("splashDuration", "Time");
982 p->timeout = 1000 * get_minutes_resource ("timeout", "Time");
983 p->lock_timeout = 1000 * get_minutes_resource ("lockTimeout", "Time");
984 p->cycle = 1000 * get_minutes_resource ("cycle", "Time");
985 p->passwd_timeout = 1000 * get_seconds_resource ("passwdTimeout", "Time");
986 p->pointer_timeout = 1000 * get_seconds_resource ("pointerPollTime", "Time");
987 p->notice_events_timeout = 1000*get_seconds_resource("windowCreationTimeout",
990 p->dpms_enabled_p = get_boolean_resource ("dpmsEnabled", "Boolean");
991 p->dpms_standby = 1000 * get_minutes_resource ("dpmsStandby", "Time");
992 p->dpms_suspend = 1000 * get_minutes_resource ("dpmsSuspend", "Time");
993 p->dpms_off = 1000 * get_minutes_resource ("dpmsOff", "Time");
995 p->grab_desktop_p = get_boolean_resource ("grabDesktopImages", "Boolean");
996 p->grab_video_p = get_boolean_resource ("grabVideoFrames", "Boolean");
997 p->random_image_p = get_boolean_resource ("chooseRandomImages", "Boolean");
998 p->image_directory = get_string_resource ("imageDirectory",
1001 p->shell = get_string_resource ("bourneShell", "BourneShell");
1003 p->demo_command = get_string_resource("demoCommand", "URL");
1004 p->prefs_command = get_string_resource("prefsCommand", "URL");
1005 p->help_url = get_string_resource("helpURL", "URL");
1006 p->load_url_command = get_string_resource("loadURL", "LoadURL");
1009 /* If "*splash" is unset, default to true. */
1011 char *s = get_string_resource ("splash", "Boolean");
1018 /* If "*grabDesktopImages" is unset, default to true. */
1020 char *s = get_string_resource ("grabDesktopImages", "Boolean");
1024 p->grab_desktop_p = True;
1027 p->use_xidle_extension = get_boolean_resource ("xidleExtension","Boolean");
1028 p->use_mit_saver_extension = get_boolean_resource ("mitSaverExtension",
1030 p->use_sgi_saver_extension = get_boolean_resource ("sgiSaverExtension",
1032 p->use_proc_interrupts = get_boolean_resource ("procInterrupts", "Boolean");
1034 /* Throttle the various timeouts to reasonable values.
1036 if (p->passwd_timeout <= 0) p->passwd_timeout = 30000; /* 30 secs */
1037 if (p->timeout < 10000) p->timeout = 10000; /* 10 secs */
1038 if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */
1039 if (p->pointer_timeout <= 0) p->pointer_timeout = 5000; /* 5 secs */
1040 if (p->notice_events_timeout <= 0)
1041 p->notice_events_timeout = 10000; /* 10 secs */
1042 if (p->fade_seconds <= 0 || p->fade_ticks <= 0)
1044 if (! p->fade_p) p->unfade_p = False;
1046 /* The DPMS settings may have the value 0.
1047 But if they are negative, or are a range less than 10 seconds,
1048 reset them to sensible defaults. (Since that must be a mistake.)
1050 if (p->dpms_standby != 0 &&
1051 p->dpms_standby < 10 * 1000)
1052 p->dpms_standby = 2 * 60 * 60 * 1000; /* 2 hours */
1053 if (p->dpms_suspend != 0 &&
1054 p->dpms_suspend < 10 * 1000)
1055 p->dpms_suspend = 2 * 60 * 60 * 1000; /* 2 hours */
1056 if (p->dpms_off != 0 &&
1057 p->dpms_off < 10 * 1000)
1058 p->dpms_off = 4 * 60 * 60 * 1000; /* 4 hours */
1060 if (p->dpms_standby == 0 && /* if *all* are 0, then DPMS is disabled */
1061 p->dpms_suspend == 0 &&
1063 p->dpms_enabled_p = False;
1066 p->watchdog_timeout = p->cycle * 0.6;
1067 if (p->watchdog_timeout < 30000) p->watchdog_timeout = 30000; /* 30 secs */
1068 if (p->watchdog_timeout > 3600000) p->watchdog_timeout = 3600000; /* 1 hr */
1070 get_screenhacks (p);
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;
1092 /* If there are any hacks in the system-wide defaults that are not in
1093 the ~/.xscreensaver file, add the new ones to the end of the list.
1094 This does *not* actually save the file.
1097 merge_system_screenhacks (saver_preferences *p,
1098 screenhack **system_list, int system_count)
1100 /* Yeah yeah, this is an N^2 operation, but I don't have hashtables handy,
1105 for (i = 0; i < system_count; i++)
1108 Bool matched_p = False;
1110 for (j = 0; j < p->screenhacks_count; j++)
1113 if (!system_list[i]->name)
1114 system_list[i]->name = make_hack_name (system_list[i]->command);
1116 name = p->screenhacks[j]->name;
1118 name = make_hack_name (p->screenhacks[j]->command);
1120 matched_p = !strcasecmp (name, system_list[i]->name);
1122 if (name != p->screenhacks[j]->name)
1131 /* We have an entry in the system-wide list that is not in the
1132 user's .xscreensaver file. Add it to the end.
1133 Note that p->screenhacks is a single malloc block, not a
1134 linked list, so we have to realloc it.
1136 screenhack *oh = system_list[i];
1137 screenhack *nh = (screenhack *) malloc (sizeof(screenhack));
1139 if (made_space == 0)
1142 p->screenhacks = (screenhack **)
1143 realloc (p->screenhacks,
1144 (p->screenhacks_count + made_space + 1)
1145 * sizeof(screenhack));
1146 if (!p->screenhacks) abort();
1149 nh->enabled_p = oh->enabled_p;
1150 nh->visual = oh->visual ? strdup(oh->visual) : 0;
1151 nh->name = oh->name ? strdup(oh->name) : 0;
1152 nh->command = oh->command ? strdup(oh->command) : 0;
1154 p->screenhacks[p->screenhacks_count++] = nh;
1155 p->screenhacks[p->screenhacks_count] = 0;
1159 fprintf (stderr, "%s: noticed new hack: %s\n", blurb(),
1160 (nh->name ? nh->name : make_hack_name (nh->command)));
1168 /* Parsing the programs resource.
1172 parse_screenhack (const char *line)
1174 screenhack *h = (screenhack *) calloc (1, sizeof(*h));
1177 h->enabled_p = True;
1179 while (isspace(*line)) line++; /* skip whitespace */
1180 if (*line == '-') /* handle "-" */
1182 h->enabled_p = False;
1184 while (isspace(*line)) line++; /* skip whitespace */
1187 s = line; /* handle "visual:" */
1188 while (*line && *line != ':' && *line != '"' && !isspace(*line))
1194 h->visual = (char *) malloc (line-s+1);
1195 strncpy (h->visual, s, line-s);
1196 h->visual[line-s] = 0;
1197 if (*line == ':') line++; /* skip ":" */
1198 while (isspace(*line)) line++; /* skip whitespace */
1201 if (*line == '"') /* handle "name" */
1205 while (*line && *line != '"')
1207 h->name = (char *) malloc (line-s+1);
1208 strncpy (h->name, s, line-s);
1209 h->name[line-s] = 0;
1210 if (*line == '"') line++; /* skip "\"" */
1211 while (isspace(*line)) line++; /* skip whitespace */
1214 h->command = format_command (line, False); /* handle command */
1220 format_command (const char *cmd, Bool wrap_p)
1224 char *cmd2 = (char *) calloc (1, 2 * (strlen (cmd) + 1));
1225 const char *in = cmd;
1229 /* shrink all whitespace to one space, for the benefit of the "demo"
1230 mode display. We only do this when we can easily tell that the
1231 whitespace is not significant (no shell metachars).
1235 case '\'': case '"': case '`': case '\\':
1236 /* Metachars are scary. Copy the rest of the line unchanged. */
1238 *out++ = *in++, col++;
1241 case ' ': case '\t':
1242 /* Squeeze all other whitespace down to one space. */
1243 while (*in == ' ' || *in == '\t')
1245 *out++ = ' ', col++;
1249 /* Copy other chars unchanged. */
1250 *out++ = *in++, col++;
1257 /* Strip trailing whitespace */
1258 while (out > cmd2 && isspace (out[-1]))
1265 /* Returns a new string describing the shell command.
1266 This may be just the name of the program, capitalized.
1267 It also may be something from the resource database (gotten
1268 by looking for "hacks.XYZ.name", where XYZ is the program.)
1271 make_hack_name (const char *shell_command)
1273 char *s = strdup (shell_command);
1277 for (s2 = s; *s2; s2++) /* truncate at first whitespace */
1284 s2 = strrchr (s, '/'); /* if pathname, take last component */
1292 if (strlen (s) > 50) /* 51 is hereby defined as "unreasonable" */
1295 sprintf (res_name, "hacks.%s.name", s); /* resource? */
1296 s2 = get_string_resource (res_name, res_name);
1300 for (s2 = s; *s2; s2++) /* if it has any capitals, return it */
1301 if (*s2 >= 'A' && *s2 <= 'Z')
1304 if (s[0] >= 'a' && s[0] <= 'z') /* else cap it */
1306 if (s[0] == 'X' && s[1] >= 'a' && s[1] <= 'z') /* (magic leading X) */
1313 format_hack (screenhack *hack, Bool wrap_p)
1316 int size = (2 * (strlen(hack->command) +
1317 (hack->visual ? strlen(hack->visual) : 0) +
1318 (hack->name ? strlen(hack->name) : 0) +
1320 char *h2 = (char *) malloc (size);
1325 if (!hack->enabled_p) *out++ = '-'; /* write disabled flag */
1327 if (hack->visual && *hack->visual) /* write visual name */
1329 if (hack->enabled_p) *out++ = ' ';
1331 strcpy (out, hack->visual);
1332 out += strlen (hack->visual);
1338 col = string_columns (h2, strlen (h2), 0);
1340 if (hack->name && *hack->name) /* write pretty name */
1342 int L = (strlen (hack->name) + 2);
1344 out = stab_to (out, col, tab - L - 2);
1348 strcpy (out, hack->name);
1349 out += strlen (hack->name);
1353 col = string_columns (h2, strlen (h2), 0);
1354 if (wrap_p && col >= tab)
1355 out = stab_to (out, col, 77);
1359 if (out >= h2+size) abort();
1363 col = string_columns (h2, strlen (h2), 0);
1364 out = stab_to (out, col, tab); /* indent */
1366 if (out >= h2+size) abort();
1367 s = format_command (hack->command, wrap_p);
1378 get_screenhacks (saver_preferences *p)
1386 d = get_string_resource ("monoPrograms", "MonoPrograms");
1387 if (d && !*d) { free(d); d = 0; }
1389 d = get_string_resource ("colorPrograms", "ColorPrograms");
1390 if (d && !*d) { free(d); d = 0; }
1395 "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
1396 see the manual for details.\n", blurb());
1400 d = get_string_resource ("programs", "Programs");
1402 free_screenhack_list (p->screenhacks, p->screenhacks_count);
1404 p->screenhacks_count = 0;
1412 /* Count up the number of newlines (which will be equal to or larger than
1413 one less than the number of hacks.)
1415 for (i = j = 0; d[i]; i++)
1420 p->screenhacks = (screenhack **) calloc (j + 1, sizeof (screenhack *));
1422 /* Iterate over the lines in `d' (the string with newlines)
1423 and make new strings to stuff into the `screenhacks' array.
1425 p->screenhacks_count = 0;
1426 while (start < size)
1428 /* skip forward over whitespace. */
1429 while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n')
1432 /* skip forward to newline or end of string. */
1434 while (d[end] != 0 && d[end] != '\n')
1437 /* null terminate. */
1440 p->screenhacks[p->screenhacks_count++] = parse_screenhack (d + start);
1441 if (p->screenhacks_count >= i)
1447 if (p->screenhacks_count == 0)
1449 free (p->screenhacks);