/* Config file stuff for hcalc. Copyright 2023, B. Watson. Distributed under the terms of the GNU GPL. */ #include #include #include #include #include #include #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); }