Freeciv-3.3
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 <stdarg.h>
156#include <stdlib.h>
157#include <string.h>
158
159/* utility */
160#include "astring.h"
161#include "bugs.h"
162#include "deprecations.h"
163#include "fcintl.h"
164#include "inputfile.h"
165#include "ioz.h"
166#include "log.h"
167#include "mem.h"
168#include "registry.h"
169#include "section_file.h"
170#include "shared.h"
171#include "support.h"
172
173#include "registry_ini.h"
174
175#define MAX_LEN_SECPATH 1024
176
177/* Set to FALSE for old-style savefiles. */
178#define SAVE_TABLES TRUE
179
180#define SPECVEC_TAG astring
181#include "specvec.h"
182
183static inline bool entry_used(const struct entry *pentry);
184static inline void entry_use(struct entry *pentry);
185
186static void entry_to_file(const struct entry *pentry, fz_FILE *fs);
187static void entry_from_inf_token(struct section *psection, const char *name,
188 const char *tok, struct inputfile *file);
189
190/* An 'entry' is a string, integer, boolean or string vector;
191 * See enum entry_type in registry.h.
192 */
193struct entry {
194 struct section *psection; /* Parent section. */
195 char *name; /* Name, not including section prefix. */
196 enum entry_type type; /* The type of the entry. */
197 int used; /* Number of times entry looked up. */
198 char *comment; /* Comment, may be nullptr. */
199
200 union {
201 /* ENTRY_BOOL */
202 struct {
203 bool value;
204 } boolean;
205 /* ENTRY_INT */
206 struct {
207 int value;
208 } integer;
209 /* ENTRY_FLOAT */
210 struct {
211 float value;
212 } floating;
213 /* ENTRY_STR */
214 struct {
215 char *value; /* Malloced string. */
216 bool escaped; /* " or $. Usually TRUE */
217 bool raw; /* Do not add anything. */
218 bool gt_marking; /* Save with gettext marking. */
219 } string;
220 };
221};
222
224 const char *name,
225 const char *value);
226static struct entry *section_entry_comment_new(struct section *psection,
227 const char *comment);
228
229/**********************************************************************/
232static const char *datafilename(const char *filename)
233{
234 return fileinfoname(get_data_dirs(), filename);
235}
236
237/**********************************************************************/
240static bool is_secfile_entry_name_valid(const char *name)
241{
242 static const char *const allowed = "_.,-[]";
243
244 while ('\0' != *name) {
245 if (!fc_isalnum(*name) && strchr(allowed, *name) == nullptr) {
246 return FALSE;
247 }
248 name++;
249 }
250 return TRUE;
251}
252
253/**********************************************************************/
256static bool secfile_hash_insert(struct section_file *secfile,
257 struct entry *pentry)
258{
259 char buf[256];
260 struct entry *hentry;
261
262 if (secfile->hash.entries == nullptr) {
263 /* Consider as success if this secfile doesn't have built the entries
264 * hash table. */
265 return TRUE;
266 }
267
268 entry_path(pentry, buf, sizeof(buf));
270 nullptr, &hentry)) {
272 if (!secfile->allow_duplicates) {
274 "Tried to insert same value twice: %s", buf);
275 return FALSE;
276 }
277 }
278
279 return TRUE;
280}
281
282/**********************************************************************/
285static bool secfile_hash_delete(struct section_file *secfile,
286 struct entry *pentry)
287{
288 char buf[256];
289
290 if (secfile->hash.entries == nullptr) {
291 /* Consider as success if this secfile doesn't have built the entries
292 * hash table. */
293 return TRUE;
294 }
295
296 entry_path(pentry, buf, sizeof(buf));
297 return entry_hash_remove(secfile->hash.entries, buf);
298}
299
300/**********************************************************************/
304 const char *filename,
305 const char *section,
306 bool allow_duplicates)
307{
308 struct section_file *secfile;
309 struct section *psection = nullptr;
310 struct section *single_section = nullptr;
311 bool table_state = FALSE; /* TRUE when within tabular format. */
312 int table_lineno = 0; /* Row number in tabular, 0 top data row. */
313 const char *tok;
314 int i;
315 struct astring base_name = ASTRING_INIT; /* for table or single entry */
317 struct astring_vector columns; /* astrings for column headings */
318 bool found_my_section = FALSE;
319 bool error = FALSE;
320
321 if (!inf) {
322 return nullptr;
323 }
324
325 /* Assign the real value later, to speed up the creation of new entries. */
326 secfile = secfile_new(TRUE);
327 if (filename) {
328 secfile->name = fc_strdup(filename);
329 } else {
330 secfile->name = nullptr;
331 }
332
334
335 if (filename) {
336 log_verbose("Reading registry from \"%s\"", filename);
337 } else {
338 log_verbose("Reading registry");
339 }
340
341 while (!inf_at_eof(inf)) {
342 if (inf_token(inf, INF_TOK_EOL)) {
343 continue;
344 }
345 if (inf_at_eof(inf)) {
346 /* may only realise at eof after trying to read eol above */
347 break;
348 }
350 if (tok) {
351 if (found_my_section) {
352 /* This shortcut will stop any further loading after the requested
353 * section has been loaded (i.e., at the start of a new section).
354 * This is needed to make the behavior useful, since the whole
355 * purpose is to short-cut further loading of the file. However
356 * normally a section may be split up, and that will no longer
357 * work here because it will be short-cut. */
358 SECFILE_LOG(secfile, psection, "%s",
359 inf_log_str(inf, "Found requested section; finishing"));
360 goto END;
361 }
362 if (table_state) {
363 SECFILE_LOG(secfile, psection, "%s",
364 inf_log_str(inf, "New section during table"));
365 error = TRUE;
366 goto END;
367 }
368 /* Check if we already have a section with this name.
369 (Could ignore this and have a duplicate sections internally,
370 but then secfile_get_secnames_prefix would return duplicates.)
371 Duplicate section in input are likely to be useful for includes.
372 */
373 psection = secfile_section_by_name(secfile, tok);
374 if (!psection) {
375 if (!section || strcmp(tok, section) == 0) {
376 psection = secfile_section_new(secfile, tok);
377 if (section) {
378 single_section = psection;
380 }
381 }
382 }
383 if (!inf_token(inf, INF_TOK_EOL)) {
384 SECFILE_LOG(secfile, psection, "%s",
385 inf_log_str(inf, "Expected end of line"));
386 error = TRUE;
387 goto END;
388 }
389 continue;
390 }
392 if (!table_state) {
393 SECFILE_LOG(secfile, psection, "%s",
394 inf_log_str(inf, "Misplaced \"}\""));
395 error = TRUE;
396 goto END;
397 }
398 if (!inf_token(inf, INF_TOK_EOL)) {
399 SECFILE_LOG(secfile, psection, "%s",
400 inf_log_str(inf, "Expected end of line"));
401 error = TRUE;
402 goto END;
403 }
405 continue;
406 }
407 if (table_state) {
408 i = -1;
409 do {
411
412 i++;
413
414 if (i < num_columns) {
415 astr_set(&field_name, "%s%d.%s", astr_str(&base_name),
417 } else {
418 astr_set(&field_name, "%s%d.%s,%d", astr_str(&base_name),
420 (int) (i - num_columns + 1));
421 }
422
423 inf_discard_tokens(inf, INF_TOK_EOL); /* Allow newlines */
424 if (!(tok = inf_token(inf, INF_TOK_VALUE))) {
425 SECFILE_LOG(secfile, psection, "%s",
426 inf_log_str(inf, "Expected value for %s",
428 error = TRUE;
429 goto END;
430 }
431
433 } while (inf_token(inf, INF_TOK_COMMA));
434
435 if (!inf_token(inf, INF_TOK_EOL)) {
436 SECFILE_LOG(secfile, psection, "%s",
437 inf_log_str(inf, "Expected end of line or comma"));
438 error = TRUE;
439 goto END;
440 }
441 table_lineno++;
442 continue;
443 }
444
445 if (!(tok = inf_token(inf, INF_TOK_ENTRY_NAME))) {
446 SECFILE_LOG(secfile, psection, "%s",
447 inf_log_str(inf, "Expected entry name"));
448 error = TRUE;
449 goto END;
450 }
451
452 /* need to store tok before next calls: */
453 astr_set(&base_name, "%s", tok);
454
455 inf_discard_tokens(inf, INF_TOK_EOL); /* allow newlines */
456
458 i = -1;
459 do {
460 i++;
461 inf_discard_tokens(inf, INF_TOK_EOL); /* Allow newlines */
462 if (!(tok = inf_token(inf, INF_TOK_VALUE))) {
463 SECFILE_LOG(secfile, psection, "%s",
464 inf_log_str(inf, "Expected column header %s,%d",
465 astr_str(&base_name), i));
466 error = TRUE;
467 goto END;
468 }
469 if (tok[0] != '\"') {
470 SECFILE_LOG(secfile, psection, "%s",
471 inf_log_str(inf, "Table column header non-string"));
472 error = TRUE;
473 goto END;
474 }
475 { /* Expand columns: */
476 int j, n_prev;
477
479 for (j = i + 1; j < n_prev; j++) {
480 astr_free(&columns.p[j]);
481 }
483 for (j = n_prev; j < i + 1; j++) {
484 astr_init(&columns.p[j]);
485 }
486 }
487 astr_set(&columns.p[i], "%s", tok + 1);
488
489 } while (inf_token(inf, INF_TOK_COMMA));
490
491 if (!inf_token(inf, INF_TOK_EOL)) {
492 SECFILE_LOG(secfile, psection, "%s",
493 inf_log_str(inf, "Expected end of line or comma"));
494 error = TRUE;
495 goto END;
496 }
498 table_lineno = 0;
499 continue;
500 }
501 /* ordinary value: */
502 i = -1;
503 do {
504 i++;
505 inf_discard_tokens(inf, INF_TOK_EOL); /* Allow newlines */
506 if (!(tok = inf_token(inf, INF_TOK_VALUE))) {
507 if (i == 0) {
508 SECFILE_LOG(secfile, psection, "%s",
509 inf_log_str(inf, "Expected value for %s",
511 } else {
512 SECFILE_LOG(secfile, psection, "%s",
513 inf_log_str(inf, "Expected value for %s,%d",
514 astr_str(&base_name), i));
515 }
516 error = TRUE;
517 goto END;
518 }
519 if (i == 0) {
521 } else {
522 astr_set(&field_name, "%s,%d", astr_str(&base_name), i);
524 }
525 } while (inf_token(inf, INF_TOK_COMMA));
526 if (!inf_token(inf, INF_TOK_EOL)) {
527 SECFILE_LOG(secfile, psection, "%s",
528 inf_log_str(inf, "Expected end of line or comma"));
529 error = TRUE;
530 goto END;
531 }
532 }
533
534 if (table_state) {
535 SECFILE_LOG(secfile, psection,
536 "Finished registry before end of table");
537 error = TRUE;
538 }
539
540END:
541 inf_close(inf);
544 for (i = 0; i < astring_vector_size(&columns); i++) {
545 astr_free(&columns.p[i]);
546 }
548
549 if (section != nullptr) {
550 if (!found_my_section) {
551 secfile_destroy(secfile);
552 return nullptr;
553 }
554
555 /* Build the entry hash table with single section information */
556 secfile->allow_duplicates = allow_duplicates;
558 if (!secfile_hash_insert(secfile, pentry)) {
559 secfile_destroy(secfile);
560 return nullptr;
561 }
563
564 return secfile;
565 }
566
567 if (!error) {
568 /* Build the entry hash table. */
569 secfile->allow_duplicates = allow_duplicates;
570 secfile->hash.entries = entry_hash_new_nentries(secfile->num_entries);
571
574 if (!secfile_hash_insert(secfile, pentry)) {
575 error = TRUE;
576 break;
577 }
579 if (error) {
580 break;
581 }
583 }
584 if (error) {
585 secfile_destroy(secfile);
586 return nullptr;
587 } else {
588 return secfile;
589 }
590}
591
592/**********************************************************************/
596struct section_file *secfile_load_section(const char *filename,
597 const char *section,
598 bool allow_duplicates)
599{
600 char real_filename[1024];
601
602 interpret_tilde(real_filename, sizeof(real_filename), filename);
604 filename, section, allow_duplicates);
605}
606
607/**********************************************************************/
616
617/**********************************************************************/
620static bool is_legal_table_entry_name(char c, bool num)
621{
622 return (num ? fc_isalnum(c) : fc_isalpha(c)) || c == '_';
623}
624
625/**********************************************************************/
644bool secfile_save(const struct section_file *secfile, const char *filename,
646{
647 char real_filename[1024];
648 char pentry_name[128];
649 const char *col_entry_name;
650 fz_FILE *fs;
651 const struct entry_list_link *ent_iter, *save_iter, *col_iter;
652 struct entry *pentry, *col_pentry;
653 int i;
654
655 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, FALSE);
656
657 if (filename == nullptr) {
658 filename = secfile->name;
659 }
660
661 interpret_tilde(real_filename, sizeof(real_filename), filename);
664
665 if (!fs) {
666 SECFILE_LOG(secfile, nullptr, _("Could not open %s for writing"), real_filename);
667
668 return FALSE;
669 }
670
672 if (psection->special == EST_INCLUDE) {
676
677 fc_assert(!strcmp(entry_name(pentry), "file"));
678
679 fz_fprintf(fs, "*include ");
681 fz_fprintf(fs, "\n");
682 }
683 } else if (psection->special == EST_COMMENT) {
687
688 fc_assert(!strcmp(entry_name(pentry), "comment"));
689
691 fz_fprintf(fs, "\n");
692 }
693 } else {
694 fz_fprintf(fs, "\n[%s]\n", section_name(psection));
695
696 /* Following doesn't use entry_list_iterate() because we want to do
697 * tricky things with the iterators...
698 */
702 const char *comment;
703
704 /* Tables: break out of this loop if this is a non-table
705 * entry (pentry and ent_iter unchanged) or after table (pentry
706 * and ent_iter suitably updated, pentry possibly nullptr).
707 * After each table, loop again in case the next entry
708 * is another table.
709 */
710 for (;;) {
711 char *c, *first, base[64];
712 int offset, irow, icol, ncol;
713
714 /* Example: for first table name of "xyz0.blah":
715 * first points to the original string pentry->name
716 * base contains "xyz";
717 * offset = 5 (so first+offset gives "blah")
718 * note strlen(base) = offset - 2
719 */
720
721 if (!SAVE_TABLES) {
722 break;
723 }
724
725 if (pentry->type == ENTRY_LONG_COMMENT) {
726 break;
727 }
728
730 c = first = pentry_name;
731 if (*c == '\0' || !is_legal_table_entry_name(*c, FALSE)) {
732 break;
733 }
734 for (; *c != '\0' && is_legal_table_entry_name(*c, FALSE); c++) {
735 /* nothing */
736 }
737 if (fc_strncmp(c, "0.", 2)) {
738 break;
739 }
740 c += 2;
741 if (*c == '\0' || !is_legal_table_entry_name(*c, TRUE)) {
742 break;
743 }
744
745 offset = c - first;
746 first[offset - 2] = '\0';
747 sz_strlcpy(base, first);
748 first[offset - 2] = '0';
749 fz_fprintf(fs, "%s={", base);
750
751 /* Save an iterator at this first entry, which we can later use
752 * to repeatedly iterate over column names:
753 */
755
756 /* Write the column names, and calculate ncol: */
757 ncol = 0;
761 if (col_pentry->type == ENTRY_LONG_COMMENT) {
762 continue;
763 }
765 if (fc_strncmp(col_entry_name, first, offset)) {
766 break;
767 }
768 fz_fprintf(fs, "%s\"%s\"", (ncol == 0 ? "" : ","),
769 col_entry_name + offset);
770 ncol++;
771 }
772 fz_fprintf(fs, "\n");
773
774 /* Iterate over rows and columns, incrementing ent_iter as we go,
775 * and writing values to the table. Have a separate iterator
776 * to the column names to check they all match.
777 */
778 irow = icol = 0;
780 for (;;) {
781 char expect[128]; /* pentry->name we're expecting */
782
785
786 if (pentry && pentry->type == ENTRY_LONG_COMMENT) {
787 if (icol == 0) {
789 } else {
790 bugreport_request("In file %s, section %s there was\n"
791 "an attempt to insert comment in the middle of table row.",
793 }
795 continue;
796 } else {
797
798 fc_snprintf(expect, sizeof(expect), "%s%d.%s",
799 base, irow, entry_name(col_pentry) + offset);
800
801 /* break out of tabular if doesn't match: */
802 if ((!pentry) || (strcmp(entry_name(pentry), expect) != 0)) {
803 if (icol != 0) {
804 /* If the second or later row of a table is missing some
805 * entries that the first row had, we drop out of the tabular
806 * format. This is inefficient so we print a warning message;
807 * the calling code probably needs to be fixed so that it can
808 * use the more efficient tabular format.
809 *
810 * FIXME: If the first row is missing some entries that the
811 * second or later row has, then we'll drop out of tabular
812 * format without an error message. */
813 bugreport_request("In file %s, there is no entry in the registry for\n"
814 "%s.%s (or the entries are out of order). This means\n"
815 "a less efficient non-tabular format will be used.\n"
816 "To avoid this make sure all rows of a table are\n"
817 "filled out with an entry for every column.",
819 fz_fprintf(fs, "\n");
820 }
821 fz_fprintf(fs, "}\n");
822 break;
823 }
824 }
825
826 if (icol > 0) {
827 fz_fprintf(fs, ",");
828 }
830
833
834 icol++;
835 if (icol == ncol) {
836 fz_fprintf(fs, "\n");
837 irow++;
838 icol = 0;
840 }
841 }
842 if (!pentry) {
843 break;
844 }
845 }
846 if (!pentry) {
847 break;
848 }
849
850 if (pentry->type == ENTRY_LONG_COMMENT) {
852 } else {
853 /* Classic entry. */
857
858 /* Check for vector. */
859 for (i = 1;; i++) {
862 if (col_pentry == nullptr || col_pentry->type == ENTRY_LONG_COMMENT) {
863 break;
864 }
866 "%s,%d", col_entry_name, i);
868 break;
869 }
870 fz_fprintf(fs, ",");
873 }
874
876 if (comment) {
877 fz_fprintf(fs, " # %s\n", comment);
878 } else {
879 fz_fprintf(fs, "\n");
880 }
881 }
882 }
883 }
885
886 if (0 != fz_ferror(fs)) {
887 SECFILE_LOG(secfile, nullptr, "Error before closing %s: %s",
889 fz_fclose(fs);
890 return FALSE;
891 }
892 if (0 != fz_fclose(fs)) {
893 SECFILE_LOG(secfile, nullptr, "Error closing %s", real_filename);
894 return FALSE;
895 }
896
897 return TRUE;
898}
899
900/**********************************************************************/
907void secfile_check_unused(const struct section_file *secfile)
908{
909 bool any = FALSE;
910
913 if (!entry_used(pentry)) {
914 if (!any && secfile->name) {
915 log_verbose("Unused entries in file %s:", secfile->name);
916 any = TRUE;
917 }
919 log_deprecation_always("%s: unused entry: %s.%s",
920 secfile->name != nullptr ? secfile->name : "nameless",
922 } else {
923#ifdef FREECIV_TESTMATIC
924 log_testmatic("%s: unused entry: %s.%s",
925 secfile->name != nullptr ? secfile->name : "nameless",
927#else /* FREECIV_TESTMATIC */
928 log_verbose(" unused entry: %s.%s",
930#endif /* FREECIV_TESTMATIC */
931 }
932 }
935}
936
937/**********************************************************************/
943const char *secfile_name(const struct section_file *secfile)
944{
945 if (secfile == nullptr) {
946 return "nullptr";
947 } else if (secfile->name) {
948 return secfile->name;
949 } else {
950 return "(anonymous)";
951 }
952}
953
954/**********************************************************************/
958 const char *path,
959 const char **pent_name)
960{
962 char *ent_name;
963 struct section *psection;
964
965 sz_strlcpy(fullpath, path);
966
967 ent_name = strchr(fullpath, '.');
968 if (!ent_name) {
969 SECFILE_LOG(secfile, nullptr,
970 "Section and entry names must be separated by a dot.");
971 return nullptr;
972 }
973
974 /* Separates section and entry names. */
975 *ent_name = '\0';
976 *pent_name = path + (ent_name - fullpath) + 1;
978 if (psection) {
979 return psection;
980 } else {
982 }
983}
984
985/**********************************************************************/
989 bool value, const char *comment,
990 bool allow_replace,
991 const char *path, ...)
992{
994 const char *ent_name;
995 struct section *psection;
996 struct entry *pentry = nullptr;
997 va_list args;
998
999 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
1000
1001 va_start(args, path);
1002 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1003 va_end(args);
1004
1006 if (!psection) {
1007 return nullptr;
1008 }
1009
1010 if (allow_replace) {
1012 if (pentry != nullptr) {
1014 if (!entry_bool_set(pentry, value)) {
1015 return nullptr;
1016 }
1017 } else {
1019 pentry = nullptr;
1020 }
1021 }
1022 }
1023
1024 if (pentry == nullptr) {
1026 }
1027
1028 if (pentry != nullptr && comment != nullptr) {
1030 }
1031
1032 return pentry;
1033}
1034
1035/**********************************************************************/
1040 const bool *values, size_t dim,
1041 const char *comment, bool allow_replace,
1042 const char *path, ...)
1043{
1045 size_t i, ret = 0;
1046 va_list args;
1047
1048 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, 0);
1049
1050 va_start(args, path);
1051 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1052 va_end(args);
1053
1054 /* NB: 'path,0' is actually 'path'. See comment in the head
1055 * of the file. */
1056 if (dim > 0
1057 && secfile_insert_bool_full(secfile, values[0], comment,
1058 allow_replace, "%s", fullpath) != nullptr) {
1059 ret++;
1060 }
1061 for (i = 1; i < dim; i++) {
1062 if (secfile_insert_bool_full(secfile, values[i], comment,
1063 allow_replace, "%s,%d",
1064 fullpath, (int) i) != nullptr) {
1065 ret++;
1066 }
1067 }
1068
1069 return ret;
1070}
1071
1072/**********************************************************************/
1076 int value, const char *comment,
1077 bool allow_replace,
1078 const char *path, ...)
1079{
1081 const char *ent_name;
1082 struct section *psection;
1083 struct entry *pentry = nullptr;
1084 va_list args;
1085
1086 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
1087
1088 va_start(args, path);
1089 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1090 va_end(args);
1091
1093 if (!psection) {
1094 return nullptr;
1095 }
1096
1097 if (allow_replace) {
1099 if (pentry != nullptr) {
1101 if (!entry_int_set(pentry, value)) {
1102 return nullptr;
1103 }
1104 } else {
1106 pentry = nullptr;
1107 }
1108 }
1109 }
1110
1111 if (pentry == nullptr) {
1113 }
1114
1115 if (pentry != nullptr && comment != nullptr) {
1117 }
1118
1119 return pentry;
1120}
1121
1122/**********************************************************************/
1127 const int *values, size_t dim,
1128 const char *comment, bool allow_replace,
1129 const char *path, ...)
1130{
1132 size_t i, ret = 0;
1133 va_list args;
1134
1135 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, 0);
1136
1137 va_start(args, path);
1138 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1139 va_end(args);
1140
1141 /* NB: 'path,0' is actually 'path'. See comment in the head
1142 * of the file. */
1143 if (dim > 0
1144 && secfile_insert_int_full(secfile, values[0], comment,
1145 allow_replace, "%s", fullpath) != nullptr) {
1146 ret++;
1147 }
1148 for (i = 1; i < dim; i++) {
1149 if (secfile_insert_int_full(secfile, values[i], comment,
1150 allow_replace, "%s,%d",
1151 fullpath, (int) i) != nullptr) {
1152 ret++;
1153 }
1154 }
1155
1156 return ret;
1157}
1158
1159/**********************************************************************/
1163 float value, const char *comment,
1164 bool allow_replace,
1165 const char *path, ...)
1166{
1168 const char *ent_name;
1169 struct section *psection;
1170 struct entry *pentry = nullptr;
1171 va_list args;
1172
1173 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
1174
1175 va_start(args, path);
1176 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1177 va_end(args);
1178
1180 if (!psection) {
1181 return nullptr;
1182 }
1183
1184 if (allow_replace) {
1186 if (pentry != nullptr) {
1188 if (!entry_float_set(pentry, value)) {
1189 return nullptr;
1190 }
1191 } else {
1193 pentry = nullptr;
1194 }
1195 }
1196 }
1197
1198 if (pentry == nullptr) {
1200 }
1201
1202 if (pentry != nullptr && comment != nullptr) {
1204 }
1205
1206 return pentry;
1207}
1208
1209/**********************************************************************/
1213 const char *filename)
1214{
1215 struct section *psection;
1216 char buffer[200];
1217
1218 fc_snprintf(buffer, sizeof(buffer), "include_%u", secfile->num_includes++);
1219
1220 fc_assert_ret_val(secfile_section_by_name(secfile, buffer) == nullptr, nullptr);
1221
1222 /* Create include section. */
1223 psection = secfile_section_new(secfile, buffer);
1224 psection->special = EST_INCLUDE;
1225
1226 /* Then add string entry "file" to it. */
1227 secfile_insert_str_full(secfile, filename, nullptr, FALSE, FALSE,
1228 EST_INCLUDE, "%s.file", buffer);
1229
1230 return psection;
1231}
1232
1233/**********************************************************************/
1237 const char *comment)
1238{
1239 struct section *psection;
1240 char buffer[200];
1241
1242 fc_snprintf(buffer, sizeof(buffer), "long_comment_%u", secfile->num_long_comments++);
1243
1244 fc_assert_ret_val(secfile_section_by_name(secfile, buffer) == nullptr, nullptr);
1245
1246 /* Create long comment section. */
1247 psection = secfile_section_new(secfile, buffer);
1248 psection->special = EST_COMMENT;
1249
1250 /* Then add string entry "comment" to it. */
1251 secfile_insert_str_full(secfile, comment, nullptr, FALSE, TRUE,
1252 EST_COMMENT, "%s.comment", buffer);
1253
1254 return psection;
1255}
1256
1257/**********************************************************************/
1261 const char *str,
1262 const char *comment,
1263 bool allow_replace,
1264 bool no_escape,
1265 enum entry_special_type stype,
1266 const char *path, ...)
1267{
1269 const char *ent_name;
1270 struct section *psection;
1271 struct entry *pentry = nullptr;
1272 va_list args;
1273
1274 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
1275
1276 va_start(args, path);
1277 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1278 va_end(args);
1279
1281 if (!psection) {
1282 return nullptr;
1283 }
1284
1285 if (psection->special != stype) {
1286 log_error("Tried to insert wrong type of entry to section");
1287 return nullptr;
1288 }
1289
1290 if (allow_replace) {
1292 if (pentry != nullptr) {
1294 if (!entry_str_set(pentry, str)) {
1295 return nullptr;
1296 }
1297 } else {
1299 pentry = nullptr;
1300 }
1301 }
1302 }
1303
1304 if (pentry == nullptr) {
1306 }
1307
1308 if (pentry != nullptr && comment != nullptr) {
1310 }
1311
1312 if (stype == EST_COMMENT) {
1313 pentry->string.raw = TRUE;
1314 }
1315
1316 return pentry;
1317}
1318
1319/**********************************************************************/
1323 const char *str,
1324 const char *path, ...)
1325{
1327 const char *ent_name;
1328 struct section *psection;
1329 struct entry *pentry = nullptr;
1330 va_list args;
1331
1332 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
1333
1334 va_start(args, path);
1335 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1336 va_end(args);
1337
1338 sz_strlcat(fullpath, ".#");
1339
1341 if (!psection) {
1342 return nullptr;
1343 }
1344
1345 if (psection->special != EST_NORMAL) {
1346 log_error("Tried to insert wrong type of entry to section");
1347 return nullptr;
1348 }
1349
1351
1352 return pentry;
1353}
1354
1355/**********************************************************************/
1360 const char *const *strings, size_t dim,
1361 const char *comment, bool allow_replace,
1362 bool no_escape, bool gt_marking,
1363 const char *path, ...)
1364{
1366 size_t i, ret = 0;
1367 va_list args;
1368
1369 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, 0);
1370
1371 va_start(args, path);
1372 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1373 va_end(args);
1374
1375 /* NB: 'path,0' is actually 'path'. See comment in the head
1376 * of the file. */
1377 if (dim > 0) {
1378 struct entry *pentry =
1381 "%s", fullpath);
1382 if (nullptr != pentry) {
1383 ret++;
1384 if (gt_marking) {
1386 }
1387 }
1388 }
1389
1390 for (i = 1; i < dim; i++) {
1391 struct entry *pentry =
1394 "%s,%d", fullpath, (int) i);
1395 if (nullptr != pentry) {
1396 ret++;
1397 if (gt_marking) {
1399 }
1400 }
1401 }
1402
1403 return ret;
1404}
1405
1406/**********************************************************************/
1410 const char *filename,
1411 const char *path, ...)
1412{
1414 const char *ent_name;
1415 struct section *psection;
1416 struct entry *pentry = nullptr;
1417 va_list args;
1418
1419 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
1420
1421 va_start(args, path);
1422 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1423 va_end(args);
1424
1426 if (!psection) {
1427 return nullptr;
1428 }
1429
1430 if (psection->special != EST_NORMAL) {
1431 log_error("Tried to insert normal entry to different kind of section");
1432 return nullptr;
1433 }
1434
1435 if (pentry == nullptr) {
1437 }
1438
1439 return pentry;
1440}
1441
1442/**********************************************************************/
1446 int enumerator,
1448 const char *comment,
1449 bool allow_replace,
1450 const char *path, ...)
1451{
1453 const char *str;
1454 const char *ent_name;
1455 struct section *psection;
1456 struct entry *pentry = nullptr;
1457 va_list args;
1458
1459 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
1460 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, name_fn != nullptr, nullptr);
1461 str = name_fn(enumerator);
1462 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, str != nullptr, nullptr);
1463
1464 va_start(args, path);
1465 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1466 va_end(args);
1467
1469 if (!psection) {
1470 return nullptr;
1471 }
1472
1473 if (allow_replace) {
1475 if (pentry != nullptr) {
1477 if (!entry_str_set(pentry, str)) {
1478 return nullptr;
1479 }
1480 } else {
1482 pentry = nullptr;
1483 }
1484 }
1485 }
1486
1487 if (pentry == nullptr) {
1489 }
1490
1491 if (pentry != nullptr && comment != nullptr) {
1493 }
1494
1495 return pentry;
1496}
1497
1498/**********************************************************************/
1503 const int *enumurators, size_t dim,
1505 const char *comment,
1506 bool allow_replace,
1507 const char *path, ...)
1508{
1510 size_t i, ret = 0;
1511 va_list args;
1512
1513 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, 0);
1514 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, name_fn != nullptr, 0);
1515
1516 va_start(args, path);
1517 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1518 va_end(args);
1519
1520 /* NB: 'path,0' is actually 'path'. See comment in the head
1521 * of the file. */
1522 if (dim > 0
1525 allow_replace, "%s",
1526 fullpath) != nullptr) {
1527 ret++;
1528 }
1529 for (i = 1; i < dim; i++) {
1532 allow_replace, "%s,%d",
1533 fullpath, (int) i) != nullptr) {
1534 ret++;
1535 }
1536 }
1537
1538 return ret;
1539}
1540
1541/**********************************************************************/
1545 int bitwise_val,
1547 name_fn,
1549 begin_fn,
1551 end_fn,
1553 next_fn,
1554 const char *comment,
1555 bool allow_replace,
1556 const char *path, ...)
1557{
1559 const char *ent_name;
1560 struct section *psection;
1561 struct entry *pentry = nullptr;
1562 va_list args;
1563 int i;
1564
1565 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
1566 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, name_fn != nullptr, nullptr);
1567 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, begin_fn != nullptr, nullptr);
1568 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, end_fn != nullptr, nullptr);
1569 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, next_fn != nullptr, nullptr);
1570
1571 /* Compute a string containing all the values separated by '|'. */
1572 str[0] = '\0'; /* Insert at least an empty string. */
1573 if (0 != bitwise_val) {
1574 for (i = begin_fn(); i != end_fn(); i = next_fn(i)) {
1575 if (i & bitwise_val) {
1576 if ('\0' == str[0]) {
1578 } else {
1579 cat_snprintf(str, sizeof(str), "|%s", name_fn(i));
1580 }
1581 }
1582 }
1583 }
1584
1585 va_start(args, path);
1586 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1587 va_end(args);
1588
1590 if (!psection) {
1591 return nullptr;
1592 }
1593
1594 if (allow_replace) {
1596 if (pentry != nullptr) {
1598 if (!entry_str_set(pentry, str)) {
1599 return nullptr;
1600 }
1601 } else {
1603 pentry = nullptr;
1604 }
1605 }
1606 }
1607
1608 if (pentry == nullptr) {
1610 }
1611
1612 if (pentry != nullptr && comment != nullptr) {
1614 }
1615
1616 return pentry;
1617}
1618
1619/**********************************************************************/
1624 const int *bitwise_vals,
1625 size_t dim,
1630 const char *comment,
1631 bool allow_replace,
1632 const char *path, ...)
1633{
1635 size_t i, ret = 0;
1636 va_list args;
1637
1638 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, 0);
1639 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, name_fn != nullptr, 0);
1640 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, begin_fn != nullptr, 0);
1641 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, end_fn != nullptr, 0);
1642 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, next_fn != nullptr, 0);
1643
1644 va_start(args, path);
1645 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1646 va_end(args);
1647
1648 /* NB: 'path,0' is actually 'path'. See comment in the head
1649 * of the file. */
1650 if (dim > 0
1654 allow_replace, "%s",
1655 fullpath) != nullptr) {
1656 ret++;
1657 }
1658 for (i = 1; i < dim; i++) {
1662 allow_replace, "%s,%d",
1663 fullpath, (int) i) != nullptr) {
1664 ret++;
1665 }
1666 }
1667
1668 return ret;
1669}
1670
1671/**********************************************************************/
1676 int value, bool bitwise,
1678 secfile_data_t data,
1679 const char *comment,
1680 bool allow_replace,
1681 const char *path, ...)
1682{
1684 const char *ent_name, *val_name;
1685 struct section *psection;
1686 struct entry *pentry = nullptr;
1687 va_list args;
1688 int i;
1689
1690 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
1691 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, name_fn != nullptr, nullptr);
1692
1693 if (bitwise) {
1694 /* Compute a string containing all the values separated by '|'. */
1695 str[0] = '\0'; /* Insert at least an empty string. */
1696 if (0 != value) {
1697 for (i = 0; (val_name = name_fn(data, i)); i++) {
1698 if ((1 << i) & value) {
1699 if ('\0' == str[0]) {
1701 } else {
1702 cat_snprintf(str, sizeof(str), "|%s", val_name);
1703 }
1704 }
1705 }
1706 }
1707 } else {
1708 if (!(val_name = name_fn(data, value))) {
1709 SECFILE_LOG(secfile, nullptr, "Value %d not supported.", value);
1710
1711 return nullptr;
1712 }
1714 }
1715
1716 va_start(args, path);
1717 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1718 va_end(args);
1719
1721 if (!psection) {
1722 return nullptr;
1723 }
1724
1725 if (allow_replace) {
1727 if (pentry != nullptr) {
1729 if (!entry_str_set(pentry, str)) {
1730 return nullptr;
1731 }
1732 } else {
1734 pentry = nullptr;
1735 }
1736 }
1737 }
1738
1739 if (pentry == nullptr) {
1741 }
1742
1743 if (pentry != nullptr && comment != nullptr) {
1745 }
1746
1747 return pentry;
1748}
1749
1750/**********************************************************************/
1755 const int *values, size_t dim,
1756 bool bitwise,
1758 secfile_data_t data,
1759 const char *comment,
1760 bool allow_replace,
1761 const char *path, ...)
1762{
1764 size_t i, ret = 0;
1765 va_list args;
1766
1767 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, 0);
1768 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, name_fn != nullptr, 0);
1769
1770 va_start(args, path);
1771 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1772 va_end(args);
1773
1774 /* NB: 'path,0' is actually 'path'. See comment in the head
1775 * of the file. */
1776 if (dim > 0
1777 && secfile_insert_enum_data_full(secfile, values[0], bitwise,
1778 name_fn, data, comment,
1779 allow_replace, "%s",
1780 fullpath) != nullptr) {
1781 ret++;
1782 }
1783 for (i = 1; i < dim; i++) {
1784 if (secfile_insert_enum_data_full(secfile, values[i], bitwise,
1785 name_fn, data, comment,
1786 allow_replace, "%s,%d",
1787 fullpath, (int) i) != nullptr) {
1788 ret++;
1789 }
1790 }
1791
1792 return ret;
1793}
1794
1795/**********************************************************************/
1798struct entry *secfile_entry_by_path(const struct section_file *secfile,
1799 const char *path)
1800{
1802 char *ent_name;
1803 size_t len;
1804 struct section *psection;
1805
1806 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
1807
1808 sz_strlcpy(fullpath, path);
1809
1810 /* Treat "sec.foo,0" as "sec.foo": */
1811 len = strlen(fullpath);
1812 if (len > 2 && fullpath[len - 2] == ',' && fullpath[len - 1] == '0') {
1813 fullpath[len - 2] = '\0';
1814 }
1815
1816 if (secfile->hash.entries != nullptr) {
1817 struct entry *pentry;
1818
1819 if (entry_hash_lookup(secfile->hash.entries, fullpath, &pentry)) {
1821 }
1822 return pentry;
1823 }
1824
1825 /* I dont like strtok().
1826 * - Me neither! */
1827 ent_name = strchr(fullpath, '.');
1828 if (!ent_name) {
1829 return nullptr;
1830 }
1831
1832 /* Separates section and entry names. */
1833 *ent_name++ = '\0';
1835 if (psection) {
1837 } else {
1838 return nullptr;
1839 }
1840}
1841
1842/**********************************************************************/
1846 const char *path, ...)
1847{
1849 va_list args;
1850 struct entry *pentry;
1851
1852 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, FALSE);
1853
1854 va_start(args, path);
1855 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1856 va_end(args);
1857
1858 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
1859 SECFILE_LOG(secfile, nullptr, "Path %s does not exists.", fullpath);
1860 return FALSE;
1861 }
1862
1864
1865 return TRUE;
1866}
1867
1868/**********************************************************************/
1871struct entry *secfile_entry_lookup(const struct section_file *secfile,
1872 const char *path, ...)
1873{
1875 va_list args;
1876
1877 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
1878
1879 va_start(args, path);
1880 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1881 va_end(args);
1882
1883 return secfile_entry_by_path(secfile, fullpath);
1884}
1885
1886/**********************************************************************/
1889bool secfile_lookup_bool(const struct section_file *secfile, bool *bval,
1890 const char *path, ...)
1891{
1893 const struct entry *pentry;
1894 va_list args;
1895
1896 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, FALSE);
1897
1898 va_start(args, path);
1899 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1900 va_end(args);
1901
1902 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
1903 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
1904
1905 return FALSE;
1906 }
1907
1908 return entry_bool_get(pentry, bval);
1909}
1910
1911/**********************************************************************/
1916 bool def, const char *path, ...)
1917{
1919 const struct entry *pentry;
1920 bool bval;
1921 va_list args;
1922
1923 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, def);
1924
1925 va_start(args, path);
1926 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1927 va_end(args);
1928
1929 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
1930 return def;
1931 }
1932
1933 if (entry_bool_get(pentry, &bval)) {
1934 return bval;
1935 }
1936
1937 return def;
1938}
1939
1940/**********************************************************************/
1945bool *secfile_lookup_bool_vec(const struct section_file *secfile,
1946 size_t *dim, const char *path, ...)
1947{
1949 size_t i = 0;
1950 bool *vec;
1951 va_list args;
1952
1953 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
1954 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, dim != nullptr, nullptr);
1955
1956 va_start(args, path);
1957 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1958 va_end(args);
1959
1960 /* Check size. */
1961 while (secfile_entry_lookup(secfile, "%s,%d", fullpath, (int) i) != nullptr) {
1962 i++;
1963 }
1964 *dim = i;
1965
1966 if (0 == i) {
1967 /* Doesn't exist. */
1968 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
1969 return nullptr;
1970 }
1971
1972 vec = fc_malloc(i * sizeof(bool));
1973 for (i = 0; i < *dim; i++) {
1974 if (!secfile_lookup_bool(secfile, vec + i, "%s,%d", fullpath, (int) i)) {
1975 SECFILE_LOG(secfile, nullptr,
1976 "An error occurred when looking up to \"%s,%d\" entry.",
1977 fullpath, (int) i);
1978 free(vec);
1979 *dim = 0;
1980
1981 return nullptr;
1982 }
1983 }
1984
1985 return vec;
1986}
1987
1988/**********************************************************************/
1991bool secfile_lookup_int(const struct section_file *secfile, int *ival,
1992 const char *path, ...)
1993{
1995 const struct entry *pentry;
1996 va_list args;
1997
1998 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, FALSE);
1999
2000 va_start(args, path);
2001 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2002 va_end(args);
2003
2004 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2005 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2006
2007 return FALSE;
2008 }
2009
2010 return entry_int_get(pentry, ival);
2011}
2012
2013/**********************************************************************/
2017int secfile_lookup_int_default(const struct section_file *secfile, int def,
2018 const char *path, ...)
2019{
2021 const struct entry *pentry;
2022 int ival;
2023 va_list args;
2024
2025 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, def);
2026
2027 va_start(args, path);
2028 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2029 va_end(args);
2030
2031 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2032 return def;
2033 }
2034
2035 if (entry_int_get(pentry, &ival)) {
2036 return ival;
2037 }
2038
2039 return def;
2040}
2041
2042/**********************************************************************/
2048 int defval, int minval, int maxval,
2049 const char *path, ...)
2050{
2052 const struct entry *pentry;
2053 int value;
2054 va_list args;
2055
2056 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, defval);
2057
2058 va_start(args, path);
2059 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2060 va_end(args);
2061
2062 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2063 return defval;
2064 }
2065
2066 if (!entry_int_get(pentry, &value)) {
2067 return defval;
2068 }
2069
2070 if (value < minval) {
2072 "\"%s\" should be in the interval [%d, %d] but is %d;"
2073 "using the minimal value.",
2075 value = minval;
2076 }
2077
2078 if (value > maxval) {
2080 "\"%s\" should be in the interval [%d, %d] but is %d;"
2081 "using the maximal value.",
2083 value = maxval;
2084 }
2085
2086 return value;
2087}
2088
2089/**********************************************************************/
2094int *secfile_lookup_int_vec(const struct section_file *secfile,
2095 size_t *dim, const char *path, ...)
2096{
2098 size_t i = 0;
2099 int *vec;
2100 va_list args;
2101
2102 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
2103 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, dim != nullptr, nullptr);
2104
2105 va_start(args, path);
2106 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2107 va_end(args);
2108
2109 /* Check size. */
2110 while (secfile_entry_lookup(secfile, "%s,%d", fullpath, (int) i) != nullptr) {
2111 i++;
2112 }
2113 *dim = i;
2114
2115 if (0 == i) {
2116 /* Doesn't exist. */
2117 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2118
2119 return nullptr;
2120 }
2121
2122 vec = fc_malloc(i * sizeof(int));
2123 for (i = 0; i < *dim; i++) {
2124 if (!secfile_lookup_int(secfile, vec + i, "%s,%d", fullpath, (int) i)) {
2125 SECFILE_LOG(secfile, nullptr,
2126 "An error occurred when looking up to \"%s,%d\" entry.",
2127 fullpath, (int) i);
2128 free(vec);
2129 *dim = 0;
2130 return nullptr;
2131 }
2132 }
2133
2134 return vec;
2135}
2136
2137/**********************************************************************/
2140bool secfile_lookup_float(const struct section_file *secfile, float *fval,
2141 const char *path, ...)
2142{
2144 const struct entry *pentry;
2145 va_list args;
2146
2147 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, FALSE);
2148
2149 va_start(args, path);
2150 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2151 va_end(args);
2152
2153 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2154 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2155 return FALSE;
2156 }
2157
2158 return entry_float_get(pentry, fval);
2159}
2160
2161/**********************************************************************/
2165float secfile_lookup_float_default(const struct section_file *secfile,
2166 float def, const char *path, ...)
2167{
2169 const struct entry *pentry;
2170 float fval;
2171 va_list args;
2172
2173 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, def);
2174
2175 va_start(args, path);
2176 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2177 va_end(args);
2178
2179 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2180 return def;
2181 }
2182
2183 if (entry_float_get(pentry, &fval)) {
2184 return fval;
2185 }
2186
2187 return def;
2188}
2189
2190/**********************************************************************/
2193const char *secfile_lookup_str(const struct section_file *secfile,
2194 const char *path, ...)
2195{
2197 const struct entry *pentry;
2198 const char *str;
2199 va_list args;
2200
2201 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
2202
2203 va_start(args, path);
2204 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2205 va_end(args);
2206
2207 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2208 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2209 return nullptr;
2210 }
2211
2212 if (entry_str_get(pentry, &str)) {
2213 return str;
2214 }
2215
2216 return nullptr;
2217}
2218
2219/**********************************************************************/
2223const char *secfile_lookup_str_default(const struct section_file *secfile,
2224 const char *def,
2225 const char *path, ...)
2226{
2228 const struct entry *pentry;
2229 const char *str;
2230 va_list args;
2231
2232 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, def);
2233
2234 va_start(args, path);
2235 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2236 va_end(args);
2237
2238 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2239 return def;
2240 }
2241
2242 if (entry_str_get(pentry, &str)) {
2243 return str;
2244 }
2245
2246 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't have a string.", fullpath);
2247
2248 return def;
2249}
2250
2251/**********************************************************************/
2257const char **secfile_lookup_str_vec(const struct section_file *secfile,
2258 size_t *dim, const char *path, ...)
2259{
2261 size_t i = 0;
2262 const char **vec;
2263 va_list args;
2264
2265 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
2266 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, dim != nullptr, nullptr);
2267
2268 va_start(args, path);
2269 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2270 va_end(args);
2271
2272 /* Check size. */
2273 while (secfile_entry_lookup(secfile, "%s,%d", fullpath, (int) i) != nullptr) {
2274 i++;
2275 }
2276 *dim = i;
2277
2278 if (0 == i) {
2279 /* Doesn't exist. */
2280 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2281 return nullptr;
2282 }
2283
2284 vec = fc_malloc(i * sizeof(const char *));
2285 for (i = 0; i < *dim; i++) {
2286 if (!(vec[i] = secfile_lookup_str(secfile, "%s,%d",
2287 fullpath, (int) i))) {
2288 SECFILE_LOG(secfile, nullptr,
2289 "An error occurred when looking up to \"%s,%d\" entry.",
2290 fullpath, (int) i);
2291 free(vec);
2292 *dim = 0;
2293 return nullptr;
2294 }
2295 }
2296
2297 return vec;
2298}
2299
2300/**********************************************************************/
2304 int *penumerator,
2307 const char *path, ...)
2308{
2310 const struct entry *pentry;
2311 const char *str;
2312 va_list args;
2313
2314 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, FALSE);
2315 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, penumerator != nullptr, FALSE);
2316 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, is_valid_fn != nullptr, FALSE);
2317 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, by_name_fn != nullptr, FALSE);
2318
2319 va_start(args, path);
2320 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2321 va_end(args);
2322
2323 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2324 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2325 return FALSE;
2326 }
2327
2328 if (!entry_str_get(pentry, &str)) {
2329 return FALSE;
2330 }
2331
2333 if (is_valid_fn(*penumerator)) {
2334 return TRUE;
2335 }
2336
2338 "Entry \"%s\": no match for \"%s\".",
2340 return FALSE;
2341}
2342
2343/**********************************************************************/
2347 *secfile, int defval,
2351 by_name_fn,
2352 const char *path, ...)
2353{
2355 const struct entry *pentry;
2356 const char *str;
2357 int val;
2358 va_list args;
2359
2360 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, defval);
2361 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, is_valid_fn != nullptr, defval);
2362 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, by_name_fn != nullptr, defval);
2363
2364 va_start(args, path);
2365 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2366 va_end(args);
2367
2368 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2369 return defval;
2370 }
2371
2372 if (!entry_str_get(pentry, &str)) {
2373 return defval;
2374 }
2375
2376 val = by_name_fn(str, strcmp);
2377 if (is_valid_fn(val)) {
2378 return val;
2379 } else {
2380 return defval;
2381 }
2382}
2383
2384/**********************************************************************/
2390 size_t *dim,
2394 by_name_fn,
2395 const char *path, ...)
2396{
2398 size_t i = 0;
2399 int *vec;
2400 va_list args;
2401
2402 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
2403 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, dim != nullptr, nullptr);
2404 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, is_valid_fn != nullptr, nullptr);
2405 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, by_name_fn != nullptr, nullptr);
2406
2407 va_start(args, path);
2408 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2409 va_end(args);
2410
2411 /* Check size. */
2412 while (secfile_entry_lookup(secfile, "%s,%d", fullpath, (int) i) != nullptr) {
2413 i++;
2414 }
2415 *dim = i;
2416
2417 if (0 == i) {
2418 /* Doesn't exist. */
2419 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2420 return nullptr;
2421 }
2422
2423 vec = fc_malloc(i * sizeof(int));
2424 for (i = 0; i < *dim; i++) {
2425 if (!secfile_lookup_plain_enum_full(secfile, vec + i, is_valid_fn,
2426 by_name_fn, "%s,%d",
2427 fullpath, (int) i)) {
2428 SECFILE_LOG(secfile, nullptr,
2429 "An error occurred when looking up to \"%s,%d\" entry.",
2430 fullpath, (int) i);
2431 free(vec);
2432 *dim = 0;
2433 return nullptr;
2434 }
2435 }
2436
2437 return vec;
2438}
2439
2440/**********************************************************************/
2444 int *penumerator,
2447 const char *path, ...)
2448{
2450 const struct entry *pentry;
2451 const char *str, *p;
2453 int val;
2454 va_list args;
2455
2456 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, FALSE);
2457 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, penumerator != nullptr, FALSE);
2458 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, is_valid_fn != nullptr, FALSE);
2459 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, by_name_fn != nullptr, FALSE);
2460
2461 va_start(args, path);
2462 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2463 va_end(args);
2464
2465 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2466 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2467 return FALSE;
2468 }
2469
2470 if (!entry_str_get(pentry, &str)) {
2471 return FALSE;
2472 }
2473
2474 *penumerator = 0;
2475 if ('\0' == str[0]) {
2476 /* Empty string = no value. */
2477 return TRUE;
2478 }
2479
2480 /* Value names are separated by '|'. */
2481 do {
2482 p = strchr(str, '|');
2483 if (p != nullptr) {
2484 p++;
2485 fc_strlcpy(val_name, str, p - str);
2486 } else {
2487 /* Last segment, full copy. */
2489 }
2491 val = by_name_fn(val_name, strcmp);
2492 if (!is_valid_fn(val)) {
2494 "Entry \"%s\": no match for \"%s\".",
2496 return FALSE;
2497 }
2498 *penumerator |= val;
2499 str = p;
2500 } while (p != nullptr);
2501
2502 return TRUE;
2503}
2504
2505/**********************************************************************/
2509 *secfile, int defval,
2513 by_name_fn,
2514 const char *path, ...)
2515{
2517 const struct entry *pentry;
2518 const char *str, *p;
2520 int val, full_val;
2521 va_list args;
2522
2523 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, defval);
2524 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, is_valid_fn != nullptr, defval);
2525 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, by_name_fn != nullptr, defval);
2526
2527 va_start(args, path);
2528 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2529 va_end(args);
2530
2531 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2532 return defval;
2533 }
2534
2535 if (!entry_str_get(pentry, &str)) {
2536 return defval;
2537 }
2538
2539 if ('\0' == str[0]) {
2540 /* Empty string = no value. */
2541 return 0;
2542 }
2543
2544 /* Value names are separated by '|'. */
2545 full_val = 0;
2546 do {
2547 p = strchr(str, '|');
2548 if (p != nullptr) {
2549 p++;
2550 fc_strlcpy(val_name, str, p - str);
2551 } else {
2552 /* Last segment, full copy. */
2554 }
2556 val = by_name_fn(val_name, strcmp);
2557 if (!is_valid_fn(val)) {
2558 return defval;
2559 }
2560 full_val |= val;
2561 str = p;
2562 } while (p != nullptr);
2563
2564 return full_val;
2565}
2566
2567/**********************************************************************/
2573 size_t *dim,
2577 by_name_fn,
2578 const char *path, ...)
2579{
2581 size_t i = 0;
2582 int *vec;
2583 va_list args;
2584
2585 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
2586 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, dim != nullptr, nullptr);
2587 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, is_valid_fn != nullptr, nullptr);
2588 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, by_name_fn != nullptr, nullptr);
2589
2590 va_start(args, path);
2591 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2592 va_end(args);
2593
2594 /* Check size. */
2595 while (secfile_entry_lookup(secfile, "%s,%d", fullpath, (int) i) != nullptr) {
2596 i++;
2597 }
2598 *dim = i;
2599
2600 if (0 == i) {
2601 /* Doesn't exist. */
2602 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2603 return nullptr;
2604 }
2605
2606 vec = fc_malloc(i * sizeof(int));
2607 for (i = 0; i < *dim; i++) {
2608 if (!secfile_lookup_bitwise_enum_full(secfile, vec + i, is_valid_fn,
2609 by_name_fn, "%s,%d",
2610 fullpath, (int) i)) {
2611 SECFILE_LOG(secfile, nullptr,
2612 "An error occurred when looking up to \"%s,%d\" entry.",
2613 fullpath, (int) i);
2614 free(vec);
2615 *dim = 0;
2616
2617 return nullptr;
2618 }
2619 }
2620
2621 return vec;
2622}
2623
2624/**********************************************************************/
2627bool secfile_lookup_enum_data(const struct section_file *secfile,
2628 int *pvalue, bool bitwise,
2630 secfile_data_t data, const char *path, ...)
2631{
2633 const struct entry *pentry;
2634 const char *str, *p, *name;
2636 int val;
2637 va_list args;
2638
2639 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, FALSE);
2640 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, pvalue != nullptr, FALSE);
2641 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, name_fn != nullptr, FALSE);
2642
2643 va_start(args, path);
2644 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2645 va_end(args);
2646
2647 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2648 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2649 return FALSE;
2650 }
2651
2652 if (!entry_str_get(pentry, &str)) {
2653 return FALSE;
2654 }
2655
2656 if (bitwise) {
2657 *pvalue = 0;
2658 if ('\0' == str[0]) {
2659 /* Empty string = no value. */
2660 return TRUE;
2661 }
2662
2663 /* Value names are separated by '|'. */
2664 do {
2665 p = strchr(str, '|');
2666 if (p != nullptr) {
2667 p++;
2668 fc_strlcpy(val_name, str, p - str);
2669 } else {
2670 /* Last segment, full copy. */
2672 }
2674 for (val = 0; (name = name_fn(data, val)); val++) {
2675 if (0 == fc_strcasecmp(name, val_name)) {
2676 break;
2677 }
2678 }
2679 if (name == nullptr) {
2681 "Entry \"%s\": no match for \"%s\".",
2683 return FALSE;
2684 }
2685 *pvalue |= 1 << val;
2686 str = p;
2687 } while (p != nullptr);
2688 } else {
2689 for (val = 0; (name = name_fn(data, val)); val++) {
2690 if (0 == fc_strcasecmp(name, str)) {
2691 *pvalue = val;
2692 break;
2693 }
2694 }
2695 if (name == nullptr) {
2697 "Entry \"%s\": no match for \"%s\".",
2699 return FALSE;
2700 }
2701 }
2702
2703 return TRUE;
2704}
2705
2706/**********************************************************************/
2710 int defval, bool bitwise,
2712 secfile_data_t data,
2713 const char *path, ...)
2714{
2716 const struct entry *pentry;
2717 const char *str, *p, *name;
2719 int value, val;
2720 va_list args;
2721
2722 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, defval);
2723 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, name_fn != nullptr, defval);
2724
2725 va_start(args, path);
2726 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2727 va_end(args);
2728
2729 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2730 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2731 return defval;
2732 }
2733
2734 if (!entry_str_get(pentry, &str)) {
2735 return defval;
2736 }
2737
2738 value = 0;
2739 if (bitwise) {
2740 if ('\0' == str[0]) {
2741 /* Empty string = no value. */
2742 return value;
2743 }
2744
2745 /* Value names are separated by '|'. */
2746 do {
2747 p = strchr(str, '|');
2748 if (p != nullptr) {
2749 p++;
2750 fc_strlcpy(val_name, str, p - str);
2751 } else {
2752 /* Last segment, full copy. */
2754 }
2756 for (val = 0; (name = name_fn(data, val)); val++) {
2757 if (0 == strcmp(name, val_name)) {
2758 break;
2759 }
2760 }
2761 if (name == nullptr) {
2763 "Entry \"%s\": no match for \"%s\".",
2765 return defval;
2766 }
2767 value |= 1 << val;
2768 str = p;
2769 } while (p != nullptr);
2770 } else {
2771 for (val = 0; (name = name_fn(data, val)); val++) {
2772 if (0 == strcmp(name, str)) {
2773 value = val;
2774 break;
2775 }
2776 }
2777 if (name == nullptr) {
2779 "Entry \"%s\": no match for \"%s\".",
2781 return defval;
2782 }
2783 }
2784
2785 return value;
2786}
2787
2788/**********************************************************************/
2793 size_t *dim, bool bitwise,
2795 secfile_data_t data, const char *path, ...)
2796{
2798 size_t i = 0;
2799 int *vec;
2800 va_list args;
2801
2802 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
2803 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, dim != nullptr, nullptr);
2804 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, name_fn != nullptr, nullptr);
2805
2806 va_start(args, path);
2807 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2808 va_end(args);
2809
2810 /* Check size. */
2811 while (secfile_entry_lookup(secfile, "%s,%d", fullpath, (int) i) != nullptr) {
2812 i++;
2813 }
2814 *dim = i;
2815
2816 if (0 == i) {
2817 /* Doesn't exist. */
2818 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2819 return nullptr;
2820 }
2821
2822 vec = fc_malloc(i * sizeof(int));
2823 for (i = 0; i < *dim; i++) {
2824 if (!secfile_lookup_enum_data(secfile, vec + i, bitwise, name_fn, data,
2825 "%s,%d", fullpath, (int) i)) {
2826 SECFILE_LOG(secfile, nullptr,
2827 "An error occurred when looking up to \"%s,%d\" entry.",
2828 fullpath, (int) i);
2829 free(vec);
2830 *dim = 0;
2831
2832 return nullptr;
2833 }
2834 }
2835
2836 return vec;
2837}
2838
2839/**********************************************************************/
2843 const char *name)
2844{
2845 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
2846
2848 if (0 == strcmp(section_name(psection), name)) {
2849 return psection;
2850 }
2852
2853 return nullptr;
2854}
2855
2856/**********************************************************************/
2860 const char *path, ...)
2861{
2863 va_list args;
2864
2865 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
2866
2867 va_start(args, path);
2868 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2869 va_end(args);
2870
2872}
2873
2874/**********************************************************************/
2878const struct section_list *
2879secfile_sections(const struct section_file *secfile)
2880{
2881 return (secfile != nullptr ? secfile->sections : nullptr);
2882}
2883
2884/**********************************************************************/
2889struct section_list *
2891 const char *prefix)
2892{
2893 struct section_list *matches = nullptr;
2894 size_t len;
2895
2896 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
2897 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, prefix != nullptr, nullptr);
2898
2899 len = strlen(prefix);
2900 if (0 == len) {
2901 return nullptr;
2902 }
2903
2904 section_list_iterate(secfile->sections, psection) {
2905 if (!fc_strncmp(section_name(psection), prefix, len)) {
2906 if (matches == nullptr) {
2908 }
2909 section_list_append(matches, psection);
2910 }
2912
2913 return matches;
2914}
2915
2916/**********************************************************************/
2920 const char *prefix)
2921{
2922 size_t len;
2923
2924 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, FALSE);
2925 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, prefix != nullptr, FALSE);
2926
2927 len = strlen(prefix);
2928 if (0 == len) {
2929 return FALSE;
2930 }
2931
2932 section_list_iterate(secfile->sections, psection) {
2933 if (!fc_strncmp(section_name(psection), prefix, len)) {
2934 return TRUE;
2935 }
2937
2938 return FALSE;
2939}
2940
2941/**********************************************************************/
2945 const char *name)
2946{
2947 struct section *psection;
2948
2949 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
2950
2951 if (name == nullptr || '\0' == name[0]) {
2952 SECFILE_LOG(secfile, nullptr, "Cannot create a section without name.");
2953 return nullptr;
2954 }
2955
2957 SECFILE_LOG(secfile, nullptr, "\"%s\" is not a valid section name.",
2958 name);
2959 return nullptr;
2960 }
2961
2962 if (secfile_section_by_name(secfile, name) != nullptr) {
2963 /* We cannot duplicate sections in any case! Not even if one is
2964 * include -section and the other not. */
2965 SECFILE_LOG(secfile, nullptr, "Section \"%s\" already exists.", name);
2966 return nullptr;
2967 }
2968
2969 psection = fc_malloc(sizeof(struct section));
2970 psection->special = EST_NORMAL;
2971 psection->name = fc_strdup(name);
2973
2974 /* Append to secfile. */
2975 psection->secfile = secfile;
2977
2978 if (secfile->hash.sections != nullptr) {
2979 section_hash_insert(secfile->hash.sections, psection->name, psection);
2980 }
2981
2982 return psection;
2983}
2984
2985/**********************************************************************/
2988void section_destroy(struct section *psection)
2989{
2990 struct section_file *secfile;
2991
2992 SECFILE_RETURN_IF_FAIL(nullptr, psection, psection != nullptr);
2993
2994 section_clear_all(psection);
2995
2996 if ((secfile = psection->secfile)) {
2997 /* Detach from secfile. */
2998 if (section_list_remove(secfile->sections, psection)) {
2999 /* This has called section_destroy() already then. */
3000 return;
3001 }
3002 if (secfile->hash.sections != nullptr) {
3003 section_hash_remove(secfile->hash.sections, psection->name);
3004 }
3005 }
3006
3007 entry_list_destroy(psection->entries);
3008 free(psection->name);
3009 free(psection);
3010}
3011
3012/**********************************************************************/
3015void section_clear_all(struct section *psection)
3016{
3017 SECFILE_RETURN_IF_FAIL(nullptr, psection, psection != nullptr);
3018
3019 /* This include the removing of the hash data. */
3020 entry_list_clear(psection->entries);
3021
3022 if (0 < entry_list_size(psection->entries)) {
3023 SECFILE_LOG(psection->secfile, psection,
3024 "After clearing all, %d entries are still remaining.",
3025 entry_list_size(psection->entries));
3026 }
3027}
3028
3029/**********************************************************************/
3032bool section_set_name(struct section *psection, const char *name)
3033{
3034 struct section_file *secfile;
3035 struct section *pother;
3036
3037 SECFILE_RETURN_VAL_IF_FAIL(nullptr, psection, psection != nullptr, FALSE);
3038 secfile = psection->secfile;
3039 SECFILE_RETURN_VAL_IF_FAIL(secfile, psection, secfile != nullptr, FALSE);
3040
3041 if (name == nullptr || '\0' == name[0]) {
3042 SECFILE_LOG(secfile, psection, "No new name for section \"%s\".",
3043 psection->name);
3044 return FALSE;
3045 }
3046
3048 SECFILE_LOG(secfile, psection,
3049 "\"%s\" is not a valid section name for section \"%s\".",
3050 name, psection->name);
3051 return FALSE;
3052 }
3053
3055 && pother != psection) {
3056 /* We cannot duplicate sections in any case! */
3057 SECFILE_LOG(secfile, psection, "Section \"%s\" already exists.", name);
3058 return FALSE;
3059 }
3060
3061 /* Remove old references in the hash tables. */
3062 if (secfile->hash.sections != nullptr) {
3064 }
3065 if (secfile->hash.entries != nullptr) {
3066 entry_list_iterate(psection->entries, pentry) {
3069 }
3070
3071 /* Really rename. */
3072 free(psection->name);
3073 psection->name = fc_strdup(name);
3074
3075 /* Reinsert new references into the hash tables. */
3076 if (secfile->hash.sections != nullptr) {
3077 section_hash_insert(secfile->hash.sections, psection->name, psection);
3078 }
3079 if (secfile->hash.entries != nullptr) {
3080 entry_list_iterate(psection->entries, pentry) {
3083 }
3084
3085 return TRUE;
3086}
3087
3088/**********************************************************************/
3092const struct entry_list *section_entries(const struct section *psection)
3093{
3094 return (psection != nullptr ? psection->entries : nullptr);
3095}
3096
3097/**********************************************************************/
3101 const char *name)
3102{
3103 SECFILE_RETURN_VAL_IF_FAIL(nullptr, psection, psection != nullptr, nullptr);
3104
3106 if (0 == strcmp(entry_name(pentry), name)) {
3108 return pentry;
3109 }
3111
3112 return nullptr;
3113}
3114
3115/**********************************************************************/
3119 const char *path, ...)
3120{
3122 struct entry *pentry;
3123 va_list args;
3124
3125 SECFILE_RETURN_VAL_IF_FAIL(nullptr, psection, psection != nullptr, nullptr);
3126
3127 va_start(args, path);
3128 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
3129 va_end(args);
3130
3132 return pentry;
3133 }
3134
3135 /* Try with full path. */
3137 && psection == entry_section(pentry)) {
3138 /* Ensured this is really owned by this section. */
3139 return pentry;
3140 }
3141
3142 return nullptr;
3143}
3144
3145/**********************************************************************/
3148static struct entry *entry_new(struct section *psection, const char *name)
3149{
3150 struct section_file *secfile;
3151 struct entry *pentry;
3152 bool long_comment = !strcmp("#", name);
3153
3154 SECFILE_RETURN_VAL_IF_FAIL(nullptr, psection, psection != nullptr, nullptr);
3155
3156 secfile = psection->secfile;
3157 if (name == nullptr || '\0' == name[0]) {
3158 SECFILE_LOG(secfile, psection, "Cannot create an entry without name.");
3159 return nullptr;
3160 }
3161
3163 SECFILE_LOG(secfile, psection, "\"%s\" is not a valid entry name.",
3164 name);
3165 return nullptr;
3166 }
3167
3168 if (!secfile->allow_duplicates
3169 && section_entry_by_name(psection, name) != nullptr) {
3170 SECFILE_LOG(secfile, psection, "Entry \"%s\" already exists.", name);
3171 return nullptr;
3172 }
3173
3174 pentry = fc_malloc(sizeof(struct entry));
3175 if (long_comment) {
3176 pentry->name = nullptr;
3177 } else {
3178 pentry->name = fc_strdup(name);
3179 }
3180 pentry->type = ENTRY_ILLEGAL;
3181 pentry->used = 0;
3182 pentry->comment = nullptr;
3183
3184 /* Append to section. */
3185 pentry->psection = psection;
3187
3188 /* Notify secfile. */
3189 secfile->num_entries++;
3190 secfile_hash_insert(secfile, pentry);
3191
3192 return pentry;
3193}
3194
3195/**********************************************************************/
3199 const char *name, int value)
3200{
3201 struct entry *pentry = entry_new(psection, name);
3202
3203 if (pentry != nullptr) {
3204 pentry->type = ENTRY_INT;
3205 pentry->integer.value = value;
3206 }
3207
3208 return pentry;
3209}
3210
3211/**********************************************************************/
3215 const char *name, bool value)
3216{
3217 struct entry *pentry = entry_new(psection, name);
3218
3219 if (pentry != nullptr) {
3220 pentry->type = ENTRY_BOOL;
3221 pentry->boolean.value = value;
3222 }
3223
3224 return pentry;
3225}
3226
3227/**********************************************************************/
3231 const char *name, float value)
3232{
3233 struct entry *pentry = entry_new(psection, name);
3234
3235 if (pentry != nullptr) {
3236 pentry->type = ENTRY_FLOAT;
3237 pentry->floating.value = value;
3238 }
3239
3240 return pentry;
3241}
3242
3243/**********************************************************************/
3247 const char *name, const char *value,
3248 bool escaped)
3249{
3250 struct entry *pentry = entry_new(psection, name);
3251
3252 if (pentry != nullptr) {
3253 pentry->type = ENTRY_STR;
3254 pentry->string.value = fc_strdup(value != nullptr ? value : "");
3255 pentry->string.escaped = escaped;
3256 pentry->string.raw = FALSE;
3257 pentry->string.gt_marking = FALSE;
3258 }
3259
3260 return pentry;
3261}
3262
3263/**********************************************************************/
3267 const char *name,
3268 const char *value)
3269{
3270 struct entry *pentry = entry_new(psection, name);
3271
3272 if (pentry != nullptr) {
3274 pentry->string.value = fc_strdup(value != nullptr ? value : "");
3275 }
3276
3277 return pentry;
3278}
3279
3280/**********************************************************************/
3284 const char *comment)
3285{
3286 struct entry *pentry = entry_new(psection, "#");
3287
3288 if (pentry != nullptr) {
3289 pentry->type = ENTRY_LONG_COMMENT;
3290 pentry->comment = fc_strdup(comment != nullptr ? comment : "");
3291 }
3292
3293 return pentry;
3294}
3295
3296/**********************************************************************/
3300{
3301 struct section_file *secfile;
3302 struct section *psection;
3303
3304 if (pentry == nullptr) {
3305 return;
3306 }
3307
3308 if ((psection = pentry->psection)) {
3309 /* Detach from section. */
3310 if (entry_list_remove(psection->entries, pentry)) {
3311 /* This has called entry_destroy() already then. */
3312 return;
3313 }
3314 if ((secfile = psection->secfile)) {
3315 /* Detach from secfile. */
3318 }
3319 }
3320
3321 /* Specific type free. */
3322 switch (pentry->type) {
3323 case ENTRY_BOOL:
3324 case ENTRY_INT:
3325 case ENTRY_FLOAT:
3326 case ENTRY_LONG_COMMENT:
3327 break;
3328
3329 case ENTRY_STR:
3331 free(pentry->string.value);
3332 break;
3333
3334 case ENTRY_ILLEGAL:
3335 fc_assert(pentry->type != ENTRY_ILLEGAL);
3336 break;
3337 }
3338
3339 /* Common free. */
3340 free(pentry->name);
3341 if (pentry->comment != nullptr) {
3342 free(pentry->comment);
3343 }
3344 free(pentry);
3345}
3346
3347/**********************************************************************/
3350struct section *entry_section(const struct entry *pentry)
3351{
3352 return (pentry != nullptr ? pentry->psection : nullptr);
3353}
3354
3355/**********************************************************************/
3359{
3360 return (pentry != nullptr ? pentry->type : ENTRY_ILLEGAL);
3361}
3362
3363/**********************************************************************/
3366int entry_path(const struct entry *pentry, char *buf, size_t buf_len)
3367{
3368 return fc_snprintf(buf, buf_len, "%s.%s",
3371}
3372
3373/**********************************************************************/
3376const char *entry_name(const struct entry *pentry)
3377{
3378 return (pentry != nullptr ? pentry->name : nullptr);
3379}
3380
3381/**********************************************************************/
3384bool entry_set_name(struct entry *pentry, const char *name)
3385{
3386 struct section *psection;
3387 struct section_file *secfile;
3388
3389 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3390 psection = pentry->psection;
3391 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, psection != nullptr, FALSE);
3392 secfile = psection->secfile;
3393 SECFILE_RETURN_VAL_IF_FAIL(nullptr, psection, secfile != nullptr, FALSE);
3394
3395 if (name == nullptr || '\0' == name[0]) {
3396 SECFILE_LOG(secfile, psection, "No new name for entry \"%s\".",
3397 pentry->name);
3398 return FALSE;
3399 }
3400
3402 SECFILE_LOG(secfile, psection,
3403 "\"%s\" is not a valid entry name for entry \"%s\".",
3404 name, pentry->name);
3405 return FALSE;
3406 }
3407
3408 if (!secfile->allow_duplicates) {
3410
3411 if (pother != nullptr && pother != pentry) {
3412 SECFILE_LOG(secfile, psection, "Entry \"%s\" already exists.", name);
3413 return FALSE;
3414 }
3415 }
3416
3417 /* Remove from hash table the old path. */
3418 secfile_hash_delete(secfile, pentry);
3419
3420 /* Really rename the entry. */
3421 free(pentry->name);
3422 pentry->name = fc_strdup(name);
3423
3424 /* Insert into hash table the new path. */
3425 secfile_hash_insert(secfile, pentry);
3426 return TRUE;
3427}
3428
3429/**********************************************************************/
3432const char *entry_comment(const struct entry *pentry)
3433{
3434 return (pentry != nullptr ? pentry->comment : nullptr);
3435}
3436
3437/**********************************************************************/
3440void entry_set_comment(struct entry *pentry, const char *comment)
3441{
3442 if (pentry == nullptr) {
3443 return;
3444 }
3445
3446 if (pentry->comment != nullptr) {
3447 free(pentry->comment);
3448 }
3449
3450 pentry->comment = (comment != nullptr ? fc_strdup(comment) : nullptr);
3451}
3452
3453/**********************************************************************/
3456static inline bool entry_used(const struct entry *pentry)
3457{
3458 return (0 < pentry->used);
3459}
3460
3461/**********************************************************************/
3464static inline void entry_use(struct entry *pentry)
3465{
3466 pentry->used++;
3467}
3468
3469/**********************************************************************/
3473bool entry_bool_get(const struct entry *pentry, bool *value)
3474{
3475 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3476
3477 if (ENTRY_INT == pentry->type
3478 && (pentry->integer.value == 0
3479 || pentry->integer.value == 1)
3480 && pentry->psection != nullptr
3481 && pentry->psection->secfile != nullptr
3482 && pentry->psection->secfile->allow_digital_boolean) {
3483 *value = (0 != pentry->integer.value);
3484 return TRUE;
3485 }
3486
3487 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection != nullptr
3488 ? pentry->psection->secfile : nullptr,
3489 pentry->psection,
3490 ENTRY_BOOL == pentry->type, FALSE);
3491
3492 if (value != nullptr) {
3493 *value = pentry->boolean.value;
3494 }
3495
3496 return TRUE;
3497}
3498
3499/**********************************************************************/
3503{
3504 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3505 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection->secfile, pentry->psection,
3506 ENTRY_BOOL == pentry->type, FALSE);
3507
3508 pentry->boolean.value = value;
3509 return TRUE;
3510}
3511
3512/**********************************************************************/
3515bool entry_float_get(const struct entry *pentry, float *value)
3516{
3517 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3518 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection->secfile, pentry->psection,
3519 ENTRY_FLOAT == pentry->type, FALSE);
3520
3521 if (value != nullptr) {
3522 *value = pentry->floating.value;
3523 }
3524
3525 return TRUE;
3526}
3527
3528/**********************************************************************/
3531bool entry_float_set(struct entry *pentry, float value)
3532{
3533 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3534 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection->secfile, pentry->psection,
3535 ENTRY_FLOAT == pentry->type, FALSE);
3536
3537 pentry->floating.value = value;
3538
3539 return TRUE;
3540}
3541
3542/**********************************************************************/
3545bool entry_int_get(const struct entry *pentry, int *value)
3546{
3547 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3548 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection->secfile, pentry->psection,
3549 ENTRY_INT == pentry->type, FALSE);
3550
3551 if (value != nullptr) {
3552 *value = pentry->integer.value;
3553 }
3554
3555 return TRUE;
3556}
3557
3558/**********************************************************************/
3562{
3563 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3564 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection->secfile, pentry->psection,
3565 ENTRY_INT == pentry->type, FALSE);
3566
3567 pentry->integer.value = value;
3568 return TRUE;
3569}
3570
3571/**********************************************************************/
3574bool entry_str_get(const struct entry *pentry, const char **value)
3575{
3576 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3577 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection->secfile, pentry->psection,
3578 ENTRY_STR == pentry->type, FALSE);
3579
3580 if (value != nullptr) {
3581 *value = pentry->string.value;
3582 }
3583
3584 return TRUE;
3585}
3586
3587/**********************************************************************/
3590bool entry_str_set(struct entry *pentry, const char *value)
3591{
3592 char *old_val;
3593
3594 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3595 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection->secfile, pentry->psection,
3596 ENTRY_STR == pentry->type, FALSE);
3597
3598 /* We free() old value only after we've placed the new one, to
3599 * support secfile_replace_str_vec() calls that want to keep some of
3600 * the entries from the old vector in the new one. We don't want
3601 * to lose the entry in between. */
3602 old_val = pentry->string.value;
3603 pentry->string.value = fc_strdup(value != nullptr ? value : "");
3604 free(old_val);
3605
3606 return TRUE;
3607}
3608
3609/**********************************************************************/
3612bool entry_str_escaped(const struct entry *pentry)
3613{
3614 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3615 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection->secfile, pentry->psection,
3616 ENTRY_STR == pentry->type, FALSE);
3617
3618 return pentry->string.escaped;
3619}
3620
3621/**********************************************************************/
3625{
3626 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3627 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection->secfile, pentry->psection,
3628 ENTRY_STR == pentry->type, FALSE);
3629
3630 pentry->string.escaped = escaped;
3631
3632 return TRUE;
3633}
3634
3635/**********************************************************************/
3639{
3640 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3641 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection->secfile, pentry->psection,
3642 ENTRY_STR == pentry->type, FALSE);
3643
3644 /* don't put gettext marking on empty strings */
3645 if (pentry->string.value[0] != '\0') {
3646 pentry->string.gt_marking = gt_marking;
3647 return TRUE;
3648 } else {
3649 pentry->string.gt_marking = FALSE;
3650 return !gt_marking;
3651 }
3652}
3653
3654/**********************************************************************/
3657static void entry_to_file(const struct entry *pentry, fz_FILE *fs)
3658{
3659 static char buf[8192];
3660 char *dot = nullptr;
3661 int i;
3662
3663 switch (pentry->type) {
3664 case ENTRY_BOOL:
3665 fz_fprintf(fs, "%s", pentry->boolean.value ? "TRUE" : "FALSE");
3666 break;
3667 case ENTRY_INT:
3668 fz_fprintf(fs, "%d", pentry->integer.value);
3669 break;
3670 case ENTRY_FLOAT:
3671 snprintf(buf, sizeof(buf), "%f", pentry->floating.value);
3672 for (i = 0; buf[i] != '\0' ; i++) {
3673 if (buf[i] == '.') {
3674 dot = &(buf[i]);
3675 break;
3676 }
3677 }
3678 if (dot == nullptr) {
3679 /* There's no '.' so it would seem like a integer value when loaded.
3680 * Force it not to look like an integer by adding ".0" */
3681 fz_fprintf(fs, "%s.0", buf);
3682 } else {
3683 fz_fprintf(fs, "%s", buf);
3684 }
3685 break;
3686 case ENTRY_STR:
3687 if (pentry->string.escaped) {
3688 make_escapes(pentry->string.value, buf, sizeof(buf));
3689 if (pentry->string.gt_marking) {
3690 fz_fprintf(fs, "_(\"%s\")", buf);
3691 } else {
3692 fz_fprintf(fs, "\"%s\"", buf);
3693 }
3694 } else if (pentry->string.raw) {
3695 fz_fprintf(fs, "%s", pentry->string.value);
3696 } else {
3697 fz_fprintf(fs, "$%s$", pentry->string.value);
3698 }
3699 break;
3701 fz_fprintf(fs, "*%s*", pentry->string.value);
3702 break;
3703 case ENTRY_LONG_COMMENT:
3704 fz_fprintf(fs, "%s\n", pentry->comment);
3705 break;
3706 case ENTRY_ILLEGAL:
3707 fc_assert(pentry->type != ENTRY_ILLEGAL);
3708 break;
3709 }
3710}
3711
3712/**********************************************************************/
3715static void entry_from_inf_token(struct section *psection, const char *name,
3716 const char *tok, struct inputfile *inf)
3717{
3719 log_error("%s", inf_log_str(inf, "Entry value not recognized: %s", tok));
3720 }
3721}
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
char * incite_cost
Definition comments.c:76
bool are_deprecation_warnings_enabled(void)
#define log_deprecation_always(message,...)
static void base(QVariant data1, QVariant data2)
Definition dialogs.cpp:3353
#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_malloc(sz)
Definition mem.h:34
int len
Definition packhand.c:127
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,...)
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
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
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,...)
bool secfile_save(const struct section_file *secfile, const char *filename, int compression_level, enum fz_method compression_method)
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)
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,...)
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)
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 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:960
bool fc_isalpha(char c)
Definition support.c:1207
size_t fc_strlcpy(char *dest, const char *src, size_t n)
Definition support.c:777
void make_escapes(const char *str, char *buf, size_t buf_len)
Definition support.c:294
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:986
int fc_vsnprintf(char *str, size_t n, const char *format, va_list ap)
Definition support.c:886
bool fc_isalnum(char c)
Definition support.c:1196
#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