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)
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 ***********************************************************************/
14 /**********************************************************************
15 A low-level object for reading a registry-format file.
16 original author: David Pfitzner <dwp@mso.anu.edu.au>
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?)
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 retuned pointer
27 is valid _only_ until another inputfile is performed. (So should
28 be used immediately, or fc_strdup-ed etc.)
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.
39 entry_name: 'foo =' (optional whitespace allowed before '=')
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.
47 returned token: should not be used except to check non-NULL.
50 returned token: should not be used except to check non-NULL.
53 returned token: should not be used except to check non-NULL.
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.
64 ***********************************************************************/
67 #include <fc_config.h>
80 #include "shared.h" /* TRUE, FALSE */
83 #include "inputfile.h"
85 #define INF_DEBUG_FOUND FALSE
86 #define INF_DEBUG_NOT_FOUND FALSE
88 #define INF_MAGIC (0xabdc0132) /* arbitrary */
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 int cur_line_pos
; /* position in current line */
97 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 */
115 /* A function to get a specific token type: */
116 typedef const char *(*get_token_fn_t
)(struct inputfile
*inf
);
118 static const char *get_token_section_name(struct inputfile
*inf
);
119 static const char *get_token_entry_name(struct inputfile
*inf
);
120 static const char *get_token_eol(struct inputfile
*inf
);
121 static const char *get_token_table_start(struct inputfile
*inf
);
122 static const char *get_token_table_end(struct inputfile
*inf
);
123 static const char *get_token_comma(struct inputfile
*inf
);
124 static const char *get_token_value(struct inputfile
*inf
);
130 tok_tab
[INF_TOK_LAST
] =
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
},
141 static bool read_a_line(struct inputfile
*inf
);
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__)); \
148 #define inf_warn(inf, message) \
149 inf_log(inf, LOG_NORMAL, "%s", message);
151 /**********************************************************************
152 Return true if c is a 'comment' character: '#' or ';'
153 ***********************************************************************/
154 static bool is_comment(int c
)
156 return (c
== '#' || c
== ';');
159 /**********************************************************************
160 Set values to zeros; should have free'd/closed everything before
162 ***********************************************************************/
163 static void init_zeros(struct inputfile
*inf
)
165 fc_assert_ret(NULL
!= inf
);
166 inf
->magic
= INF_MAGIC
;
167 inf
->filename
= 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
);
179 /**********************************************************************
180 Check sensible values for an opened inputfile.
181 ***********************************************************************/
182 static bool inf_sanity_check(struct inputfile
*inf
)
184 fc_assert_ret_val(NULL
!= inf
, FALSE
);
185 fc_assert_ret_val(INF_MAGIC
== inf
->magic
, FALSE
);
186 fc_assert_ret_val(NULL
!= inf
->fp
, FALSE
);
187 fc_assert_ret_val(0 <= inf
->line_num
, FALSE
);
188 fc_assert_ret_val(0 <= inf
->cur_line_pos
, FALSE
);
189 fc_assert_ret_val(FALSE
== inf
->at_eof
190 || TRUE
== inf
->at_eof
, FALSE
);
191 fc_assert_ret_val(FALSE
== inf
->in_string
192 || TRUE
== inf
->in_string
, FALSE
);
194 fc_assert_ret_val(0 <= inf
->string_start_line
, FALSE
);
195 if (inf
->included_from
&& !inf_sanity_check(inf
->included_from
)) {
202 /**************************************************************************
203 Return the filename the inputfile was loaded as, or "(anonymous)"
204 if this inputfile was loaded from a stream rather than from a file.
205 **************************************************************************/
206 static const char *inf_filename(struct inputfile
*inf
)
209 return inf
->filename
;
211 return "(anonymous)";
215 /**********************************************************************
216 Open the file, and return an allocated, initialized structure.
217 Returns NULL if the file could not be opened.
218 ***********************************************************************/
219 struct inputfile
*inf_from_file(const char *filename
,
220 datafilename_fn_t datafn
)
222 struct inputfile
*inf
;
225 fc_assert_ret_val(NULL
!= filename
, NULL
);
226 fc_assert_ret_val(0 < strlen(filename
), NULL
);
227 fp
= fz_from_file(filename
, "r", -1, 0);
231 log_debug("inputfile: opened \"%s\" ok", filename
);
232 inf
= inf_from_stream(fp
, datafn
);
233 inf
->filename
= fc_strdup(filename
);
237 /**********************************************************************
238 Open the stream, and return an allocated, initialized structure.
239 Returns NULL if the file could not be opened.
240 ***********************************************************************/
241 struct inputfile
*inf_from_stream(fz_FILE
*stream
, datafilename_fn_t datafn
)
243 struct inputfile
*inf
;
245 fc_assert_ret_val(NULL
!= stream
, NULL
);
246 inf
= fc_malloc(sizeof(*inf
));
249 inf
->filename
= NULL
;
251 inf
->datafn
= datafn
;
253 log_debug("inputfile: opened \"%s\" ok", inf_filename(inf
));
258 /**********************************************************************
259 Close the file and free associated memory, but don't recurse
260 included_from files, and don't free the actual memory where
261 the inf record is stored (ie, the memory where the users pointer
262 points to). This is used when closing an included file.
263 ***********************************************************************/
264 static void inf_close_partial(struct inputfile
*inf
)
266 fc_assert_ret(inf_sanity_check(inf
));
268 log_debug("inputfile: sub-closing \"%s\"", inf_filename(inf
));
270 if (fz_ferror(inf
->fp
) != 0) {
271 log_error("Error before closing %s: %s", inf_filename(inf
),
272 fz_strerror(inf
->fp
));
276 else if (fz_fclose(inf
->fp
) != 0) {
277 log_error("Error closing %s", inf_filename(inf
));
282 inf
->filename
= NULL
;
283 astr_free(&inf
->cur_line
);
284 astr_free(&inf
->token
);
285 astr_free(&inf
->partial
);
287 /* assign zeros for safety if accidently re-use etc: */
289 inf
->magic
= ~INF_MAGIC
;
291 log_debug("inputfile: sub-closed ok");
294 /**********************************************************************
295 Close the file and free associated memory, included any partially
296 recursed included files, and the memory allocated for 'inf' itself.
297 Should only be used on an actually open inputfile.
298 After this, the pointer should not be used.
299 ***********************************************************************/
300 void inf_close(struct inputfile
*inf
)
302 fc_assert_ret(inf_sanity_check(inf
));
304 log_debug("inputfile: closing \"%s\"", inf_filename(inf
));
305 if (inf
->included_from
) {
306 inf_close(inf
->included_from
);
308 inf_close_partial(inf
);
310 log_debug("inputfile: closed ok");
313 /**********************************************************************
314 Return TRUE if have data for current line.
315 ***********************************************************************/
316 static bool have_line(struct inputfile
*inf
)
318 fc_assert_ret_val(inf_sanity_check(inf
), FALSE
);
319 return !astr_empty(&inf
->cur_line
);
322 /**********************************************************************
323 Return TRUE if current pos is at end of current line.
324 ***********************************************************************/
325 static bool at_eol(struct inputfile
*inf
)
327 fc_assert_ret_val(inf_sanity_check(inf
), TRUE
);
328 fc_assert_ret_val(inf
->cur_line_pos
<= astr_len(&inf
->cur_line
), TRUE
);
329 return (inf
->cur_line_pos
>= astr_len(&inf
->cur_line
));
332 /**********************************************************************
333 Return TRUE if current pos is at end of file.
334 ***********************************************************************/
335 bool inf_at_eof(struct inputfile
*inf
)
337 fc_assert_ret_val(inf_sanity_check(inf
), TRUE
);
341 /**********************************************************************
342 Check for an include command, which is an isolated line with:
344 If a file is included via this mechanism, returns 1, and sets up
345 data appropriately: (*inf) will now correspond to the new file,
346 which is opened but no data read, and inf->included_from is set
347 to newly malloced memory which corresponds to the old file.
348 ***********************************************************************/
349 static bool check_include(struct inputfile
*inf
)
351 const char *include_prefix
= "*include";
352 static size_t len
= 0;
353 size_t bare_name_len
;
355 const char *c
, *bare_name_start
, *full_name
;
356 struct inputfile
*new_inf
, temp
;
359 len
= strlen(include_prefix
);
361 fc_assert_ret_val(inf_sanity_check(inf
), FALSE
);
362 if (inf
->in_string
|| astr_len(&inf
->cur_line
) <= len
363 || inf
->cur_line_pos
> 0) {
366 if (strncmp(astr_str(&inf
->cur_line
), include_prefix
, len
) != 0) {
369 /* from here, the include-line must be well formed */
370 /* keep inf->cur_line_pos accurate just so error messages are useful */
372 /* skip any whitespace: */
373 inf
->cur_line_pos
= len
;
374 c
= astr_str(&inf
->cur_line
) + len
;
375 while (*c
!= '\0' && fc_isspace(*c
)){
380 inf_log(inf
, LOG_ERROR
,
381 "Did not find opening doublequote for '*include' line");
385 inf
->cur_line_pos
= c
- astr_str(&inf
->cur_line
);
388 while (*c
!= '\0' && *c
!= '\"') c
++;
390 inf_log(inf
, LOG_ERROR
,
391 "Did not find closing doublequote for '*include' line");
395 bare_name_len
= c
- bare_name_start
;
396 bare_name
= fc_malloc(bare_name_len
);
397 strncpy(bare_name
, bare_name_start
, bare_name_len
- 1);
398 bare_name
[bare_name_len
- 1] = '\0';
399 inf
->cur_line_pos
= c
- astr_str(&inf
->cur_line
);
401 /* check rest of line is well-formed: */
402 while (*c
!= '\0' && fc_isspace(*c
) && !is_comment(*c
)) {
405 if (!(*c
== '\0' || is_comment(*c
))) {
406 inf_log(inf
, LOG_ERROR
, "Junk after filename for '*include' line");
409 inf
->cur_line_pos
= astr_len(&inf
->cur_line
) - 1;
411 full_name
= inf
->datafn(bare_name
);
413 log_error("Could not find included file \"%s\"", bare_name
);
419 /* avoid recursion: (first filename may not have the same path,
420 * but will at least stop infinite recursion) */
422 struct inputfile
*inc
= inf
;
424 if (inc
->filename
&& strcmp(full_name
, inc
->filename
) == 0) {
425 log_error("Recursion trap on '*include' for \"%s\"", full_name
);
428 } while ((inc
= inc
->included_from
));
431 new_inf
= inf_from_file(full_name
, inf
->datafn
);
433 /* Swap things around so that memory pointed to by inf (user pointer,
434 and pointer in calling functions) contains the new inputfile,
435 and newly allocated memory for new_inf contains the old inputfile.
436 This is pretty scary, lets hope it works...
441 inf
->included_from
= new_inf
;
445 /**********************************************************************
446 Read a new line into cur_line.
447 Increments line_num and cur_line_pos.
448 Returns 0 if didn't read or other problem: treat as EOF.
449 Strips newline from input.
450 ***********************************************************************/
451 static bool read_a_line(struct inputfile
*inf
)
453 struct astring
*line
;
457 fc_assert_ret_val(inf_sanity_check(inf
), FALSE
);
464 line
= &inf
->cur_line
;
466 /* minimum initial line length: */
467 astr_reserve(line
, 80);
471 /* Read until we get a full line:
472 * At start of this loop, pos is index to trailing null
473 * (or first position) in line.
476 ret
= fz_fgets((char *) astr_str(line
) + pos
,
477 astr_capacity(line
) - pos
, inf
->fp
);
482 inf_log(inf
, LOG_ERROR
, _("End-of-file not in line of its own"));
485 if (inf
->in_string
) {
486 /* Note: Don't allow multi-line strings to cross "include"
488 inf_log(inf
, LOG_ERROR
, "Multi-line string went to end-of-file");
494 /* Cope with \n\r line endings if not caught by library:
495 * strip off any leading \r */
496 if (0 == pos
&& 0 < astr_len(line
) && astr_str(line
)[0] == '\r') {
497 memmove((char *)astr_str(line
), astr_str(line
)+1, astr_len(line
));
500 pos
= astr_len(line
);
502 if (0 < pos
&& astr_str(line
)[pos
- 1] == '\n') {
504 /* Cope with \r\n line endings if not caught by library:
505 * strip off any trailing \r */
506 if (1 < pos
&& astr_str(line
)[pos
- 2] == '\r') {
511 *((char *) astr_str(line
) + end
) = '\0';
514 astr_reserve(line
, pos
* 2);
519 inf
->cur_line_pos
= 0;
521 if (check_include(inf
)) {
522 return read_a_line(inf
);
527 if (inf
->included_from
) {
528 /* Pop the include, and get next line from file above instead. */
529 struct inputfile
*inc
= inf
->included_from
;
530 inf_close_partial(inf
);
531 *inf
= *inc
; /* so the user pointer in still valid
532 * (and inf pointers in calling functions) */
534 return read_a_line(inf
);
540 /**********************************************************************
541 Return a detailed log message, including information on current line
542 number etc. Message can be NULL: then just logs information on where
544 ***********************************************************************/
545 char *inf_log_str(struct inputfile
*inf
, const char *message
, ...)
548 static char str
[512];
550 fc_assert_ret_val(inf_sanity_check(inf
), NULL
);
553 va_start(args
, message
);
554 fc_vsnprintf(str
, sizeof(str
), message
, args
);
556 sz_strlcat(str
, "\n");
561 cat_snprintf(str
, sizeof(str
), " file \"%s\", line %d, pos %d%s",
562 inf_filename(inf
), inf
->line_num
, inf
->cur_line_pos
,
563 (inf
->at_eof
? ", EOF" : ""));
565 if (!astr_empty(&inf
->cur_line
)) {
566 cat_snprintf(str
, sizeof(str
), "\n looking at: '%s'",
567 astr_str(&inf
->cur_line
) + inf
->cur_line_pos
);
569 if (inf
->in_string
) {
570 cat_snprintf(str
, sizeof(str
),
571 "\n processing string starting at line %d",
572 inf
->string_start_line
);
574 while ((inf
= inf
->included_from
)) { /* local pointer assignment */
575 cat_snprintf(str
, sizeof(str
), "\n included from file \"%s\", line %d",
576 inf_filename(inf
), inf
->line_num
);
582 /**********************************************************************
583 Returns token of given type from given inputfile.
584 ***********************************************************************/
585 const char *inf_token(struct inputfile
*inf
, enum inf_token_type type
)
591 fc_assert_ret_val(inf_sanity_check(inf
), NULL
);
592 fc_assert_ret_val(INF_TOK_FIRST
<= type
&& INF_TOK_LAST
> type
, NULL
);
594 name
= tok_tab
[type
].name
? tok_tab
[type
].name
: "(unnamed)";
595 func
= tok_tab
[type
].func
;
598 log_error("token type %d (%s) not supported yet", type
, name
);
601 while (!have_line(inf
) && read_a_line(inf
)) {
604 if (!have_line(inf
)) {
610 if (c
&& INF_DEBUG_FOUND
) {
611 log_debug("inputfile: found %s '%s'", name
, astr_str(&inf
->token
));
616 /**********************************************************************
617 Read as many tokens of specified type as possible, discarding
618 the results; returns number of such tokens read and discarded.
619 ***********************************************************************/
620 int inf_discard_tokens(struct inputfile
*inf
, enum inf_token_type type
)
624 while(inf_token(inf
, type
))
629 /**********************************************************************
630 Returns section name in current position of inputfile. Returns NULL
631 if there is no section name on that position. Sets inputfile position
633 ***********************************************************************/
634 static const char *get_token_section_name(struct inputfile
*inf
)
636 const char *c
, *start
;
638 fc_assert_ret_val(have_line(inf
), NULL
);
640 c
= astr_str(&inf
->cur_line
) + inf
->cur_line_pos
;
645 while (*c
!= '\0' && *c
!= ']') {
651 *((char *) c
) = '\0'; /* Tricky. */
652 astr_set(&inf
->token
, "%s", start
);
653 *((char *) c
) = ']'; /* Revert. */
654 inf
->cur_line_pos
= c
+ 1 - astr_str(&inf
->cur_line
);
655 return astr_str(&inf
->token
);
658 /**********************************************************************
659 Returns next entry name from inputfile. Skips white spaces and
660 comments. Sets inputfile position after entry name.
661 ***********************************************************************/
662 static const char *get_token_entry_name(struct inputfile
*inf
)
664 const char *c
, *start
, *end
;
667 fc_assert_ret_val(have_line(inf
), NULL
);
669 c
= astr_str(&inf
->cur_line
) + inf
->cur_line_pos
;
670 while (*c
!= '\0' && fc_isspace(*c
)) {
677 while (*c
!= '\0' && !fc_isspace(*c
) && *c
!= '=' && !is_comment(*c
)) {
680 if (!(*c
!= '\0' && (fc_isspace(*c
) || *c
== '='))) {
684 while (*c
!= '\0' && *c
!= '=' && !is_comment(*c
)) {
691 *((char *) end
) = '\0'; /* Tricky. */
692 astr_set(&inf
->token
, "%s", start
);
693 *((char *) end
) = trailing
; /* Revert. */
694 inf
->cur_line_pos
= c
+ 1 - astr_str(&inf
->cur_line
);
695 return astr_str(&inf
->token
);
698 /**********************************************************************
699 If inputfile is at end-of-line, frees current line, and returns " ".
700 If there is still something on that line, returns NULL.
701 ***********************************************************************/
702 static const char *get_token_eol(struct inputfile
*inf
)
706 fc_assert_ret_val(have_line(inf
), NULL
);
709 c
= astr_str(&inf
->cur_line
) + inf
->cur_line_pos
;
710 while (*c
!= '\0' && fc_isspace(*c
)) {
713 if (*c
!= '\0' && !is_comment(*c
)) {
718 /* finished with this line: say that we don't have it any more: */
719 astr_clear(&inf
->cur_line
);
720 inf
->cur_line_pos
= 0;
722 astr_set(&inf
->token
, " ");
723 return astr_str(&inf
->token
);
726 /**********************************************************************
727 Get a flag token of a single character, with optional
728 preceeding whitespace.
729 ***********************************************************************/
730 static const char *get_token_white_char(struct inputfile
*inf
,
735 fc_assert_ret_val(have_line(inf
), NULL
);
737 c
= astr_str(&inf
->cur_line
) + inf
->cur_line_pos
;
738 while (*c
!= '\0' && fc_isspace(*c
)) {
744 inf
->cur_line_pos
= c
+ 1 - astr_str(&inf
->cur_line
);
745 astr_set(&inf
->token
, "%c", target
);
746 return astr_str(&inf
->token
);
749 /**********************************************************************
750 Get flag token for table start, or NULL if that is not next token.
751 ***********************************************************************/
752 static const char *get_token_table_start(struct inputfile
*inf
)
754 return get_token_white_char(inf
, '{');
757 /**********************************************************************
758 Get flag token for table end, or NULL if that is not next token.
759 ***********************************************************************/
760 static const char *get_token_table_end(struct inputfile
*inf
)
762 return get_token_white_char(inf
, '}');
765 /**********************************************************************
766 Get flag token comma, or NULL if that is not next token.
767 ***********************************************************************/
768 static const char *get_token_comma(struct inputfile
*inf
)
770 return get_token_white_char(inf
, ',');
773 /**********************************************************************
774 This one is more complicated; note that it may read in multiple lines.
775 ***********************************************************************/
776 static const char *get_token_value(struct inputfile
*inf
)
778 struct astring
*partial
;
779 const char *c
, *start
;
781 bool has_i18n_marking
= FALSE
;
782 char border_character
= '\"';
784 fc_assert_ret_val(have_line(inf
), NULL
);
786 c
= astr_str(&inf
->cur_line
) + inf
->cur_line_pos
;
787 while (*c
!= '\0' && fc_isspace(*c
)) {
794 if (*c
== '-' || *c
== '+' || fc_isdigit(*c
)) {
797 while (*c
!= '\0' && fc_isdigit(*c
)) {
803 while (*c
!= '\0' && fc_isdigit(*c
)) {
807 /* check that the trailing stuff is ok: */
808 if (!(*c
== '\0' || *c
== ',' || fc_isspace(*c
) || is_comment(*c
))) {
811 /* If its a comma, we don't want to obliterate it permanently,
814 *((char *) c
) = '\0'; /* Tricky. */
816 inf
->cur_line_pos
= c
- astr_str(&inf
->cur_line
);
817 astr_set(&inf
->token
, "%s", start
);
819 *((char *) c
) = trailing
; /* Revert. */
820 return astr_str(&inf
->token
);
823 /* allow gettext marker: */
824 if (*c
== '_' && *(c
+ 1) == '(') {
825 has_i18n_marking
= TRUE
;
827 while (*c
!= '\0' && fc_isspace(*c
)) {
835 border_character
= *c
;
837 if (border_character
== '*') {
847 if (*c
== '\0' || *c
== '\n') {
853 /* check that the trailing stuff is ok: */
854 if (!(*c
== '\0' || *c
== ',' || fc_isspace(*c
) || is_comment(*c
))) {
857 /* We don't want to obliterate ending '*' permanently,
860 *((char *) (c
- 1)) = '\0'; /* Tricky. */
862 rfname
= fileinfoname(get_data_dirs(), start
);
863 if (rfname
== NULL
) {
864 inf_log(inf
, LOG_ERROR
,
865 _("Cannot find stringfile \"%s\"."), start
);
866 *((char *) c
) = trailing
; /* Revert. */
869 *((char *) c
) = trailing
; /* Revert. */
870 fp
= fz_from_file(rfname
, "r", -1, 0);
872 inf_log(inf
, LOG_ERROR
,
873 _("Cannot open stringfile \"%s\"."), rfname
);
876 log_debug("Stringfile \"%s\" opened ok", start
);
877 *((char *) (c
- 1)) = trailing
; /* Revert. */
878 astr_set(&inf
->token
, "*"); /* Mark as a string read from a file */
881 pos
= 1; /* Past 'filestring' marker */
885 ret
= fz_fgets((char *) astr_str(&inf
->token
) + pos
,
886 astr_capacity(&inf
->token
) - pos
, fp
);
890 pos
= astr_len(&inf
->token
);
891 astr_reserve(&inf
->token
, pos
+ 200);
897 inf
->cur_line_pos
= c
+ 1 - astr_str(&inf
->cur_line
);
899 return astr_str(&inf
->token
);
900 } else if (border_character
!= '\"'
901 && border_character
!= '\''
902 && border_character
!= '$') {
903 /* A one-word string: maybe FALSE or TRUE. */
905 while (fc_isalnum(*c
)) {
908 /* check that the trailing stuff is ok: */
909 if (!(*c
== '\0' || *c
== ',' || fc_isspace(*c
) || is_comment(*c
))) {
912 /* If its a comma, we don't want to obliterate it permanently,
915 *((char *) c
) = '\0'; /* Tricky. */
917 inf
->cur_line_pos
= c
- astr_str(&inf
->cur_line
);
918 astr_set(&inf
->token
, "%s", start
);
920 *((char *) c
) = trailing
; /* Revert. */
921 return astr_str(&inf
->token
);
924 /* From here, we know we have a string, we just have to find the
925 trailing (un-escaped) double-quote. We read in extra lines if
926 necessary to find it. If we _don't_ find the end-of-string
927 (that is, we come to end-of-file), we return NULL, but we
928 leave the file in at_eof, and don't try to back-up to the
929 current point. (That would be more difficult, and probably
930 not necessary: at that point we probably have a malformed
933 As we read extra lines, the string value from previous
934 lines is placed in partial.
937 /* prepare for possibly multi-line string: */
938 inf
->string_start_line
= inf
->line_num
;
939 inf
->in_string
= TRUE
;
941 partial
= &inf
->partial
; /* abbreviation */
944 start
= c
++; /* start includes the initial \", to
945 * distinguish from a number */
947 while (*c
!= '\0' && *c
!= border_character
) {
948 /* skip over escaped chars, including backslash-doublequote,
949 * and backslash-backslash: */
950 if (*c
== '\\' && *(c
+ 1) != '\0') {
956 if (*c
== border_character
) {
957 /* Found end of string */
961 astr_add(partial
, "%s\n", start
);
963 if (!read_a_line(inf
)) {
964 /* shouldn't happen */
965 inf_log(inf
, LOG_ERROR
,
966 "Bad return for multi-line string from read_a_line");
969 c
= start
= astr_str(&inf
->cur_line
);
972 /* found end of string */
974 *((char *) c
) = '\0'; /* Tricky. */
976 inf
->cur_line_pos
= c
+ 1 - astr_str(&inf
->cur_line
);
977 astr_set(&inf
->token
, "%s%s", astr_str(partial
), start
);
979 *((char *) c
) = trailing
; /* Revert. */
981 /* check gettext tag at end: */
982 if (has_i18n_marking
) {
986 inf_warn(inf
, "Missing end of i18n string marking");
989 inf
->in_string
= FALSE
;
990 return astr_str(&inf
->token
);