config.c (8381B) download
1#include "config.h"
2
3#include "common.h"
4#include "mount.h"
5
6#include <ctype.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <sys/fcntl.h>
11#include <sys/mount.h>
12#include <unistd.h>
13
14#define CHECK_SECTION \
15 if (current_section == NULL) { \
16 result = P_USAGE; \
17 goto error; \
18 }
19
20#define CHECK_ROOT \
21 if (current_section != NULL) { \
22 result = P_USAGE; \
23 goto error; \
24 }
25
26#define CHECK_PARAMS_EQUALS(n) \
27 if (columns_size != (n)) { \
28 result = P_USAGE; \
29 goto error; \
30 }
31
32#define CHECK_PARAMS_BETWEEN(a, b) \
33 if (columns_size < (a) || columns_size > (b)) { \
34 result = P_USAGE; \
35 goto error; \
36 }
37
38#define CHECK_PARAMS_MORE(a) \
39 if (columns_size < (a)) { \
40 result = P_USAGE; \
41 goto error; \
42 }
43
44#define PARSE_BOOL(dest) \
45 if (streq(columns[1], "true")) \
46 dest = true; \
47 else if (streq(columns[1], "false")) { \
48 dest = false; \
49 } else { \
50 result = P_DATA; \
51 goto error; \
52 }
53
54
55section_t sections[SECTION_MAX];
56mount_t mounts[SECTION_MOUNT_MAX];
57int section_size = 0;
58int mount_size = 0;
59bool color = false;
60bool verbose = true;
61int timeout = 10;
62
63
64parse_error_t config_parse(int fd, const char* filename) {
65 FILE* file = fdopen(fd, "r");
66 return config_parsef(file, filename);
67}
68
69parse_error_t config_parsef(FILE* file, const char* filename) {
70 section_t* current_section = NULL;
71 bool in_mount = false;
72 int linenr = 0;
73 parse_error_t result = 0;
74 int columns_size = 0;
75 size_t alloc = 0;
76 char* line_origin = NULL;
77 ssize_t len = 0;
78 ssize_t last_blank = 0;
79 char* line = NULL;
80 char columns[10][100];
81
82 // `getline` fetches one line from `file`
83 while ((len = getline(&line_origin, &alloc, file)) > 0) {
84 linenr++;
85 // as `getline` doesn't add a terminating `\0`, concatinate it
86 if (len + 1 > (ssize_t) alloc) {
87 line = realloc(line_origin, alloc = len + 1);
88 if (line == NULL) {
89 printf("error: cannot allocate line\n");
90 result = P_ALLOC;
91 goto error;
92 }
93 line_origin = line;
94 } else {
95 line = line_origin;
96 }
97 line[len] = '\0';
98
99 last_blank = -1;
100
101 // some truncating
102 while (isblank(line[0]))
103 line++, len--;
104
105 for (ssize_t i = 0; i < len; i++) {
106 if (isblank(line[i])) {
107 if (last_blank == -1)
108 last_blank = i;
109 } else if (line[i] == '\n' || line[i] == ';' || line[i] == '#') {
110 len = (last_blank != -1) ? last_blank : i;
111 break;
112 } else {
113 last_blank = -1;
114 }
115 }
116
117 // if it's an empty string, skip it
118 if (len == 0)
119 continue;
120
121 // parse colums (aka. split by string)
122 columns_size = 0;
123 int column_index = 0;
124 bool string = false;
125 for (ssize_t i = 0; i <= len; i++) {
126 if (i == len || (isblank(line[i]) && !string)) {
127 if (column_index == 1 && columns[columns_size][0] == '-') {
128 columns[columns_size][0] = '\0';
129 columns_size++;
130 column_index = 0;
131 } else if (column_index > 0) {
132 columns[columns_size][column_index] = '\0';
133 columns_size++;
134 column_index = 0;
135 }
136 } else if (line[i] == '"') {
137 string = !string;
138 } else {
139 columns[columns_size][column_index++] = line[i];
140 }
141 }
142
143 // end
144 if (streq(columns[0], "end")) {
145 CHECK_PARAMS_EQUALS(1);
146
147 if (in_mount) {
148 in_mount = false;
149 } else if (current_section != NULL) {
150 current_section = NULL;
151 } else {
152 result = P_SCOPE;
153 goto error;
154 }
155 // <fstype> <source> <target> [options]
156 } else if (in_mount) {
157 CHECK_PARAMS_BETWEEN(3, 4);
158
159 mount_t* mnt = (current_section != NULL)
160 ? ¤t_section->mounts[current_section->mount_size++]
161 : &mounts[mount_size++];
162
163 mnt->try = columns[0][0] == '*';
164 mnt->type = strdupn(mnt->try ? columns[0] + 1 : columns[0]);
165 mnt->source = strdupn(columns[1]);
166 mnt->target = strdupn(columns[2]);
167 if (columns_size == 4) {
168 mnt->flags = mount_flags(columns[3], &mnt->options);
169 } else {
170 mnt->flags = 0;
171 mnt->options = NULL;
172 }
173 // mount
174 } else if (streq(columns[0], "mount")) {
175 CHECK_PARAMS_EQUALS(1);
176
177 in_mount = true;
178 // section
179 } else if (streq(columns[0], "section")) {
180 CHECK_ROOT;
181 CHECK_PARAMS_EQUALS(3);
182
183 if (current_section != NULL) {
184 result = P_REDEF;
185 goto error;
186 }
187
188 current_section = §ions[section_size++];
189 current_section->init = NULL;
190 current_section->mount_size = 0;
191 current_section->name = strdupn(columns[1]);
192 current_section->root = strdupn(columns[2]);
193 // rshare/share <dirs...>
194 } else if (streq(columns[0], "rshare") || streq(columns[0], "share")) {
195 CHECK_PARAMS_MORE(2);
196
197 for (int i = 1; i < columns_size; i++) {
198 mount_t* mnt = (current_section != NULL)
199 ? ¤t_section->mounts[current_section->mount_size++]
200 : &mounts[mount_size++];
201
202 char* target = columns[i];
203
204 mnt->try = target[0] == '*';
205 if (mnt->try)
206 target++;
207 mnt->source = strdupn(target);
208 mnt->target = strdupn(target);
209 mnt->type = NULL;
210 mnt->options = NULL;
211 mnt->flags = MS_BIND;
212 if (columns[0][0] == 'r') // aka. equals rshare
213 mnt->flags |= MS_REC;
214 }
215 // color <enable>
216 } else if (streq(columns[0], "color")) {
217 CHECK_ROOT;
218 CHECK_PARAMS_EQUALS(2);
219
220 PARSE_BOOL(color);
221 // verbose <enable>
222 } else if (streq(columns[0], "verbose")) {
223 CHECK_ROOT;
224 CHECK_PARAMS_EQUALS(2);
225
226 PARSE_BOOL(verbose);
227 // timeout <seconds>
228 } else if (streq(columns[0], "timeout")) {
229 CHECK_ROOT;
230 CHECK_PARAMS_EQUALS(2);
231
232 char* end;
233 timeout = strtol(columns[1], &end, 10);
234 if (end != strchr(columns[1], '\0')) {
235 result = P_DATA;
236 goto error;
237 }
238 // init <path>
239 } else if (streq(columns[0], "init")) {
240 CHECK_SECTION;
241 CHECK_PARAMS_MORE(2);
242
243 if (current_section->init != NULL) {
244 result = P_REDEF;
245 goto error;
246 }
247
248 current_section->init = strdupn(columns[1]);
249 // include <file>
250 } else if (streq(columns[0], "include")) {
251 CHECK_ROOT;
252 CHECK_PARAMS_EQUALS(2);
253
254 int fd = open(columns[1], O_RDONLY | O_NONBLOCK);
255 result = config_parse(fd, columns[1]);
256 close(fd);
257 if (result != 0)
258 goto cleanup;
259 // mmpf, unknown command
260 } else {
261 result = P_IDENTIFIER;
262 goto error;
263 }
264 }
265
266 // if there were no sections, raise an error as that not the intension
267 if (section_size == 0) {
268 result = P_SECTION;
269 goto error;
270 }
271
272 // if you reach this, there were no errors
273 // just skip to `cleanup`
274 goto cleanup;
275
276error:
277 // pretty-print error
278 printf("error in %s:%d: ", filename, linenr);
279 switch (result) {
280 case P_ALLOC:
281 printf("cannot allocate line\n");
282 break;
283 case P_SECTION:
284 printf("no section defined\n");
285 break;
286 case P_IDENTIFIER:
287 printf("unknown identifier '%s'\n", columns[0]);
288 break;
289 case P_USAGE:
290 printf("invalid usage of command '%s'\n", columns[0]);
291 break;
292 case P_SCOPE:
293 printf("invalid scope of command '%s'\n", columns[0]);
294 break;
295 case P_DATA:
296 printf("invalid paramter-type of '%s'\n", columns[0]);
297 break;
298 case P_REDEF:
299 printf("redefinition of '%s'\n", columns[0]);
300 break;
301 }
302 config_cleanup();
303
304cleanup:
305 if (line_origin != NULL)
306 free(line_origin);
307
308 return result;
309}
310
311void config_free_mount(mount_t* mount, int size) {
312 for (int i = 0; i < size; i++) {
313 if (mount[i].type != NULL)
314 free((void*) mount[i].type);
315 if (mount[i].source != NULL)
316 free((void*) mount[i].source);
317 if (mount[i].target != NULL)
318 free((void*) mount[i].target);
319 if (mount[i].options != NULL)
320 free((void*) mount[i].options);
321 }
322}
323
324void config_cleanup() {
325 config_free_mount(mounts, mount_size);
326 mount_size = 0;
327
328 for (int i = 0; i < section_size; i++) {
329 if (sections[i].name != NULL)
330 free((void*) sections[i].name);
331 if (sections[i].root != NULL)
332 free((void*) sections[i].root);
333 if (sections[i].init != NULL)
334 free((void*) sections[i].init);
335 config_free_mount(sections[i].mounts, sections[i].mount_size);
336 }
337 section_size = 0;
338}
339
340void config_reset() {
341 config_cleanup();
342
343 section_size = 0;
344 mount_size = 0;
345 color = false;
346 verbose = true;
347 timeout = 10;
348}