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