/* -*- mode: c; style: linux -*- */ /* pref-file.c * Copyright (C) 2000 Helix Code, Inc. * * Written by Bradford Hovinen * Parts written by Jamie Zawinski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include #include #include #include #include #include #include #include #include "pref-file.h" #include "rc-parse.h" #include "daemon.h" #define START_BUF_SIZE 1024 #define MIN_FREE_BUF 128 /* From xscreensaver 3.24 driver/prefs.c line 80 ... */ static char * chase_symlinks (const char *file) { # ifdef HAVE_REALPATH char buf[2048], *msg; if (file) { if (realpath (file, buf)) return g_strdup (buf); msg = g_strdup_printf ("realpath: %s", g_strerror (errno)); g_warning (msg); } # endif /* HAVE_REALPATH */ return 0; } static const char * init_file_name (void) { static char *file = 0; if (!file) file = g_concat_dir_and_file (g_get_home_dir (), ".xscreensaver"); if (file && *file) return file; else return NULL; } 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 = g_strconcat (name, suffix, NULL); if (n2) g_free (n2); } if (file && *file) return file; else return 0; } /* From xscreensaver 3.24 driver/prefs.c line 211 ... */ static char * get_line (FILE *file, int *line_no) { char *buf, *start; int buf_size = START_BUF_SIZE, free_buf = START_BUF_SIZE; int len, l; buf = start = g_new (char, buf_size); (*line_no)++; if (!fgets (start, buf_size, file)) { g_free (start); return NULL; } len = strlen (start); while (buf[len-1] != '\n' || buf[len-2] == '\\') { free_buf -= len; buf += len; if (free_buf < MIN_FREE_BUF) { free_buf += buf_size; buf_size *= 2; l = buf - start; start = g_renew (char, start, buf_size); buf = start + l; } if (buf[-2] == '\\') (*line_no)++; if (!fgets (buf, free_buf - 1, file)) return start; len = strlen (buf); } return start; } static char * transform_line (char *line) { char *line1; int i, i1 = 0, len; len = strlen (line); line1 = g_new (char, len + 1); for (i = 0; i <= len; i++) { if (line[i] != '\\') { line1[i1++] = line[i]; } else { i++; switch (line[i]) { case 'n': line1[i1++] = '\n'; break; case 'r': line1[i1++] = '\r'; break; case 't': line1[i1++] = '\t'; break; case '\\': line1[i1++] = '\\'; break; case '\n': break; default: line1[i1++] = line[i]; break; } } } return line1; } /* Adapted from xscreensaver 3.24 driver/prefs.c line 257 ... */ static GTree * parse_config_file (const char *file_name) { struct stat st; FILE *in; char *buf; GTree *config_db; gint line_no; char *line, *key, *value; if (!g_file_test (file_name, G_FILE_TEST_ISFILE)) return NULL; in = fopen (file_name, "r"); if (!in) { g_warning ("error reading \"%s\": %s", file_name, g_strerror (errno)); return NULL; } if (fstat (fileno(in), &st) != 0) { g_warning ("couldn't re-stat \"%s\": %s", file_name, g_strerror (errno)); return NULL; } config_db = g_tree_new ((GCompareFunc) strcmp); line_no = 0; while ((buf = get_line (in, &line_no))) { line = transform_line (buf); g_free (buf); key = g_strstrip (line); if (*key == '#' || *key == '!' || *key == ';' || *key == '\n' || *key == 0) continue; value = strchr (key, ':'); if (!value) { g_warning("%s:%d: unparsable line: %s\n", file_name, line_no, key); continue; } else { *value++ = 0; value = g_strstrip (value); } g_tree_insert (config_db, g_strdup (key), g_strdup (value)); } fclose (in); return config_db; } gboolean preferences_load_from_file (Preferences *prefs) { prefs->config_db = parse_config_file (init_file_name ()); if (!prefs->config_db) return FALSE; else return TRUE; } /* Writing */ /* From xscreensaver 3.24 driver/prefs.c line 168 ... */ static const char * const pref_names[] = { "timeout", "cycle", "lock", "lockVTs", "lockTimeout", "passwdTimeout", "visualID", "installColormap", "verbose", "timestamp", "splashDuration", "demoCommand", "prefsCommand", "helpURL", "loadURL", "nice", "fade", "unfade", "fadeSeconds", "fadeTicks", "captureStderr", "font", "", "programs", "", "pointerPollTime", "windowCreationTimeout", "initialDelay", "sgiSaverExtension", "mitSaverExtension", "xidleExtension", "procInterrupts", "overlayStderr", 0 }; /* From xscreensaver 3.24 driver/prefs.c line 397 ... */ 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; } /* From xscreensaver 3.24 driver/prefs.c line 453 ... */ static void write_entry (FILE *out, const char *key, const char *value) { char *v = g_strdup(value ? value : ""); char *v2 = v; char *nl = 0; int col; gboolean programs_p = (!strcmp(key, "programs")); int tab = (programs_p ? 32 : 16); gboolean first = TRUE; fprintf (out, "%s:", key); col = strlen(key) + 1; while (1) { if (!programs_p) v2 = g_strstrip(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"); g_free(v); } static int write_preference_cb (gchar *key, gchar *value, FILE *out) { write_entry (out, key, value); return FALSE; } /* Adapted from xscreensaver 3.24 driver/prefs.c line 537 ... */ gint preferences_save_to_file (Preferences *prefs) { 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; FILE *out; if (!name) goto END; if (n2) name = n2; if (prefs->verbose) g_message ("writing \"%s\".\n", name); unlink (tmp_name); out = fopen(tmp_name, "w"); if (!out) { g_warning ("error writing \"%s\": %s", name, g_strerror (errno)); 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 (g_file_test (name, G_FILE_TEST_ISFILE)) { 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) { g_warning ("error fchmodding \"%s\" to 0%o: %s", tmp_name, (guint) mode, g_strerror (errno)); goto END; } } { struct passwd *pw = getpwuid (getuid ()); char *whoami = (pw && pw->pw_name && *pw->pw_name ? pw->pw_name : ""); 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", "XScreenSaver", "xscreensaver", "3.24", whoami, timestr); } g_tree_traverse (prefs->config_db, (GTraverseFunc) write_preference_cb, G_IN_ORDER, out); fprintf(out, "\n"); if (fclose(out) == 0) { time_t write_date = 0; if (stat (tmp_name, &st) == 0) { write_date = st.st_mtime; } else { g_warning ("couldn't stat \"%s\": %s", tmp_name, g_strerror (errno)); unlink (tmp_name); goto END; } if (rename (tmp_name, name) != 0) { g_warning ("error renaming \"%s\" to \"%s\": %s", tmp_name, name, g_strerror (errno)); unlink (tmp_name); goto END; } else { prefs->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 (); restart_xscreensaver (); status = 0; /* wrote and renamed successfully! */ } } else { g_warning ("error closing \"%s\": %s", name, g_strerror (errno)); unlink (tmp_name); goto END; } END: if (n2) g_free (n2); return status; }