Freeciv-3.4
Loading...
Searching...
No Matches
registry_ini.c
Go to the documentation of this file.
1/***********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12***********************************************************************/
13
14/**************************************************************************
15 The idea with this file is to create something similar to the ms-windows
16 .ini files functions.
17 however the interface is nice. ie:
18 secfile_lookup_str(file, "player%d.unit%d.name", plrno, unitno);
19***************************************************************************/
20
21/**************************************************************************
22 Description of the file format:
23 (This is based on a format by the original authors, with
24 various incremental extensions. --dwp)
25
26 - Whitespace lines are ignored, as are lines where the first
27 non-whitespace character is ';' (comment lines).
28 Optionally '#' can also be used for comments.
29
30 - A line of the form:
31 *include "filename"
32 includes the named file at that point. (The '*' must be the
33 first character on the line.) The file is found by looking in
34 FREECIV_DATA_PATH. Non-infinite recursive includes are allowed.
35
36 - A line with "[name]" labels the start of a section with
37 that name; one of these must be the first non-comment line in
38 the file. Any spaces within the brackets are included in the
39 name, but this feature (?) should probably not be used...
40
41 - Within a section, lines have one of the following forms:
42 subname = "stringvalue"
43 subname = -digits
44 subname = digits
45 subname = TRUE
46 sunname = FALSE
47 for a value with given name and string, negative integer, and
48 positive integer values, respectively. These entries are
49 referenced in the following functions as "sectionname.subname".
50 The section name should not contain any dots ('.'); the subname
51 can, but they have no particular significance. There can be
52 optional whitespace before and/or after the equals sign.
53 You can put a newline after (but not before) the equals sign.
54
55 Backslash is an escape character in strings (double-quoted strings
56 only, not names); recognised escapes are \n, \\, and \".
57 (Any other <char> is just treated as <char>.)
58
59 - Gettext markings: You can surround strings like so:
60 foo = _("stringvalue")
61 The registry just ignores these extra markings, but this is
62 useful for marking strings for translations via gettext tools.
63
64 - Multiline strings: Strings can have embedded newlines, eg:
65 foo = _("
66 This is a string
67 over multiple lines
68 ")
69 This is equivalent to:
70 foo = _("\nThis is a string\nover multiple lines\n")
71 Note that if you misplace the trailing doublequote you can
72 easily end up with strange errors reading the file...
73
74 - Strings read from a file: A file can be read as a string value:
75 foo = *filename.txt*
76
77 - Vector format: An entry can have multiple values separated
78 by commas, eg:
79 foo = 10, 11, "x"
80 These are accessed by names "foo", "foo,1" and "foo,2"
81 (with section prefix as above). So the above is equivalent to:
82 foo = 10
83 foo,1 = 11
84 foo,2 = "x"
85 As in the example, in principle you can mix integers and strings,
86 but the calling program will probably require elements to be the
87 same type. Note that the first element of a vector is not "foo,0",
88 in order that the name of the first element is the same whether or
89 not there are subsequent elements. However as a convenience, if
90 you try to lookup "foo,0" then you get back "foo". (So you should
91 never have "foo,0" as a real name in the datafile.)
92
93 - Tabular format: The lines:
94 foo = { "bar", "baz", "bax"
95 "wow", 10, -5
96 "cool", "str"
97 "hmm", 314, 99, 33, 11
98 }
99 are equivalent to the following:
100 foo0.bar = "wow"
101 foo0.baz = 10
102 foo0.bax = -5
103 foo1.bar = "cool"
104 foo1.baz = "str"
105 foo2.bar = "hmm"
106 foo2.baz = 314
107 foo2.bax = 99
108 foo2.bax,1 = 33
109 foo2.bax,2 = 11
110 The first line specifies the base name and the column names, and the
111 subsequent lines have data. Again it is possible to mix string and
112 integer values in a column, and have either more or less values
113 in a row than there are column headings, but the code which uses
114 this information (via the registry) may set more stringent conditions.
115 If a row has more entries than column headings, the last column is
116 treated as a vector (as above). You can optionally put a newline
117 after '=' and/or after '{'.
118
119 The equivalence above between the new and old formats is fairly
120 direct: internally, data is converted to the old format.
121 In principle it could be a good idea to represent the data
122 as a table (2-d array) internally, but the current method
123 seems sufficient and relatively simple...
124
125 There is a limited ability to save data in tabular:
126 So long as the section_file is constructed in an expected way,
127 tabular data (with no missing or extra values) can be saved
128 in tabular form. (See section_file_save().)
129
130 - Multiline vectors: if the last non-comment non-whitespace
131 character in a line is a comma, the line is considered to
132 continue on to the next line. Eg:
133 foo = 10,
134 11,
135 "x"
136 This is equivalent to the original "vector format" example above.
137 Such multi-lines can occur for column headings, vectors, or
138 table rows, again with some potential for strange errors...
139
140***************************************************************************/
141
142/**************************************************************************
143 Hashing registry lookups: (by dwp)
144 - Have a hash table direct to entries, bypassing sections division.
145 - For convenience, store the key (the full section+entry name)
146 in the hash table (some memory overhead).
147 - The number of entries is fixed when the hash table is built.
148 - Now uses hash.c
149**************************************************************************/
150
151#ifdef HAVE_CONFIG_H
152#include <fc_config.h>
153#endif
154
155#include <ctype.h>
156#include <stdarg.h>
157#include <stdlib.h>
158#include <string.h>
159
160/* utility */
161#include "astring.h"
162#include "bugs.h"
163#include "deprecations.h"
164#include "fcintl.h"
165#include "inputfile.h"
166#include "ioz.h"
167#include "log.h"
168#include "mem.h"
169#include "registry.h"
170#include "section_file.h"
171#include "shared.h"
172#include "support.h"
173
174#include "registry_ini.h"
175
176#define MAX_LEN_SECPATH 1024
177
178/* used for word wrapping, the longest output line may actually be
179 * 78 chars after stuff like _("..."), gets added.
180 */
181#define MAX_LINE_LEN 72
182
183/* Set to FALSE for old-style savefiles. */
184#define SAVE_TABLES TRUE
185
186#define SPECVEC_TAG astring
187#include "specvec.h"
188
189static inline bool entry_used(const struct entry *pentry);
190static inline void entry_use(struct entry *pentry);
191
192static void entry_to_file(const struct entry *pentry, fz_FILE *fs);
193static void entry_from_inf_token(struct section *psection, const char *name,
194 const char *tok, struct inputfile *file);
195
196/* An 'entry' is a string, integer, boolean or string vector;
197 * See enum entry_type in registry.h.
198 */
199struct entry {
200 struct section *psection; /* Parent section. */
201 char *name; /* Name, not including section prefix. */
202 enum entry_type type; /* The type of the entry. */
203 int used; /* Number of times entry looked up. */
204 char *comment; /* Comment, may be nullptr. */
205
206 union {
207 /* ENTRY_BOOL */
208 struct {
209 bool value;
210 } boolean;
211 /* ENTRY_INT */
212 struct {
213 int value;
214 } integer;
215 /* ENTRY_FLOAT */
216 struct {
217 float value;
218 } floating;
219 /* ENTRY_STR */
220 struct {
221 char *value; /* Malloced string. */
222 bool escaped; /* " or $. Usually TRUE */
223 bool raw; /* Do not add anything. */
224 bool gt_marking; /* Save with gettext marking. */
225 } string;
226 };
227};
228
230 const char *name,
231 const char *value);
232static struct entry *section_entry_comment_new(struct section *psection,
233 const char *comment);
234
235/**********************************************************************/
238static const char *datafilename(const char *filename)
239{
240 return fileinfoname(get_data_dirs(), filename);
241}
242
243/**********************************************************************/
246static bool is_secfile_entry_name_valid(const char *name)
247{
248 static const char *const allowed = "_.,-[]";
249
250 while ('\0' != *name) {
251 if (!fc_isalnum(*name) && strchr(allowed, *name) == nullptr) {
252 return FALSE;
253 }
254 name++;
255 }
256 return TRUE;
257}
258
259/**********************************************************************/
262static bool secfile_hash_insert(struct section_file *secfile,
263 struct entry *pentry)
264{
265 char buf[256];
266 struct entry *hentry;
267
268 if (secfile->hash.entries == nullptr) {
269 /* Consider as success if this secfile doesn't have built the entries
270 * hash table. */
271 return TRUE;
272 }
273
274 entry_path(pentry, buf, sizeof(buf));
276 nullptr, &hentry)) {
278 if (!secfile->allow_duplicates) {
280 "Tried to insert same value twice: %s", buf);
281 return FALSE;
282 }
283 }
284
285 return TRUE;
286}
287
288/**********************************************************************/
291static bool secfile_hash_delete(struct section_file *secfile,
292 struct entry *pentry)
293{
294 char buf[256];
295
296 if (secfile->hash.entries == nullptr) {
297 /* Consider as success if this secfile doesn't have built the entries
298 * hash table. */
299 return TRUE;
300 }
301
302 entry_path(pentry, buf, sizeof(buf));
303 return entry_hash_remove(secfile->hash.entries, buf);
304}
305
306/**********************************************************************/
310 const char *filename,
311 const char *section,
312 bool allow_duplicates)
313{
314 struct section_file *secfile;
315 struct section *psection = nullptr;
316 struct section *single_section = nullptr;
317 bool table_state = FALSE; /* TRUE when within tabular format. */
318 int table_lineno = 0; /* Row number in tabular, 0 top data row. */
319 const char *tok;
320 int i;
321 struct astring base_name = ASTRING_INIT; /* for table or single entry */
323 struct astring_vector columns; /* astrings for column headings */
324 bool found_my_section = FALSE;
325 bool error = FALSE;
326
327 if (!inf) {
328 return nullptr;
329 }
330
331 /* Assign the real value later, to speed up the creation of new entries. */
332 secfile = secfile_new(TRUE);
333 if (filename) {
334 secfile->name = fc_strdup(filename);
335 } else {
336 secfile->name = nullptr;
337 }
338
339 astring_vector_init(&columns);
340
341 if (filename) {
342 log_verbose("Reading registry from \"%s\"", filename);
343 } else {
344 log_verbose("Reading registry");
345 }
346
347 while (!inf_at_eof(inf)) {
348 if (inf_token(inf, INF_TOK_EOL)) {
349 continue;
350 }
351 if (inf_at_eof(inf)) {
352 /* may only realise at eof after trying to read eol above */
353 break;
354 }
356 if (tok) {
357 if (found_my_section) {
358 /* This shortcut will stop any further loading after the requested
359 * section has been loaded (i.e., at the start of a new section).
360 * This is needed to make the behavior useful, since the whole
361 * purpose is to short-cut further loading of the file. However
362 * normally a section may be split up, and that will no longer
363 * work here because it will be short-cut. */
364 SECFILE_LOG(secfile, psection, "%s",
365 inf_log_str(inf, "Found requested section; finishing"));
366 goto END;
367 }
368 if (table_state) {
369 SECFILE_LOG(secfile, psection, "%s",
370 inf_log_str(inf, "New section during table"));
371 error = TRUE;
372 goto END;
373 }
374 /* Check if we already have a section with this name.
375 (Could ignore this and have a duplicate sections internally,
376 but then secfile_get_secnames_prefix would return duplicates.)
377 Duplicate section in input are likely to be useful for includes.
378 */
379 psection = secfile_section_by_name(secfile, tok);
380 if (!psection) {
381 if (!section || strcmp(tok, section) == 0) {
382 psection = secfile_section_new(secfile, tok);
383 if (section) {
384 single_section = psection;
386 }
387 }
388 }
389 if (!inf_token(inf, INF_TOK_EOL)) {
390 SECFILE_LOG(secfile, psection, "%s",
391 inf_log_str(inf, "Expected end of line"));
392 error = TRUE;
393 goto END;
394 }
395 continue;
396 }
398 if (!table_state) {
399 SECFILE_LOG(secfile, psection, "%s",
400 inf_log_str(inf, "Misplaced \"}\""));
401 error = TRUE;
402 goto END;
403 }
404 if (!inf_token(inf, INF_TOK_EOL)) {
405 SECFILE_LOG(secfile, psection, "%s",
406 inf_log_str(inf, "Expected end of line"));
407 error = TRUE;
408 goto END;
409 }
411 continue;
412 }
413 if (table_state) {
414 i = -1;
415 do {
416 int num_columns = astring_vector_size(&columns);
417
418 i++;
419
420 if (i < num_columns) {
421 astr_set(&field_name, "%s%d.%s", astr_str(&base_name),
422 table_lineno, astr_str(&columns.p[i]));
423 } else {
424 astr_set(&field_name, "%s%d.%s,%d", astr_str(&base_name),
425 table_lineno, astr_str(&columns.p[num_columns - 1]),
426 (int) (i - num_columns + 1));
427 }
428
429 inf_discard_tokens(inf, INF_TOK_EOL); /* Allow newlines */
430 if (!(tok = inf_token(inf, INF_TOK_VALUE))) {
431 SECFILE_LOG(secfile, psection, "%s",
432 inf_log_str(inf, "Expected value for %s",
434 error = TRUE;
435 goto END;
436 }
437
439 } while (inf_token(inf, INF_TOK_COMMA));
440
441 if (!inf_token(inf, INF_TOK_EOL)) {
442 SECFILE_LOG(secfile, psection, "%s",
443 inf_log_str(inf, "Expected end of line or comma"));
444 error = TRUE;
445 goto END;
446 }
447 table_lineno++;
448 continue;
449 }
450
451 if (!(tok = inf_token(inf, INF_TOK_ENTRY_NAME))) {
452 SECFILE_LOG(secfile, psection, "%s",
453 inf_log_str(inf, "Expected entry name"));
454 error = TRUE;
455 goto END;
456 }
457
458 /* need to store tok before next calls: */
459 astr_set(&base_name, "%s", tok);
460
461 inf_discard_tokens(inf, INF_TOK_EOL); /* allow newlines */
462
464 i = -1;
465 do {
466 i++;
467 inf_discard_tokens(inf, INF_TOK_EOL); /* Allow newlines */
468 if (!(tok = inf_token(inf, INF_TOK_VALUE))) {
469 SECFILE_LOG(secfile, psection, "%s",
470 inf_log_str(inf, "Expected column header %s,%d",
471 astr_str(&base_name), i));
472 error = TRUE;
473 goto END;
474 }
475 if (tok[0] != '\"') {
476 SECFILE_LOG(secfile, psection, "%s",
477 inf_log_str(inf, "Table column header non-string"));
478 error = TRUE;
479 goto END;
480 }
481 { /* Expand columns: */
482 int j, n_prev;
483
484 n_prev = astring_vector_size(&columns);
485 for (j = i + 1; j < n_prev; j++) {
486 astr_free(&columns.p[j]);
487 }
488 astring_vector_reserve(&columns, i + 1);
489 for (j = n_prev; j < i + 1; j++) {
490 astr_init(&columns.p[j]);
491 }
492 }
493 astr_set(&columns.p[i], "%s", tok + 1);
494
495 } while (inf_token(inf, INF_TOK_COMMA));
496
497 if (!inf_token(inf, INF_TOK_EOL)) {
498 SECFILE_LOG(secfile, psection, "%s",
499 inf_log_str(inf, "Expected end of line or comma"));
500 error = TRUE;
501 goto END;
502 }
504 table_lineno = 0;
505 continue;
506 }
507 /* ordinary value: */
508 i = -1;
509 do {
510 i++;
511 inf_discard_tokens(inf, INF_TOK_EOL); /* Allow newlines */
512 if (!(tok = inf_token(inf, INF_TOK_VALUE))) {
513 if (i == 0) {
514 SECFILE_LOG(secfile, psection, "%s",
515 inf_log_str(inf, "Expected value for %s",
517 } else {
518 SECFILE_LOG(secfile, psection, "%s",
519 inf_log_str(inf, "Expected value for %s,%d",
520 astr_str(&base_name), i));
521 }
522 error = TRUE;
523 goto END;
524 }
525 if (i == 0) {
527 } else {
528 astr_set(&field_name, "%s,%d", astr_str(&base_name), i);
530 }
531 } while (inf_token(inf, INF_TOK_COMMA));
532 if (!inf_token(inf, INF_TOK_EOL)) {
533 SECFILE_LOG(secfile, psection, "%s",
534 inf_log_str(inf, "Expected end of line or comma"));
535 error = TRUE;
536 goto END;
537 }
538 }
539
540 if (table_state) {
541 SECFILE_LOG(secfile, psection,
542 "Finished registry before end of table");
543 error = TRUE;
544 }
545
546END:
547 inf_close(inf);
550 for (i = 0; i < astring_vector_size(&columns); i++) {
551 astr_free(&columns.p[i]);
552 }
553 astring_vector_free(&columns);
554
555 if (section != nullptr) {
556 if (!found_my_section) {
557 secfile_destroy(secfile);
558 return nullptr;
559 }
560
561 /* Build the entry hash table with single section information */
562 secfile->allow_duplicates = allow_duplicates;
564 if (!secfile_hash_insert(secfile, pentry)) {
565 secfile_destroy(secfile);
566 return nullptr;
567 }
569
570 return secfile;
571 }
572
573 if (!error) {
574 /* Build the entry hash table. */
575 secfile->allow_duplicates = allow_duplicates;
576 secfile->hash.entries = entry_hash_new_nentries(secfile->num_entries);
577
580 if (!secfile_hash_insert(secfile, pentry)) {
581 error = TRUE;
582 break;
583 }
585 if (error) {
586 break;
587 }
589 }
590 if (error) {
591 secfile_destroy(secfile);
592 return nullptr;
593 } else {
594 return secfile;
595 }
596}
597
598/**********************************************************************/
602struct section_file *secfile_load_section(const char *filename,
603 const char *section,
604 bool allow_duplicates)
605{
606 char real_filename[1024];
607
608 interpret_tilde(real_filename, sizeof(real_filename), filename);
610 filename, section, allow_duplicates);
611}
612
613/**********************************************************************/
617 bool allow_duplicates)
618{
620 nullptr, nullptr, allow_duplicates);
621}
622
623/**********************************************************************/
626static bool is_legal_table_entry_name(char c, bool num)
627{
628 return (num ? fc_isalnum(c) : fc_isalpha(c)) || c == '_';
629}
630
631/**********************************************************************/
637static bool is_delim(char ch)
638{
639 if (isalnum(ch) || ch & 0x80) {
640 return FALSE;
641 } else {
642 return (ch != '%' && ch != '.' && ch != ',' && ch != '?'
643 && ch != '!' && ch != ';' && ch != ':' && ch != ')');
644 }
645}
646
647/**********************************************************************/
653static void find_next_delims(char *start_ptr,
654 int *delim1_offset, int *delim2_offset)
655{
656 char *p = start_ptr;
657
658 *delim1_offset = -1;
659 *delim2_offset = -1;
660 while (*p && !is_delim(*p)) {
661 p++;
662 }
663 if (*p) {
665 } else {
666 return;
667 }
668 p++;
669 while (*p && !is_delim(*p)) {
670 p++;
671 }
672 if (*p) {
674 }
675}
676
677/**********************************************************************/
684static int word_wrap_internal(char *new_buf, int indent, bool is_counting)
685{
686 bool is_finished = FALSE;
687 size_t str_length = strlen(new_buf);
691 int looked_at = 0;
692 int width = MAX_LINE_LEN - indent;
694 int counter = 0;
695
696 while (! is_finished) {
697 if (num_undone > width) {
699 if (delim1_offset == -1) {
701 } else {
703 && (looked_at + delim2_offset >= width || delim2_offset == -1)) {
705 /* found where we want to word wrap */
706 if (is_counting) {
707 counter++;
708 } else {
709 char *p;
710
711 /* insert '\' and newline char into new_buf at break_ptr */
712 /* first, move to make room */
713 p = end_new_buf_ptr;
714 while (p >= break_ptr) {
715 *(p + 2) = *p;
716 p--;
717 }
718 *break_ptr++ = '\\';
719 *break_ptr++ = '\n';
720 end_new_buf_ptr += 2;
721 }
722
723 /* update for next time thru loop */
725 looked_at = 0;
728 } else {
729 /* still looking, update for next time thru loop */
732 }
733 /* if now pointing at a new line, update for next time thru loop */
734 if (*progress_ptr == '\n') {
735 looked_at = 0;
737 }
738 }
739 } else {
741 }
742 }
743 return counter;
744}
745
746/**********************************************************************/
759static void format_entry_string(struct entry *pentry, int indent)
760{
761 char *new_buf;
762 char *new_buf_ptr;
763 char *buf_ptr = pentry->string.value;
764 size_t entry_str_length = strlen(pentry->string.value);
765 int new_buf_size;
766 char *old_val;
767 int counter = 0;
768
769 fc_assert(pentry->type == ENTRY_STR);
770
771 /* count newline chars */
772 while (*buf_ptr) {
773 if (*buf_ptr == '\n') {
774 counter++;
775 }
776 buf_ptr++;
777 }
778
779 /* allocate new buffer with room for the added chars */
783
784 /* insert "\n\" before newline chars */
785 buf_ptr = pentry->string.value;
786 while (*buf_ptr) {
787 if (*buf_ptr == '\n') {
788 /* found a newline char, insert "\n\" */
789 *new_buf_ptr++ = '\\';
790 *new_buf_ptr++ = 'n';
791 *new_buf_ptr++ = '\\';
792 }
793 *new_buf_ptr++ = *buf_ptr++;
794 }
795 *new_buf_ptr = 0;
796
797 /* dry run of word wrapping to count how many line breaks we will add */
799
800 if (counter > 0) {
801 /* expand new_buf to accomodate added chars */
802 new_buf_size += 2 * counter;
804
805 /* do word wrapping */
807 }
808
809 /* swap in new_buf as the pentry->string.value,
810 * and free the old pentry->string.value
811 */
812 old_val = pentry->string.value;
813 pentry->string.value = new_buf;
814 free(old_val);
815}
816
817/**********************************************************************/
823static size_t entry_str_length(struct entry *pentry)
824{
825 size_t len;
826
827 fc_assert(pentry->type == ENTRY_STR);
828
829 /* + 2 for the enclosing " chars */
830 len = strlen(pentry->string.value) + 2;
831 if (pentry->string.gt_marking) {
832 len +=3; /* + 3 for the initial "_(" and terminating ")" */
833 }
834 return len;
835}
836
837/**********************************************************************/
856bool secfile_save(const struct section_file *secfile, const char *filename,
858 bool do_formatting)
859{
860 char real_filename[1024];
861 char pentry_name[128];
862 const char *col_entry_name;
863 fz_FILE *fs;
864 const struct entry_list_link *ent_iter, *save_iter, *col_iter;
865 struct entry *pentry, *col_pentry;
866 int i;
867 /* these vars are only used for formatting ENTRY_STR's */
868 bool need_new_line = FALSE;
869 int col = 0, indent, entry_len;
870
871 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, FALSE);
872
873 if (filename == nullptr) {
874 filename = secfile->name;
875 }
876
877 interpret_tilde(real_filename, sizeof(real_filename), filename);
880
881 if (!fs) {
882 SECFILE_LOG(secfile, nullptr, _("Could not open %s for writing"), real_filename);
883
884 return FALSE;
885 }
886
888 if (psection->special == EST_INCLUDE) {
892
893 fc_assert(!strcmp(entry_name(pentry), "file"));
894
895 fz_fprintf(fs, "*include ");
897 fz_fprintf(fs, "\n");
898 }
899 } else if (psection->special == EST_COMMENT) {
903
904 fc_assert(!strcmp(entry_name(pentry), "comment"));
905
907 fz_fprintf(fs, "\n");
908 }
909 } else {
910 fz_fprintf(fs, "\n[%s]\n", section_name(psection));
911
912 /* Following doesn't use entry_list_iterate() because we want to do
913 * tricky things with the iterators...
914 */
918 const char *comment;
919
920 /* Tables: break out of this loop if this is a non-table
921 * entry (pentry and ent_iter unchanged) or after table (pentry
922 * and ent_iter suitably updated, pentry possibly nullptr).
923 * After each table, loop again in case the next entry
924 * is another table.
925 */
926 for (;;) {
927 char *c, *first, base[64];
928 int offset, irow, icol, ncol;
929
930 /* Example: for first table name of "xyz0.blah":
931 * first points to the original string pentry->name
932 * base contains "xyz";
933 * offset = 5 (so first+offset gives "blah")
934 * note strlen(base) = offset - 2
935 */
936
937 if (!SAVE_TABLES) {
938 break;
939 }
940
941 if (pentry->type == ENTRY_LONG_COMMENT) {
942 break;
943 }
944
946 c = first = pentry_name;
947 if (*c == '\0' || !is_legal_table_entry_name(*c, FALSE)) {
948 break;
949 }
950 for (; *c != '\0' && is_legal_table_entry_name(*c, FALSE); c++) {
951 /* nothing */
952 }
953 if (fc_strncmp(c, "0.", 2)) {
954 break;
955 }
956 c += 2;
957 if (*c == '\0' || !is_legal_table_entry_name(*c, TRUE)) {
958 break;
959 }
960
961 offset = c - first;
962 first[offset - 2] = '\0';
963 sz_strlcpy(base, first);
964 first[offset - 2] = '0';
965 fz_fprintf(fs, "%s={", base);
966
967 /* Save an iterator at this first entry, which we can later use
968 * to repeatedly iterate over column names:
969 */
971
972 /* Write the column names, and calculate ncol: */
973 ncol = 0;
977 if (col_pentry->type == ENTRY_LONG_COMMENT) {
978 continue;
979 }
981 if (fc_strncmp(col_entry_name, first, offset)) {
982 break;
983 }
984 fz_fprintf(fs, "%s\"%s\"", (ncol == 0 ? "" : ","),
985 col_entry_name + offset);
986 ncol++;
987 }
988 fz_fprintf(fs, "\n");
989
990 /* Iterate over rows and columns, incrementing ent_iter as we go,
991 * and writing values to the table. Have a separate iterator
992 * to the column names to check they all match.
993 */
994 irow = icol = 0;
996 for (;;) {
997 char expect[128]; /* pentry->name we're expecting */
998
1001
1002 if (pentry && pentry->type == ENTRY_LONG_COMMENT) {
1003 if (icol == 0) {
1005 } else {
1006 bugreport_request("In file %s, section %s there was\n"
1007 "an attempt to insert comment in the middle of table row.",
1009 }
1011 continue;
1012 } else {
1013
1014 fc_snprintf(expect, sizeof(expect), "%s%d.%s",
1015 base, irow, entry_name(col_pentry) + offset);
1016
1017 /* break out of tabular if doesn't match: */
1018 if ((!pentry) || (strcmp(entry_name(pentry), expect) != 0)) {
1019 if (icol != 0) {
1020 /* If the second or later row of a table is missing some
1021 * entries that the first row had, we drop out of the tabular
1022 * format. This is inefficient so we print a warning message;
1023 * the calling code probably needs to be fixed so that it can
1024 * use the more efficient tabular format.
1025 *
1026 * FIXME: If the first row is missing some entries that the
1027 * second or later row has, then we'll drop out of tabular
1028 * format without an error message. */
1029 bugreport_request("In file %s, there is no entry in the registry for\n"
1030 "%s.%s (or the entries are out of order). This means\n"
1031 "a less efficient non-tabular format will be used.\n"
1032 "To avoid this make sure all rows of a table are\n"
1033 "filled out with an entry for every column.",
1035 fz_fprintf(fs, "\n");
1036 }
1037 fz_fprintf(fs, "}\n");
1038 break;
1039 }
1040 }
1041
1042 if (icol == 0 && pentry->type == ENTRY_STR && do_formatting) {
1044 }
1045 if (icol > 0) {
1046 fz_fprintf(fs, ",");
1047 /* maybe do formatting for ENTRY_STR's */
1048 if (pentry->type == ENTRY_STR && do_formatting) {
1050 }
1051 }
1053
1056
1057 icol++;
1058 if (icol == ncol) {
1059 fz_fprintf(fs, "\n");
1060 irow++;
1061 icol = 0;
1063 }
1064 }
1065 if (!pentry) {
1066 break;
1067 }
1068 }
1069 if (!pentry) {
1070 break;
1071 }
1072
1073 if (pentry->type == ENTRY_LONG_COMMENT) {
1075 } else {
1076 /* Classic entry. */
1079
1080 /* maybe do formatting for ENTRY_STR's */
1081 if (pentry->type == ENTRY_STR && do_formatting) {
1082 col = indent;
1084 /* + 1 for the comma */
1086 col += entry_len;
1087 }
1088
1090
1091 /* Check for vector. */
1092 for (i = 1;; i++) {
1095 if (col_pentry == nullptr || col_pentry->type == ENTRY_LONG_COMMENT) {
1096 break;
1097 }
1099 "%s,%d", col_entry_name, i);
1101 break;
1102 }
1103 fz_fprintf(fs, ",");
1104
1105 /* maybe do formatting for ENTRY_STR's */
1106 if (pentry->type == ENTRY_STR && do_formatting) {
1107 col++;
1109 /* + 1 for the comma */
1111 if (col + entry_len > MAX_LINE_LEN) {
1113 }
1114 if (need_new_line) {
1115 fz_fprintf(fs, "\n%*s", indent, " ");
1117 col = indent + entry_len;
1118 } else {
1119 col += entry_len;
1120 }
1121 }
1122
1124
1126 }
1127
1129 if (comment) {
1130 fz_fprintf(fs, " # %s\n", comment);
1131 } else {
1132 fz_fprintf(fs, "\n");
1133 }
1134 }
1135 }
1136 }
1138
1139 if (0 != fz_ferror(fs)) {
1140 SECFILE_LOG(secfile, nullptr, "Error before closing %s: %s",
1142 fz_fclose(fs);
1143 return FALSE;
1144 }
1145 if (0 != fz_fclose(fs)) {
1146 SECFILE_LOG(secfile, nullptr, "Error closing %s", real_filename);
1147 return FALSE;
1148 }
1149
1150 return TRUE;
1151}
1152
1153/**********************************************************************/
1160void secfile_check_unused(const struct section_file *secfile)
1161{
1162 bool any = FALSE;
1163
1166 if (!entry_used(pentry)) {
1167 if (!any && secfile->name) {
1168 log_verbose("Unused entries in file %s:", secfile->name);
1169 any = TRUE;
1170 }
1172 log_deprecation_always("%s: unused entry: %s.%s",
1173 secfile->name != nullptr ? secfile->name : "nameless",
1175 } else {
1176#ifdef FREECIV_TESTMATIC
1177 log_testmatic("%s: unused entry: %s.%s",
1178 secfile->name != nullptr ? secfile->name : "nameless",
1180#else /* FREECIV_TESTMATIC */
1181 log_verbose(" unused entry: %s.%s",
1183#endif /* FREECIV_TESTMATIC */
1184 }
1185 }
1188}
1189
1190/**********************************************************************/
1196const char *secfile_name(const struct section_file *secfile)
1197{
1198 if (secfile == nullptr) {
1199 return "nullptr";
1200 } else if (secfile->name) {
1201 return secfile->name;
1202 } else {
1203 return "(anonymous)";
1204 }
1205}
1206
1207/**********************************************************************/
1211 const char *path,
1212 const char **pent_name)
1213{
1215 char *ent_name;
1216 struct section *psection;
1217
1218 sz_strlcpy(fullpath, path);
1219
1220 ent_name = strchr(fullpath, '.');
1221 if (!ent_name) {
1222 SECFILE_LOG(secfile, nullptr,
1223 "Section and entry names must be separated by a dot.");
1224 return nullptr;
1225 }
1226
1227 /* Separates section and entry names. */
1228 *ent_name = '\0';
1229 *pent_name = path + (ent_name - fullpath) + 1;
1231 if (psection) {
1232 return psection;
1233 } else {
1235 }
1236}
1237
1238/**********************************************************************/
1242 bool value, const char *comment,
1243 bool allow_replace,
1244 const char *path, ...)
1245{
1247 const char *ent_name;
1248 struct section *psection;
1249 struct entry *pentry = nullptr;
1250 va_list args;
1251
1252 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
1253
1254 va_start(args, path);
1255 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1256 va_end(args);
1257
1259 if (!psection) {
1260 return nullptr;
1261 }
1262
1263 if (allow_replace) {
1265 if (pentry != nullptr) {
1267 if (!entry_bool_set(pentry, value)) {
1268 return nullptr;
1269 }
1270 } else {
1272 pentry = nullptr;
1273 }
1274 }
1275 }
1276
1277 if (pentry == nullptr) {
1279 }
1280
1281 if (pentry != nullptr && comment != nullptr) {
1283 }
1284
1285 return pentry;
1286}
1287
1288/**********************************************************************/
1293 const bool *values, size_t dim,
1294 const char *comment, bool allow_replace,
1295 const char *path, ...)
1296{
1298 size_t i, ret = 0;
1299 va_list args;
1300
1301 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, 0);
1302
1303 va_start(args, path);
1304 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1305 va_end(args);
1306
1307 /* NB: 'path,0' is actually 'path'. See comment in the head
1308 * of the file. */
1309 if (dim > 0
1310 && secfile_insert_bool_full(secfile, values[0], comment,
1311 allow_replace, "%s", fullpath) != nullptr) {
1312 ret++;
1313 }
1314 for (i = 1; i < dim; i++) {
1315 if (secfile_insert_bool_full(secfile, values[i], comment,
1316 allow_replace, "%s,%d",
1317 fullpath, (int) i) != nullptr) {
1318 ret++;
1319 }
1320 }
1321
1322 return ret;
1323}
1324
1325/**********************************************************************/
1329 int value, const char *comment,
1330 bool allow_replace,
1331 const char *path, ...)
1332{
1334 const char *ent_name;
1335 struct section *psection;
1336 struct entry *pentry = nullptr;
1337 va_list args;
1338
1339 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
1340
1341 va_start(args, path);
1342 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1343 va_end(args);
1344
1346 if (!psection) {
1347 return nullptr;
1348 }
1349
1350 if (allow_replace) {
1352 if (pentry != nullptr) {
1354 if (!entry_int_set(pentry, value)) {
1355 return nullptr;
1356 }
1357 } else {
1359 pentry = nullptr;
1360 }
1361 }
1362 }
1363
1364 if (pentry == nullptr) {
1366 }
1367
1368 if (pentry != nullptr && comment != nullptr) {
1370 }
1371
1372 return pentry;
1373}
1374
1375/**********************************************************************/
1380 const int *values, size_t dim,
1381 const char *comment, bool allow_replace,
1382 const char *path, ...)
1383{
1385 size_t i, ret = 0;
1386 va_list args;
1387
1388 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, 0);
1389
1390 va_start(args, path);
1391 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1392 va_end(args);
1393
1394 /* NB: 'path,0' is actually 'path'. See comment in the head
1395 * of the file. */
1396 if (dim > 0
1397 && secfile_insert_int_full(secfile, values[0], comment,
1398 allow_replace, "%s", fullpath) != nullptr) {
1399 ret++;
1400 }
1401 for (i = 1; i < dim; i++) {
1402 if (secfile_insert_int_full(secfile, values[i], comment,
1403 allow_replace, "%s,%d",
1404 fullpath, (int) i) != nullptr) {
1405 ret++;
1406 }
1407 }
1408
1409 return ret;
1410}
1411
1412/**********************************************************************/
1416 float value, const char *comment,
1417 bool allow_replace,
1418 const char *path, ...)
1419{
1421 const char *ent_name;
1422 struct section *psection;
1423 struct entry *pentry = nullptr;
1424 va_list args;
1425
1426 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
1427
1428 va_start(args, path);
1429 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1430 va_end(args);
1431
1433 if (!psection) {
1434 return nullptr;
1435 }
1436
1437 if (allow_replace) {
1439 if (pentry != nullptr) {
1441 if (!entry_float_set(pentry, value)) {
1442 return nullptr;
1443 }
1444 } else {
1446 pentry = nullptr;
1447 }
1448 }
1449 }
1450
1451 if (pentry == nullptr) {
1453 }
1454
1455 if (pentry != nullptr && comment != nullptr) {
1457 }
1458
1459 return pentry;
1460}
1461
1462/**********************************************************************/
1466 const char *filename)
1467{
1468 struct section *psection;
1469 char buffer[200];
1470
1471 fc_snprintf(buffer, sizeof(buffer), "include_%u", secfile->num_includes++);
1472
1473 fc_assert_ret_val(secfile_section_by_name(secfile, buffer) == nullptr, nullptr);
1474
1475 /* Create include section. */
1476 psection = secfile_section_new(secfile, buffer);
1477 psection->special = EST_INCLUDE;
1478
1479 /* Then add string entry "file" to it. */
1480 secfile_insert_str_full(secfile, filename, nullptr, FALSE, FALSE,
1481 EST_INCLUDE, "%s.file", buffer);
1482
1483 return psection;
1484}
1485
1486/**********************************************************************/
1490 const char *comment)
1491{
1492 struct section *psection;
1493 char buffer[200];
1494
1495 fc_snprintf(buffer, sizeof(buffer), "long_comment_%u", secfile->num_long_comments++);
1496
1497 fc_assert_ret_val(secfile_section_by_name(secfile, buffer) == nullptr, nullptr);
1498
1499 /* Create long comment section. */
1500 psection = secfile_section_new(secfile, buffer);
1501 psection->special = EST_COMMENT;
1502
1503 /* Then add string entry "comment" to it. */
1504 secfile_insert_str_full(secfile, comment, nullptr, FALSE, TRUE,
1505 EST_COMMENT, "%s.comment", buffer);
1506
1507 return psection;
1508}
1509
1510/**********************************************************************/
1514 const char *str,
1515 const char *comment,
1516 bool allow_replace,
1517 bool no_escape,
1518 enum entry_special_type stype,
1519 const char *path, ...)
1520{
1522 const char *ent_name;
1523 struct section *psection;
1524 struct entry *pentry = nullptr;
1525 va_list args;
1526
1527 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
1528
1529 va_start(args, path);
1530 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1531 va_end(args);
1532
1534 if (!psection) {
1535 return nullptr;
1536 }
1537
1538 if (psection->special != stype) {
1539 log_error("Tried to insert wrong type of entry to section");
1540 return nullptr;
1541 }
1542
1543 if (allow_replace) {
1545 if (pentry != nullptr) {
1547 if (!entry_str_set(pentry, str)) {
1548 return nullptr;
1549 }
1550 } else {
1552 pentry = nullptr;
1553 }
1554 }
1555 }
1556
1557 if (pentry == nullptr) {
1559 }
1560
1561 if (pentry != nullptr && comment != nullptr) {
1563 }
1564
1565 if (stype == EST_COMMENT) {
1566 pentry->string.raw = TRUE;
1567 }
1568
1569 return pentry;
1570}
1571
1572/**********************************************************************/
1576 const char *str,
1577 const char *path, ...)
1578{
1580 const char *ent_name;
1581 struct section *psection;
1582 struct entry *pentry = nullptr;
1583 va_list args;
1584
1585 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
1586
1587 va_start(args, path);
1588 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1589 va_end(args);
1590
1591 sz_strlcat(fullpath, ".#");
1592
1594 if (!psection) {
1595 return nullptr;
1596 }
1597
1598 if (psection->special != EST_NORMAL) {
1599 log_error("Tried to insert wrong type of entry to section");
1600 return nullptr;
1601 }
1602
1604
1605 return pentry;
1606}
1607
1608/**********************************************************************/
1613 const char *const *strings, size_t dim,
1614 const char *comment, bool allow_replace,
1615 bool no_escape, bool gt_marking,
1616 const char *path, ...)
1617{
1619 size_t i, ret = 0;
1620 va_list args;
1621
1622 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, 0);
1623
1624 va_start(args, path);
1625 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1626 va_end(args);
1627
1628 /* NB: 'path,0' is actually 'path'. See comment in the head
1629 * of the file. */
1630 if (dim > 0) {
1631 struct entry *pentry =
1634 "%s", fullpath);
1635 if (nullptr != pentry) {
1636 ret++;
1637 if (gt_marking) {
1639 }
1640 }
1641 }
1642
1643 for (i = 1; i < dim; i++) {
1644 struct entry *pentry =
1647 "%s,%d", fullpath, (int) i);
1648 if (nullptr != pentry) {
1649 ret++;
1650 if (gt_marking) {
1652 }
1653 }
1654 }
1655
1656 return ret;
1657}
1658
1659/**********************************************************************/
1663 const char *filename,
1664 const char *path, ...)
1665{
1667 const char *ent_name;
1668 struct section *psection;
1669 struct entry *pentry = nullptr;
1670 va_list args;
1671
1672 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
1673
1674 va_start(args, path);
1675 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1676 va_end(args);
1677
1679 if (!psection) {
1680 return nullptr;
1681 }
1682
1683 if (psection->special != EST_NORMAL) {
1684 log_error("Tried to insert normal entry to different kind of section");
1685 return nullptr;
1686 }
1687
1688 if (pentry == nullptr) {
1690 }
1691
1692 return pentry;
1693}
1694
1695/**********************************************************************/
1699 int enumerator,
1701 const char *comment,
1702 bool allow_replace,
1703 const char *path, ...)
1704{
1706 const char *str;
1707 const char *ent_name;
1708 struct section *psection;
1709 struct entry *pentry = nullptr;
1710 va_list args;
1711
1712 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
1713 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, name_fn != nullptr, nullptr);
1714 str = name_fn(enumerator);
1715 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, str != nullptr, nullptr);
1716
1717 va_start(args, path);
1718 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1719 va_end(args);
1720
1722 if (!psection) {
1723 return nullptr;
1724 }
1725
1726 if (allow_replace) {
1728 if (pentry != nullptr) {
1730 if (!entry_str_set(pentry, str)) {
1731 return nullptr;
1732 }
1733 } else {
1735 pentry = nullptr;
1736 }
1737 }
1738 }
1739
1740 if (pentry == nullptr) {
1742 }
1743
1744 if (pentry != nullptr && comment != nullptr) {
1746 }
1747
1748 return pentry;
1749}
1750
1751/**********************************************************************/
1756 const int *enumurators, size_t dim,
1758 const char *comment,
1759 bool allow_replace,
1760 const char *path, ...)
1761{
1763 size_t i, ret = 0;
1764 va_list args;
1765
1766 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, 0);
1767 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, name_fn != nullptr, 0);
1768
1769 va_start(args, path);
1770 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1771 va_end(args);
1772
1773 /* NB: 'path,0' is actually 'path'. See comment in the head
1774 * of the file. */
1775 if (dim > 0
1778 allow_replace, "%s",
1779 fullpath) != nullptr) {
1780 ret++;
1781 }
1782 for (i = 1; i < dim; i++) {
1785 allow_replace, "%s,%d",
1786 fullpath, (int) i) != nullptr) {
1787 ret++;
1788 }
1789 }
1790
1791 return ret;
1792}
1793
1794/**********************************************************************/
1798 int bitwise_val,
1800 name_fn,
1802 begin_fn,
1804 end_fn,
1806 next_fn,
1807 const char *comment,
1808 bool allow_replace,
1809 const char *path, ...)
1810{
1812 const char *ent_name;
1813 struct section *psection;
1814 struct entry *pentry = nullptr;
1815 va_list args;
1816 int i;
1817
1818 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
1819 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, name_fn != nullptr, nullptr);
1820 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, begin_fn != nullptr, nullptr);
1821 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, end_fn != nullptr, nullptr);
1822 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, next_fn != nullptr, nullptr);
1823
1824 /* Compute a string containing all the values separated by '|'. */
1825 str[0] = '\0'; /* Insert at least an empty string. */
1826 if (0 != bitwise_val) {
1827 for (i = begin_fn(); i != end_fn(); i = next_fn(i)) {
1828 if (i & bitwise_val) {
1829 if ('\0' == str[0]) {
1831 } else {
1832 cat_snprintf(str, sizeof(str), "|%s", name_fn(i));
1833 }
1834 }
1835 }
1836 }
1837
1838 va_start(args, path);
1839 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1840 va_end(args);
1841
1843 if (!psection) {
1844 return nullptr;
1845 }
1846
1847 if (allow_replace) {
1849 if (pentry != nullptr) {
1851 if (!entry_str_set(pentry, str)) {
1852 return nullptr;
1853 }
1854 } else {
1856 pentry = nullptr;
1857 }
1858 }
1859 }
1860
1861 if (pentry == nullptr) {
1863 }
1864
1865 if (pentry != nullptr && comment != nullptr) {
1867 }
1868
1869 return pentry;
1870}
1871
1872/**********************************************************************/
1877 const int *bitwise_vals,
1878 size_t dim,
1883 const char *comment,
1884 bool allow_replace,
1885 const char *path, ...)
1886{
1888 size_t i, ret = 0;
1889 va_list args;
1890
1891 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, 0);
1892 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, name_fn != nullptr, 0);
1893 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, begin_fn != nullptr, 0);
1894 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, end_fn != nullptr, 0);
1895 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, next_fn != nullptr, 0);
1896
1897 va_start(args, path);
1898 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1899 va_end(args);
1900
1901 /* NB: 'path,0' is actually 'path'. See comment in the head
1902 * of the file. */
1903 if (dim > 0
1907 allow_replace, "%s",
1908 fullpath) != nullptr) {
1909 ret++;
1910 }
1911 for (i = 1; i < dim; i++) {
1915 allow_replace, "%s,%d",
1916 fullpath, (int) i) != nullptr) {
1917 ret++;
1918 }
1919 }
1920
1921 return ret;
1922}
1923
1924/**********************************************************************/
1929 int value, bool bitwise,
1931 secfile_data_t data,
1932 const char *comment,
1933 bool allow_replace,
1934 const char *path, ...)
1935{
1937 const char *ent_name, *val_name;
1938 struct section *psection;
1939 struct entry *pentry = nullptr;
1940 va_list args;
1941 int i;
1942
1943 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
1944 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, name_fn != nullptr, nullptr);
1945
1946 if (bitwise) {
1947 /* Compute a string containing all the values separated by '|'. */
1948 str[0] = '\0'; /* Insert at least an empty string. */
1949 if (0 != value) {
1950 for (i = 0; (val_name = name_fn(data, i)); i++) {
1951 if ((1 << i) & value) {
1952 if ('\0' == str[0]) {
1954 } else {
1955 cat_snprintf(str, sizeof(str), "|%s", val_name);
1956 }
1957 }
1958 }
1959 }
1960 } else {
1961 if (!(val_name = name_fn(data, value))) {
1962 SECFILE_LOG(secfile, nullptr, "Value %d not supported.", value);
1963
1964 return nullptr;
1965 }
1967 }
1968
1969 va_start(args, path);
1970 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1971 va_end(args);
1972
1974 if (!psection) {
1975 return nullptr;
1976 }
1977
1978 if (allow_replace) {
1980 if (pentry != nullptr) {
1982 if (!entry_str_set(pentry, str)) {
1983 return nullptr;
1984 }
1985 } else {
1987 pentry = nullptr;
1988 }
1989 }
1990 }
1991
1992 if (pentry == nullptr) {
1994 }
1995
1996 if (pentry != nullptr && comment != nullptr) {
1998 }
1999
2000 return pentry;
2001}
2002
2003/**********************************************************************/
2008 const int *values, size_t dim,
2009 bool bitwise,
2011 secfile_data_t data,
2012 const char *comment,
2013 bool allow_replace,
2014 const char *path, ...)
2015{
2017 size_t i, ret = 0;
2018 va_list args;
2019
2020 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, 0);
2021 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, name_fn != nullptr, 0);
2022
2023 va_start(args, path);
2024 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2025 va_end(args);
2026
2027 /* NB: 'path,0' is actually 'path'. See comment in the head
2028 * of the file. */
2029 if (dim > 0
2030 && secfile_insert_enum_data_full(secfile, values[0], bitwise,
2031 name_fn, data, comment,
2032 allow_replace, "%s",
2033 fullpath) != nullptr) {
2034 ret++;
2035 }
2036 for (i = 1; i < dim; i++) {
2037 if (secfile_insert_enum_data_full(secfile, values[i], bitwise,
2038 name_fn, data, comment,
2039 allow_replace, "%s,%d",
2040 fullpath, (int) i) != nullptr) {
2041 ret++;
2042 }
2043 }
2044
2045 return ret;
2046}
2047
2048/**********************************************************************/
2051struct entry *secfile_entry_by_path(const struct section_file *secfile,
2052 const char *path)
2053{
2055 char *ent_name;
2056 size_t len;
2057 struct section *psection;
2058
2059 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
2060
2061 sz_strlcpy(fullpath, path);
2062
2063 /* Treat "sec.foo,0" as "sec.foo": */
2064 len = strlen(fullpath);
2065 if (len > 2 && fullpath[len - 2] == ',' && fullpath[len - 1] == '0') {
2066 fullpath[len - 2] = '\0';
2067 }
2068
2069 if (secfile->hash.entries != nullptr) {
2070 struct entry *pentry;
2071
2072 if (entry_hash_lookup(secfile->hash.entries, fullpath, &pentry)) {
2074 }
2075 return pentry;
2076 }
2077
2078 /* I dont like strtok().
2079 * - Me neither! */
2080 ent_name = strchr(fullpath, '.');
2081 if (!ent_name) {
2082 return nullptr;
2083 }
2084
2085 /* Separates section and entry names. */
2086 *ent_name++ = '\0';
2088 if (psection) {
2090 } else {
2091 return nullptr;
2092 }
2093}
2094
2095/**********************************************************************/
2099 const char *path, ...)
2100{
2102 va_list args;
2103 struct entry *pentry;
2104
2105 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, FALSE);
2106
2107 va_start(args, path);
2108 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2109 va_end(args);
2110
2111 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2112 SECFILE_LOG(secfile, nullptr, "Path %s does not exists.", fullpath);
2113 return FALSE;
2114 }
2115
2117
2118 return TRUE;
2119}
2120
2121/**********************************************************************/
2124struct entry *secfile_entry_lookup(const struct section_file *secfile,
2125 const char *path, ...)
2126{
2128 va_list args;
2129
2130 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
2131
2132 va_start(args, path);
2133 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2134 va_end(args);
2135
2136 return secfile_entry_by_path(secfile, fullpath);
2137}
2138
2139/**********************************************************************/
2142bool secfile_lookup_bool(const struct section_file *secfile, bool *bval,
2143 const char *path, ...)
2144{
2146 const struct entry *pentry;
2147 va_list args;
2148
2149 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, FALSE);
2150
2151 va_start(args, path);
2152 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2153 va_end(args);
2154
2155 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2156 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2157
2158 return FALSE;
2159 }
2160
2161 return entry_bool_get(pentry, bval);
2162}
2163
2164/**********************************************************************/
2169 bool def, const char *path, ...)
2170{
2172 const struct entry *pentry;
2173 bool bval;
2174 va_list args;
2175
2176 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, def);
2177
2178 va_start(args, path);
2179 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2180 va_end(args);
2181
2182 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2183 return def;
2184 }
2185
2186 if (entry_bool_get(pentry, &bval)) {
2187 return bval;
2188 }
2189
2190 return def;
2191}
2192
2193/**********************************************************************/
2198bool *secfile_lookup_bool_vec(const struct section_file *secfile,
2199 size_t *dim, const char *path, ...)
2200{
2202 size_t i = 0;
2203 bool *vec;
2204 va_list args;
2205
2206 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
2207 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, dim != nullptr, nullptr);
2208
2209 va_start(args, path);
2210 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2211 va_end(args);
2212
2213 /* Check size. */
2214 while (secfile_entry_lookup(secfile, "%s,%d", fullpath, (int) i) != nullptr) {
2215 i++;
2216 }
2217 *dim = i;
2218
2219 if (0 == i) {
2220 /* Doesn't exist. */
2221 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2222 return nullptr;
2223 }
2224
2225 vec = fc_malloc(i * sizeof(bool));
2226 for (i = 0; i < *dim; i++) {
2227 if (!secfile_lookup_bool(secfile, vec + i, "%s,%d", fullpath, (int) i)) {
2228 SECFILE_LOG(secfile, nullptr,
2229 "An error occurred when looking up to \"%s,%d\" entry.",
2230 fullpath, (int) i);
2231 free(vec);
2232 *dim = 0;
2233
2234 return nullptr;
2235 }
2236 }
2237
2238 return vec;
2239}
2240
2241/**********************************************************************/
2244bool secfile_lookup_int(const struct section_file *secfile, int *ival,
2245 const char *path, ...)
2246{
2248 const struct entry *pentry;
2249 va_list args;
2250
2251 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, FALSE);
2252
2253 va_start(args, path);
2254 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2255 va_end(args);
2256
2257 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2258 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2259
2260 return FALSE;
2261 }
2262
2263 return entry_int_get(pentry, ival);
2264}
2265
2266/**********************************************************************/
2270int secfile_lookup_int_default(const struct section_file *secfile, int def,
2271 const char *path, ...)
2272{
2274 const struct entry *pentry;
2275 int ival;
2276 va_list args;
2277
2278 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, def);
2279
2280 va_start(args, path);
2281 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2282 va_end(args);
2283
2284 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2285 return def;
2286 }
2287
2288 if (entry_int_get(pentry, &ival)) {
2289 return ival;
2290 }
2291
2292 return def;
2293}
2294
2295/**********************************************************************/
2301 int defval, int minval, int maxval,
2302 const char *path, ...)
2303{
2305 const struct entry *pentry;
2306 int value;
2307 va_list args;
2308
2309 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, defval);
2310
2311 va_start(args, path);
2312 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2313 va_end(args);
2314
2315 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2316 return defval;
2317 }
2318
2319 if (!entry_int_get(pentry, &value)) {
2320 return defval;
2321 }
2322
2323 if (value < minval) {
2325 "\"%s\" should be in the interval [%d, %d] but is %d;"
2326 "using the minimal value.",
2328 value = minval;
2329 }
2330
2331 if (value > maxval) {
2333 "\"%s\" should be in the interval [%d, %d] but is %d;"
2334 "using the maximal value.",
2336 value = maxval;
2337 }
2338
2339 return value;
2340}
2341
2342/**********************************************************************/
2347int *secfile_lookup_int_vec(const struct section_file *secfile,
2348 size_t *dim, const char *path, ...)
2349{
2351 size_t i = 0;
2352 int *vec;
2353 va_list args;
2354
2355 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
2356 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, dim != nullptr, nullptr);
2357
2358 va_start(args, path);
2359 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2360 va_end(args);
2361
2362 /* Check size. */
2363 while (secfile_entry_lookup(secfile, "%s,%d", fullpath, (int) i) != nullptr) {
2364 i++;
2365 }
2366 *dim = i;
2367
2368 if (0 == i) {
2369 /* Doesn't exist. */
2370 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2371
2372 return nullptr;
2373 }
2374
2375 vec = fc_malloc(i * sizeof(int));
2376 for (i = 0; i < *dim; i++) {
2377 if (!secfile_lookup_int(secfile, vec + i, "%s,%d", fullpath, (int) i)) {
2378 SECFILE_LOG(secfile, nullptr,
2379 "An error occurred when looking up to \"%s,%d\" entry.",
2380 fullpath, (int) i);
2381 free(vec);
2382 *dim = 0;
2383 return nullptr;
2384 }
2385 }
2386
2387 return vec;
2388}
2389
2390/**********************************************************************/
2393bool secfile_lookup_float(const struct section_file *secfile, float *fval,
2394 const char *path, ...)
2395{
2397 const struct entry *pentry;
2398 va_list args;
2399
2400 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, FALSE);
2401
2402 va_start(args, path);
2403 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2404 va_end(args);
2405
2406 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2407 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2408 return FALSE;
2409 }
2410
2411 return entry_float_get(pentry, fval);
2412}
2413
2414/**********************************************************************/
2418float secfile_lookup_float_default(const struct section_file *secfile,
2419 float def, const char *path, ...)
2420{
2422 const struct entry *pentry;
2423 float fval;
2424 va_list args;
2425
2426 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, def);
2427
2428 va_start(args, path);
2429 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2430 va_end(args);
2431
2432 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2433 return def;
2434 }
2435
2436 if (entry_float_get(pentry, &fval)) {
2437 return fval;
2438 }
2439
2440 return def;
2441}
2442
2443/**********************************************************************/
2446const char *secfile_lookup_str(const struct section_file *secfile,
2447 const char *path, ...)
2448{
2450 const struct entry *pentry;
2451 const char *str;
2452 va_list args;
2453
2454 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
2455
2456 va_start(args, path);
2457 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2458 va_end(args);
2459
2460 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2461 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2462 return nullptr;
2463 }
2464
2465 if (entry_str_get(pentry, &str)) {
2466 return str;
2467 }
2468
2469 return nullptr;
2470}
2471
2472/**********************************************************************/
2476const char *secfile_lookup_str_default(const struct section_file *secfile,
2477 const char *def,
2478 const char *path, ...)
2479{
2481 const struct entry *pentry;
2482 const char *str;
2483 va_list args;
2484
2485 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, def);
2486
2487 va_start(args, path);
2488 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2489 va_end(args);
2490
2491 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2492 return def;
2493 }
2494
2495 if (entry_str_get(pentry, &str)) {
2496 return str;
2497 }
2498
2499 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't have a string.", fullpath);
2500
2501 return def;
2502}
2503
2504/**********************************************************************/
2510const char **secfile_lookup_str_vec(const struct section_file *secfile,
2511 size_t *dim, const char *path, ...)
2512{
2514 size_t i = 0;
2515 const char **vec;
2516 va_list args;
2517
2518 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
2519 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, dim != nullptr, nullptr);
2520
2521 va_start(args, path);
2522 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2523 va_end(args);
2524
2525 /* Check size. */
2526 while (secfile_entry_lookup(secfile, "%s,%d", fullpath, (int) i) != nullptr) {
2527 i++;
2528 }
2529 *dim = i;
2530
2531 if (0 == i) {
2532 /* Doesn't exist. */
2533 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2534 return nullptr;
2535 }
2536
2537 vec = fc_malloc(i * sizeof(const char *));
2538 for (i = 0; i < *dim; i++) {
2539 if (!(vec[i] = secfile_lookup_str(secfile, "%s,%d",
2540 fullpath, (int) i))) {
2541 SECFILE_LOG(secfile, nullptr,
2542 "An error occurred when looking up to \"%s,%d\" entry.",
2543 fullpath, (int) i);
2544 free(vec);
2545 *dim = 0;
2546 return nullptr;
2547 }
2548 }
2549
2550 return vec;
2551}
2552
2553/**********************************************************************/
2557 int *penumerator,
2560 const char *path, ...)
2561{
2563 const struct entry *pentry;
2564 const char *str;
2565 va_list args;
2566
2567 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, FALSE);
2568 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, penumerator != nullptr, FALSE);
2569 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, is_valid_fn != nullptr, FALSE);
2570 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, by_name_fn != nullptr, FALSE);
2571
2572 va_start(args, path);
2573 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2574 va_end(args);
2575
2576 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2577 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2578 return FALSE;
2579 }
2580
2581 if (!entry_str_get(pentry, &str)) {
2582 return FALSE;
2583 }
2584
2586 if (is_valid_fn(*penumerator)) {
2587 return TRUE;
2588 }
2589
2591 "Entry \"%s\": no match for \"%s\".",
2593 return FALSE;
2594}
2595
2596/**********************************************************************/
2600 *secfile, int defval,
2604 by_name_fn,
2605 const char *path, ...)
2606{
2608 const struct entry *pentry;
2609 const char *str;
2610 int val;
2611 va_list args;
2612
2613 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, defval);
2614 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, is_valid_fn != nullptr, defval);
2615 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, by_name_fn != nullptr, defval);
2616
2617 va_start(args, path);
2618 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2619 va_end(args);
2620
2621 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2622 return defval;
2623 }
2624
2625 if (!entry_str_get(pentry, &str)) {
2626 return defval;
2627 }
2628
2629 val = by_name_fn(str, strcmp);
2630 if (is_valid_fn(val)) {
2631 return val;
2632 } else {
2633 return defval;
2634 }
2635}
2636
2637/**********************************************************************/
2643 size_t *dim,
2647 by_name_fn,
2648 const char *path, ...)
2649{
2651 size_t i = 0;
2652 int *vec;
2653 va_list args;
2654
2655 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
2656 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, dim != nullptr, nullptr);
2657 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, is_valid_fn != nullptr, nullptr);
2658 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, by_name_fn != nullptr, nullptr);
2659
2660 va_start(args, path);
2661 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2662 va_end(args);
2663
2664 /* Check size. */
2665 while (secfile_entry_lookup(secfile, "%s,%d", fullpath, (int) i) != nullptr) {
2666 i++;
2667 }
2668 *dim = i;
2669
2670 if (0 == i) {
2671 /* Doesn't exist. */
2672 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2673 return nullptr;
2674 }
2675
2676 vec = fc_malloc(i * sizeof(int));
2677 for (i = 0; i < *dim; i++) {
2678 if (!secfile_lookup_plain_enum_full(secfile, vec + i, is_valid_fn,
2679 by_name_fn, "%s,%d",
2680 fullpath, (int) i)) {
2681 SECFILE_LOG(secfile, nullptr,
2682 "An error occurred when looking up to \"%s,%d\" entry.",
2683 fullpath, (int) i);
2684 free(vec);
2685 *dim = 0;
2686 return nullptr;
2687 }
2688 }
2689
2690 return vec;
2691}
2692
2693/**********************************************************************/
2697 int *penumerator,
2700 const char *path, ...)
2701{
2703 const struct entry *pentry;
2704 const char *str, *p;
2706 int val;
2707 va_list args;
2708
2709 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, FALSE);
2710 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, penumerator != nullptr, FALSE);
2711 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, is_valid_fn != nullptr, FALSE);
2712 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, by_name_fn != nullptr, FALSE);
2713
2714 va_start(args, path);
2715 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2716 va_end(args);
2717
2718 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2719 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2720 return FALSE;
2721 }
2722
2723 if (!entry_str_get(pentry, &str)) {
2724 return FALSE;
2725 }
2726
2727 *penumerator = 0;
2728 if ('\0' == str[0]) {
2729 /* Empty string = no value. */
2730 return TRUE;
2731 }
2732
2733 /* Value names are separated by '|'. */
2734 do {
2735 p = strchr(str, '|');
2736 if (p != nullptr) {
2737 p++;
2738 fc_strlcpy(val_name, str, p - str);
2739 } else {
2740 /* Last segment, full copy. */
2742 }
2744 val = by_name_fn(val_name, strcmp);
2745 if (!is_valid_fn(val)) {
2747 "Entry \"%s\": no match for \"%s\".",
2749 return FALSE;
2750 }
2751 *penumerator |= val;
2752 str = p;
2753 } while (p != nullptr);
2754
2755 return TRUE;
2756}
2757
2758/**********************************************************************/
2762 *secfile, int defval,
2766 by_name_fn,
2767 const char *path, ...)
2768{
2770 const struct entry *pentry;
2771 const char *str, *p;
2773 int val, full_val;
2774 va_list args;
2775
2776 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, defval);
2777 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, is_valid_fn != nullptr, defval);
2778 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, by_name_fn != nullptr, defval);
2779
2780 va_start(args, path);
2781 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2782 va_end(args);
2783
2784 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2785 return defval;
2786 }
2787
2788 if (!entry_str_get(pentry, &str)) {
2789 return defval;
2790 }
2791
2792 if ('\0' == str[0]) {
2793 /* Empty string = no value. */
2794 return 0;
2795 }
2796
2797 /* Value names are separated by '|'. */
2798 full_val = 0;
2799 do {
2800 p = strchr(str, '|');
2801 if (p != nullptr) {
2802 p++;
2803 fc_strlcpy(val_name, str, p - str);
2804 } else {
2805 /* Last segment, full copy. */
2807 }
2809 val = by_name_fn(val_name, strcmp);
2810 if (!is_valid_fn(val)) {
2811 return defval;
2812 }
2813 full_val |= val;
2814 str = p;
2815 } while (p != nullptr);
2816
2817 return full_val;
2818}
2819
2820/**********************************************************************/
2826 size_t *dim,
2830 by_name_fn,
2831 const char *path, ...)
2832{
2834 size_t i = 0;
2835 int *vec;
2836 va_list args;
2837
2838 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
2839 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, dim != nullptr, nullptr);
2840 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, is_valid_fn != nullptr, nullptr);
2841 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, by_name_fn != nullptr, nullptr);
2842
2843 va_start(args, path);
2844 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2845 va_end(args);
2846
2847 /* Check size. */
2848 while (secfile_entry_lookup(secfile, "%s,%d", fullpath, (int) i) != nullptr) {
2849 i++;
2850 }
2851 *dim = i;
2852
2853 if (0 == i) {
2854 /* Doesn't exist. */
2855 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2856 return nullptr;
2857 }
2858
2859 vec = fc_malloc(i * sizeof(int));
2860 for (i = 0; i < *dim; i++) {
2861 if (!secfile_lookup_bitwise_enum_full(secfile, vec + i, is_valid_fn,
2862 by_name_fn, "%s,%d",
2863 fullpath, (int) i)) {
2864 SECFILE_LOG(secfile, nullptr,
2865 "An error occurred when looking up to \"%s,%d\" entry.",
2866 fullpath, (int) i);
2867 free(vec);
2868 *dim = 0;
2869
2870 return nullptr;
2871 }
2872 }
2873
2874 return vec;
2875}
2876
2877/**********************************************************************/
2880bool secfile_lookup_enum_data(const struct section_file *secfile,
2881 int *pvalue, bool bitwise,
2883 secfile_data_t data, const char *path, ...)
2884{
2886 const struct entry *pentry;
2887 const char *str, *p, *name;
2889 int val;
2890 va_list args;
2891
2892 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, FALSE);
2893 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, pvalue != nullptr, FALSE);
2894 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, name_fn != nullptr, FALSE);
2895
2896 va_start(args, path);
2897 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2898 va_end(args);
2899
2900 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2901 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2902 return FALSE;
2903 }
2904
2905 if (!entry_str_get(pentry, &str)) {
2906 return FALSE;
2907 }
2908
2909 if (bitwise) {
2910 *pvalue = 0;
2911 if ('\0' == str[0]) {
2912 /* Empty string = no value. */
2913 return TRUE;
2914 }
2915
2916 /* Value names are separated by '|'. */
2917 do {
2918 p = strchr(str, '|');
2919 if (p != nullptr) {
2920 p++;
2921 fc_strlcpy(val_name, str, p - str);
2922 } else {
2923 /* Last segment, full copy. */
2925 }
2927 for (val = 0; (name = name_fn(data, val)); val++) {
2928 if (0 == fc_strcasecmp(name, val_name)) {
2929 break;
2930 }
2931 }
2932 if (name == nullptr) {
2934 "Entry \"%s\": no match for \"%s\".",
2936 return FALSE;
2937 }
2938 *pvalue |= 1 << val;
2939 str = p;
2940 } while (p != nullptr);
2941 } else {
2942 for (val = 0; (name = name_fn(data, val)); val++) {
2943 if (0 == fc_strcasecmp(name, str)) {
2944 *pvalue = val;
2945 break;
2946 }
2947 }
2948 if (name == nullptr) {
2950 "Entry \"%s\": no match for \"%s\".",
2952 return FALSE;
2953 }
2954 }
2955
2956 return TRUE;
2957}
2958
2959/**********************************************************************/
2963 int defval, bool bitwise,
2965 secfile_data_t data,
2966 const char *path, ...)
2967{
2969 const struct entry *pentry;
2970 const char *str, *p, *name;
2972 int value, val;
2973 va_list args;
2974
2975 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, defval);
2976 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, name_fn != nullptr, defval);
2977
2978 va_start(args, path);
2979 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2980 va_end(args);
2981
2982 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2983 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2984 return defval;
2985 }
2986
2987 if (!entry_str_get(pentry, &str)) {
2988 return defval;
2989 }
2990
2991 value = 0;
2992 if (bitwise) {
2993 if ('\0' == str[0]) {
2994 /* Empty string = no value. */
2995 return value;
2996 }
2997
2998 /* Value names are separated by '|'. */
2999 do {
3000 p = strchr(str, '|');
3001 if (p != nullptr) {
3002 p++;
3003 fc_strlcpy(val_name, str, p - str);
3004 } else {
3005 /* Last segment, full copy. */
3007 }
3009 for (val = 0; (name = name_fn(data, val)); val++) {
3010 if (0 == strcmp(name, val_name)) {
3011 break;
3012 }
3013 }
3014 if (name == nullptr) {
3016 "Entry \"%s\": no match for \"%s\".",
3018 return defval;
3019 }
3020 value |= 1 << val;
3021 str = p;
3022 } while (p != nullptr);
3023 } else {
3024 for (val = 0; (name = name_fn(data, val)); val++) {
3025 if (0 == strcmp(name, str)) {
3026 value = val;
3027 break;
3028 }
3029 }
3030 if (name == nullptr) {
3032 "Entry \"%s\": no match for \"%s\".",
3034 return defval;
3035 }
3036 }
3037
3038 return value;
3039}
3040
3041/**********************************************************************/
3046 size_t *dim, bool bitwise,
3048 secfile_data_t data, const char *path, ...)
3049{
3051 size_t i = 0;
3052 int *vec;
3053 va_list args;
3054
3055 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
3056 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, dim != nullptr, nullptr);
3057 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, name_fn != nullptr, nullptr);
3058
3059 va_start(args, path);
3060 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
3061 va_end(args);
3062
3063 /* Check size. */
3064 while (secfile_entry_lookup(secfile, "%s,%d", fullpath, (int) i) != nullptr) {
3065 i++;
3066 }
3067 *dim = i;
3068
3069 if (0 == i) {
3070 /* Doesn't exist. */
3071 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
3072 return nullptr;
3073 }
3074
3075 vec = fc_malloc(i * sizeof(int));
3076 for (i = 0; i < *dim; i++) {
3077 if (!secfile_lookup_enum_data(secfile, vec + i, bitwise, name_fn, data,
3078 "%s,%d", fullpath, (int) i)) {
3079 SECFILE_LOG(secfile, nullptr,
3080 "An error occurred when looking up to \"%s,%d\" entry.",
3081 fullpath, (int) i);
3082 free(vec);
3083 *dim = 0;
3084
3085 return nullptr;
3086 }
3087 }
3088
3089 return vec;
3090}
3091
3092/**********************************************************************/
3096 const char *name)
3097{
3098 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
3099
3101 if (0 == strcmp(section_name(psection), name)) {
3102 return psection;
3103 }
3105
3106 return nullptr;
3107}
3108
3109/**********************************************************************/
3113 const char *path, ...)
3114{
3116 va_list args;
3117
3118 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
3119
3120 va_start(args, path);
3121 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
3122 va_end(args);
3123
3125}
3126
3127/**********************************************************************/
3131const struct section_list *
3132secfile_sections(const struct section_file *secfile)
3133{
3134 return (secfile != nullptr ? secfile->sections : nullptr);
3135}
3136
3137/**********************************************************************/
3142struct section_list *
3144 const char *prefix)
3145{
3146 struct section_list *matches = nullptr;
3147 size_t len;
3148
3149 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
3150 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, prefix != nullptr, nullptr);
3151
3152 len = strlen(prefix);
3153 if (0 == len) {
3154 return nullptr;
3155 }
3156
3157 section_list_iterate(secfile->sections, psection) {
3158 if (!fc_strncmp(section_name(psection), prefix, len)) {
3159 if (matches == nullptr) {
3161 }
3162 section_list_append(matches, psection);
3163 }
3165
3166 return matches;
3167}
3168
3169/**********************************************************************/
3173 const char *prefix)
3174{
3175 size_t len;
3176
3177 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, FALSE);
3178 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, prefix != nullptr, FALSE);
3179
3180 len = strlen(prefix);
3181 if (0 == len) {
3182 return FALSE;
3183 }
3184
3185 section_list_iterate(secfile->sections, psection) {
3186 if (!fc_strncmp(section_name(psection), prefix, len)) {
3187 return TRUE;
3188 }
3190
3191 return FALSE;
3192}
3193
3194/**********************************************************************/
3198 const char *name)
3199{
3200 struct section *psection;
3201
3202 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
3203
3204 if (name == nullptr || '\0' == name[0]) {
3205 SECFILE_LOG(secfile, nullptr, "Cannot create a section without name.");
3206 return nullptr;
3207 }
3208
3210 SECFILE_LOG(secfile, nullptr, "\"%s\" is not a valid section name.",
3211 name);
3212 return nullptr;
3213 }
3214
3215 if (secfile_section_by_name(secfile, name) != nullptr) {
3216 /* We cannot duplicate sections in any case! Not even if one is
3217 * include -section and the other not. */
3218 SECFILE_LOG(secfile, nullptr, "Section \"%s\" already exists.", name);
3219 return nullptr;
3220 }
3221
3222 psection = fc_malloc(sizeof(struct section));
3223 psection->special = EST_NORMAL;
3224 psection->name = fc_strdup(name);
3226
3227 /* Append to secfile. */
3228 psection->secfile = secfile;
3230
3231 if (secfile->hash.sections != nullptr) {
3232 section_hash_insert(secfile->hash.sections, psection->name, psection);
3233 }
3234
3235 return psection;
3236}
3237
3238/**********************************************************************/
3241void section_destroy(struct section *psection)
3242{
3243 struct section_file *secfile;
3244
3245 SECFILE_RETURN_IF_FAIL(nullptr, psection, psection != nullptr);
3246
3247 section_clear_all(psection);
3248
3249 if ((secfile = psection->secfile)) {
3250 /* Detach from secfile. */
3251 if (section_list_remove(secfile->sections, psection)) {
3252 /* This has called section_destroy() already then. */
3253 return;
3254 }
3255 if (secfile->hash.sections != nullptr) {
3256 section_hash_remove(secfile->hash.sections, psection->name);
3257 }
3258 }
3259
3260 entry_list_destroy(psection->entries);
3261 free(psection->name);
3262 free(psection);
3263}
3264
3265/**********************************************************************/
3268void section_clear_all(struct section *psection)
3269{
3270 SECFILE_RETURN_IF_FAIL(nullptr, psection, psection != nullptr);
3271
3272 /* This include the removing of the hash data. */
3273 entry_list_clear(psection->entries);
3274
3275 if (0 < entry_list_size(psection->entries)) {
3276 SECFILE_LOG(psection->secfile, psection,
3277 "After clearing all, %d entries are still remaining.",
3278 entry_list_size(psection->entries));
3279 }
3280}
3281
3282/**********************************************************************/
3285bool section_set_name(struct section *psection, const char *name)
3286{
3287 struct section_file *secfile;
3288 struct section *pother;
3289
3290 SECFILE_RETURN_VAL_IF_FAIL(nullptr, psection, psection != nullptr, FALSE);
3291 secfile = psection->secfile;
3292 SECFILE_RETURN_VAL_IF_FAIL(secfile, psection, secfile != nullptr, FALSE);
3293
3294 if (name == nullptr || '\0' == name[0]) {
3295 SECFILE_LOG(secfile, psection, "No new name for section \"%s\".",
3296 psection->name);
3297 return FALSE;
3298 }
3299
3301 SECFILE_LOG(secfile, psection,
3302 "\"%s\" is not a valid section name for section \"%s\".",
3303 name, psection->name);
3304 return FALSE;
3305 }
3306
3308 && pother != psection) {
3309 /* We cannot duplicate sections in any case! */
3310 SECFILE_LOG(secfile, psection, "Section \"%s\" already exists.", name);
3311 return FALSE;
3312 }
3313
3314 /* Remove old references in the hash tables. */
3315 if (secfile->hash.sections != nullptr) {
3317 }
3318 if (secfile->hash.entries != nullptr) {
3319 entry_list_iterate(psection->entries, pentry) {
3322 }
3323
3324 /* Really rename. */
3325 free(psection->name);
3326 psection->name = fc_strdup(name);
3327
3328 /* Reinsert new references into the hash tables. */
3329 if (secfile->hash.sections != nullptr) {
3330 section_hash_insert(secfile->hash.sections, psection->name, psection);
3331 }
3332 if (secfile->hash.entries != nullptr) {
3333 entry_list_iterate(psection->entries, pentry) {
3336 }
3337
3338 return TRUE;
3339}
3340
3341/**********************************************************************/
3345const struct entry_list *section_entries(const struct section *psection)
3346{
3347 return (psection != nullptr ? psection->entries : nullptr);
3348}
3349
3350/**********************************************************************/
3354 const char *name)
3355{
3356 SECFILE_RETURN_VAL_IF_FAIL(nullptr, psection, psection != nullptr, nullptr);
3357
3359 if (0 == strcmp(entry_name(pentry), name)) {
3361 return pentry;
3362 }
3364
3365 return nullptr;
3366}
3367
3368/**********************************************************************/
3372 const char *path, ...)
3373{
3375 struct entry *pentry;
3376 va_list args;
3377
3378 SECFILE_RETURN_VAL_IF_FAIL(nullptr, psection, psection != nullptr, nullptr);
3379
3380 va_start(args, path);
3381 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
3382 va_end(args);
3383
3385 return pentry;
3386 }
3387
3388 /* Try with full path. */
3390 && psection == entry_section(pentry)) {
3391 /* Ensured this is really owned by this section. */
3392 return pentry;
3393 }
3394
3395 return nullptr;
3396}
3397
3398/**********************************************************************/
3401static struct entry *entry_new(struct section *psection, const char *name)
3402{
3403 struct section_file *secfile;
3404 struct entry *pentry;
3405 bool long_comment = !strcmp("#", name);
3406
3407 SECFILE_RETURN_VAL_IF_FAIL(nullptr, psection, psection != nullptr, nullptr);
3408
3409 secfile = psection->secfile;
3410 if (name == nullptr || '\0' == name[0]) {
3411 SECFILE_LOG(secfile, psection, "Cannot create an entry without name.");
3412 return nullptr;
3413 }
3414
3416 SECFILE_LOG(secfile, psection, "\"%s\" is not a valid entry name.",
3417 name);
3418 return nullptr;
3419 }
3420
3421 if (!secfile->allow_duplicates
3422 && section_entry_by_name(psection, name) != nullptr) {
3423 SECFILE_LOG(secfile, psection, "Entry \"%s\" already exists.", name);
3424 return nullptr;
3425 }
3426
3427 pentry = fc_malloc(sizeof(struct entry));
3428 if (long_comment) {
3429 pentry->name = nullptr;
3430 } else {
3431 pentry->name = fc_strdup(name);
3432 }
3433 pentry->type = ENTRY_ILLEGAL;
3434 pentry->used = 0;
3435 pentry->comment = nullptr;
3436
3437 /* Append to section. */
3438 pentry->psection = psection;
3440
3441 /* Notify secfile. */
3442 secfile->num_entries++;
3443 secfile_hash_insert(secfile, pentry);
3444
3445 return pentry;
3446}
3447
3448/**********************************************************************/
3452 const char *name, int value)
3453{
3454 struct entry *pentry = entry_new(psection, name);
3455
3456 if (pentry != nullptr) {
3457 pentry->type = ENTRY_INT;
3458 pentry->integer.value = value;
3459 }
3460
3461 return pentry;
3462}
3463
3464/**********************************************************************/
3468 const char *name, bool value)
3469{
3470 struct entry *pentry = entry_new(psection, name);
3471
3472 if (pentry != nullptr) {
3473 pentry->type = ENTRY_BOOL;
3474 pentry->boolean.value = value;
3475 }
3476
3477 return pentry;
3478}
3479
3480/**********************************************************************/
3484 const char *name, float value)
3485{
3486 struct entry *pentry = entry_new(psection, name);
3487
3488 if (pentry != nullptr) {
3489 pentry->type = ENTRY_FLOAT;
3490 pentry->floating.value = value;
3491 }
3492
3493 return pentry;
3494}
3495
3496/**********************************************************************/
3500 const char *name, const char *value,
3501 bool escaped)
3502{
3503 struct entry *pentry = entry_new(psection, name);
3504
3505 if (pentry != nullptr) {
3506 pentry->type = ENTRY_STR;
3507 pentry->string.value = fc_strdup(value != nullptr ? value : "");
3508 pentry->string.escaped = escaped;
3509 pentry->string.raw = FALSE;
3510 pentry->string.gt_marking = FALSE;
3511 }
3512
3513 return pentry;
3514}
3515
3516/**********************************************************************/
3520 const char *name,
3521 const char *value)
3522{
3523 struct entry *pentry = entry_new(psection, name);
3524
3525 if (pentry != nullptr) {
3527 pentry->string.value = fc_strdup(value != nullptr ? value : "");
3528 }
3529
3530 return pentry;
3531}
3532
3533/**********************************************************************/
3537 const char *comment)
3538{
3539 struct entry *pentry = entry_new(psection, "#");
3540
3541 if (pentry != nullptr) {
3542 pentry->type = ENTRY_LONG_COMMENT;
3543 pentry->comment = fc_strdup(comment != nullptr ? comment : "");
3544 }
3545
3546 return pentry;
3547}
3548
3549/**********************************************************************/
3553{
3554 struct section_file *secfile;
3555 struct section *psection;
3556
3557 if (pentry == nullptr) {
3558 return;
3559 }
3560
3561 if ((psection = pentry->psection)) {
3562 /* Detach from section. */
3563 if (entry_list_remove(psection->entries, pentry)) {
3564 /* This has called entry_destroy() already then. */
3565 return;
3566 }
3567 if ((secfile = psection->secfile)) {
3568 /* Detach from secfile. */
3571 }
3572 }
3573
3574 /* Specific type free. */
3575 switch (pentry->type) {
3576 case ENTRY_BOOL:
3577 case ENTRY_INT:
3578 case ENTRY_FLOAT:
3579 case ENTRY_LONG_COMMENT:
3580 break;
3581
3582 case ENTRY_STR:
3584 free(pentry->string.value);
3585 break;
3586
3587 case ENTRY_ILLEGAL:
3588 fc_assert(pentry->type != ENTRY_ILLEGAL);
3589 break;
3590 }
3591
3592 /* Common free. */
3593 free(pentry->name);
3594 if (pentry->comment != nullptr) {
3595 free(pentry->comment);
3596 }
3597 free(pentry);
3598}
3599
3600/**********************************************************************/
3603struct section *entry_section(const struct entry *pentry)
3604{
3605 return (pentry != nullptr ? pentry->psection : nullptr);
3606}
3607
3608/**********************************************************************/
3612{
3613 return (pentry != nullptr ? pentry->type : ENTRY_ILLEGAL);
3614}
3615
3616/**********************************************************************/
3619int entry_path(const struct entry *pentry, char *buf, size_t buf_len)
3620{
3621 return fc_snprintf(buf, buf_len, "%s.%s",
3624}
3625
3626/**********************************************************************/
3629const char *entry_name(const struct entry *pentry)
3630{
3631 return (pentry != nullptr ? pentry->name : nullptr);
3632}
3633
3634/**********************************************************************/
3637bool entry_set_name(struct entry *pentry, const char *name)
3638{
3639 struct section *psection;
3640 struct section_file *secfile;
3641
3642 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3643 psection = pentry->psection;
3644 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, psection != nullptr, FALSE);
3645 secfile = psection->secfile;
3646 SECFILE_RETURN_VAL_IF_FAIL(nullptr, psection, secfile != nullptr, FALSE);
3647
3648 if (name == nullptr || '\0' == name[0]) {
3649 SECFILE_LOG(secfile, psection, "No new name for entry \"%s\".",
3650 pentry->name);
3651 return FALSE;
3652 }
3653
3655 SECFILE_LOG(secfile, psection,
3656 "\"%s\" is not a valid entry name for entry \"%s\".",
3657 name, pentry->name);
3658 return FALSE;
3659 }
3660
3661 if (!secfile->allow_duplicates) {
3663
3664 if (pother != nullptr && pother != pentry) {
3665 SECFILE_LOG(secfile, psection, "Entry \"%s\" already exists.", name);
3666 return FALSE;
3667 }
3668 }
3669
3670 /* Remove from hash table the old path. */
3671 secfile_hash_delete(secfile, pentry);
3672
3673 /* Really rename the entry. */
3674 free(pentry->name);
3675 pentry->name = fc_strdup(name);
3676
3677 /* Insert into hash table the new path. */
3678 secfile_hash_insert(secfile, pentry);
3679 return TRUE;
3680}
3681
3682/**********************************************************************/
3685const char *entry_comment(const struct entry *pentry)
3686{
3687 return (pentry != nullptr ? pentry->comment : nullptr);
3688}
3689
3690/**********************************************************************/
3693void entry_set_comment(struct entry *pentry, const char *comment)
3694{
3695 if (pentry == nullptr) {
3696 return;
3697 }
3698
3699 if (pentry->comment != nullptr) {
3700 free(pentry->comment);
3701 }
3702
3703 pentry->comment = (comment != nullptr ? fc_strdup(comment) : nullptr);
3704}
3705
3706/**********************************************************************/
3709static inline bool entry_used(const struct entry *pentry)
3710{
3711 return (0 < pentry->used);
3712}
3713
3714/**********************************************************************/
3717static inline void entry_use(struct entry *pentry)
3718{
3719 pentry->used++;
3720}
3721
3722/**********************************************************************/
3726bool entry_bool_get(const struct entry *pentry, bool *value)
3727{
3728 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3729
3730 if (ENTRY_INT == pentry->type
3731 && (pentry->integer.value == 0
3732 || pentry->integer.value == 1)
3733 && pentry->psection != nullptr
3734 && pentry->psection->secfile != nullptr
3735 && pentry->psection->secfile->allow_digital_boolean) {
3736 *value = (0 != pentry->integer.value);
3737 return TRUE;
3738 }
3739
3740 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection != nullptr
3741 ? pentry->psection->secfile : nullptr,
3742 pentry->psection,
3743 ENTRY_BOOL == pentry->type, FALSE);
3744
3745 if (value != nullptr) {
3746 *value = pentry->boolean.value;
3747 }
3748
3749 return TRUE;
3750}
3751
3752/**********************************************************************/
3756{
3757 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3758 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection->secfile, pentry->psection,
3759 ENTRY_BOOL == pentry->type, FALSE);
3760
3761 pentry->boolean.value = value;
3762 return TRUE;
3763}
3764
3765/**********************************************************************/
3768bool entry_float_get(const struct entry *pentry, float *value)
3769{
3770 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3771 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection->secfile, pentry->psection,
3772 ENTRY_FLOAT == pentry->type, FALSE);
3773
3774 if (value != nullptr) {
3775 *value = pentry->floating.value;
3776 }
3777
3778 return TRUE;
3779}
3780
3781/**********************************************************************/
3784bool entry_float_set(struct entry *pentry, float value)
3785{
3786 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3787 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection->secfile, pentry->psection,
3788 ENTRY_FLOAT == pentry->type, FALSE);
3789
3790 pentry->floating.value = value;
3791
3792 return TRUE;
3793}
3794
3795/**********************************************************************/
3798bool entry_int_get(const struct entry *pentry, int *value)
3799{
3800 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3801 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection->secfile, pentry->psection,
3802 ENTRY_INT == pentry->type, FALSE);
3803
3804 if (value != nullptr) {
3805 *value = pentry->integer.value;
3806 }
3807
3808 return TRUE;
3809}
3810
3811/**********************************************************************/
3815{
3816 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3817 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection->secfile, pentry->psection,
3818 ENTRY_INT == pentry->type, FALSE);
3819
3820 pentry->integer.value = value;
3821 return TRUE;
3822}
3823
3824/**********************************************************************/
3827bool entry_str_get(const struct entry *pentry, const char **value)
3828{
3829 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3830 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection->secfile, pentry->psection,
3831 ENTRY_STR == pentry->type, FALSE);
3832
3833 if (value != nullptr) {
3834 *value = pentry->string.value;
3835 }
3836
3837 return TRUE;
3838}
3839
3840/**********************************************************************/
3843bool entry_str_set(struct entry *pentry, const char *value)
3844{
3845 char *old_val;
3846
3847 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3848 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection->secfile, pentry->psection,
3849 ENTRY_STR == pentry->type, FALSE);
3850
3851 /* We free() old value only after we've placed the new one, to
3852 * support secfile_replace_str_vec() calls that want to keep some of
3853 * the entries from the old vector in the new one. We don't want
3854 * to lose the entry in between. */
3855 old_val = pentry->string.value;
3856 pentry->string.value = fc_strdup(value != nullptr ? value : "");
3857 free(old_val);
3858
3859 return TRUE;
3860}
3861
3862/**********************************************************************/
3865bool entry_str_escaped(const struct entry *pentry)
3866{
3867 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3868 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection->secfile, pentry->psection,
3869 ENTRY_STR == pentry->type, FALSE);
3870
3871 return pentry->string.escaped;
3872}
3873
3874/**********************************************************************/
3878{
3879 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3880 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection->secfile, pentry->psection,
3881 ENTRY_STR == pentry->type, FALSE);
3882
3883 pentry->string.escaped = escaped;
3884
3885 return TRUE;
3886}
3887
3888/**********************************************************************/
3892{
3893 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3894 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection->secfile, pentry->psection,
3895 ENTRY_STR == pentry->type, FALSE);
3896
3897 /* don't put gettext marking on empty strings */
3898 if (pentry->string.value[0] != '\0') {
3899 pentry->string.gt_marking = gt_marking;
3900 return TRUE;
3901 } else {
3902 pentry->string.gt_marking = FALSE;
3903 return !gt_marking;
3904 }
3905}
3906
3907/************************************************************************/
3911static void make_escapes_quotes(const char *str, char *buf, size_t buf_len)
3912{
3913 char *dest = buf;
3914 /* Sometimes we insert a character, so keep
3915 * place for '\0' and an extra character. */
3916 const char *max = buf + buf_len - 2;
3917
3918 while (*str != '\0' && dest < max) {
3919 if (*str == '\"') {
3920 *dest++ = '\\';
3921 }
3922 *dest++ = *str++;
3923 }
3924 *dest = 0;
3925}
3926
3927/**********************************************************************/
3930static void entry_to_file(const struct entry *pentry, fz_FILE *fs)
3931{
3932 static char buf[8192];
3933 char *dot = nullptr;
3934 int i;
3935
3936 switch (pentry->type) {
3937 case ENTRY_BOOL:
3938 fz_fprintf(fs, "%s", pentry->boolean.value ? "TRUE" : "FALSE");
3939 break;
3940 case ENTRY_INT:
3941 fz_fprintf(fs, "%d", pentry->integer.value);
3942 break;
3943 case ENTRY_FLOAT:
3944 snprintf(buf, sizeof(buf), "%f", pentry->floating.value);
3945 for (i = 0; buf[i] != '\0' ; i++) {
3946 if (buf[i] == '.') {
3947 dot = &(buf[i]);
3948 break;
3949 }
3950 }
3951 if (dot == nullptr) {
3952 /* There's no '.' so it would seem like a integer value when loaded.
3953 * Force it not to look like an integer by adding ".0" */
3954 fz_fprintf(fs, "%s.0", buf);
3955 } else {
3956 fz_fprintf(fs, "%s", buf);
3957 }
3958 break;
3959 case ENTRY_STR:
3960 if (pentry->string.escaped) {
3961 make_escapes_quotes(pentry->string.value, buf, sizeof(buf));
3962 if (pentry->string.gt_marking) {
3963 fz_fprintf(fs, "_(\"%s\")", buf);
3964 } else {
3965 fz_fprintf(fs, "\"%s\"", buf);
3966 }
3967 } else if (pentry->string.raw) {
3968 fz_fprintf(fs, "%s", pentry->string.value);
3969 } else {
3970 fz_fprintf(fs, "$%s$", pentry->string.value);
3971 }
3972 break;
3974 fz_fprintf(fs, "*%s*", pentry->string.value);
3975 break;
3976 case ENTRY_LONG_COMMENT:
3977 fz_fprintf(fs, "%s\n", pentry->comment);
3978 break;
3979 case ENTRY_ILLEGAL:
3980 fc_assert(pentry->type != ENTRY_ILLEGAL);
3981 break;
3982 }
3983}
3984
3985/**********************************************************************/
3988static void entry_from_inf_token(struct section *psection, const char *name,
3989 const char *tok, struct inputfile *inf)
3990{
3992 log_error("%s", inf_log_str(inf, "Entry value not recognized: %s", tok));
3993 }
3994}
void astr_free(struct astring *astr)
Definition astring.c:148
void astr_set(struct astring *astr, const char *format,...)
Definition astring.c:251
void astr_init(struct astring *astr)
Definition astring.c:139
#define str
Definition astring.c:76
static const char * astr_str(const struct astring *astr) fc__attribute((nonnull(1)))
Definition astring.h:93
#define ASTRING_INIT
Definition astring.h:44
void bugreport_request(const char *reason_format,...)
Definition bugs.c:31
struct canvas int int struct sprite int int int width
Definition canvas_g.h:44
char * incite_cost
Definition comments.c:77
bool are_deprecation_warnings_enabled(void)
#define log_deprecation_always(message,...)
static void base(QVariant data1, QVariant data2)
Definition dialogs.cpp:3356
#define _(String)
Definition fcintl.h:67
char * inf_log_str(struct inputfile *inf, const char *message,...)
Definition inputfile.c:571
bool inf_at_eof(struct inputfile *inf)
Definition inputfile.c:349
struct inputfile * inf_from_file(const char *filename, datafilename_fn_t datafn)
Definition inputfile.c:220
struct inputfile * inf_from_stream(fz_FILE *stream, datafilename_fn_t datafn)
Definition inputfile.c:244
const char * name
Definition inputfile.c:127
void inf_close(struct inputfile *inf)
Definition inputfile.c:303
int inf_discard_tokens(struct inputfile *inf, enum inf_token_type type)
Definition inputfile.c:650
const char * inf_token(struct inputfile *inf, enum inf_token_type type)
Definition inputfile.c:611
@ INF_TOK_EOL
Definition inputfile.h:45
@ INF_TOK_COMMA
Definition inputfile.h:48
@ INF_TOK_TABLE_END
Definition inputfile.h:47
@ INF_TOK_VALUE
Definition inputfile.h:49
@ INF_TOK_TABLE_START
Definition inputfile.h:46
@ INF_TOK_ENTRY_NAME
Definition inputfile.h:44
@ INF_TOK_SECTION_NAME
Definition inputfile.h:43
int fz_fprintf(fz_FILE *fp, const char *format,...)
Definition ioz.c:971
fz_FILE * fz_from_file(const char *filename, const char *in_mode, enum fz_method method, int compress_level)
Definition ioz.c:230
const char * fz_strerror(fz_FILE *fp)
Definition ioz.c:1157
int fz_fclose(fz_FILE *fp)
Definition ioz.c:571
int fz_ferror(fz_FILE *fp)
Definition ioz.c:1099
fz_method
Definition ioz.h:37
#define log_verbose(message,...)
Definition log.h:110
#define fc_assert(condition)
Definition log.h:177
#define fc_assert_ret_val(condition, val)
Definition log.h:195
#define log_error(message,...)
Definition log.h:104
#define log_testmatic(message,...)
Definition log.h:124
#define fc_strdup(str)
Definition mem.h:43
#define fc_realloc(ptr, sz)
Definition mem.h:36
#define fc_malloc(sz)
Definition mem.h:34
int len
Definition packhand.c:129
struct section_file * secfile_new(bool allow_duplicates)
const char * section_name(const struct section *psection)
void secfile_destroy(struct section_file *secfile)
static struct entry * entry_new(struct section *psection, const char *name)
int secfile_lookup_bitwise_enum_default_full(const struct section_file *secfile, int defval, secfile_enum_is_valid_fn_t is_valid_fn, secfile_enum_by_name_fn_t by_name_fn, const char *path,...)
#define MAX_LINE_LEN
void entry_set_comment(struct entry *pentry, const char *comment)
size_t secfile_insert_bool_vec_full(struct section_file *secfile, const bool *values, size_t dim, const char *comment, bool allow_replace, const char *path,...)
bool entry_bool_get(const struct entry *pentry, bool *value)
void section_destroy(struct section *psection)
bool secfile_lookup_float(const struct section_file *secfile, float *fval, const char *path,...)
void secfile_check_unused(const struct section_file *secfile)
int entry_path(const struct entry *pentry, char *buf, size_t buf_len)
bool secfile_lookup_int(const struct section_file *secfile, int *ival, const char *path,...)
struct section * secfile_insert_long_comment(struct section_file *secfile, const char *comment)
static struct entry * section_entry_filereference_new(struct section *psection, const char *name, const char *value)
#define MAX_LEN_SECPATH
static void make_escapes_quotes(const char *str, char *buf, size_t buf_len)
bool secfile_entry_delete(struct section_file *secfile, const char *path,...)
bool entry_set_name(struct entry *pentry, const char *name)
bool secfile_lookup_enum_data(const struct section_file *secfile, int *pvalue, bool bitwise, secfile_enum_name_data_fn_t name_fn, secfile_data_t data, const char *path,...)
static bool secfile_hash_insert(struct section_file *secfile, struct entry *pentry)
bool entry_str_escaped(const struct entry *pentry)
struct entry * section_entry_int_new(struct section *psection, const char *name, int value)
struct entry * secfile_insert_bool_full(struct section_file *secfile, bool value, const char *comment, bool allow_replace, const char *path,...)
struct entry * secfile_insert_comment(struct section_file *secfile, const char *str, const char *path,...)
struct entry * secfile_insert_int_full(struct section_file *secfile, int value, const char *comment, bool allow_replace, const char *path,...)
struct section * entry_section(const struct entry *pentry)
static struct entry * section_entry_comment_new(struct section *psection, const char *comment)
struct section_list * secfile_sections_by_name_prefix(const struct section_file *secfile, const char *prefix)
int secfile_lookup_enum_default_data(const struct section_file *secfile, int defval, bool bitwise, secfile_enum_name_data_fn_t name_fn, secfile_data_t data, const char *path,...)
const struct entry_list * section_entries(const struct section *psection)
struct section_file * secfile_load_section(const char *filename, const char *section, bool allow_duplicates)
bool entry_bool_set(struct entry *pentry, bool value)
struct section_file * secfile_from_stream(fz_FILE *stream, bool allow_duplicates)
size_t secfile_insert_bitwise_enum_vec_full(struct section_file *secfile, const int *bitwise_vals, size_t dim, secfile_enum_name_fn_t name_fn, secfile_enum_iter_fn_t begin_fn, secfile_enum_iter_fn_t end_fn, secfile_enum_next_fn_t next_fn, const char *comment, bool allow_replace, const char *path,...)
static void entry_to_file(const struct entry *pentry, fz_FILE *fs)
static void entry_from_inf_token(struct section *psection, const char *name, const char *tok, struct inputfile *file)
bool entry_float_get(const struct entry *pentry, float *value)
const char ** secfile_lookup_str_vec(const struct section_file *secfile, size_t *dim, const char *path,...)
bool entry_str_set_escaped(struct entry *pentry, bool escaped)
const char * entry_name(const struct entry *pentry)
struct entry * secfile_entry_lookup(const struct section_file *secfile, const char *path,...)
bool entry_str_get(const struct entry *pentry, const char **value)
struct entry * secfile_insert_enum_data_full(struct section_file *secfile, int value, bool bitwise, secfile_enum_name_data_fn_t name_fn, secfile_data_t data, const char *comment, bool allow_replace, const char *path,...)
bool entry_str_set_gt_marking(struct entry *pentry, bool gt_marking)
#define SAVE_TABLES
bool secfile_save(const struct section_file *secfile, const char *filename, int compression_level, enum fz_method compression_method, bool do_formatting)
size_t secfile_insert_int_vec_full(struct section_file *secfile, const int *values, size_t dim, const char *comment, bool allow_replace, const char *path,...)
const char * secfile_lookup_str(const struct section_file *secfile, const char *path,...)
void entry_destroy(struct entry *pentry)
static bool entry_used(const struct entry *pentry)
void section_clear_all(struct section *psection)
int * secfile_lookup_int_vec(const struct section_file *secfile, size_t *dim, const char *path,...)
bool entry_float_set(struct entry *pentry, float value)
float secfile_lookup_float_default(const struct section_file *secfile, float def, const char *path,...)
bool * secfile_lookup_bool_vec(const struct section_file *secfile, size_t *dim, const char *path,...)
int secfile_lookup_plain_enum_default_full(const struct section_file *secfile, int defval, secfile_enum_is_valid_fn_t is_valid_fn, secfile_enum_by_name_fn_t by_name_fn, const char *path,...)
size_t secfile_insert_plain_enum_vec_full(struct section_file *secfile, const int *enumurators, size_t dim, secfile_enum_name_fn_t name_fn, const char *comment, bool allow_replace, const char *path,...)
struct entry * secfile_insert_float_full(struct section_file *secfile, float value, const char *comment, bool allow_replace, const char *path,...)
struct section * secfile_insert_include(struct section_file *secfile, const char *filename)
struct entry * secfile_insert_plain_enum_full(struct section_file *secfile, int enumerator, secfile_enum_name_fn_t name_fn, const char *comment, bool allow_replace, const char *path,...)
bool secfile_lookup_bool_default(const struct section_file *secfile, bool def, const char *path,...)
bool entry_int_set(struct entry *pentry, int value)
static bool is_secfile_entry_name_valid(const char *name)
struct entry * section_entry_by_name(const struct section *psection, const char *name)
const struct section_list * secfile_sections(const struct section_file *secfile)
static bool is_delim(char ch)
static int word_wrap_internal(char *new_buf, int indent, bool is_counting)
struct entry * secfile_insert_filereference(struct section_file *secfile, const char *filename, const char *path,...)
bool section_set_name(struct section *psection, const char *name)
struct entry * section_entry_str_new(struct section *psection, const char *name, const char *value, bool escaped)
struct entry * section_entry_float_new(struct section *psection, const char *name, float value)
int secfile_lookup_int_default(const struct section_file *secfile, int def, const char *path,...)
struct entry * section_entry_lookup(const struct section *psection, const char *path,...)
size_t secfile_insert_str_vec_full(struct section_file *secfile, const char *const *strings, size_t dim, const char *comment, bool allow_replace, bool no_escape, bool gt_marking, const char *path,...)
bool entry_int_get(const struct entry *pentry, int *value)
int * secfile_lookup_bitwise_enum_vec_full(const struct section_file *secfile, size_t *dim, secfile_enum_is_valid_fn_t is_valid_fn, secfile_enum_by_name_fn_t by_name_fn, const char *path,...)
static void entry_use(struct entry *pentry)
int * secfile_lookup_enum_vec_data(const struct section_file *secfile, size_t *dim, bool bitwise, secfile_enum_name_data_fn_t name_fn, secfile_data_t data, const char *path,...)
struct entry * section_entry_bool_new(struct section *psection, const char *name, bool value)
bool secfile_lookup_plain_enum_full(const struct section_file *secfile, int *penumerator, secfile_enum_is_valid_fn_t is_valid_fn, secfile_enum_by_name_fn_t by_name_fn, const char *path,...)
const char * secfile_name(const struct section_file *secfile)
struct section * secfile_section_new(struct section_file *secfile, const char *name)
const char * entry_comment(const struct entry *pentry)
static bool secfile_hash_delete(struct section_file *secfile, struct entry *pentry)
struct entry * secfile_entry_by_path(const struct section_file *secfile, const char *path)
static struct section_file * secfile_from_input_file(struct inputfile *inf, const char *filename, const char *section, bool allow_duplicates)
struct section * secfile_section_by_name(const struct section_file *secfile, const char *name)
struct entry * secfile_insert_bitwise_enum_full(struct section_file *secfile, int bitwise_val, secfile_enum_name_fn_t name_fn, secfile_enum_iter_fn_t begin_fn, secfile_enum_iter_fn_t end_fn, secfile_enum_next_fn_t next_fn, const char *comment, bool allow_replace, const char *path,...)
struct entry * secfile_insert_str_full(struct section_file *secfile, const char *str, const char *comment, bool allow_replace, bool no_escape, enum entry_special_type stype, const char *path,...)
int * secfile_lookup_plain_enum_vec_full(const struct section_file *secfile, size_t *dim, secfile_enum_is_valid_fn_t is_valid_fn, secfile_enum_by_name_fn_t by_name_fn, const char *path,...)
static size_t entry_str_length(struct entry *pentry)
struct section * secfile_section_lookup(const struct section_file *secfile, const char *path,...)
const char * secfile_lookup_str_default(const struct section_file *secfile, const char *def, const char *path,...)
bool secfile_section_prefix_present(const struct section_file *secfile, const char *prefix)
static bool is_legal_table_entry_name(char c, bool num)
bool entry_str_set(struct entry *pentry, const char *value)
static void format_entry_string(struct entry *pentry, int indent)
bool secfile_lookup_bitwise_enum_full(const struct section_file *secfile, int *penumerator, secfile_enum_is_valid_fn_t is_valid_fn, secfile_enum_by_name_fn_t by_name_fn, const char *path,...)
size_t secfile_insert_enum_vec_data_full(struct section_file *secfile, const int *values, size_t dim, bool bitwise, secfile_enum_name_data_fn_t name_fn, secfile_data_t data, const char *comment, bool allow_replace, const char *path,...)
bool secfile_lookup_bool(const struct section_file *secfile, bool *bval, const char *path,...)
static const char * datafilename(const char *filename)
int secfile_lookup_int_def_min_max(const struct section_file *secfile, int defval, int minval, int maxval, const char *path,...)
static void find_next_delims(char *start_ptr, int *delim1_offset, int *delim2_offset)
static struct section * secfile_insert_base(struct section_file *secfile, const char *path, const char **pent_name)
enum entry_type entry_type_get(const struct entry *pentry)
int(* secfile_enum_iter_fn_t)(void)
const char *(* secfile_enum_name_data_fn_t)(secfile_data_t data, int enumerator)
entry_type
@ ENTRY_FILEREFERENCE
@ ENTRY_LONG_COMMENT
@ ENTRY_INT
@ ENTRY_FLOAT
@ ENTRY_STR
@ ENTRY_ILLEGAL
@ ENTRY_BOOL
#define section_list_iterate(seclist, psection)
#define entry_list_iterate_end
bool(* secfile_enum_is_valid_fn_t)(int enumerator)
entry_special_type
@ EST_COMMENT
@ EST_NORMAL
@ EST_INCLUDE
const char *(* secfile_enum_name_fn_t)(int enumerator)
#define section_list_iterate_end
#define entry_list_iterate(entlist, pentry)
int(* secfile_enum_by_name_fn_t)(const char *enum_name, int(*strcmp_fn)(const char *, const char *))
int(* secfile_enum_next_fn_t)(int enumerator)
const void * secfile_data_t
bool entry_from_token(struct section *psection, const char *name, const char *tok)
#define SECFILE_LOG(secfile, psection, format,...)
#define SECFILE_RETURN_VAL_IF_FAIL(secfile, psection, condition, value)
#define SECFILE_RETURN_IF_FAIL(secfile, psection, condition)
const char * fileinfoname(const struct strvec *dirs, const char *filename)
Definition shared.c:1094
void interpret_tilde(char *buf, size_t buf_size, const char *filename)
Definition shared.c:1713
void remove_leading_trailing_spaces(char *s)
Definition shared.c:444
const struct strvec * get_data_dirs(void)
Definition shared.c:886
enum entry_type type
int value
bool gt_marking
char * value
bool raw
struct section * psection
float value
bool escaped
bool value
char * comment
char * name
struct entry_hash * entries
struct section_file::@8 hash
struct section_list * sections
bool allow_duplicates
unsigned int num_includes
unsigned int num_long_comments
size_t num_entries
struct entry_list * entries
char * name
enum entry_special_type special
struct section_file * secfile
int fc_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:925
bool fc_isalpha(char c)
Definition support.c:1172
size_t fc_strlcpy(char *dest, const char *src, size_t n)
Definition support.c:742
int fc_strcasecmp(const char *str0, const char *str1)
Definition support.c:186
int cat_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:951
int fc_vsnprintf(char *str, size_t n, const char *format, va_list ap)
Definition support.c:851
bool fc_isalnum(char c)
Definition support.c:1161
#define sz_strlcpy(dest, src)
Definition support.h:195
#define TRUE
Definition support.h:46
#define FALSE
Definition support.h:47
#define sz_strlcat(dest, src)
Definition support.h:196
#define fc_strncmp(_s1_, _s2_, _len_)
Definition support.h:160