Freeciv-3.3: /fast/freeciv/nightly/main/utility/inputfile.c Source File

Freeciv-3.3

Loading...
Searching...
No Matches
inputfile.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 A low-level object for reading a registry-format file.
16 original author: David Pfitzner <dwp@mso.anu.edu.au>
17
18 This module implements an object which is useful for reading/parsing
19 a file in the registry format of registry.c. It takes care of the
20 low-level file-reading details, and provides functions to return
21 specific "tokens" from the file. Probably this should really use
22 higher-level tools... (flex/lex bison/yacc?)
23
24 When the user tries to read a token, we return a (const char*)
25 pointing to some data if the token was found, or nullptr otherwise.
26 The data pointed to should not be modified. The retuned pointer
27 is valid _only_ until another inputfile is performed. (So should
28 be used immediately, or fc_strdup-ed etc.)
29
30 The tokens recognised are as follows:
31 (Single quotes are delimiters used here, but are not part of the
32 actual tokens/strings.)
33 Most tokens can be preceeded by optional whitespace; exceptions
34 are section_name and entry_name.
35
36 section_name: '[foo]'
37 returned token: 'foo'
38
39 entry_name: 'foo =' (optional whitespace allowed before '=')
40 returned token: 'foo'
41
42 end_of_line: newline, or optional '#' or ';' (comment characters)
43 followed by any other chars, then newline.
44 returned token: should not be used except to check non-nullptr.
45
46 table_start: '{'
47 returned token: should not be used except to check non-nullptr.
48
49 table_end: '}'
50 returned token: should not be used except to check non-nullptr.
51
52 comma: literal ','
53 returned token: should not be used except to check non-nullptr.
54
55 value: a signed integer, or a double-quoted string, or a
56 gettext-marked double quoted string. Strings _may_ contain
57 raw embedded newlines, and escaped doublequotes, or \.
58 eg: '123', '-999', '"foo"', '_("foo")'
59 returned token: string containing number, for numeric, or string
60 starting at first doublequote for strings, but ommiting
61 trailing double-quote. Note this does _not_ translate
62 escaped doublequotes etc back to normal.
63
64***********************************************************************/
65
66#ifdef HAVE_CONFIG_H
67#include <fc_config.h>
68#endif
69
70#include <stdarg.h>
71#include <stdio.h>
72#include <string.h>
73
74/* utility */
75#include "astring.h"
76#include "fcintl.h"
77#include "ioz.h"
78#include "log.h"
79#include "mem.h"
80#include "shared.h" /* TRUE, FALSE */
81#include "support.h"
82
83#include "inputfile.h"
84
85#define INF_DEBUG_FOUND FALSE
86#define INF_DEBUG_NOT_FOUND FALSE
87
88#define INF_MAGIC (0xabdc0132) /* Arbitrary */
89
90 struct inputfile {
91 unsigned int magic; /* Memory check */
92 char *filename; /* Filename as passed to fopen */
93 fz_FILE *fp; /* Read from this */
94 bool at_eof; /* Flag for end-of-file */
95 struct astring cur_line; /* Data from current line */
96 unsigned int cur_line_pos; /* Position in current line */
97 unsigned int line_num; /* Line number from file in cur_line */
98 struct astring token; /* Data returned to user */
99 struct astring partial; /* Used in accumulating multi-line strings;
100 used only in get_token_value, but put
101 here so it gets freed when file closed */
102 datafilename_fn_t datafn; /* Function like datafilename(); use a
103 function pointer just to keep this
104 inputfile module "generic" */
105 bool in_string; /* Set when reading multi-line strings,
106 to know not to handle *include at start
107 of line as include mechanism */
108 int string_start_line; /* When in_string is true, this is the
109 start line of current string */
110 struct inputfile *included_from; /* nullptr for toplevel file, otherwise
111 points back to files which this one
112 has been included from */
113};
114
115/* A function to get a specific token type: */
116 typedef const char *(*get_token_fn_t)(struct inputfile *inf);
117
118static const char *get_token_section_name(struct inputfile *inf);
119static const char *get_token_entry_name(struct inputfile *inf);
120static const char *get_token_eol(struct inputfile *inf);
121static const char *get_token_table_start(struct inputfile *inf);
122static const char *get_token_table_end(struct inputfile *inf);
123static const char *get_token_comma(struct inputfile *inf);
124static const char *get_token_value(struct inputfile *inf);
125
126static struct {
127 const char *name;
128 get_token_fn_t func;
129}
130 tok_tab[INF_TOK_LAST] =
131{
132 { "section_name", get_token_section_name },
133 { "entry_name", get_token_entry_name },
134 { "end_of_line", get_token_eol },
135 { "table_start", get_token_table_start },
136 { "table_end", get_token_table_end },
137 { "comma", get_token_comma },
138 { "value", get_token_value },
139};
140
141static bool read_a_line(struct inputfile *inf);
142
143#define inf_log(inf, level, message, ...) \
144 if (log_do_output_for_level(level)) { \
145 do_log(__FILE__, __FUNCTION__, __FC_LINE__, FALSE, level, "%s", \
146 inf_log_str(inf, message, ## __VA_ARGS__)); \
147 }
148#define inf_warn(inf, message) \
149 inf_log(inf, LOG_NORMAL, "%s", message);
150
151/*******************************************************************/
154 static bool is_comment(int c)
155{
156 return (c == '#' || c == ';');
157}
158
159/*******************************************************************/
163 static void init_zeros(struct inputfile *inf)
164{
165 fc_assert_ret(inf != nullptr);
166
167 inf->magic = INF_MAGIC;
168 inf->filename = nullptr;
169 inf->fp = nullptr;
170 inf->datafn = nullptr;
171 inf->included_from = nullptr;
172 inf->line_num = inf->cur_line_pos = 0;
173 inf->at_eof = inf->in_string = FALSE;
174 inf->string_start_line = 0;
175 astr_init(&inf->cur_line);
176 astr_init(&inf->token);
177 astr_init(&inf->partial);
178}
179
180/*******************************************************************/
183 static bool inf_sanity_check(struct inputfile *inf)
184{
185 fc_assert_ret_val(inf != nullptr, FALSE);
186 fc_assert_ret_val(INF_MAGIC == inf->magic, FALSE);
187 fc_assert_ret_val(inf->fp != nullptr, FALSE);
188 fc_assert_ret_val(!inf->at_eof
189 || inf->at_eof, FALSE);
190 fc_assert_ret_val(!inf->in_string
191 || inf->in_string, FALSE);
192
193#ifdef FREECIV_DEBUG
194 fc_assert_ret_val(0 <= inf->string_start_line, FALSE);
195 if (inf->included_from && !inf_sanity_check(inf->included_from)) {
196 return FALSE;
197 }
198#endif /* FREECIV_DEBUG */
199
200 return TRUE;
201}
202
203/*******************************************************************/
207 static const char *inf_filename(struct inputfile *inf)
208{
209 if (inf->filename) {
210 return inf->filename;
211 } else {
212 return "(anonymous)";
213 }
214}
215
216/*******************************************************************/
220 struct inputfile *inf_from_file(const char *filename,
221 datafilename_fn_t datafn)
222{
223 struct inputfile *inf;
224 fz_FILE *fp;
225
226 fc_assert_ret_val(filename != nullptr, nullptr);
227 fc_assert_ret_val(0 < strlen(filename), nullptr);
228
229 fp = fz_from_file(filename, "r", -1, 0);
230 if (fp == nullptr) {
231 return nullptr;
232 }
233 log_debug("inputfile: opened \"%s\" ok", filename);
234 inf = inf_from_stream(fp, datafn);
235 inf->filename = fc_strdup(filename);
236
237 return inf;
238}
239
240/*******************************************************************/
244 struct inputfile *inf_from_stream(fz_FILE *stream, datafilename_fn_t datafn)
245{
246 struct inputfile *inf;
247
248 fc_assert_ret_val(stream != nullptr, nullptr);
249
250 inf = fc_malloc(sizeof(*inf));
251 init_zeros(inf);
252
253 inf->filename = nullptr;
254 inf->fp = stream;
255 inf->datafn = datafn;
256
257 log_debug("inputfile: opened \"%s\" ok", inf_filename(inf));
258
259 return inf;
260}
261
262/*******************************************************************/
268 static void inf_close_partial(struct inputfile *inf)
269{
270 inf_sanity_check(inf);
271
272 log_debug("inputfile: sub-closing \"%s\"", inf_filename(inf));
273
274 if (fz_ferror(inf->fp) != 0) {
275 log_error("Error before closing %s: %s", inf_filename(inf),
276 fz_strerror(inf->fp));
277 fz_fclose(inf->fp);
278 inf->fp = nullptr;
279 } else if (fz_fclose(inf->fp) != 0) {
280 log_error("Error closing %s", inf_filename(inf));
281 }
282 if (inf->filename) {
283 free(inf->filename);
284 }
285 inf->filename = nullptr;
286 astr_free(&inf->cur_line);
287 astr_free(&inf->token);
288 astr_free(&inf->partial);
289
290 /* Assign zeros for safety if accidently reuse etc: */
291 init_zeros(inf);
292 inf->magic = ~INF_MAGIC;
293
294 log_debug("inputfile: sub-closed ok");
295}
296
297/*******************************************************************/
303 void inf_close(struct inputfile *inf)
304{
305 inf_sanity_check(inf);
306
307 log_debug("inputfile: closing \"%s\"", inf_filename(inf));
308
309 if (inf->included_from) {
310 inf_close(inf->included_from);
311 /* Stop anything from recursing to already closed including file */
312 inf->included_from = nullptr;
313 }
314 inf_close_partial(inf);
315 free(inf);
316
317 log_debug("inputfile: closed ok");
318}
319
320/*******************************************************************/
323 static bool have_line(struct inputfile *inf)
324{
325 if (!inf_sanity_check(inf)) {
326 return FALSE;
327 }
328
329 return !astr_empty(&inf->cur_line);
330}
331
332/*******************************************************************/
335 static bool at_eol(struct inputfile *inf)
336{
337 if (!inf_sanity_check(inf)) {
338 return TRUE;
339 }
340
341 fc_assert_ret_val(inf->cur_line_pos <= astr_len(&inf->cur_line), TRUE);
342
343 return (inf->cur_line_pos >= astr_len(&inf->cur_line));
344}
345
346/*******************************************************************/
349 bool inf_at_eof(struct inputfile *inf)
350{
351 if (!inf_sanity_check(inf)) {
352 return TRUE;
353 }
354
355 return inf->at_eof;
356}
357
358/*******************************************************************/
366 static bool check_include(struct inputfile *inf)
367{
368 const char *include_prefix = "*include";
369 static size_t len = 0;
370 size_t bare_name_len;
371 char *bare_name;
372 const char *c, *bare_name_start, *full_name;
373 struct inputfile *new_inf, temp;
374
375 if (len == 0) {
376 len = strlen(include_prefix);
377 }
378
379 if (!inf_sanity_check(inf)) {
380 return FALSE;
381 }
382
383 if (inf->in_string || astr_len(&inf->cur_line) <= len
384 || inf->cur_line_pos > 0) {
385 return FALSE;
386 }
387
388 if (fc_strncmp(astr_str(&inf->cur_line), include_prefix, len)) {
389 return FALSE;
390 }
391
392 /* From here, the include-line must be well formed.
393 * Keep inf->cur_line_pos accurate just so error messages are useful */
394
395 /* Skip any whitespace: */
396 inf->cur_line_pos = len;
397 c = astr_str(&inf->cur_line) + len;
398 while (*c != '\0' && fc_isspace(*c)) {
399 c++;
400 }
401
402 if (*c != '\"') {
403 inf_log(inf, LOG_ERROR,
404 "Did not find opening doublequote for '*include' line");
405 return FALSE;
406 }
407 c++;
408 inf->cur_line_pos = c - astr_str(&inf->cur_line);
409
410 bare_name_start = c;
411 while (*c != '\0' && *c != '\"') c++;
412 if (*c != '\"') {
413 inf_log(inf, LOG_ERROR,
414 "Did not find closing doublequote for '*include' line");
415 return FALSE;
416 }
417 c++;
418 bare_name_len = c - bare_name_start;
419 bare_name = fc_malloc(bare_name_len);
420 strncpy(bare_name, bare_name_start, bare_name_len - 1);
421 bare_name[bare_name_len - 1] = '\0';
422 inf->cur_line_pos = c - astr_str(&inf->cur_line);
423
424 /* Check rest of line is well-formed: */
425 while (*c != '\0' && fc_isspace(*c) && !is_comment(*c)) {
426 c++;
427 }
428 if (!(*c == '\0' || is_comment(*c))) {
429 inf_log(inf, LOG_ERROR, "Junk after filename for '*include' line");
430 return FALSE;
431 }
432 inf->cur_line_pos = astr_len(&inf->cur_line) - 1;
433
434 full_name = inf->datafn(bare_name);
435 if (!full_name) {
436 log_error("Could not find included file \"%s\"", bare_name);
437 free(bare_name);
438 return FALSE;
439 }
440 free(bare_name);
441
442 /* Avoid recursion: (First filename may not have the same path,
443 * but will at least stop infinite recursion) */
444 {
445 struct inputfile *inc = inf;
446 do {
447 if (inc->filename && strcmp(full_name, inc->filename) == 0) {
448 log_error("Recursion trap on '*include' for \"%s\"", full_name);
449 return FALSE;
450 }
451 } while ((inc = inc->included_from));
452 }
453
454 new_inf = inf_from_file(full_name, inf->datafn);
455
456 /* Swap things around so that memory pointed to by inf (user pointer,
457 and pointer in calling functions) contains the new inputfile,
458 and newly allocated memory for new_inf contains the old inputfile.
459 This is pretty scary, lets hope it works...
460 */
461 temp = *new_inf;
462 *new_inf = *inf;
463 *inf = temp;
464 inf->included_from = new_inf;
465
466 return TRUE;
467}
468
469/*******************************************************************/
475 static bool read_a_line(struct inputfile *inf)
476{
477 struct astring *line;
478 char *ret;
479 int pos;
480
481 if (!inf_sanity_check(inf)) {
482 return FALSE;
483 }
484
485 if (inf->at_eof) {
486 return FALSE;
487 }
488
489 /* Abbreviation: */
490 line = &inf->cur_line;
491
492 /* Minimum initial line length: */
493 astr_reserve(line, 80);
494 astr_clear(line);
495 pos = 0;
496
497 /* Read until we get a full line:
498 * At start of this loop, pos is index to trailing null
499 * (or first position) in line.
500 */
501 for (;;) {
502 ret = fz_fgets((char *) astr_str(line) + pos,
503 astr_capacity(line) - pos, inf->fp);
504
505 if (!ret) {
506 /* fgets failed */
507 if (pos > 0) {
508 inf_log(inf, LOG_ERROR, _("End-of-file not in line of its own"));
509 }
510 inf->at_eof = TRUE;
511 if (inf->in_string) {
512 /* Note: Don't allow multi-line strings to cross "include"
513 * boundaries */
514 inf_log(inf, LOG_ERROR, "Multi-line string went to end-of-file");
515 return FALSE;
516 }
517 break;
518 }
519
520 /* Cope with \n\r line endings if not caught by library:
521 * strip off any leading \r */
522 if (0 == pos && 0 < astr_len(line) && astr_str(line)[0] == '\r') {
523 memmove((char *)astr_str(line), astr_str(line)+1, astr_len(line));
524 }
525
526 pos = astr_len(line);
527
528 if (0 < pos && astr_str(line)[pos - 1] == '\n') {
529 int end;
530 /* Cope with \r\n line endings if not caught by library:
531 * strip off any trailing \r */
532 if (1 < pos && astr_str(line)[pos - 2] == '\r') {
533 end = pos - 2;
534 } else {
535 end = pos - 1;
536 }
537 *((char *) astr_str(line) + end) = '\0';
538 break;
539 }
540 astr_reserve(line, pos * 2);
541 }
542
543 if (!inf->at_eof) {
544 inf->line_num++;
545 inf->cur_line_pos = 0;
546
547 if (check_include(inf)) {
548 return read_a_line(inf);
549 }
550 return TRUE;
551 } else {
552 astr_clear(line);
553 if (inf->included_from) {
554 /* Pop the include, and get next line from file above instead. */
555 struct inputfile *inc = inf->included_from;
556 inf_close_partial(inf);
557 *inf = *inc; /* So the user pointer in still valid
558 * (and inf pointers in calling functions) */
559 free(inc);
560 return read_a_line(inf);
561 }
562 return FALSE;
563 }
564}
565
566/*******************************************************************/
571 char *inf_log_str(struct inputfile *inf, const char *message, ...)
572{
573 va_list args;
574 static char str[512];
575
576 if (message) {
577 va_start(args, message);
578 fc_vsnprintf(str, sizeof(str), message, args);
579 va_end(args);
580 sz_strlcat(str, "\n");
581 } else {
582 str[0] = '\0';
583 }
584
585 if (inf_sanity_check(inf)) {
586 cat_snprintf(str, sizeof(str), " file \"%s\", line %d, pos %d%s",
587 inf_filename(inf), inf->line_num, inf->cur_line_pos,
588 (inf->at_eof ? ", EOF" : ""));
589
590 if (!astr_empty(&inf->cur_line)) {
591 cat_snprintf(str, sizeof(str), "\n looking at: '%s'",
592 astr_str(&inf->cur_line) + inf->cur_line_pos);
593 }
594 if (inf->in_string) {
595 cat_snprintf(str, sizeof(str),
596 "\n processing string starting at line %d",
597 inf->string_start_line);
598 }
599 while ((inf = inf->included_from)) { /* Local pointer assignment */
600 cat_snprintf(str, sizeof(str), "\n included from file \"%s\", line %d",
601 inf_filename(inf), inf->line_num);
602 }
603 }
604
605 return str;
606}
607
608/*******************************************************************/
611 const char *inf_token(struct inputfile *inf, enum inf_token_type type)
612{
613 const char *c;
614 const char *name;
615 get_token_fn_t func;
616
617 if (!inf_sanity_check(inf)) {
618 return nullptr;
619 }
620
621 fc_assert_ret_val(INF_TOK_FIRST <= type && INF_TOK_LAST > type,
622 nullptr);
623
624 name = tok_tab[type].name ? tok_tab[type].name : "(unnamed)";
625 func = tok_tab[type].func;
626
627 if (!func) {
628 log_error("token type %d (%s) not supported yet", type, name);
629 c = nullptr;
630 } else {
631 while (!have_line(inf) && read_a_line(inf)) {
632 /* Nothing. */
633 }
634 if (!have_line(inf)) {
635 c = nullptr;
636 } else {
637 c = func(inf);
638 }
639 }
640 if (c && INF_DEBUG_FOUND) {
641 log_debug("inputfile: found %s '%s'", name, astr_str(&inf->token));
642 }
643 return c;
644}
645
646/*******************************************************************/
650 int inf_discard_tokens(struct inputfile *inf, enum inf_token_type type)
651{
652 int count = 0;
653
654 while (inf_token(inf, type)) {
655 count++;
656 }
657
658 return count;
659}
660
661/*******************************************************************/
666 static const char *get_token_section_name(struct inputfile *inf)
667{
668 const char *c, *start;
669
670 fc_assert_ret_val(have_line(inf), nullptr);
671
672 c = astr_str(&inf->cur_line) + inf->cur_line_pos;
673 if (*c++ != '[') {
674 return nullptr;
675 }
676 start = c;
677 while (*c != '\0' && *c != ']') {
678 c++;
679 }
680 if (*c != ']') {
681 return nullptr;
682 }
683 *((char *) c) = '\0'; /* Tricky. */
684 astr_set(&inf->token, "%s", start);
685 *((char *) c) = ']'; /* Revert. */
686 inf->cur_line_pos = c + 1 - astr_str(&inf->cur_line);
687
688 return astr_str(&inf->token);
689}
690
691/*******************************************************************/
695 static const char *get_token_entry_name(struct inputfile *inf)
696{
697 const char *c, *start, *end;
698 char trailing;
699
700 fc_assert_ret_val(have_line(inf), nullptr);
701
702 c = astr_str(&inf->cur_line) + inf->cur_line_pos;
703 while (*c != '\0' && fc_isspace(*c)) {
704 c++;
705 }
706 if (*c == '\0') {
707 return nullptr;
708 }
709 start = c;
710 while (*c != '\0' && !fc_isspace(*c) && *c != '=' && !is_comment(*c)) {
711 c++;
712 }
713 if (!(*c != '\0' && (fc_isspace(*c) || *c == '='))) {
714 return nullptr;
715 }
716 end = c;
717 while (*c != '\0' && *c != '=' && !is_comment(*c)) {
718 c++;
719 }
720 if (*c != '=') {
721 return nullptr;
722 }
723 trailing = *end;
724 *((char *) end) = '\0'; /* Tricky. */
725 astr_set(&inf->token, "%s", start);
726 *((char *) end) = trailing; /* Revert. */
727 inf->cur_line_pos = c + 1 - astr_str(&inf->cur_line);
728
729 return astr_str(&inf->token);
730}
731
732/*******************************************************************/
736 static const char *get_token_eol(struct inputfile *inf)
737{
738 const char *c;
739
740 fc_assert_ret_val(have_line(inf), nullptr);
741
742 if (!at_eol(inf)) {
743 c = astr_str(&inf->cur_line) + inf->cur_line_pos;
744 while (*c != '\0' && fc_isspace(*c)) {
745 c++;
746 }
747 if (*c != '\0' && !is_comment(*c)) {
748 return nullptr;
749 }
750 }
751
752 /* Finished with this line: say that we don't have it any more: */
753 astr_clear(&inf->cur_line);
754 inf->cur_line_pos = 0;
755
756 astr_set(&inf->token, " ");
757
758 return astr_str(&inf->token);
759}
760
761/*******************************************************************/
765 static const char *get_token_white_char(struct inputfile *inf,
766 char target)
767{
768 const char *c;
769
770 fc_assert_ret_val(have_line(inf), nullptr);
771
772 c = astr_str(&inf->cur_line) + inf->cur_line_pos;
773 while (*c != '\0' && fc_isspace(*c)) {
774 c++;
775 }
776 if (*c != target) {
777 return nullptr;
778 }
779 inf->cur_line_pos = c + 1 - astr_str(&inf->cur_line);
780 astr_set(&inf->token, "%c", target);
781
782 return astr_str(&inf->token);
783}
784
785/*******************************************************************/
788 static const char *get_token_table_start(struct inputfile *inf)
789{
790 return get_token_white_char(inf, '{');
791}
792
793/*******************************************************************/
796 static const char *get_token_table_end(struct inputfile *inf)
797{
798 return get_token_white_char(inf, '}');
799}
800
801/*******************************************************************/
804 static const char *get_token_comma(struct inputfile *inf)
805{
806 return get_token_white_char(inf, ',');
807}
808
809/*******************************************************************/
812 static const char *get_token_value(struct inputfile *inf)
813{
814 struct astring *partial;
815 const char *c, *start;
816 char trailing;
817 bool has_i18n_marking = FALSE;
818 char border_character = '\"';
819
820 fc_assert_ret_val(have_line(inf), nullptr);
821
822 c = astr_str(&inf->cur_line) + inf->cur_line_pos;
823 while (*c != '\0' && fc_isspace(*c)) {
824 c++;
825 }
826 if (*c == '\0') {
827 return nullptr;
828 }
829
830 if (*c == '-' || *c == '+' || fc_isdigit(*c)) {
831 /* A number: */
832 start = c++;
833 while (*c != '\0' && fc_isdigit(*c)) {
834 c++;
835 }
836 if (*c == '.') {
837 /* Float maybe */
838 c++;
839 while (*c != '\0' && fc_isdigit(*c)) {
840 c++;
841 }
842 }
843 /* Check that the trailing stuff is ok: */
844 if (!(*c == '\0' || *c == ',' || fc_isspace(*c) || is_comment(*c))) {
845 return nullptr;
846 }
847 /* If its a comma, we don't want to obliterate it permanently,
848 * so remember it: */
849 trailing = *c;
850 *((char *) c) = '\0'; /* Tricky. */
851
852 inf->cur_line_pos = c - astr_str(&inf->cur_line);
853 astr_set(&inf->token, "%s", start);
854
855 *((char *) c) = trailing; /* Revert. */
856 return astr_str(&inf->token);
857 }
858
859 /* Allow gettext marker: */
860 if (*c == '_' && *(c + 1) == '(') {
861 has_i18n_marking = TRUE;
862 c += 2;
863 while (*c != '\0' && fc_isspace(*c)) {
864 c++;
865 }
866 if (*c == '\0') {
867 return nullptr;
868 }
869 }
870
871 border_character = *c;
872
873 if (border_character == '*') {
874 const char *rfname;
875 fz_FILE *fp;
876 bool eof;
877 int pos;
878
879 c++;
880
881 start = c;
882 while (*c != '*') {
883 if (*c == '\0' || *c == '\n') {
884 return nullptr;
885 }
886 c++;
887 }
888 c++;
889 /* Check that the trailing stuff is ok: */
890 if (!(*c == '\0' || *c == ',' || fc_isspace(*c) || is_comment(*c))) {
891 return nullptr;
892 }
893 /* We don't want to obliterate ending '*' permanently,
894 * so remember it: */
895 trailing = *(c - 1);
896 *((char *) (c - 1)) = '\0'; /* Tricky. */
897
898 rfname = fileinfoname(get_data_dirs(), start);
899 if (rfname == nullptr) {
900 inf_log(inf, LOG_ERROR,
901 _("Cannot find stringfile \"%s\"."), start);
902 *((char *) c) = trailing; /* Revert. */
903 return nullptr;
904 }
905 *((char *) c) = trailing; /* Revert. */
906 fp = fz_from_file(rfname, "r", -1, 0);
907 if (!fp) {
908 inf_log(inf, LOG_ERROR,
909 _("Cannot open stringfile \"%s\"."), rfname);
910 return nullptr;
911 }
912 log_debug("Stringfile \"%s\" opened ok", start);
913 *((char *) (c - 1)) = trailing; /* Revert. */
914 astr_set(&inf->token, "*"); /* Mark as a string read from a file */
915
916 eof = FALSE;
917 pos = 1; /* Past 'filestring' marker */
918 while (!eof) {
919 char *ret;
920
921 ret = fz_fgets((char *) astr_str(&inf->token) + pos,
922 astr_capacity(&inf->token) - pos, fp);
923 if (ret == nullptr) {
924 eof = TRUE;
925 } else {
926 pos = astr_len(&inf->token);
927 astr_reserve(&inf->token, pos + 200);
928 }
929 }
930
931 fz_fclose(fp);
932
933 inf->cur_line_pos = c + 1 - astr_str(&inf->cur_line);
934
935 return astr_str(&inf->token);
936 } else if (border_character != '\"'
937 && border_character != '\''
938 && border_character != '$') {
939 /* A one-word string: maybe FALSE or TRUE. */
940 start = c;
941 while (fc_isalnum(*c)) {
942 c++;
943 }
944 /* Check that the trailing stuff is ok: */
945 if (!(*c == '\0' || *c == ',' || fc_isspace(*c) || is_comment(*c))) {
946 return nullptr;
947 }
948 /* If its a comma, we don't want to obliterate it permanently,
949 * so remember it: */
950 trailing = *c;
951 *((char *) c) = '\0'; /* Tricky. */
952
953 inf->cur_line_pos = c - astr_str(&inf->cur_line);
954 astr_set(&inf->token, "%s", start);
955
956 *((char *) c) = trailing; /* Revert. */
957
958 return astr_str(&inf->token);
959 }
960
961 /* From here, we know we have a string, we just have to find the
962 trailing (un-escaped) double-quote. We read in extra lines if
963 necessary to find it. If we _don't_ find the end-of-string
964 (that is, we come to end-of-file), we return nullptr, but we
965 leave the file in at_eof, and don't try to back-up to the
966 current point. (That would be more difficult, and probably
967 not necessary: at that point we probably have a malformed
968 string/file.)
969
970 As we read extra lines, the string value from previous
971 lines is placed in partial.
972 */
973
974 /* Prepare for possibly multi-line string: */
975 inf->string_start_line = inf->line_num;
976 inf->in_string = TRUE;
977
978 partial = &inf->partial; /* Abbreviation */
979 astr_clear(partial);
980
981 start = c++; /* Atart includes the initial \", to
982 * distinguish from a number */
983 for (;;) {
984 while (*c != '\0' && *c != border_character) {
985 /* Skip over escaped chars, including backslash-doublequote,
986 * and backslash-backslash: */
987 if (*c == '\\' && *(c + 1) != '\0') {
988 c++;
989 }
990 c++;
991 }
992
993 if (*c == border_character) {
994 /* Found end of string */
995 break;
996 }
997
998 astr_add(partial, "%s\n", start);
999
1000 if (!read_a_line(inf)) {
1001 /* Shouldn't happen */
1002 inf_log(inf, LOG_ERROR,
1003 "Bad return for multi-line string from read_a_line");
1004 return nullptr;
1005 }
1006 c = start = astr_str(&inf->cur_line);
1007 }
1008
1009 /* Found end of string */
1010 trailing = *c;
1011 *((char *) c) = '\0'; /* Tricky. */
1012
1013 inf->cur_line_pos = c + 1 - astr_str(&inf->cur_line);
1014 astr_set(&inf->token, "%s%s", astr_str(partial), start);
1015
1016 *((char *) c) = trailing; /* Revert. */
1017
1018 /* Check gettext tag at end: */
1019 if (has_i18n_marking) {
1020 if (*++c == ')') {
1021 inf->cur_line_pos++;
1022 } else {
1023 inf_warn(inf, "Missing end of i18n string marking");
1024 }
1025 }
1026 inf->in_string = FALSE;
1027
1028 return astr_str(&inf->token);
1029}
astr_free
void astr_free(struct astring *astr)
Definition astring.c:148
astr_set
void astr_set(struct astring *astr, const char *format,...)
Definition astring.c:251
astr_reserve
void astr_reserve(struct astring *astr, size_t n)
Definition astring.c:179
astr_clear
void astr_clear(struct astring *astr)
Definition astring.c:201
astr_init
void astr_init(struct astring *astr)
Definition astring.c:139
astr_add
void astr_add(struct astring *astr, const char *format,...)
Definition astring.c:271
str
#define str
Definition astring.c:76
astring.h
astr_capacity
static size_t astr_capacity(const struct astring *astr) fc__attribute((nonnull(1)))
Definition astring.h:117
astr_len
static size_t astr_len(const struct astring *astr) fc__attribute((nonnull(1)))
Definition astring.h:101
astr_str
static const char * astr_str(const struct astring *astr) fc__attribute((nonnull(1)))
Definition astring.h:93
astr_empty
static bool astr_empty(const struct astring *astr) fc__attribute((nonnull(1)))
Definition astring.h:125
incite_cost
char * incite_cost
Definition comments.c:74
fcintl.h
_
#define _(String)
Definition fcintl.h:67
pos
static struct tile * pos
Definition finddlg.c:53
type
GType type
Definition repodlgs.c:1313
tok_tab
static struct @0 tok_tab[INF_TOK_LAST]
get_token_value
static const char * get_token_value(struct inputfile *inf)
Definition inputfile.c:812
is_comment
static bool is_comment(int c)
Definition inputfile.c:154
get_token_entry_name
static const char * get_token_entry_name(struct inputfile *inf)
Definition inputfile.c:695
inf_log_str
char * inf_log_str(struct inputfile *inf, const char *message,...)
Definition inputfile.c:571
inf_sanity_check
static bool inf_sanity_check(struct inputfile *inf)
Definition inputfile.c:183
get_token_section_name
static const char * get_token_section_name(struct inputfile *inf)
Definition inputfile.c:666
inf_at_eof
bool inf_at_eof(struct inputfile *inf)
Definition inputfile.c:349
inf_from_file
struct inputfile * inf_from_file(const char *filename, datafilename_fn_t datafn)
Definition inputfile.c:220
have_line
static bool have_line(struct inputfile *inf)
Definition inputfile.c:323
init_zeros
static void init_zeros(struct inputfile *inf)
Definition inputfile.c:163
at_eol
static bool at_eol(struct inputfile *inf)
Definition inputfile.c:335
inf_from_stream
struct inputfile * inf_from_stream(fz_FILE *stream, datafilename_fn_t datafn)
Definition inputfile.c:244
name
const char * name
Definition inputfile.c:127
get_token_table_end
static const char * get_token_table_end(struct inputfile *inf)
Definition inputfile.c:796
get_token_white_char
static const char * get_token_white_char(struct inputfile *inf, char target)
Definition inputfile.c:765
inf_close
void inf_close(struct inputfile *inf)
Definition inputfile.c:303
INF_MAGIC
#define INF_MAGIC
Definition inputfile.c:88
check_include
static bool check_include(struct inputfile *inf)
Definition inputfile.c:366
get_token_table_start
static const char * get_token_table_start(struct inputfile *inf)
Definition inputfile.c:788
inf_log
#define inf_log(inf, level, message,...)
Definition inputfile.c:143
read_a_line
static bool read_a_line(struct inputfile *inf)
Definition inputfile.c:475
inf_discard_tokens
int inf_discard_tokens(struct inputfile *inf, enum inf_token_type type)
Definition inputfile.c:650
get_token_comma
static const char * get_token_comma(struct inputfile *inf)
Definition inputfile.c:804
get_token_fn_t
const char *(* get_token_fn_t)(struct inputfile *inf)
Definition inputfile.c:116
get_token_eol
static const char * get_token_eol(struct inputfile *inf)
Definition inputfile.c:736
inf_token
const char * inf_token(struct inputfile *inf, enum inf_token_type type)
Definition inputfile.c:611
inf_filename
static const char * inf_filename(struct inputfile *inf)
Definition inputfile.c:207
func
get_token_fn_t func
Definition inputfile.c:128
inf_warn
#define inf_warn(inf, message)
Definition inputfile.c:148
INF_DEBUG_FOUND
#define INF_DEBUG_FOUND
Definition inputfile.c:85
inf_close_partial
static void inf_close_partial(struct inputfile *inf)
Definition inputfile.c:268
inputfile.h
inf_token_type
inf_token_type
Definition inputfile.h:42
INF_TOK_LAST
@ INF_TOK_LAST
Definition inputfile.h:50
datafilename_fn_t
const char *(* datafilename_fn_t)(const char *filename)
Definition inputfile.h:33
fz_fgets
char * fz_fgets(char *buffer, int size, fz_FILE *fp)
Definition ioz.c:655
fz_from_file
fz_FILE * fz_from_file(const char *filename, const char *in_mode, enum fz_method method, int compress_level)
Definition ioz.c:230
fz_strerror
const char * fz_strerror(fz_FILE *fp)
Definition ioz.c:1157
fz_fclose
int fz_fclose(fz_FILE *fp)
Definition ioz.c:571
fz_ferror
int fz_ferror(fz_FILE *fp)
Definition ioz.c:1099
ioz.h
log.h
fc_assert_ret
#define fc_assert_ret(condition)
Definition log.h:191
fc_assert_ret_val
#define fc_assert_ret_val(condition, val)
Definition log.h:194
log_debug
#define log_debug(message,...)
Definition log.h:115
LOG_ERROR
@ LOG_ERROR
Definition log.h:30
log_error
#define log_error(message,...)
Definition log.h:103
mem.h
fc_strdup
#define fc_strdup(str)
Definition mem.h:43
fc_malloc
#define fc_malloc(sz)
Definition mem.h:34
len
int len
Definition packhand.c:127
fileinfoname
const char * fileinfoname(const struct strvec *dirs, const char *filename)
Definition shared.c:1101
get_data_dirs
const struct strvec * get_data_dirs(void)
Definition shared.c:893
shared.h
astring
Definition astring.h:36
fz_FILE_s
Definition ioz.c:149
inputfile
Definition inputfile.c:90
inputfile::cur_line
struct astring cur_line
Definition inputfile.c:95
inputfile::filename
char * filename
Definition inputfile.c:92
inputfile::partial
struct astring partial
Definition inputfile.c:99
inputfile::string_start_line
int string_start_line
Definition inputfile.c:108
inputfile::fp
fz_FILE * fp
Definition inputfile.c:93
inputfile::magic
unsigned int magic
Definition inputfile.c:91
inputfile::at_eof
bool at_eof
Definition inputfile.c:94
inputfile::line_num
unsigned int line_num
Definition inputfile.c:97
inputfile::cur_line_pos
unsigned int cur_line_pos
Definition inputfile.c:96
inputfile::token
struct astring token
Definition inputfile.c:98
inputfile::included_from
struct inputfile * included_from
Definition inputfile.c:110
inputfile::in_string
bool in_string
Definition inputfile.c:105
inputfile::datafn
datafilename_fn_t datafn
Definition inputfile.c:102
message
Definition messagewin_common.h:29
fc_isspace
bool fc_isspace(char c)
Definition support.c:1240
cat_snprintf
int cat_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:986
fc_vsnprintf
int fc_vsnprintf(char *str, size_t n, const char *format, va_list ap)
Definition support.c:886
fc_isdigit
bool fc_isdigit(char c)
Definition support.c:1218
fc_isalnum
bool fc_isalnum(char c)
Definition support.c:1196
support.h
TRUE
#define TRUE
Definition support.h:46
FALSE
#define FALSE
Definition support.h:47
sz_strlcat
#define sz_strlcat(dest, src)
Definition support.h:190
fc_strncmp
#define fc_strncmp(_s1_, _s2_, _len_)
Definition support.h:154