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 */
227 "chooseRandomImages",
233 "windowCreationTimeout",
240 "overlayTextBackground", /* not saved -- X resources only */
241 "overlayTextForeground", /* not saved -- X resources only */
242 "bourneShell", /* not saved -- X resources only */
250 while (*s == '\t' || *s == ' ' || *s == '\r' || *s == '\n')
252 for (s2 = s; *s2; s2++)
254 for (s2--; s2 >= s; s2--)
255 if (*s2 == '\t' || *s2 == ' ' || *s2 == '\r' || *s2 =='\n')
267 handle_entry (XrmDatabase *db, const char *key, const char *value,
268 const char *filename, int line)
271 for (i = 0; prefs[i]; i++)
272 if (*prefs[i] && !strcasecmp(key, prefs[i]))
274 char *val = strdup(value);
275 char *spec = (char *) malloc(strlen(progclass) + strlen(prefs[i]) +10);
276 strcpy(spec, progclass);
278 strcat(spec, prefs[i]);
280 XrmPutStringResource (db, spec, val);
287 fprintf(stderr, "%s: %s:%d: unknown option \"%s\"\n",
288 blurb(), filename, line, key);
294 parse_init_file (saver_preferences *p)
296 time_t write_date = 0;
297 const char *name = init_file_name();
306 if (stat(name, &st) != 0)
308 p->init_file_date = 0;
312 in = fopen(name, "r");
315 char *buf = (char *) malloc(1024 + strlen(name));
316 sprintf(buf, "%s: error reading \"%s\"", blurb(), name);
322 if (fstat (fileno(in), &st) == 0)
324 write_date = st.st_mtime;
328 char *buf = (char *) malloc(1024 + strlen(name));
329 sprintf(buf, "%s: couldn't re-stat \"%s\"", blurb(), name);
335 buf = (char *) malloc(buf_size);
337 while (fgets (buf, buf_size-1, in))
344 (buf[L-1] != '\n' || /* whole line didn't fit in buffer */
345 buf[L-2] == '\\')) /* or line ended with backslash */
347 if (buf[L-2] == '\\') /* backslash-newline gets swallowed */
353 buf = (char *) realloc(buf, buf_size);
357 if (!fgets (buf + L, buf_size-L-1, in))
362 /* Now handle other backslash escapes. */
365 for (i = 0; buf[i]; i++)
370 case 'n': buf[i] = '\n'; break;
371 case 'r': buf[i] = '\r'; break;
372 case 't': buf[i] = '\t'; break;
373 default: buf[i] = buf[i+1]; break;
375 for (j = i+2; buf[j]; j++)
383 if (*key == '#' || *key == '!' || *key == ';' ||
384 *key == '\n' || *key == 0)
387 value = strchr (key, ':');
390 fprintf(stderr, "%s: %s:%d: unparsable line: %s\n", blurb(),
397 value = strip(value);
401 handle_entry (&p->db, key, value, name, line);
406 p->init_file_date = write_date;
412 init_file_changed_p (saver_preferences *p)
414 const char *name = init_file_name();
417 if (!name) return False;
419 if (stat(name, &st) != 0)
422 if (p->init_file_date == st.st_mtime)
433 tab_to (FILE *out, int from, int to)
436 int to_mod = (to / tab_width) * tab_width;
437 while (from < to_mod)
440 from = (((from / tab_width) + 1) * tab_width);
451 stab_to (char *out, int from, int to)
454 int to_mod = (to / tab_width) * tab_width;
455 while (from < to_mod)
458 from = (((from / tab_width) + 1) * tab_width);
469 string_columns (const char *string, int length, int start)
473 const char *end = string + length;
478 else if (*string == '\t')
479 col = (((col / tab_width) + 1) * tab_width);
489 write_entry (FILE *out, const char *key, const char *value)
491 char *v = strdup(value ? value : "");
495 Bool programs_p = (!strcmp(key, "programs"));
496 int tab = (programs_p ? 32 : 16);
499 fprintf(out, "%s:", key);
500 col = strlen(key) + 1;
502 if (strlen(key) > 14)
503 col = tab_to (out, col, 20);
509 nl = strchr(v2, '\n');
513 if (first && programs_p)
515 col = tab_to (out, col, 77);
516 fprintf (out, " \\\n");
524 col = tab_to (out, col, 75);
525 fprintf (out, " \\n\\\n");
530 col = tab_to (out, col, tab);
533 string_columns(v2, strlen (v2), col) + col > 75)
540 while (v2[end] == ' ' || v2[end] == '\t')
542 while (v2[end] != ' ' && v2[end] != '\t' &&
543 v2[end] != '\n' && v2[end] != 0)
545 if (string_columns (v2 + start, (end - start), col) >= 74)
547 col = tab_to (out, col, 75);
548 fprintf(out, " \\\n");
549 col = tab_to (out, 0, tab + 2);
550 while (v2[start] == ' ' || v2[start] == '\t')
554 col = string_columns (v2 + start, (end - start), col);
556 fputc(v2[start++], out);
561 fprintf (out, "%s", v2);
562 col += string_columns(v2, strlen (v2), col);
576 write_init_file (saver_preferences *p, const char *version_string,
580 const char *name = init_file_name();
581 const char *tmp_name = init_file_tmp_name();
582 char *n2 = chase_symlinks (name);
586 /* Kludge, since these aren't in the saver_preferences struct as strings...
590 Bool overlay_stderr_p;
599 fprintf (stderr, "%s: writing \"%s\".\n", blurb(), name);
602 out = fopen(tmp_name, "w");
605 char *buf = (char *) malloc(1024 + strlen(name));
606 sprintf(buf, "%s: error writing \"%s\"", blurb(), name);
612 /* Give the new .xscreensaver file the same permissions as the old one;
613 except ensure that it is readable and writable by owner, and not
614 executable. Extra hack: if we're running as root, make the file
615 be world-readable (so that the daemon, running as "nobody", will
616 still be able to read it.)
618 if (stat(name, &st) == 0)
620 mode_t mode = st.st_mode;
621 mode |= S_IRUSR | S_IWUSR; /* read/write by user */
622 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); /* executable by none */
624 if (getuid() == (uid_t) 0) /* read by group/other */
625 mode |= S_IRGRP | S_IROTH;
627 if (fchmod (fileno(out), mode) != 0)
629 char *buf = (char *) malloc(1024 + strlen(name));
630 sprintf (buf, "%s: error fchmodding \"%s\" to 0%o", blurb(),
631 tmp_name, (unsigned int) mode);
638 /* Kludge, since these aren't in the saver_preferences struct... */
639 visual_name = get_string_resource ("visualID", "VisualID");
641 overlay_stderr_p = get_boolean_resource ("overlayStderr", "Boolean");
642 stderr_font = get_string_resource ("font", "Font");
647 char **hack_strings = (char **)
648 calloc (p->screenhacks_count, sizeof(char *));
650 for (j = 0; j < p->screenhacks_count; j++)
652 hack_strings[j] = format_hack (p->screenhacks[j], True);
653 i += strlen (hack_strings[j]);
657 ss = programs = (char *) malloc(i + 10);
659 for (j = 0; j < p->screenhacks_count; j++)
661 strcat (ss, hack_strings[j]);
662 free (hack_strings[j]);
670 struct passwd *pw = getpwuid (getuid ());
671 char *whoami = (pw && pw->pw_name && *pw->pw_name
674 time_t now = time ((time_t *) 0);
675 char *timestr = (char *) ctime (&now);
676 char *nl = (char *) strchr (timestr, '\n');
679 "# %s Preferences File\n"
680 "# Written by %s %s for %s on %s.\n"
681 "# http://www.jwz.org/xscreensaver/\n"
683 progclass, progname, version_string, whoami, timestr);
686 for (j = 0; prefs[j]; j++)
689 const char *pr = prefs[j];
690 enum pref_type { pref_str, pref_int, pref_bool, pref_time
704 # define CHECK(X) else if (!strcmp(pr, X))
706 CHECK("timeout") type = pref_time, t = p->timeout;
707 CHECK("cycle") type = pref_time, t = p->cycle;
708 CHECK("lock") type = pref_bool, b = p->lock_p;
709 # if 0 /* #### not ready yet */
710 CHECK("lockVTs") type = pref_bool, b = p->lock_vt_p;
712 CHECK("lockVTs") continue; /* don't save */
714 CHECK("lockTimeout") type = pref_time, t = p->lock_timeout;
715 CHECK("passwdTimeout") type = pref_time, t = p->passwd_timeout;
716 CHECK("visualID") type = pref_str, s = visual_name;
717 CHECK("installColormap") type = pref_bool, b = p->install_cmap_p;
718 CHECK("verbose") type = pref_bool, b = p->verbose_p;
719 CHECK("timestamp") type = pref_bool, b = p->timestamp_p;
720 CHECK("splash") type = pref_bool, b = p->splash_p;
721 CHECK("splashDuration") type = pref_time, t = p->splash_duration;
722 CHECK("demoCommand") type = pref_str, s = p->demo_command;
723 CHECK("prefsCommand") type = pref_str, s = p->prefs_command;
724 CHECK("helpURL") type = pref_str, s = p->help_url;
725 CHECK("loadURL") type = pref_str, s = p->load_url_command;
726 CHECK("nice") type = pref_int, i = p->nice_inferior;
727 CHECK("fade") type = pref_bool, b = p->fade_p;
728 CHECK("unfade") type = pref_bool, b = p->unfade_p;
729 CHECK("fadeSeconds") type = pref_time, t = p->fade_seconds;
730 CHECK("fadeTicks") type = pref_int, i = p->fade_ticks;
731 CHECK("captureStderr") type = pref_bool, b = p->capture_stderr_p;
732 CHECK("captureStdout") continue; /* don't save */
733 CHECK("font") type = pref_str, s = stderr_font;
735 CHECK("dpmsEnabled") type = pref_bool, b = p->dpms_enabled_p;
736 CHECK("dpmsStandby") type = pref_time, t = p->dpms_standby;
737 CHECK("dpmsSuspend") type = pref_time, t = p->dpms_suspend;
738 CHECK("dpmsOff") type = pref_time, t = p->dpms_off;
740 CHECK("grabDesktopImages") type =pref_bool, b = p->grab_desktop_p;
741 CHECK("grabVideoFrames") type =pref_bool, b = p->grab_video_p;
742 CHECK("chooseRandomImages")type =pref_bool, b = p->random_image_p;
743 CHECK("imageDirectory") type =pref_str, s = p->image_directory;
745 CHECK("programs") type = pref_str, s = programs;
746 CHECK("pointerPollTime") type = pref_time, t = p->pointer_timeout;
747 CHECK("windowCreationTimeout")type=pref_time,t= p->notice_events_timeout;
748 CHECK("initialDelay") type = pref_time, t = p->initial_delay;
749 CHECK("sgiSaverExtension")type = pref_bool, b=p->use_sgi_saver_extension;
750 CHECK("mitSaverExtension")type = pref_bool, b=p->use_mit_saver_extension;
751 CHECK("xidleExtension") type = pref_bool, b = p->use_xidle_extension;
752 CHECK("procInterrupts") type = pref_bool, b = p->use_proc_interrupts;
753 CHECK("overlayStderr") type = pref_bool, b = overlay_stderr_p;
754 CHECK("overlayTextBackground") continue; /* don't save */
755 CHECK("overlayTextForeground") continue; /* don't save */
756 CHECK("bourneShell") continue;
765 sprintf(buf, "%d", i);
769 s = b ? "True" : "False";
773 unsigned int hour = 0, min = 0, sec = (unsigned int) (t/1000);
784 sprintf (buf, "%u:%02u:%02u", hour, min, sec);
792 write_entry (out, pr, s);
797 if (visual_name) free(visual_name);
798 if (stderr_font) free(stderr_font);
799 if (programs) free(programs);
801 if (fclose(out) == 0)
803 time_t write_date = 0;
805 if (stat(tmp_name, &st) == 0)
807 write_date = st.st_mtime;
811 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
812 sprintf(buf, "%s: couldn't stat \"%s\"", blurb(), tmp_name);
819 if (rename (tmp_name, name) != 0)
821 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
822 sprintf(buf, "%s: error renaming \"%s\" to \"%s\"",
823 blurb(), tmp_name, name);
831 p->init_file_date = write_date;
833 /* Since the .xscreensaver file is used for IPC, let's try and make
834 sure that the bits actually land on the disk right away. */
837 status = 0; /* wrote and renamed successfully! */
842 char *buf = (char *) malloc(1024 + strlen(name));
843 sprintf(buf, "%s: error closing \"%s\"", blurb(), name);
856 /* Parsing the resource database
860 free_screenhack (screenhack *hack)
862 if (hack->visual) free (hack->visual);
863 if (hack->name) free (hack->name);
864 free (hack->command);
865 memset (hack, 0, sizeof(*hack));
870 free_screenhack_list (screenhack **list, int count)
874 for (i = 0; i < count; i++)
876 free_screenhack (list[i]);
881 /* Populate `saver_preferences' with the contents of the resource database.
882 Note that this may be called multiple times -- it is re-run each time
883 the ~/.xscreensaver file is reloaded.
885 This function can be very noisy, since it issues resource syntax errors
889 load_init_file (saver_preferences *p)
891 static Bool first_time = True;
893 screenhack **system_default_screenhacks = 0;
894 int system_default_screenhack_count = 0;
898 /* Get the programs resource before the .xscreensaver file has been
899 parsed and merged into the resource database for the first time:
900 this is the value of *programs from the app-defaults file.
901 Then clear it out so that it will be parsed again later, after
902 the init file has been read.
905 system_default_screenhacks = p->screenhacks;
906 system_default_screenhack_count = p->screenhacks_count;
908 p->screenhacks_count = 0;
911 if (parse_init_file (p) != 0) /* file might have gone away */
912 if (!first_time) return;
916 p->xsync_p = get_boolean_resource ("synchronous", "Synchronous");
917 p->verbose_p = get_boolean_resource ("verbose", "Boolean");
918 p->timestamp_p = get_boolean_resource ("timestamp", "Boolean");
919 p->lock_p = get_boolean_resource ("lock", "Boolean");
920 p->lock_vt_p = get_boolean_resource ("lockVTs", "Boolean");
921 p->fade_p = get_boolean_resource ("fade", "Boolean");
922 p->unfade_p = get_boolean_resource ("unfade", "Boolean");
923 p->fade_seconds = 1000 * get_seconds_resource ("fadeSeconds", "Time");
924 p->fade_ticks = get_integer_resource ("fadeTicks", "Integer");
925 p->install_cmap_p = get_boolean_resource ("installColormap", "Boolean");
926 p->nice_inferior = get_integer_resource ("nice", "Nice");
927 p->splash_p = get_boolean_resource ("splash", "Boolean");
928 p->capture_stderr_p = get_boolean_resource ("captureStderr", "Boolean");
930 p->initial_delay = 1000 * get_seconds_resource ("initialDelay", "Time");
931 p->splash_duration = 1000 * get_seconds_resource ("splashDuration", "Time");
932 p->timeout = 1000 * get_minutes_resource ("timeout", "Time");
933 p->lock_timeout = 1000 * get_minutes_resource ("lockTimeout", "Time");
934 p->cycle = 1000 * get_minutes_resource ("cycle", "Time");
935 p->passwd_timeout = 1000 * get_seconds_resource ("passwdTimeout", "Time");
936 p->pointer_timeout = 1000 * get_seconds_resource ("pointerPollTime", "Time");
937 p->notice_events_timeout = 1000*get_seconds_resource("windowCreationTimeout",
940 p->dpms_enabled_p = get_boolean_resource ("dpmsEnabled", "Boolean");
941 p->dpms_standby = 1000 * get_minutes_resource ("dpmsStandby", "Time");
942 p->dpms_suspend = 1000 * get_minutes_resource ("dpmsSuspend", "Time");
943 p->dpms_off = 1000 * get_minutes_resource ("dpmsOff", "Time");
945 p->grab_desktop_p = get_boolean_resource ("grabDesktopImages", "Boolean");
946 p->grab_video_p = get_boolean_resource ("grabVideoFrames", "Boolean");
947 p->random_image_p = get_boolean_resource ("chooseRandomImages", "Boolean");
948 p->image_directory = get_string_resource ("imageDirectory",
951 p->shell = get_string_resource ("bourneShell", "BourneShell");
953 p->demo_command = get_string_resource("demoCommand", "URL");
954 p->prefs_command = get_string_resource("prefsCommand", "URL");
955 p->help_url = get_string_resource("helpURL", "URL");
956 p->load_url_command = get_string_resource("loadURL", "LoadURL");
959 /* If "*splash" is unset, default to true. */
961 char *s = get_string_resource ("splash", "Boolean");
968 /* If "*grabDesktopImages" is unset, default to true. */
970 char *s = get_string_resource ("grabDesktopImages", "Boolean");
974 p->grab_desktop_p = True;
977 p->use_xidle_extension = get_boolean_resource ("xidleExtension","Boolean");
978 p->use_mit_saver_extension = get_boolean_resource ("mitSaverExtension",
980 p->use_sgi_saver_extension = get_boolean_resource ("sgiSaverExtension",
982 p->use_proc_interrupts = get_boolean_resource ("procInterrupts", "Boolean");
984 /* Throttle the various timeouts to reasonable values.
986 if (p->passwd_timeout <= 0) p->passwd_timeout = 30000; /* 30 secs */
987 if (p->timeout < 10000) p->timeout = 10000; /* 10 secs */
988 if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */
989 if (p->pointer_timeout <= 0) p->pointer_timeout = 5000; /* 5 secs */
990 if (p->notice_events_timeout <= 0)
991 p->notice_events_timeout = 10000; /* 10 secs */
992 if (p->fade_seconds <= 0 || p->fade_ticks <= 0)
994 if (! p->fade_p) p->unfade_p = False;
996 /* The DPMS settings may have the value 0.
997 But if they are negative, or are a range less than 10 seconds,
998 reset them to sensible defaults. (Since that must be a mistake.)
1000 if (p->dpms_standby != 0 &&
1001 p->dpms_standby < 10 * 1000)
1002 p->dpms_standby = 2 * 60 * 60 * 1000; /* 2 hours */
1003 if (p->dpms_suspend != 0 &&
1004 p->dpms_suspend < 10 * 1000)
1005 p->dpms_suspend = 2 * 60 * 60 * 1000; /* 2 hours */
1006 if (p->dpms_off != 0 &&
1007 p->dpms_off < 10 * 1000)
1008 p->dpms_off = 4 * 60 * 60 * 1000; /* 4 hours */
1010 if (p->dpms_standby == 0 && /* if *all* are 0, then DPMS is disabled */
1011 p->dpms_suspend == 0 &&
1013 p->dpms_enabled_p = False;
1016 p->watchdog_timeout = p->cycle * 0.6;
1017 if (p->watchdog_timeout < 30000) p->watchdog_timeout = 30000; /* 30 secs */
1018 if (p->watchdog_timeout > 3600000) p->watchdog_timeout = 3600000; /* 1 hr */
1020 get_screenhacks (p);
1022 if (system_default_screenhack_count) /* note: first_time is also true */
1024 merge_system_screenhacks (p, system_default_screenhacks,
1025 system_default_screenhack_count);
1026 free_screenhack_list (system_default_screenhacks,
1027 system_default_screenhack_count);
1028 system_default_screenhacks = 0;
1029 system_default_screenhack_count = 0;
1035 p->verbose_p = True;
1036 p->timestamp_p = True;
1037 p->initial_delay = 0;
1042 /* If there are any hacks in the system-wide defaults that are not in
1043 the ~/.xscreensaver file, add the new ones to the end of the list.
1044 This does *not* actually save the file.
1047 merge_system_screenhacks (saver_preferences *p,
1048 screenhack **system_list, int system_count)
1050 /* Yeah yeah, this is an N^2 operation, but I don't have hashtables handy,
1055 for (i = 0; i < system_count; i++)
1058 Bool matched_p = False;
1060 for (j = 0; j < p->screenhacks_count; j++)
1063 if (!system_list[i]->name)
1064 system_list[i]->name = make_hack_name (system_list[i]->command);
1066 name = p->screenhacks[j]->name;
1068 name = make_hack_name (p->screenhacks[j]->command);
1070 matched_p = !strcasecmp (name, system_list[i]->name);
1072 if (name != p->screenhacks[j]->name)
1081 /* We have an entry in the system-wide list that is not in the
1082 user's .xscreensaver file. Add it to the end.
1083 Note that p->screenhacks is a single malloc block, not a
1084 linked list, so we have to realloc it.
1086 screenhack *oh = system_list[i];
1087 screenhack *nh = (screenhack *) malloc (sizeof(screenhack));
1089 if (made_space == 0)
1092 p->screenhacks = (screenhack **)
1093 realloc (p->screenhacks,
1094 (p->screenhacks_count + made_space)
1095 * sizeof(screenhack));
1096 if (!p->screenhacks) abort();
1099 nh->enabled_p = oh->enabled_p;
1100 nh->visual = oh->visual ? strdup(oh->visual) : 0;
1101 nh->name = oh->name ? strdup(oh->name) : 0;
1102 nh->command = oh->command ? strdup(oh->command) : 0;
1104 p->screenhacks[p->screenhacks_count++] = nh;
1108 fprintf (stderr, "%s: noticed new hack: %s\n", blurb(),
1109 (nh->name ? nh->name : make_hack_name (nh->command)));
1117 /* Parsing the programs resource.
1121 parse_screenhack (const char *line)
1123 screenhack *h = (screenhack *) calloc (1, sizeof(*h));
1126 h->enabled_p = True;
1128 while (isspace(*line)) line++; /* skip whitespace */
1129 if (*line == '-') /* handle "-" */
1131 h->enabled_p = False;
1133 while (isspace(*line)) line++; /* skip whitespace */
1136 s = line; /* handle "visual:" */
1137 while (*line && *line != ':' && *line != '"' && !isspace(*line))
1143 h->visual = (char *) malloc (line-s+1);
1144 strncpy (h->visual, s, line-s);
1145 h->visual[line-s] = 0;
1146 if (*line == ':') line++; /* skip ":" */
1147 while (isspace(*line)) line++; /* skip whitespace */
1150 if (*line == '"') /* handle "name" */
1154 while (*line && *line != '"')
1156 h->name = (char *) malloc (line-s+1);
1157 strncpy (h->name, s, line-s);
1158 h->name[line-s] = 0;
1159 if (*line == '"') line++; /* skip "\"" */
1160 while (isspace(*line)) line++; /* skip whitespace */
1163 h->command = format_command (line, False); /* handle command */
1169 format_command (const char *cmd, Bool wrap_p)
1173 char *cmd2 = (char *) calloc (1, 2 * (strlen (cmd) + 1));
1174 const char *in = cmd;
1178 /* shrink all whitespace to one space, for the benefit of the "demo"
1179 mode display. We only do this when we can easily tell that the
1180 whitespace is not significant (no shell metachars).
1184 case '\'': case '"': case '`': case '\\':
1185 /* Metachars are scary. Copy the rest of the line unchanged. */
1187 *out++ = *in++, col++;
1190 case ' ': case '\t':
1191 /* Squeeze all other whitespace down to one space. */
1192 while (*in == ' ' || *in == '\t')
1194 *out++ = ' ', col++;
1198 /* Copy other chars unchanged. */
1199 *out++ = *in++, col++;
1206 /* Strip trailing whitespace */
1207 while (out > cmd2 && isspace (out[-1]))
1214 /* Returns a new string describing the shell command.
1215 This may be just the name of the program, capitalized.
1216 It also may be something from the resource database (gotten
1217 by looking for "hacks.XYZ.name", where XYZ is the program.)
1220 make_hack_name (const char *shell_command)
1222 char *s = strdup (shell_command);
1226 for (s2 = s; *s2; s2++) /* truncate at first whitespace */
1233 s2 = strrchr (s, '/'); /* if pathname, take last component */
1241 if (strlen (s) > 50) /* 51 is hereby defined as "unreasonable" */
1244 sprintf (res_name, "hacks.%s.name", s); /* resource? */
1245 s2 = get_string_resource (res_name, res_name);
1249 for (s2 = s; *s2; s2++) /* if it has any capitals, return it */
1250 if (*s2 >= 'A' && *s2 <= 'Z')
1253 if (s[0] >= 'a' && s[0] <= 'z') /* else cap it */
1255 if (s[0] == 'X' && s[1] >= 'a' && s[1] <= 'z') /* (magic leading X) */
1262 format_hack (screenhack *hack, Bool wrap_p)
1265 int size = (2 * (strlen(hack->command) +
1266 (hack->visual ? strlen(hack->visual) : 0) +
1267 (hack->name ? strlen(hack->name) : 0) +
1269 char *h2 = (char *) malloc (size);
1274 if (!hack->enabled_p) *out++ = '-'; /* write disabled flag */
1276 if (hack->visual && *hack->visual) /* write visual name */
1278 if (hack->enabled_p) *out++ = ' ';
1280 strcpy (out, hack->visual);
1281 out += strlen (hack->visual);
1287 col = string_columns (h2, strlen (h2), 0);
1289 if (hack->name && *hack->name) /* write pretty name */
1291 int L = (strlen (hack->name) + 2);
1293 out = stab_to (out, col, tab - L - 2);
1297 strcpy (out, hack->name);
1298 out += strlen (hack->name);
1302 col = string_columns (h2, strlen (h2), 0);
1303 if (wrap_p && col >= tab)
1305 out = stab_to (out, col, 77);
1306 *out += strlen(out);
1311 if (out >= h2+size) abort();
1315 col = string_columns (h2, strlen (h2), 0);
1316 out = stab_to (out, col, tab); /* indent */
1318 if (out >= h2+size) abort();
1319 s = format_command (hack->command, wrap_p);
1330 get_screenhacks (saver_preferences *p)
1338 d = get_string_resource ("monoPrograms", "MonoPrograms");
1339 if (d && !*d) { free(d); d = 0; }
1341 d = get_string_resource ("colorPrograms", "ColorPrograms");
1342 if (d && !*d) { free(d); d = 0; }
1347 "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
1348 see the manual for details.\n", blurb());
1352 d = get_string_resource ("programs", "Programs");
1354 free_screenhack_list (p->screenhacks, p->screenhacks_count);
1356 p->screenhacks_count = 0;
1364 /* Count up the number of newlines (which will be equal to or larger than
1365 the number of hacks.)
1368 for (i = 0; d[i]; i++)
1373 p->screenhacks = (screenhack **) calloc (sizeof (screenhack *), i+1);
1375 /* Iterate over the lines in `d' (the string with newlines)
1376 and make new strings to stuff into the `screenhacks' array.
1378 p->screenhacks_count = 0;
1379 while (start < size)
1381 /* skip forward over whitespace. */
1382 while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n')
1385 /* skip forward to newline or end of string. */
1387 while (d[end] != 0 && d[end] != '\n')
1390 /* null terminate. */
1393 p->screenhacks[p->screenhacks_count++] = parse_screenhack (d + start);
1394 if (p->screenhacks_count >= i)
1400 if (p->screenhacks_count == 0)
1402 free (p->screenhacks);