1 /* xgettext awk backend.
2 Copyright (C) 2002-2003, 2005-2009, 2018-2023 Free Software Foundation, Inc.
3
4 This file was written by Bruno Haible <haible@clisp.cons.org>, 2002.
5
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <https://www.gnu.org/licenses/>. */
18
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22
23 /* Specification. */
24 #include "x-awk.h"
25
26 #include <errno.h>
27 #include <stdbool.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31
32 #include "attribute.h"
33 #include "message.h"
34 #include "xgettext.h"
35 #include "xg-pos.h"
36 #include "xg-mixed-string.h"
37 #include "xg-arglist-context.h"
38 #include "xg-arglist-callshape.h"
39 #include "xg-arglist-parser.h"
40 #include "xg-message.h"
41 #include "error.h"
42 #include "error-progname.h"
43 #include "xalloc.h"
44 #include "gettext.h"
45
46 #define _(s) gettext(s)
47
48
49 /* The awk syntax is defined in the gawk manual page and documentation.
50 See also gawk/awkgram.y. */
51
52
53 /* ====================== Keyword set customization. ====================== */
54
55 /* If true extract all strings. */
56 static bool extract_all = false;
57
58 static hash_table keywords;
59 static bool default_keywords = true;
60
61
62 void
63 x_awk_extract_all ()
64 {
65 extract_all = true;
66 }
67
68
69 void
70 x_awk_keyword (const char *name)
71 {
72 if (name == NULL)
73 default_keywords = false;
74 else
75 {
76 const char *end;
77 struct callshape shape;
78 const char *colon;
79
80 if (keywords.table == NULL)
81 hash_init (&keywords, 100);
82
83 split_keywordspec (name, &end, &shape);
84
85 /* The characters between name and end should form a valid C identifier.
86 A colon means an invalid parse in split_keywordspec(). */
87 colon = strchr (name, ':');
88 if (colon == NULL || colon >= end)
89 insert_keyword_callshape (&keywords, name, end - name, &shape);
90 }
91 }
92
93 /* Finish initializing the keywords hash table.
94 Called after argument processing, before each file is processed. */
95 static void
96 init_keywords ()
97 {
98 if (default_keywords)
99 {
100 /* When adding new keywords here, also update the documentation in
101 xgettext.texi! */
102 x_awk_keyword ("dcgettext");
103 x_awk_keyword ("dcngettext:1,2");
104 default_keywords = false;
105 }
106 }
107
108 void
109 init_flag_table_awk ()
110 {
111 xgettext_record_flag ("dcgettext:1:pass-awk-format");
112 xgettext_record_flag ("dcngettext:1:pass-awk-format");
113 xgettext_record_flag ("dcngettext:2:pass-awk-format");
114 xgettext_record_flag ("printf:1:awk-format");
115 }
116
117
118 /* ======================== Reading of characters. ======================== */
119
120 /* The input file stream. */
121 static FILE *fp;
122
123 /* These are for tracking whether comments count as immediately before
124 keyword. */
125 static int last_comment_line;
126 static int last_non_comment_line;
127
128
129 /* 1. line_number handling. */
130
131 static int
132 phase1_getc ()
133 {
134 int c = getc (fp);
135
136 if (c == EOF)
137 {
138 if (ferror (fp))
139 error (EXIT_FAILURE, errno, _("error while reading \"%s\""),
140 real_file_name);
141 return EOF;
142 }
143
144 if (c == '\n')
145 line_number++;
146
147 return c;
148 }
149
150 /* Supports only one pushback character. */
151 static void
152 phase1_ungetc (int c)
153 {
154 if (c != EOF)
155 {
156 if (c == '\n')
157 --line_number;
158
159 ungetc (c, fp);
160 }
161 }
162
163
164 /* 2. Replace each comment that is not inside a string literal or regular
165 expression with a newline character. We need to remember the comment
166 for later, because it may be attached to a keyword string. */
167
168 static int
169 phase2_getc ()
170 {
171 static char *buffer;
172 static size_t bufmax;
173 size_t buflen;
174 int lineno;
175 int c;
176
177 c = phase1_getc ();
178 if (c == '#')
179 {
180 buflen = 0;
181 lineno = line_number;
182 for (;;)
183 {
184 c = phase1_getc ();
185 if (c == '\n' || c == EOF)
186 break;
187 /* We skip all leading white space, but not EOLs. */
188 if (!(buflen == 0 && (c == ' ' || c == '\t')))
189 {
190 if (buflen >= bufmax)
191 {
192 bufmax = 2 * bufmax + 10;
193 buffer = xrealloc (buffer, bufmax);
194 }
195 buffer[buflen++] = c;
196 }
197 }
198 if (buflen >= bufmax)
199 {
200 bufmax = 2 * bufmax + 10;
201 buffer = xrealloc (buffer, bufmax);
202 }
203 buffer[buflen] = '\0';
204 savable_comment_add (buffer);
205 last_comment_line = lineno;
206 }
207 return c;
208 }
209
210 /* Supports only one pushback character. */
211 static void
212 phase2_ungetc (int c)
213 {
214 if (c != EOF)
215 phase1_ungetc (c);
216 }
217
218
219 /* ========================== Reading of tokens. ========================== */
220
221
222 enum token_type_ty
223 {
224 token_type_eof,
225 token_type_lparen, /* ( */
226 token_type_rparen, /* ) */
227 token_type_comma, /* , */
228 token_type_string, /* "abc" */
229 token_type_i18nstring, /* _"abc" */
230 token_type_symbol, /* symbol, number */
231 token_type_semicolon, /* ; */
232 token_type_other /* regexp, misc. operator */
233 };
234 typedef enum token_type_ty token_type_ty;
235
236 typedef struct token_ty token_ty;
237 struct token_ty
238 {
239 token_type_ty type;
240 char *string; /* for token_type_{symbol,string,i18nstring} */
241 int line_number;
242 };
243
244
245 /* 7. Replace escape sequences within character strings with their
246 single character equivalents. */
247
248 #define P7_QUOTES (1000 + '"')
249
250 static int
251 phase7_getc ()
252 {
253 int c;
254
255 for (;;)
256 {
257 /* Use phase 1, because phase 2 elides comments. */
258 c = phase1_getc ();
259
260 if (c == EOF || c == '\n')
261 break;
262 if (c == '"')
263 return P7_QUOTES;
264 if (c != '\\')
265 return c;
266 c = phase1_getc ();
267 if (c == EOF)
268 break;
269 if (c != '\n')
270 switch (c)
271 {
272 case 'a':
273 return '\a';
274 case 'b':
275 return '\b';
276 case 'f':
277 return '\f';
278 case 'n':
279 return '\n';
280 case 'r':
281 return '\r';
282 case 't':
283 return '\t';
284 case 'v':
285 return '\v';
286 case '0': case '1': case '2': case '3': case '4':
287 case '5': case '6': case '7':
288 {
289 int n = c - '0';
290
291 c = phase1_getc ();
292 if (c != EOF)
293 {
294 if (c >= '0' && c <= '7')
295 {
296 n = (n << 3) + (c - '0');
297 c = phase1_getc ();
298 if (c != EOF)
299 {
300 if (c >= '0' && c <= '7')
301 n = (n << 3) + (c - '0');
302 else
303 phase1_ungetc (c);
304 }
305 }
306 else
307 phase1_ungetc (c);
308 }
309 return (unsigned char) n;
310 }
311 case 'x':
312 {
313 int n = 0;
314
315 for (;;)
316 {
317 c = phase1_getc ();
318 if (c == EOF)
319 break;
320 else if (c >= '0' && c <= '9')
321 n = (n << 4) + (c - '0');
322 else if (c >= 'A' && c <= 'F')
323 n = (n << 4) + (c - 'A' + 10);
324 else if (c >= 'a' && c <= 'f')
325 n = (n << 4) + (c - 'a' + 10);
326 else
327 {
328 phase1_ungetc (c);
329 break;
330 }
331 }
332 return (unsigned char) n;
333 }
334 default:
335 return c;
336 }
337 }
338
339 phase1_ungetc (c);
340 error_with_progname = false;
341 error (0, 0, _("%s:%d: warning: unterminated string"), logical_file_name,
342 line_number);
343 error_with_progname = true;
344 return P7_QUOTES;
345 }
346
347
348 /* Free the memory pointed to by a 'struct token_ty'. */
349 static inline void
350 free_token (token_ty *tp)
351 {
352 switch (tp->type)
353 {
354 case token_type_string:
355 case token_type_i18nstring:
356 case token_type_symbol:
357 free (tp->string);
358 break;
359 default:
360 break;
361 }
362 }
363
364
365 /* Combine characters into tokens. Discard whitespace. */
366
367 /* There is an ambiguity about '/': It can start a division operator ('/' or
368 '/=') or it can start a regular expression. The distinction is important
369 because inside regular expressions, '#' and '"' lose its special meanings.
370 If you look at the awk grammar, you see that the operator is only allowed
371 right after a 'variable' or 'simp_exp' nonterminal, and these nonterminals
372 can only end in the NAME, LENGTH, YSTRING, YNUMBER, ')', ']' terminals.
373 So we prefer the division operator interpretation only right after
374 symbol, string, number, ')', ']', with whitespace but no newline allowed
375 in between. */
376 static bool prefer_division_over_regexp;
377
378 static void
379 x_awk_lex (token_ty *tp)
380 {
381 static char *buffer;
382 static int bufmax;
383 int bufpos;
384 int c;
385
386 for (;;)
387 {
388 tp->line_number = line_number;
389 c = phase2_getc ();
390
391 switch (c)
392 {
393 case EOF:
394 tp->type = token_type_eof;
395 return;
396
397 case '\n':
398 if (last_non_comment_line > last_comment_line)
399 savable_comment_reset ();
400 /* Newline is not allowed inside expressions. It usually
401 introduces a fresh statement.
402 FIXME: Newlines after any of ',' '{' '?' ':' '||' '&&' 'do' 'else'
403 does *not* introduce a fresh statement. */
404 prefer_division_over_regexp = false;
405 FALLTHROUGH;
406 case '\t':
407 case ' ':
408 /* Ignore whitespace and comments. */
409 continue;
410
411 case '\\':
412 /* Backslash ought to be immediately followed by a newline. */
413 continue;
414 }
415
416 last_non_comment_line = tp->line_number;
417
418 switch (c)
419 {
420 case '.':
421 {
422 int c2 = phase2_getc ();
423 phase2_ungetc (c2);
424 if (!(c2 >= '0' && c2 <= '9'))
425 {
426
427 tp->type = token_type_other;
428 prefer_division_over_regexp = false;
429 return;
430 }
431 }
432 FALLTHROUGH;
433 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
434 case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
435 case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
436 case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
437 case 'Y': case 'Z':
438 case '_':
439 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
440 case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
441 case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
442 case 's': case 't': case 'u': case 'v': case 'w': case 'x':
443 case 'y': case 'z':
444 case '0': case '1': case '2': case '3': case '4':
445 case '5': case '6': case '7': case '8': case '9':
446 /* Symbol, or part of a number. */
447 bufpos = 0;
448 for (;;)
449 {
450 if (bufpos >= bufmax)
451 {
452 bufmax = 2 * bufmax + 10;
453 buffer = xrealloc (buffer, bufmax);
454 }
455 buffer[bufpos++] = c;
456 c = phase2_getc ();
457 switch (c)
458 {
459 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
460 case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
461 case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
462 case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
463 case 'Y': case 'Z':
464 case '_':
465 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
466 case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
467 case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
468 case 's': case 't': case 'u': case 'v': case 'w': case 'x':
469 case 'y': case 'z':
470 case '0': case '1': case '2': case '3': case '4':
471 case '5': case '6': case '7': case '8': case '9':
472 continue;
473 default:
474 if (bufpos == 1 && buffer[0] == '_' && c == '"')
475 {
476 tp->type = token_type_i18nstring;
477 goto case_string;
478 }
479 phase2_ungetc (c);
480 break;
481 }
482 break;
483 }
484 if (bufpos >= bufmax)
485 {
486 bufmax = 2 * bufmax + 10;
487 buffer = xrealloc (buffer, bufmax);
488 }
489 buffer[bufpos] = '\0';
490 tp->string = xstrdup (buffer);
491 tp->type = token_type_symbol;
492 /* Most identifiers can be variable names; after them we must
493 interpret '/' as division operator. But for awk's builtin
494 keywords we have three cases:
495 (a) Must interpret '/' as division operator. "length".
496 (b) Must interpret '/' as start of a regular expression.
497 "do", "exit", "print", "printf", "return".
498 (c) '/' after this keyword in invalid anyway. All others.
499 I used the following script for the distinction.
500 for k in $awk_keywords; do
501 echo; echo $k; awk "function foo () { $k / 10 }" < /dev/null
502 done
503 */
504 if (strcmp (buffer, "do") == 0
505 || strcmp (buffer, "exit") == 0
506 || strcmp (buffer, "print") == 0
507 || strcmp (buffer, "printf") == 0
508 || strcmp (buffer, "return") == 0)
509 prefer_division_over_regexp = false;
510 else
511 prefer_division_over_regexp = true;
512 return;
513
514 case '"':
515 tp->type = token_type_string;
516 case_string:
517 bufpos = 0;
518 for (;;)
519 {
520 c = phase7_getc ();
521 if (c == EOF || c == P7_QUOTES)
522 break;
523 if (bufpos >= bufmax)
524 {
525 bufmax = 2 * bufmax + 10;
526 buffer = xrealloc (buffer, bufmax);
527 }
528 buffer[bufpos++] = c;
529 }
530 if (bufpos >= bufmax)
531 {
532 bufmax = 2 * bufmax + 10;
533 buffer = xrealloc (buffer, bufmax);
534 }
535 buffer[bufpos] = '\0';
536 tp->string = xstrdup (buffer);
537 prefer_division_over_regexp = true;
538 return;
539
540 case '(':
541 tp->type = token_type_lparen;
542 prefer_division_over_regexp = false;
543 return;
544
545 case ')':
546 tp->type = token_type_rparen;
547 prefer_division_over_regexp = true;
548 return;
549
550 case ',':
551 tp->type = token_type_comma;
552 prefer_division_over_regexp = false;
553 return;
554
555 case ';':
556 tp->type = token_type_semicolon;
557 prefer_division_over_regexp = false;
558 return;
559
560 case ']':
561 tp->type = token_type_other;
562 prefer_division_over_regexp = true;
563 return;
564
565 case '/':
566 if (!prefer_division_over_regexp)
567 {
568 /* Regular expression.
569 Counting brackets is non-trivial. [[] is balanced, and so is
570 [\]]. Also, /[/]/ is balanced and ends at the third slash.
571 Do not count [ or ] if either one is preceded by a \.
572 A '[' should be counted if
573 a) it is the first one so far (brackets == 0), or
574 b) it is the '[' in '[:'.
575 A ']' should be counted if not preceded by a \.
576 According to POSIX, []] is how you put a ] into a set.
577 Try to handle that too.
578 */
579 int brackets = 0;
580 bool pos0 = true; /* true at start of regexp */
581 bool pos1_open = false; /* true after [ at start of regexp */
582 bool pos2_open_not = false; /* true after [^ at start of regexp */
583
584 for (;;)
585 {
586 c = phase1_getc ();
587
588 if (c == EOF || c == '\n')
589 {
590 phase1_ungetc (c);
591 error_with_progname = false;
592 error (0, 0, _("%s:%d: warning: unterminated regular expression"),
593 logical_file_name, line_number);
594 error_with_progname = true;
595 break;
596 }
597 else if (c == '[')
598 {
599 if (brackets == 0)
600 brackets++;
601 else
602 {
603 c = phase1_getc ();
604 if (c == ':')
605 brackets++;
606 phase1_ungetc (c);
607 }
608 if (pos0)
609 {
610 pos0 = false;
611 pos1_open = true;
612 continue;
613 }
614 }
615 else if (c == ']')
616 {
617 if (!(pos1_open || pos2_open_not))
618 brackets--;
619 }
620 else if (c == '^')
621 {
622 if (pos1_open)
623 {
624 pos1_open = false;
625 pos2_open_not = true;
626 continue;
627 }
628 }
629 else if (c == '\\')
630 {
631 c = phase1_getc ();
632 /* Backslash-newline is valid and ignored. */
633 }
634 else if (c == '/')
635 {
636 if (brackets <= 0)
637 break;
638 }
639
640 pos0 = false;
641 pos1_open = false;
642 pos2_open_not = false;
643 }
644
645 tp->type = token_type_other;
646 prefer_division_over_regexp = false;
647 return;
648 }
649 FALLTHROUGH;
650
651 default:
652 /* We could carefully recognize each of the 2 and 3 character
653 operators, but it is not necessary, as we only need to recognize
654 gettext invocations. Don't bother. */
655 tp->type = token_type_other;
656 prefer_division_over_regexp = false;
657 return;
658 }
659 }
660 }
661
662
663 /* ========================= Extracting strings. ========================== */
664
665
666 /* Context lookup table. */
667 static flag_context_list_table_ty *flag_context_list_table;
668
669
670 /* Maximum supported nesting depth. */
671 #define MAX_NESTING_DEPTH 1000
672
673 /* Current nesting depth. */
674 static int nesting_depth;
675
676
677 /* The file is broken into tokens. Scan the token stream, looking for
678 a keyword, followed by a left paren, followed by a string. When we
679 see this sequence, we have something to remember. We assume we are
680 looking at a valid C or C++ program, and leave the complaints about
681 the grammar to the compiler.
682
683 Normal handling: Look for
684 keyword ( ... msgid ... )
685 Plural handling: Look for
686 keyword ( ... msgid ... msgid_plural ... )
687
688 We use recursion because the arguments before msgid or between msgid
689 and msgid_plural can contain subexpressions of the same form. */
690
691
692 /* Extract messages until the next balanced closing parenthesis.
693 Extracted messages are added to MLP.
694 Return true upon eof, false upon closing parenthesis. */
695 static bool
696 extract_parenthesized (message_list_ty *mlp,
697 flag_context_ty outer_context,
698 flag_context_list_iterator_ty context_iter,
699 struct arglist_parser *argparser)
700 {
701 /* Current argument number. */
702 int arg = 1;
703 /* 0 when no keyword has been seen. 1 right after a keyword is seen. */
704 int state;
705 /* Parameters of the keyword just seen. Defined only in state 1. */
706 const struct callshapes *next_shapes = NULL;
707 /* Whether to implicitly assume the next tokens are arguments even without
708 a '('. */
709 bool next_is_argument = false;
710 /* Context iterator that will be used if the next token is a '('. */
711 flag_context_list_iterator_ty next_context_iter =
712 passthrough_context_list_iterator;
713 /* Current context. */
714 flag_context_ty inner_context =
715 inherited_context (outer_context,
716 flag_context_list_iterator_advance (&context_iter));
717
718 /* Start state is 0. */
719 state = 0;
720
721 for (;;)
722 {
723 token_ty token;
724
725 x_awk_lex (&token);
726
727 if (next_is_argument && token.type != token_type_lparen)
728 {
729 /* An argument list starts, even though there is no '('. */
730 context_iter = next_context_iter;
731 outer_context = inner_context;
732 inner_context =
733 inherited_context (outer_context,
734 flag_context_list_iterator_advance (
735 &context_iter));
736 }
737
738 switch (token.type)
739 {
740 case token_type_symbol:
741 {
742 void *keyword_value;
743
744 if (hash_find_entry (&keywords, token.string, strlen (token.string),
745 &keyword_value)
746 == 0)
747 {
748 next_shapes = (const struct callshapes *) keyword_value;
749 state = 1;
750 }
751 else
752 state = 0;
753 }
754 next_is_argument =
755 (strcmp (token.string, "print") == 0
756 || strcmp (token.string, "printf") == 0);
757 next_context_iter =
758 flag_context_list_iterator (
759 flag_context_list_table_lookup (
760 flag_context_list_table,
761 token.string, strlen (token.string)));
762 free (token.string);
763 continue;
764
765 case token_type_lparen:
766 if (++nesting_depth > MAX_NESTING_DEPTH)
767 {
768 error_with_progname = false;
769 error (EXIT_FAILURE, 0, _("%s:%d: error: too many open parentheses"),
770 logical_file_name, line_number);
771 }
772 if (extract_parenthesized (mlp, inner_context, next_context_iter,
773 arglist_parser_alloc (mlp,
774 state ? next_shapes : NULL)))
775 {
776 arglist_parser_done (argparser, arg);
777 return true;
778 }
779 nesting_depth--;
780 next_is_argument = false;
781 next_context_iter = null_context_list_iterator;
782 state = 0;
783 continue;
784
785 case token_type_rparen:
786 arglist_parser_done (argparser, arg);
787 return false;
788
789 case token_type_comma:
790 arg++;
791 inner_context =
792 inherited_context (outer_context,
793 flag_context_list_iterator_advance (
794 &context_iter));
795 next_is_argument = false;
796 next_context_iter = passthrough_context_list_iterator;
797 state = 0;
798 continue;
799
800 case token_type_string:
801 {
802 lex_pos_ty pos;
803 pos.file_name = logical_file_name;
804 pos.line_number = token.line_number;
805
806 if (extract_all)
807 remember_a_message (mlp, NULL, token.string, false, false,
808 inner_context, &pos,
809 NULL, savable_comment, false);
810 else
811 {
812 mixed_string_ty *ms =
813 mixed_string_alloc_simple (token.string, lc_string,
814 pos.file_name, pos.line_number);
815 free (token.string);
816 arglist_parser_remember (argparser, arg, ms,
817 inner_context,
818 pos.file_name, pos.line_number,
819 savable_comment, false);
820 }
821 }
822 next_is_argument = false;
823 next_context_iter = null_context_list_iterator;
824 state = 0;
825 continue;
826
827 case token_type_i18nstring:
828 {
829 lex_pos_ty pos;
830 pos.file_name = logical_file_name;
831 pos.line_number = token.line_number;
832
833 remember_a_message (mlp, NULL, token.string, false, false,
834 inner_context, &pos,
835 NULL, savable_comment, false);
836 }
837 next_is_argument = false;
838 next_context_iter = null_context_list_iterator;
839 state = 0;
840 continue;
841
842 case token_type_semicolon:
843 /* An argument list ends, and a new statement begins. */
844 /* FIXME: Should handle newline that acts as statement separator
845 in the same way. */
846 /* FIXME: Instead of resetting outer_context here, it may be better
847 to recurse in the next_is_argument handling above, waiting for
848 the next semicolon or other statement terminator. */
849 outer_context = null_context;
850 context_iter = null_context_list_iterator;
851 next_is_argument = false;
852 next_context_iter = passthrough_context_list_iterator;
853 inner_context =
854 inherited_context (outer_context,
855 flag_context_list_iterator_advance (
856 &context_iter));
857 state = 0;
858 continue;
859
860 case token_type_eof:
861 arglist_parser_done (argparser, arg);
862 return true;
863
864 case token_type_other:
865 next_is_argument = false;
866 next_context_iter = null_context_list_iterator;
867 state = 0;
868 continue;
869
870 default:
871 abort ();
872 }
873 }
874 }
875
876
877 void
878 extract_awk (FILE *f,
879 const char *real_filename, const char *logical_filename,
880 flag_context_list_table_ty *flag_table,
881 msgdomain_list_ty *mdlp)
882 {
883 message_list_ty *mlp = mdlp->item[0]->messages;
884
885 fp = f;
886 real_file_name = real_filename;
887 logical_file_name = xstrdup (logical_filename);
888 line_number = 1;
889
890 last_comment_line = -1;
891 last_non_comment_line = -1;
892
893 prefer_division_over_regexp = false;
894
895 flag_context_list_table = flag_table;
896 nesting_depth = 0;
897
898 init_keywords ();
899
900 /* Eat tokens until eof is seen. When extract_parenthesized returns
901 due to an unbalanced closing parenthesis, just restart it. */
902 while (!extract_parenthesized (mlp, null_context, null_context_list_iterator,
903 arglist_parser_alloc (mlp, NULL)))
904 ;
905
906 fp = NULL;
907 real_file_name = NULL;
908 logical_file_name = NULL;
909 line_number = 0;
910 }