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
333 astring_vector_init(&columns);
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 {
410 int num_columns = astring_vector_size(&columns);
411
412 i++;
413
414 if (i < num_columns) {
415 astr_set(&field_name, "%s%d.%s", astr_str(&base_name),
416 table_lineno, astr_str(&columns.p[i]));
417 } else {
418 astr_set(&field_name, "%s%d.%s,%d", astr_str(&base_name),
419 table_lineno, astr_str(&columns.p[num_columns - 1]),
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
478 n_prev = astring_vector_size(&columns);
479 for (j = i + 1; j < n_prev; j++) {
480 astr_free(&columns.p[j]);
481 }
482 astring_vector_reserve(&columns, i + 1);
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 }
547 astring_vector_free(&columns);
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, const char *path, ...)
1363{
1365 size_t i, ret = 0;
1366 va_list args;
1367
1368 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, 0);
1369
1370 va_start(args, path);
1371 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1372 va_end(args);
1373
1374 /* NB: 'path,0' is actually 'path'. See comment in the head
1375 * of the file. */
1376 if (dim > 0
1377 && secfile_insert_str_full(secfile, strings[0], comment,
1379 "%s", fullpath) != nullptr) {
1380 ret++;
1381 }
1382 for (i = 1; i < dim; i++) {
1385 "%s,%d", fullpath, (int) i) != nullptr) {
1386 ret++;
1387 }
1388 }
1389
1390 return ret;
1391}
1392
1393/**********************************************************************/
1397 const char *filename,
1398 const char *path, ...)
1399{
1401 const char *ent_name;
1402 struct section *psection;
1403 struct entry *pentry = nullptr;
1404 va_list args;
1405
1406 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
1407
1408 va_start(args, path);
1409 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1410 va_end(args);
1411
1413 if (!psection) {
1414 return nullptr;
1415 }
1416
1417 if (psection->special != EST_NORMAL) {
1418 log_error("Tried to insert normal entry to different kind of section");
1419 return nullptr;
1420 }
1421
1422 if (pentry == nullptr) {
1424 }
1425
1426 return pentry;
1427}
1428
1429/**********************************************************************/
1433 int enumerator,
1435 const char *comment,
1436 bool allow_replace,
1437 const char *path, ...)
1438{
1440 const char *str;
1441 const char *ent_name;
1442 struct section *psection;
1443 struct entry *pentry = nullptr;
1444 va_list args;
1445
1446 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
1447 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, name_fn != nullptr, nullptr);
1448 str = name_fn(enumerator);
1449 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, str != nullptr, nullptr);
1450
1451 va_start(args, path);
1452 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1453 va_end(args);
1454
1456 if (!psection) {
1457 return nullptr;
1458 }
1459
1460 if (allow_replace) {
1462 if (pentry != nullptr) {
1464 if (!entry_str_set(pentry, str)) {
1465 return nullptr;
1466 }
1467 } else {
1469 pentry = nullptr;
1470 }
1471 }
1472 }
1473
1474 if (pentry == nullptr) {
1476 }
1477
1478 if (pentry != nullptr && comment != nullptr) {
1480 }
1481
1482 return pentry;
1483}
1484
1485/**********************************************************************/
1490 const int *enumurators, size_t dim,
1492 const char *comment,
1493 bool allow_replace,
1494 const char *path, ...)
1495{
1497 size_t i, ret = 0;
1498 va_list args;
1499
1500 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, 0);
1501 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, name_fn != nullptr, 0);
1502
1503 va_start(args, path);
1504 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1505 va_end(args);
1506
1507 /* NB: 'path,0' is actually 'path'. See comment in the head
1508 * of the file. */
1509 if (dim > 0
1512 allow_replace, "%s",
1513 fullpath) != nullptr) {
1514 ret++;
1515 }
1516 for (i = 1; i < dim; i++) {
1519 allow_replace, "%s,%d",
1520 fullpath, (int) i) != nullptr) {
1521 ret++;
1522 }
1523 }
1524
1525 return ret;
1526}
1527
1528/**********************************************************************/
1532 int bitwise_val,
1534 name_fn,
1536 begin_fn,
1538 end_fn,
1540 next_fn,
1541 const char *comment,
1542 bool allow_replace,
1543 const char *path, ...)
1544{
1546 const char *ent_name;
1547 struct section *psection;
1548 struct entry *pentry = nullptr;
1549 va_list args;
1550 int i;
1551
1552 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
1553 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, name_fn != nullptr, nullptr);
1554 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, begin_fn != nullptr, nullptr);
1555 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, end_fn != nullptr, nullptr);
1556 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, next_fn != nullptr, nullptr);
1557
1558 /* Compute a string containing all the values separated by '|'. */
1559 str[0] = '\0'; /* Insert at least an empty string. */
1560 if (0 != bitwise_val) {
1561 for (i = begin_fn(); i != end_fn(); i = next_fn(i)) {
1562 if (i & bitwise_val) {
1563 if ('\0' == str[0]) {
1565 } else {
1566 cat_snprintf(str, sizeof(str), "|%s", name_fn(i));
1567 }
1568 }
1569 }
1570 }
1571
1572 va_start(args, path);
1573 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1574 va_end(args);
1575
1577 if (!psection) {
1578 return nullptr;
1579 }
1580
1581 if (allow_replace) {
1583 if (pentry != nullptr) {
1585 if (!entry_str_set(pentry, str)) {
1586 return nullptr;
1587 }
1588 } else {
1590 pentry = nullptr;
1591 }
1592 }
1593 }
1594
1595 if (pentry == nullptr) {
1597 }
1598
1599 if (pentry != nullptr && comment != nullptr) {
1601 }
1602
1603 return pentry;
1604}
1605
1606/**********************************************************************/
1611 const int *bitwise_vals,
1612 size_t dim,
1617 const char *comment,
1618 bool allow_replace,
1619 const char *path, ...)
1620{
1622 size_t i, ret = 0;
1623 va_list args;
1624
1625 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, 0);
1626 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, name_fn != nullptr, 0);
1627 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, begin_fn != nullptr, 0);
1628 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, end_fn != nullptr, 0);
1629 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, next_fn != nullptr, 0);
1630
1631 va_start(args, path);
1632 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1633 va_end(args);
1634
1635 /* NB: 'path,0' is actually 'path'. See comment in the head
1636 * of the file. */
1637 if (dim > 0
1641 allow_replace, "%s",
1642 fullpath) != nullptr) {
1643 ret++;
1644 }
1645 for (i = 1; i < dim; i++) {
1649 allow_replace, "%s,%d",
1650 fullpath, (int) i) != nullptr) {
1651 ret++;
1652 }
1653 }
1654
1655 return ret;
1656}
1657
1658/**********************************************************************/
1663 int value, bool bitwise,
1665 secfile_data_t data,
1666 const char *comment,
1667 bool allow_replace,
1668 const char *path, ...)
1669{
1671 const char *ent_name, *val_name;
1672 struct section *psection;
1673 struct entry *pentry = nullptr;
1674 va_list args;
1675 int i;
1676
1677 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
1678 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, name_fn != nullptr, nullptr);
1679
1680 if (bitwise) {
1681 /* Compute a string containing all the values separated by '|'. */
1682 str[0] = '\0'; /* Insert at least an empty string. */
1683 if (0 != value) {
1684 for (i = 0; (val_name = name_fn(data, i)); i++) {
1685 if ((1 << i) & value) {
1686 if ('\0' == str[0]) {
1688 } else {
1689 cat_snprintf(str, sizeof(str), "|%s", val_name);
1690 }
1691 }
1692 }
1693 }
1694 } else {
1695 if (!(val_name = name_fn(data, value))) {
1696 SECFILE_LOG(secfile, nullptr, "Value %d not supported.", value);
1697
1698 return nullptr;
1699 }
1701 }
1702
1703 va_start(args, path);
1704 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1705 va_end(args);
1706
1708 if (!psection) {
1709 return nullptr;
1710 }
1711
1712 if (allow_replace) {
1714 if (pentry != nullptr) {
1716 if (!entry_str_set(pentry, str)) {
1717 return nullptr;
1718 }
1719 } else {
1721 pentry = nullptr;
1722 }
1723 }
1724 }
1725
1726 if (pentry == nullptr) {
1728 }
1729
1730 if (pentry != nullptr && comment != nullptr) {
1732 }
1733
1734 return pentry;
1735}
1736
1737/**********************************************************************/
1742 const int *values, size_t dim,
1743 bool bitwise,
1745 secfile_data_t data,
1746 const char *comment,
1747 bool allow_replace,
1748 const char *path, ...)
1749{
1751 size_t i, ret = 0;
1752 va_list args;
1753
1754 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, 0);
1755 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, name_fn != nullptr, 0);
1756
1757 va_start(args, path);
1758 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1759 va_end(args);
1760
1761 /* NB: 'path,0' is actually 'path'. See comment in the head
1762 * of the file. */
1763 if (dim > 0
1764 && secfile_insert_enum_data_full(secfile, values[0], bitwise,
1765 name_fn, data, comment,
1766 allow_replace, "%s",
1767 fullpath) != nullptr) {
1768 ret++;
1769 }
1770 for (i = 1; i < dim; i++) {
1771 if (secfile_insert_enum_data_full(secfile, values[i], bitwise,
1772 name_fn, data, comment,
1773 allow_replace, "%s,%d",
1774 fullpath, (int) i) != nullptr) {
1775 ret++;
1776 }
1777 }
1778
1779 return ret;
1780}
1781
1782/**********************************************************************/
1785struct entry *secfile_entry_by_path(const struct section_file *secfile,
1786 const char *path)
1787{
1789 char *ent_name;
1790 size_t len;
1791 struct section *psection;
1792
1793 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
1794
1795 sz_strlcpy(fullpath, path);
1796
1797 /* Treat "sec.foo,0" as "sec.foo": */
1798 len = strlen(fullpath);
1799 if (len > 2 && fullpath[len - 2] == ',' && fullpath[len - 1] == '0') {
1800 fullpath[len - 2] = '\0';
1801 }
1802
1803 if (secfile->hash.entries != nullptr) {
1804 struct entry *pentry;
1805
1806 if (entry_hash_lookup(secfile->hash.entries, fullpath, &pentry)) {
1808 }
1809 return pentry;
1810 }
1811
1812 /* I dont like strtok().
1813 * - Me neither! */
1814 ent_name = strchr(fullpath, '.');
1815 if (!ent_name) {
1816 return nullptr;
1817 }
1818
1819 /* Separates section and entry names. */
1820 *ent_name++ = '\0';
1822 if (psection) {
1824 } else {
1825 return nullptr;
1826 }
1827}
1828
1829/**********************************************************************/
1833 const char *path, ...)
1834{
1836 va_list args;
1837 struct entry *pentry;
1838
1839 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, FALSE);
1840
1841 va_start(args, path);
1842 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1843 va_end(args);
1844
1845 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
1846 SECFILE_LOG(secfile, nullptr, "Path %s does not exists.", fullpath);
1847 return FALSE;
1848 }
1849
1851
1852 return TRUE;
1853}
1854
1855/**********************************************************************/
1858struct entry *secfile_entry_lookup(const struct section_file *secfile,
1859 const char *path, ...)
1860{
1862 va_list args;
1863
1864 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
1865
1866 va_start(args, path);
1867 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1868 va_end(args);
1869
1870 return secfile_entry_by_path(secfile, fullpath);
1871}
1872
1873/**********************************************************************/
1876bool secfile_lookup_bool(const struct section_file *secfile, bool *bval,
1877 const char *path, ...)
1878{
1880 const struct entry *pentry;
1881 va_list args;
1882
1883 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, FALSE);
1884
1885 va_start(args, path);
1886 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1887 va_end(args);
1888
1889 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
1890 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
1891
1892 return FALSE;
1893 }
1894
1895 return entry_bool_get(pentry, bval);
1896}
1897
1898/**********************************************************************/
1903 bool def, const char *path, ...)
1904{
1906 const struct entry *pentry;
1907 bool bval;
1908 va_list args;
1909
1910 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, def);
1911
1912 va_start(args, path);
1913 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1914 va_end(args);
1915
1916 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
1917 return def;
1918 }
1919
1920 if (entry_bool_get(pentry, &bval)) {
1921 return bval;
1922 }
1923
1924 return def;
1925}
1926
1927/**********************************************************************/
1932bool *secfile_lookup_bool_vec(const struct section_file *secfile,
1933 size_t *dim, const char *path, ...)
1934{
1936 size_t i = 0;
1937 bool *vec;
1938 va_list args;
1939
1940 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
1941 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, dim != nullptr, nullptr);
1942
1943 va_start(args, path);
1944 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1945 va_end(args);
1946
1947 /* Check size. */
1948 while (secfile_entry_lookup(secfile, "%s,%d", fullpath, (int) i) != nullptr) {
1949 i++;
1950 }
1951 *dim = i;
1952
1953 if (0 == i) {
1954 /* Doesn't exist. */
1955 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
1956 return nullptr;
1957 }
1958
1959 vec = fc_malloc(i * sizeof(bool));
1960 for (i = 0; i < *dim; i++) {
1961 if (!secfile_lookup_bool(secfile, vec + i, "%s,%d", fullpath, (int) i)) {
1962 SECFILE_LOG(secfile, nullptr,
1963 "An error occurred when looking up to \"%s,%d\" entry.",
1964 fullpath, (int) i);
1965 free(vec);
1966 *dim = 0;
1967
1968 return nullptr;
1969 }
1970 }
1971
1972 return vec;
1973}
1974
1975/**********************************************************************/
1978bool secfile_lookup_int(const struct section_file *secfile, int *ival,
1979 const char *path, ...)
1980{
1982 const struct entry *pentry;
1983 va_list args;
1984
1985 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, FALSE);
1986
1987 va_start(args, path);
1988 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
1989 va_end(args);
1990
1991 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
1992 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
1993
1994 return FALSE;
1995 }
1996
1997 return entry_int_get(pentry, ival);
1998}
1999
2000/**********************************************************************/
2004int secfile_lookup_int_default(const struct section_file *secfile, int def,
2005 const char *path, ...)
2006{
2008 const struct entry *pentry;
2009 int ival;
2010 va_list args;
2011
2012 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, def);
2013
2014 va_start(args, path);
2015 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2016 va_end(args);
2017
2018 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2019 return def;
2020 }
2021
2022 if (entry_int_get(pentry, &ival)) {
2023 return ival;
2024 }
2025
2026 return def;
2027}
2028
2029/**********************************************************************/
2035 int defval, int minval, int maxval,
2036 const char *path, ...)
2037{
2039 const struct entry *pentry;
2040 int value;
2041 va_list args;
2042
2043 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, defval);
2044
2045 va_start(args, path);
2046 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2047 va_end(args);
2048
2049 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2050 return defval;
2051 }
2052
2053 if (!entry_int_get(pentry, &value)) {
2054 return defval;
2055 }
2056
2057 if (value < minval) {
2059 "\"%s\" should be in the interval [%d, %d] but is %d;"
2060 "using the minimal value.",
2062 value = minval;
2063 }
2064
2065 if (value > maxval) {
2067 "\"%s\" should be in the interval [%d, %d] but is %d;"
2068 "using the maximal value.",
2070 value = maxval;
2071 }
2072
2073 return value;
2074}
2075
2076/**********************************************************************/
2081int *secfile_lookup_int_vec(const struct section_file *secfile,
2082 size_t *dim, const char *path, ...)
2083{
2085 size_t i = 0;
2086 int *vec;
2087 va_list args;
2088
2089 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
2090 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, dim != nullptr, nullptr);
2091
2092 va_start(args, path);
2093 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2094 va_end(args);
2095
2096 /* Check size. */
2097 while (secfile_entry_lookup(secfile, "%s,%d", fullpath, (int) i) != nullptr) {
2098 i++;
2099 }
2100 *dim = i;
2101
2102 if (0 == i) {
2103 /* Doesn't exist. */
2104 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2105
2106 return nullptr;
2107 }
2108
2109 vec = fc_malloc(i * sizeof(int));
2110 for (i = 0; i < *dim; i++) {
2111 if (!secfile_lookup_int(secfile, vec + i, "%s,%d", fullpath, (int) i)) {
2112 SECFILE_LOG(secfile, nullptr,
2113 "An error occurred when looking up to \"%s,%d\" entry.",
2114 fullpath, (int) i);
2115 free(vec);
2116 *dim = 0;
2117 return nullptr;
2118 }
2119 }
2120
2121 return vec;
2122}
2123
2124/**********************************************************************/
2127bool secfile_lookup_float(const struct section_file *secfile, float *fval,
2128 const char *path, ...)
2129{
2131 const struct entry *pentry;
2132 va_list args;
2133
2134 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, FALSE);
2135
2136 va_start(args, path);
2137 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2138 va_end(args);
2139
2140 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2141 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2142 return FALSE;
2143 }
2144
2145 return entry_float_get(pentry, fval);
2146}
2147
2148/**********************************************************************/
2152float secfile_lookup_float_default(const struct section_file *secfile,
2153 float def, const char *path, ...)
2154{
2156 const struct entry *pentry;
2157 float fval;
2158 va_list args;
2159
2160 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, def);
2161
2162 va_start(args, path);
2163 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2164 va_end(args);
2165
2166 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2167 return def;
2168 }
2169
2170 if (entry_float_get(pentry, &fval)) {
2171 return fval;
2172 }
2173
2174 return def;
2175}
2176
2177/**********************************************************************/
2180const char *secfile_lookup_str(const struct section_file *secfile,
2181 const char *path, ...)
2182{
2184 const struct entry *pentry;
2185 const char *str;
2186 va_list args;
2187
2188 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
2189
2190 va_start(args, path);
2191 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2192 va_end(args);
2193
2194 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2195 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2196 return nullptr;
2197 }
2198
2199 if (entry_str_get(pentry, &str)) {
2200 return str;
2201 }
2202
2203 return nullptr;
2204}
2205
2206/**********************************************************************/
2210const char *secfile_lookup_str_default(const struct section_file *secfile,
2211 const char *def,
2212 const char *path, ...)
2213{
2215 const struct entry *pentry;
2216 const char *str;
2217 va_list args;
2218
2219 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, def);
2220
2221 va_start(args, path);
2222 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2223 va_end(args);
2224
2225 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2226 return def;
2227 }
2228
2229 if (entry_str_get(pentry, &str)) {
2230 return str;
2231 }
2232
2233 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't have a string.", fullpath);
2234
2235 return def;
2236}
2237
2238/**********************************************************************/
2244const char **secfile_lookup_str_vec(const struct section_file *secfile,
2245 size_t *dim, const char *path, ...)
2246{
2248 size_t i = 0;
2249 const char **vec;
2250 va_list args;
2251
2252 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
2253 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, dim != nullptr, nullptr);
2254
2255 va_start(args, path);
2256 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2257 va_end(args);
2258
2259 /* Check size. */
2260 while (secfile_entry_lookup(secfile, "%s,%d", fullpath, (int) i) != nullptr) {
2261 i++;
2262 }
2263 *dim = i;
2264
2265 if (0 == i) {
2266 /* Doesn't exist. */
2267 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2268 return nullptr;
2269 }
2270
2271 vec = fc_malloc(i * sizeof(const char *));
2272 for (i = 0; i < *dim; i++) {
2273 if (!(vec[i] = secfile_lookup_str(secfile, "%s,%d",
2274 fullpath, (int) i))) {
2275 SECFILE_LOG(secfile, nullptr,
2276 "An error occurred when looking up to \"%s,%d\" entry.",
2277 fullpath, (int) i);
2278 free(vec);
2279 *dim = 0;
2280 return nullptr;
2281 }
2282 }
2283
2284 return vec;
2285}
2286
2287/**********************************************************************/
2291 int *penumerator,
2294 const char *path, ...)
2295{
2297 const struct entry *pentry;
2298 const char *str;
2299 va_list args;
2300
2301 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, FALSE);
2302 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, penumerator != nullptr, FALSE);
2303 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, is_valid_fn != nullptr, FALSE);
2304 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, by_name_fn != nullptr, FALSE);
2305
2306 va_start(args, path);
2307 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2308 va_end(args);
2309
2310 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2311 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2312 return FALSE;
2313 }
2314
2315 if (!entry_str_get(pentry, &str)) {
2316 return FALSE;
2317 }
2318
2320 if (is_valid_fn(*penumerator)) {
2321 return TRUE;
2322 }
2323
2325 "Entry \"%s\": no match for \"%s\".",
2327 return FALSE;
2328}
2329
2330/**********************************************************************/
2334 *secfile, int defval,
2338 by_name_fn,
2339 const char *path, ...)
2340{
2342 const struct entry *pentry;
2343 const char *str;
2344 int val;
2345 va_list args;
2346
2347 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, defval);
2348 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, is_valid_fn != nullptr, defval);
2349 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, by_name_fn != nullptr, defval);
2350
2351 va_start(args, path);
2352 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2353 va_end(args);
2354
2355 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2356 return defval;
2357 }
2358
2359 if (!entry_str_get(pentry, &str)) {
2360 return defval;
2361 }
2362
2363 val = by_name_fn(str, strcmp);
2364 if (is_valid_fn(val)) {
2365 return val;
2366 } else {
2367 return defval;
2368 }
2369}
2370
2371/**********************************************************************/
2377 size_t *dim,
2381 by_name_fn,
2382 const char *path, ...)
2383{
2385 size_t i = 0;
2386 int *vec;
2387 va_list args;
2388
2389 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
2390 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, dim != nullptr, nullptr);
2391 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, is_valid_fn != nullptr, nullptr);
2392 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, by_name_fn != nullptr, nullptr);
2393
2394 va_start(args, path);
2395 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2396 va_end(args);
2397
2398 /* Check size. */
2399 while (secfile_entry_lookup(secfile, "%s,%d", fullpath, (int) i) != nullptr) {
2400 i++;
2401 }
2402 *dim = i;
2403
2404 if (0 == i) {
2405 /* Doesn't exist. */
2406 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2407 return nullptr;
2408 }
2409
2410 vec = fc_malloc(i * sizeof(int));
2411 for (i = 0; i < *dim; i++) {
2412 if (!secfile_lookup_plain_enum_full(secfile, vec + i, is_valid_fn,
2413 by_name_fn, "%s,%d",
2414 fullpath, (int) i)) {
2415 SECFILE_LOG(secfile, nullptr,
2416 "An error occurred when looking up to \"%s,%d\" entry.",
2417 fullpath, (int) i);
2418 free(vec);
2419 *dim = 0;
2420 return nullptr;
2421 }
2422 }
2423
2424 return vec;
2425}
2426
2427/**********************************************************************/
2431 int *penumerator,
2434 const char *path, ...)
2435{
2437 const struct entry *pentry;
2438 const char *str, *p;
2440 int val;
2441 va_list args;
2442
2443 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, FALSE);
2444 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, penumerator != nullptr, FALSE);
2445 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, is_valid_fn != nullptr, FALSE);
2446 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, by_name_fn != nullptr, FALSE);
2447
2448 va_start(args, path);
2449 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2450 va_end(args);
2451
2452 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2453 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2454 return FALSE;
2455 }
2456
2457 if (!entry_str_get(pentry, &str)) {
2458 return FALSE;
2459 }
2460
2461 *penumerator = 0;
2462 if ('\0' == str[0]) {
2463 /* Empty string = no value. */
2464 return TRUE;
2465 }
2466
2467 /* Value names are separated by '|'. */
2468 do {
2469 p = strchr(str, '|');
2470 if (p != nullptr) {
2471 p++;
2472 fc_strlcpy(val_name, str, p - str);
2473 } else {
2474 /* Last segment, full copy. */
2476 }
2478 val = by_name_fn(val_name, strcmp);
2479 if (!is_valid_fn(val)) {
2481 "Entry \"%s\": no match for \"%s\".",
2483 return FALSE;
2484 }
2485 *penumerator |= val;
2486 str = p;
2487 } while (p != nullptr);
2488
2489 return TRUE;
2490}
2491
2492/**********************************************************************/
2496 *secfile, int defval,
2500 by_name_fn,
2501 const char *path, ...)
2502{
2504 const struct entry *pentry;
2505 const char *str, *p;
2507 int val, full_val;
2508 va_list args;
2509
2510 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, defval);
2511 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, is_valid_fn != nullptr, defval);
2512 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, by_name_fn != nullptr, defval);
2513
2514 va_start(args, path);
2515 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2516 va_end(args);
2517
2518 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2519 return defval;
2520 }
2521
2522 if (!entry_str_get(pentry, &str)) {
2523 return defval;
2524 }
2525
2526 if ('\0' == str[0]) {
2527 /* Empty string = no value. */
2528 return 0;
2529 }
2530
2531 /* Value names are separated by '|'. */
2532 full_val = 0;
2533 do {
2534 p = strchr(str, '|');
2535 if (p != nullptr) {
2536 p++;
2537 fc_strlcpy(val_name, str, p - str);
2538 } else {
2539 /* Last segment, full copy. */
2541 }
2543 val = by_name_fn(val_name, strcmp);
2544 if (!is_valid_fn(val)) {
2545 return defval;
2546 }
2547 full_val |= val;
2548 str = p;
2549 } while (p != nullptr);
2550
2551 return full_val;
2552}
2553
2554/**********************************************************************/
2560 size_t *dim,
2564 by_name_fn,
2565 const char *path, ...)
2566{
2568 size_t i = 0;
2569 int *vec;
2570 va_list args;
2571
2572 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
2573 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, dim != nullptr, nullptr);
2574 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, is_valid_fn != nullptr, nullptr);
2575 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, by_name_fn != nullptr, nullptr);
2576
2577 va_start(args, path);
2578 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2579 va_end(args);
2580
2581 /* Check size. */
2582 while (secfile_entry_lookup(secfile, "%s,%d", fullpath, (int) i) != nullptr) {
2583 i++;
2584 }
2585 *dim = i;
2586
2587 if (0 == i) {
2588 /* Doesn't exist. */
2589 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2590 return nullptr;
2591 }
2592
2593 vec = fc_malloc(i * sizeof(int));
2594 for (i = 0; i < *dim; i++) {
2595 if (!secfile_lookup_bitwise_enum_full(secfile, vec + i, is_valid_fn,
2596 by_name_fn, "%s,%d",
2597 fullpath, (int) i)) {
2598 SECFILE_LOG(secfile, nullptr,
2599 "An error occurred when looking up to \"%s,%d\" entry.",
2600 fullpath, (int) i);
2601 free(vec);
2602 *dim = 0;
2603
2604 return nullptr;
2605 }
2606 }
2607
2608 return vec;
2609}
2610
2611/**********************************************************************/
2614bool secfile_lookup_enum_data(const struct section_file *secfile,
2615 int *pvalue, bool bitwise,
2617 secfile_data_t data, const char *path, ...)
2618{
2620 const struct entry *pentry;
2621 const char *str, *p, *name;
2623 int val;
2624 va_list args;
2625
2626 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, FALSE);
2627 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, pvalue != nullptr, FALSE);
2628 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, name_fn != nullptr, FALSE);
2629
2630 va_start(args, path);
2631 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2632 va_end(args);
2633
2634 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2635 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2636 return FALSE;
2637 }
2638
2639 if (!entry_str_get(pentry, &str)) {
2640 return FALSE;
2641 }
2642
2643 if (bitwise) {
2644 *pvalue = 0;
2645 if ('\0' == str[0]) {
2646 /* Empty string = no value. */
2647 return TRUE;
2648 }
2649
2650 /* Value names are separated by '|'. */
2651 do {
2652 p = strchr(str, '|');
2653 if (p != nullptr) {
2654 p++;
2655 fc_strlcpy(val_name, str, p - str);
2656 } else {
2657 /* Last segment, full copy. */
2659 }
2661 for (val = 0; (name = name_fn(data, val)); val++) {
2662 if (0 == fc_strcasecmp(name, val_name)) {
2663 break;
2664 }
2665 }
2666 if (name == nullptr) {
2668 "Entry \"%s\": no match for \"%s\".",
2670 return FALSE;
2671 }
2672 *pvalue |= 1 << val;
2673 str = p;
2674 } while (p != nullptr);
2675 } else {
2676 for (val = 0; (name = name_fn(data, val)); val++) {
2677 if (0 == fc_strcasecmp(name, str)) {
2678 *pvalue = val;
2679 break;
2680 }
2681 }
2682 if (name == nullptr) {
2684 "Entry \"%s\": no match for \"%s\".",
2686 return FALSE;
2687 }
2688 }
2689
2690 return TRUE;
2691}
2692
2693/**********************************************************************/
2697 int defval, bool bitwise,
2699 secfile_data_t data,
2700 const char *path, ...)
2701{
2703 const struct entry *pentry;
2704 const char *str, *p, *name;
2706 int value, val;
2707 va_list args;
2708
2709 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, defval);
2710 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, name_fn != nullptr, defval);
2711
2712 va_start(args, path);
2713 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2714 va_end(args);
2715
2716 if (!(pentry = secfile_entry_by_path(secfile, fullpath))) {
2717 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2718 return defval;
2719 }
2720
2721 if (!entry_str_get(pentry, &str)) {
2722 return defval;
2723 }
2724
2725 value = 0;
2726 if (bitwise) {
2727 if ('\0' == str[0]) {
2728 /* Empty string = no value. */
2729 return value;
2730 }
2731
2732 /* Value names are separated by '|'. */
2733 do {
2734 p = strchr(str, '|');
2735 if (p != nullptr) {
2736 p++;
2737 fc_strlcpy(val_name, str, p - str);
2738 } else {
2739 /* Last segment, full copy. */
2741 }
2743 for (val = 0; (name = name_fn(data, val)); val++) {
2744 if (0 == strcmp(name, val_name)) {
2745 break;
2746 }
2747 }
2748 if (name == nullptr) {
2750 "Entry \"%s\": no match for \"%s\".",
2752 return defval;
2753 }
2754 value |= 1 << val;
2755 str = p;
2756 } while (p != nullptr);
2757 } else {
2758 for (val = 0; (name = name_fn(data, val)); val++) {
2759 if (0 == strcmp(name, str)) {
2760 value = val;
2761 break;
2762 }
2763 }
2764 if (name == nullptr) {
2766 "Entry \"%s\": no match for \"%s\".",
2768 return defval;
2769 }
2770 }
2771
2772 return value;
2773}
2774
2775/**********************************************************************/
2780 size_t *dim, bool bitwise,
2782 secfile_data_t data, const char *path, ...)
2783{
2785 size_t i = 0;
2786 int *vec;
2787 va_list args;
2788
2789 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
2790 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, dim != nullptr, nullptr);
2791 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, name_fn != nullptr, nullptr);
2792
2793 va_start(args, path);
2794 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2795 va_end(args);
2796
2797 /* Check size. */
2798 while (secfile_entry_lookup(secfile, "%s,%d", fullpath, (int) i) != nullptr) {
2799 i++;
2800 }
2801 *dim = i;
2802
2803 if (0 == i) {
2804 /* Doesn't exist. */
2805 SECFILE_LOG(secfile, nullptr, "\"%s\" entry doesn't exist.", fullpath);
2806 return nullptr;
2807 }
2808
2809 vec = fc_malloc(i * sizeof(int));
2810 for (i = 0; i < *dim; i++) {
2811 if (!secfile_lookup_enum_data(secfile, vec + i, bitwise, name_fn, data,
2812 "%s,%d", fullpath, (int) i)) {
2813 SECFILE_LOG(secfile, nullptr,
2814 "An error occurred when looking up to \"%s,%d\" entry.",
2815 fullpath, (int) i);
2816 free(vec);
2817 *dim = 0;
2818
2819 return nullptr;
2820 }
2821 }
2822
2823 return vec;
2824}
2825
2826/**********************************************************************/
2830 const char *name)
2831{
2832 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
2833
2835 if (0 == strcmp(section_name(psection), name)) {
2836 return psection;
2837 }
2839
2840 return nullptr;
2841}
2842
2843/**********************************************************************/
2847 const char *path, ...)
2848{
2850 va_list args;
2851
2852 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
2853
2854 va_start(args, path);
2855 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
2856 va_end(args);
2857
2859}
2860
2861/**********************************************************************/
2865const struct section_list *
2866secfile_sections(const struct section_file *secfile)
2867{
2868 return (secfile != nullptr ? secfile->sections : nullptr);
2869}
2870
2871/**********************************************************************/
2876struct section_list *
2878 const char *prefix)
2879{
2880 struct section_list *matches = nullptr;
2881 size_t len;
2882
2883 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
2884 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, prefix != nullptr, nullptr);
2885
2886 len = strlen(prefix);
2887 if (0 == len) {
2888 return nullptr;
2889 }
2890
2891 section_list_iterate(secfile->sections, psection) {
2892 if (!fc_strncmp(section_name(psection), prefix, len)) {
2893 if (matches == nullptr) {
2895 }
2896 section_list_append(matches, psection);
2897 }
2899
2900 return matches;
2901}
2902
2903/**********************************************************************/
2907 const char *prefix)
2908{
2909 size_t len;
2910
2911 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, FALSE);
2912 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, prefix != nullptr, FALSE);
2913
2914 len = strlen(prefix);
2915 if (0 == len) {
2916 return FALSE;
2917 }
2918
2919 section_list_iterate(secfile->sections, psection) {
2920 if (!fc_strncmp(section_name(psection), prefix, len)) {
2921 return TRUE;
2922 }
2924
2925 return FALSE;
2926}
2927
2928/**********************************************************************/
2932 const char *name)
2933{
2934 struct section *psection;
2935
2936 SECFILE_RETURN_VAL_IF_FAIL(secfile, nullptr, secfile != nullptr, nullptr);
2937
2938 if (name == nullptr || '\0' == name[0]) {
2939 SECFILE_LOG(secfile, nullptr, "Cannot create a section without name.");
2940 return nullptr;
2941 }
2942
2944 SECFILE_LOG(secfile, nullptr, "\"%s\" is not a valid section name.",
2945 name);
2946 return nullptr;
2947 }
2948
2949 if (secfile_section_by_name(secfile, name) != nullptr) {
2950 /* We cannot duplicate sections in any case! Not even if one is
2951 * include -section and the other not. */
2952 SECFILE_LOG(secfile, nullptr, "Section \"%s\" already exists.", name);
2953 return nullptr;
2954 }
2955
2956 psection = fc_malloc(sizeof(struct section));
2957 psection->special = EST_NORMAL;
2958 psection->name = fc_strdup(name);
2960
2961 /* Append to secfile. */
2962 psection->secfile = secfile;
2964
2965 if (secfile->hash.sections != nullptr) {
2966 section_hash_insert(secfile->hash.sections, psection->name, psection);
2967 }
2968
2969 return psection;
2970}
2971
2972/**********************************************************************/
2975void section_destroy(struct section *psection)
2976{
2977 struct section_file *secfile;
2978
2979 SECFILE_RETURN_IF_FAIL(nullptr, psection, psection != nullptr);
2980
2981 section_clear_all(psection);
2982
2983 if ((secfile = psection->secfile)) {
2984 /* Detach from secfile. */
2985 if (section_list_remove(secfile->sections, psection)) {
2986 /* This has called section_destroy() already then. */
2987 return;
2988 }
2989 if (secfile->hash.sections != nullptr) {
2990 section_hash_remove(secfile->hash.sections, psection->name);
2991 }
2992 }
2993
2994 entry_list_destroy(psection->entries);
2995 free(psection->name);
2996 free(psection);
2997}
2998
2999/**********************************************************************/
3002void section_clear_all(struct section *psection)
3003{
3004 SECFILE_RETURN_IF_FAIL(nullptr, psection, psection != nullptr);
3005
3006 /* This include the removing of the hash data. */
3007 entry_list_clear(psection->entries);
3008
3009 if (0 < entry_list_size(psection->entries)) {
3010 SECFILE_LOG(psection->secfile, psection,
3011 "After clearing all, %d entries are still remaining.",
3012 entry_list_size(psection->entries));
3013 }
3014}
3015
3016/**********************************************************************/
3019bool section_set_name(struct section *psection, const char *name)
3020{
3021 struct section_file *secfile;
3022 struct section *pother;
3023
3024 SECFILE_RETURN_VAL_IF_FAIL(nullptr, psection, psection != nullptr, FALSE);
3025 secfile = psection->secfile;
3026 SECFILE_RETURN_VAL_IF_FAIL(secfile, psection, secfile != nullptr, FALSE);
3027
3028 if (name == nullptr || '\0' == name[0]) {
3029 SECFILE_LOG(secfile, psection, "No new name for section \"%s\".",
3030 psection->name);
3031 return FALSE;
3032 }
3033
3035 SECFILE_LOG(secfile, psection,
3036 "\"%s\" is not a valid section name for section \"%s\".",
3037 name, psection->name);
3038 return FALSE;
3039 }
3040
3042 && pother != psection) {
3043 /* We cannot duplicate sections in any case! */
3044 SECFILE_LOG(secfile, psection, "Section \"%s\" already exists.", name);
3045 return FALSE;
3046 }
3047
3048 /* Remove old references in the hash tables. */
3049 if (secfile->hash.sections != nullptr) {
3051 }
3052 if (secfile->hash.entries != nullptr) {
3053 entry_list_iterate(psection->entries, pentry) {
3056 }
3057
3058 /* Really rename. */
3059 free(psection->name);
3060 psection->name = fc_strdup(name);
3061
3062 /* Reinsert new references into the hash tables. */
3063 if (secfile->hash.sections != nullptr) {
3064 section_hash_insert(secfile->hash.sections, psection->name, psection);
3065 }
3066 if (secfile->hash.entries != nullptr) {
3067 entry_list_iterate(psection->entries, pentry) {
3070 }
3071
3072 return TRUE;
3073}
3074
3075/**********************************************************************/
3079const struct entry_list *section_entries(const struct section *psection)
3080{
3081 return (psection != nullptr ? psection->entries : nullptr);
3082}
3083
3084/**********************************************************************/
3088 const char *name)
3089{
3090 SECFILE_RETURN_VAL_IF_FAIL(nullptr, psection, psection != nullptr, nullptr);
3091
3093 if (0 == strcmp(entry_name(pentry), name)) {
3095 return pentry;
3096 }
3098
3099 return nullptr;
3100}
3101
3102/**********************************************************************/
3106 const char *path, ...)
3107{
3109 struct entry *pentry;
3110 va_list args;
3111
3112 SECFILE_RETURN_VAL_IF_FAIL(nullptr, psection, psection != nullptr, nullptr);
3113
3114 va_start(args, path);
3115 fc_vsnprintf(fullpath, sizeof(fullpath), path, args);
3116 va_end(args);
3117
3119 return pentry;
3120 }
3121
3122 /* Try with full path. */
3124 && psection == entry_section(pentry)) {
3125 /* Ensured this is really owned by this section. */
3126 return pentry;
3127 }
3128
3129 return nullptr;
3130}
3131
3132/**********************************************************************/
3135static struct entry *entry_new(struct section *psection, const char *name)
3136{
3137 struct section_file *secfile;
3138 struct entry *pentry;
3139 bool long_comment = !strcmp("#", name);
3140
3141 SECFILE_RETURN_VAL_IF_FAIL(nullptr, psection, psection != nullptr, nullptr);
3142
3143 secfile = psection->secfile;
3144 if (name == nullptr || '\0' == name[0]) {
3145 SECFILE_LOG(secfile, psection, "Cannot create an entry without name.");
3146 return nullptr;
3147 }
3148
3150 SECFILE_LOG(secfile, psection, "\"%s\" is not a valid entry name.",
3151 name);
3152 return nullptr;
3153 }
3154
3155 if (!secfile->allow_duplicates
3156 && section_entry_by_name(psection, name) != nullptr) {
3157 SECFILE_LOG(secfile, psection, "Entry \"%s\" already exists.", name);
3158 return nullptr;
3159 }
3160
3161 pentry = fc_malloc(sizeof(struct entry));
3162 if (long_comment) {
3163 pentry->name = nullptr;
3164 } else {
3165 pentry->name = fc_strdup(name);
3166 }
3167 pentry->type = -1; /* Invalid case. */
3168 pentry->used = 0;
3169 pentry->comment = nullptr;
3170
3171 /* Append to section. */
3172 pentry->psection = psection;
3174
3175 /* Notify secfile. */
3176 secfile->num_entries++;
3177 secfile_hash_insert(secfile, pentry);
3178
3179 return pentry;
3180}
3181
3182/**********************************************************************/
3186 const char *name, int value)
3187{
3188 struct entry *pentry = entry_new(psection, name);
3189
3190 if (pentry != nullptr) {
3191 pentry->type = ENTRY_INT;
3192 pentry->integer.value = value;
3193 }
3194
3195 return pentry;
3196}
3197
3198/**********************************************************************/
3202 const char *name, bool value)
3203{
3204 struct entry *pentry = entry_new(psection, name);
3205
3206 if (pentry != nullptr) {
3207 pentry->type = ENTRY_BOOL;
3208 pentry->boolean.value = value;
3209 }
3210
3211 return pentry;
3212}
3213
3214/**********************************************************************/
3218 const char *name, float value)
3219{
3220 struct entry *pentry = entry_new(psection, name);
3221
3222 if (pentry != nullptr) {
3223 pentry->type = ENTRY_FLOAT;
3224 pentry->floating.value = value;
3225 }
3226
3227 return pentry;
3228}
3229
3230/**********************************************************************/
3234 const char *name, const char *value,
3235 bool escaped)
3236{
3237 struct entry *pentry = entry_new(psection, name);
3238
3239 if (pentry != nullptr) {
3240 pentry->type = ENTRY_STR;
3241 pentry->string.value = fc_strdup(value != nullptr ? value : "");
3242 pentry->string.escaped = escaped;
3243 pentry->string.raw = FALSE;
3244 pentry->string.gt_marking = FALSE;
3245 }
3246
3247 return pentry;
3248}
3249
3250/**********************************************************************/
3254 const char *name,
3255 const char *value)
3256{
3257 struct entry *pentry = entry_new(psection, name);
3258
3259 if (pentry != nullptr) {
3261 pentry->string.value = fc_strdup(value != nullptr ? value : "");
3262 }
3263
3264 return pentry;
3265}
3266
3267/**********************************************************************/
3271 const char *comment)
3272{
3273 struct entry *pentry = entry_new(psection, "#");
3274
3275 if (pentry != nullptr) {
3276 pentry->type = ENTRY_LONG_COMMENT;
3277 pentry->comment = fc_strdup(comment != nullptr ? comment : "");
3278 }
3279
3280 return pentry;
3281}
3282
3283/**********************************************************************/
3287{
3288 struct section_file *secfile;
3289 struct section *psection;
3290
3291 if (pentry == nullptr) {
3292 return;
3293 }
3294
3295 if ((psection = pentry->psection)) {
3296 /* Detach from section. */
3297 if (entry_list_remove(psection->entries, pentry)) {
3298 /* This has called entry_destroy() already then. */
3299 return;
3300 }
3301 if ((secfile = psection->secfile)) {
3302 /* Detach from secfile. */
3305 }
3306 }
3307
3308 /* Specific type free. */
3309 switch (pentry->type) {
3310 case ENTRY_BOOL:
3311 case ENTRY_INT:
3312 case ENTRY_FLOAT:
3313 case ENTRY_LONG_COMMENT:
3314 break;
3315
3316 case ENTRY_STR:
3318 free(pentry->string.value);
3319 break;
3320
3321 case ENTRY_ILLEGAL:
3322 fc_assert(pentry->type != ENTRY_ILLEGAL);
3323 break;
3324 }
3325
3326 /* Common free. */
3327 free(pentry->name);
3328 if (pentry->comment != nullptr) {
3329 free(pentry->comment);
3330 }
3331 free(pentry);
3332}
3333
3334/**********************************************************************/
3337struct section *entry_section(const struct entry *pentry)
3338{
3339 return (pentry != nullptr ? pentry->psection : nullptr);
3340}
3341
3342/**********************************************************************/
3346{
3347 return (pentry != nullptr ? pentry->type : ENTRY_ILLEGAL);
3348}
3349
3350/**********************************************************************/
3353int entry_path(const struct entry *pentry, char *buf, size_t buf_len)
3354{
3355 return fc_snprintf(buf, buf_len, "%s.%s",
3358}
3359
3360/**********************************************************************/
3363const char *entry_name(const struct entry *pentry)
3364{
3365 return (pentry != nullptr ? pentry->name : nullptr);
3366}
3367
3368/**********************************************************************/
3371bool entry_set_name(struct entry *pentry, const char *name)
3372{
3373 struct section *psection;
3374 struct section_file *secfile;
3375
3376 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3377 psection = pentry->psection;
3378 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, psection != nullptr, FALSE);
3379 secfile = psection->secfile;
3380 SECFILE_RETURN_VAL_IF_FAIL(nullptr, psection, secfile != nullptr, FALSE);
3381
3382 if (name == nullptr || '\0' == name[0]) {
3383 SECFILE_LOG(secfile, psection, "No new name for entry \"%s\".",
3384 pentry->name);
3385 return FALSE;
3386 }
3387
3389 SECFILE_LOG(secfile, psection,
3390 "\"%s\" is not a valid entry name for entry \"%s\".",
3391 name, pentry->name);
3392 return FALSE;
3393 }
3394
3395 if (!secfile->allow_duplicates) {
3397
3398 if (pother != nullptr && pother != pentry) {
3399 SECFILE_LOG(secfile, psection, "Entry \"%s\" already exists.", name);
3400 return FALSE;
3401 }
3402 }
3403
3404 /* Remove from hash table the old path. */
3405 secfile_hash_delete(secfile, pentry);
3406
3407 /* Really rename the entry. */
3408 free(pentry->name);
3409 pentry->name = fc_strdup(name);
3410
3411 /* Insert into hash table the new path. */
3412 secfile_hash_insert(secfile, pentry);
3413 return TRUE;
3414}
3415
3416/**********************************************************************/
3419const char *entry_comment(const struct entry *pentry)
3420{
3421 return (pentry != nullptr ? pentry->comment : nullptr);
3422}
3423
3424/**********************************************************************/
3427void entry_set_comment(struct entry *pentry, const char *comment)
3428{
3429 if (pentry == nullptr) {
3430 return;
3431 }
3432
3433 if (pentry->comment != nullptr) {
3434 free(pentry->comment);
3435 }
3436
3437 pentry->comment = (comment != nullptr ? fc_strdup(comment) : nullptr);
3438}
3439
3440/**********************************************************************/
3443static inline bool entry_used(const struct entry *pentry)
3444{
3445 return (0 < pentry->used);
3446}
3447
3448/**********************************************************************/
3451static inline void entry_use(struct entry *pentry)
3452{
3453 pentry->used++;
3454}
3455
3456/**********************************************************************/
3460bool entry_bool_get(const struct entry *pentry, bool *value)
3461{
3462 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3463
3464 if (ENTRY_INT == pentry->type
3465 && (pentry->integer.value == 0
3466 || pentry->integer.value == 1)
3467 && pentry->psection != nullptr
3468 && pentry->psection->secfile != nullptr
3469 && pentry->psection->secfile->allow_digital_boolean) {
3470 *value = (0 != pentry->integer.value);
3471 return TRUE;
3472 }
3473
3474 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection != nullptr
3475 ? pentry->psection->secfile : nullptr,
3476 pentry->psection,
3477 ENTRY_BOOL == pentry->type, FALSE);
3478
3479 if (value != nullptr) {
3480 *value = pentry->boolean.value;
3481 }
3482
3483 return TRUE;
3484}
3485
3486/**********************************************************************/
3490{
3491 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3492 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection->secfile, pentry->psection,
3493 ENTRY_BOOL == pentry->type, FALSE);
3494
3495 pentry->boolean.value = value;
3496 return TRUE;
3497}
3498
3499/**********************************************************************/
3502bool entry_float_get(const struct entry *pentry, float *value)
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_FLOAT == pentry->type, FALSE);
3507
3508 if (value != nullptr) {
3509 *value = pentry->floating.value;
3510 }
3511
3512 return TRUE;
3513}
3514
3515/**********************************************************************/
3518bool entry_float_set(struct entry *pentry, float value)
3519{
3520 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3521 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection->secfile, pentry->psection,
3522 ENTRY_FLOAT == pentry->type, FALSE);
3523
3524 pentry->floating.value = value;
3525
3526 return TRUE;
3527}
3528
3529/**********************************************************************/
3532bool entry_int_get(const struct entry *pentry, int *value)
3533{
3534 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3535 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection->secfile, pentry->psection,
3536 ENTRY_INT == pentry->type, FALSE);
3537
3538 if (value != nullptr) {
3539 *value = pentry->integer.value;
3540 }
3541
3542 return TRUE;
3543}
3544
3545/**********************************************************************/
3549{
3550 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3551 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection->secfile, pentry->psection,
3552 ENTRY_INT == pentry->type, FALSE);
3553
3554 pentry->integer.value = value;
3555 return TRUE;
3556}
3557
3558/**********************************************************************/
3561bool entry_str_get(const struct entry *pentry, const char **value)
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_STR == pentry->type, FALSE);
3566
3567 if (value != nullptr) {
3568 *value = pentry->string.value;
3569 }
3570
3571 return TRUE;
3572}
3573
3574/**********************************************************************/
3577bool entry_str_set(struct entry *pentry, const char *value)
3578{
3579 char *old_val;
3580
3581 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3582 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection->secfile, pentry->psection,
3583 ENTRY_STR == pentry->type, FALSE);
3584
3585 /* We free() old value only after we've placed the new one, to
3586 * support secfile_replace_str_vec() calls that want to keep some of
3587 * the entries from the old vector in the new one. We don't want
3588 * to lose the entry in between. */
3589 old_val = pentry->string.value;
3590 pentry->string.value = fc_strdup(value != nullptr ? value : "");
3591 free(old_val);
3592
3593 return TRUE;
3594}
3595
3596/**********************************************************************/
3599bool entry_str_escaped(const struct entry *pentry)
3600{
3601 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3602 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection->secfile, pentry->psection,
3603 ENTRY_STR == pentry->type, FALSE);
3604
3605 return pentry->string.escaped;
3606}
3607
3608/**********************************************************************/
3612{
3613 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3614 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection->secfile, pentry->psection,
3615 ENTRY_STR == pentry->type, FALSE);
3616
3617 pentry->string.escaped = escaped;
3618
3619 return TRUE;
3620}
3621
3622/**********************************************************************/
3626{
3627 SECFILE_RETURN_VAL_IF_FAIL(nullptr, nullptr, pentry != nullptr, FALSE);
3628 SECFILE_RETURN_VAL_IF_FAIL(pentry->psection->secfile, pentry->psection,
3629 ENTRY_STR == pentry->type, FALSE);
3630
3631 pentry->string.gt_marking = gt_marking;
3632
3633 return TRUE;
3634}
3635
3636/**********************************************************************/
3639static void entry_to_file(const struct entry *pentry, fz_FILE *fs)
3640{
3641 static char buf[8192];
3642 char *dot = nullptr;
3643 int i;
3644
3645 switch (pentry->type) {
3646 case ENTRY_BOOL:
3647 fz_fprintf(fs, "%s", pentry->boolean.value ? "TRUE" : "FALSE");
3648 break;
3649 case ENTRY_INT:
3650 fz_fprintf(fs, "%d", pentry->integer.value);
3651 break;
3652 case ENTRY_FLOAT:
3653 snprintf(buf, sizeof(buf), "%f", pentry->floating.value);
3654 for (i = 0; buf[i] != '\0' ; i++) {
3655 if (buf[i] == '.') {
3656 dot = &(buf[i]);
3657 break;
3658 }
3659 }
3660 if (dot == nullptr) {
3661 /* There's no '.' so it would seem like a integer value when loaded.
3662 * Force it not to look like an integer by adding ".0" */
3663 fz_fprintf(fs, "%s.0", buf);
3664 } else {
3665 fz_fprintf(fs, "%s", buf);
3666 }
3667 break;
3668 case ENTRY_STR:
3669 if (pentry->string.escaped) {
3670 make_escapes(pentry->string.value, buf, sizeof(buf));
3671 if (pentry->string.gt_marking) {
3672 fz_fprintf(fs, "_(\"%s\")", buf);
3673 } else {
3674 fz_fprintf(fs, "\"%s\"", buf);
3675 }
3676 } else if (pentry->string.raw) {
3677 fz_fprintf(fs, "%s", pentry->string.value);
3678 } else {
3679 fz_fprintf(fs, "$%s$", pentry->string.value);
3680 }
3681 break;
3683 fz_fprintf(fs, "*%s*", pentry->string.value);
3684 break;
3685 case ENTRY_LONG_COMMENT:
3686 fz_fprintf(fs, "%s\n", pentry->comment);
3687 break;
3688 case ENTRY_ILLEGAL:
3689 fc_assert(pentry->type != ENTRY_ILLEGAL);
3690 break;
3691 }
3692}
3693
3694/**********************************************************************/
3697static void entry_from_inf_token(struct section *psection, const char *name,
3698 const char *tok, struct inputfile *inf)
3699{
3701 log_error("%s", inf_log_str(inf, "Entry value not recognized: %s", tok));
3702 }
3703}
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:2956
#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_str_vec_full(struct section_file *secfile, const char *const *strings, size_t dim, const char *comment, bool allow_replace, bool no_escape, 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,...)
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