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