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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
|
#include "pom.h"
#include <stdio.h> // still needed for sprintf, even if POM_NO_STDIO is defined.
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <inttypes.h>
#if __GNUC__ >= 6
#define ATTRIBUTE_PRINTF(fmt, args) __attribute__ ((format(printf, fmt, args)))
#else
#define ATTRIBUTE_PRINTF(fmt, args)
#endif
#if _MSC_VER >= 1600
#define PRINTF_FORMAT_STRING _Printf_format_string_
#else
#define PRINTF_FORMAT_STRING
#endif
struct pom_error {
const pom_error *next;
const char *file;
uint64_t line;
const char *message;
};
struct main_conf;
struct pom_conf {
struct main_conf *main;
size_t prefix_len;
const struct conf_item *items;
size_t items_count;
};
struct conf_items {
const char *key;
const char *value;
const pom_conf *section;
};
struct main_conf {
struct conf_item *items;
size_t items_count;
};
#ifdef POM_NO_STDIO
#define fatal_error(...) abort()
#else
// fatal_error should only be called when the API is misused
// (e.g. `NULL` argument that shouldn't be `NULL`).
static void fatal_error(PRINTF_FORMAT_STRING const char *fmt, ...) ATTRIBUTE_PRINTF(1, 2);
static void
fatal_error(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
fprintf(stderr, "\n");
abort();
}
#endif
// Make an error with no next-error.
static pom_error *make_error(PRINTF_FORMAT_STRING const char *file, uint64_t line, const char *fmt, ...) ATTRIBUTE_PRINTF(3, 4);
static pom_error *
make_error(const char *file, uint64_t line, const char *fmt, ...) {
va_list args, args_copy;
va_start(args, fmt);
va_copy(args_copy, args);
bool bad_fmt = false;
int len = vsnprintf(NULL, 0, fmt, args);
if (len < 0 || len > INT_MAX - sizeof(pom_error) - 1) {
// Should probably never happen? Who knows though.
// In this case, we just use fmt as the error message.
bad_fmt = true;
len = strlen(fmt);
}
pom_error *err = malloc(sizeof(pom_error) + len + 1);
if (err) {
char *message = (char *)(err + 1);
if (bad_fmt) {
strcpy(message, fmt);
} else {
vsnprintf(message, len + 1, fmt, args);
}
err->file = file;
err->line = line;
err->message = message;
err->next = NULL;
}
return err;
}
const pom_error *
pom_error_next(const pom_error *error) {
if (!error) return NULL;
return error->next;
}
const char *
pom_error_file(const pom_error *error) {
if (!error)
fatal_error("%s called with NULL argument", __func__);
return error->file;
}
uint64_t
pom_error_line(const pom_error *error) {
if (!error)
fatal_error("%s called with NULL argument", __func__);
return error->line;
}
const char *
pom_error_message(const pom_error *error) {
if (!error)
fatal_error("%s called with NULL argument", __func__);
return error->message;
}
#ifndef POM_NO_STDIO
void
pom_error_print(const pom_error *error) {
if (!error) {
fprintf(stderr, "No error.\n");
return;
}
fprintf(stderr, "Error:\n");
for (; error; error = pom_error_next(error)) {
fprintf(stderr, "%s:%" PRIu64 ": %s\n", error->file, error->line, error->message);
}
}
#endif
pom_conf *
pom_load(const char *filename,
size_t (*read_func)(void *userdata, char *buf, size_t len),
void *userdata, pom_error **error) {
if (!filename)
fatal_error("%s called with NULL file name", __func__);
if (!read_func)
fatal_error("%s called with NULL read function", __func__);
// TODO
if (error) *error = NULL;
return NULL;
}
static size_t
read_string(void *vpstring, char *buf, size_t len) {
const char **pstring = vpstring;
const char *string = *pstring;
size_t i;
for (i = 0; i < len; i++, string++) {
if (*string == 0) break;
buf[i] = *string;
}
*pstring = string;
return i;
}
pom_conf *
pom_load_string(const char *filename, const char *string, pom_error **error) {
return pom_load(filename, read_string, &string, error);
}
#ifndef POM_NO_STDIO
static size_t
read_file(void *file, char *buf, size_t len) {
return fread(buf, 1, len, file);
}
pom_conf *
pom_load_file(const char *filename, FILE *file, pom_error **error) {
if (!filename)
fatal_error("%s called with NULL file name", __func__);
if (!file)
fatal_error("%s called with NULL file", __func__);
return pom_load(filename, read_file, file, error);
}
pom_conf *
pom_load_path(const char *path, pom_error **error) {
if (!path)
fatal_error("%s called with NULL file name", __func__);
FILE *fp = fopen(path, "rb");
if (!fp) {
if (error) {
const char *message = strerror(errno);
*error = make_error(path, 1, "Couldn't open file: %s", message);
}
return NULL;
}
pom_conf *conf = pom_load_file(path, fp, error);
fclose(fp);
return conf;
}
#endif
|