]>
Commit | Line | Data |
---|---|---|
d76ed9a9 AS |
1 | /* recdb.c - recursive/record database implementation |
2 | * Copyright 2000-2004 srvx Development Team | |
3 | * | |
83ff05c3 | 4 | * This file is part of x3. |
d76ed9a9 | 5 | * |
d0f04f71 | 6 | * x3 is free software; you can redistribute it and/or modify |
d76ed9a9 | 7 | * it under the terms of the GNU General Public License as published by |
348683aa | 8 | * the Free Software Foundation; either version 3 of the License, or |
d76ed9a9 AS |
9 | * (at your option) any later version. |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with srvx; if not, write to the Free Software Foundation, | |
18 | * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | |
19 | */ | |
20 | ||
21 | #include "recdb.h" | |
22 | #include "log.h" | |
23 | ||
24 | #ifdef HAVE_FCNTL_H | |
25 | #include <fcntl.h> | |
26 | #endif | |
27 | #ifdef HAVE_SYS_MMAN_H | |
28 | #include <sys/mman.h> | |
29 | #endif | |
30 | #ifdef HAVE_SYS_STAT_H | |
31 | #include <sys/stat.h> | |
32 | #endif | |
33 | ||
34 | /* 4 MiB on x86 */ | |
35 | #define MMAP_MAP_LENGTH (getpagesize()*1024) | |
36 | ||
37 | /* file format (grammar in Backus-Naur Form): | |
38 | * | |
39 | * database := record* | |
40 | * record := qstring [ '=' ] ( qstring | object | stringlist ) ';' | |
41 | * qstring := '"' ( [^\\] | ('\\' [\\n]) )* '"' | |
42 | * object := '{' record* '}' | |
43 | * stringlist := '(' [ qstring [',' qstring]* ] ')' | |
44 | * | |
45 | */ | |
46 | ||
47 | /* when a database or object is read from disk, it is represented as | |
48 | * a dictionary object, keys are names (what's left of the '=') and | |
49 | * values are 'struct record_data's | |
50 | */ | |
51 | ||
52 | struct recdb_context { | |
53 | int line; | |
54 | int col; | |
55 | }; | |
56 | ||
57 | enum recdb_filetype { | |
58 | RECDB_FILE, | |
59 | RECDB_STRING, | |
60 | RECDB_MMAP | |
61 | }; | |
62 | ||
63 | typedef struct recdb_file { | |
64 | const char *source; | |
65 | FILE *f; /* For RECDB_FILE, RECDB_MMAP */ | |
66 | char *s; /* For RECDB_STRING, RECDB_MMAP */ | |
67 | enum recdb_filetype type; | |
68 | size_t length; | |
69 | off_t pos; | |
70 | struct recdb_context ctx; | |
71 | jmp_buf env; | |
72 | } RECDB; | |
73 | ||
74 | typedef struct recdb_outfile { | |
75 | FILE *f; /* For RECDB_FILE, RECDB_MMAP */ | |
76 | char *s; /* For RECDB_STRING, RECDB_MMAP */ | |
77 | union { | |
78 | struct { /* For RECDB_STRING */ | |
79 | size_t chunksize; | |
80 | size_t alloc_length; | |
81 | } s; | |
82 | struct { /* For RECDB_MMAP */ | |
83 | off_t mmap_begin; | |
84 | size_t mmap_length; | |
85 | } m; | |
86 | } state; | |
87 | enum recdb_filetype type; | |
88 | off_t pos; | |
89 | int tablvl; | |
90 | #ifdef NDEBUG | |
91 | int need_tab; | |
92 | #endif | |
93 | } RECDB_OUT; | |
94 | ||
95 | #ifdef HAVE_MMAP | |
96 | static int mmap_error=0; | |
97 | #endif | |
98 | ||
99 | #define EOL '\n' | |
100 | ||
101 | #if 1 | |
102 | #define ABORT(recdb, code, ch) longjmp((recdb)->env, ((code) << 8) | (ch)) | |
103 | #else | |
104 | static void | |
105 | ABORT(RECDB *recdb, int code, unsigned char ch) { | |
106 | longjmp(recdb->env, code << 8 | ch); | |
107 | } | |
108 | #endif | |
109 | ||
110 | enum fail_codes { | |
111 | UNTERMINATED_STRING, | |
112 | EXPECTED_OPEN_QUOTE, | |
113 | EXPECTED_OPEN_BRACE, | |
114 | EXPECTED_OPEN_PAREN, | |
115 | EXPECTED_COMMA, | |
116 | EXPECTED_START_RECORD_DATA, | |
117 | EXPECTED_SEMICOLON, | |
118 | EXPECTED_RECORD_DATA | |
119 | }; | |
120 | ||
121 | static void parse_record_int(RECDB *recdb, char **pname, struct record_data **prd); | |
122 | ||
123 | /* allocation functions */ | |
124 | ||
125 | #define alloc_record_data_int() malloc(sizeof(struct record_data)) | |
126 | ||
127 | struct record_data * | |
128 | alloc_record_data_qstring(const char *string) | |
129 | { | |
130 | struct record_data *rd; | |
131 | rd = alloc_record_data_int(); | |
132 | SET_RECORD_QSTRING(rd, string); | |
133 | return rd; | |
134 | } | |
135 | ||
136 | struct record_data * | |
137 | alloc_record_data_object(dict_t obj) | |
138 | { | |
139 | struct record_data *rd; | |
140 | rd = alloc_record_data_int(); | |
141 | SET_RECORD_OBJECT(rd, obj); | |
142 | return rd; | |
143 | } | |
144 | ||
145 | struct record_data * | |
146 | alloc_record_data_string_list(struct string_list *slist) | |
147 | { | |
148 | struct record_data *rd; | |
149 | rd = alloc_record_data_int(); | |
150 | SET_RECORD_STRING_LIST(rd, slist); | |
151 | return rd; | |
152 | } | |
153 | ||
154 | struct string_list* | |
155 | alloc_string_list(int size) | |
156 | { | |
157 | struct string_list *slist; | |
158 | slist = malloc(sizeof(struct string_list)); | |
159 | slist->used = 0; | |
160 | slist->size = size; | |
161 | slist->list = slist->size ? malloc(size*sizeof(char*)) : NULL; | |
162 | return slist; | |
163 | } | |
164 | ||
165 | dict_t | |
166 | alloc_database(void) | |
167 | { | |
168 | dict_t db = dict_new(); | |
169 | dict_set_free_data(db, free_record_data); | |
170 | return db; | |
171 | } | |
172 | ||
173 | /* misc. operations */ | |
174 | ||
175 | void | |
176 | string_list_append(struct string_list *slist, char *string) | |
177 | { | |
178 | if (slist->used == slist->size) { | |
179 | if (slist->size) { | |
180 | slist->size <<= 1; | |
181 | slist->list = realloc(slist->list, slist->size*sizeof(char*)); | |
182 | } else { | |
183 | slist->size = 4; | |
184 | slist->list = malloc(slist->size*sizeof(char*)); | |
185 | } | |
186 | } | |
187 | slist->list[slist->used++] = string; | |
188 | } | |
189 | ||
190 | struct string_list * | |
191 | string_list_copy(struct string_list *slist) | |
192 | { | |
193 | struct string_list *new_list; | |
194 | unsigned int i; | |
195 | new_list = alloc_string_list(slist->size); | |
196 | new_list->used = slist->used; | |
197 | for (i=0; i<new_list->used; i++) { | |
198 | new_list->list[i] = strdup(slist->list[i]); | |
199 | } | |
200 | return new_list; | |
201 | } | |
202 | ||
203 | int slist_compare_two(const void *pa, const void *pb) | |
204 | { | |
205 | return irccasecmp(*(const char**)pa, *(const char **)pb); | |
206 | } | |
207 | ||
208 | void | |
209 | string_list_sort(struct string_list *slist) | |
210 | { | |
211 | qsort(slist->list, slist->used, sizeof(slist->list[0]), slist_compare_two); | |
212 | } | |
213 | ||
214 | struct record_data* | |
215 | database_get_path(dict_t db, const char *path) | |
216 | { | |
217 | char *new_path = strdup(path), *orig_path = new_path; | |
218 | char *part; | |
219 | struct record_data *rd; | |
220 | ||
221 | for (part=new_path; *new_path; new_path++) { | |
222 | if (*new_path != '/') continue; | |
223 | *new_path = 0; | |
224 | ||
225 | rd = dict_find(db, part, NULL); | |
226 | if (!rd || rd->type != RECDB_OBJECT) { | |
227 | free(orig_path); | |
228 | return NULL; | |
229 | } | |
230 | ||
231 | db = rd->d.object; | |
232 | part = new_path+1; | |
233 | } | |
234 | ||
235 | rd = dict_find(db, part, NULL); | |
236 | free(orig_path); | |
237 | return rd; | |
238 | } | |
239 | ||
240 | void* | |
241 | database_get_data(dict_t db, const char *path, enum recdb_type type) | |
242 | { | |
31f23f13 AS |
243 | assert(path != NULL); |
244 | if(!path) | |
245 | log_module(MAIN_LOG, LOG_WARNING, "Null path in database_get_data()"); | |
246 | /* log_module(MAIN_LOG, LOG_DEBUG, "Reading config option '%s'", path); */ | |
d76ed9a9 AS |
247 | struct record_data *rd = database_get_path(db, path); |
248 | return (rd && rd->type == type) ? rd->d.whatever : NULL; | |
249 | } | |
250 | ||
251 | /* free functions */ | |
252 | ||
253 | void | |
254 | free_string_list(struct string_list *slist) | |
255 | { | |
256 | unsigned int i; | |
257 | if (!slist) | |
258 | return; | |
259 | for (i=0; i<slist->used; i++) | |
260 | free(slist->list[i]); | |
261 | free(slist->list); | |
262 | free(slist); | |
263 | } | |
264 | ||
265 | void | |
266 | free_record_data(void *rdata) | |
267 | { | |
268 | struct record_data *r = rdata; | |
269 | switch (r->type) { | |
270 | case RECDB_INVALID: break; | |
271 | case RECDB_QSTRING: free(r->d.qstring); break; | |
272 | case RECDB_OBJECT: dict_delete(r->d.object); break; | |
273 | case RECDB_STRING_LIST: free_string_list(r->d.slist); break; | |
274 | } | |
275 | free(r); | |
276 | } | |
277 | ||
278 | /* parse functions */ | |
279 | ||
280 | static int | |
281 | dbeof(RECDB *recdb) | |
282 | { | |
283 | switch (recdb->type) { | |
284 | case RECDB_FILE: | |
285 | return feof(recdb->f); | |
286 | break; | |
287 | case RECDB_STRING: | |
288 | return !*recdb->s; | |
289 | break; | |
290 | case RECDB_MMAP: | |
291 | return ((size_t)recdb->pos >= recdb->length); | |
292 | break; | |
293 | default: | |
294 | return 1; | |
295 | break; | |
296 | } | |
297 | } | |
298 | ||
299 | static int | |
300 | dbgetc(RECDB *recdb) | |
301 | { | |
302 | int res; | |
303 | switch (recdb->type) { | |
304 | case RECDB_FILE: | |
305 | res = fgetc(recdb->f); | |
306 | break; | |
307 | case RECDB_STRING: | |
308 | case RECDB_MMAP: | |
309 | res = dbeof(recdb) ? EOF : recdb->s[recdb->pos++]; | |
310 | break; | |
311 | default: | |
312 | res = EOF; | |
313 | break; | |
314 | } | |
315 | if (res == EOL) recdb->ctx.line++, recdb->ctx.col=1; | |
316 | else if (res != EOF) recdb->ctx.col++; | |
317 | return res; | |
318 | } | |
319 | ||
320 | static void | |
321 | dbungetc(int c, RECDB *recdb) | |
322 | { | |
323 | switch (recdb->type) { | |
324 | case RECDB_FILE: | |
325 | ungetc(c, recdb->f); | |
326 | break; | |
327 | case RECDB_STRING: | |
328 | case RECDB_MMAP: | |
329 | recdb->s[--recdb->pos] = c; | |
330 | break; | |
331 | } | |
332 | if (c == EOL) recdb->ctx.line--, recdb->ctx.col=-1; | |
333 | else recdb->ctx.col--; | |
334 | } | |
335 | ||
336 | /* returns first non-whitespace, non-comment character (-1 for EOF found) */ | |
337 | int | |
338 | parse_skip_ws(RECDB *recdb) | |
339 | { | |
340 | int c, d, in_comment = 0; | |
341 | while (!dbeof(recdb)) { | |
342 | c = dbgetc(recdb); | |
343 | if (c == EOF) return EOF; | |
344 | if (isspace(c)) continue; | |
345 | if (c != '/') return c; | |
346 | if ((d = dbgetc(recdb)) == '*') { | |
347 | /* C style comment, with slash star comment star slash */ | |
348 | in_comment = 1; | |
349 | do { | |
350 | do { | |
351 | c = dbgetc(recdb); | |
352 | } while (c != '*' && c != EOF); | |
353 | if ((c = dbgetc(recdb)) == '/') in_comment = 0; | |
354 | } while (in_comment); | |
355 | } else if (d == '/') { | |
356 | /* C++ style comment, with slash slash comment newline */ | |
357 | do { | |
358 | c = dbgetc(recdb); | |
359 | } while (c != EOF && c != EOL); | |
360 | } else { | |
361 | if (d != EOF) dbungetc(d, recdb); | |
362 | return c; | |
363 | } | |
364 | } | |
365 | return -1; | |
366 | } | |
367 | ||
368 | char * | |
369 | parse_qstring(RECDB *recdb) | |
370 | { | |
371 | char *buff; | |
372 | int used=0, size=8, c; | |
373 | struct recdb_context start_ctx; | |
374 | unsigned int i; | |
375 | ||
376 | if ((c = parse_skip_ws(recdb)) == EOF) return NULL; | |
377 | start_ctx = recdb->ctx; | |
378 | if (c != '"') ABORT(recdb, EXPECTED_OPEN_QUOTE, c); | |
379 | buff = malloc(size); | |
380 | while (!dbeof(recdb) && (c = dbgetc(recdb)) != '"') { | |
381 | if (c != '\\') { | |
382 | /* There should never be a literal newline, as it is saved as a \n */ | |
383 | if (c == EOL) { | |
384 | dbungetc(c, recdb); | |
385 | ABORT(recdb, UNTERMINATED_STRING, ' '); | |
386 | } | |
387 | buff[used++] = c; | |
388 | } else { | |
389 | switch (c = dbgetc(recdb)) { | |
390 | case '0': /* \<octal>, 000 through 377 */ | |
391 | case '1': | |
392 | case '2': | |
393 | case '3': | |
394 | case '4': | |
395 | case '5': | |
396 | case '6': | |
397 | case '7': | |
398 | { | |
399 | char digits[3] = { (char)c, '\0', '\0' }; | |
400 | for (i=1; i < 3; i++) { | |
401 | /* Maximum of \377, so there's a max of 2 digits | |
402 | * if digits[0] > '3' (no \400, but \40 is fine) */ | |
403 | if (i == 2 && digits[0] > '3') { | |
404 | break; | |
405 | } | |
406 | if ((c = dbgetc(recdb)) == EOF) { | |
407 | break; | |
408 | } | |
409 | if ((c < '0') || (c > '7')) { | |
410 | dbungetc(c, recdb); | |
411 | break; | |
412 | } | |
413 | digits[i] = (char)c; | |
414 | } | |
415 | if (i) { | |
416 | c = (int)strtol(digits, NULL, 8); | |
417 | buff[used++] = c; | |
418 | } else { | |
419 | buff[used++] = '\0'; | |
420 | } | |
421 | } | |
422 | break; | |
423 | case 'x': /* Hex */ | |
424 | { | |
425 | char digits[3] = { '\0', '\0', '\0' }; | |
426 | for (i=0; i < 2; i++) { | |
427 | if ((c = dbgetc(recdb)) == EOF) { | |
428 | break; | |
429 | } | |
430 | if (!isxdigit(c)) { | |
431 | dbungetc(c, recdb); | |
432 | break; | |
433 | } | |
434 | digits[i] = (char)c; | |
435 | } | |
436 | if (i) { | |
437 | c = (int)strtol(digits, NULL, 16); | |
438 | buff[used++] = c; | |
439 | } else { | |
440 | buff[used++] = '\\'; | |
441 | buff[used++] = 'x'; | |
442 | } | |
443 | } | |
444 | break; | |
445 | case 'a': buff[used++] = '\a'; break; | |
446 | case 'b': buff[used++] = '\b'; break; | |
447 | case 't': buff[used++] = '\t'; break; | |
448 | case 'n': buff[used++] = EOL; break; | |
449 | case 'v': buff[used++] = '\v'; break; | |
450 | case 'f': buff[used++] = '\f'; break; | |
451 | case 'r': buff[used++] = '\r'; break; | |
452 | case '\\': buff[used++] = '\\'; break; | |
453 | case '"': buff[used++] = '"'; break; | |
454 | default: buff[used++] = '\\'; buff[used++] = c; break; | |
455 | } | |
456 | } | |
457 | if (used == size) { | |
458 | size <<= 1; | |
459 | buff = realloc(buff, size); | |
460 | } | |
461 | } | |
462 | if (c != '"' && dbeof(recdb)) { | |
463 | free(buff); | |
464 | recdb->ctx = start_ctx; | |
465 | ABORT(recdb, UNTERMINATED_STRING, EOF); | |
466 | } | |
467 | buff[used] = 0; | |
468 | return buff; | |
469 | } | |
470 | ||
471 | dict_t | |
472 | parse_object(RECDB *recdb) | |
473 | { | |
474 | dict_t obj; | |
475 | char *name; | |
476 | struct record_data *rd; | |
477 | int c; | |
478 | if ((c = parse_skip_ws(recdb)) == EOF) return NULL; | |
479 | if (c != '{') ABORT(recdb, EXPECTED_OPEN_BRACE, c); | |
480 | obj = alloc_object(); | |
481 | dict_set_free_keys(obj, free); | |
482 | while (!dbeof(recdb)) { | |
483 | if ((c = parse_skip_ws(recdb)) == '}') break; | |
484 | if (c == EOF) break; | |
485 | dbungetc(c, recdb); | |
486 | parse_record_int(recdb, &name, &rd); | |
487 | dict_insert(obj, name, rd); | |
488 | } | |
489 | return obj; | |
490 | } | |
491 | ||
492 | struct string_list * | |
493 | parse_string_list(RECDB *recdb) | |
494 | { | |
495 | struct string_list *slist; | |
496 | int c; | |
497 | if ((c = parse_skip_ws(recdb)) == EOF) return NULL; | |
498 | if (c != '(') ABORT(recdb, EXPECTED_OPEN_PAREN, c); | |
499 | slist = alloc_string_list(4); | |
500 | while (true) { | |
501 | c = parse_skip_ws(recdb); | |
502 | if (c == EOF || c == ')') break; | |
503 | dbungetc(c, recdb); | |
504 | string_list_append(slist, parse_qstring(recdb)); | |
505 | c = parse_skip_ws(recdb); | |
506 | if (c == EOF || c == ')') break; | |
507 | if (c != ',') ABORT(recdb, EXPECTED_COMMA, c); | |
508 | } | |
509 | return slist; | |
510 | } | |
511 | ||
512 | static void | |
513 | parse_record_int(RECDB *recdb, char **pname, struct record_data **prd) | |
514 | { | |
515 | int c; | |
516 | *pname = parse_qstring(recdb); | |
517 | c = parse_skip_ws(recdb); | |
518 | if (c == EOF) { | |
519 | if (!*pname) return; | |
520 | free(*pname); | |
521 | ABORT(recdb, EXPECTED_RECORD_DATA, EOF); | |
522 | } | |
523 | if (c == '=') c = parse_skip_ws(recdb); | |
524 | dbungetc(c, recdb); | |
525 | *prd = malloc(sizeof(**prd)); | |
526 | switch (c) { | |
527 | case '"': | |
528 | /* Don't use SET_RECORD_QSTRING, since that does an extra strdup() of the string. */ | |
529 | (*prd)->type = RECDB_QSTRING; | |
530 | (*prd)->d.qstring = parse_qstring(recdb); | |
531 | break; | |
532 | case '{': SET_RECORD_OBJECT(*prd, parse_object(recdb)); break; | |
533 | case '(': SET_RECORD_STRING_LIST(*prd, parse_string_list(recdb)); break; | |
534 | default: ABORT(recdb, EXPECTED_START_RECORD_DATA, c); | |
535 | } | |
536 | if ((c = parse_skip_ws(recdb)) != ';') ABORT(recdb, EXPECTED_SEMICOLON, c); | |
537 | } | |
538 | ||
539 | static dict_t | |
540 | parse_database_int(RECDB *recdb) | |
541 | { | |
542 | char *name; | |
543 | struct record_data *rd; | |
544 | dict_t db = alloc_database(); | |
545 | dict_set_free_keys(db, free); | |
546 | while (!dbeof(recdb)) { | |
547 | parse_record_int(recdb, &name, &rd); | |
548 | if (name) dict_insert(db, name, rd); | |
549 | } | |
550 | return db; | |
551 | } | |
552 | ||
553 | const char * | |
554 | failure_reason(int code) | |
555 | { | |
556 | const char *reason; | |
557 | switch (code >> 8) { | |
558 | case UNTERMINATED_STRING: reason = "Unterminated string"; break; | |
559 | case EXPECTED_OPEN_QUOTE: reason = "Expected '\"'"; break; | |
560 | case EXPECTED_OPEN_BRACE: reason = "Expected '{'"; break; | |
561 | case EXPECTED_OPEN_PAREN: reason = "Expected '('"; break; | |
562 | case EXPECTED_COMMA: reason = "Expected ','"; break; | |
563 | case EXPECTED_START_RECORD_DATA: reason = "Expected start of some record data"; break; | |
564 | case EXPECTED_SEMICOLON: reason = "Expected ';'"; break; | |
565 | case EXPECTED_RECORD_DATA: reason = "Expected record data"; break; | |
566 | default: reason = "Unknown error"; | |
567 | } | |
568 | if (code == -1) reason = "Premature end of file"; | |
569 | return reason; | |
570 | } | |
571 | ||
572 | void | |
573 | explain_failure(RECDB *recdb, int code) | |
574 | { | |
575 | log_module(MAIN_LOG, LOG_ERROR, "%s (got '%c') at %s line %d column %d.", | |
576 | failure_reason(code), code & 255, | |
577 | recdb->source, recdb->ctx.line, recdb->ctx.col); | |
578 | } | |
579 | ||
580 | const char * | |
581 | parse_record(const char *text, char **pname, struct record_data **prd) | |
582 | { | |
583 | RECDB recdb; | |
584 | int res; | |
585 | *pname = NULL; | |
586 | *prd = NULL; | |
587 | recdb.source = "<user-supplied text>"; | |
588 | recdb.f = NULL; | |
589 | recdb.s = strdup(text); | |
590 | recdb.length = strlen(text); | |
591 | recdb.pos = 0; | |
592 | recdb.type = RECDB_STRING; | |
593 | recdb.ctx.line = recdb.ctx.col = 1; | |
594 | if ((res = setjmp(recdb.env)) == 0) { | |
595 | parse_record_int(&recdb, pname, prd); | |
596 | return 0; | |
597 | } else { | |
598 | free(*pname); | |
599 | free(*prd); | |
600 | return failure_reason(res); | |
601 | } | |
602 | } | |
603 | ||
604 | dict_t | |
605 | parse_database(const char *filename) | |
606 | { | |
607 | RECDB recdb; | |
608 | int res; | |
609 | dict_t db; | |
610 | struct stat statinfo; | |
611 | ||
612 | recdb.source = filename; | |
613 | if (!(recdb.f = fopen(filename, "r"))) { | |
614 | log_module(MAIN_LOG, LOG_ERROR, "Unable to open database file '%s' for reading: %s", filename, strerror(errno)); | |
615 | return NULL; | |
616 | } | |
617 | ||
618 | if (fstat(fileno(recdb.f), &statinfo)) { | |
619 | log_module(MAIN_LOG, LOG_ERROR, "Unable to fstat database file '%s': %s", filename, strerror(errno)); | |
620 | return NULL; | |
621 | } | |
622 | recdb.length = (size_t)statinfo.st_size; | |
623 | ||
624 | #ifdef HAVE_MMAP | |
625 | /* Try mmap */ | |
626 | if (!mmap_error && (recdb.s = mmap(NULL, recdb.length, PROT_READ|PROT_WRITE, MAP_PRIVATE, fileno(recdb.f), 0)) != MAP_FAILED) { | |
627 | recdb.type = RECDB_MMAP; | |
2f61d1d7 | 628 | madvise(recdb.s, recdb.length, MADV_SEQUENTIAL); |
d76ed9a9 AS |
629 | } else { |
630 | /* Fall back to stdio */ | |
631 | if (!mmap_error) { | |
632 | log_module(MAIN_LOG, LOG_WARNING, "Unable to mmap database file '%s' (falling back to stdio): %s", filename, strerror(errno)); | |
633 | mmap_error = 1; | |
634 | } | |
635 | #else | |
636 | if (1) { | |
637 | #endif | |
638 | recdb.s = NULL; | |
639 | recdb.type = RECDB_FILE; | |
640 | } | |
641 | ||
642 | recdb.ctx.line = recdb.ctx.col = 1; | |
643 | recdb.pos = 0; | |
644 | ||
645 | if ((res = setjmp(recdb.env)) == 0) { | |
646 | db = parse_database_int(&recdb); | |
647 | } else { | |
648 | explain_failure(&recdb, res); | |
649 | _exit(1); | |
650 | } | |
651 | ||
652 | switch (recdb.type) { | |
653 | case RECDB_MMAP: | |
654 | #ifdef HAVE_MMAP | |
655 | munmap(recdb.s, recdb.length); | |
656 | #endif | |
657 | case RECDB_FILE: | |
658 | fclose(recdb.f); | |
659 | break; | |
660 | /* Appease gcc */ | |
661 | default: | |
662 | break; | |
663 | } | |
664 | return db; | |
665 | } |