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",
270 "windowCreationTimeout",
277 "overlayTextBackground", /* not saved -- X resources only */
278 "overlayTextForeground", /* not saved -- X resources only */
279 "bourneShell", /* not saved -- X resources only */
287 while (*s == '\t' || *s == ' ' || *s == '\r' || *s == '\n')
289 for (s2 = s; *s2; s2++)
291 for (s2--; s2 >= s; s2--)
292 if (*s2 == '\t' || *s2 == ' ' || *s2 == '\r' || *s2 =='\n')
304 handle_entry (XrmDatabase *db, const char *key, const char *value,
305 const char *filename, int line)
308 for (i = 0; prefs[i]; i++)
309 if (*prefs[i] && !strcasecmp(key, prefs[i]))
311 char *val = strdup(value);
312 char *spec = (char *) malloc(strlen(progclass) + strlen(prefs[i]) +10);
313 strcpy(spec, progclass);
315 strcat(spec, prefs[i]);
317 XrmPutStringResource (db, spec, val);
324 fprintf(stderr, "%s: %s:%d: unknown option \"%s\"\n",
325 blurb(), filename, line, key);
331 parse_init_file (saver_preferences *p)
333 time_t write_date = 0;
334 const char *name = init_file_name();
343 if (stat(name, &st) != 0)
345 p->init_file_date = 0;
349 in = fopen(name, "r");
352 char *buf = (char *) malloc(1024 + strlen(name));
353 sprintf(buf, "%s: error reading \"%s\"", blurb(), name);
359 if (fstat (fileno(in), &st) == 0)
361 write_date = st.st_mtime;
365 char *buf = (char *) malloc(1024 + strlen(name));
366 sprintf(buf, "%s: couldn't re-stat \"%s\"", blurb(), name);
372 buf = (char *) malloc(buf_size);
374 while (fgets (buf, buf_size-1, in))
381 (buf[L-1] != '\n' || /* whole line didn't fit in buffer */
382 buf[L-2] == '\\')) /* or line ended with backslash */
384 if (buf[L-2] == '\\') /* backslash-newline gets swallowed */
390 buf = (char *) realloc(buf, buf_size);
394 if (!fgets (buf + L, buf_size-L-1, in))
399 /* Now handle other backslash escapes. */
402 for (i = 0; buf[i]; i++)
407 case 'n': buf[i] = '\n'; break;
408 case 'r': buf[i] = '\r'; break;
409 case 't': buf[i] = '\t'; break;
410 default: buf[i] = buf[i+1]; break;
412 for (j = i+2; buf[j]; j++)
420 if (*key == '#' || *key == '!' || *key == ';' ||
421 *key == '\n' || *key == 0)
424 value = strchr (key, ':');
427 fprintf(stderr, "%s: %s:%d: unparsable line: %s\n", blurb(),
434 value = strip(value);
438 handle_entry (&p->db, key, value, name, line);
443 p->init_file_date = write_date;
449 init_file_changed_p (saver_preferences *p)
451 const char *name = init_file_name();
454 if (!name) return False;
456 if (stat(name, &st) != 0)
459 if (p->init_file_date == st.st_mtime)
470 tab_to (FILE *out, int from, int to)
473 int to_mod = (to / tab_width) * tab_width;
474 while (from < to_mod)
477 from = (((from / tab_width) + 1) * tab_width);
488 stab_to (char *out, int from, int to)
491 int to_mod = (to / tab_width) * tab_width;
492 while (from < to_mod)
495 from = (((from / tab_width) + 1) * tab_width);
506 string_columns (const char *string, int length, int start)
510 const char *end = string + length;
515 else if (*string == '\t')
516 col = (((col / tab_width) + 1) * tab_width);
526 write_entry (FILE *out, const char *key, const char *value)
528 char *v = strdup(value ? value : "");
532 Bool programs_p = (!strcmp(key, "programs"));
533 int tab = (programs_p ? 32 : 16);
536 fprintf(out, "%s:", key);
537 col = strlen(key) + 1;
539 if (strlen(key) > 14)
540 col = tab_to (out, col, 20);
546 nl = strchr(v2, '\n');
550 if (first && programs_p)
552 col = tab_to (out, col, 77);
553 fprintf (out, " \\\n");
561 col = tab_to (out, col, 75);
562 fprintf (out, " \\n\\\n");
567 col = tab_to (out, col, tab);
570 string_columns(v2, strlen (v2), col) + col > 75)
577 while (v2[end] == ' ' || v2[end] == '\t')
579 while (v2[end] != ' ' && v2[end] != '\t' &&
580 v2[end] != '\n' && v2[end] != 0)
582 if (string_columns (v2 + start, (end - start), col) >= 74)
584 col = tab_to (out, col, 75);
585 fprintf(out, " \\\n");
586 col = tab_to (out, 0, tab + 2);
587 while (v2[start] == ' ' || v2[start] == '\t')
591 col = string_columns (v2 + start, (end - start), col);
593 fputc(v2[start++], out);
598 fprintf (out, "%s", v2);
599 col += string_columns(v2, strlen (v2), col);
613 write_init_file (saver_preferences *p, const char *version_string,
617 const char *name = init_file_name();
618 const char *tmp_name = init_file_tmp_name();
619 char *n2 = chase_symlinks (name);
623 /* Kludge, since these aren't in the saver_preferences struct as strings...
627 Bool overlay_stderr_p;
636 fprintf (stderr, "%s: writing \"%s\".\n", blurb(), name);
639 out = fopen(tmp_name, "w");
642 char *buf = (char *) malloc(1024 + strlen(name));
643 sprintf(buf, "%s: error writing \"%s\"", blurb(), name);
649 /* Give the new .xscreensaver file the same permissions as the old one;
650 except ensure that it is readable and writable by owner, and not
651 executable. Extra hack: if we're running as root, make the file
652 be world-readable (so that the daemon, running as "nobody", will
653 still be able to read it.)
655 if (stat(name, &st) == 0)
657 mode_t mode = st.st_mode;
658 mode |= S_IRUSR | S_IWUSR; /* read/write by user */
659 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); /* executable by none */
661 if (getuid() == (uid_t) 0) /* read by group/other */
662 mode |= S_IRGRP | S_IROTH;
664 if (fchmod (fileno(out), mode) != 0)
666 char *buf = (char *) malloc(1024 + strlen(name));
667 sprintf (buf, "%s: error fchmodding \"%s\" to 0%o", blurb(),
668 tmp_name, (unsigned int) mode);
675 /* Kludge, since these aren't in the saver_preferences struct... */
676 visual_name = get_string_resource ("visualID", "VisualID");
678 overlay_stderr_p = get_boolean_resource ("overlayStderr", "Boolean");
679 stderr_font = get_string_resource ("font", "Font");
684 char **hack_strings = (char **)
685 calloc (p->screenhacks_count, sizeof(char *));
687 for (j = 0; j < p->screenhacks_count; j++)
689 hack_strings[j] = format_hack (p->screenhacks[j], True);
690 i += strlen (hack_strings[j]);
694 ss = programs = (char *) malloc(i + 10);
696 for (j = 0; j < p->screenhacks_count; j++)
698 strcat (ss, hack_strings[j]);
699 free (hack_strings[j]);
707 struct passwd *pw = getpwuid (getuid ());
708 char *whoami = (pw && pw->pw_name && *pw->pw_name
711 time_t now = time ((time_t *) 0);
712 char *timestr = (char *) ctime (&now);
713 char *nl = (char *) strchr (timestr, '\n');
716 "# %s Preferences File\n"
717 "# Written by %s %s for %s on %s.\n"
718 "# http://www.jwz.org/xscreensaver/\n"
720 progclass, progname, version_string, whoami, timestr);
723 for (j = 0; prefs[j]; j++)
726 const char *pr = prefs[j];
727 enum pref_type { pref_str, pref_int, pref_bool, pref_byte, pref_time
741 # define CHECK(X) else if (!strcmp(pr, X))
743 CHECK("timeout") type = pref_time, t = p->timeout;
744 CHECK("cycle") type = pref_time, t = p->cycle;
745 CHECK("lock") type = pref_bool, b = p->lock_p;
746 # if 0 /* #### not ready yet */
747 CHECK("lockVTs") type = pref_bool, b = p->lock_vt_p;
749 CHECK("lockVTs") continue; /* don't save */
751 CHECK("lockTimeout") type = pref_time, t = p->lock_timeout;
752 CHECK("passwdTimeout") type = pref_time, t = p->passwd_timeout;
753 CHECK("visualID") type = pref_str, s = visual_name;
754 CHECK("installColormap") type = pref_bool, b = p->install_cmap_p;
755 CHECK("verbose") type = pref_bool, b = p->verbose_p;
756 CHECK("timestamp") type = pref_bool, b = p->timestamp_p;
757 CHECK("splash") type = pref_bool, b = p->splash_p;
758 CHECK("splashDuration") type = pref_time, t = p->splash_duration;
759 CHECK("demoCommand") type = pref_str, s = p->demo_command;
760 CHECK("prefsCommand") type = pref_str, s = p->prefs_command;
761 CHECK("helpURL") type = pref_str, s = p->help_url;
762 CHECK("loadURL") type = pref_str, s = p->load_url_command;
763 CHECK("nice") type = pref_int, i = p->nice_inferior;
764 CHECK("memoryLimit") type = pref_byte, i = p->inferior_memory_limit;
765 CHECK("fade") type = pref_bool, b = p->fade_p;
766 CHECK("unfade") type = pref_bool, b = p->unfade_p;
767 CHECK("fadeSeconds") type = pref_time, t = p->fade_seconds;
768 CHECK("fadeTicks") type = pref_int, i = p->fade_ticks;
769 CHECK("captureStderr") type = pref_bool, b = p->capture_stderr_p;
770 CHECK("captureStdout") continue; /* don't save */
771 CHECK("font") type = pref_str, s = stderr_font;
773 CHECK("dpmsEnabled") type = pref_bool, b = p->dpms_enabled_p;
774 CHECK("dpmsStandby") type = pref_time, t = p->dpms_standby;
775 CHECK("dpmsSuspend") type = pref_time, t = p->dpms_suspend;
776 CHECK("dpmsOff") type = pref_time, t = p->dpms_off;
778 CHECK("grabDesktopImages") type =pref_bool, b = p->grab_desktop_p;
779 CHECK("grabVideoFrames") type =pref_bool, b = p->grab_video_p;
780 CHECK("chooseRandomImages")type =pref_bool, b = p->random_image_p;
781 CHECK("imageDirectory") type =pref_str, s = p->image_directory;
783 CHECK("mode") type = pref_str,
784 s = (p->mode == ONE_HACK ? "one" :
785 p->mode == BLANK_ONLY ? "blank" :
786 p->mode == DONT_BLANK ? "off" :
788 CHECK("selected") type = pref_int, i = p->selected_hack;
790 CHECK("programs") type = pref_str, s = programs;
791 CHECK("pointerPollTime") type = pref_time, t = p->pointer_timeout;
792 CHECK("windowCreationTimeout")type=pref_time,t= p->notice_events_timeout;
793 CHECK("initialDelay") type = pref_time, t = p->initial_delay;
794 CHECK("sgiSaverExtension")type = pref_bool, b=p->use_sgi_saver_extension;
795 CHECK("mitSaverExtension")type = pref_bool, b=p->use_mit_saver_extension;
796 CHECK("xidleExtension") type = pref_bool, b = p->use_xidle_extension;
797 CHECK("procInterrupts") type = pref_bool, b = p->use_proc_interrupts;
798 CHECK("overlayStderr") type = pref_bool, b = overlay_stderr_p;
799 CHECK("overlayTextBackground") continue; /* don't save */
800 CHECK("overlayTextForeground") continue; /* don't save */
801 CHECK("bourneShell") continue;
810 sprintf(buf, "%d", i);
814 s = b ? "True" : "False";
818 unsigned int hour = 0, min = 0, sec = (unsigned int) (t/1000);
829 sprintf (buf, "%u:%02u:%02u", hour, min, sec);
835 if (i >= (1<<30) && i == ((i >> 30) << 30))
836 sprintf(buf, "%dG", i >> 30);
837 else if (i >= (1<<20) && i == ((i >> 20) << 20))
838 sprintf(buf, "%dM", i >> 20);
839 else if (i >= (1<<10) && i == ((i >> 10) << 10))
840 sprintf(buf, "%dK", i >> 10);
842 sprintf(buf, "%d", i);
851 if (pr && !strcmp(pr, "mode")) fprintf(out, "\n");
853 write_entry (out, pr, s);
858 if (visual_name) free(visual_name);
859 if (stderr_font) free(stderr_font);
860 if (programs) free(programs);
862 if (fclose(out) == 0)
864 time_t write_date = 0;
866 if (stat(tmp_name, &st) == 0)
868 write_date = st.st_mtime;
872 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
873 sprintf(buf, "%s: couldn't stat \"%s\"", blurb(), tmp_name);
880 if (rename (tmp_name, name) != 0)
882 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
883 sprintf(buf, "%s: error renaming \"%s\" to \"%s\"",
884 blurb(), tmp_name, name);
892 p->init_file_date = write_date;
894 /* Since the .xscreensaver file is used for IPC, let's try and make
895 sure that the bits actually land on the disk right away. */
898 status = 0; /* wrote and renamed successfully! */
903 char *buf = (char *) malloc(1024 + strlen(name));
904 sprintf(buf, "%s: error closing \"%s\"", blurb(), name);
917 /* Parsing the resource database
921 free_screenhack (screenhack *hack)
923 if (hack->visual) free (hack->visual);
924 if (hack->name) free (hack->name);
925 free (hack->command);
926 memset (hack, 0, sizeof(*hack));
931 free_screenhack_list (screenhack **list, int count)
935 for (i = 0; i < count; i++)
937 free_screenhack (list[i]);
942 /* Populate `saver_preferences' with the contents of the resource database.
943 Note that this may be called multiple times -- it is re-run each time
944 the ~/.xscreensaver file is reloaded.
946 This function can be very noisy, since it issues resource syntax errors
950 load_init_file (saver_preferences *p)
952 static Bool first_time = True;
954 screenhack **system_default_screenhacks = 0;
955 int system_default_screenhack_count = 0;
959 /* Get the programs resource before the .xscreensaver file has been
960 parsed and merged into the resource database for the first time:
961 this is the value of *programs from the app-defaults file.
962 Then clear it out so that it will be parsed again later, after
963 the init file has been read.
966 system_default_screenhacks = p->screenhacks;
967 system_default_screenhack_count = p->screenhacks_count;
969 p->screenhacks_count = 0;
972 if (parse_init_file (p) != 0) /* file might have gone away */
973 if (!first_time) return;
977 p->xsync_p = get_boolean_resource ("synchronous", "Synchronous");
978 p->verbose_p = get_boolean_resource ("verbose", "Boolean");
979 p->timestamp_p = get_boolean_resource ("timestamp", "Boolean");
980 p->lock_p = get_boolean_resource ("lock", "Boolean");
981 p->lock_vt_p = get_boolean_resource ("lockVTs", "Boolean");
982 p->fade_p = get_boolean_resource ("fade", "Boolean");
983 p->unfade_p = get_boolean_resource ("unfade", "Boolean");
984 p->fade_seconds = 1000 * get_seconds_resource ("fadeSeconds", "Time");
985 p->fade_ticks = get_integer_resource ("fadeTicks", "Integer");
986 p->install_cmap_p = get_boolean_resource ("installColormap", "Boolean");
987 p->nice_inferior = get_integer_resource ("nice", "Nice");
988 p->inferior_memory_limit = get_byte_resource ("memoryLimit", "MemoryLimit");
989 p->splash_p = get_boolean_resource ("splash", "Boolean");
990 p->capture_stderr_p = get_boolean_resource ("captureStderr", "Boolean");
992 p->initial_delay = 1000 * get_seconds_resource ("initialDelay", "Time");
993 p->splash_duration = 1000 * get_seconds_resource ("splashDuration", "Time");
994 p->timeout = 1000 * get_minutes_resource ("timeout", "Time");
995 p->lock_timeout = 1000 * get_minutes_resource ("lockTimeout", "Time");
996 p->cycle = 1000 * get_minutes_resource ("cycle", "Time");
997 p->passwd_timeout = 1000 * get_seconds_resource ("passwdTimeout", "Time");
998 p->pointer_timeout = 1000 * get_seconds_resource ("pointerPollTime", "Time");
999 p->notice_events_timeout = 1000*get_seconds_resource("windowCreationTimeout",
1002 p->dpms_enabled_p = get_boolean_resource ("dpmsEnabled", "Boolean");
1003 p->dpms_standby = 1000 * get_minutes_resource ("dpmsStandby", "Time");
1004 p->dpms_suspend = 1000 * get_minutes_resource ("dpmsSuspend", "Time");
1005 p->dpms_off = 1000 * get_minutes_resource ("dpmsOff", "Time");
1007 p->grab_desktop_p = get_boolean_resource ("grabDesktopImages", "Boolean");
1008 p->grab_video_p = get_boolean_resource ("grabVideoFrames", "Boolean");
1009 p->random_image_p = get_boolean_resource ("chooseRandomImages", "Boolean");
1010 p->image_directory = get_string_resource ("imageDirectory",
1013 p->shell = get_string_resource ("bourneShell", "BourneShell");
1015 p->demo_command = get_string_resource("demoCommand", "URL");
1016 p->prefs_command = get_string_resource("prefsCommand", "URL");
1017 p->help_url = get_string_resource("helpURL", "URL");
1018 p->load_url_command = get_string_resource("loadURL", "LoadURL");
1021 /* If "*splash" is unset, default to true. */
1023 char *s = get_string_resource ("splash", "Boolean");
1030 /* If "*grabDesktopImages" is unset, default to true. */
1032 char *s = get_string_resource ("grabDesktopImages", "Boolean");
1036 p->grab_desktop_p = True;
1039 p->use_xidle_extension = get_boolean_resource ("xidleExtension","Boolean");
1040 p->use_mit_saver_extension = get_boolean_resource ("mitSaverExtension",
1042 p->use_sgi_saver_extension = get_boolean_resource ("sgiSaverExtension",
1044 p->use_proc_interrupts = get_boolean_resource ("procInterrupts", "Boolean");
1046 /* Throttle the various timeouts to reasonable values.
1048 if (p->passwd_timeout <= 0) p->passwd_timeout = 30000; /* 30 secs */
1049 if (p->timeout < 10000) p->timeout = 10000; /* 10 secs */
1050 if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */
1051 if (p->pointer_timeout <= 0) p->pointer_timeout = 5000; /* 5 secs */
1052 if (p->notice_events_timeout <= 0)
1053 p->notice_events_timeout = 10000; /* 10 secs */
1054 if (p->fade_seconds <= 0 || p->fade_ticks <= 0)
1056 if (! p->fade_p) p->unfade_p = False;
1058 /* The DPMS settings may have the value 0.
1059 But if they are negative, or are a range less than 10 seconds,
1060 reset them to sensible defaults. (Since that must be a mistake.)
1062 if (p->dpms_standby != 0 &&
1063 p->dpms_standby < 10 * 1000)
1064 p->dpms_standby = 2 * 60 * 60 * 1000; /* 2 hours */
1065 if (p->dpms_suspend != 0 &&
1066 p->dpms_suspend < 10 * 1000)
1067 p->dpms_suspend = 2 * 60 * 60 * 1000; /* 2 hours */
1068 if (p->dpms_off != 0 &&
1069 p->dpms_off < 10 * 1000)
1070 p->dpms_off = 4 * 60 * 60 * 1000; /* 4 hours */
1072 if (p->dpms_standby == 0 && /* if *all* are 0, then DPMS is disabled */
1073 p->dpms_suspend == 0 &&
1075 p->dpms_enabled_p = False;
1078 p->watchdog_timeout = p->cycle * 0.6;
1079 if (p->watchdog_timeout < 30000) p->watchdog_timeout = 30000; /* 30 secs */
1080 if (p->watchdog_timeout > 3600000) p->watchdog_timeout = 3600000; /* 1 hr */
1082 get_screenhacks (p);
1084 p->selected_hack = get_integer_resource ("selected", "Integer");
1085 if (p->selected_hack < 0 || p->selected_hack >= p->screenhacks_count)
1086 p->selected_hack = -1;
1089 char *s = get_string_resource ("mode", "Mode");
1090 if (s && !strcasecmp (s, "one")) p->mode = ONE_HACK;
1091 else if (s && !strcasecmp (s, "blank")) p->mode = BLANK_ONLY;
1092 else if (s && !strcasecmp (s, "off")) p->mode = DONT_BLANK;
1093 else p->mode = RANDOM_HACKS;
1096 if (system_default_screenhack_count) /* note: first_time is also true */
1098 merge_system_screenhacks (p, system_default_screenhacks,
1099 system_default_screenhack_count);
1100 free_screenhack_list (system_default_screenhacks,
1101 system_default_screenhack_count);
1102 system_default_screenhacks = 0;
1103 system_default_screenhack_count = 0;
1109 p->verbose_p = True;
1110 p->timestamp_p = True;
1111 p->initial_delay = 0;
1116 /* If there are any hacks in the system-wide defaults that are not in
1117 the ~/.xscreensaver file, add the new ones to the end of the list.
1118 This does *not* actually save the file.
1121 merge_system_screenhacks (saver_preferences *p,
1122 screenhack **system_list, int system_count)
1124 /* Yeah yeah, this is an N^2 operation, but I don't have hashtables handy,
1129 for (i = 0; i < system_count; i++)
1132 Bool matched_p = False;
1134 for (j = 0; j < p->screenhacks_count; j++)
1137 if (!system_list[i]->name)
1138 system_list[i]->name = make_hack_name (system_list[i]->command);
1140 name = p->screenhacks[j]->name;
1142 name = make_hack_name (p->screenhacks[j]->command);
1144 matched_p = !strcasecmp (name, system_list[i]->name);
1146 if (name != p->screenhacks[j]->name)
1155 /* We have an entry in the system-wide list that is not in the
1156 user's .xscreensaver file. Add it to the end.
1157 Note that p->screenhacks is a single malloc block, not a
1158 linked list, so we have to realloc it.
1160 screenhack *oh = system_list[i];
1161 screenhack *nh = (screenhack *) malloc (sizeof(screenhack));
1163 if (made_space == 0)
1166 p->screenhacks = (screenhack **)
1167 realloc (p->screenhacks,
1168 (p->screenhacks_count + made_space + 1)
1169 * sizeof(screenhack));
1170 if (!p->screenhacks) abort();
1173 nh->enabled_p = oh->enabled_p;
1174 nh->visual = oh->visual ? strdup(oh->visual) : 0;
1175 nh->name = oh->name ? strdup(oh->name) : 0;
1176 nh->command = oh->command ? strdup(oh->command) : 0;
1178 p->screenhacks[p->screenhacks_count++] = nh;
1179 p->screenhacks[p->screenhacks_count] = 0;
1183 fprintf (stderr, "%s: noticed new hack: %s\n", blurb(),
1184 (nh->name ? nh->name : make_hack_name (nh->command)));
1192 /* Parsing the programs resource.
1196 parse_screenhack (const char *line)
1198 screenhack *h = (screenhack *) calloc (1, sizeof(*h));
1201 h->enabled_p = True;
1203 while (isspace(*line)) line++; /* skip whitespace */
1204 if (*line == '-') /* handle "-" */
1206 h->enabled_p = False;
1208 while (isspace(*line)) line++; /* skip whitespace */
1211 s = line; /* handle "visual:" */
1212 while (*line && *line != ':' && *line != '"' && !isspace(*line))
1218 h->visual = (char *) malloc (line-s+1);
1219 strncpy (h->visual, s, line-s);
1220 h->visual[line-s] = 0;
1221 if (*line == ':') line++; /* skip ":" */
1222 while (isspace(*line)) line++; /* skip whitespace */
1225 if (*line == '"') /* handle "name" */
1229 while (*line && *line != '"')
1231 h->name = (char *) malloc (line-s+1);
1232 strncpy (h->name, s, line-s);
1233 h->name[line-s] = 0;
1234 if (*line == '"') line++; /* skip "\"" */
1235 while (isspace(*line)) line++; /* skip whitespace */
1238 h->command = format_command (line, False); /* handle command */
1244 format_command (const char *cmd, Bool wrap_p)
1248 char *cmd2 = (char *) calloc (1, 2 * (strlen (cmd) + 1));
1249 const char *in = cmd;
1253 /* shrink all whitespace to one space, for the benefit of the "demo"
1254 mode display. We only do this when we can easily tell that the
1255 whitespace is not significant (no shell metachars).
1259 case '\'': case '"': case '`': case '\\':
1260 /* Metachars are scary. Copy the rest of the line unchanged. */
1262 *out++ = *in++, col++;
1265 case ' ': case '\t':
1266 /* Squeeze all other whitespace down to one space. */
1267 while (*in == ' ' || *in == '\t')
1269 *out++ = ' ', col++;
1273 /* Copy other chars unchanged. */
1274 *out++ = *in++, col++;
1281 /* Strip trailing whitespace */
1282 while (out > cmd2 && isspace (out[-1]))
1289 /* Returns a new string describing the shell command.
1290 This may be just the name of the program, capitalized.
1291 It also may be something from the resource database (gotten
1292 by looking for "hacks.XYZ.name", where XYZ is the program.)
1295 make_hack_name (const char *shell_command)
1297 char *s = strdup (shell_command);
1301 for (s2 = s; *s2; s2++) /* truncate at first whitespace */
1308 s2 = strrchr (s, '/'); /* if pathname, take last component */
1316 if (strlen (s) > 50) /* 51 is hereby defined as "unreasonable" */
1319 sprintf (res_name, "hacks.%s.name", s); /* resource? */
1320 s2 = get_string_resource (res_name, res_name);
1324 for (s2 = s; *s2; s2++) /* if it has any capitals, return it */
1325 if (*s2 >= 'A' && *s2 <= 'Z')
1328 if (s[0] >= 'a' && s[0] <= 'z') /* else cap it */
1330 if (s[0] == 'X' && s[1] >= 'a' && s[1] <= 'z') /* (magic leading X) */
1337 format_hack (screenhack *hack, Bool wrap_p)
1344 char *def_name = make_hack_name (hack->command);
1346 /* Don't ever write out a name for a hack if it's the same as the default.
1348 if (hack->name && !strcmp (hack->name, def_name))
1355 size = (2 * (strlen(hack->command) +
1356 (hack->visual ? strlen(hack->visual) : 0) +
1357 (hack->name ? strlen(hack->name) : 0) +
1359 h2 = (char *) malloc (size);
1362 if (!hack->enabled_p) *out++ = '-'; /* write disabled flag */
1364 if (hack->visual && *hack->visual) /* write visual name */
1366 if (hack->enabled_p) *out++ = ' ';
1368 strcpy (out, hack->visual);
1369 out += strlen (hack->visual);
1375 col = string_columns (h2, strlen (h2), 0);
1377 if (hack->name && *hack->name) /* write pretty name */
1379 int L = (strlen (hack->name) + 2);
1381 out = stab_to (out, col, tab - L - 2);
1385 strcpy (out, hack->name);
1386 out += strlen (hack->name);
1390 col = string_columns (h2, strlen (h2), 0);
1391 if (wrap_p && col >= tab)
1392 out = stab_to (out, col, 77);
1396 if (out >= h2+size) abort();
1400 col = string_columns (h2, strlen (h2), 0);
1401 out = stab_to (out, col, tab); /* indent */
1403 if (out >= h2+size) abort();
1404 s = format_command (hack->command, wrap_p);
1415 get_screenhacks (saver_preferences *p)
1423 d = get_string_resource ("monoPrograms", "MonoPrograms");
1424 if (d && !*d) { free(d); d = 0; }
1426 d = get_string_resource ("colorPrograms", "ColorPrograms");
1427 if (d && !*d) { free(d); d = 0; }
1432 "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
1433 see the manual for details.\n", blurb());
1437 d = get_string_resource ("programs", "Programs");
1439 free_screenhack_list (p->screenhacks, p->screenhacks_count);
1441 p->screenhacks_count = 0;
1449 /* Count up the number of newlines (which will be equal to or larger than
1450 one less than the number of hacks.)
1452 for (i = j = 0; d[i]; i++)
1457 p->screenhacks = (screenhack **) calloc (j + 1, sizeof (screenhack *));
1459 /* Iterate over the lines in `d' (the string with newlines)
1460 and make new strings to stuff into the `screenhacks' array.
1462 p->screenhacks_count = 0;
1463 while (start < size)
1465 /* skip forward over whitespace. */
1466 while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n')
1469 /* skip forward to newline or end of string. */
1471 while (d[end] != 0 && d[end] != '\n')
1474 /* null terminate. */
1477 p->screenhacks[p->screenhacks_count++] = parse_screenhack (d + start);
1478 if (p->screenhacks_count >= i)
1484 if (p->screenhacks_count == 0)
1486 free (p->screenhacks);