1 /* dotfile.c --- management of the ~/.xscreensaver file.
2 * xscreensaver, Copyright (c) 1998-2020 Jamie Zawinski <jwz@jwz.org>
3 *
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
10 * implied warranty.
11 */
12
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16
17 #include <stdlib.h>
18
19 #ifdef HAVE_UNISTD_H
20 # include <unistd.h>
21 #endif
22
23 #include <stdio.h>
24 #include <ctype.h>
25 #include <string.h>
26 #include <time.h>
27 #include <sys/stat.h>
28 #include <sys/time.h>
29 #include <sys/param.h> /* for PATH_MAX */
30
31 #include <X11/Xlib.h>
32 #include <X11/Xresource.h>
33
34 #ifndef VMS
35 # include <pwd.h>
36 #else /* VMS */
37 # include "vms-pwd.h"
38 #endif /* VMS */
39
40
41 /* This file doesn't need the Xt headers, so stub these types out... */
42 #undef XtPointer
43 #define XtAppContext void*
44 #define XtIntervalId void*
45 #define XtPointer void*
46 #define Widget void*
47
48
49 /* Just in case there's something pathological about stat.h... */
50 #ifndef S_IRUSR
51 # define S_IRUSR 00400
52 #endif
53 #ifndef S_IWUSR
54 # define S_IWUSR 00200
55 #endif
56 #ifndef S_IXUSR
57 # define S_IXUSR 00100
58 #endif
59 #ifndef S_IXGRP
60 # define S_IXGRP 00010
61 #endif
62 #ifndef S_IXOTH
63 # define S_IXOTH 00001
64 #endif
65
66
67 #include "version.h"
68 #include "prefs.h"
69 #include "resources.h"
70
71 /* don't use realpath() on fedora system */
72 #ifdef _FORTIFY_SOURCE
73 #undef HAVE_REALPATH
74 #endif
75
76
77 extern char *progname;
78 extern char *progclass;
79 extern const char *blurb (void);
80
81
82
83 static void get_screenhacks (Display *, saver_preferences *);
84 static char *format_command (const char *cmd, Bool wrap_p);
85 static void merge_system_screenhacks (Display *, saver_preferences *,
86 screenhack **system_list, int count);
87 static void stop_the_insanity (saver_preferences *p);
88
89
90 static char *
chase_symlinks(const char * file)91 chase_symlinks (const char *file)
92 {
93 # ifdef HAVE_REALPATH
94 if (file)
95 {
96 # ifndef PATH_MAX
97 # ifdef MAXPATHLEN
98 # define PATH_MAX MAXPATHLEN
99 # else
100 # define PATH_MAX 2048
101 # endif
102 # endif
103 char buf[PATH_MAX];
104 if (realpath (file, buf))
105 return strdup (buf);
106
107 /* sprintf (buf, "%.100s: realpath %.200s", blurb(), file);
108 perror(buf);*/
109 }
110 # endif /* HAVE_REALPATH */
111 return 0;
112 }
113
114
115 static Bool
i_am_a_nobody(uid_t uid)116 i_am_a_nobody (uid_t uid)
117 {
118 struct passwd *p;
119
120 p = getpwnam ("nobody");
121 if (! p) p = getpwnam ("noaccess");
122 if (! p) p = getpwnam ("daemon");
123
124 if (! p) /* There is no nobody? */
125 return False;
126
127 return (uid == p->pw_uid);
128 }
129
130
131 const char *
init_file_name(void)132 init_file_name (void)
133 {
134 static char *file = 0;
135
136 if (!file)
137 {
138 uid_t uid = getuid ();
139 const char *home = getenv("HOME");
140
141 if (i_am_a_nobody (uid) || !home || !*home)
142 {
143 /* If we're running as nobody, then use root's .xscreensaver file
144 (since ~root/.xscreensaver and ~nobody/.xscreensaver are likely
145 to be different -- if we didn't do this, then xscreensaver-demo
146 would appear to have no effect when the luser is running as root.)
147 */
148 struct passwd *p = getpwuid (uid);
149 uid = 0;
150 if (!p || !p->pw_name || !*p->pw_name)
151 {
152 fprintf (stderr, "%s: couldn't get user info of uid %d\n",
153 blurb(), getuid ());
154 }
155 else if (!p->pw_dir || !*p->pw_dir)
156 {
157 fprintf (stderr, "%s: couldn't get home directory of \"%s\"\n",
158 blurb(), (p->pw_name ? p->pw_name : "???"));
159 }
160 else
161 {
162 home = p->pw_dir;
163 }
164 }
165 if (home && *home)
166 {
167 const char *name = ".xscreensaver";
168 file = (char *) malloc(strlen(home) + strlen(name) + 2);
169 strcpy(file, home);
170 if (!*home || home[strlen(home)-1] != '/')
171 strcat(file, "/");
172 strcat(file, name);
173 }
174 else
175 {
176 file = "";
177 }
178 }
179
180 if (file && *file)
181 return file;
182 else
183 return 0;
184 }
185
186
187 static const char *
init_file_tmp_name(void)188 init_file_tmp_name (void)
189 {
190 static char *file = 0;
191 if (!file)
192 {
193 const char *name = init_file_name();
194 const char *suffix = ".tmp";
195
196 char *n2 = chase_symlinks (name);
197 if (n2) name = n2;
198
199 if (!name || !*name)
200 file = "";
201 else
202 {
203 file = (char *) malloc(strlen(name) + strlen(suffix) + 2);
204 strcpy(file, name);
205 strcat(file, suffix);
206 }
207
208 if (n2) free (n2);
209 }
210
211 if (file && *file)
212 return file;
213 else
214 return 0;
215 }
216
217 static int
get_byte_resource(Display * dpy,char * name,char * class)218 get_byte_resource (Display *dpy, char *name, char *class)
219 {
220 char *s = get_string_resource (dpy, name, class);
221 char *s2 = s;
222 int n = 0;
223 if (!s) return 0;
224
225 while (isspace(*s2)) s2++;
226 while (*s2 >= '0' && *s2 <= '9')
227 {
228 n = (n * 10) + (*s2 - '0');
229 s2++;
230 }
231 while (isspace(*s2)) s2++;
232 if (*s2 == 'k' || *s2 == 'K') n <<= 10;
233 else if (*s2 == 'm' || *s2 == 'M') n <<= 20;
234 else if (*s2 == 'g' || *s2 == 'G') n <<= 30;
235 else if (*s2)
236 {
237 LOSE:
238 fprintf (stderr, "%s: %s must be a number of bytes, not \"%s\".\n",
239 progname, name, s);
240 free (s);
241 return 0;
242 }
243 s2++;
244 if (*s2 == 'b' || *s2 == 'B') s2++;
245 while (isspace(*s2)) s2++;
246 if (*s2) goto LOSE;
247
248 free (s);
249 return n;
250 }
251
252
253 static const char * const prefs[] = {
254 "timeout",
255 "cycle",
256 "lock",
257 "lockVTs", /* not saved */
258 "lockTimeout",
259 "passwdTimeout",
260 "visualID",
261 "installColormap",
262 "verbose",
263 "timestamp",
264 "splash",
265 "splashDuration",
266 "quad",
267 "demoCommand",
268 "prefsCommand",
269 "newLoginCommand",
270 "helpURL", /* not saved */
271 "loadURL", /* not saved */
272 "newLoginCommand", /* not saved */
273 "nice",
274 "memoryLimit",
275 "fade",
276 "unfade",
277 "fadeSeconds",
278 "fadeTicks",
279 "captureStderr",
280 "captureStdout", /* not saved -- obsolete */
281 "logFile", /* not saved */
282 "ignoreUninstalledPrograms",
283 "font",
284 "dpmsEnabled",
285 "dpmsQuickOff",
286 "dpmsStandby",
287 "dpmsSuspend",
288 "dpmsOff",
289 "grabDesktopImages",
290 "grabVideoFrames",
291 "chooseRandomImages",
292 "imageDirectory",
293 "mode",
294 "selected",
295 "textMode",
296 "textLiteral",
297 "textFile",
298 "textProgram",
299 "textURL",
300 "",
301 "programs",
302 "",
303 "pointerPollTime",
304 "pointerHysteresis",
305 "windowCreationTimeout",
306 "initialDelay",
307 "sgiSaverExtension", /* not saved -- obsolete */
308 "mitSaverExtension", /* not saved -- obsolete */
309 "xidleExtension", /* not saved -- obsolete */
310 "GetViewPortIsFullOfLies",
311 "procInterrupts",
312 "xinputExtensionDev",
313 "overlayStderr",
314 "overlayTextBackground", /* not saved -- X resources only */
315 "overlayTextForeground", /* not saved -- X resources only */
316 "bourneShell", /* not saved -- X resources only */
317 "authWarningSlack",
318 0
319 };
320
321 static char *
strip(char * s)322 strip (char *s)
323 {
324 char *s2;
325 while (*s == '\t' || *s == ' ' || *s == '\r' || *s == '\n')
326 s++;
327 for (s2 = s; *s2; s2++)
328 ;
329 for (s2--; s2 >= s; s2--)
330 if (*s2 == '\t' || *s2 == ' ' || *s2 == '\r' || *s2 =='\n')
331 *s2 = 0;
332 else
333 break;
334 return s;
335 }
336
337
338 /* Reading
339 */
340
341 static int
handle_entry(XrmDatabase * db,const char * key,const char * value,const char * filename,int line)342 handle_entry (XrmDatabase *db, const char *key, const char *value,
343 const char *filename, int line)
344 {
345 int i;
346 for (i = 0; prefs[i]; i++)
347 if (*prefs[i] && !strcasecmp(key, prefs[i]))
348 {
349 char *val = strdup(value);
350 char *spec = (char *) malloc(strlen(progclass) + strlen(prefs[i]) +10);
351 strcpy(spec, progclass);
352 strcat(spec, ".");
353 strcat(spec, prefs[i]);
354
355 XrmPutStringResource (db, spec, val);
356
357 free(spec);
358 free(val);
359 return 0;
360 }
361
362 fprintf(stderr, "%s: %s:%d: unknown option \"%s\"\n",
363 blurb(), filename, line, key);
364 return 1;
365 }
366
367
368 static int
parse_init_file(saver_preferences * p)369 parse_init_file (saver_preferences *p)
370 {
371 time_t write_date = 0;
372 const char *name = init_file_name();
373 int line = 0;
374 struct stat st;
375 FILE *in;
376 int buf_size = 1024;
377 char *buf;
378
379 if (!name) return 0;
380
381 if (stat(name, &st) != 0)
382 {
383 p->init_file_date = 0;
384 return 0;
385 }
386
387 in = fopen(name, "r");
388 if (!in)
389 {
390 char *buf = (char *) malloc(1024 + strlen(name));
391 sprintf(buf, "%s: error reading \"%s\"", blurb(), name);
392 perror(buf);
393 free(buf);
394 return -1;
395 }
396
397 if (fstat (fileno(in), &st) == 0)
398 {
399 write_date = st.st_mtime;
400 }
401 else
402 {
403 char *buf = (char *) malloc(1024 + strlen(name));
404 sprintf(buf, "%s: couldn't re-stat \"%s\"", blurb(), name);
405 perror(buf);
406 free(buf);
407 return -1;
408 }
409
410 buf = (char *) malloc(buf_size);
411
412 while (fgets (buf, buf_size-1, in))
413 {
414 char *key, *value;
415 int L = strlen(buf);
416
417 line++;
418 while (L > 2 &&
419 (buf[L-1] != '\n' || /* whole line didn't fit in buffer */
420 buf[L-2] == '\\')) /* or line ended with backslash */
421 {
422 if (buf[L-2] == '\\') /* backslash-newline gets swallowed */
423 {
424 buf[L-2] = 0;
425 L -= 2;
426 }
427 buf_size += 1024;
428 buf = (char *) realloc(buf, buf_size);
429 if (!buf) exit(1);
430
431 line++;
432 if (!fgets (buf + L, buf_size-L-1, in))
433 break;
434 L = strlen(buf);
435 }
436
437 /* Now handle other backslash escapes. */
438 {
439 int i, j;
440 for (i = 0; buf[i]; i++)
441 if (buf[i] == '\\')
442 {
443 switch (buf[i+1])
444 {
445 case 'n': buf[i] = '\n'; break;
446 case 'r': buf[i] = '\r'; break;
447 case 't': buf[i] = '\t'; break;
448 default: buf[i] = buf[i+1]; break;
449 }
450 for (j = i+2; buf[j]; j++)
451 buf[j-1] = buf[j];
452 buf[j-1] = 0;
453 }
454 }
455
456 key = strip(buf);
457
458 if (*key == '#' || *key == '!' || *key == ';' ||
459 *key == '\n' || *key == 0)
460 continue;
461
462 value = strchr (key, ':');
463 if (!value)
464 {
465 fprintf(stderr, "%s: %s:%d: unparsable line: %s\n", blurb(),
466 name, line, key);
467 continue;
468 }
469 else
470 {
471 *value++ = 0;
472 value = strip(value);
473 }
474
475 if (!p->db) abort();
476 handle_entry (&p->db, key, value, name, line);
477 }
478 fclose (in);
479 free(buf);
480
481 p->init_file_date = write_date;
482 return 0;
483 }
484
485
486 Bool
init_file_changed_p(saver_preferences * p)487 init_file_changed_p (saver_preferences *p)
488 {
489 const char *name = init_file_name();
490 struct stat st;
491
492 if (!name) return False;
493
494 if (stat(name, &st) != 0)
495 return False;
496
497 if (p->init_file_date == st.st_mtime)
498 return False;
499
500 return True;
501 }
502
503
504 /* Writing
505 */
506
507 static int
tab_to(FILE * out,int from,int to)508 tab_to (FILE *out, int from, int to)
509 {
510 int tab_width = 8;
511 int to_mod = (to / tab_width) * tab_width;
512 while (from < to_mod)
513 {
514 fprintf(out, "\t");
515 from = (((from / tab_width) + 1) * tab_width);
516 }
517 while (from < to)
518 {
519 fprintf(out, " ");
520 from++;
521 }
522 return from;
523 }
524
525 static char *
stab_to(char * out,int from,int to)526 stab_to (char *out, int from, int to)
527 {
528 int tab_width = 8;
529 int to_mod = (to / tab_width) * tab_width;
530 while (from < to_mod)
531 {
532 *out++ = '\t';
533 from = (((from / tab_width) + 1) * tab_width);
534 }
535 while (from < to)
536 {
537 *out++ = ' ';
538 from++;
539 }
540 return out;
541 }
542
543 static int
string_columns(const char * string,int length,int start)544 string_columns (const char *string, int length, int start)
545 {
546 int tab_width = 8;
547 int col = start;
548 const char *end = string + length;
549 while (string < end)
550 {
551 if (*string == '\n')
552 col = 0;
553 else if (*string == '\t')
554 col = (((col / tab_width) + 1) * tab_width);
555 else
556 col++;
557 string++;
558 }
559 return col;
560 }
561
562
563 static void
write_entry(FILE * out,const char * key,const char * value)564 write_entry (FILE *out, const char *key, const char *value)
565 {
566 char *v = strdup(value ? value : "");
567 char *v2 = v;
568 char *nl = 0;
569 int col;
570 Bool programs_p = (!strcmp(key, "programs"));
571 int tab = (programs_p ? 32 : 16);
572 Bool first = True;
573
574 fprintf(out, "%s:", key);
575 col = strlen(key) + 1;
576
577 if (strlen(key) > 14)
578 col = tab_to (out, col, 20);
579
580 while (1)
581 {
582 if (!programs_p)
583 v2 = strip(v2);
584 nl = strchr(v2, '\n');
585 if (nl)
586 *nl = 0;
587
588 if (first && programs_p)
589 {
590 col = tab_to (out, col, 77);
591 fprintf (out, " \\\n");
592 col = 0;
593 }
594
595 if (first)
596 first = False;
597 else
598 {
599 col = tab_to (out, col, 75);
600 fprintf (out, " \\n\\\n");
601 col = 0;
602 }
603
604 if (!programs_p)
605 col = tab_to (out, col, tab);
606
607 if (programs_p &&
608 string_columns(v2, strlen (v2), col) + col > 75)
609 {
610 int L = strlen (v2);
611 int start = 0;
612 int end = start;
613 while (start < L)
614 {
615 while (v2[end] == ' ' || v2[end] == '\t')
616 end++;
617 while (v2[end] != ' ' && v2[end] != '\t' &&
618 v2[end] != '\n' && v2[end] != 0)
619 end++;
620 if (string_columns (v2 + start, (end - start), col) >= 74)
621 {
622 col = tab_to (out, col, 75);
623 fprintf(out, " \\\n");
624 col = tab_to (out, 0, tab + 2);
625 while (v2[start] == ' ' || v2[start] == '\t')
626 start++;
627 }
628
629 col = string_columns (v2 + start, (end - start), col);
630 while (start < end)
631 fputc(v2[start++], out);
632 }
633 }
634 else
635 {
636 fprintf (out, "%s", v2);
637 col += string_columns(v2, strlen (v2), col);
638 }
639
640 if (nl)
641 v2 = nl + 1;
642 else
643 break;
644 }
645
646 fprintf(out, "\n");
647 free(v);
648 }
649
650 int
write_init_file(Display * dpy,saver_preferences * p,const char * version_string,Bool verbose_p)651 write_init_file (Display *dpy,
652 saver_preferences *p, const char *version_string,
653 Bool verbose_p)
654 {
655 int status = -1;
656 const char *name = init_file_name();
657 const char *tmp_name = init_file_tmp_name();
658 char *n2 = chase_symlinks (name);
659 struct stat st;
660 int i, j;
661
662 /* Kludge, since these aren't in the saver_preferences struct as strings...
663 */
664 char *visual_name;
665 char *programs;
666 Bool overlay_stderr_p;
667 char *stderr_font;
668 FILE *out;
669
670 if (!name) goto END;
671
672 if (n2) name = n2;
673
674 /* Throttle the various timeouts to reasonable values before writing
675 the file to disk. */
676 stop_the_insanity (p);
677
678
679 if (verbose_p)
680 fprintf (stderr, "%s: writing \"%s\".\n", blurb(), name);
681
682 unlink (tmp_name);
683 out = fopen(tmp_name, "w");
684 if (!out)
685 {
686 char *buf = (char *) malloc(1024 + strlen(name));
687 sprintf(buf, "%s: error writing \"%s\"", blurb(), name);
688 perror(buf);
689 free(buf);
690 goto END;
691 }
692
693 /* Give the new .xscreensaver file the same permissions as the old one;
694 except ensure that it is readable and writable by owner, and not
695 executable. Extra hack: if we're running as root, make the file
696 be world-readable (so that the daemon, running as "nobody", will
697 still be able to read it.)
698 */
699 if (stat(name, &st) == 0)
700 {
701 mode_t mode = st.st_mode;
702 mode |= S_IRUSR | S_IWUSR; /* read/write by user */
703 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); /* executable by none */
704
705 if (getuid() == (uid_t) 0) /* read by group/other */
706 mode |= S_IRGRP | S_IROTH;
707
708 if (fchmod (fileno(out), mode) != 0)
709 {
710 char *buf = (char *) malloc(1024 + strlen(name));
711 sprintf (buf, "%s: error fchmodding \"%s\" to 0%o", blurb(),
712 tmp_name, (unsigned int) mode);
713 perror(buf);
714 free(buf);
715 goto END;
716 }
717 }
718
719 /* Kludge, since these aren't in the saver_preferences struct... */
720 visual_name = get_string_resource (dpy, "visualID", "VisualID");
721 programs = 0;
722 overlay_stderr_p = get_boolean_resource (dpy, "overlayStderr", "Boolean");
723 stderr_font = get_string_resource (dpy, "font", "Font");
724
725 i = 0;
726 {
727 char *ss;
728 char **hack_strings = (char **)
729 calloc (p->screenhacks_count, sizeof(char *));
730
731 for (j = 0; j < p->screenhacks_count; j++)
732 {
733 hack_strings[j] = format_hack (dpy, p->screenhacks[j], True);
734 i += strlen (hack_strings[j]);
735 i += 2;
736 }
737
738 ss = programs = (char *) malloc(i + 10);
739 *ss = 0;
740 for (j = 0; j < p->screenhacks_count; j++)
741 {
742 strcat (ss, hack_strings[j]);
743 free (hack_strings[j]);
744 ss += strlen(ss);
745 *ss++ = '\n';
746 *ss = 0;
747 }
748 free (hack_strings);
749 }
750
751 {
752 struct passwd *pw = getpwuid (getuid ());
753 char *whoami = (pw && pw->pw_name && *pw->pw_name
754 ? pw->pw_name
755 : "<unknown>");
756 time_t now = time ((time_t *) 0);
757 char *timestr = (char *) ctime (&now);
758 char *nl = (char *) strchr (timestr, '\n');
759 if (nl) *nl = 0;
760 fprintf (out,
761 "# %s Preferences File\n"
762 "# Written by %s %s for %s on %s.\n"
763 "# https://www.jwz.org/xscreensaver/\n"
764 "\n",
765 progclass, progname, version_string, whoami, timestr);
766 }
767
768 for (j = 0; prefs[j]; j++)
769 {
770 char buf[255];
771 const char *pr = prefs[j];
772 enum pref_type { pref_str, pref_int, pref_bool, pref_byte, pref_time
773 } type = pref_str;
774 const char *s = 0;
775 int i = 0;
776 Bool b = False;
777 Time t = 0;
778
779 if (pr && !*pr)
780 {
781 fprintf(out, "\n");
782 continue;
783 }
784
785 # undef CHECK
786 # define CHECK(X) else if (!strcmp(pr, X))
787 if (!pr || !*pr) ;
788 CHECK("timeout") type = pref_time, t = p->timeout;
789 CHECK("cycle") type = pref_time, t = p->cycle;
790 CHECK("lock") type = pref_bool, b = p->lock_p;
791 CHECK("lockVTs") continue; /* don't save, unused */
792 CHECK("lockTimeout") type = pref_time, t = p->lock_timeout;
793 CHECK("passwdTimeout") type = pref_time, t = p->passwd_timeout;
794 CHECK("visualID") type = pref_str, s = visual_name;
795 CHECK("installColormap") type = pref_bool, b = p->install_cmap_p;
796 CHECK("verbose") type = pref_bool, b = p->verbose_p;
797 CHECK("timestamp") type = pref_bool, b = p->timestamp_p;
798 CHECK("splash") type = pref_bool, b = p->splash_p;
799 CHECK("splashDuration") type = pref_time, t = p->splash_duration;
800 # ifdef QUAD_MODE
801 CHECK("quad") type = pref_bool, b = p->quad_p;
802 # else /* !QUAD_MODE */
803 CHECK("quad") continue; /* don't save */
804 # endif /* !QUAD_MODE */
805 CHECK("demoCommand") type = pref_str, s = p->demo_command;
806 CHECK("prefsCommand") type = pref_str, s = p->prefs_command;
807 /* CHECK("helpURL") type = pref_str, s = p->help_url; */
808 CHECK("helpURL") continue; /* don't save */
809 /* CHECK("loadURL") type = pref_str, s = p->load_url_command; */
810 CHECK("loadURL") continue; /* don't save */
811 /* CHECK("newLoginCommand") type = pref_str, s = p->new_login_command; */
812 CHECK("newLoginCommand") continue; /* don't save */
813 CHECK("nice") type = pref_int, i = p->nice_inferior;
814 CHECK("memoryLimit") type = pref_byte, i = p->inferior_memory_limit;
815 CHECK("fade") type = pref_bool, b = p->fade_p;
816 CHECK("unfade") type = pref_bool, b = p->unfade_p;
817 CHECK("fadeSeconds") type = pref_time, t = p->fade_seconds;
818 CHECK("fadeTicks") type = pref_int, i = p->fade_ticks;
819 CHECK("captureStderr") type = pref_bool, b = p->capture_stderr_p;
820 CHECK("captureStdout") continue; /* don't save */
821 CHECK("logFile") continue; /* don't save */
822 CHECK("ignoreUninstalledPrograms")
823 type = pref_bool, b = p->ignore_uninstalled_p;
824
825 CHECK("font") type = pref_str, s = stderr_font;
826
827 CHECK("dpmsEnabled") type = pref_bool, b = p->dpms_enabled_p;
828 CHECK("dpmsQuickOff") type = pref_bool, b = p->dpms_quickoff_p;
829 CHECK("dpmsStandby") type = pref_time, t = p->dpms_standby;
830 CHECK("dpmsSuspend") type = pref_time, t = p->dpms_suspend;
831 CHECK("dpmsOff") type = pref_time, t = p->dpms_off;
832
833 CHECK("grabDesktopImages") type =pref_bool, b = p->grab_desktop_p;
834 CHECK("grabVideoFrames") type =pref_bool, b = p->grab_video_p;
835 CHECK("chooseRandomImages")type =pref_bool, b = p->random_image_p;
836 CHECK("imageDirectory") type =pref_str, s = p->image_directory;
837
838 CHECK("mode") type = pref_str,
839 s = (p->mode == ONE_HACK ? "one" :
840 p->mode == BLANK_ONLY ? "blank" :
841 p->mode == DONT_BLANK ? "off" :
842 p->mode == RANDOM_HACKS_SAME
843 ? "random-same"
844 : "random");
845 CHECK("selected") type = pref_int, i = p->selected_hack;
846
847 CHECK("textMode") type = pref_str,
848 s = (p->tmode == TEXT_URL ? "url" :
849 p->tmode == TEXT_LITERAL ? "literal" :
850 p->tmode == TEXT_FILE ? "file" :
851 p->tmode == TEXT_PROGRAM ? "program" :
852 "date");
853 CHECK("textLiteral") type = pref_str, s = p->text_literal;
854 CHECK("textFile") type = pref_str, s = p->text_file;
855 CHECK("textProgram") type = pref_str, s = p->text_program;
856 CHECK("textURL") type = pref_str, s = p->text_url;
857
858 CHECK("programs") type = pref_str, s = programs;
859 CHECK("pointerPollTime") type = pref_time, t = p->pointer_timeout;
860 CHECK("pointerHysteresis")type = pref_int, i = p->pointer_hysteresis;
861 CHECK("windowCreationTimeout")type=pref_time,t= p->notice_events_timeout;
862 CHECK("initialDelay") type = pref_time, t = p->initial_delay;
863 CHECK("sgiSaverExtension") continue; /* don't save */
864 CHECK("mitSaverExtension") continue; /* don't save */
865 CHECK("xidleExtension") continue; /* don't save */
866 CHECK("procInterrupts") type = pref_bool, b = p->use_proc_interrupts;
867 CHECK("xinputExtensionDev") type = pref_bool, b = p->use_xinput_extension;
868 CHECK("GetViewPortIsFullOfLies") type = pref_bool,
869 b = p->getviewport_full_of_lies_p;
870 CHECK("overlayStderr") type = pref_bool, b = overlay_stderr_p;
871 CHECK("overlayTextBackground") continue; /* don't save */
872 CHECK("overlayTextForeground") continue; /* don't save */
873 CHECK("bourneShell") continue; /* don't save */
874 CHECK("authWarningSlack") type = pref_int, i = p->auth_warning_slack;
875 else abort();
876 # undef CHECK
877
878 switch (type)
879 {
880 case pref_str:
881 break;
882 case pref_int:
883 sprintf(buf, "%d", i);
884 s = buf;
885 break;
886 case pref_bool:
887 s = b ? "True" : "False";
888 break;
889 case pref_time:
890 {
891 unsigned int hour = 0, min = 0, sec = (unsigned int) (t/1000);
892 if (sec >= 60)
893 {
894 min += (sec / 60);
895 sec %= 60;
896 }
897 if (min >= 60)
898 {
899 hour += (min / 60);
900 min %= 60;
901 }
902 sprintf (buf, "%u:%02u:%02u", hour, min, sec);
903 s = buf;
904 }
905 break;
906 case pref_byte:
907 {
908 if (i >= (1<<30) && i == ((i >> 30) << 30))
909 sprintf(buf, "%dG", i >> 30);
910 else if (i >= (1<<20) && i == ((i >> 20) << 20))
911 sprintf(buf, "%dM", i >> 20);
912 else if (i >= (1<<10) && i == ((i >> 10) << 10))
913 sprintf(buf, "%dK", i >> 10);
914 else
915 sprintf(buf, "%d", i);
916 s = buf;
917 }
918 break;
919 default:
920 abort();
921 break;
922 }
923
924 if (pr && (!strcmp(pr, "mode") || !strcmp(pr, "textMode")))
925 fprintf(out, "\n");
926
927 write_entry (out, pr, s);
928 }
929
930 fprintf(out, "\n");
931
932 if (visual_name) free(visual_name);
933 if (stderr_font) free(stderr_font);
934 if (programs) free(programs);
935
936 if (fclose(out) == 0)
937 {
938 time_t write_date = 0;
939
940 if (stat(tmp_name, &st) == 0)
941 {
942 write_date = st.st_mtime;
943 }
944 else
945 {
946 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
947 sprintf(buf, "%s: couldn't stat \"%s\"", blurb(), tmp_name);
948 perror(buf);
949 unlink (tmp_name);
950 free(buf);
951 goto END;
952 }
953
954 if (rename (tmp_name, name) != 0)
955 {
956 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
957 sprintf(buf, "%s: error renaming \"%s\" to \"%s\"",
958 blurb(), tmp_name, name);
959 perror(buf);
960 unlink (tmp_name);
961 free(buf);
962 goto END;
963 }
964 else
965 {
966 p->init_file_date = write_date;
967
968 /* Since the .xscreensaver file is used for IPC, let's try and make
969 sure that the bits actually land on the disk right away. */
970 sync ();
971
972 status = 0; /* wrote and renamed successfully! */
973 }
974 }
975 else
976 {
977 char *buf = (char *) malloc(1024 + strlen(name));
978 sprintf(buf, "%s: error closing \"%s\"", blurb(), name);
979 perror(buf);
980 free(buf);
981 unlink (tmp_name);
982 goto END;
983 }
984
985 END:
986 if (n2) free (n2);
987 return status;
988 }
989
990
991 /* Parsing the resource database
992 */
993
994 void
free_screenhack(screenhack * hack)995 free_screenhack (screenhack *hack)
996 {
997 if (hack->visual) free (hack->visual);
998 if (hack->name) free (hack->name);
999 free (hack->command);
1000 memset (hack, 0, sizeof(*hack));
1001 free (hack);
1002 }
1003
1004 static void
free_screenhack_list(screenhack ** list,int count)1005 free_screenhack_list (screenhack **list, int count)
1006 {
1007 int i;
1008 if (!list) return;
1009 for (i = 0; i < count; i++)
1010 if (list[i])
1011 free_screenhack (list[i]);
1012 free (list);
1013 }
1014
1015
1016
1017 /* Populate `saver_preferences' with the contents of the resource database.
1018 Note that this may be called multiple times -- it is re-run each time
1019 the ~/.xscreensaver file is reloaded.
1020
1021 This function can be very noisy, since it issues resource syntax errors
1022 and so on.
1023 */
1024 void
load_init_file(Display * dpy,saver_preferences * p)1025 load_init_file (Display *dpy, saver_preferences *p)
1026 {
1027 static Bool first_time = True;
1028
1029 screenhack **system_default_screenhacks = 0;
1030 int system_default_screenhack_count = 0;
1031
1032 if (first_time)
1033 {
1034 /* Get the programs resource before the .xscreensaver file has been
1035 parsed and merged into the resource database for the first time:
1036 this is the value of *programs from the app-defaults file.
1037 Then clear it out so that it will be parsed again later, after
1038 the init file has been read.
1039 */
1040 get_screenhacks (dpy, p);
1041 system_default_screenhacks = p->screenhacks;
1042 system_default_screenhack_count = p->screenhacks_count;
1043 p->screenhacks = 0;
1044 p->screenhacks_count = 0;
1045 }
1046
1047 if (parse_init_file (p) != 0) /* file might have gone away */
1048 if (!first_time) return;
1049
1050 first_time = False;
1051
1052 p->xsync_p = get_boolean_resource (dpy, "synchronous", "Synchronous");
1053 p->verbose_p = get_boolean_resource (dpy, "verbose", "Boolean");
1054 p->timestamp_p = get_boolean_resource (dpy, "timestamp", "Boolean");
1055 p->lock_p = get_boolean_resource (dpy, "lock", "Boolean");
1056 p->fade_p = get_boolean_resource (dpy, "fade", "Boolean");
1057 p->unfade_p = get_boolean_resource (dpy, "unfade", "Boolean");
1058 p->fade_seconds = 1000 * get_seconds_resource (dpy, "fadeSeconds", "Time");
1059 p->fade_ticks = get_integer_resource (dpy, "fadeTicks", "Integer");
1060 p->install_cmap_p = get_boolean_resource (dpy, "installColormap", "Boolean");
1061 p->nice_inferior = get_integer_resource (dpy, "nice", "Nice");
1062 p->inferior_memory_limit = get_byte_resource (dpy, "memoryLimit",
1063 "MemoryLimit");
1064 p->splash_p = get_boolean_resource (dpy, "splash", "Boolean");
1065 # ifdef QUAD_MODE
1066 p->quad_p = get_boolean_resource (dpy, "quad", "Boolean");
1067 # endif
1068 p->capture_stderr_p = get_boolean_resource (dpy, "captureStderr", "Boolean");
1069 p->ignore_uninstalled_p = get_boolean_resource (dpy,
1070 "ignoreUninstalledPrograms",
1071 "Boolean");
1072
1073 p->initial_delay = 1000 * get_seconds_resource (dpy, "initialDelay", "Time");
1074 p->splash_duration = 1000 * get_seconds_resource (dpy, "splashDuration", "Time");
1075 p->timeout = 1000 * get_minutes_resource (dpy, "timeout", "Time");
1076 p->lock_timeout = 1000 * get_minutes_resource (dpy, "lockTimeout", "Time");
1077 p->cycle = 1000 * get_minutes_resource (dpy, "cycle", "Time");
1078 p->passwd_timeout = 1000 * get_seconds_resource (dpy, "passwdTimeout", "Time");
1079 p->pointer_timeout = 1000 * get_seconds_resource (dpy, "pointerPollTime", "Time");
1080 p->pointer_hysteresis = get_integer_resource (dpy, "pointerHysteresis","Integer");
1081 p->notice_events_timeout = 1000*get_seconds_resource(dpy,
1082 "windowCreationTimeout",
1083 "Time");
1084
1085 p->dpms_enabled_p = get_boolean_resource (dpy, "dpmsEnabled", "Boolean");
1086 p->dpms_quickoff_p = get_boolean_resource (dpy, "dpmsQuickOff", "Boolean");
1087 p->dpms_standby = 1000 * get_minutes_resource (dpy, "dpmsStandby", "Time");
1088 p->dpms_suspend = 1000 * get_minutes_resource (dpy, "dpmsSuspend", "Time");
1089 p->dpms_off = 1000 * get_minutes_resource (dpy, "dpmsOff", "Time");
1090
1091 p->grab_desktop_p = get_boolean_resource (dpy, "grabDesktopImages", "Boolean");
1092 p->grab_video_p = get_boolean_resource (dpy, "grabVideoFrames", "Boolean");
1093 p->random_image_p = get_boolean_resource (dpy, "chooseRandomImages", "Boolean");
1094 p->image_directory = get_string_resource (dpy,
1095 "imageDirectory",
1096 "ImageDirectory");
1097
1098 p->text_literal = get_string_resource (dpy, "textLiteral", "TextLiteral");
1099 p->text_file = get_string_resource (dpy, "textFile", "TextFile");
1100 p->text_program = get_string_resource (dpy, "textProgram", "TextProgram");
1101 p->text_url = get_string_resource (dpy, "textURL", "TextURL");
1102
1103 p->shell = get_string_resource (dpy, "bourneShell", "BourneShell");
1104
1105 p->demo_command = get_string_resource(dpy, "demoCommand", "URL");
1106 p->prefs_command = get_string_resource(dpy, "prefsCommand", "URL");
1107 p->help_url = get_string_resource(dpy, "helpURL", "URL");
1108 p->load_url_command = get_string_resource(dpy, "loadURL", "LoadURL");
1109 p->new_login_command = get_string_resource(dpy,
1110 "newLoginCommand",
1111 "NewLoginCommand");
1112 p->auth_warning_slack = get_integer_resource(dpy, "authWarningSlack",
1113 "Integer");
1114
1115 /* If "*splash" is unset, default to true. */
1116 {
1117 char *s = get_string_resource (dpy, "splash", "Boolean");
1118 if (s)
1119 free (s);
1120 else
1121 p->splash_p = True;
1122 }
1123
1124 /* If "*grabDesktopImages" is unset, default to true. */
1125 {
1126 char *s = get_string_resource (dpy, "grabDesktopImages", "Boolean");
1127 if (s)
1128 free (s);
1129 else
1130 p->grab_desktop_p = True;
1131 }
1132
1133 p->use_xidle_extension = get_boolean_resource (dpy, "xidleExtension","Boolean");
1134 #if 0 /* obsolete. */
1135 p->use_sgi_saver_extension = get_boolean_resource (dpy,
1136 "sgiSaverExtension",
1137 "Boolean");
1138 #endif
1139 #ifdef HAVE_XINPUT
1140 p->use_xinput_extension = get_boolean_resource (dpy, "xinputExtensionDev",
1141 "Boolean");
1142 #endif
1143 #if 0 /* broken and evil. */
1144 p->use_mit_saver_extension = get_boolean_resource (dpy,
1145 "mitSaverExtension",
1146 "Boolean");
1147 #endif
1148
1149 p->use_proc_interrupts = get_boolean_resource (dpy,
1150 "procInterrupts", "Boolean");
1151
1152 p->getviewport_full_of_lies_p =
1153 get_boolean_resource (dpy, "GetViewPortIsFullOfLies", "Boolean");
1154
1155 get_screenhacks (dpy, p); /* Parse the "programs" resource. */
1156
1157 {
1158 char *s = get_string_resource (dpy, "selected", "Integer");
1159 if (!s || !*s)
1160 p->selected_hack = -1;
1161 else
1162 p->selected_hack = get_integer_resource (dpy, "selected", "Integer");
1163 if (s) free (s);
1164 if (p->selected_hack < 0 || p->selected_hack >= p->screenhacks_count)
1165 p->selected_hack = -1;
1166 }
1167
1168 {
1169 char *s = get_string_resource (dpy, "mode", "Mode");
1170 if (s && !strcasecmp (s, "one")) p->mode = ONE_HACK;
1171 else if (s && !strcasecmp (s, "blank")) p->mode = BLANK_ONLY;
1172 else if (s && !strcasecmp (s, "off")) p->mode = DONT_BLANK;
1173 else if (s && !strcasecmp (s, "random-same")) p->mode = RANDOM_HACKS_SAME;
1174 else p->mode = RANDOM_HACKS;
1175 if (s) free (s);
1176 }
1177
1178 {
1179 char *s = get_string_resource (dpy, "textMode", "TextMode");
1180 if (s && !strcasecmp (s, "url")) p->tmode = TEXT_URL;
1181 else if (s && !strcasecmp (s, "literal")) p->tmode = TEXT_LITERAL;
1182 else if (s && !strcasecmp (s, "file")) p->tmode = TEXT_FILE;
1183 else if (s && !strcasecmp (s, "program")) p->tmode = TEXT_PROGRAM;
1184 else p->tmode = TEXT_DATE;
1185 if (s) free (s);
1186 }
1187
1188 if (system_default_screenhack_count) /* note: first_time is also true */
1189 {
1190 merge_system_screenhacks (dpy, p, system_default_screenhacks,
1191 system_default_screenhack_count);
1192 free_screenhack_list (system_default_screenhacks,
1193 system_default_screenhack_count);
1194 system_default_screenhacks = 0;
1195 system_default_screenhack_count = 0;
1196 }
1197
1198 if (p->debug_p)
1199 {
1200 p->xsync_p = True;
1201 p->verbose_p = True;
1202 p->timestamp_p = True;
1203 p->initial_delay = 0;
1204 }
1205
1206 /* Throttle the various timeouts to reasonable values after reading the
1207 disk file. */
1208 stop_the_insanity (p);
1209 }
1210
1211
1212 /* If there are any hacks in the system-wide defaults that are not in
1213 the ~/.xscreensaver file, add the new ones to the end of the list.
1214 This does *not* actually save the file.
1215 */
1216 static void
merge_system_screenhacks(Display * dpy,saver_preferences * p,screenhack ** system_list,int system_count)1217 merge_system_screenhacks (Display *dpy, saver_preferences *p,
1218 screenhack **system_list, int system_count)
1219 {
1220 /* Yeah yeah, this is an N^2 operation, but I don't have hashtables handy,
1221 so fuck it. */
1222
1223 int made_space = 0;
1224 int i;
1225 for (i = 0; i < system_count; i++)
1226 {
1227 int j;
1228 Bool matched_p = False;
1229
1230 for (j = 0; j < p->screenhacks_count; j++)
1231 {
1232 char *name;
1233 if (!system_list[i]->name)
1234 system_list[i]->name = make_hack_name (dpy,
1235 system_list[i]->command);
1236
1237 name = p->screenhacks[j]->name;
1238 if (!name)
1239 name = make_hack_name (dpy, p->screenhacks[j]->command);
1240
1241 matched_p = !strcasecmp (name, system_list[i]->name);
1242
1243 if (name != p->screenhacks[j]->name)
1244 free (name);
1245
1246 if (matched_p)
1247 break;
1248 }
1249
1250 if (!matched_p)
1251 {
1252 /* We have an entry in the system-wide list that is not in the
1253 user's .xscreensaver file. Add it to the end.
1254 Note that p->screenhacks is a single malloc block, not a
1255 linked list, so we have to realloc it.
1256 */
1257 screenhack *oh = system_list[i];
1258 screenhack *nh = (screenhack *) malloc (sizeof(screenhack));
1259
1260 if (made_space == 0)
1261 {
1262 made_space = 10;
1263 p->screenhacks = (screenhack **)
1264 realloc (p->screenhacks,
1265 (p->screenhacks_count + made_space + 1)
1266 * sizeof(screenhack));
1267 if (!p->screenhacks) abort();
1268 }
1269
1270 nh->enabled_p = oh->enabled_p;
1271 nh->visual = oh->visual ? strdup(oh->visual) : 0;
1272 nh->name = oh->name ? strdup(oh->name) : 0;
1273 nh->command = oh->command ? strdup(oh->command) : 0;
1274
1275 p->screenhacks[p->screenhacks_count++] = nh;
1276 p->screenhacks[p->screenhacks_count] = 0;
1277 made_space--;
1278
1279 #if 0
1280 fprintf (stderr, "%s: noticed new hack: %s\n", blurb(),
1281 (nh->name ? nh->name : make_hack_name (dpy, nh->command)));
1282 #endif
1283 }
1284 }
1285 }
1286
1287
1288
1289 /* Parsing the programs resource.
1290 */
1291
1292 screenhack *
parse_screenhack(const char * line)1293 parse_screenhack (const char *line)
1294 {
1295 screenhack *h = (screenhack *) calloc (1, sizeof(*h));
1296 const char *s;
1297
1298 h->enabled_p = True;
1299
1300 while (isspace(*line)) line++; /* skip whitespace */
1301 if (*line == '-') /* handle "-" */
1302 {
1303 h->enabled_p = False;
1304 line++;
1305 while (isspace(*line)) line++; /* skip whitespace */
1306 }
1307
1308 s = line; /* handle "visual:" */
1309 while (*line && *line != ':' && *line != '"' && !isspace(*line))
1310 line++;
1311 if (*line != ':')
1312 line = s;
1313 else
1314 {
1315 h->visual = (char *) malloc (line-s+1);
1316 strncpy (h->visual, s, line-s);
1317 h->visual[line-s] = 0;
1318 if (*line == ':') line++; /* skip ":" */
1319 while (isspace(*line)) line++; /* skip whitespace */
1320 }
1321
1322 if (*line == '"') /* handle "name" */
1323 {
1324 line++;
1325 s = line;
1326 while (*line && *line != '"')
1327 line++;
1328 h->name = (char *) malloc (line-s+1);
1329 strncpy (h->name, s, line-s);
1330 h->name[line-s] = 0;
1331 if (*line == '"') line++; /* skip "\"" */
1332 while (isspace(*line)) line++; /* skip whitespace */
1333 }
1334
1335 h->command = format_command (line, False); /* handle command */
1336 return h;
1337 }
1338
1339
1340 static char *
format_command(const char * cmd,Bool wrap_p)1341 format_command (const char *cmd, Bool wrap_p)
1342 {
1343 int tab = 30;
1344 int col = tab;
1345 char *cmd2 = (char *) calloc (1, 2 * (strlen (cmd) + 1));
1346 const char *in = cmd;
1347 char *out = cmd2;
1348 while (*in)
1349 {
1350 /* shrink all whitespace to one space, for the benefit of the "demo"
1351 mode display. We only do this when we can easily tell that the
1352 whitespace is not significant (no shell metachars).
1353 */
1354 switch (*in)
1355 {
1356 case '\'': case '"': case '`': case '\\':
1357 /* Metachars are scary. Copy the rest of the line unchanged. */
1358 while (*in)
1359 *out++ = *in++, col++;
1360 break;
1361
1362 case ' ': case '\t':
1363 /* Squeeze all other whitespace down to one space. */
1364 while (*in == ' ' || *in == '\t')
1365 in++;
1366 *out++ = ' ', col++;
1367 break;
1368
1369 default:
1370 /* Copy other chars unchanged. */
1371 *out++ = *in++, col++;
1372 break;
1373 }
1374 }
1375
1376 *out = 0;
1377
1378 /* Strip trailing whitespace */
1379 while (out > cmd2 && isspace (out[-1]))
1380 *(--out) = 0;
1381
1382 return cmd2;
1383 }
1384
1385
1386 /* Returns a new string describing the shell command.
1387 This may be just the name of the program, capitalized.
1388 It also may be something from the resource database (gotten
1389 by looking for "hacks.XYZ.name", where XYZ is the program.)
1390 */
1391 char *
make_hack_name(Display * dpy,const char * shell_command)1392 make_hack_name (Display *dpy, const char *shell_command)
1393 {
1394 char *s = strdup (shell_command);
1395 char *s2;
1396 char res_name[255];
1397
1398 for (s2 = s; *s2; s2++) /* truncate at first whitespace */
1399 if (isspace (*s2))
1400 {
1401 *s2 = 0;
1402 break;
1403 }
1404
1405 s2 = strrchr (s, '/'); /* if pathname, take last component */
1406 if (s2)
1407 {
1408 s2 = strdup (s2+1);
1409 free (s);
1410 s = s2;
1411 }
1412
1413 if (strlen (s) > 50) /* 51 is hereby defined as "unreasonable" */
1414 s[50] = 0;
1415
1416 sprintf (res_name, "hacks.%s.name", s); /* resource? */
1417 s2 = get_string_resource (dpy, res_name, res_name);
1418 if (s2)
1419 {
1420 free (s);
1421 return s2;
1422 }
1423
1424 for (s2 = s; *s2; s2++) /* if it has any capitals, return it */
1425 if (*s2 >= 'A' && *s2 <= 'Z')
1426 return s;
1427
1428 if (s[0] >= 'a' && s[0] <= 'z') /* else cap it */
1429 s[0] -= 'a'-'A';
1430 if (s[0] == 'X' && s[1] >= 'a' && s[1] <= 'z') /* (magic leading X) */
1431 s[1] -= 'a'-'A';
1432 if (s[0] == 'G' && s[1] == 'l' &&
1433 s[2] >= 'a' && s[2] <= 'z') /* (magic leading GL) */
1434 s[1] -= 'a'-'A',
1435 s[2] -= 'a'-'A';
1436 return s;
1437 }
1438
1439
1440 char *
format_hack(Display * dpy,screenhack * hack,Bool wrap_p)1441 format_hack (Display *dpy, screenhack *hack, Bool wrap_p)
1442 {
1443 int tab = 32;
1444 int size;
1445 char *h2, *out, *s;
1446 int col = 0;
1447
1448 char *def_name = make_hack_name (dpy, hack->command);
1449
1450 /* Don't ever write out a name for a hack if it's the same as the default.
1451 */
1452 if (hack->name && !strcmp (hack->name, def_name))
1453 {
1454 free (hack->name);
1455 hack->name = 0;
1456 }
1457 free (def_name);
1458
1459 size = (2 * (strlen(hack->command) +
1460 (hack->visual ? strlen(hack->visual) : 0) +
1461 (hack->name ? strlen(hack->name) : 0) +
1462 tab));
1463 h2 = (char *) malloc (size);
1464 out = h2;
1465
1466 if (!hack->enabled_p) *out++ = '-'; /* write disabled flag */
1467
1468 if (hack->visual && *hack->visual) /* write visual name */
1469 {
1470 if (hack->enabled_p) *out++ = ' ';
1471 *out++ = ' ';
1472 strcpy (out, hack->visual);
1473 out += strlen (hack->visual);
1474 *out++ = ':';
1475 *out++ = ' ';
1476 }
1477
1478 *out = 0;
1479 col = string_columns (h2, strlen (h2), 0);
1480
1481 if (hack->name && *hack->name) /* write pretty name */
1482 {
1483 int L = (strlen (hack->name) + 2);
1484 if (L + col < tab)
1485 out = stab_to (out, col, tab - L - 2);
1486 else
1487 *out++ = ' ';
1488 *out++ = '"';
1489 strcpy (out, hack->name);
1490 out += strlen (hack->name);
1491 *out++ = '"';
1492 *out = 0;
1493
1494 col = string_columns (h2, strlen (h2), 0);
1495 if (wrap_p && col >= tab)
1496 out = stab_to (out, col, 77);
1497 else
1498 *out++ = ' ';
1499
1500 if (out >= h2+size) abort();
1501 }
1502
1503 *out = 0;
1504 col = string_columns (h2, strlen (h2), 0);
1505 out = stab_to (out, col, tab); /* indent */
1506
1507 if (out >= h2+size) abort();
1508 s = format_command (hack->command, wrap_p);
1509 strcpy (out, s);
1510 out += strlen (s);
1511 free (s);
1512 *out = 0;
1513
1514 return h2;
1515 }
1516
1517
1518 static void
get_screenhacks(Display * dpy,saver_preferences * p)1519 get_screenhacks (Display *dpy, saver_preferences *p)
1520 {
1521 int i, j;
1522 int start = 0;
1523 int end = 0;
1524 int size;
1525 char *d;
1526
1527 d = get_string_resource (dpy, "monoPrograms", "MonoPrograms");
1528 if (d && !*d) { free(d); d = 0; }
1529 if (!d)
1530 d = get_string_resource (dpy, "colorPrograms", "ColorPrograms");
1531 if (d && !*d) { free(d); d = 0; }
1532
1533 if (d)
1534 {
1535 fprintf (stderr,
1536 "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
1537 see the manual for details.\n", blurb());
1538 free(d);
1539 }
1540
1541 d = get_string_resource (dpy, "programs", "Programs");
1542
1543 free_screenhack_list (p->screenhacks, p->screenhacks_count);
1544 p->screenhacks = 0;
1545 p->screenhacks_count = 0;
1546
1547 if (!d || !*d)
1548 return;
1549
1550 size = strlen (d);
1551
1552
1553 /* Count up the number of newlines (which will be equal to or larger than
1554 one less than the number of hacks.)
1555 */
1556 for (i = j = 0; d[i]; i++)
1557 if (d[i] == '\n')
1558 j++;
1559 j++;
1560
1561 p->screenhacks = (screenhack **) calloc (j + 1, sizeof (screenhack *));
1562
1563 /* Iterate over the lines in `d' (the string with newlines)
1564 and make new strings to stuff into the `screenhacks' array.
1565 */
1566 p->screenhacks_count = 0;
1567 while (start < size)
1568 {
1569 /* skip forward over whitespace. */
1570 while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n')
1571 start++;
1572
1573 /* skip forward to newline or end of string. */
1574 end = start;
1575 while (d[end] != 0 && d[end] != '\n')
1576 end++;
1577
1578 /* null terminate. */
1579 d[end] = 0;
1580
1581 p->screenhacks[p->screenhacks_count++] = parse_screenhack (d + start);
1582 if (p->screenhacks_count >= i)
1583 abort();
1584
1585 start = end+1;
1586 }
1587
1588 free (d);
1589
1590 if (p->screenhacks_count == 0)
1591 {
1592 free (p->screenhacks);
1593 p->screenhacks = 0;
1594 }
1595 }
1596
1597
1598 /* Make sure all the values in the preferences struct are sane.
1599 */
1600 static void
stop_the_insanity(saver_preferences * p)1601 stop_the_insanity (saver_preferences *p)
1602 {
1603 if (p->passwd_timeout <= 0) p->passwd_timeout = 30000; /* 30 secs */
1604 if (p->timeout < 15000) p->timeout = 15000; /* 15 secs */
1605 if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */
1606 if (p->pointer_timeout <= 0) p->pointer_timeout = 5000; /* 5 secs */
1607 if (p->notice_events_timeout <= 0)
1608 p->notice_events_timeout = 10000; /* 10 secs */
1609 if (p->fade_seconds <= 0 || p->fade_ticks <= 0)
1610 p->fade_p = False;
1611 if (! p->fade_p) p->unfade_p = False;
1612
1613 /* The DPMS settings may have the value 0.
1614 But if they are negative, or are a range less than 10 seconds,
1615 reset them to sensible defaults. (Since that must be a mistake.)
1616 */
1617 if (p->dpms_standby != 0 &&
1618 p->dpms_standby < 10 * 1000)
1619 p->dpms_standby = 2 * 60 * 60 * 1000; /* 2 hours */
1620 if (p->dpms_suspend != 0 &&
1621 p->dpms_suspend < 10 * 1000)
1622 p->dpms_suspend = 2 * 60 * 60 * 1000; /* 2 hours */
1623 if (p->dpms_off != 0 &&
1624 p->dpms_off < 10 * 1000)
1625 p->dpms_off = 4 * 60 * 60 * 1000; /* 4 hours */
1626
1627 /* suspend may not be greater than off, unless off is 0.
1628 standby may not be greater than suspend, unless suspend is 0.
1629 */
1630 if (p->dpms_off != 0 &&
1631 p->dpms_suspend > p->dpms_off)
1632 p->dpms_suspend = p->dpms_off;
1633 if (p->dpms_suspend != 0 &&
1634 p->dpms_standby > p->dpms_suspend)
1635 p->dpms_standby = p->dpms_suspend;
1636
1637 /* These fixes above ignores the case
1638 suspend = 0 and standby > off ...
1639 */
1640 if (p->dpms_off != 0 &&
1641 p->dpms_standby > p->dpms_off)
1642 p->dpms_standby = p->dpms_off;
1643
1644
1645 if (p->dpms_standby == 0 && /* if *all* are 0, then DPMS is disabled */
1646 p->dpms_suspend == 0 &&
1647 p->dpms_off == 0 &&
1648 !(p->dpms_quickoff_p) /* ... but we want to do DPMS quick off */
1649 )
1650 p->dpms_enabled_p = False;
1651
1652
1653 /* Set watchdog timeout to about half of the cycle timeout, but
1654 don't let it be faster than 1/2 minute or slower than 1 minute.
1655 */
1656 p->watchdog_timeout = p->cycle * 0.6;
1657 if (p->watchdog_timeout < 27000) p->watchdog_timeout = 27000; /* 27 secs */
1658 if (p->watchdog_timeout > 57000) p->watchdog_timeout = 57000; /* 57 secs */
1659
1660 if (p->pointer_hysteresis < 0) p->pointer_hysteresis = 0;
1661 /* if (p->pointer_hysteresis > 100) p->pointer_hysteresis = 100; */
1662
1663 if (p->auth_warning_slack < 0) p->auth_warning_slack = 0;
1664 if (p->auth_warning_slack > 300) p->auth_warning_slack = 300;
1665 }
1666
1667
1668 Bool
senesculent_p(void)1669 senesculent_p (void)
1670 {
1671 /* If you are in here because you're planning on disabling this warning
1672 before redistributing my software, please don't.
1673
1674 I sincerely request that you do one of the following:
1675
1676 1: leave this code intact and this warning in place, -OR-
1677
1678 2: Remove xscreensaver from your distribution.
1679
1680 I would seriously prefer that you not distribute my software at all
1681 than that you distribute one version and then never update it for
1682 years.
1683
1684 I am *constantly* getting email from users reporting bugs that have
1685 been fixed for literally years who have no idea that the software
1686 they are running is years out of date. Yes, it would be great if we
1687 lived in the ideal world where people checked that they were running
1688 the latest release before they report a bug, but we don't. To most
1689 people, "running the latest release" is synonymous with "running the
1690 latest release that my distro packages for me."
1691
1692 When they even bother to tell me what version they're running, I
1693 say, "That version is three years old!", and they say "But this is
1694 the latest version my distro ships". Then I say, "your distro
1695 sucks", and they say "but I don't know how to compile from source,
1696 herp derp I eat paste", and *everybody* goes away unhappy.
1697
1698 It wastes an enormous amount of my time, but worse than that, it
1699 does a grave disservice to the users, who are stuck experiencing
1700 bugs that are already fixed! These users think they are running the
1701 latest release, and they are not. They would like to be running the
1702 actual latest release, but they don't know how, because their distro
1703 makes that very difficult for them. It's terrible for everyone, and
1704 kind of makes me regret ever having released this software in the
1705 first place.
1706
1707 So seriously. I ask that if you're planning on disabling this
1708 obsolescence warning, that you instead just remove xscreensaver from
1709 your distro entirely. Everybody will be happier that way. Check
1710 out gnome-screensaver instead, I understand it's really nice.
1711
1712 Of course, my license allows you to ignore me and do whatever the
1713 fuck you want, but as the author, I hope you will have the common
1714 courtesy of complying with my request.
1715
1716 Thank you!
1717
1718 jwz, 2014, 2016, 2018.
1719
1720 PS: In particular, since Debian refuses to upgrade software on any
1721 kind of rational timeline, I have asked that they stop shipping
1722 xscreensaver at all. They have refused. Instead of upgrading the
1723 software, they simply patched out this warning.
1724
1725 If you want to witness the sad state of the open source peanut
1726 gallery, look no farther than the comments on my blog:
1727 http://jwz.org/b/yiYo
1728
1729 Many of these people fall back on their go-to argument of, "If it is
1730 legal, it must be right." If you believe in that rhetorical device
1731 then you are a terrible person, and possibly a sociopath.
1732
1733 There are also the armchair lawyers who say "Well, instead of
1734 *asking* people to do the right thing out of common courtesy, you
1735 should just change your license to prohibit them from acting
1736 amorally." Again, this is the answer of a sociopath, but that aside,
1737 if you devote even a second's thought to this you will realize that
1738 the end result of this would be for distros like Debian to just keep
1739 shipping the last version with the old license and then never
1740 upgrading it again -- which would be the worst possible outcome for
1741 everyone involved, most especially the users.
1742 */
1743
1744 time_t now = time ((time_t *) 0); /* d */
1745 struct tm *tm = localtime (&now); /* o */
1746 const char *s = screensaver_id; /* n */
1747 char mon[4], year[5]; /* ' */
1748 int m, y, mrnths; /* t */
1749 s = strchr (s, ' '); if (!s) abort(); s++; /* */
1750 s = strchr (s, '('); if (!s) abort(); s++; /* d */
1751 s = strchr (s, '-'); if (!s) abort(); s++; /* o */
1752 strncpy (mon, s, 3); /* o */
1753 mon[3] = 0; /* */
1754 s = strchr (s, '-'); if (!s) abort(); s++; /* e */
1755 strncpy (year, s, 4); /* e */
1756 year[4] = 0; /* t */
1757 y = atoi (year); /* , */
1758 if (!strcmp(mon, "Jan")) m = 0; /* */
1759 else if (!strcmp(mon, "Feb")) m = 1; /* s */
1760 else if (!strcmp(mon, "Mar")) m = 2; /* t */
1761 else if (!strcmp(mon, "Apr")) m = 3; /* o */
1762 else if (!strcmp(mon, "May")) m = 4; /* p */
1763 else if (!strcmp(mon, "Jun")) m = 5; /* , */
1764 else if (!strcmp(mon, "Jul")) m = 6; /* */
1765 else if (!strcmp(mon, "Aug")) m = 7; /* s */
1766 else if (!strcmp(mon, "Sep")) m = 8; /* t */
1767 else if (!strcmp(mon, "Oct")) m = 9; /* a */
1768 else if (!strcmp(mon, "Nov")) m = 10; /* a */
1769 else if (!strcmp(mon, "Dec")) m = 11; /* a */
1770 else abort(); /* h */
1771 mrnths = ((((tm->tm_year + 1900) * 12) + tm->tm_mon) - /* h */
1772 (y * 12 + m)); /* h */
1773 /* p */
1774 return (mrnths >= 17); /* . */
1775 }
1776