1 /* xgettext PHP backend.
2 Copyright (C) 2001-2003, 2005-2010, 2015 Free Software Foundation,
5 This file was written by Bruno Haible <bruno@clisp.org>, 2002.
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
38 #define _(s) gettext(s)
40 #define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
43 /* The PHP syntax is defined in phpdoc/manual/langref.html.
44 See also php-4.1.0/Zend/zend_language_scanner.l
45 and php-4.1.0/Zend/zend_language_parser.y.
46 Note that variable and function names can contain bytes in the range
48 http://www.php.net/manual/en/language.variables.php
49 http://www.php.net/manual/en/language.functions.php */
52 /* ====================== Keyword set customization. ====================== */
54 /* If true extract all strings. */
55 static bool extract_all = false;
57 static hash_table keywords;
58 static bool default_keywords = true;
69 x_php_keyword (const char *name)
72 default_keywords = false;
76 struct callshape shape;
79 if (keywords.table == NULL)
80 hash_init (&keywords, 100);
82 split_keywordspec (name, &end, &shape);
84 /* The characters between name and end should form a valid C identifier.
85 A colon means an invalid parse in split_keywordspec(). */
86 colon = strchr (name, ':');
87 if (colon == NULL || colon >= end)
88 insert_keyword_callshape (&keywords, name, end - name, &shape);
92 /* Finish initializing the keywords hash table.
93 Called after argument processing, before each file is processed. */
99 /* When adding new keywords here, also update the documentation in
102 x_php_keyword ("gettext");
103 x_php_keyword ("dgettext:2");
104 x_php_keyword ("dcgettext:2");
105 /* The following were added in PHP 4.2.0. */
106 x_php_keyword ("ngettext:1,2");
107 x_php_keyword ("dngettext:2,3");
108 x_php_keyword ("dcngettext:2,3");
109 default_keywords = false;
114 init_flag_table_php ()
116 xgettext_record_flag ("_:1:pass-php-format");
117 xgettext_record_flag ("gettext:1:pass-php-format");
118 xgettext_record_flag ("dgettext:2:pass-php-format");
119 xgettext_record_flag ("dcgettext:2:pass-php-format");
120 xgettext_record_flag ("ngettext:1:pass-php-format");
121 xgettext_record_flag ("ngettext:2:pass-php-format");
122 xgettext_record_flag ("dngettext:2:pass-php-format");
123 xgettext_record_flag ("dngettext:3:pass-php-format");
124 xgettext_record_flag ("dcngettext:2:pass-php-format");
125 xgettext_record_flag ("dcngettext:3:pass-php-format");
126 xgettext_record_flag ("sprintf:1:php-format");
127 xgettext_record_flag ("printf:1:php-format");
131 /* ======================== Reading of characters. ======================== */
134 /* Real filename, used in error messages about the input file. */
135 static const char *real_file_name;
137 /* Logical filename and line number, used to label the extracted messages. */
138 static char *logical_file_name;
139 static int line_number;
141 /* The input file stream. */
145 /* 1. line_number handling. */
147 static unsigned char phase1_pushback[2];
148 static int phase1_pushback_length;
155 if (phase1_pushback_length)
156 c = phase1_pushback[--phase1_pushback_length];
164 error (EXIT_FAILURE, errno, _("error while reading \"%s\""),
176 /* Supports 2 characters of pushback. */
178 phase1_ungetc (int c)
185 if (phase1_pushback_length == SIZEOF (phase1_pushback))
187 phase1_pushback[phase1_pushback_length++] = c;
192 /* 2. Ignore HTML sections. They are equivalent to PHP echo commands and
193 therefore don't contain translatable strings. */
200 int c = phase1_getc ();
207 int c2 = phase1_getc ();
214 /* <?php is the normal way to enter PHP mode. <? and <?= are
215 recognized by PHP depending on a configuration setting. */
216 int c3 = phase1_getc ();
226 /* <% and <%= are recognized by PHP depending on a configuration
228 int c3 = phase1_getc ();
242 /* < script language = php >
243 < script language = "php" >
244 < script language = 'php' >
245 are always recognized. */
246 while (c2 == ' ' || c2 == '\t' || c2 == '\n' || c2 == '\r')
248 if (c2 != 's' && c2 != 'S')
254 if (c2 != 'c' && c2 != 'C')
260 if (c2 != 'r' && c2 != 'R')
266 if (c2 != 'i' && c2 != 'I')
272 if (c2 != 'p' && c2 != 'P')
278 if (c2 != 't' && c2 != 'T')
284 if (!(c2 == ' ' || c2 == '\t' || c2 == '\n' || c2 == '\r'))
291 while (c2 == ' ' || c2 == '\t' || c2 == '\n' || c2 == '\r');
292 if (c2 != 'l' && c2 != 'L')
298 if (c2 != 'a' && c2 != 'A')
304 if (c2 != 'n' && c2 != 'N')
310 if (c2 != 'g' && c2 != 'G')
316 if (c2 != 'u' && c2 != 'U')
322 if (c2 != 'a' && c2 != 'A')
328 if (c2 != 'g' && c2 != 'G')
334 if (c2 != 'e' && c2 != 'E')
340 while (c2 == ' ' || c2 == '\t' || c2 == '\n' || c2 == '\r')
348 while (c2 == ' ' || c2 == '\t' || c2 == '\n' || c2 == '\r')
425 while (c2 == ' ' || c2 == '\t' || c2 == '\n' || c2 == '\r')
439 static unsigned char phase2_pushback[1];
440 static int phase2_pushback_length;
447 if (phase2_pushback_length)
448 return phase2_pushback[--phase2_pushback_length];
456 int c2 = phase1_getc ();
459 /* ?> and %> terminate PHP mode and switch back to HTML mode. */
469 int c2 = phase1_getc ();
471 /* < / script > terminates PHP mode and switches back to HTML mode. */
472 while (c2 == ' ' || c2 == '\t' || c2 == '\n' || c2 == '\r')
478 while (c2 == ' ' || c2 == '\t' || c2 == '\n' || c2 == '\r');
479 if (c2 == 's' || c2 == 'S')
482 if (c2 == 'c' || c2 == 'C')
485 if (c2 == 'r' || c2 == 'R')
488 if (c2 == 'i' || c2 == 'I')
491 if (c2 == 'p' || c2 == 'P')
494 if (c2 == 't' || c2 == 'T')
498 while (c2 == ' ' || c2 == '\t'
499 || c2 == '\n' || c2 == '\r');
521 phase2_ungetc (int c)
525 if (phase2_pushback_length == SIZEOF (phase2_pushback))
527 phase2_pushback[phase2_pushback_length++] = c;
534 /* Accumulating comments. */
537 static size_t bufmax;
538 static size_t buflen;
549 if (buflen >= bufmax)
551 bufmax = 2 * bufmax + 10;
552 buffer = xrealloc (buffer, bufmax);
554 buffer[buflen++] = c;
558 comment_line_end (size_t chars_to_remove)
560 buflen -= chars_to_remove;
562 && (buffer[buflen - 1] == ' ' || buffer[buflen - 1] == '\t'))
564 if (chars_to_remove == 0 && buflen >= bufmax)
566 bufmax = 2 * bufmax + 10;
567 buffer = xrealloc (buffer, bufmax);
569 buffer[buflen] = '\0';
570 savable_comment_add (buffer);
574 /* 3. Replace each comment that is not inside a string literal with a
575 space character. We need to remember the comment for later, because
576 it may be attached to a keyword string. */
578 /* These are for tracking whether comments count as immediately before
580 static int last_comment_line;
581 static int last_non_comment_line;
583 static unsigned char phase3_pushback[1];
584 static int phase3_pushback_length;
592 if (phase3_pushback_length)
593 return phase3_pushback[--phase3_pushback_length];
600 bool last_was_qmark = false;
603 lineno = line_number;
607 if (c == '\n' || c == EOF)
609 comment_line_end (0);
612 if (last_was_qmark && c == '>')
614 comment_line_end (1);
618 /* We skip all leading white space, but not EOLs. */
619 if (!(buflen == 0 && (c == ' ' || c == '\t')))
621 last_was_qmark = (c == '?' || c == '%');
623 last_comment_line = lineno;
642 lineno = line_number;
643 last_was_star = false;
649 /* We skip all leading white space, but not EOLs. */
650 if (buflen == 0 && (c == ' ' || c == '\t'))
656 comment_line_end (1);
658 lineno = line_number;
659 last_was_star = false;
663 last_was_star = true;
669 comment_line_end (2);
675 last_was_star = false;
680 last_comment_line = lineno;
687 bool last_was_qmark = false;
690 lineno = line_number;
694 if (c == '\n' || c == EOF)
696 comment_line_end (0);
699 if (last_was_qmark && c == '>')
701 comment_line_end (1);
705 /* We skip all leading white space, but not EOLs. */
706 if (!(buflen == 0 && (c == ' ' || c == '\t')))
708 last_was_qmark = (c == '?' || c == '%');
710 last_comment_line = lineno;
721 phase3_ungetc (int c)
725 if (phase3_pushback_length == SIZEOF (phase3_pushback))
727 phase3_pushback[phase3_pushback_length++] = c;
733 /* ========================== Reading of tokens. ========================== */
739 token_type_lparen, /* ( */
740 token_type_rparen, /* ) */
741 token_type_comma, /* , */
742 token_type_lbracket, /* [ */
743 token_type_rbracket, /* ] */
744 token_type_dot, /* . */
745 token_type_operator1, /* * / % ++ -- */
746 token_type_operator2, /* + - ! ~ @ */
747 token_type_string_literal, /* "abc" */
748 token_type_symbol, /* symbol, number */
749 token_type_other /* misc. operator */
751 typedef enum token_type_ty token_type_ty;
753 typedef struct token_ty token_ty;
757 char *string; /* for token_type_string_literal, token_type_symbol */
758 refcounted_string_list_ty *comment; /* for token_type_string_literal */
763 /* Free the memory pointed to by a 'struct token_ty'. */
765 free_token (token_ty *tp)
767 if (tp->type == token_type_string_literal || tp->type == token_type_symbol)
769 if (tp->type == token_type_string_literal)
770 drop_reference (tp->comment);
774 /* 4. Combine characters into tokens. Discard whitespace. */
776 static token_ty phase4_pushback[3];
777 static int phase4_pushback_length;
780 phase4_get (token_ty *tp)
787 if (phase4_pushback_length)
789 *tp = phase4_pushback[--phase4_pushback_length];
796 tp->line_number = line_number;
801 tp->type = token_type_eof;
805 if (last_non_comment_line > last_comment_line)
806 savable_comment_reset ();
811 /* Ignore whitespace. */
815 last_non_comment_line = tp->line_number;
819 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
820 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
821 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
822 case 'V': case 'W': case 'X': case 'Y': case 'Z':
824 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
825 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
826 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
827 case 'v': case 'w': case 'x': case 'y': case 'z':
828 case 127: case 128: case 129: case 130: case 131: case 132: case 133:
829 case 134: case 135: case 136: case 137: case 138: case 139: case 140:
830 case 141: case 142: case 143: case 144: case 145: case 146: case 147:
831 case 148: case 149: case 150: case 151: case 152: case 153: case 154:
832 case 155: case 156: case 157: case 158: case 159: case 160: case 161:
833 case 162: case 163: case 164: case 165: case 166: case 167: case 168:
834 case 169: case 170: case 171: case 172: case 173: case 174: case 175:
835 case 176: case 177: case 178: case 179: case 180: case 181: case 182:
836 case 183: case 184: case 185: case 186: case 187: case 188: case 189:
837 case 190: case 191: case 192: case 193: case 194: case 195: case 196:
838 case 197: case 198: case 199: case 200: case 201: case 202: case 203:
839 case 204: case 205: case 206: case 207: case 208: case 209: case 210:
840 case 211: case 212: case 213: case 214: case 215: case 216: case 217:
841 case 218: case 219: case 220: case 221: case 222: case 223: case 224:
842 case 225: case 226: case 227: case 228: case 229: case 230: case 231:
843 case 232: case 233: case 234: case 235: case 236: case 237: case 238:
844 case 239: case 240: case 241: case 242: case 243: case 244: case 245:
845 case 246: case 247: case 248: case 249: case 250: case 251: case 252:
846 case 253: case 254: case 255:
850 if (bufpos >= bufmax)
852 bufmax = 2 * bufmax + 10;
853 buffer = xrealloc (buffer, bufmax);
855 buffer[bufpos++] = c;
859 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
860 case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
861 case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
862 case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
865 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
866 case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
867 case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
868 case 's': case 't': case 'u': case 'v': case 'w': case 'x':
870 case '0': case '1': case '2': case '3': case '4':
871 case '5': case '6': case '7': case '8': case '9':
872 case 127: case 128: case 129: case 130: case 131: case 132:
873 case 133: case 134: case 135: case 136: case 137: case 138:
874 case 139: case 140: case 141: case 142: case 143: case 144:
875 case 145: case 146: case 147: case 148: case 149: case 150:
876 case 151: case 152: case 153: case 154: case 155: case 156:
877 case 157: case 158: case 159: case 160: case 161: case 162:
878 case 163: case 164: case 165: case 166: case 167: case 168:
879 case 169: case 170: case 171: case 172: case 173: case 174:
880 case 175: case 176: case 177: case 178: case 179: case 180:
881 case 181: case 182: case 183: case 184: case 185: case 186:
882 case 187: case 188: case 189: case 190: case 191: case 192:
883 case 193: case 194: case 195: case 196: case 197: case 198:
884 case 199: case 200: case 201: case 202: case 203: case 204:
885 case 205: case 206: case 207: case 208: case 209: case 210:
886 case 211: case 212: case 213: case 214: case 215: case 216:
887 case 217: case 218: case 219: case 220: case 221: case 222:
888 case 223: case 224: case 225: case 226: case 227: case 228:
889 case 229: case 230: case 231: case 232: case 233: case 234:
890 case 235: case 236: case 237: case 238: case 239: case 240:
891 case 241: case 242: case 243: case 244: case 245: case 246:
892 case 247: case 248: case 249: case 250: case 251: case 252:
893 case 253: case 254: case 255:
902 if (bufpos >= bufmax)
904 bufmax = 2 * bufmax + 10;
905 buffer = xrealloc (buffer, bufmax);
908 tp->string = xstrdup (buffer);
909 tp->type = token_type_symbol;
913 /* Single-quoted string literal. */
918 if (c == EOF || c == '\'')
923 if (c != '\\' && c != '\'')
929 if (bufpos >= bufmax)
931 bufmax = 2 * bufmax + 10;
932 buffer = xrealloc (buffer, bufmax);
934 buffer[bufpos++] = c;
936 if (bufpos >= bufmax)
938 bufmax = 2 * bufmax + 10;
939 buffer = xrealloc (buffer, bufmax);
942 tp->type = token_type_string_literal;
943 tp->string = xstrdup (buffer);
944 tp->comment = add_reference (savable_comment);
948 /* Double-quoted string literal. */
949 tp->type = token_type_string_literal;
954 if (c == EOF || c == '"')
959 if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
960 || c == '_' || c == '{' || c >= 0x7f)
962 /* String with variables. */
963 tp->type = token_type_other;
974 /* String with expressions. */
975 tp->type = token_type_other;
993 case '0': case '1': case '2': case '3':
994 case '4': case '5': case '6': case '7':
996 for (j = 0; j < 3; ++j)
1005 case '0': case '1': case '2': case '3':
1006 case '4': case '5': case '6': case '7':
1017 for (j = 0; j < 2; ++j)
1022 case '0': case '1': case '2': case '3': case '4':
1023 case '5': case '6': case '7': case '8': case '9':
1024 n = n * 16 + c - '0';
1026 case 'A': case 'B': case 'C': case 'D': case 'E':
1028 n = n * 16 + 10 + c - 'A';
1030 case 'a': case 'b': case 'c': case 'd': case 'e':
1032 n = n * 16 + 10 + c - 'a';
1044 phase1_ungetc ('x');
1067 if (bufpos >= bufmax)
1069 bufmax = 2 * bufmax + 10;
1070 buffer = xrealloc (buffer, bufmax);
1072 buffer[bufpos++] = c;
1074 if (bufpos >= bufmax)
1076 bufmax = 2 * bufmax + 10;
1077 buffer = xrealloc (buffer, bufmax);
1080 if (tp->type == token_type_string_literal)
1082 tp->string = xstrdup (buffer);
1083 tp->comment = add_reference (savable_comment);
1090 int c2 = phase1_getc ();
1093 /* ?> and %> terminate PHP mode and switch back to HTML
1096 tp->type = token_type_other;
1101 tp->type = (c == '%' ? token_type_operator1 : token_type_other);
1107 tp->type = token_type_lparen;
1111 tp->type = token_type_rparen;
1115 tp->type = token_type_comma;
1119 tp->type = token_type_lbracket;
1123 tp->type = token_type_rbracket;
1127 tp->type = token_type_dot;
1132 tp->type = token_type_operator1;
1138 int c2 = phase1_getc ();
1141 tp->type = token_type_operator1;
1146 tp->type = token_type_operator2;
1154 tp->type = token_type_operator2;
1159 int c2 = phase1_getc ();
1162 int c3 = phase1_getc ();
1165 int label_start = 0;
1167 /* Start of here and now document.
1168 Parse whitespace, then label, then newline. */
1171 while (c == ' ' || c == '\t' || c == '\n' || c == '\r');
1176 if (bufpos >= bufmax)
1178 bufmax = 2 * bufmax + 10;
1179 buffer = xrealloc (buffer, bufmax);
1181 buffer[bufpos++] = c;
1184 while (c != EOF && c != '\n' && c != '\r');
1185 /* buffer[0..bufpos-1] now contains the label
1186 (including single or double quotes). */
1188 if (*buffer == '\'' || *buffer == '"')
1194 /* Now skip the here document. */
1200 if (c == '\n' || c == '\r')
1202 int bufidx = label_start;
1204 while (bufidx < bufpos)
1209 if (c != buffer[bufidx])
1216 if (bufidx == bufpos)
1222 if (c == '\n' || c == '\r')
1228 /* FIXME: Ideally we should turn the here document into a
1229 string literal if it didn't contain $ substitution. And
1230 we should also respect backslash escape sequences like
1231 in double-quoted strings. */
1232 tp->type = token_type_other;
1238 /* < / script > terminates PHP mode and switches back to HTML
1240 while (c2 == ' ' || c2 == '\t' || c2 == '\n' || c2 == '\r')
1241 c2 = phase1_getc ();
1245 c2 = phase1_getc ();
1246 while (c2 == ' ' || c2 == '\t' || c2 == '\n' || c2 == '\r');
1247 if (c2 == 's' || c2 == 'S')
1249 c2 = phase1_getc ();
1250 if (c2 == 'c' || c2 == 'C')
1252 c2 = phase1_getc ();
1253 if (c2 == 'r' || c2 == 'R')
1255 c2 = phase1_getc ();
1256 if (c2 == 'i' || c2 == 'I')
1258 c2 = phase1_getc ();
1259 if (c2 == 'p' || c2 == 'P')
1261 c2 = phase1_getc ();
1262 if (c2 == 't' || c2 == 'T')
1265 c2 = phase1_getc ();
1266 while (c2 == ' ' || c2 == '\t'
1267 || c2 == '\n' || c2 == '\r');
1296 tp->type = token_type_other;
1301 /* Execution operator. */
1303 /* We could carefully recognize each of the 2 and 3 character
1304 operators, but it is not necessary, as we only need to recognize
1305 gettext invocations. Don't bother. */
1306 tp->type = token_type_other;
1312 /* Supports 3 tokens of pushback. */
1314 phase4_unget (token_ty *tp)
1316 if (tp->type != token_type_eof)
1318 if (phase4_pushback_length == SIZEOF (phase4_pushback))
1320 phase4_pushback[phase4_pushback_length++] = *tp;
1325 /* 5. Compile-time optimization of string literal concatenation.
1326 Combine "string1" . ... . "stringN" to the concatenated string if
1327 - the token before this expression is none of
1328 '+' '-' '.' '*' '/' '%' '!' '~' '++' '--' ')' '@'
1329 (because then the first string could be part of an expression with
1330 the same or higher precedence as '.', such as an additive,
1331 multiplicative, negation, preincrement, or cast expression),
1332 - the token after this expression is none of
1333 '*' '/' '%' '++' '--'
1334 (because then the last string could be part of an expression with
1335 higher precedence as '.', such as a multiplicative or postincrement
1338 static token_type_ty phase5_last;
1341 x_php_lex (token_ty *tp)
1344 if (tp->type == token_type_string_literal
1345 && !(phase5_last == token_type_dot
1346 || phase5_last == token_type_operator1
1347 || phase5_last == token_type_operator2
1348 || phase5_last == token_type_rparen))
1350 char *sum = tp->string;
1351 size_t sum_len = strlen (sum);
1357 phase4_get (&token2);
1358 if (token2.type == token_type_dot)
1362 phase4_get (&token3);
1363 if (token3.type == token_type_string_literal)
1365 token_ty token_after;
1367 phase4_get (&token_after);
1368 if (token_after.type != token_type_operator1)
1370 char *addend = token3.string;
1371 size_t addend_len = strlen (addend);
1373 sum = (char *) xrealloc (sum, sum_len + addend_len + 1);
1374 memcpy (sum + sum_len, addend, addend_len + 1);
1375 sum_len += addend_len;
1377 phase4_unget (&token_after);
1378 free_token (&token3);
1379 free_token (&token2);
1382 phase4_unget (&token_after);
1384 phase4_unget (&token3);
1386 phase4_unget (&token2);
1391 phase5_last = tp->type;
1395 /* ========================= Extracting strings. ========================== */
1398 /* Context lookup table. */
1399 static flag_context_list_table_ty *flag_context_list_table;
1402 /* The file is broken into tokens. Scan the token stream, looking for
1403 a keyword, followed by a left paren, followed by a string. When we
1404 see this sequence, we have something to remember. We assume we are
1405 looking at a valid C or C++ program, and leave the complaints about
1406 the grammar to the compiler.
1408 Normal handling: Look for
1409 keyword ( ... msgid ... )
1410 Plural handling: Look for
1411 keyword ( ... msgid ... msgid_plural ... )
1413 We use recursion because the arguments before msgid or between msgid
1414 and msgid_plural can contain subexpressions of the same form. */
1417 /* Extract messages until the next balanced closing parenthesis or bracket.
1418 Extracted messages are added to MLP.
1419 DELIM can be either token_type_rparen or token_type_rbracket, or
1420 token_type_eof to accept both.
1421 Return true upon eof, false upon closing parenthesis or bracket. */
1423 extract_balanced (message_list_ty *mlp,
1424 token_type_ty delim,
1425 flag_context_ty outer_context,
1426 flag_context_list_iterator_ty context_iter,
1427 struct arglist_parser *argparser)
1429 /* Current argument number. */
1431 /* 0 when no keyword has been seen. 1 right after a keyword is seen. */
1433 /* Parameters of the keyword just seen. Defined only in state 1. */
1434 const struct callshapes *next_shapes = NULL;
1435 /* Context iterator that will be used if the next token is a '('. */
1436 flag_context_list_iterator_ty next_context_iter =
1437 passthrough_context_list_iterator;
1438 /* Current context. */
1439 flag_context_ty inner_context =
1440 inherited_context (outer_context,
1441 flag_context_list_iterator_advance (&context_iter));
1443 /* Start state is 0. */
1453 case token_type_symbol:
1455 void *keyword_value;
1457 if (hash_find_entry (&keywords, token.string, strlen (token.string),
1461 next_shapes = (const struct callshapes *) keyword_value;
1468 flag_context_list_iterator (
1469 flag_context_list_table_lookup (
1470 flag_context_list_table,
1471 token.string, strlen (token.string)));
1472 free (token.string);
1475 case token_type_lparen:
1476 if (extract_balanced (mlp, token_type_rparen,
1477 inner_context, next_context_iter,
1478 arglist_parser_alloc (mlp,
1479 state ? next_shapes : NULL)))
1481 arglist_parser_done (argparser, arg);
1484 next_context_iter = null_context_list_iterator;
1488 case token_type_rparen:
1489 if (delim == token_type_rparen || delim == token_type_eof)
1491 arglist_parser_done (argparser, arg);
1494 next_context_iter = null_context_list_iterator;
1498 case token_type_comma:
1501 inherited_context (outer_context,
1502 flag_context_list_iterator_advance (
1504 next_context_iter = passthrough_context_list_iterator;
1508 case token_type_lbracket:
1509 if (extract_balanced (mlp, token_type_rbracket,
1510 null_context, null_context_list_iterator,
1511 arglist_parser_alloc (mlp, NULL)))
1513 arglist_parser_done (argparser, arg);
1516 next_context_iter = null_context_list_iterator;
1520 case token_type_rbracket:
1521 if (delim == token_type_rbracket || delim == token_type_eof)
1523 arglist_parser_done (argparser, arg);
1526 next_context_iter = null_context_list_iterator;
1530 case token_type_string_literal:
1533 pos.file_name = logical_file_name;
1534 pos.line_number = token.line_number;
1537 remember_a_message (mlp, NULL, token.string, inner_context,
1538 &pos, NULL, token.comment);
1540 arglist_parser_remember (argparser, arg, token.string,
1542 pos.file_name, pos.line_number,
1544 drop_reference (token.comment);
1546 next_context_iter = null_context_list_iterator;
1550 case token_type_dot:
1551 case token_type_operator1:
1552 case token_type_operator2:
1553 case token_type_other:
1554 next_context_iter = null_context_list_iterator;
1558 case token_type_eof:
1559 arglist_parser_done (argparser, arg);
1570 extract_php (FILE *f,
1571 const char *real_filename, const char *logical_filename,
1572 flag_context_list_table_ty *flag_table,
1573 msgdomain_list_ty *mdlp)
1575 message_list_ty *mlp = mdlp->item[0]->messages;
1578 real_file_name = real_filename;
1579 logical_file_name = xstrdup (logical_filename);
1582 last_comment_line = -1;
1583 last_non_comment_line = -1;
1585 phase5_last = token_type_eof;
1587 flag_context_list_table = flag_table;
1591 /* Initial mode is HTML mode, not PHP mode. */
1594 /* Eat tokens until eof is seen. When extract_balanced returns
1595 due to an unbalanced closing parenthesis, just restart it. */
1596 while (!extract_balanced (mlp, token_type_eof,
1597 null_context, null_context_list_iterator,
1598 arglist_parser_alloc (mlp, NULL)))
1601 /* Close scanner. */
1603 real_file_name = NULL;
1604 logical_file_name = NULL;