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 static const char * const prefs[] = {
219 "captureStdout", /* not saved -- obsolete */
229 "windowCreationTimeout",
236 "overlayTextBackground", /* not saved -- X resources only */
237 "overlayTextForeground", /* not saved -- X resources only */
238 "bourneShell", /* not saved -- X resources only */
246 while (*s == '\t' || *s == ' ' || *s == '\r' || *s == '\n')
248 for (s2 = s; *s2; s2++)
250 for (s2--; s2 >= s; s2--)
251 if (*s2 == '\t' || *s2 == ' ' || *s2 == '\r' || *s2 =='\n')
263 handle_entry (XrmDatabase *db, const char *key, const char *value,
264 const char *filename, int line)
267 for (i = 0; prefs[i]; i++)
268 if (*prefs[i] && !strcasecmp(key, prefs[i]))
270 char *val = strdup(value);
271 char *spec = (char *) malloc(strlen(progclass) + strlen(prefs[i]) +10);
272 strcpy(spec, progclass);
274 strcat(spec, prefs[i]);
276 XrmPutStringResource (db, spec, val);
283 fprintf(stderr, "%s: %s:%d: unknown option \"%s\"\n",
284 blurb(), filename, line, key);
290 parse_init_file (saver_preferences *p)
292 time_t write_date = 0;
293 const char *name = init_file_name();
302 if (stat(name, &st) != 0)
304 p->init_file_date = 0;
308 in = fopen(name, "r");
311 char *buf = (char *) malloc(1024 + strlen(name));
312 sprintf(buf, "%s: error reading \"%s\"", blurb(), name);
318 if (fstat (fileno(in), &st) == 0)
320 write_date = st.st_mtime;
324 char *buf = (char *) malloc(1024 + strlen(name));
325 sprintf(buf, "%s: couldn't re-stat \"%s\"", blurb(), name);
331 buf = (char *) malloc(buf_size);
333 while (fgets (buf, buf_size-1, in))
340 (buf[L-1] != '\n' || /* whole line didn't fit in buffer */
341 buf[L-2] == '\\')) /* or line ended with backslash */
343 if (buf[L-2] == '\\') /* backslash-newline gets swallowed */
349 buf = (char *) realloc(buf, buf_size);
353 if (!fgets (buf + L, buf_size-L-1, in))
358 /* Now handle other backslash escapes. */
361 for (i = 0; buf[i]; i++)
366 case 'n': buf[i] = '\n'; break;
367 case 'r': buf[i] = '\r'; break;
368 case 't': buf[i] = '\t'; break;
369 default: buf[i] = buf[i+1]; break;
371 for (j = i+2; buf[j]; j++)
379 if (*key == '#' || *key == '!' || *key == ';' ||
380 *key == '\n' || *key == 0)
383 value = strchr (key, ':');
386 fprintf(stderr, "%s: %s:%d: unparsable line: %s\n", blurb(),
393 value = strip(value);
397 handle_entry (&p->db, key, value, name, line);
402 p->init_file_date = write_date;
408 init_file_changed_p (saver_preferences *p)
410 const char *name = init_file_name();
413 if (!name) return False;
415 if (stat(name, &st) != 0)
418 if (p->init_file_date == st.st_mtime)
429 tab_to (FILE *out, int from, int to)
432 int to_mod = (to / tab_width) * tab_width;
433 while (from < to_mod)
436 from = (((from / tab_width) + 1) * tab_width);
447 stab_to (char *out, int from, int to)
450 int to_mod = (to / tab_width) * tab_width;
451 while (from < to_mod)
454 from = (((from / tab_width) + 1) * tab_width);
465 string_columns (const char *string, int length, int start)
469 const char *end = string + length;
474 else if (*string == '\t')
475 col = (((col / tab_width) + 1) * tab_width);
485 write_entry (FILE *out, const char *key, const char *value)
487 char *v = strdup(value ? value : "");
491 Bool programs_p = (!strcmp(key, "programs"));
492 int tab = (programs_p ? 32 : 16);
495 fprintf(out, "%s:", key);
496 col = strlen(key) + 1;
502 nl = strchr(v2, '\n');
506 if (first && programs_p)
508 col = tab_to (out, col, 77);
509 fprintf (out, " \\\n");
517 col = tab_to (out, col, 75);
518 fprintf (out, " \\n\\\n");
523 col = tab_to (out, col, tab);
526 string_columns(v2, strlen (v2), col) + col > 75)
533 while (v2[end] == ' ' || v2[end] == '\t')
535 while (v2[end] != ' ' && v2[end] != '\t' &&
536 v2[end] != '\n' && v2[end] != 0)
538 if (string_columns (v2 + start, (end - start), col) >= 74)
540 col = tab_to (out, col, 75);
541 fprintf(out, " \\\n");
542 col = tab_to (out, 0, tab + 2);
543 while (v2[start] == ' ' || v2[start] == '\t')
547 col = string_columns (v2 + start, (end - start), col);
549 fputc(v2[start++], out);
554 fprintf (out, "%s", v2);
555 col += string_columns(v2, strlen (v2), col);
569 write_init_file (saver_preferences *p, const char *version_string,
573 const char *name = init_file_name();
574 const char *tmp_name = init_file_tmp_name();
575 char *n2 = chase_symlinks (name);
579 /* Kludge, since these aren't in the saver_preferences struct as strings...
583 Bool overlay_stderr_p;
592 fprintf (stderr, "%s: writing \"%s\".\n", blurb(), name);
595 out = fopen(tmp_name, "w");
598 char *buf = (char *) malloc(1024 + strlen(name));
599 sprintf(buf, "%s: error writing \"%s\"", blurb(), name);
605 /* Give the new .xscreensaver file the same permissions as the old one;
606 except ensure that it is readable and writable by owner, and not
607 executable. Extra hack: if we're running as root, make the file
608 be world-readable (so that the daemon, running as "nobody", will
609 still be able to read it.)
611 if (stat(name, &st) == 0)
613 mode_t mode = st.st_mode;
614 mode |= S_IRUSR | S_IWUSR; /* read/write by user */
615 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); /* executable by none */
617 if (getuid() == (uid_t) 0) /* read by group/other */
618 mode |= S_IRGRP | S_IROTH;
620 if (fchmod (fileno(out), mode) != 0)
622 char *buf = (char *) malloc(1024 + strlen(name));
623 sprintf (buf, "%s: error fchmodding \"%s\" to 0%o", blurb(),
624 tmp_name, (unsigned int) mode);
631 /* Kludge, since these aren't in the saver_preferences struct... */
632 visual_name = get_string_resource ("visualID", "VisualID");
634 overlay_stderr_p = get_boolean_resource ("overlayStderr", "Boolean");
635 stderr_font = get_string_resource ("font", "Font");
640 char **hack_strings = (char **)
641 calloc (p->screenhacks_count, sizeof(char *));
643 for (j = 0; j < p->screenhacks_count; j++)
645 hack_strings[j] = format_hack (p->screenhacks[j], True);
646 i += strlen (hack_strings[j]);
650 ss = programs = (char *) malloc(i + 10);
652 for (j = 0; j < p->screenhacks_count; j++)
654 strcat (ss, hack_strings[j]);
655 free (hack_strings[j]);
663 struct passwd *pw = getpwuid (getuid ());
664 char *whoami = (pw && pw->pw_name && *pw->pw_name
667 time_t now = time ((time_t *) 0);
668 char *timestr = (char *) ctime (&now);
669 char *nl = (char *) strchr (timestr, '\n');
672 "# %s Preferences File\n"
673 "# Written by %s %s for %s on %s.\n"
674 "# http://www.jwz.org/xscreensaver/\n"
676 progclass, progname, version_string, whoami, timestr);
679 for (j = 0; prefs[j]; j++)
682 const char *pr = prefs[j];
683 enum pref_type { pref_str, pref_int, pref_bool, pref_time
697 # define CHECK(X) else if (!strcmp(pr, X))
699 CHECK("timeout") type = pref_time, t = p->timeout;
700 CHECK("cycle") type = pref_time, t = p->cycle;
701 CHECK("lock") type = pref_bool, b = p->lock_p;
702 # if 0 /* #### not ready yet */
703 CHECK("lockVTs") type = pref_bool, b = p->lock_vt_p;
705 CHECK("lockVTs") continue; /* don't save */
707 CHECK("lockTimeout") type = pref_time, t = p->lock_timeout;
708 CHECK("passwdTimeout") type = pref_time, t = p->passwd_timeout;
709 CHECK("visualID") type = pref_str, s = visual_name;
710 CHECK("installColormap") type = pref_bool, b = p->install_cmap_p;
711 CHECK("verbose") type = pref_bool, b = p->verbose_p;
712 CHECK("timestamp") type = pref_bool, b = p->timestamp_p;
713 CHECK("splash") type = pref_bool, b = p->splash_p;
714 CHECK("splashDuration") type = pref_time, t = p->splash_duration;
715 CHECK("demoCommand") type = pref_str, s = p->demo_command;
716 CHECK("prefsCommand") type = pref_str, s = p->prefs_command;
717 CHECK("helpURL") type = pref_str, s = p->help_url;
718 CHECK("loadURL") type = pref_str, s = p->load_url_command;
719 CHECK("nice") type = pref_int, i = p->nice_inferior;
720 CHECK("fade") type = pref_bool, b = p->fade_p;
721 CHECK("unfade") type = pref_bool, b = p->unfade_p;
722 CHECK("fadeSeconds") type = pref_time, t = p->fade_seconds;
723 CHECK("fadeTicks") type = pref_int, i = p->fade_ticks;
724 CHECK("captureStderr") type = pref_bool, b = p->capture_stderr_p;
725 CHECK("captureStdout") continue; /* don't save */
726 CHECK("font") type = pref_str, s = stderr_font;
727 CHECK("dpmsEnabled") type = pref_bool, b = p->dpms_enabled_p;
728 CHECK("dpmsStandby") type = pref_time, t = p->dpms_standby;
729 CHECK("dpmsSuspend") type = pref_time, t = p->dpms_suspend;
730 CHECK("dpmsOff") type = pref_time, t = p->dpms_off;
731 CHECK("programs") type = pref_str, s = programs;
732 CHECK("pointerPollTime") type = pref_time, t = p->pointer_timeout;
733 CHECK("windowCreationTimeout")type=pref_time,t= p->notice_events_timeout;
734 CHECK("initialDelay") type = pref_time, t = p->initial_delay;
735 CHECK("sgiSaverExtension")type = pref_bool, b=p->use_sgi_saver_extension;
736 CHECK("mitSaverExtension")type = pref_bool, b=p->use_mit_saver_extension;
737 CHECK("xidleExtension") type = pref_bool, b = p->use_xidle_extension;
738 CHECK("procInterrupts") type = pref_bool, b = p->use_proc_interrupts;
739 CHECK("overlayStderr") type = pref_bool, b = overlay_stderr_p;
740 CHECK("overlayTextBackground") continue; /* don't save */
741 CHECK("overlayTextForeground") continue; /* don't save */
742 CHECK("bourneShell") continue;
751 sprintf(buf, "%d", i);
755 s = b ? "True" : "False";
759 unsigned int hour = 0, min = 0, sec = (unsigned int) (t/1000);
770 sprintf (buf, "%u:%02u:%02u", hour, min, sec);
778 write_entry (out, pr, s);
783 if (visual_name) free(visual_name);
784 if (stderr_font) free(stderr_font);
785 if (programs) free(programs);
787 if (fclose(out) == 0)
789 time_t write_date = 0;
791 if (stat(tmp_name, &st) == 0)
793 write_date = st.st_mtime;
797 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
798 sprintf(buf, "%s: couldn't stat \"%s\"", blurb(), tmp_name);
805 if (rename (tmp_name, name) != 0)
807 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
808 sprintf(buf, "%s: error renaming \"%s\" to \"%s\"",
809 blurb(), tmp_name, name);
817 p->init_file_date = write_date;
819 /* Since the .xscreensaver file is used for IPC, let's try and make
820 sure that the bits actually land on the disk right away. */
823 status = 0; /* wrote and renamed successfully! */
828 char *buf = (char *) malloc(1024 + strlen(name));
829 sprintf(buf, "%s: error closing \"%s\"", blurb(), name);
842 /* Parsing the resource database
846 free_screenhack (screenhack *hack)
848 if (hack->visual) free (hack->visual);
849 if (hack->name) free (hack->name);
850 free (hack->command);
851 memset (hack, 0, sizeof(*hack));
856 free_screenhack_list (screenhack **list, int count)
860 for (i = 0; i < count; i++)
862 free_screenhack (list[i]);
867 /* Populate `saver_preferences' with the contents of the resource database.
868 Note that this may be called multiple times -- it is re-run each time
869 the ~/.xscreensaver file is reloaded.
871 This function can be very noisy, since it issues resource syntax errors
875 load_init_file (saver_preferences *p)
877 static Bool first_time = True;
879 screenhack **system_default_screenhacks = 0;
880 int system_default_screenhack_count = 0;
884 /* Get the programs resource before the .xscreensaver file has been
885 parsed and merged into the resource database for the first time:
886 this is the value of *programs from the app-defaults file.
887 Then clear it out so that it will be parsed again later, after
888 the init file has been read.
891 system_default_screenhacks = p->screenhacks;
892 system_default_screenhack_count = p->screenhacks_count;
894 p->screenhacks_count = 0;
897 if (parse_init_file (p) != 0) /* file might have gone away */
898 if (!first_time) return;
902 p->xsync_p = get_boolean_resource ("synchronous", "Synchronous");
903 p->verbose_p = get_boolean_resource ("verbose", "Boolean");
904 p->timestamp_p = get_boolean_resource ("timestamp", "Boolean");
905 p->lock_p = get_boolean_resource ("lock", "Boolean");
906 p->lock_vt_p = get_boolean_resource ("lockVTs", "Boolean");
907 p->fade_p = get_boolean_resource ("fade", "Boolean");
908 p->unfade_p = get_boolean_resource ("unfade", "Boolean");
909 p->fade_seconds = 1000 * get_seconds_resource ("fadeSeconds", "Time");
910 p->fade_ticks = get_integer_resource ("fadeTicks", "Integer");
911 p->install_cmap_p = get_boolean_resource ("installColormap", "Boolean");
912 p->nice_inferior = get_integer_resource ("nice", "Nice");
913 p->splash_p = get_boolean_resource ("splash", "Boolean");
914 p->capture_stderr_p = get_boolean_resource ("captureStderr", "Boolean");
916 p->initial_delay = 1000 * get_seconds_resource ("initialDelay", "Time");
917 p->splash_duration = 1000 * get_seconds_resource ("splashDuration", "Time");
918 p->timeout = 1000 * get_minutes_resource ("timeout", "Time");
919 p->lock_timeout = 1000 * get_minutes_resource ("lockTimeout", "Time");
920 p->cycle = 1000 * get_minutes_resource ("cycle", "Time");
921 p->passwd_timeout = 1000 * get_seconds_resource ("passwdTimeout", "Time");
922 p->pointer_timeout = 1000 * get_seconds_resource ("pointerPollTime", "Time");
923 p->notice_events_timeout = 1000*get_seconds_resource("windowCreationTimeout",
926 p->dpms_enabled_p = get_boolean_resource ("dpmsEnabled", "Boolean");
927 p->dpms_standby = 1000 * get_seconds_resource ("dpmsStandby", "Time");
928 p->dpms_suspend = 1000 * get_seconds_resource ("dpmsSuspend", "Time");
929 p->dpms_off = 1000 * get_seconds_resource ("dpmsOff", "Time");
931 p->shell = get_string_resource ("bourneShell", "BourneShell");
933 p->demo_command = get_string_resource("demoCommand", "URL");
934 p->prefs_command = get_string_resource("prefsCommand", "URL");
935 p->help_url = get_string_resource("helpURL", "URL");
936 p->load_url_command = get_string_resource("loadURL", "LoadURL");
939 /* If "*splash" is unset, default to true. */
941 char *s = get_string_resource ("splash", "Boolean");
948 p->use_xidle_extension = get_boolean_resource ("xidleExtension","Boolean");
949 p->use_mit_saver_extension = get_boolean_resource ("mitSaverExtension",
951 p->use_sgi_saver_extension = get_boolean_resource ("sgiSaverExtension",
953 p->use_proc_interrupts = get_boolean_resource ("procInterrupts", "Boolean");
955 /* Throttle the various timeouts to reasonable values.
957 if (p->passwd_timeout <= 0) p->passwd_timeout = 30000; /* 30 secs */
958 if (p->timeout < 10000) p->timeout = 10000; /* 10 secs */
959 if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */
960 if (p->pointer_timeout <= 0) p->pointer_timeout = 5000; /* 5 secs */
961 if (p->notice_events_timeout <= 0)
962 p->notice_events_timeout = 10000; /* 10 secs */
963 if (p->fade_seconds <= 0 || p->fade_ticks <= 0)
965 if (! p->fade_p) p->unfade_p = False;
967 /* The DPMS settings may have the value 0.
968 But if they are negative, or are a range less than 10 seconds,
969 reset them to sensible defaults. (Since that must be a mistake.)
971 if (p->dpms_standby != 0 &&
972 p->dpms_standby < 10 * 1000)
973 p->dpms_standby = 2 * 60 * 60 * 1000; /* 2 hours */
974 if (p->dpms_suspend != 0 &&
975 p->dpms_suspend < 10 * 1000)
976 p->dpms_suspend = 2 * 60 * 60 * 1000; /* 2 hours */
977 if (p->dpms_off != 0 &&
978 p->dpms_off < 10 * 1000)
979 p->dpms_off = 4 * 60 * 60 * 1000; /* 4 hours */
981 if (p->dpms_standby == 0 && /* if *all* are 0, then DPMS is disabled */
982 p->dpms_suspend == 0 &&
984 p->dpms_enabled_p = False;
987 p->watchdog_timeout = p->cycle * 0.6;
988 if (p->watchdog_timeout < 30000) p->watchdog_timeout = 30000; /* 30 secs */
989 if (p->watchdog_timeout > 3600000) p->watchdog_timeout = 3600000; /* 1 hr */
993 if (system_default_screenhack_count) /* note: first_time is also true */
995 merge_system_screenhacks (p, system_default_screenhacks,
996 system_default_screenhack_count);
997 free_screenhack_list (system_default_screenhacks,
998 system_default_screenhack_count);
999 system_default_screenhacks = 0;
1000 system_default_screenhack_count = 0;
1006 p->verbose_p = True;
1007 p->timestamp_p = True;
1008 p->initial_delay = 0;
1013 /* If there are any hacks in the system-wide defaults that are not in
1014 the ~/.xscreensaver file, add the new ones to the end of the list.
1015 This does *not* actually save the file.
1018 merge_system_screenhacks (saver_preferences *p,
1019 screenhack **system_list, int system_count)
1021 /* Yeah yeah, this is an N^2 operation, but I don't have hashtables handy,
1026 for (i = 0; i < system_count; i++)
1029 Bool matched_p = False;
1031 for (j = 0; j < p->screenhacks_count; j++)
1034 if (!system_list[i]->name)
1035 system_list[i]->name = make_hack_name (system_list[i]->command);
1037 name = p->screenhacks[j]->name;
1039 name = make_hack_name (p->screenhacks[j]->command);
1041 matched_p = !strcasecmp (name, system_list[i]->name);
1043 if (name != p->screenhacks[j]->name)
1052 /* We have an entry in the system-wide list that is not in the
1053 user's .xscreensaver file. Add it to the end.
1054 Note that p->screenhacks is a single malloc block, not a
1055 linked list, so we have to realloc it.
1057 screenhack *oh = system_list[i];
1058 screenhack *nh = (screenhack *) malloc (sizeof(screenhack));
1060 if (made_space == 0)
1063 p->screenhacks = (screenhack **)
1064 realloc (p->screenhacks,
1065 (p->screenhacks_count + made_space)
1066 * sizeof(screenhack));
1067 if (!p->screenhacks) abort();
1070 nh->enabled_p = oh->enabled_p;
1071 nh->visual = oh->visual ? strdup(oh->visual) : 0;
1072 nh->name = oh->name ? strdup(oh->name) : 0;
1073 nh->command = oh->command ? strdup(oh->command) : 0;
1075 p->screenhacks[p->screenhacks_count++] = nh;
1079 fprintf (stderr, "%s: noticed new hack: %s\n", blurb(),
1080 (nh->name ? nh->name : make_hack_name (nh->command)));
1088 /* Parsing the programs resource.
1092 parse_screenhack (const char *line)
1094 screenhack *h = (screenhack *) calloc (1, sizeof(*h));
1097 h->enabled_p = True;
1099 while (isspace(*line)) line++; /* skip whitespace */
1100 if (*line == '-') /* handle "-" */
1102 h->enabled_p = False;
1104 while (isspace(*line)) line++; /* skip whitespace */
1107 s = line; /* handle "visual:" */
1108 while (*line && *line != ':' && *line != '"' && !isspace(*line))
1114 h->visual = (char *) malloc (line-s+1);
1115 strncpy (h->visual, s, line-s);
1116 h->visual[line-s] = 0;
1117 if (*line == ':') line++; /* skip ":" */
1118 while (isspace(*line)) line++; /* skip whitespace */
1121 if (*line == '"') /* handle "name" */
1125 while (*line && *line != '"')
1127 h->name = (char *) malloc (line-s+1);
1128 strncpy (h->name, s, line-s);
1129 h->name[line-s] = 0;
1130 if (*line == '"') line++; /* skip "\"" */
1131 while (isspace(*line)) line++; /* skip whitespace */
1134 h->command = format_command (line, False); /* handle command */
1140 format_command (const char *cmd, Bool wrap_p)
1144 char *cmd2 = (char *) calloc (1, 2 * (strlen (cmd) + 1));
1145 const char *in = cmd;
1149 /* shrink all whitespace to one space, for the benefit of the "demo"
1150 mode display. We only do this when we can easily tell that the
1151 whitespace is not significant (no shell metachars).
1155 case '\'': case '"': case '`': case '\\':
1156 /* Metachars are scary. Copy the rest of the line unchanged. */
1158 *out++ = *in++, col++;
1161 case ' ': case '\t':
1162 /* Squeeze all other whitespace down to one space. */
1163 while (*in == ' ' || *in == '\t')
1165 *out++ = ' ', col++;
1169 /* Copy other chars unchanged. */
1170 *out++ = *in++, col++;
1177 /* Strip trailing whitespace */
1178 while (out > cmd2 && isspace (out[-1]))
1185 /* Returns a new string describing the shell command.
1186 This may be just the name of the program, capitalized.
1187 It also may be something from the resource database (gotten
1188 by looking for "hacks.XYZ.name", where XYZ is the program.)
1191 make_hack_name (const char *shell_command)
1193 char *s = strdup (shell_command);
1197 for (s2 = s; *s2; s2++) /* truncate at first whitespace */
1204 s2 = strrchr (s, '/'); /* if pathname, take last component */
1212 if (strlen (s) > 50) /* 51 is hereby defined as "unreasonable" */
1215 sprintf (res_name, "hacks.%s.name", s); /* resource? */
1216 s2 = get_string_resource (res_name, res_name);
1220 for (s2 = s; *s2; s2++) /* if it has any capitals, return it */
1221 if (*s2 >= 'A' && *s2 <= 'Z')
1224 if (s[0] >= 'a' && s[0] <= 'z') /* else cap it */
1226 if (s[0] == 'X' && s[1] >= 'a' && s[1] <= 'z') /* (magic leading X) */
1233 format_hack (screenhack *hack, Bool wrap_p)
1236 int size = (2 * (strlen(hack->command) +
1237 (hack->visual ? strlen(hack->visual) : 0) +
1238 (hack->name ? strlen(hack->name) : 0) +
1240 char *h2 = (char *) malloc (size);
1245 if (!hack->enabled_p) *out++ = '-'; /* write disabled flag */
1247 if (hack->visual && *hack->visual) /* write visual name */
1249 if (hack->enabled_p) *out++ = ' ';
1251 strcpy (out, hack->visual);
1252 out += strlen (hack->visual);
1258 col = string_columns (h2, strlen (h2), 0);
1260 if (hack->name && *hack->name) /* write pretty name */
1262 int L = (strlen (hack->name) + 2);
1264 out = stab_to (out, col, tab - L - 2);
1268 strcpy (out, hack->name);
1269 out += strlen (hack->name);
1273 col = string_columns (h2, strlen (h2), 0);
1274 if (wrap_p && col >= tab)
1276 out = stab_to (out, col, 77);
1277 *out += strlen(out);
1282 if (out >= h2+size) abort();
1286 col = string_columns (h2, strlen (h2), 0);
1287 out = stab_to (out, col, tab); /* indent */
1289 if (out >= h2+size) abort();
1290 s = format_command (hack->command, wrap_p);
1301 get_screenhacks (saver_preferences *p)
1309 d = get_string_resource ("monoPrograms", "MonoPrograms");
1310 if (d && !*d) { free(d); d = 0; }
1312 d = get_string_resource ("colorPrograms", "ColorPrograms");
1313 if (d && !*d) { free(d); d = 0; }
1318 "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
1319 see the manual for details.\n", blurb());
1323 d = get_string_resource ("programs", "Programs");
1325 free_screenhack_list (p->screenhacks, p->screenhacks_count);
1327 p->screenhacks_count = 0;
1335 /* Count up the number of newlines (which will be equal to or larger than
1336 the number of hacks.)
1339 for (i = 0; d[i]; i++)
1344 p->screenhacks = (screenhack **) calloc (sizeof (screenhack *), i+1);
1346 /* Iterate over the lines in `d' (the string with newlines)
1347 and make new strings to stuff into the `screenhacks' array.
1349 p->screenhacks_count = 0;
1350 while (start < size)
1352 /* skip forward over whitespace. */
1353 while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n')
1356 /* skip forward to newline or end of string. */
1358 while (d[end] != 0 && d[end] != '\n')
1361 /* null terminate. */
1364 p->screenhacks[p->screenhacks_count++] = parse_screenhack (d + start);
1365 if (p->screenhacks_count >= i)
1371 if (p->screenhacks_count == 0)
1373 free (p->screenhacks);