diff options
Diffstat (limited to 'config.c')
-rw-r--r-- | config.c | 157 |
1 files changed, 157 insertions, 0 deletions
diff --git a/config.c b/config.c new file mode 100644 index 0000000..752dec5 --- /dev/null +++ b/config.c @@ -0,0 +1,157 @@ +/* Config file stuff for hcalc. + Copyright 2023, B. Watson. + Distributed under the terms of the GNU GPL. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <sys/stat.h> +#include "hcalc.h" + +#define SIGNATURE "\x88\x83\xa1\xac\xa3\x82\x97\x00" +#define SIGLEN (sizeof(SIGNATURE) - 1) +#define TRAILER 0x626c6168L + +#define FILENAME ".hcalc.cfg" + +#define MAX_HOME 8192 + +/* signature may end up in file(1) magic. + doublesize and trailer are there to avoid using configs created + on a different architecture. + Also, the file size is checked against the size of the struct. + In theory this could be affected by compiler options, but on my + system, Xlib code compiled with -fpack-struct fails to run anyway. + I suspect this is true everywhere... + */ +struct hcalc_config { + char signature[SIGLEN]; + char doublesize; + char winsize; + char base; + char quiet; + double value, saved, stored; + long trailer; +}; + +char *home_dir = NULL; +char config_path[MAX_HOME]; /* ludicrous size */ + +size_t filesize(FILE *f) { + struct stat st; + + if(fstat(fileno(f), &st) < 0) { + perror("fstat"); + return 0; + } + + return st.st_size; +} + +void set_home_dir(void) { + home_dir = getenv("HOME"); /* could be fancier (getpwent()) */ + if(!home_dir) return; + if(strlen(home_dir) > (MAX_HOME - 100)) + home_dir = NULL; +} + +void set_config_path(void) { + sprintf(config_path, "%s/%s", home_dir, FILENAME); +/* fprintf(stderr, "Using config file: %s\n", config_path); */ +} + +void load_config(void) { + FILE *f; + struct hcalc_config conf; + int result; + +/* printf("load_config() called\n"); */ + set_home_dir(); + if(!home_dir) { + fprintf(stderr, "HOME not set or too long, not loading/saving config file.\n"); + return; + } + set_config_path(); + + f = fopen(config_path, "rb"); + if(!f) { + if(errno != ENOENT) perror(config_path); + return; + } + + if(filesize(f) != sizeof(conf)) { + fprintf(stderr, "%s: wrong size (should be %ld bytes), ignoring.\n", config_path, sizeof(conf)); + fclose(f); + return; + } + + fread(&conf, 1, sizeof(conf), f); + result = ferror(f); + fclose(f); + + if(result) { + perror(config_path); + return; + } + + if(memcmp(conf.signature, SIGNATURE, SIGLEN) != 0) { + fprintf(stderr, "%s: Bad config file signature, ignoring\n", config_path); + return; + } + + if(conf.doublesize != sizeof(double) || conf.trailer != TRAILER) { + fprintf(stderr, "%s: Bad config file architecture, ignoring\n", config_path); + return; + } + +/* fprintf(stderr, "%s: config file OK\n", config_path); */ + value = conf.value; + saved = conf.saved; + stored = conf.stored; + winsize = conf.winsize; + base = conf.base; + quiet = conf.quiet; +} + +void save_config(void) { + FILE *f; + struct hcalc_config conf; + int result; + +/* printf("save_config() called\n"); */ + set_home_dir(); + if(!home_dir) return; + set_config_path(); + + unlink(config_path); /* avoid symlink weirdness */ + + f = fopen(config_path, "wb"); + if(!f) { + perror(config_path); + return; + } + + memcpy(conf.signature, SIGNATURE, SIGLEN); + conf.doublesize = sizeof(double); + conf.value = value; + conf.saved = saved; + conf.stored = stored; + conf.winsize = winsize; + conf.base = base; + conf.quiet = quiet; + conf.trailer = TRAILER; + + fwrite(&conf, 1, sizeof(conf), f); + result = ferror(f); + + if(result) { + perror(config_path); + } else { +/* fprintf(stderr, "Wrote config OK\n"); */ + } + + fclose(f); +} |