Freeciv-3.2
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 NULL otherwise.
26 The data pointed to should not be modified. The returned 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 preceded 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-NULL.
45
46 table_start: '{'
47 returned token: should not be used except to check non-NULL.
48
49 table_end: '}'
50 returned token: should not be used except to check non-NULL.
51
52 comma: literal ','
53 returned token: should not be used except to check non-NULL.
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 omitting
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
90struct 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; /* NULL 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: */
116typedef 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;
129}
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 },
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/*******************************************************************/
154static bool is_comment(int c)
155{
156 return (c == '#' || c == ';');
157}
158
159/*******************************************************************/
163static void init_zeros(struct inputfile *inf)
164{
166 inf->magic = INF_MAGIC;
167 inf->filename = NULL;
168 inf->fp = NULL;
169 inf->datafn = NULL;
170 inf->included_from = NULL;
171 inf->line_num = inf->cur_line_pos = 0;
172 inf->at_eof = inf->in_string = FALSE;
173 inf->string_start_line = 0;
174 astr_init(&inf->cur_line);
175 astr_init(&inf->token);
176 astr_init(&inf->partial);
177}
178
179/*******************************************************************/
182static bool inf_sanity_check(struct inputfile *inf)
183{
187 fc_assert_ret_val(!inf->at_eof
188 || inf->at_eof, FALSE);
189 fc_assert_ret_val(!inf->in_string
190 || inf->in_string, FALSE);
191
192#ifdef FREECIV_DEBUG
193 fc_assert_ret_val(0 <= inf->string_start_line, FALSE);
194 if (inf->included_from && !inf_sanity_check(inf->included_from)) {
195 return FALSE;
196 }
197#endif /* FREECIV_DEBUG */
198
199 return TRUE;
200}
201
202/*******************************************************************/
206static const char *inf_filename(struct inputfile *inf)
207{
208 if (inf->filename) {
209 return inf->filename;
210 } else {
211 return "(anonymous)";
212 }
213}
214
215/*******************************************************************/
219struct inputfile *inf_from_file(const char *filename,
221{
222 struct inputfile *inf;
223 fz_FILE *fp;
224
227 fp = fz_from_file(filename, "r", -1, 0);
228 if (!fp) {
229 return NULL;
230 }
231 log_debug("inputfile: opened \"%s\" ok", filename);
233 inf->filename = fc_strdup(filename);
234 return inf;
235}
236
237/*******************************************************************/
242{
243 struct inputfile *inf;
244
246 inf = fc_malloc(sizeof(*inf));
248
249 inf->filename = NULL;
250 inf->fp = stream;
251 inf->datafn = datafn;
252
253 log_debug("inputfile: opened \"%s\" ok", inf_filename(inf));
254 return inf;
255}
256
257
258/*******************************************************************/
264static void inf_close_partial(struct inputfile *inf)
265{
267
268 log_debug("inputfile: sub-closing \"%s\"", inf_filename(inf));
269
270 if (fz_ferror(inf->fp) != 0) {
271 log_error("Error before closing %s: %s", inf_filename(inf),
272 fz_strerror(inf->fp));
273 fz_fclose(inf->fp);
274 inf->fp = NULL;
275 }
276 else if (fz_fclose(inf->fp) != 0) {
277 log_error("Error closing %s", inf_filename(inf));
278 }
279 if (inf->filename) {
280 free(inf->filename);
281 }
282 inf->filename = NULL;
283 astr_free(&inf->cur_line);
284 astr_free(&inf->token);
285 astr_free(&inf->partial);
286
287 /* Assign zeros for safety if accidentally reuse etc: */
289 inf->magic = ~INF_MAGIC;
290
291 log_debug("inputfile: sub-closed ok");
292}
293
294/*******************************************************************/
301{
303
304 log_debug("inputfile: closing \"%s\"", inf_filename(inf));
305
306 if (inf->included_from) {
307 inf_close(inf->included_from);
308 /* Stop anything from recursing to already closed including file */
309 inf->included_from = NULL;
310 }
312 free(inf);
313
314 log_debug("inputfile: closed ok");
315}
316
317/*******************************************************************/
320static bool have_line(struct inputfile *inf)
321{
322 if (!inf_sanity_check(inf)) {
323 return FALSE;
324 }
325
326 return !astr_empty(&inf->cur_line);
327}
328
329/*******************************************************************/
332static bool at_eol(struct inputfile *inf)
333{
334 if (!inf_sanity_check(inf)) {
335 return TRUE;
336 }
337
338 fc_assert_ret_val(inf->cur_line_pos <= astr_len(&inf->cur_line), TRUE);
339
340 return (inf->cur_line_pos >= astr_len(&inf->cur_line));
341}
342
343/*******************************************************************/
347{
348 if (!inf_sanity_check(inf)) {
349 return TRUE;
350 }
351
352 return inf->at_eof;
353}
354
355/*******************************************************************/
363static bool check_include(struct inputfile *inf)
364{
365 const char *include_prefix = "*include";
366 static size_t len = 0;
367 size_t bare_name_len;
368 char *bare_name;
369 const char *c, *bare_name_start, *full_name;
370 struct inputfile *new_inf, temp;
371
372 if (len == 0) {
374 }
375
376 if (!inf_sanity_check(inf)) {
377 return FALSE;
378 }
379
380 if (inf->in_string || astr_len(&inf->cur_line) <= len
381 || inf->cur_line_pos > 0) {
382 return FALSE;
383 }
384
385 if (fc_strncmp(astr_str(&inf->cur_line), include_prefix, len)) {
386 return FALSE;
387 }
388
389 /* From here, the include-line must be well formed.
390 * Keep inf->cur_line_pos accurate just so error messages are useful */
391
392 /* Skip any whitespace: */
393 inf->cur_line_pos = len;
394 c = astr_str(&inf->cur_line) + len;
395 while (*c != '\0' && fc_isspace(*c)) {
396 c++;
397 }
398
399 if (*c != '\"') {
401 "Did not find opening doublequote for '*include' line");
402 return FALSE;
403 }
404 c++;
405 inf->cur_line_pos = c - astr_str(&inf->cur_line);
406
407 bare_name_start = c;
408 while (*c != '\0' && *c != '\"') c++;
409 if (*c != '\"') {
411 "Did not find closing doublequote for '*include' line");
412 return FALSE;
413 }
414 c++;
418 bare_name[bare_name_len - 1] = '\0';
419 inf->cur_line_pos = c - astr_str(&inf->cur_line);
420
421 /* check rest of line is well-formed: */
422 while (*c != '\0' && fc_isspace(*c) && !is_comment(*c)) {
423 c++;
424 }
425 if (!(*c == '\0' || is_comment(*c))) {
426 inf_log(inf, LOG_ERROR, "Junk after filename for '*include' line");
427 return FALSE;
428 }
429 inf->cur_line_pos = astr_len(&inf->cur_line) - 1;
430
431 full_name = inf->datafn(bare_name);
432 if (!full_name) {
433 log_error("Could not find included file \"%s\"", bare_name);
435 return FALSE;
436 }
438
439 /* avoid recursion: (first filename may not have the same path,
440 * but will at least stop infinite recursion) */
441 {
442 struct inputfile *inc = inf;
443 do {
444 if (inc->filename && strcmp(full_name, inc->filename) == 0) {
445 log_error("Recursion trap on '*include' for \"%s\"", full_name);
446 return FALSE;
447 }
448 } while ((inc = inc->included_from));
449 }
450
452
453 /* Swap things around so that memory pointed to by inf (user pointer,
454 and pointer in calling functions) contains the new inputfile,
455 and newly allocated memory for new_inf contains the old inputfile.
456 This is pretty scary, lets hope it works...
457 */
458 temp = *new_inf;
459 *new_inf = *inf;
460 *inf = temp;
461 inf->included_from = new_inf;
462 return TRUE;
463}
464
465/*******************************************************************/
471static bool read_a_line(struct inputfile *inf)
472{
473 struct astring *line;
474 char *ret;
475 int pos;
476
477 if (!inf_sanity_check(inf)) {
478 return FALSE;
479 }
480
481 if (inf->at_eof) {
482 return FALSE;
483 }
484
485 /* abbreviation: */
486 line = &inf->cur_line;
487
488 /* minimum initial line length: */
489 astr_reserve(line, 80);
491 pos = 0;
492
493 /* Read until we get a full line:
494 * At start of this loop, pos is index to trailing null
495 * (or first position) in line.
496 */
497 for (;;) {
498 ret = fz_fgets((char *) astr_str(line) + pos,
499 astr_capacity(line) - pos, inf->fp);
500
501 if (!ret) {
502 /* fgets failed */
503 if (pos > 0) {
504 inf_log(inf, LOG_ERROR, _("End-of-file not in line of its own"));
505 }
506 inf->at_eof = TRUE;
507 if (inf->in_string) {
508 /* Note: Don't allow multi-line strings to cross "include"
509 * boundaries */
510 inf_log(inf, LOG_ERROR, "Multi-line string went to end-of-file");
511 return FALSE;
512 }
513 break;
514 }
515
516 /* Cope with \n\r line endings if not caught by library:
517 * strip off any leading \r */
518 if (0 == pos && 0 < astr_len(line) && astr_str(line)[0] == '\r') {
520 }
521
522 pos = astr_len(line);
523
524 if (0 < pos && astr_str(line)[pos - 1] == '\n') {
525 int end;
526 /* Cope with \r\n line endings if not caught by library:
527 * strip off any trailing \r */
528 if (1 < pos && astr_str(line)[pos - 2] == '\r') {
529 end = pos - 2;
530 } else {
531 end = pos - 1;
532 }
533 *((char *) astr_str(line) + end) = '\0';
534 break;
535 }
536 astr_reserve(line, pos * 2);
537 }
538
539 if (!inf->at_eof) {
540 inf->line_num++;
541 inf->cur_line_pos = 0;
542
543 if (check_include(inf)) {
544 return read_a_line(inf);
545 }
546 return TRUE;
547 } else {
549 if (inf->included_from) {
550 /* Pop the include, and get next line from file above instead. */
551 struct inputfile *inc = inf->included_from;
553 *inf = *inc; /* so the user pointer in still valid
554 * (and inf pointers in calling functions) */
555 free(inc);
556 return read_a_line(inf);
557 }
558 return FALSE;
559 }
560}
561
562/*******************************************************************/
567char *inf_log_str(struct inputfile *inf, const char *message, ...)
568{
569 va_list args;
570 static char str[512];
571
572 if (message) {
573 va_start(args, message);
574 fc_vsnprintf(str, sizeof(str), message, args);
575 va_end(args);
576 sz_strlcat(str, "\n");
577 } else {
578 str[0] = '\0';
579 }
580
581 if (inf_sanity_check(inf)) {
582 cat_snprintf(str, sizeof(str), " file \"%s\", line %d, pos %d%s",
583 inf_filename(inf), inf->line_num, inf->cur_line_pos,
584 (inf->at_eof ? ", EOF" : ""));
585
586 if (!astr_empty(&inf->cur_line)) {
587 cat_snprintf(str, sizeof(str), "\n looking at: '%s'",
588 astr_str(&inf->cur_line) + inf->cur_line_pos);
589 }
590 if (inf->in_string) {
591 cat_snprintf(str, sizeof(str),
592 "\n processing string starting at line %d",
593 inf->string_start_line);
594 }
595 while ((inf = inf->included_from)) { /* local pointer assignment */
596 cat_snprintf(str, sizeof(str), "\n included from file \"%s\", line %d",
597 inf_filename(inf), inf->line_num);
598 }
599 }
600
601 return str;
602}
603
604/*******************************************************************/
607const char *inf_token(struct inputfile *inf, enum inf_token_type type)
608{
609 const char *c;
610 const char *name;
612
613 if (!inf_sanity_check(inf)) {
614 return NULL;
615 }
616
618
619 name = tok_tab[type].name ? tok_tab[type].name : "(unnamed)";
620 func = tok_tab[type].func;
621
622 if (!func) {
623 log_error("token type %d (%s) not supported yet", type, name);
624 c = NULL;
625 } else {
626 while (!have_line(inf) && read_a_line(inf)) {
627 /* Nothing. */
628 }
629 if (!have_line(inf)) {
630 c = NULL;
631 } else {
632 c = func(inf);
633 }
634 }
635 if (c && INF_DEBUG_FOUND) {
636 log_debug("inputfile: found %s '%s'", name, astr_str(&inf->token));
637 }
638 return c;
639}
640
641/*******************************************************************/
646{
647 int count = 0;
648
649 while (inf_token(inf, type)) {
650 count++;
651 }
652
653 return count;
654}
655
656/*******************************************************************/
661static const char *get_token_section_name(struct inputfile *inf)
662{
663 const char *c, *start;
664
666
667 c = astr_str(&inf->cur_line) + inf->cur_line_pos;
668 if (*c++ != '[') {
669 return NULL;
670 }
671 start = c;
672 while (*c != '\0' && *c != ']') {
673 c++;
674 }
675 if (*c != ']') {
676 return NULL;
677 }
678 *((char *) c) = '\0'; /* Tricky. */
679 astr_set(&inf->token, "%s", start);
680 *((char *) c) = ']'; /* Revert. */
681 inf->cur_line_pos = c + 1 - astr_str(&inf->cur_line);
682 return astr_str(&inf->token);
683}
684
685/*******************************************************************/
689static const char *get_token_entry_name(struct inputfile *inf)
690{
691 const char *c, *start, *end;
692 char trailing;
693
695
696 c = astr_str(&inf->cur_line) + inf->cur_line_pos;
697 while (*c != '\0' && fc_isspace(*c)) {
698 c++;
699 }
700 if (*c == '\0') {
701 return NULL;
702 }
703 start = c;
704 while (*c != '\0' && !fc_isspace(*c) && *c != '=' && !is_comment(*c)) {
705 c++;
706 }
707 if (!(*c != '\0' && (fc_isspace(*c) || *c == '='))) {
708 return NULL;
709 }
710 end = c;
711 while (*c != '\0' && *c != '=' && !is_comment(*c)) {
712 c++;
713 }
714 if (*c != '=') {
715 return NULL;
716 }
717 trailing = *end;
718 *((char *) end) = '\0'; /* Tricky. */
719 astr_set(&inf->token, "%s", start);
720 *((char *) end) = trailing; /* Revert. */
721 inf->cur_line_pos = c + 1 - astr_str(&inf->cur_line);
722 return astr_str(&inf->token);
723}
724
725/*******************************************************************/
729static const char *get_token_eol(struct inputfile *inf)
730{
731 const char *c;
732
734
735 if (!at_eol(inf)) {
736 c = astr_str(&inf->cur_line) + inf->cur_line_pos;
737 while (*c != '\0' && fc_isspace(*c)) {
738 c++;
739 }
740 if (*c != '\0' && !is_comment(*c)) {
741 return NULL;
742 }
743 }
744
745 /* finished with this line: say that we don't have it any more: */
746 astr_clear(&inf->cur_line);
747 inf->cur_line_pos = 0;
748
749 astr_set(&inf->token, " ");
750 return astr_str(&inf->token);
751}
752
753/*******************************************************************/
757static const char *get_token_white_char(struct inputfile *inf,
758 char target)
759{
760 const char *c;
761
763
764 c = astr_str(&inf->cur_line) + inf->cur_line_pos;
765 while (*c != '\0' && fc_isspace(*c)) {
766 c++;
767 }
768 if (*c != target) {
769 return NULL;
770 }
771 inf->cur_line_pos = c + 1 - astr_str(&inf->cur_line);
772 astr_set(&inf->token, "%c", target);
773 return astr_str(&inf->token);
774}
775
776/*******************************************************************/
779static const char *get_token_table_start(struct inputfile *inf)
780{
781 return get_token_white_char(inf, '{');
782}
783
784/*******************************************************************/
787static const char *get_token_table_end(struct inputfile *inf)
788{
789 return get_token_white_char(inf, '}');
790}
791
792/*******************************************************************/
795static const char *get_token_comma(struct inputfile *inf)
796{
797 return get_token_white_char(inf, ',');
798}
799
800/*******************************************************************/
803static const char *get_token_value(struct inputfile *inf)
804{
805 struct astring *partial;
806 const char *c, *start;
807 char trailing;
808 bool has_i18n_marking = FALSE;
809 char border_character = '\"';
810
812
813 c = astr_str(&inf->cur_line) + inf->cur_line_pos;
814 while (*c != '\0' && fc_isspace(*c)) {
815 c++;
816 }
817 if (*c == '\0') {
818 return NULL;
819 }
820
821 if (*c == '-' || *c == '+' || fc_isdigit(*c)) {
822 /* a number: */
823 start = c++;
824 while (*c != '\0' && fc_isdigit(*c)) {
825 c++;
826 }
827 if (*c == '.') {
828 /* Float maybe */
829 c++;
830 while (*c != '\0' && fc_isdigit(*c)) {
831 c++;
832 }
833 }
834 /* check that the trailing stuff is ok: */
835 if (!(*c == '\0' || *c == ',' || fc_isspace(*c) || is_comment(*c))) {
836 return NULL;
837 }
838 /* If its a comma, we don't want to obliterate it permanently,
839 * so remember it: */
840 trailing = *c;
841 *((char *) c) = '\0'; /* Tricky. */
842
843 inf->cur_line_pos = c - astr_str(&inf->cur_line);
844 astr_set(&inf->token, "%s", start);
845
846 *((char *) c) = trailing; /* Revert. */
847 return astr_str(&inf->token);
848 }
849
850 /* allow gettext marker: */
851 if (*c == '_' && *(c + 1) == '(') {
853 c += 2;
854 while (*c != '\0' && fc_isspace(*c)) {
855 c++;
856 }
857 if (*c == '\0') {
858 return NULL;
859 }
860 }
861
862 border_character = *c;
863
864 if (border_character == '*') {
865 const char *rfname;
866 fz_FILE *fp;
867 bool eof;
868 int pos;
869
870 c++;
871
872 start = c;
873 while (*c != '*') {
874 if (*c == '\0' || *c == '\n') {
875 return NULL;
876 }
877 c++;
878 }
879 c++;
880 /* check that the trailing stuff is ok: */
881 if (!(*c == '\0' || *c == ',' || fc_isspace(*c) || is_comment(*c))) {
882 return NULL;
883 }
884 /* We don't want to obliterate ending '*' permanently,
885 * so remember it: */
886 trailing = *(c - 1);
887 *((char *) (c - 1)) = '\0'; /* Tricky. */
888
890 if (rfname == NULL) {
892 _("Cannot find stringfile \"%s\"."), start);
893 *((char *) c) = trailing; /* Revert. */
894 return NULL;
895 }
896 *((char *) c) = trailing; /* Revert. */
897 fp = fz_from_file(rfname, "r", -1, 0);
898 if (!fp) {
900 _("Cannot open stringfile \"%s\"."), rfname);
901 return NULL;
902 }
903 log_debug("Stringfile \"%s\" opened ok", start);
904 *((char *) (c - 1)) = trailing; /* Revert. */
905 astr_set(&inf->token, "*"); /* Mark as a string read from a file */
906
907 eof = FALSE;
908 pos = 1; /* Past 'filestring' marker */
909 while (!eof) {
910 char *ret;
911
912 ret = fz_fgets((char *) astr_str(&inf->token) + pos,
913 astr_capacity(&inf->token) - pos, fp);
914 if (ret == NULL) {
915 eof = TRUE;
916 } else {
917 pos = astr_len(&inf->token);
918 astr_reserve(&inf->token, pos + 200);
919 }
920 }
921
922 fz_fclose(fp);
923
924 inf->cur_line_pos = c + 1 - astr_str(&inf->cur_line);
925
926 return astr_str(&inf->token);
927 } else if (border_character != '\"'
928 && border_character != '\''
929 && border_character != '$') {
930 /* A one-word string: maybe FALSE or TRUE. */
931 start = c;
932 while (fc_isalnum(*c)) {
933 c++;
934 }
935 /* check that the trailing stuff is ok: */
936 if (!(*c == '\0' || *c == ',' || fc_isspace(*c) || is_comment(*c))) {
937 return NULL;
938 }
939 /* If its a comma, we don't want to obliterate it permanently,
940 * so remember it: */
941 trailing = *c;
942 *((char *) c) = '\0'; /* Tricky. */
943
944 inf->cur_line_pos = c - astr_str(&inf->cur_line);
945 astr_set(&inf->token, "%s", start);
946
947 *((char *) c) = trailing; /* Revert. */
948 return astr_str(&inf->token);
949 }
950
951 /* From here, we know we have a string, we just have to find the
952 trailing (un-escaped) double-quote. We read in extra lines if
953 necessary to find it. If we _don't_ find the end-of-string
954 (that is, we come to end-of-file), we return NULL, but we
955 leave the file in at_eof, and don't try to back-up to the
956 current point. (That would be more difficult, and probably
957 not necessary: at that point we probably have a malformed
958 string/file.)
959
960 As we read extra lines, the string value from previous
961 lines is placed in partial.
962 */
963
964 /* prepare for possibly multi-line string: */
965 inf->string_start_line = inf->line_num;
966 inf->in_string = TRUE;
967
968 partial = &inf->partial; /* abbreviation */
969 astr_clear(partial);
970
971 start = c++; /* start includes the initial \", to
972 * distinguish from a number */
973 for (;;) {
974 while (*c != '\0' && *c != border_character) {
975 /* skip over escaped chars, including backslash-doublequote,
976 * and backslash-backslash: */
977 if (*c == '\\' && *(c + 1) != '\0') {
978 c++;
979 }
980 c++;
981 }
982
983 if (*c == border_character) {
984 /* Found end of string */
985 break;
986 }
987
988 astr_add(partial, "%s\n", start);
989
990 if (!read_a_line(inf)) {
991 /* shouldn't happen */
993 "Bad return for multi-line string from read_a_line");
994 return NULL;
995 }
996 c = start = astr_str(&inf->cur_line);
997 }
998
999 /* found end of string */
1000 trailing = *c;
1001 *((char *) c) = '\0'; /* Tricky. */
1002
1003 inf->cur_line_pos = c + 1 - astr_str(&inf->cur_line);
1004 astr_set(&inf->token, "%s%s", astr_str(partial), start);
1005
1006 *((char *) c) = trailing; /* Revert. */
1007
1008 /* check gettext tag at end: */
1009 if (has_i18n_marking) {
1010 if (*++c == ')') {
1011 inf->cur_line_pos++;
1012 } else {
1013 inf_warn(inf, "Missing end of i18n string marking");
1014 }
1015 }
1016 inf->in_string = FALSE;
1017 return astr_str(&inf->token);
1018}
void astr_free(struct astring *astr)
Definition astring.c:153
void astr_set(struct astring *astr, const char *format,...)
Definition astring.c:267
void astr_reserve(struct astring *astr, size_t n)
Definition astring.c:183
void astr_clear(struct astring *astr)
Definition astring.c:205
void astr_init(struct astring *astr)
Definition astring.c:144
void astr_add(struct astring *astr, const char *format,...)
Definition astring.c:287
#define str
Definition astring.c:76
static size_t astr_capacity(const struct astring *astr) fc__attribute((nonnull(1)))
Definition astring.h:117
static size_t astr_len(const struct astring *astr) fc__attribute((nonnull(1)))
Definition astring.h:101
static const char * astr_str(const struct astring *astr) fc__attribute((nonnull(1)))
Definition astring.h:93
static bool astr_empty(const struct astring *astr) fc__attribute((nonnull(1)))
Definition astring.h:125
char * incite_cost
Definition comments.c:75
#define _(String)
Definition fcintl.h:67
static struct tile * pos
Definition finddlg.c:53
GType type
Definition repodlgs.c:1313
static struct @0 tok_tab[INF_TOK_LAST]
static const char * get_token_value(struct inputfile *inf)
Definition inputfile.c:803
static bool is_comment(int c)
Definition inputfile.c:154
static const char * get_token_entry_name(struct inputfile *inf)
Definition inputfile.c:689
char * inf_log_str(struct inputfile *inf, const char *message,...)
Definition inputfile.c:567
static bool inf_sanity_check(struct inputfile *inf)
Definition inputfile.c:182
static const char * get_token_section_name(struct inputfile *inf)
Definition inputfile.c:661
bool inf_at_eof(struct inputfile *inf)
Definition inputfile.c:346
struct inputfile * inf_from_file(const char *filename, datafilename_fn_t datafn)
Definition inputfile.c:219
static bool have_line(struct inputfile *inf)
Definition inputfile.c:320
static void init_zeros(struct inputfile *inf)
Definition inputfile.c:163
static bool at_eol(struct inputfile *inf)
Definition inputfile.c:332
struct inputfile * inf_from_stream(fz_FILE *stream, datafilename_fn_t datafn)
Definition inputfile.c:241
const char * name
Definition inputfile.c:127
static const char * get_token_table_end(struct inputfile *inf)
Definition inputfile.c:787
static const char * get_token_white_char(struct inputfile *inf, char target)
Definition inputfile.c:757
void inf_close(struct inputfile *inf)
Definition inputfile.c:300
#define INF_MAGIC
Definition inputfile.c:88
static bool check_include(struct inputfile *inf)
Definition inputfile.c:363
static const char * get_token_table_start(struct inputfile *inf)
Definition inputfile.c:779
#define inf_log(inf, level, message,...)
Definition inputfile.c:143
static bool read_a_line(struct inputfile *inf)
Definition inputfile.c:471
int inf_discard_tokens(struct inputfile *inf, enum inf_token_type type)
Definition inputfile.c:645
static const char * get_token_comma(struct inputfile *inf)
Definition inputfile.c:795
const char *(* get_token_fn_t)(struct inputfile *inf)
Definition inputfile.c:116
static const char * get_token_eol(struct inputfile *inf)
Definition inputfile.c:729
const char * inf_token(struct inputfile *inf, enum inf_token_type type)
Definition inputfile.c:607
static const char * inf_filename(struct inputfile *inf)
Definition inputfile.c:206
get_token_fn_t func
Definition inputfile.c:128
#define inf_warn(inf, message)
Definition inputfile.c:148
#define INF_DEBUG_FOUND
Definition inputfile.c:85
static void inf_close_partial(struct inputfile *inf)
Definition inputfile.c:264
inf_token_type
Definition inputfile.h:42
@ INF_TOK_LAST
Definition inputfile.h:50
const char *(* datafilename_fn_t)(const char *filename)
Definition inputfile.h:33
char * fz_fgets(char *buffer, int size, fz_FILE *fp)
Definition ioz.c:655
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
#define fc_assert_ret(condition)
Definition log.h:191
#define fc_assert_ret_val(condition, val)
Definition log.h:194
#define log_debug(message,...)
Definition log.h:115
@ LOG_ERROR
Definition log.h:30
#define log_error(message,...)
Definition log.h:103
#define fc_strdup(str)
Definition mem.h:43
#define fc_malloc(sz)
Definition mem.h:34
int len
Definition packhand.c:127
const char * fileinfoname(const struct strvec *dirs, const char *filename)
Definition shared.c:1101
const struct strvec * get_data_dirs(void)
Definition shared.c:893
struct astring cur_line
Definition inputfile.c:95
char * filename
Definition inputfile.c:92
struct astring partial
Definition inputfile.c:99
int string_start_line
Definition inputfile.c:108
fz_FILE * fp
Definition inputfile.c:93
unsigned int magic
Definition inputfile.c:91
bool at_eof
Definition inputfile.c:94
unsigned int line_num
Definition inputfile.c:97
unsigned int cur_line_pos
Definition inputfile.c:96
struct astring token
Definition inputfile.c:98
struct inputfile * included_from
Definition inputfile.c:110
bool in_string
Definition inputfile.c:105
datafilename_fn_t datafn
Definition inputfile.c:102
bool fc_isspace(char c)
Definition support.c:1254
int cat_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:1000
int fc_vsnprintf(char *str, size_t n, const char *format, va_list ap)
Definition support.c:900
bool fc_isdigit(char c)
Definition support.c:1232
bool fc_isalnum(char c)
Definition support.c:1210
#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