aboutsummaryrefslogtreecommitdiff
path: root/config.c
blob: 2e644b556692e5b429fb70239dca3201796d6b0c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/* 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);
}