Permalink
Cannot retrieve contributors at this time
Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign up
Fetching contributors…
| /* dotfile.c --- management of the ~/.xscreensaver file. | |
| * xscreensaver, Copyright (c) 1998-2014 Jamie Zawinski <jwz@jwz.org> | |
| * | |
| * Permission to use, copy, modify, distribute, and sell this software and its | |
| * documentation for any purpose is hereby granted without fee, provided that | |
| * the above copyright notice appear in all copies and that both that | |
| * copyright notice and this permission notice appear in supporting | |
| * documentation. No representations are made about the suitability of this | |
| * software for any purpose. It is provided "as is" without express or | |
| * implied warranty. | |
| */ | |
| #ifdef HAVE_CONFIG_H | |
| # include "config.h" | |
| #endif | |
| #include <stdlib.h> | |
| #ifdef HAVE_UNISTD_H | |
| # include <unistd.h> | |
| #endif | |
| #include <stdio.h> | |
| #include <ctype.h> | |
| #include <string.h> | |
| #include <time.h> | |
| #include <sys/stat.h> | |
| #include <sys/time.h> | |
| #include <sys/param.h> /* for PATH_MAX */ | |
| #include <X11/Xlib.h> | |
| #include <X11/Xresource.h> | |
| #ifndef VMS | |
| # include <pwd.h> | |
| #else /* VMS */ | |
| # include "vms-pwd.h" | |
| #endif /* VMS */ | |
| /* This file doesn't need the Xt headers, so stub these types out... */ | |
| #undef XtPointer | |
| #define XtAppContext void* | |
| #define XtIntervalId void* | |
| #define XtPointer void* | |
| #define Widget void* | |
| /* Just in case there's something pathological about stat.h... */ | |
| #ifndef S_IRUSR | |
| # define S_IRUSR 00400 | |
| #endif | |
| #ifndef S_IWUSR | |
| # define S_IWUSR 00200 | |
| #endif | |
| #ifndef S_IXUSR | |
| # define S_IXUSR 00100 | |
| #endif | |
| #ifndef S_IXGRP | |
| # define S_IXGRP 00010 | |
| #endif | |
| #ifndef S_IXOTH | |
| # define S_IXOTH 00001 | |
| #endif | |
| #include "version.h" | |
| #include "prefs.h" | |
| #include "resources.h" | |
| /* don't use realpath() on fedora system */ | |
| #ifdef _FORTIFY_SOURCE | |
| #undef HAVE_REALPATH | |
| #endif | |
| extern char *progname; | |
| extern char *progclass; | |
| extern const char *blurb (void); | |
| static void get_screenhacks (Display *, saver_preferences *); | |
| static char *format_command (const char *cmd, Bool wrap_p); | |
| static void merge_system_screenhacks (Display *, saver_preferences *, | |
| screenhack **system_list, int count); | |
| static void stop_the_insanity (saver_preferences *p); | |
| static char * | |
| chase_symlinks (const char *file) | |
| { | |
| # ifdef HAVE_REALPATH | |
| if (file) | |
| { | |
| # ifndef PATH_MAX | |
| # ifdef MAXPATHLEN | |
| # define PATH_MAX MAXPATHLEN | |
| # else | |
| # define PATH_MAX 2048 | |
| # endif | |
| # endif | |
| char buf[PATH_MAX]; | |
| if (realpath (file, buf)) | |
| return strdup (buf); | |
| /* sprintf (buf, "%.100s: realpath %.200s", blurb(), file); | |
| perror(buf);*/ | |
| } | |
| # endif /* HAVE_REALPATH */ | |
| return 0; | |
| } | |
| static Bool | |
| i_am_a_nobody (uid_t uid) | |
| { | |
| struct passwd *p; | |
| p = getpwnam ("nobody"); | |
| if (! p) p = getpwnam ("noaccess"); | |
| if (! p) p = getpwnam ("daemon"); | |
| if (! p) /* There is no nobody? */ | |
| return False; | |
| return (uid == p->pw_uid); | |
| } | |
| const char * | |
| init_file_name (void) | |
| { | |
| static char *file = 0; | |
| if (!file) | |
| { | |
| uid_t uid = getuid (); | |
| struct passwd *p = getpwuid (uid); | |
| if (i_am_a_nobody (uid)) | |
| /* If we're running as nobody, then use root's .xscreensaver file | |
| (since ~root/.xscreensaver and ~nobody/.xscreensaver are likely | |
| to be different -- if we didn't do this, then xscreensaver-demo | |
| would appear to have no effect when the luser is running as root.) | |
| */ | |
| uid = 0; | |
| p = getpwuid (uid); | |
| if (!p || !p->pw_name || !*p->pw_name) | |
| { | |
| fprintf (stderr, "%s: couldn't get user info of uid %d\n", | |
| blurb(), getuid ()); | |
| file = ""; | |
| } | |
| else if (!p->pw_dir || !*p->pw_dir) | |
| { | |
| fprintf (stderr, "%s: couldn't get home directory of \"%s\"\n", | |
| blurb(), (p->pw_name ? p->pw_name : "???")); | |
| file = ""; | |
| } | |
| else | |
| { | |
| const char *home = p->pw_dir; | |
| const char *name = ".xscreensaver"; | |
| file = (char *) malloc(strlen(home) + strlen(name) + 2); | |
| strcpy(file, home); | |
| if (!*home || home[strlen(home)-1] != '/') | |
| strcat(file, "/"); | |
| strcat(file, name); | |
| } | |
| } | |
| if (file && *file) | |
| return file; | |
| else | |
| return 0; | |
| } | |
| static const char * | |
| init_file_tmp_name (void) | |
| { | |
| static char *file = 0; | |
| if (!file) | |
| { | |
| const char *name = init_file_name(); | |
| const char *suffix = ".tmp"; | |
| char *n2 = chase_symlinks (name); | |
| if (n2) name = n2; | |
| if (!name || !*name) | |
| file = ""; | |
| else | |
| { | |
| file = (char *) malloc(strlen(name) + strlen(suffix) + 2); | |
| strcpy(file, name); | |
| strcat(file, suffix); | |
| } | |
| if (n2) free (n2); | |
| } | |
| if (file && *file) | |
| return file; | |
| else | |
| return 0; | |
| } | |
| static int | |
| get_byte_resource (Display *dpy, char *name, char *class) | |
| { | |
| char *s = get_string_resource (dpy, name, class); | |
| char *s2 = s; | |
| int n = 0; | |
| if (!s) return 0; | |
| while (isspace(*s2)) s2++; | |
| while (*s2 >= '0' && *s2 <= '9') | |
| { | |
| n = (n * 10) + (*s2 - '0'); | |
| s2++; | |
| } | |
| while (isspace(*s2)) s2++; | |
| if (*s2 == 'k' || *s2 == 'K') n <<= 10; | |
| else if (*s2 == 'm' || *s2 == 'M') n <<= 20; | |
| else if (*s2 == 'g' || *s2 == 'G') n <<= 30; | |
| else if (*s2) | |
| { | |
| LOSE: | |
| fprintf (stderr, "%s: %s must be a number of bytes, not \"%s\".\n", | |
| progname, name, s); | |
| free (s); | |
| return 0; | |
| } | |
| s2++; | |
| if (*s2 == 'b' || *s2 == 'B') s2++; | |
| while (isspace(*s2)) s2++; | |
| if (*s2) goto LOSE; | |
| free (s); | |
| return n; | |
| } | |
| static const char * const prefs[] = { | |
| "timeout", | |
| "cycle", | |
| "lock", | |
| "lockVTs", /* not saved */ | |
| "lockTimeout", | |
| "passwdTimeout", | |
| "visualID", | |
| "installColormap", | |
| "verbose", | |
| "timestamp", | |
| "splash", | |
| "splashDuration", | |
| "quad", | |
| "demoCommand", | |
| "prefsCommand", | |
| "newLoginCommand", | |
| "helpURL", /* not saved */ | |
| "loadURL", /* not saved */ | |
| "newLoginCommand", /* not saved */ | |
| "nice", | |
| "memoryLimit", | |
| "fade", | |
| "unfade", | |
| "fadeSeconds", | |
| "fadeTicks", | |
| "captureStderr", | |
| "captureStdout", /* not saved -- obsolete */ | |
| "logFile", /* not saved */ | |
| "ignoreUninstalledPrograms", | |
| "font", | |
| "dpmsEnabled", | |
| "dpmsQuickOff", | |
| "dpmsStandby", | |
| "dpmsSuspend", | |
| "dpmsOff", | |
| "grabDesktopImages", | |
| "grabVideoFrames", | |
| "chooseRandomImages", | |
| "imageDirectory", | |
| "mode", | |
| "selected", | |
| "textMode", | |
| "textLiteral", | |
| "textFile", | |
| "textProgram", | |
| "textURL", | |
| "", | |
| "programs", | |
| "", | |
| "pointerPollTime", | |
| "pointerHysteresis", | |
| "windowCreationTimeout", | |
| "initialDelay", | |
| "sgiSaverExtension", /* not saved -- obsolete */ | |
| "mitSaverExtension", /* not saved -- obsolete */ | |
| "xidleExtension", /* not saved -- obsolete */ | |
| "GetViewPortIsFullOfLies", | |
| "procInterrupts", | |
| "xinputExtensionDev", | |
| "overlayStderr", | |
| "overlayTextBackground", /* not saved -- X resources only */ | |
| "overlayTextForeground", /* not saved -- X resources only */ | |
| "bourneShell", /* not saved -- X resources only */ | |
| "authWarningSlack", | |
| 0 | |
| }; | |
| static char * | |
| strip (char *s) | |
| { | |
| char *s2; | |
| while (*s == '\t' || *s == ' ' || *s == '\r' || *s == '\n') | |
| s++; | |
| for (s2 = s; *s2; s2++) | |
| ; | |
| for (s2--; s2 >= s; s2--) | |
| if (*s2 == '\t' || *s2 == ' ' || *s2 == '\r' || *s2 =='\n') | |
| *s2 = 0; | |
| else | |
| break; | |
| return s; | |
| } | |
| /* Reading | |
| */ | |
| static int | |
| handle_entry (XrmDatabase *db, const char *key, const char *value, | |
| const char *filename, int line) | |
| { | |
| int i; | |
| for (i = 0; prefs[i]; i++) | |
| if (*prefs[i] && !strcasecmp(key, prefs[i])) | |
| { | |
| char *val = strdup(value); | |
| char *spec = (char *) malloc(strlen(progclass) + strlen(prefs[i]) +10); | |
| strcpy(spec, progclass); | |
| strcat(spec, "."); | |
| strcat(spec, prefs[i]); | |
| XrmPutStringResource (db, spec, val); | |
| free(spec); | |
| free(val); | |
| return 0; | |
| } | |
| fprintf(stderr, "%s: %s:%d: unknown option \"%s\"\n", | |
| blurb(), filename, line, key); | |
| return 1; | |
| } | |
| static int | |
| parse_init_file (saver_preferences *p) | |
| { | |
| time_t write_date = 0; | |
| const char *name = init_file_name(); | |
| int line = 0; | |
| struct stat st; | |
| FILE *in; | |
| int buf_size = 1024; | |
| char *buf; | |
| if (!name) return 0; | |
| if (stat(name, &st) != 0) | |
| { | |
| p->init_file_date = 0; | |
| return 0; | |
| } | |
| in = fopen(name, "r"); | |
| if (!in) | |
| { | |
| char *buf = (char *) malloc(1024 + strlen(name)); | |
| sprintf(buf, "%s: error reading \"%s\"", blurb(), name); | |
| perror(buf); | |
| free(buf); | |
| return -1; | |
| } | |
| if (fstat (fileno(in), &st) == 0) | |
| { | |
| write_date = st.st_mtime; | |
| } | |
| else | |
| { | |
| char *buf = (char *) malloc(1024 + strlen(name)); | |
| sprintf(buf, "%s: couldn't re-stat \"%s\"", blurb(), name); | |
| perror(buf); | |
| free(buf); | |
| return -1; | |
| } | |
| buf = (char *) malloc(buf_size); | |
| while (fgets (buf, buf_size-1, in)) | |
| { | |
| char *key, *value; | |
| int L = strlen(buf); | |
| line++; | |
| while (L > 2 && | |
| (buf[L-1] != '\n' || /* whole line didn't fit in buffer */ | |
| buf[L-2] == '\\')) /* or line ended with backslash */ | |
| { | |
| if (buf[L-2] == '\\') /* backslash-newline gets swallowed */ | |
| { | |
| buf[L-2] = 0; | |
| L -= 2; | |
| } | |
| buf_size += 1024; | |
| buf = (char *) realloc(buf, buf_size); | |
| if (!buf) exit(1); | |
| line++; | |
| if (!fgets (buf + L, buf_size-L-1, in)) | |
| break; | |
| L = strlen(buf); | |
| } | |
| /* Now handle other backslash escapes. */ | |
| { | |
| int i, j; | |
| for (i = 0; buf[i]; i++) | |
| if (buf[i] == '\\') | |
| { | |
| switch (buf[i+1]) | |
| { | |
| case 'n': buf[i] = '\n'; break; | |
| case 'r': buf[i] = '\r'; break; | |
| case 't': buf[i] = '\t'; break; | |
| default: buf[i] = buf[i+1]; break; | |
| } | |
| for (j = i+2; buf[j]; j++) | |
| buf[j-1] = buf[j]; | |
| buf[j-1] = 0; | |
| } | |
| } | |
| key = strip(buf); | |
| if (*key == '#' || *key == '!' || *key == ';' || | |
| *key == '\n' || *key == 0) | |
| continue; | |
| value = strchr (key, ':'); | |
| if (!value) | |
| { | |
| fprintf(stderr, "%s: %s:%d: unparsable line: %s\n", blurb(), | |
| name, line, key); | |
| continue; | |
| } | |
| else | |
| { | |
| *value++ = 0; | |
| value = strip(value); | |
| } | |
| if (!p->db) abort(); | |
| handle_entry (&p->db, key, value, name, line); | |
| } | |
| fclose (in); | |
| free(buf); | |
| p->init_file_date = write_date; | |
| return 0; | |
| } | |
| Bool | |
| init_file_changed_p (saver_preferences *p) | |
| { | |
| const char *name = init_file_name(); | |
| struct stat st; | |
| if (!name) return False; | |
| if (stat(name, &st) != 0) | |
| return False; | |
| if (p->init_file_date == st.st_mtime) | |
| return False; | |
| return True; | |
| } | |
| /* Writing | |
| */ | |
| static int | |
| tab_to (FILE *out, int from, int to) | |
| { | |
| int tab_width = 8; | |
| int to_mod = (to / tab_width) * tab_width; | |
| while (from < to_mod) | |
| { | |
| fprintf(out, "\t"); | |
| from = (((from / tab_width) + 1) * tab_width); | |
| } | |
| while (from < to) | |
| { | |
| fprintf(out, " "); | |
| from++; | |
| } | |
| return from; | |
| } | |
| static char * | |
| stab_to (char *out, int from, int to) | |
| { | |
| int tab_width = 8; | |
| int to_mod = (to / tab_width) * tab_width; | |
| while (from < to_mod) | |
| { | |
| *out++ = '\t'; | |
| from = (((from / tab_width) + 1) * tab_width); | |
| } | |
| while (from < to) | |
| { | |
| *out++ = ' '; | |
| from++; | |
| } | |
| return out; | |
| } | |
| static int | |
| string_columns (const char *string, int length, int start) | |
| { | |
| int tab_width = 8; | |
| int col = start; | |
| const char *end = string + length; | |
| while (string < end) | |
| { | |
| if (*string == '\n') | |
| col = 0; | |
| else if (*string == '\t') | |
| col = (((col / tab_width) + 1) * tab_width); | |
| else | |
| col++; | |
| string++; | |
| } | |
| return col; | |
| } | |
| static void | |
| write_entry (FILE *out, const char *key, const char *value) | |
| { | |
| char *v = strdup(value ? value : ""); | |
| char *v2 = v; | |
| char *nl = 0; | |
| int col; | |
| Bool programs_p = (!strcmp(key, "programs")); | |
| int tab = (programs_p ? 32 : 16); | |
| Bool first = True; | |
| fprintf(out, "%s:", key); | |
| col = strlen(key) + 1; | |
| if (strlen(key) > 14) | |
| col = tab_to (out, col, 20); | |
| while (1) | |
| { | |
| if (!programs_p) | |
| v2 = strip(v2); | |
| nl = strchr(v2, '\n'); | |
| if (nl) | |
| *nl = 0; | |
| if (first && programs_p) | |
| { | |
| col = tab_to (out, col, 77); | |
| fprintf (out, " \\\n"); | |
| col = 0; | |
| } | |
| if (first) | |
| first = False; | |
| else | |
| { | |
| col = tab_to (out, col, 75); | |
| fprintf (out, " \\n\\\n"); | |
| col = 0; | |
| } | |
| if (!programs_p) | |
| col = tab_to (out, col, tab); | |
| if (programs_p && | |
| string_columns(v2, strlen (v2), col) + col > 75) | |
| { | |
| int L = strlen (v2); | |
| int start = 0; | |
| int end = start; | |
| while (start < L) | |
| { | |
| while (v2[end] == ' ' || v2[end] == '\t') | |
| end++; | |
| while (v2[end] != ' ' && v2[end] != '\t' && | |
| v2[end] != '\n' && v2[end] != 0) | |
| end++; | |
| if (string_columns (v2 + start, (end - start), col) >= 74) | |
| { | |
| col = tab_to (out, col, 75); | |
| fprintf(out, " \\\n"); | |
| col = tab_to (out, 0, tab + 2); | |
| while (v2[start] == ' ' || v2[start] == '\t') | |
| start++; | |
| } | |
| col = string_columns (v2 + start, (end - start), col); | |
| while (start < end) | |
| fputc(v2[start++], out); | |
| } | |
| } | |
| else | |
| { | |
| fprintf (out, "%s", v2); | |
| col += string_columns(v2, strlen (v2), col); | |
| } | |
| if (nl) | |
| v2 = nl + 1; | |
| else | |
| break; | |
| } | |
| fprintf(out, "\n"); | |
| free(v); | |
| } | |
| int | |
| write_init_file (Display *dpy, | |
| saver_preferences *p, const char *version_string, | |
| Bool verbose_p) | |
| { | |
| int status = -1; | |
| const char *name = init_file_name(); | |
| const char *tmp_name = init_file_tmp_name(); | |
| char *n2 = chase_symlinks (name); | |
| struct stat st; | |
| int i, j; | |
| /* Kludge, since these aren't in the saver_preferences struct as strings... | |
| */ | |
| char *visual_name; | |
| char *programs; | |
| Bool overlay_stderr_p; | |
| char *stderr_font; | |
| FILE *out; | |
| if (!name) goto END; | |
| if (n2) name = n2; | |
| /* Throttle the various timeouts to reasonable values before writing | |
| the file to disk. */ | |
| stop_the_insanity (p); | |
| if (verbose_p) | |
| fprintf (stderr, "%s: writing \"%s\".\n", blurb(), name); | |
| unlink (tmp_name); | |
| out = fopen(tmp_name, "w"); | |
| if (!out) | |
| { | |
| char *buf = (char *) malloc(1024 + strlen(name)); | |
| sprintf(buf, "%s: error writing \"%s\"", blurb(), name); | |
| perror(buf); | |
| free(buf); | |
| goto END; | |
| } | |
| /* Give the new .xscreensaver file the same permissions as the old one; | |
| except ensure that it is readable and writable by owner, and not | |
| executable. Extra hack: if we're running as root, make the file | |
| be world-readable (so that the daemon, running as "nobody", will | |
| still be able to read it.) | |
| */ | |
| if (stat(name, &st) == 0) | |
| { | |
| mode_t mode = st.st_mode; | |
| mode |= S_IRUSR | S_IWUSR; /* read/write by user */ | |
| mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); /* executable by none */ | |
| if (getuid() == (uid_t) 0) /* read by group/other */ | |
| mode |= S_IRGRP | S_IROTH; | |
| if (fchmod (fileno(out), mode) != 0) | |
| { | |
| char *buf = (char *) malloc(1024 + strlen(name)); | |
| sprintf (buf, "%s: error fchmodding \"%s\" to 0%o", blurb(), | |
| tmp_name, (unsigned int) mode); | |
| perror(buf); | |
| free(buf); | |
| goto END; | |
| } | |
| } | |
| /* Kludge, since these aren't in the saver_preferences struct... */ | |
| visual_name = get_string_resource (dpy, "visualID", "VisualID"); | |
| programs = 0; | |
| overlay_stderr_p = get_boolean_resource (dpy, "overlayStderr", "Boolean"); | |
| stderr_font = get_string_resource (dpy, "font", "Font"); | |
| i = 0; | |
| { | |
| char *ss; | |
| char **hack_strings = (char **) | |
| calloc (p->screenhacks_count, sizeof(char *)); | |
| for (j = 0; j < p->screenhacks_count; j++) | |
| { | |
| hack_strings[j] = format_hack (dpy, p->screenhacks[j], True); | |
| i += strlen (hack_strings[j]); | |
| i += 2; | |
| } | |
| ss = programs = (char *) malloc(i + 10); | |
| *ss = 0; | |
| for (j = 0; j < p->screenhacks_count; j++) | |
| { | |
| strcat (ss, hack_strings[j]); | |
| free (hack_strings[j]); | |
| ss += strlen(ss); | |
| *ss++ = '\n'; | |
| *ss = 0; | |
| } | |
| free (hack_strings); | |
| } | |
| { | |
| struct passwd *pw = getpwuid (getuid ()); | |
| char *whoami = (pw && pw->pw_name && *pw->pw_name | |
| ? pw->pw_name | |
| : "<unknown>"); | |
| time_t now = time ((time_t *) 0); | |
| char *timestr = (char *) ctime (&now); | |
| char *nl = (char *) strchr (timestr, '\n'); | |
| if (nl) *nl = 0; | |
| fprintf (out, | |
| "# %s Preferences File\n" | |
| "# Written by %s %s for %s on %s.\n" | |
| "# http://www.jwz.org/xscreensaver/\n" | |
| "\n", | |
| progclass, progname, version_string, whoami, timestr); | |
| } | |
| for (j = 0; prefs[j]; j++) | |
| { | |
| char buf[255]; | |
| const char *pr = prefs[j]; | |
| enum pref_type { pref_str, pref_int, pref_bool, pref_byte, pref_time | |
| } type = pref_str; | |
| const char *s = 0; | |
| int i = 0; | |
| Bool b = False; | |
| Time t = 0; | |
| if (pr && !*pr) | |
| { | |
| fprintf(out, "\n"); | |
| continue; | |
| } | |
| # undef CHECK | |
| # define CHECK(X) else if (!strcmp(pr, X)) | |
| if (!pr || !*pr) ; | |
| CHECK("timeout") type = pref_time, t = p->timeout; | |
| CHECK("cycle") type = pref_time, t = p->cycle; | |
| CHECK("lock") type = pref_bool, b = p->lock_p; | |
| CHECK("lockVTs") continue; /* don't save, unused */ | |
| CHECK("lockTimeout") type = pref_time, t = p->lock_timeout; | |
| CHECK("passwdTimeout") type = pref_time, t = p->passwd_timeout; | |
| CHECK("visualID") type = pref_str, s = visual_name; | |
| CHECK("installColormap") type = pref_bool, b = p->install_cmap_p; | |
| CHECK("verbose") type = pref_bool, b = p->verbose_p; | |
| CHECK("timestamp") type = pref_bool, b = p->timestamp_p; | |
| CHECK("splash") type = pref_bool, b = p->splash_p; | |
| CHECK("splashDuration") type = pref_time, t = p->splash_duration; | |
| # ifdef QUAD_MODE | |
| CHECK("quad") type = pref_bool, b = p->quad_p; | |
| # else /* !QUAD_MODE */ | |
| CHECK("quad") continue; /* don't save */ | |
| # endif /* !QUAD_MODE */ | |
| CHECK("demoCommand") type = pref_str, s = p->demo_command; | |
| CHECK("prefsCommand") type = pref_str, s = p->prefs_command; | |
| /* CHECK("helpURL") type = pref_str, s = p->help_url; */ | |
| CHECK("helpURL") continue; /* don't save */ | |
| /* CHECK("loadURL") type = pref_str, s = p->load_url_command; */ | |
| CHECK("loadURL") continue; /* don't save */ | |
| /* CHECK("newLoginCommand") type = pref_str, s = p->new_login_command; */ | |
| CHECK("newLoginCommand") continue; /* don't save */ | |
| CHECK("nice") type = pref_int, i = p->nice_inferior; | |
| CHECK("memoryLimit") type = pref_byte, i = p->inferior_memory_limit; | |
| CHECK("fade") type = pref_bool, b = p->fade_p; | |
| CHECK("unfade") type = pref_bool, b = p->unfade_p; | |
| CHECK("fadeSeconds") type = pref_time, t = p->fade_seconds; | |
| CHECK("fadeTicks") type = pref_int, i = p->fade_ticks; | |
| CHECK("captureStderr") type = pref_bool, b = p->capture_stderr_p; | |
| CHECK("captureStdout") continue; /* don't save */ | |
| CHECK("logFile") continue; /* don't save */ | |
| CHECK("ignoreUninstalledPrograms") | |
| type = pref_bool, b = p->ignore_uninstalled_p; | |
| CHECK("font") type = pref_str, s = stderr_font; | |
| CHECK("dpmsEnabled") type = pref_bool, b = p->dpms_enabled_p; | |
| CHECK("dpmsQuickOff") type = pref_bool, b = p->dpms_quickoff_p; | |
| CHECK("dpmsStandby") type = pref_time, t = p->dpms_standby; | |
| CHECK("dpmsSuspend") type = pref_time, t = p->dpms_suspend; | |
| CHECK("dpmsOff") type = pref_time, t = p->dpms_off; | |
| CHECK("grabDesktopImages") type =pref_bool, b = p->grab_desktop_p; | |
| CHECK("grabVideoFrames") type =pref_bool, b = p->grab_video_p; | |
| CHECK("chooseRandomImages")type =pref_bool, b = p->random_image_p; | |
| CHECK("imageDirectory") type =pref_str, s = p->image_directory; | |
| CHECK("mode") type = pref_str, | |
| s = (p->mode == ONE_HACK ? "one" : | |
| p->mode == BLANK_ONLY ? "blank" : | |
| p->mode == DONT_BLANK ? "off" : | |
| p->mode == RANDOM_HACKS_SAME | |
| ? "random-same" | |
| : "random"); | |
| CHECK("selected") type = pref_int, i = p->selected_hack; | |
| CHECK("textMode") type = pref_str, | |
| s = (p->tmode == TEXT_URL ? "url" : | |
| p->tmode == TEXT_LITERAL ? "literal" : | |
| p->tmode == TEXT_FILE ? "file" : | |
| p->tmode == TEXT_PROGRAM ? "program" : | |
| "date"); | |
| CHECK("textLiteral") type = pref_str, s = p->text_literal; | |
| CHECK("textFile") type = pref_str, s = p->text_file; | |
| CHECK("textProgram") type = pref_str, s = p->text_program; | |
| CHECK("textURL") type = pref_str, s = p->text_url; | |
| CHECK("programs") type = pref_str, s = programs; | |
| CHECK("pointerPollTime") type = pref_time, t = p->pointer_timeout; | |
| CHECK("pointerHysteresis")type = pref_int, i = p->pointer_hysteresis; | |
| CHECK("windowCreationTimeout")type=pref_time,t= p->notice_events_timeout; | |
| CHECK("initialDelay") type = pref_time, t = p->initial_delay; | |
| CHECK("sgiSaverExtension") continue; /* don't save */ | |
| CHECK("mitSaverExtension") continue; /* don't save */ | |
| CHECK("xidleExtension") continue; /* don't save */ | |
| CHECK("procInterrupts") type = pref_bool, b = p->use_proc_interrupts; | |
| CHECK("xinputExtensionDev") type = pref_bool, b = p->use_xinput_extension; | |
| CHECK("GetViewPortIsFullOfLies") type = pref_bool, | |
| b = p->getviewport_full_of_lies_p; | |
| CHECK("overlayStderr") type = pref_bool, b = overlay_stderr_p; | |
| CHECK("overlayTextBackground") continue; /* don't save */ | |
| CHECK("overlayTextForeground") continue; /* don't save */ | |
| CHECK("bourneShell") continue; /* don't save */ | |
| CHECK("authWarningSlack") type = pref_int, i = p->auth_warning_slack; | |
| else abort(); | |
| # undef CHECK | |
| switch (type) | |
| { | |
| case pref_str: | |
| break; | |
| case pref_int: | |
| sprintf(buf, "%d", i); | |
| s = buf; | |
| break; | |
| case pref_bool: | |
| s = b ? "True" : "False"; | |
| break; | |
| case pref_time: | |
| { | |
| unsigned int hour = 0, min = 0, sec = (unsigned int) (t/1000); | |
| if (sec >= 60) | |
| { | |
| min += (sec / 60); | |
| sec %= 60; | |
| } | |
| if (min >= 60) | |
| { | |
| hour += (min / 60); | |
| min %= 60; | |
| } | |
| sprintf (buf, "%u:%02u:%02u", hour, min, sec); | |
| s = buf; | |
| } | |
| break; | |
| case pref_byte: | |
| { | |
| if (i >= (1<<30) && i == ((i >> 30) << 30)) | |
| sprintf(buf, "%dG", i >> 30); | |
| else if (i >= (1<<20) && i == ((i >> 20) << 20)) | |
| sprintf(buf, "%dM", i >> 20); | |
| else if (i >= (1<<10) && i == ((i >> 10) << 10)) | |
| sprintf(buf, "%dK", i >> 10); | |
| else | |
| sprintf(buf, "%d", i); | |
| s = buf; | |
| } | |
| break; | |
| default: | |
| abort(); | |
| break; | |
| } | |
| if (pr && (!strcmp(pr, "mode") || !strcmp(pr, "textMode"))) | |
| fprintf(out, "\n"); | |
| write_entry (out, pr, s); | |
| } | |
| fprintf(out, "\n"); | |
| if (visual_name) free(visual_name); | |
| if (stderr_font) free(stderr_font); | |
| if (programs) free(programs); | |
| if (fclose(out) == 0) | |
| { | |
| time_t write_date = 0; | |
| if (stat(tmp_name, &st) == 0) | |
| { | |
| write_date = st.st_mtime; | |
| } | |
| else | |
| { | |
| char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name)); | |
| sprintf(buf, "%s: couldn't stat \"%s\"", blurb(), tmp_name); | |
| perror(buf); | |
| unlink (tmp_name); | |
| free(buf); | |
| goto END; | |
| } | |
| if (rename (tmp_name, name) != 0) | |
| { | |
| char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name)); | |
| sprintf(buf, "%s: error renaming \"%s\" to \"%s\"", | |
| blurb(), tmp_name, name); | |
| perror(buf); | |
| unlink (tmp_name); | |
| free(buf); | |
| goto END; | |
| } | |
| else | |
| { | |
| p->init_file_date = write_date; | |
| /* Since the .xscreensaver file is used for IPC, let's try and make | |
| sure that the bits actually land on the disk right away. */ | |
| sync (); | |
| status = 0; /* wrote and renamed successfully! */ | |
| } | |
| } | |
| else | |
| { | |
| char *buf = (char *) malloc(1024 + strlen(name)); | |
| sprintf(buf, "%s: error closing \"%s\"", blurb(), name); | |
| perror(buf); | |
| free(buf); | |
| unlink (tmp_name); | |
| goto END; | |
| } | |
| END: | |
| if (n2) free (n2); | |
| return status; | |
| } | |
| /* Parsing the resource database | |
| */ | |
| void | |
| free_screenhack (screenhack *hack) | |
| { | |
| if (hack->visual) free (hack->visual); | |
| if (hack->name) free (hack->name); | |
| free (hack->command); | |
| memset (hack, 0, sizeof(*hack)); | |
| free (hack); | |
| } | |
| static void | |
| free_screenhack_list (screenhack **list, int count) | |
| { | |
| int i; | |
| if (!list) return; | |
| for (i = 0; i < count; i++) | |
| if (list[i]) | |
| free_screenhack (list[i]); | |
| free (list); | |
| } | |
| /* Populate `saver_preferences' with the contents of the resource database. | |
| Note that this may be called multiple times -- it is re-run each time | |
| the ~/.xscreensaver file is reloaded. | |
| This function can be very noisy, since it issues resource syntax errors | |
| and so on. | |
| */ | |
| void | |
| load_init_file (Display *dpy, saver_preferences *p) | |
| { | |
| static Bool first_time = True; | |
| screenhack **system_default_screenhacks = 0; | |
| int system_default_screenhack_count = 0; | |
| if (first_time) | |
| { | |
| /* Get the programs resource before the .xscreensaver file has been | |
| parsed and merged into the resource database for the first time: | |
| this is the value of *programs from the app-defaults file. | |
| Then clear it out so that it will be parsed again later, after | |
| the init file has been read. | |
| */ | |
| get_screenhacks (dpy, p); | |
| system_default_screenhacks = p->screenhacks; | |
| system_default_screenhack_count = p->screenhacks_count; | |
| p->screenhacks = 0; | |
| p->screenhacks_count = 0; | |
| } | |
| if (parse_init_file (p) != 0) /* file might have gone away */ | |
| if (!first_time) return; | |
| first_time = False; | |
| p->xsync_p = get_boolean_resource (dpy, "synchronous", "Synchronous"); | |
| p->verbose_p = get_boolean_resource (dpy, "verbose", "Boolean"); | |
| p->timestamp_p = get_boolean_resource (dpy, "timestamp", "Boolean"); | |
| p->lock_p = get_boolean_resource (dpy, "lock", "Boolean"); | |
| p->fade_p = get_boolean_resource (dpy, "fade", "Boolean"); | |
| p->unfade_p = get_boolean_resource (dpy, "unfade", "Boolean"); | |
| p->fade_seconds = 1000 * get_seconds_resource (dpy, "fadeSeconds", "Time"); | |
| p->fade_ticks = get_integer_resource (dpy, "fadeTicks", "Integer"); | |
| p->install_cmap_p = get_boolean_resource (dpy, "installColormap", "Boolean"); | |
| p->nice_inferior = get_integer_resource (dpy, "nice", "Nice"); | |
| p->inferior_memory_limit = get_byte_resource (dpy, "memoryLimit", | |
| "MemoryLimit"); | |
| p->splash_p = get_boolean_resource (dpy, "splash", "Boolean"); | |
| # ifdef QUAD_MODE | |
| p->quad_p = get_boolean_resource (dpy, "quad", "Boolean"); | |
| # endif | |
| p->capture_stderr_p = get_boolean_resource (dpy, "captureStderr", "Boolean"); | |
| p->ignore_uninstalled_p = get_boolean_resource (dpy, | |
| "ignoreUninstalledPrograms", | |
| "Boolean"); | |
| p->initial_delay = 1000 * get_seconds_resource (dpy, "initialDelay", "Time"); | |
| p->splash_duration = 1000 * get_seconds_resource (dpy, "splashDuration", "Time"); | |
| p->timeout = 1000 * get_minutes_resource (dpy, "timeout", "Time"); | |
| p->lock_timeout = 1000 * get_minutes_resource (dpy, "lockTimeout", "Time"); | |
| p->cycle = 1000 * get_minutes_resource (dpy, "cycle", "Time"); | |
| p->passwd_timeout = 1000 * get_seconds_resource (dpy, "passwdTimeout", "Time"); | |
| p->pointer_timeout = 1000 * get_seconds_resource (dpy, "pointerPollTime", "Time"); | |
| p->pointer_hysteresis = get_integer_resource (dpy, "pointerHysteresis","Integer"); | |
| p->notice_events_timeout = 1000*get_seconds_resource(dpy, | |
| "windowCreationTimeout", | |
| "Time"); | |
| p->dpms_enabled_p = get_boolean_resource (dpy, "dpmsEnabled", "Boolean"); | |
| p->dpms_quickoff_p = get_boolean_resource (dpy, "dpmsQuickOff", "Boolean"); | |
| p->dpms_standby = 1000 * get_minutes_resource (dpy, "dpmsStandby", "Time"); | |
| p->dpms_suspend = 1000 * get_minutes_resource (dpy, "dpmsSuspend", "Time"); | |
| p->dpms_off = 1000 * get_minutes_resource (dpy, "dpmsOff", "Time"); | |
| p->grab_desktop_p = get_boolean_resource (dpy, "grabDesktopImages", "Boolean"); | |
| p->grab_video_p = get_boolean_resource (dpy, "grabVideoFrames", "Boolean"); | |
| p->random_image_p = get_boolean_resource (dpy, "chooseRandomImages", "Boolean"); | |
| p->image_directory = get_string_resource (dpy, | |
| "imageDirectory", | |
| "ImageDirectory"); | |
| p->text_literal = get_string_resource (dpy, "textLiteral", "TextLiteral"); | |
| p->text_file = get_string_resource (dpy, "textFile", "TextFile"); | |
| p->text_program = get_string_resource (dpy, "textProgram", "TextProgram"); | |
| p->text_url = get_string_resource (dpy, "textURL", "TextURL"); | |
| p->shell = get_string_resource (dpy, "bourneShell", "BourneShell"); | |
| p->demo_command = get_string_resource(dpy, "demoCommand", "URL"); | |
| p->prefs_command = get_string_resource(dpy, "prefsCommand", "URL"); | |
| p->help_url = get_string_resource(dpy, "helpURL", "URL"); | |
| p->load_url_command = get_string_resource(dpy, "loadURL", "LoadURL"); | |
| p->new_login_command = get_string_resource(dpy, | |
| "newLoginCommand", | |
| "NewLoginCommand"); | |
| p->auth_warning_slack = get_integer_resource(dpy, "authWarningSlack", | |
| "Integer"); | |
| /* If "*splash" is unset, default to true. */ | |
| { | |
| char *s = get_string_resource (dpy, "splash", "Boolean"); | |
| if (s) | |
| free (s); | |
| else | |
| p->splash_p = True; | |
| } | |
| /* If "*grabDesktopImages" is unset, default to true. */ | |
| { | |
| char *s = get_string_resource (dpy, "grabDesktopImages", "Boolean"); | |
| if (s) | |
| free (s); | |
| else | |
| p->grab_desktop_p = True; | |
| } | |
| p->use_xidle_extension = get_boolean_resource (dpy, "xidleExtension","Boolean"); | |
| #if 0 /* obsolete. */ | |
| p->use_sgi_saver_extension = get_boolean_resource (dpy, | |
| "sgiSaverExtension", | |
| "Boolean"); | |
| #endif | |
| #ifdef HAVE_XINPUT | |
| p->use_xinput_extension = get_boolean_resource (dpy, "xinputExtensionDev", | |
| "Boolean"); | |
| #endif | |
| #if 0 /* broken and evil. */ | |
| p->use_mit_saver_extension = get_boolean_resource (dpy, | |
| "mitSaverExtension", | |
| "Boolean"); | |
| #endif | |
| p->use_proc_interrupts = get_boolean_resource (dpy, | |
| "procInterrupts", "Boolean"); | |
| p->getviewport_full_of_lies_p = | |
| get_boolean_resource (dpy, "GetViewPortIsFullOfLies", "Boolean"); | |
| get_screenhacks (dpy, p); /* Parse the "programs" resource. */ | |
| { | |
| char *s = get_string_resource (dpy, "selected", "Integer"); | |
| if (!s || !*s) | |
| p->selected_hack = -1; | |
| else | |
| p->selected_hack = get_integer_resource (dpy, "selected", "Integer"); | |
| if (s) free (s); | |
| if (p->selected_hack < 0 || p->selected_hack >= p->screenhacks_count) | |
| p->selected_hack = -1; | |
| } | |
| { | |
| char *s = get_string_resource (dpy, "mode", "Mode"); | |
| if (s && !strcasecmp (s, "one")) p->mode = ONE_HACK; | |
| else if (s && !strcasecmp (s, "blank")) p->mode = BLANK_ONLY; | |
| else if (s && !strcasecmp (s, "off")) p->mode = DONT_BLANK; | |
| else if (s && !strcasecmp (s, "random-same")) p->mode = RANDOM_HACKS_SAME; | |
| else p->mode = RANDOM_HACKS; | |
| if (s) free (s); | |
| } | |
| { | |
| char *s = get_string_resource (dpy, "textMode", "TextMode"); | |
| if (s && !strcasecmp (s, "url")) p->tmode = TEXT_URL; | |
| else if (s && !strcasecmp (s, "literal")) p->tmode = TEXT_LITERAL; | |
| else if (s && !strcasecmp (s, "file")) p->tmode = TEXT_FILE; | |
| else if (s && !strcasecmp (s, "program")) p->tmode = TEXT_PROGRAM; | |
| else p->tmode = TEXT_DATE; | |
| if (s) free (s); | |
| } | |
| if (system_default_screenhack_count) /* note: first_time is also true */ | |
| { | |
| merge_system_screenhacks (dpy, p, system_default_screenhacks, | |
| system_default_screenhack_count); | |
| free_screenhack_list (system_default_screenhacks, | |
| system_default_screenhack_count); | |
| system_default_screenhacks = 0; | |
| system_default_screenhack_count = 0; | |
| } | |
| if (p->debug_p) | |
| { | |
| p->xsync_p = True; | |
| p->verbose_p = True; | |
| p->timestamp_p = True; | |
| p->initial_delay = 0; | |
| } | |
| /* Throttle the various timeouts to reasonable values after reading the | |
| disk file. */ | |
| stop_the_insanity (p); | |
| } | |
| /* If there are any hacks in the system-wide defaults that are not in | |
| the ~/.xscreensaver file, add the new ones to the end of the list. | |
| This does *not* actually save the file. | |
| */ | |
| static void | |
| merge_system_screenhacks (Display *dpy, saver_preferences *p, | |
| screenhack **system_list, int system_count) | |
| { | |
| /* Yeah yeah, this is an N^2 operation, but I don't have hashtables handy, | |
| so fuck it. */ | |
| int made_space = 0; | |
| int i; | |
| for (i = 0; i < system_count; i++) | |
| { | |
| int j; | |
| Bool matched_p = False; | |
| for (j = 0; j < p->screenhacks_count; j++) | |
| { | |
| char *name; | |
| if (!system_list[i]->name) | |
| system_list[i]->name = make_hack_name (dpy, | |
| system_list[i]->command); | |
| name = p->screenhacks[j]->name; | |
| if (!name) | |
| name = make_hack_name (dpy, p->screenhacks[j]->command); | |
| matched_p = !strcasecmp (name, system_list[i]->name); | |
| if (name != p->screenhacks[j]->name) | |
| free (name); | |
| if (matched_p) | |
| break; | |
| } | |
| if (!matched_p) | |
| { | |
| /* We have an entry in the system-wide list that is not in the | |
| user's .xscreensaver file. Add it to the end. | |
| Note that p->screenhacks is a single malloc block, not a | |
| linked list, so we have to realloc it. | |
| */ | |
| screenhack *oh = system_list[i]; | |
| screenhack *nh = (screenhack *) malloc (sizeof(screenhack)); | |
| if (made_space == 0) | |
| { | |
| made_space = 10; | |
| p->screenhacks = (screenhack **) | |
| realloc (p->screenhacks, | |
| (p->screenhacks_count + made_space + 1) | |
| * sizeof(screenhack)); | |
| if (!p->screenhacks) abort(); | |
| } | |
| nh->enabled_p = oh->enabled_p; | |
| nh->visual = oh->visual ? strdup(oh->visual) : 0; | |
| nh->name = oh->name ? strdup(oh->name) : 0; | |
| nh->command = oh->command ? strdup(oh->command) : 0; | |
| p->screenhacks[p->screenhacks_count++] = nh; | |
| p->screenhacks[p->screenhacks_count] = 0; | |
| made_space--; | |
| #if 0 | |
| fprintf (stderr, "%s: noticed new hack: %s\n", blurb(), | |
| (nh->name ? nh->name : make_hack_name (dpy, nh->command))); | |
| #endif | |
| } | |
| } | |
| } | |
| /* Parsing the programs resource. | |
| */ | |
| screenhack * | |
| parse_screenhack (const char *line) | |
| { | |
| screenhack *h = (screenhack *) calloc (1, sizeof(*h)); | |
| const char *s; | |
| h->enabled_p = True; | |
| while (isspace(*line)) line++; /* skip whitespace */ | |
| if (*line == '-') /* handle "-" */ | |
| { | |
| h->enabled_p = False; | |
| line++; | |
| while (isspace(*line)) line++; /* skip whitespace */ | |
| } | |
| s = line; /* handle "visual:" */ | |
| while (*line && *line != ':' && *line != '"' && !isspace(*line)) | |
| line++; | |
| if (*line != ':') | |
| line = s; | |
| else | |
| { | |
| h->visual = (char *) malloc (line-s+1); | |
| strncpy (h->visual, s, line-s); | |
| h->visual[line-s] = 0; | |
| if (*line == ':') line++; /* skip ":" */ | |
| while (isspace(*line)) line++; /* skip whitespace */ | |
| } | |
| if (*line == '"') /* handle "name" */ | |
| { | |
| line++; | |
| s = line; | |
| while (*line && *line != '"') | |
| line++; | |
| h->name = (char *) malloc (line-s+1); | |
| strncpy (h->name, s, line-s); | |
| h->name[line-s] = 0; | |
| if (*line == '"') line++; /* skip "\"" */ | |
| while (isspace(*line)) line++; /* skip whitespace */ | |
| } | |
| h->command = format_command (line, False); /* handle command */ | |
| return h; | |
| } | |
| static char * | |
| format_command (const char *cmd, Bool wrap_p) | |
| { | |
| int tab = 30; | |
| int col = tab; | |
| char *cmd2 = (char *) calloc (1, 2 * (strlen (cmd) + 1)); | |
| const char *in = cmd; | |
| char *out = cmd2; | |
| while (*in) | |
| { | |
| /* shrink all whitespace to one space, for the benefit of the "demo" | |
| mode display. We only do this when we can easily tell that the | |
| whitespace is not significant (no shell metachars). | |
| */ | |
| switch (*in) | |
| { | |
| case '\'': case '"': case '`': case '\\': | |
| /* Metachars are scary. Copy the rest of the line unchanged. */ | |
| while (*in) | |
| *out++ = *in++, col++; | |
| break; | |
| case ' ': case '\t': | |
| /* Squeeze all other whitespace down to one space. */ | |
| while (*in == ' ' || *in == '\t') | |
| in++; | |
| *out++ = ' ', col++; | |
| break; | |
| default: | |
| /* Copy other chars unchanged. */ | |
| *out++ = *in++, col++; | |
| break; | |
| } | |
| } | |
| *out = 0; | |
| /* Strip trailing whitespace */ | |
| while (out > cmd2 && isspace (out[-1])) | |
| *(--out) = 0; | |
| return cmd2; | |
| } | |
| /* Returns a new string describing the shell command. | |
| This may be just the name of the program, capitalized. | |
| It also may be something from the resource database (gotten | |
| by looking for "hacks.XYZ.name", where XYZ is the program.) | |
| */ | |
| char * | |
| make_hack_name (Display *dpy, const char *shell_command) | |
| { | |
| char *s = strdup (shell_command); | |
| char *s2; | |
| char res_name[255]; | |
| for (s2 = s; *s2; s2++) /* truncate at first whitespace */ | |
| if (isspace (*s2)) | |
| { | |
| *s2 = 0; | |
| break; | |
| } | |
| s2 = strrchr (s, '/'); /* if pathname, take last component */ | |
| if (s2) | |
| { | |
| s2 = strdup (s2+1); | |
| free (s); | |
| s = s2; | |
| } | |
| if (strlen (s) > 50) /* 51 is hereby defined as "unreasonable" */ | |
| s[50] = 0; | |
| sprintf (res_name, "hacks.%s.name", s); /* resource? */ | |
| s2 = get_string_resource (dpy, res_name, res_name); | |
| if (s2) | |
| { | |
| free (s); | |
| return s2; | |
| } | |
| for (s2 = s; *s2; s2++) /* if it has any capitals, return it */ | |
| if (*s2 >= 'A' && *s2 <= 'Z') | |
| return s; | |
| if (s[0] >= 'a' && s[0] <= 'z') /* else cap it */ | |
| s[0] -= 'a'-'A'; | |
| if (s[0] == 'X' && s[1] >= 'a' && s[1] <= 'z') /* (magic leading X) */ | |
| s[1] -= 'a'-'A'; | |
| if (s[0] == 'G' && s[1] == 'l' && | |
| s[2] >= 'a' && s[2] <= 'z') /* (magic leading GL) */ | |
| s[1] -= 'a'-'A', | |
| s[2] -= 'a'-'A'; | |
| return s; | |
| } | |
| char * | |
| format_hack (Display *dpy, screenhack *hack, Bool wrap_p) | |
| { | |
| int tab = 32; | |
| int size; | |
| char *h2, *out, *s; | |
| int col = 0; | |
| char *def_name = make_hack_name (dpy, hack->command); | |
| /* Don't ever write out a name for a hack if it's the same as the default. | |
| */ | |
| if (hack->name && !strcmp (hack->name, def_name)) | |
| { | |
| free (hack->name); | |
| hack->name = 0; | |
| } | |
| free (def_name); | |
| size = (2 * (strlen(hack->command) + | |
| (hack->visual ? strlen(hack->visual) : 0) + | |
| (hack->name ? strlen(hack->name) : 0) + | |
| tab)); | |
| h2 = (char *) malloc (size); | |
| out = h2; | |
| if (!hack->enabled_p) *out++ = '-'; /* write disabled flag */ | |
| if (hack->visual && *hack->visual) /* write visual name */ | |
| { | |
| if (hack->enabled_p) *out++ = ' '; | |
| *out++ = ' '; | |
| strcpy (out, hack->visual); | |
| out += strlen (hack->visual); | |
| *out++ = ':'; | |
| *out++ = ' '; | |
| } | |
| *out = 0; | |
| col = string_columns (h2, strlen (h2), 0); | |
| if (hack->name && *hack->name) /* write pretty name */ | |
| { | |
| int L = (strlen (hack->name) + 2); | |
| if (L + col < tab) | |
| out = stab_to (out, col, tab - L - 2); | |
| else | |
| *out++ = ' '; | |
| *out++ = '"'; | |
| strcpy (out, hack->name); | |
| out += strlen (hack->name); | |
| *out++ = '"'; | |
| *out = 0; | |
| col = string_columns (h2, strlen (h2), 0); | |
| if (wrap_p && col >= tab) | |
| out = stab_to (out, col, 77); | |
| else | |
| *out++ = ' '; | |
| if (out >= h2+size) abort(); | |
| } | |
| *out = 0; | |
| col = string_columns (h2, strlen (h2), 0); | |
| out = stab_to (out, col, tab); /* indent */ | |
| if (out >= h2+size) abort(); | |
| s = format_command (hack->command, wrap_p); | |
| strcpy (out, s); | |
| out += strlen (s); | |
| free (s); | |
| *out = 0; | |
| return h2; | |
| } | |
| static void | |
| get_screenhacks (Display *dpy, saver_preferences *p) | |
| { | |
| int i, j; | |
| int start = 0; | |
| int end = 0; | |
| int size; | |
| char *d; | |
| d = get_string_resource (dpy, "monoPrograms", "MonoPrograms"); | |
| if (d && !*d) { free(d); d = 0; } | |
| if (!d) | |
| d = get_string_resource (dpy, "colorPrograms", "ColorPrograms"); | |
| if (d && !*d) { free(d); d = 0; } | |
| if (d) | |
| { | |
| fprintf (stderr, | |
| "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\ | |
| see the manual for details.\n", blurb()); | |
| free(d); | |
| } | |
| d = get_string_resource (dpy, "programs", "Programs"); | |
| free_screenhack_list (p->screenhacks, p->screenhacks_count); | |
| p->screenhacks = 0; | |
| p->screenhacks_count = 0; | |
| if (!d || !*d) | |
| return; | |
| size = strlen (d); | |
| /* Count up the number of newlines (which will be equal to or larger than | |
| one less than the number of hacks.) | |
| */ | |
| for (i = j = 0; d[i]; i++) | |
| if (d[i] == '\n') | |
| j++; | |
| j++; | |
| p->screenhacks = (screenhack **) calloc (j + 1, sizeof (screenhack *)); | |
| /* Iterate over the lines in `d' (the string with newlines) | |
| and make new strings to stuff into the `screenhacks' array. | |
| */ | |
| p->screenhacks_count = 0; | |
| while (start < size) | |
| { | |
| /* skip forward over whitespace. */ | |
| while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n') | |
| start++; | |
| /* skip forward to newline or end of string. */ | |
| end = start; | |
| while (d[end] != 0 && d[end] != '\n') | |
| end++; | |
| /* null terminate. */ | |
| d[end] = 0; | |
| p->screenhacks[p->screenhacks_count++] = parse_screenhack (d + start); | |
| if (p->screenhacks_count >= i) | |
| abort(); | |
| start = end+1; | |
| } | |
| free (d); | |
| if (p->screenhacks_count == 0) | |
| { | |
| free (p->screenhacks); | |
| p->screenhacks = 0; | |
| } | |
| } | |
| /* Make sure all the values in the preferences struct are sane. | |
| */ | |
| static void | |
| stop_the_insanity (saver_preferences *p) | |
| { | |
| if (p->passwd_timeout <= 0) p->passwd_timeout = 30000; /* 30 secs */ | |
| if (p->timeout < 15000) p->timeout = 15000; /* 15 secs */ | |
| if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */ | |
| if (p->pointer_timeout <= 0) p->pointer_timeout = 5000; /* 5 secs */ | |
| if (p->notice_events_timeout <= 0) | |
| p->notice_events_timeout = 10000; /* 10 secs */ | |
| if (p->fade_seconds <= 0 || p->fade_ticks <= 0) | |
| p->fade_p = False; | |
| if (! p->fade_p) p->unfade_p = False; | |
| /* The DPMS settings may have the value 0. | |
| But if they are negative, or are a range less than 10 seconds, | |
| reset them to sensible defaults. (Since that must be a mistake.) | |
| */ | |
| if (p->dpms_standby != 0 && | |
| p->dpms_standby < 10 * 1000) | |
| p->dpms_standby = 2 * 60 * 60 * 1000; /* 2 hours */ | |
| if (p->dpms_suspend != 0 && | |
| p->dpms_suspend < 10 * 1000) | |
| p->dpms_suspend = 2 * 60 * 60 * 1000; /* 2 hours */ | |
| if (p->dpms_off != 0 && | |
| p->dpms_off < 10 * 1000) | |
| p->dpms_off = 4 * 60 * 60 * 1000; /* 4 hours */ | |
| /* suspend may not be greater than off, unless off is 0. | |
| standby may not be greater than suspend, unless suspend is 0. | |
| */ | |
| if (p->dpms_off != 0 && | |
| p->dpms_suspend > p->dpms_off) | |
| p->dpms_suspend = p->dpms_off; | |
| if (p->dpms_suspend != 0 && | |
| p->dpms_standby > p->dpms_suspend) | |
| p->dpms_standby = p->dpms_suspend; | |
| /* These fixes above ignores the case | |
| suspend = 0 and standby > off ... | |
| */ | |
| if (p->dpms_off != 0 && | |
| p->dpms_standby > p->dpms_off) | |
| p->dpms_standby = p->dpms_off; | |
| if (p->dpms_standby == 0 && /* if *all* are 0, then DPMS is disabled */ | |
| p->dpms_suspend == 0 && | |
| p->dpms_off == 0 && | |
| !(p->dpms_quickoff_p) /* ... but we want to do DPMS quick off */ | |
| ) | |
| p->dpms_enabled_p = False; | |
| /* Set watchdog timeout to about half of the cycle timeout, but | |
| don't let it be faster than 1/2 minute or slower than 1 minute. | |
| */ | |
| p->watchdog_timeout = p->cycle * 0.6; | |
| if (p->watchdog_timeout < 27000) p->watchdog_timeout = 27000; /* 27 secs */ | |
| if (p->watchdog_timeout > 57000) p->watchdog_timeout = 57000; /* 57 secs */ | |
| if (p->pointer_hysteresis < 0) p->pointer_hysteresis = 0; | |
| if (p->pointer_hysteresis > 100) p->pointer_hysteresis = 100; | |
| if (p->auth_warning_slack < 0) p->auth_warning_slack = 0; | |
| if (p->auth_warning_slack > 300) p->auth_warning_slack = 300; | |
| } | |
| Bool | |
| senescent_p (void) | |
| { | |
| /* If you are in here because you're planning on disabling this warning | |
| before redistributing my software, please don't. | |
| I sincerely request that you do one of the following: | |
| 1: leave this code intact and this warning in place, -OR- | |
| 2: Remove xscreensaver from your distribution. | |
| I would seriously prefer that you not distribute my software at all | |
| than that you distribute one version and then never update it for | |
| years. | |
| I am *constantly* getting email from users reporting bugs that have | |
| been fixed for literally years who have no idea that the software | |
| they are running is years out of date. Yes, it would be great if we | |
| lived in the ideal world where people checked that they were running | |
| the latest release before they report a bug, but we don't. To most | |
| people, "running the latest release" is synonymous with "running the | |
| latest release that my distro packages for me." | |
| When they even bother to tell me what version they're running, I | |
| say, "That version is three years old!", and they say "But this is | |
| the latest version my distro ships". Then I say, "your distro | |
| sucks", and they say "but I don't know how to compile from source, | |
| herp derp I eat paste", and *everybody* goes away unhappy. | |
| It wastes an enormous amount of my time, and kind of makes me regret | |
| ever having released this software in the first place. | |
| So seriously. I ask that if you're planning on disabling this | |
| obsolescence warning, that you instead just remove xscreensaver from | |
| your distro entirely. Everybody will be happier that way. Check | |
| out gnome-screensaver instead, I understand it's really nice. | |
| Of course, my license allows you to ignore me and do whatever the | |
| fuck you want, but as the author, I hope you will have the common | |
| courtesy of complying with my request. | |
| Thank you! | |
| jwz, 2014 | |
| */ | |
| time_t now = time ((time_t *) 0); /* N */ | |
| struct tm *tm = localtime (&now); /* o */ | |
| const char *s = screensaver_id; /* */ | |
| char mon[4], year[5]; /* d */ | |
| int m, y, months; /* o */ | |
| s = strchr (s, ' '); if (!s) abort(); s++; /* n */ | |
| s = strchr (s, '('); if (!s) abort(); s++; /* ' */ | |
| s = strchr (s, '-'); if (!s) abort(); s++; /* t */ | |
| strncpy (mon, s, 3); /* */ | |
| mon[3] = 0; /* d */ | |
| s = strchr (s, '-'); if (!s) abort(); s++; /* o */ | |
| strncpy (year, s, 4); /* */ | |
| year[4] = 0; /* i */ | |
| y = atoi (year); /* t */ | |
| if (!strcmp(mon, "Jan")) m = 0; /* , */ | |
| else if (!strcmp(mon, "Feb")) m = 1; /* */ | |
| else if (!strcmp(mon, "Mar")) m = 2; /* s */ | |
| else if (!strcmp(mon, "Apr")) m = 3; /* t */ | |
| else if (!strcmp(mon, "May")) m = 4; /* o */ | |
| else if (!strcmp(mon, "Jun")) m = 5; /* p */ | |
| else if (!strcmp(mon, "Jul")) m = 6; /* , */ | |
| else if (!strcmp(mon, "Aug")) m = 7; /* */ | |
| else if (!strcmp(mon, "Sep")) m = 8; /* s */ | |
| else if (!strcmp(mon, "Oct")) m = 9; /* t */ | |
| else if (!strcmp(mon, "Nov")) m = 10; /* a */ | |
| else if (!strcmp(mon, "Dec")) m = 11; /* a */ | |
| else abort(); /* a */ | |
| months = ((((tm->tm_year + 1900) * 12) + tm->tm_mon) - /* h */ | |
| (y * 12 + m)); /* h */ | |
| /* h */ | |
| return (months > 18); /* p */ | |
| } |