xref: /netbsd-src/external/gpl2/gettext/dist/gettext-tools/src/x-php.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
1*946379e7Schristos /* xgettext PHP backend.
2*946379e7Schristos    Copyright (C) 2001-2003, 2005-2006 Free Software Foundation, Inc.
3*946379e7Schristos 
4*946379e7Schristos    This file was written by Bruno Haible <bruno@clisp.org>, 2002.
5*946379e7Schristos 
6*946379e7Schristos    This program is free software; you can redistribute it and/or modify
7*946379e7Schristos    it under the terms of the GNU General Public License as published by
8*946379e7Schristos    the Free Software Foundation; either version 2, or (at your option)
9*946379e7Schristos    any later version.
10*946379e7Schristos 
11*946379e7Schristos    This program is distributed in the hope that it will be useful,
12*946379e7Schristos    but WITHOUT ANY WARRANTY; without even the implied warranty of
13*946379e7Schristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14*946379e7Schristos    GNU General Public License for more details.
15*946379e7Schristos 
16*946379e7Schristos    You should have received a copy of the GNU General Public License
17*946379e7Schristos    along with this program; if not, write to the Free Software Foundation,
18*946379e7Schristos    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
19*946379e7Schristos 
20*946379e7Schristos #ifdef HAVE_CONFIG_H
21*946379e7Schristos # include "config.h"
22*946379e7Schristos #endif
23*946379e7Schristos 
24*946379e7Schristos #include <errno.h>
25*946379e7Schristos #include <stdbool.h>
26*946379e7Schristos #include <stdio.h>
27*946379e7Schristos #include <stdlib.h>
28*946379e7Schristos 
29*946379e7Schristos #include "message.h"
30*946379e7Schristos #include "xgettext.h"
31*946379e7Schristos #include "x-php.h"
32*946379e7Schristos #include "error.h"
33*946379e7Schristos #include "xalloc.h"
34*946379e7Schristos #include "exit.h"
35*946379e7Schristos #include "gettext.h"
36*946379e7Schristos 
37*946379e7Schristos #define _(s) gettext(s)
38*946379e7Schristos 
39*946379e7Schristos #define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
40*946379e7Schristos 
41*946379e7Schristos 
42*946379e7Schristos /* The PHP syntax is defined in phpdoc/manual/langref.html.
43*946379e7Schristos    See also php-4.1.0/Zend/zend_language_scanner.l.
44*946379e7Schristos    Note that variable and function names can contain bytes in the range
45*946379e7Schristos    0x7f..0xff; see
46*946379e7Schristos      http://www.php.net/manual/en/language.variables.php
47*946379e7Schristos      http://www.php.net/manual/en/language.functions.php  */
48*946379e7Schristos 
49*946379e7Schristos 
50*946379e7Schristos /* ====================== Keyword set customization.  ====================== */
51*946379e7Schristos 
52*946379e7Schristos /* If true extract all strings.  */
53*946379e7Schristos static bool extract_all = false;
54*946379e7Schristos 
55*946379e7Schristos static hash_table keywords;
56*946379e7Schristos static bool default_keywords = true;
57*946379e7Schristos 
58*946379e7Schristos 
59*946379e7Schristos void
x_php_extract_all()60*946379e7Schristos x_php_extract_all ()
61*946379e7Schristos {
62*946379e7Schristos   extract_all = true;
63*946379e7Schristos }
64*946379e7Schristos 
65*946379e7Schristos 
66*946379e7Schristos void
x_php_keyword(const char * name)67*946379e7Schristos x_php_keyword (const char *name)
68*946379e7Schristos {
69*946379e7Schristos   if (name == NULL)
70*946379e7Schristos     default_keywords = false;
71*946379e7Schristos   else
72*946379e7Schristos     {
73*946379e7Schristos       const char *end;
74*946379e7Schristos       struct callshape shape;
75*946379e7Schristos       const char *colon;
76*946379e7Schristos 
77*946379e7Schristos       if (keywords.table == NULL)
78*946379e7Schristos 	hash_init (&keywords, 100);
79*946379e7Schristos 
80*946379e7Schristos       split_keywordspec (name, &end, &shape);
81*946379e7Schristos 
82*946379e7Schristos       /* The characters between name and end should form a valid C identifier.
83*946379e7Schristos 	 A colon means an invalid parse in split_keywordspec().  */
84*946379e7Schristos       colon = strchr (name, ':');
85*946379e7Schristos       if (colon == NULL || colon >= end)
86*946379e7Schristos 	insert_keyword_callshape (&keywords, name, end - name, &shape);
87*946379e7Schristos     }
88*946379e7Schristos }
89*946379e7Schristos 
90*946379e7Schristos /* Finish initializing the keywords hash table.
91*946379e7Schristos    Called after argument processing, before each file is processed.  */
92*946379e7Schristos static void
init_keywords()93*946379e7Schristos init_keywords ()
94*946379e7Schristos {
95*946379e7Schristos   if (default_keywords)
96*946379e7Schristos     {
97*946379e7Schristos       /* When adding new keywords here, also update the documentation in
98*946379e7Schristos 	 xgettext.texi!  */
99*946379e7Schristos       x_php_keyword ("_");
100*946379e7Schristos       x_php_keyword ("gettext");
101*946379e7Schristos       x_php_keyword ("dgettext:2");
102*946379e7Schristos       x_php_keyword ("dcgettext:2");
103*946379e7Schristos       /* The following were added in PHP 4.2.0.  */
104*946379e7Schristos       x_php_keyword ("ngettext:1,2");
105*946379e7Schristos       x_php_keyword ("dngettext:2,3");
106*946379e7Schristos       x_php_keyword ("dcngettext:2,3");
107*946379e7Schristos       default_keywords = false;
108*946379e7Schristos     }
109*946379e7Schristos }
110*946379e7Schristos 
111*946379e7Schristos void
init_flag_table_php()112*946379e7Schristos init_flag_table_php ()
113*946379e7Schristos {
114*946379e7Schristos   xgettext_record_flag ("_:1:pass-php-format");
115*946379e7Schristos   xgettext_record_flag ("gettext:1:pass-php-format");
116*946379e7Schristos   xgettext_record_flag ("dgettext:2:pass-php-format");
117*946379e7Schristos   xgettext_record_flag ("dcgettext:2:pass-php-format");
118*946379e7Schristos   xgettext_record_flag ("ngettext:1:pass-php-format");
119*946379e7Schristos   xgettext_record_flag ("ngettext:2:pass-php-format");
120*946379e7Schristos   xgettext_record_flag ("dngettext:2:pass-php-format");
121*946379e7Schristos   xgettext_record_flag ("dngettext:3:pass-php-format");
122*946379e7Schristos   xgettext_record_flag ("dcngettext:2:pass-php-format");
123*946379e7Schristos   xgettext_record_flag ("dcngettext:3:pass-php-format");
124*946379e7Schristos   xgettext_record_flag ("sprintf:1:php-format");
125*946379e7Schristos   xgettext_record_flag ("printf:1:php-format");
126*946379e7Schristos }
127*946379e7Schristos 
128*946379e7Schristos 
129*946379e7Schristos /* ======================== Reading of characters.  ======================== */
130*946379e7Schristos 
131*946379e7Schristos 
132*946379e7Schristos /* Real filename, used in error messages about the input file.  */
133*946379e7Schristos static const char *real_file_name;
134*946379e7Schristos 
135*946379e7Schristos /* Logical filename and line number, used to label the extracted messages.  */
136*946379e7Schristos static char *logical_file_name;
137*946379e7Schristos static int line_number;
138*946379e7Schristos 
139*946379e7Schristos /* The input file stream.  */
140*946379e7Schristos static FILE *fp;
141*946379e7Schristos 
142*946379e7Schristos 
143*946379e7Schristos /* 1. line_number handling.  */
144*946379e7Schristos 
145*946379e7Schristos static unsigned char phase1_pushback[2];
146*946379e7Schristos static int phase1_pushback_length;
147*946379e7Schristos 
148*946379e7Schristos static int
phase1_getc()149*946379e7Schristos phase1_getc ()
150*946379e7Schristos {
151*946379e7Schristos   int c;
152*946379e7Schristos 
153*946379e7Schristos   if (phase1_pushback_length)
154*946379e7Schristos     c = phase1_pushback[--phase1_pushback_length];
155*946379e7Schristos   else
156*946379e7Schristos     {
157*946379e7Schristos       c = getc (fp);
158*946379e7Schristos 
159*946379e7Schristos       if (c == EOF)
160*946379e7Schristos 	{
161*946379e7Schristos 	  if (ferror (fp))
162*946379e7Schristos 	    error (EXIT_FAILURE, errno, _("error while reading \"%s\""),
163*946379e7Schristos 		   real_file_name);
164*946379e7Schristos 	  return EOF;
165*946379e7Schristos 	}
166*946379e7Schristos     }
167*946379e7Schristos 
168*946379e7Schristos   if (c == '\n')
169*946379e7Schristos     line_number++;
170*946379e7Schristos 
171*946379e7Schristos   return c;
172*946379e7Schristos }
173*946379e7Schristos 
174*946379e7Schristos /* Supports 2 characters of pushback.  */
175*946379e7Schristos static void
phase1_ungetc(int c)176*946379e7Schristos phase1_ungetc (int c)
177*946379e7Schristos {
178*946379e7Schristos   if (c != EOF)
179*946379e7Schristos     {
180*946379e7Schristos       if (c == '\n')
181*946379e7Schristos 	--line_number;
182*946379e7Schristos 
183*946379e7Schristos       if (phase1_pushback_length == SIZEOF (phase1_pushback))
184*946379e7Schristos 	abort ();
185*946379e7Schristos       phase1_pushback[phase1_pushback_length++] = c;
186*946379e7Schristos     }
187*946379e7Schristos }
188*946379e7Schristos 
189*946379e7Schristos 
190*946379e7Schristos /* 2. Ignore HTML sections.  They are equivalent to PHP echo commands and
191*946379e7Schristos    therefore don't contain translatable strings.  */
192*946379e7Schristos 
193*946379e7Schristos static void
skip_html()194*946379e7Schristos skip_html ()
195*946379e7Schristos {
196*946379e7Schristos   for (;;)
197*946379e7Schristos     {
198*946379e7Schristos       int c = phase1_getc ();
199*946379e7Schristos 
200*946379e7Schristos       if (c == EOF)
201*946379e7Schristos 	return;
202*946379e7Schristos 
203*946379e7Schristos       if (c == '<')
204*946379e7Schristos 	{
205*946379e7Schristos 	  int c2 = phase1_getc ();
206*946379e7Schristos 
207*946379e7Schristos 	  if (c2 == EOF)
208*946379e7Schristos 	    break;
209*946379e7Schristos 
210*946379e7Schristos 	  if (c2 == '?')
211*946379e7Schristos 	    {
212*946379e7Schristos 	      /* <?php is the normal way to enter PHP mode. <? and <?= are
213*946379e7Schristos 		 recognized by PHP depending on a configuration setting.  */
214*946379e7Schristos 	      int c3 = phase1_getc ();
215*946379e7Schristos 
216*946379e7Schristos 	      if (c3 != '=')
217*946379e7Schristos 		phase1_ungetc (c3);
218*946379e7Schristos 
219*946379e7Schristos 	      return;
220*946379e7Schristos 	    }
221*946379e7Schristos 
222*946379e7Schristos 	  if (c2 == '%')
223*946379e7Schristos 	    {
224*946379e7Schristos 	      /* <% and <%= are recognized by PHP depending on a configuration
225*946379e7Schristos 		 setting.  */
226*946379e7Schristos 	      int c3 = phase1_getc ();
227*946379e7Schristos 
228*946379e7Schristos 	      if (c3 != '=')
229*946379e7Schristos 		phase1_ungetc (c3);
230*946379e7Schristos 
231*946379e7Schristos 	      return;
232*946379e7Schristos 	    }
233*946379e7Schristos 
234*946379e7Schristos 	  if (c2 == '<')
235*946379e7Schristos 	    {
236*946379e7Schristos 	      phase1_ungetc (c2);
237*946379e7Schristos 	      continue;
238*946379e7Schristos 	    }
239*946379e7Schristos 
240*946379e7Schristos 	  /* < script language = php >
241*946379e7Schristos 	     < script language = "php" >
242*946379e7Schristos 	     < script language = 'php' >
243*946379e7Schristos 	     are always recognized.  */
244*946379e7Schristos 	  while (c2 == ' ' || c2 == '\t' || c2 == '\n' || c2 == '\r')
245*946379e7Schristos 	    c2 = phase1_getc ();
246*946379e7Schristos 	  if (c2 != 's' && c2 != 'S')
247*946379e7Schristos 	    {
248*946379e7Schristos 	      phase1_ungetc (c2);
249*946379e7Schristos 	      continue;
250*946379e7Schristos 	    }
251*946379e7Schristos 	  c2 = phase1_getc ();
252*946379e7Schristos 	  if (c2 != 'c' && c2 != 'C')
253*946379e7Schristos 	    {
254*946379e7Schristos 	      phase1_ungetc (c2);
255*946379e7Schristos 	      continue;
256*946379e7Schristos 	    }
257*946379e7Schristos 	  c2 = phase1_getc ();
258*946379e7Schristos 	  if (c2 != 'r' && c2 != 'R')
259*946379e7Schristos 	    {
260*946379e7Schristos 	      phase1_ungetc (c2);
261*946379e7Schristos 	      continue;
262*946379e7Schristos 	    }
263*946379e7Schristos 	  c2 = phase1_getc ();
264*946379e7Schristos 	  if (c2 != 'i' && c2 != 'I')
265*946379e7Schristos 	    {
266*946379e7Schristos 	      phase1_ungetc (c2);
267*946379e7Schristos 	      continue;
268*946379e7Schristos 	    }
269*946379e7Schristos 	  c2 = phase1_getc ();
270*946379e7Schristos 	  if (c2 != 'p' && c2 != 'P')
271*946379e7Schristos 	    {
272*946379e7Schristos 	      phase1_ungetc (c2);
273*946379e7Schristos 	      continue;
274*946379e7Schristos 	    }
275*946379e7Schristos 	  c2 = phase1_getc ();
276*946379e7Schristos 	  if (c2 != 't' && c2 != 'T')
277*946379e7Schristos 	    {
278*946379e7Schristos 	      phase1_ungetc (c2);
279*946379e7Schristos 	      continue;
280*946379e7Schristos 	    }
281*946379e7Schristos 	  c2 = phase1_getc ();
282*946379e7Schristos 	  if (!(c2 == ' ' || c2 == '\t' || c2 == '\n' || c2 == '\r'))
283*946379e7Schristos 	    {
284*946379e7Schristos 	      phase1_ungetc (c2);
285*946379e7Schristos 	      continue;
286*946379e7Schristos 	    }
287*946379e7Schristos 	  do
288*946379e7Schristos 	    c2 = phase1_getc ();
289*946379e7Schristos 	  while (c2 == ' ' || c2 == '\t' || c2 == '\n' || c2 == '\r');
290*946379e7Schristos 	  if (c2 != 'l' && c2 != 'L')
291*946379e7Schristos 	    {
292*946379e7Schristos 	      phase1_ungetc (c2);
293*946379e7Schristos 	      continue;
294*946379e7Schristos 	    }
295*946379e7Schristos 	  c2 = phase1_getc ();
296*946379e7Schristos 	  if (c2 != 'a' && c2 != 'A')
297*946379e7Schristos 	    {
298*946379e7Schristos 	      phase1_ungetc (c2);
299*946379e7Schristos 	      continue;
300*946379e7Schristos 	    }
301*946379e7Schristos 	  c2 = phase1_getc ();
302*946379e7Schristos 	  if (c2 != 'n' && c2 != 'N')
303*946379e7Schristos 	    {
304*946379e7Schristos 	      phase1_ungetc (c2);
305*946379e7Schristos 	      continue;
306*946379e7Schristos 	    }
307*946379e7Schristos 	  c2 = phase1_getc ();
308*946379e7Schristos 	  if (c2 != 'g' && c2 != 'G')
309*946379e7Schristos 	    {
310*946379e7Schristos 	      phase1_ungetc (c2);
311*946379e7Schristos 	      continue;
312*946379e7Schristos 	    }
313*946379e7Schristos 	  c2 = phase1_getc ();
314*946379e7Schristos 	  if (c2 != 'u' && c2 != 'U')
315*946379e7Schristos 	    {
316*946379e7Schristos 	      phase1_ungetc (c2);
317*946379e7Schristos 	      continue;
318*946379e7Schristos 	    }
319*946379e7Schristos 	  c2 = phase1_getc ();
320*946379e7Schristos 	  if (c2 != 'a' && c2 != 'A')
321*946379e7Schristos 	    {
322*946379e7Schristos 	      phase1_ungetc (c2);
323*946379e7Schristos 	      continue;
324*946379e7Schristos 	    }
325*946379e7Schristos 	  c2 = phase1_getc ();
326*946379e7Schristos 	  if (c2 != 'g' && c2 != 'G')
327*946379e7Schristos 	    {
328*946379e7Schristos 	      phase1_ungetc (c2);
329*946379e7Schristos 	      continue;
330*946379e7Schristos 	    }
331*946379e7Schristos 	  c2 = phase1_getc ();
332*946379e7Schristos 	  if (c2 != 'e' && c2 != 'E')
333*946379e7Schristos 	    {
334*946379e7Schristos 	      phase1_ungetc (c2);
335*946379e7Schristos 	      continue;
336*946379e7Schristos 	    }
337*946379e7Schristos 	  c2 = phase1_getc ();
338*946379e7Schristos 	  while (c2 == ' ' || c2 == '\t' || c2 == '\n' || c2 == '\r')
339*946379e7Schristos 	    c2 = phase1_getc ();
340*946379e7Schristos 	  if (c2 != '=')
341*946379e7Schristos 	    {
342*946379e7Schristos 	      phase1_ungetc (c2);
343*946379e7Schristos 	      continue;
344*946379e7Schristos 	    }
345*946379e7Schristos 	  c2 = phase1_getc ();
346*946379e7Schristos 	  while (c2 == ' ' || c2 == '\t' || c2 == '\n' || c2 == '\r')
347*946379e7Schristos 	    c2 = phase1_getc ();
348*946379e7Schristos 	  if (c2 == '"')
349*946379e7Schristos 	    {
350*946379e7Schristos 	      c2 = phase1_getc ();
351*946379e7Schristos 	      if (c2 != 'p')
352*946379e7Schristos 		{
353*946379e7Schristos 		  phase1_ungetc (c2);
354*946379e7Schristos 		  continue;
355*946379e7Schristos 		}
356*946379e7Schristos 	      c2 = phase1_getc ();
357*946379e7Schristos 	      if (c2 != 'h')
358*946379e7Schristos 		{
359*946379e7Schristos 		  phase1_ungetc (c2);
360*946379e7Schristos 		  continue;
361*946379e7Schristos 		}
362*946379e7Schristos 	      c2 = phase1_getc ();
363*946379e7Schristos 	      if (c2 != 'p')
364*946379e7Schristos 		{
365*946379e7Schristos 		  phase1_ungetc (c2);
366*946379e7Schristos 		  continue;
367*946379e7Schristos 		}
368*946379e7Schristos 	      c2 = phase1_getc ();
369*946379e7Schristos 	      if (c2 != '"')
370*946379e7Schristos 		{
371*946379e7Schristos 		  phase1_ungetc (c2);
372*946379e7Schristos 		  continue;
373*946379e7Schristos 		}
374*946379e7Schristos 	    }
375*946379e7Schristos 	  else if (c2 == '\'')
376*946379e7Schristos 	    {
377*946379e7Schristos 	      c2 = phase1_getc ();
378*946379e7Schristos 	      if (c2 != 'p')
379*946379e7Schristos 		{
380*946379e7Schristos 		  phase1_ungetc (c2);
381*946379e7Schristos 		  continue;
382*946379e7Schristos 		}
383*946379e7Schristos 	      c2 = phase1_getc ();
384*946379e7Schristos 	      if (c2 != 'h')
385*946379e7Schristos 		{
386*946379e7Schristos 		  phase1_ungetc (c2);
387*946379e7Schristos 		  continue;
388*946379e7Schristos 		}
389*946379e7Schristos 	      c2 = phase1_getc ();
390*946379e7Schristos 	      if (c2 != 'p')
391*946379e7Schristos 		{
392*946379e7Schristos 		  phase1_ungetc (c2);
393*946379e7Schristos 		  continue;
394*946379e7Schristos 		}
395*946379e7Schristos 	      c2 = phase1_getc ();
396*946379e7Schristos 	      if (c2 != '\'')
397*946379e7Schristos 		{
398*946379e7Schristos 		  phase1_ungetc (c2);
399*946379e7Schristos 		  continue;
400*946379e7Schristos 		}
401*946379e7Schristos 	    }
402*946379e7Schristos 	  else
403*946379e7Schristos 	    {
404*946379e7Schristos 	      if (c2 != 'p')
405*946379e7Schristos 		{
406*946379e7Schristos 		  phase1_ungetc (c2);
407*946379e7Schristos 		  continue;
408*946379e7Schristos 		}
409*946379e7Schristos 	      c2 = phase1_getc ();
410*946379e7Schristos 	      if (c2 != 'h')
411*946379e7Schristos 		{
412*946379e7Schristos 		  phase1_ungetc (c2);
413*946379e7Schristos 		  continue;
414*946379e7Schristos 		}
415*946379e7Schristos 	      c2 = phase1_getc ();
416*946379e7Schristos 	      if (c2 != 'p')
417*946379e7Schristos 		{
418*946379e7Schristos 		  phase1_ungetc (c2);
419*946379e7Schristos 		  continue;
420*946379e7Schristos 		}
421*946379e7Schristos 	    }
422*946379e7Schristos 	  c2 = phase1_getc ();
423*946379e7Schristos 	  while (c2 == ' ' || c2 == '\t' || c2 == '\n' || c2 == '\r')
424*946379e7Schristos 	    c2 = phase1_getc ();
425*946379e7Schristos 	  if (c2 != '>')
426*946379e7Schristos 	    {
427*946379e7Schristos 	      phase1_ungetc (c2);
428*946379e7Schristos 	      continue;
429*946379e7Schristos 	    }
430*946379e7Schristos 	  return;
431*946379e7Schristos 	}
432*946379e7Schristos     }
433*946379e7Schristos }
434*946379e7Schristos 
435*946379e7Schristos #if 0
436*946379e7Schristos 
437*946379e7Schristos static unsigned char phase2_pushback[1];
438*946379e7Schristos static int phase2_pushback_length;
439*946379e7Schristos 
440*946379e7Schristos static int
441*946379e7Schristos phase2_getc ()
442*946379e7Schristos {
443*946379e7Schristos   int c;
444*946379e7Schristos 
445*946379e7Schristos   if (phase2_pushback_length)
446*946379e7Schristos     return phase2_pushback[--phase2_pushback_length];
447*946379e7Schristos 
448*946379e7Schristos   c = phase1_getc ();
449*946379e7Schristos   switch (c)
450*946379e7Schristos     {
451*946379e7Schristos     case '?':
452*946379e7Schristos     case '%':
453*946379e7Schristos       {
454*946379e7Schristos 	int c2 = phase1_getc ();
455*946379e7Schristos 	if (c2 == '>')
456*946379e7Schristos 	  {
457*946379e7Schristos 	    /* ?> and %> terminate PHP mode and switch back to HTML mode.  */
458*946379e7Schristos 	    skip_html ();
459*946379e7Schristos 	    return ' ';
460*946379e7Schristos 	  }
461*946379e7Schristos 	phase1_ungetc (c2);
462*946379e7Schristos       }
463*946379e7Schristos       break;
464*946379e7Schristos 
465*946379e7Schristos     case '<':
466*946379e7Schristos       {
467*946379e7Schristos 	int c2 = phase1_getc ();
468*946379e7Schristos 
469*946379e7Schristos 	/* < / script > terminates PHP mode and switches back to HTML mode.  */
470*946379e7Schristos 	while (c2 == ' ' || c2 == '\t' || c2 == '\n' || c2 == '\r')
471*946379e7Schristos 	  c2 = phase1_getc ();
472*946379e7Schristos 	if (c2 == '/')
473*946379e7Schristos 	  {
474*946379e7Schristos 	    do
475*946379e7Schristos 	      c2 = phase1_getc ();
476*946379e7Schristos 	    while (c2 == ' ' || c2 == '\t' || c2 == '\n' || c2 == '\r');
477*946379e7Schristos 	    if (c2 == 's' || c2 == 'S')
478*946379e7Schristos 	      {
479*946379e7Schristos 		c2 = phase1_getc ();
480*946379e7Schristos 		if (c2 == 'c' || c2 == 'C')
481*946379e7Schristos 		  {
482*946379e7Schristos 		    c2 = phase1_getc ();
483*946379e7Schristos 		    if (c2 == 'r' || c2 == 'R')
484*946379e7Schristos 		      {
485*946379e7Schristos 			c2 = phase1_getc ();
486*946379e7Schristos 			if (c2 == 'i' || c2 == 'I')
487*946379e7Schristos 			  {
488*946379e7Schristos 			    c2 = phase1_getc ();
489*946379e7Schristos 			    if (c2 == 'p' || c2 == 'P')
490*946379e7Schristos 			      {
491*946379e7Schristos 				c2 = phase1_getc ();
492*946379e7Schristos 				if (c2 == 't' || c2 == 'T')
493*946379e7Schristos 				  {
494*946379e7Schristos 				    do
495*946379e7Schristos 				      c2 = phase1_getc ();
496*946379e7Schristos 				    while (c2 == ' ' || c2 == '\t'
497*946379e7Schristos 					   || c2 == '\n' || c2 == '\r');
498*946379e7Schristos 				    if (c2 == '>')
499*946379e7Schristos 				      {
500*946379e7Schristos 					skip_html ();
501*946379e7Schristos 					return ' ';
502*946379e7Schristos 				      }
503*946379e7Schristos 				  }
504*946379e7Schristos 			      }
505*946379e7Schristos 			  }
506*946379e7Schristos 		      }
507*946379e7Schristos 		  }
508*946379e7Schristos 	      }
509*946379e7Schristos 	  }
510*946379e7Schristos 	phase1_ungetc (c2);
511*946379e7Schristos       }
512*946379e7Schristos       break;
513*946379e7Schristos     }
514*946379e7Schristos 
515*946379e7Schristos   return c;
516*946379e7Schristos }
517*946379e7Schristos 
518*946379e7Schristos static void
519*946379e7Schristos phase2_ungetc (int c)
520*946379e7Schristos {
521*946379e7Schristos   if (c != EOF)
522*946379e7Schristos     {
523*946379e7Schristos       if (phase2_pushback_length == SIZEOF (phase2_pushback))
524*946379e7Schristos 	abort ();
525*946379e7Schristos       phase2_pushback[phase2_pushback_length++] = c;
526*946379e7Schristos     }
527*946379e7Schristos }
528*946379e7Schristos 
529*946379e7Schristos #endif
530*946379e7Schristos 
531*946379e7Schristos 
532*946379e7Schristos /* Accumulating comments.  */
533*946379e7Schristos 
534*946379e7Schristos static char *buffer;
535*946379e7Schristos static size_t bufmax;
536*946379e7Schristos static size_t buflen;
537*946379e7Schristos 
538*946379e7Schristos static inline void
comment_start()539*946379e7Schristos comment_start ()
540*946379e7Schristos {
541*946379e7Schristos   buflen = 0;
542*946379e7Schristos }
543*946379e7Schristos 
544*946379e7Schristos static inline void
comment_add(int c)545*946379e7Schristos comment_add (int c)
546*946379e7Schristos {
547*946379e7Schristos   if (buflen >= bufmax)
548*946379e7Schristos     {
549*946379e7Schristos       bufmax = 2 * bufmax + 10;
550*946379e7Schristos       buffer = xrealloc (buffer, bufmax);
551*946379e7Schristos     }
552*946379e7Schristos   buffer[buflen++] = c;
553*946379e7Schristos }
554*946379e7Schristos 
555*946379e7Schristos static inline void
comment_line_end(size_t chars_to_remove)556*946379e7Schristos comment_line_end (size_t chars_to_remove)
557*946379e7Schristos {
558*946379e7Schristos   buflen -= chars_to_remove;
559*946379e7Schristos   while (buflen >= 1
560*946379e7Schristos 	 && (buffer[buflen - 1] == ' ' || buffer[buflen - 1] == '\t'))
561*946379e7Schristos     --buflen;
562*946379e7Schristos   if (chars_to_remove == 0 && buflen >= bufmax)
563*946379e7Schristos     {
564*946379e7Schristos       bufmax = 2 * bufmax + 10;
565*946379e7Schristos       buffer = xrealloc (buffer, bufmax);
566*946379e7Schristos     }
567*946379e7Schristos   buffer[buflen] = '\0';
568*946379e7Schristos   savable_comment_add (buffer);
569*946379e7Schristos }
570*946379e7Schristos 
571*946379e7Schristos 
572*946379e7Schristos /* 3. Replace each comment that is not inside a string literal with a
573*946379e7Schristos    space character.  We need to remember the comment for later, because
574*946379e7Schristos    it may be attached to a keyword string.  */
575*946379e7Schristos 
576*946379e7Schristos /* These are for tracking whether comments count as immediately before
577*946379e7Schristos    keyword.  */
578*946379e7Schristos static int last_comment_line;
579*946379e7Schristos static int last_non_comment_line;
580*946379e7Schristos 
581*946379e7Schristos static unsigned char phase3_pushback[1];
582*946379e7Schristos static int phase3_pushback_length;
583*946379e7Schristos 
584*946379e7Schristos static int
phase3_getc()585*946379e7Schristos phase3_getc ()
586*946379e7Schristos {
587*946379e7Schristos   int lineno;
588*946379e7Schristos   int c;
589*946379e7Schristos 
590*946379e7Schristos   if (phase3_pushback_length)
591*946379e7Schristos     return phase3_pushback[--phase3_pushback_length];
592*946379e7Schristos 
593*946379e7Schristos   c = phase1_getc ();
594*946379e7Schristos 
595*946379e7Schristos   if (c == '#')
596*946379e7Schristos     {
597*946379e7Schristos       /* sh comment.  */
598*946379e7Schristos       bool last_was_qmark = false;
599*946379e7Schristos 
600*946379e7Schristos       comment_start ();
601*946379e7Schristos       lineno = line_number;
602*946379e7Schristos       for (;;)
603*946379e7Schristos 	{
604*946379e7Schristos 	  c = phase1_getc ();
605*946379e7Schristos 	  if (c == '\n' || c == EOF)
606*946379e7Schristos 	    {
607*946379e7Schristos 	      comment_line_end (0);
608*946379e7Schristos 	      break;
609*946379e7Schristos 	    }
610*946379e7Schristos 	  if (last_was_qmark && c == '>')
611*946379e7Schristos 	    {
612*946379e7Schristos 	      comment_line_end (1);
613*946379e7Schristos 	      skip_html ();
614*946379e7Schristos 	      break;
615*946379e7Schristos 	    }
616*946379e7Schristos 	  /* We skip all leading white space, but not EOLs.  */
617*946379e7Schristos 	  if (!(buflen == 0 && (c == ' ' || c == '\t')))
618*946379e7Schristos 	    comment_add (c);
619*946379e7Schristos 	  last_was_qmark = (c == '?' || c == '%');
620*946379e7Schristos 	}
621*946379e7Schristos       last_comment_line = lineno;
622*946379e7Schristos       return '\n';
623*946379e7Schristos     }
624*946379e7Schristos   else if (c == '/')
625*946379e7Schristos     {
626*946379e7Schristos       c = phase1_getc ();
627*946379e7Schristos 
628*946379e7Schristos       switch (c)
629*946379e7Schristos 	{
630*946379e7Schristos 	default:
631*946379e7Schristos 	  phase1_ungetc (c);
632*946379e7Schristos 	  return '/';
633*946379e7Schristos 
634*946379e7Schristos 	case '*':
635*946379e7Schristos 	  {
636*946379e7Schristos 	    /* C comment.  */
637*946379e7Schristos 	    bool last_was_star;
638*946379e7Schristos 
639*946379e7Schristos 	    comment_start ();
640*946379e7Schristos 	    lineno = line_number;
641*946379e7Schristos 	    last_was_star = false;
642*946379e7Schristos 	    for (;;)
643*946379e7Schristos 	      {
644*946379e7Schristos 		c = phase1_getc ();
645*946379e7Schristos 		if (c == EOF)
646*946379e7Schristos 		  break;
647*946379e7Schristos 		/* We skip all leading white space, but not EOLs.  */
648*946379e7Schristos 		if (buflen == 0 && (c == ' ' || c == '\t'))
649*946379e7Schristos 		  continue;
650*946379e7Schristos 		comment_add (c);
651*946379e7Schristos 		switch (c)
652*946379e7Schristos 		  {
653*946379e7Schristos 		  case '\n':
654*946379e7Schristos 		    comment_line_end (1);
655*946379e7Schristos 		    comment_start ();
656*946379e7Schristos 		    lineno = line_number;
657*946379e7Schristos 		    last_was_star = false;
658*946379e7Schristos 		    continue;
659*946379e7Schristos 
660*946379e7Schristos 		  case '*':
661*946379e7Schristos 		    last_was_star = true;
662*946379e7Schristos 		    continue;
663*946379e7Schristos 
664*946379e7Schristos 		  case '/':
665*946379e7Schristos 		    if (last_was_star)
666*946379e7Schristos 		      {
667*946379e7Schristos 			comment_line_end (2);
668*946379e7Schristos 			break;
669*946379e7Schristos 		      }
670*946379e7Schristos 		    /* FALLTHROUGH */
671*946379e7Schristos 
672*946379e7Schristos 		  default:
673*946379e7Schristos 		    last_was_star = false;
674*946379e7Schristos 		    continue;
675*946379e7Schristos 		  }
676*946379e7Schristos 		break;
677*946379e7Schristos 	      }
678*946379e7Schristos 	    last_comment_line = lineno;
679*946379e7Schristos 	    return ' ';
680*946379e7Schristos 	  }
681*946379e7Schristos 
682*946379e7Schristos 	case '/':
683*946379e7Schristos 	  {
684*946379e7Schristos 	    /* C++ comment.  */
685*946379e7Schristos 	    bool last_was_qmark = false;
686*946379e7Schristos 
687*946379e7Schristos 	    comment_start ();
688*946379e7Schristos 	    lineno = line_number;
689*946379e7Schristos 	    for (;;)
690*946379e7Schristos 	      {
691*946379e7Schristos 		c = phase1_getc ();
692*946379e7Schristos 		if (c == '\n' || c == EOF)
693*946379e7Schristos 		  {
694*946379e7Schristos 		    comment_line_end (0);
695*946379e7Schristos 		    break;
696*946379e7Schristos 		  }
697*946379e7Schristos 		if (last_was_qmark && c == '>')
698*946379e7Schristos 		  {
699*946379e7Schristos 		    comment_line_end (1);
700*946379e7Schristos 		    skip_html ();
701*946379e7Schristos 		    break;
702*946379e7Schristos 		  }
703*946379e7Schristos 		/* We skip all leading white space, but not EOLs.  */
704*946379e7Schristos 		if (!(buflen == 0 && (c == ' ' || c == '\t')))
705*946379e7Schristos 		  comment_add (c);
706*946379e7Schristos 		last_was_qmark = (c == '?' || c == '%');
707*946379e7Schristos 	      }
708*946379e7Schristos 	    last_comment_line = lineno;
709*946379e7Schristos 	    return '\n';
710*946379e7Schristos 	  }
711*946379e7Schristos 	}
712*946379e7Schristos     }
713*946379e7Schristos   else
714*946379e7Schristos     return c;
715*946379e7Schristos }
716*946379e7Schristos 
717*946379e7Schristos #ifdef unused
718*946379e7Schristos static void
phase3_ungetc(int c)719*946379e7Schristos phase3_ungetc (int c)
720*946379e7Schristos {
721*946379e7Schristos   if (c != EOF)
722*946379e7Schristos     {
723*946379e7Schristos       if (phase3_pushback_length == SIZEOF (phase3_pushback))
724*946379e7Schristos 	abort ();
725*946379e7Schristos       phase3_pushback[phase3_pushback_length++] = c;
726*946379e7Schristos     }
727*946379e7Schristos }
728*946379e7Schristos #endif
729*946379e7Schristos 
730*946379e7Schristos 
731*946379e7Schristos /* ========================== Reading of tokens.  ========================== */
732*946379e7Schristos 
733*946379e7Schristos 
734*946379e7Schristos enum token_type_ty
735*946379e7Schristos {
736*946379e7Schristos   token_type_eof,
737*946379e7Schristos   token_type_lparen,		/* ( */
738*946379e7Schristos   token_type_rparen,		/* ) */
739*946379e7Schristos   token_type_comma,		/* , */
740*946379e7Schristos   token_type_string_literal,	/* "abc" */
741*946379e7Schristos   token_type_symbol,		/* symbol, number */
742*946379e7Schristos   token_type_other		/* misc. operator */
743*946379e7Schristos };
744*946379e7Schristos typedef enum token_type_ty token_type_ty;
745*946379e7Schristos 
746*946379e7Schristos typedef struct token_ty token_ty;
747*946379e7Schristos struct token_ty
748*946379e7Schristos {
749*946379e7Schristos   token_type_ty type;
750*946379e7Schristos   char *string;		/* for token_type_string_literal, token_type_symbol */
751*946379e7Schristos   int line_number;
752*946379e7Schristos };
753*946379e7Schristos 
754*946379e7Schristos 
755*946379e7Schristos /* Free the memory pointed to by a 'struct token_ty'.  */
756*946379e7Schristos static inline void
free_token(token_ty * tp)757*946379e7Schristos free_token (token_ty *tp)
758*946379e7Schristos {
759*946379e7Schristos   if (tp->type == token_type_string_literal || tp->type == token_type_symbol)
760*946379e7Schristos     free (tp->string);
761*946379e7Schristos }
762*946379e7Schristos 
763*946379e7Schristos 
764*946379e7Schristos /* 4. Combine characters into tokens.  Discard whitespace.  */
765*946379e7Schristos 
766*946379e7Schristos static void
x_php_lex(token_ty * tp)767*946379e7Schristos x_php_lex (token_ty *tp)
768*946379e7Schristos {
769*946379e7Schristos   static char *buffer;
770*946379e7Schristos   static int bufmax;
771*946379e7Schristos   int bufpos;
772*946379e7Schristos   int c;
773*946379e7Schristos 
774*946379e7Schristos   tp->string = NULL;
775*946379e7Schristos 
776*946379e7Schristos   for (;;)
777*946379e7Schristos     {
778*946379e7Schristos       tp->line_number = line_number;
779*946379e7Schristos       c = phase3_getc ();
780*946379e7Schristos       switch (c)
781*946379e7Schristos 	{
782*946379e7Schristos 	case EOF:
783*946379e7Schristos 	  tp->type = token_type_eof;
784*946379e7Schristos 	  return;
785*946379e7Schristos 
786*946379e7Schristos 	case '\n':
787*946379e7Schristos 	  if (last_non_comment_line > last_comment_line)
788*946379e7Schristos 	    savable_comment_reset ();
789*946379e7Schristos 	  /* FALLTHROUGH */
790*946379e7Schristos 	case ' ':
791*946379e7Schristos 	case '\t':
792*946379e7Schristos 	case '\r':
793*946379e7Schristos 	  /* Ignore whitespace.  */
794*946379e7Schristos 	  continue;
795*946379e7Schristos 	}
796*946379e7Schristos 
797*946379e7Schristos       last_non_comment_line = tp->line_number;
798*946379e7Schristos 
799*946379e7Schristos       switch (c)
800*946379e7Schristos 	{
801*946379e7Schristos 	case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
802*946379e7Schristos 	case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
803*946379e7Schristos 	case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
804*946379e7Schristos 	case 'V': case 'W': case 'X': case 'Y': case 'Z':
805*946379e7Schristos 	case '_':
806*946379e7Schristos 	case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
807*946379e7Schristos 	case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
808*946379e7Schristos 	case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
809*946379e7Schristos 	case 'v': case 'w': case 'x': case 'y': case 'z':
810*946379e7Schristos 	case 127: case 128: case 129: case 130: case 131: case 132: case 133:
811*946379e7Schristos 	case 134: case 135: case 136: case 137: case 138: case 139: case 140:
812*946379e7Schristos 	case 141: case 142: case 143: case 144: case 145: case 146: case 147:
813*946379e7Schristos 	case 148: case 149: case 150: case 151: case 152: case 153: case 154:
814*946379e7Schristos 	case 155: case 156: case 157: case 158: case 159: case 160: case 161:
815*946379e7Schristos 	case 162: case 163: case 164: case 165: case 166: case 167: case 168:
816*946379e7Schristos 	case 169: case 170: case 171: case 172: case 173: case 174: case 175:
817*946379e7Schristos 	case 176: case 177: case 178: case 179: case 180: case 181: case 182:
818*946379e7Schristos 	case 183: case 184: case 185: case 186: case 187: case 188: case 189:
819*946379e7Schristos 	case 190: case 191: case 192: case 193: case 194: case 195: case 196:
820*946379e7Schristos 	case 197: case 198: case 199: case 200: case 201: case 202: case 203:
821*946379e7Schristos 	case 204: case 205: case 206: case 207: case 208: case 209: case 210:
822*946379e7Schristos 	case 211: case 212: case 213: case 214: case 215: case 216: case 217:
823*946379e7Schristos 	case 218: case 219: case 220: case 221: case 222: case 223: case 224:
824*946379e7Schristos 	case 225: case 226: case 227: case 228: case 229: case 230: case 231:
825*946379e7Schristos 	case 232: case 233: case 234: case 235: case 236: case 237: case 238:
826*946379e7Schristos 	case 239: case 240: case 241: case 242: case 243: case 244: case 245:
827*946379e7Schristos 	case 246: case 247: case 248: case 249: case 250: case 251: case 252:
828*946379e7Schristos 	case 253: case 254: case 255:
829*946379e7Schristos 	  bufpos = 0;
830*946379e7Schristos 	  for (;;)
831*946379e7Schristos 	    {
832*946379e7Schristos 	      if (bufpos >= bufmax)
833*946379e7Schristos 		{
834*946379e7Schristos 		  bufmax = 2 * bufmax + 10;
835*946379e7Schristos 		  buffer = xrealloc (buffer, bufmax);
836*946379e7Schristos 		}
837*946379e7Schristos 	      buffer[bufpos++] = c;
838*946379e7Schristos 	      c = phase1_getc ();
839*946379e7Schristos 	      switch (c)
840*946379e7Schristos 		{
841*946379e7Schristos 		case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
842*946379e7Schristos 		case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
843*946379e7Schristos 		case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
844*946379e7Schristos 		case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
845*946379e7Schristos 		case 'Y': case 'Z':
846*946379e7Schristos 		case '_':
847*946379e7Schristos 		case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
848*946379e7Schristos 		case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
849*946379e7Schristos 		case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
850*946379e7Schristos 		case 's': case 't': case 'u': case 'v': case 'w': case 'x':
851*946379e7Schristos 		case 'y': case 'z':
852*946379e7Schristos 		case '0': case '1': case '2': case '3': case '4':
853*946379e7Schristos 		case '5': case '6': case '7': case '8': case '9':
854*946379e7Schristos 		case 127: case 128: case 129: case 130: case 131: case 132:
855*946379e7Schristos 		case 133: case 134: case 135: case 136: case 137: case 138:
856*946379e7Schristos 		case 139: case 140: case 141: case 142: case 143: case 144:
857*946379e7Schristos 		case 145: case 146: case 147: case 148: case 149: case 150:
858*946379e7Schristos 		case 151: case 152: case 153: case 154: case 155: case 156:
859*946379e7Schristos 		case 157: case 158: case 159: case 160: case 161: case 162:
860*946379e7Schristos 		case 163: case 164: case 165: case 166: case 167: case 168:
861*946379e7Schristos 		case 169: case 170: case 171: case 172: case 173: case 174:
862*946379e7Schristos 		case 175: case 176: case 177: case 178: case 179: case 180:
863*946379e7Schristos 		case 181: case 182: case 183: case 184: case 185: case 186:
864*946379e7Schristos 		case 187: case 188: case 189: case 190: case 191: case 192:
865*946379e7Schristos 		case 193: case 194: case 195: case 196: case 197: case 198:
866*946379e7Schristos 		case 199: case 200: case 201: case 202: case 203: case 204:
867*946379e7Schristos 		case 205: case 206: case 207: case 208: case 209: case 210:
868*946379e7Schristos 		case 211: case 212: case 213: case 214: case 215: case 216:
869*946379e7Schristos 		case 217: case 218: case 219: case 220: case 221: case 222:
870*946379e7Schristos 		case 223: case 224: case 225: case 226: case 227: case 228:
871*946379e7Schristos 		case 229: case 230: case 231: case 232: case 233: case 234:
872*946379e7Schristos 		case 235: case 236: case 237: case 238: case 239: case 240:
873*946379e7Schristos 		case 241: case 242: case 243: case 244: case 245: case 246:
874*946379e7Schristos 		case 247: case 248: case 249: case 250: case 251: case 252:
875*946379e7Schristos 		case 253: case 254: case 255:
876*946379e7Schristos 		  continue;
877*946379e7Schristos 
878*946379e7Schristos 		default:
879*946379e7Schristos 		  phase1_ungetc (c);
880*946379e7Schristos 		  break;
881*946379e7Schristos 		}
882*946379e7Schristos 	      break;
883*946379e7Schristos 	    }
884*946379e7Schristos 	  if (bufpos >= bufmax)
885*946379e7Schristos 	    {
886*946379e7Schristos 	      bufmax = 2 * bufmax + 10;
887*946379e7Schristos 	      buffer = xrealloc (buffer, bufmax);
888*946379e7Schristos 	    }
889*946379e7Schristos 	  buffer[bufpos] = 0;
890*946379e7Schristos 	  tp->string = xstrdup (buffer);
891*946379e7Schristos 	  tp->type = token_type_symbol;
892*946379e7Schristos 	  return;
893*946379e7Schristos 
894*946379e7Schristos 	case '\'':
895*946379e7Schristos 	  /* Single-quoted string literal.  */
896*946379e7Schristos 	  bufpos = 0;
897*946379e7Schristos 	  for (;;)
898*946379e7Schristos 	    {
899*946379e7Schristos 	      c = phase1_getc ();
900*946379e7Schristos 	      if (c == EOF || c == '\'')
901*946379e7Schristos 		break;
902*946379e7Schristos 	      if (c == '\\')
903*946379e7Schristos 		{
904*946379e7Schristos 		  c = phase1_getc ();
905*946379e7Schristos 		  if (c != '\\' && c != '\'')
906*946379e7Schristos 		    {
907*946379e7Schristos 		      phase1_ungetc (c);
908*946379e7Schristos 		      c = '\\';
909*946379e7Schristos 		    }
910*946379e7Schristos 		}
911*946379e7Schristos 	      if (bufpos >= bufmax)
912*946379e7Schristos 		{
913*946379e7Schristos 		  bufmax = 2 * bufmax + 10;
914*946379e7Schristos 		  buffer = xrealloc (buffer, bufmax);
915*946379e7Schristos 		}
916*946379e7Schristos 	      buffer[bufpos++] = c;
917*946379e7Schristos 	    }
918*946379e7Schristos 	  if (bufpos >= bufmax)
919*946379e7Schristos 	    {
920*946379e7Schristos 	      bufmax = 2 * bufmax + 10;
921*946379e7Schristos 	      buffer = xrealloc (buffer, bufmax);
922*946379e7Schristos 	    }
923*946379e7Schristos 	  buffer[bufpos] = 0;
924*946379e7Schristos 	  tp->type = token_type_string_literal;
925*946379e7Schristos 	  tp->string = xstrdup (buffer);
926*946379e7Schristos 	  return;
927*946379e7Schristos 
928*946379e7Schristos 	case '"':
929*946379e7Schristos 	  /* Double-quoted string literal.  */
930*946379e7Schristos 	  tp->type = token_type_string_literal;
931*946379e7Schristos 	  bufpos = 0;
932*946379e7Schristos 	  for (;;)
933*946379e7Schristos 	    {
934*946379e7Schristos 	      c = phase1_getc ();
935*946379e7Schristos 	      if (c == EOF || c == '"')
936*946379e7Schristos 		break;
937*946379e7Schristos 	      if (c == '$')
938*946379e7Schristos 		{
939*946379e7Schristos 		  c = phase1_getc ();
940*946379e7Schristos 		  if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
941*946379e7Schristos 		      || c == '_' || c == '{' || c >= 0x7f)
942*946379e7Schristos 		    {
943*946379e7Schristos 		      /* String with variables.  */
944*946379e7Schristos 		      tp->type = token_type_other;
945*946379e7Schristos 		      continue;
946*946379e7Schristos 		    }
947*946379e7Schristos 		  phase1_ungetc (c);
948*946379e7Schristos 		  c = '$';
949*946379e7Schristos 		}
950*946379e7Schristos 	      if (c == '{')
951*946379e7Schristos 		{
952*946379e7Schristos 		  c = phase1_getc ();
953*946379e7Schristos 		  if (c == '$')
954*946379e7Schristos 		    {
955*946379e7Schristos 		      /* String with expressions.  */
956*946379e7Schristos 		      tp->type = token_type_other;
957*946379e7Schristos 		      continue;
958*946379e7Schristos 		    }
959*946379e7Schristos 		  phase1_ungetc (c);
960*946379e7Schristos 		  c = '{';
961*946379e7Schristos 		}
962*946379e7Schristos 	      if (c == '\\')
963*946379e7Schristos 		{
964*946379e7Schristos 		  int n, j;
965*946379e7Schristos 
966*946379e7Schristos 		  c = phase1_getc ();
967*946379e7Schristos 		  switch (c)
968*946379e7Schristos 		    {
969*946379e7Schristos 		    case '"':
970*946379e7Schristos 		    case '\\':
971*946379e7Schristos 		    case '$':
972*946379e7Schristos 		      break;
973*946379e7Schristos 
974*946379e7Schristos 		    case '0': case '1': case '2': case '3':
975*946379e7Schristos 		    case '4': case '5': case '6': case '7':
976*946379e7Schristos 		      n = 0;
977*946379e7Schristos 		      for (j = 0; j < 3; ++j)
978*946379e7Schristos 			{
979*946379e7Schristos 			  n = n * 8 + c - '0';
980*946379e7Schristos 			  c = phase1_getc ();
981*946379e7Schristos 			  switch (c)
982*946379e7Schristos 			    {
983*946379e7Schristos 			    default:
984*946379e7Schristos 			      break;
985*946379e7Schristos 
986*946379e7Schristos 			    case '0': case '1': case '2': case '3':
987*946379e7Schristos 			    case '4': case '5': case '6': case '7':
988*946379e7Schristos 			      continue;
989*946379e7Schristos 			    }
990*946379e7Schristos 			  break;
991*946379e7Schristos 			}
992*946379e7Schristos 		      phase1_ungetc (c);
993*946379e7Schristos 		      c = n;
994*946379e7Schristos 		      break;
995*946379e7Schristos 
996*946379e7Schristos 		    case 'x':
997*946379e7Schristos 		      n = 0;
998*946379e7Schristos 		      for (j = 0; j < 2; ++j)
999*946379e7Schristos 			{
1000*946379e7Schristos 			  c = phase1_getc ();
1001*946379e7Schristos 			  switch (c)
1002*946379e7Schristos 			    {
1003*946379e7Schristos 			    case '0': case '1': case '2': case '3': case '4':
1004*946379e7Schristos 			    case '5': case '6': case '7': case '8': case '9':
1005*946379e7Schristos 			      n = n * 16 + c - '0';
1006*946379e7Schristos 			      break;
1007*946379e7Schristos 			    case 'A': case 'B': case 'C': case 'D': case 'E':
1008*946379e7Schristos 			    case 'F':
1009*946379e7Schristos 			      n = n * 16 + 10 + c - 'A';
1010*946379e7Schristos 			      break;
1011*946379e7Schristos 			    case 'a': case 'b': case 'c': case 'd': case 'e':
1012*946379e7Schristos 			    case 'f':
1013*946379e7Schristos 			      n = n * 16 + 10 + c - 'a';
1014*946379e7Schristos 			      break;
1015*946379e7Schristos 			    default:
1016*946379e7Schristos 			      phase1_ungetc (c);
1017*946379e7Schristos 			      c = 0;
1018*946379e7Schristos 			      break;
1019*946379e7Schristos 			    }
1020*946379e7Schristos 			  if (c == 0)
1021*946379e7Schristos 			    break;
1022*946379e7Schristos 			}
1023*946379e7Schristos 		      if (j == 0)
1024*946379e7Schristos 			{
1025*946379e7Schristos 			  phase1_ungetc ('x');
1026*946379e7Schristos 			  c = '\\';
1027*946379e7Schristos 			}
1028*946379e7Schristos 		      else
1029*946379e7Schristos 			c = n;
1030*946379e7Schristos 		      break;
1031*946379e7Schristos 
1032*946379e7Schristos 		    case 'n':
1033*946379e7Schristos 		      c = '\n';
1034*946379e7Schristos 		      break;
1035*946379e7Schristos 		    case 't':
1036*946379e7Schristos 		      c = '\t';
1037*946379e7Schristos 		      break;
1038*946379e7Schristos 		    case 'r':
1039*946379e7Schristos 		      c = '\r';
1040*946379e7Schristos 		      break;
1041*946379e7Schristos 
1042*946379e7Schristos 		    default:
1043*946379e7Schristos 		      phase1_ungetc (c);
1044*946379e7Schristos 		      c = '\\';
1045*946379e7Schristos 		      break;
1046*946379e7Schristos 		    }
1047*946379e7Schristos 		}
1048*946379e7Schristos 	      if (bufpos >= bufmax)
1049*946379e7Schristos 		{
1050*946379e7Schristos 		  bufmax = 2 * bufmax + 10;
1051*946379e7Schristos 		  buffer = xrealloc (buffer, bufmax);
1052*946379e7Schristos 		}
1053*946379e7Schristos 	      buffer[bufpos++] = c;
1054*946379e7Schristos 	    }
1055*946379e7Schristos 	  if (bufpos >= bufmax)
1056*946379e7Schristos 	    {
1057*946379e7Schristos 	      bufmax = 2 * bufmax + 10;
1058*946379e7Schristos 	      buffer = xrealloc (buffer, bufmax);
1059*946379e7Schristos 	    }
1060*946379e7Schristos 	  buffer[bufpos] = 0;
1061*946379e7Schristos 	  if (tp->type == token_type_string_literal)
1062*946379e7Schristos 	    tp->string = xstrdup (buffer);
1063*946379e7Schristos 	  return;
1064*946379e7Schristos 
1065*946379e7Schristos 	case '?':
1066*946379e7Schristos 	case '%':
1067*946379e7Schristos 	  {
1068*946379e7Schristos 	    int c2 = phase1_getc ();
1069*946379e7Schristos 	    if (c2 == '>')
1070*946379e7Schristos 	      {
1071*946379e7Schristos 		/* ?> and %> terminate PHP mode and switch back to HTML
1072*946379e7Schristos 		   mode.  */
1073*946379e7Schristos 		skip_html ();
1074*946379e7Schristos 	      }
1075*946379e7Schristos 	    else
1076*946379e7Schristos 	      phase1_ungetc (c2);
1077*946379e7Schristos 	    tp->type = token_type_other;
1078*946379e7Schristos 	    return;
1079*946379e7Schristos 	  }
1080*946379e7Schristos 
1081*946379e7Schristos 	case '(':
1082*946379e7Schristos 	  tp->type = token_type_lparen;
1083*946379e7Schristos 	  return;
1084*946379e7Schristos 
1085*946379e7Schristos 	case ')':
1086*946379e7Schristos 	  tp->type = token_type_rparen;
1087*946379e7Schristos 	  return;
1088*946379e7Schristos 
1089*946379e7Schristos 	case ',':
1090*946379e7Schristos 	  tp->type = token_type_comma;
1091*946379e7Schristos 	  return;
1092*946379e7Schristos 
1093*946379e7Schristos 	case '<':
1094*946379e7Schristos 	  {
1095*946379e7Schristos 	    int c2 = phase1_getc ();
1096*946379e7Schristos 	    if (c2 == '<')
1097*946379e7Schristos 	      {
1098*946379e7Schristos 		int c3 = phase1_getc ();
1099*946379e7Schristos 		if (c3 == '<')
1100*946379e7Schristos 		  {
1101*946379e7Schristos 		    /* Start of here document.
1102*946379e7Schristos 		       Parse whitespace, then label, then newline.  */
1103*946379e7Schristos 		    do
1104*946379e7Schristos 		      c = phase3_getc ();
1105*946379e7Schristos 		    while (c == ' ' || c == '\t' || c == '\n' || c == '\r');
1106*946379e7Schristos 
1107*946379e7Schristos 		    bufpos = 0;
1108*946379e7Schristos 		    do
1109*946379e7Schristos 		      {
1110*946379e7Schristos 			if (bufpos >= bufmax)
1111*946379e7Schristos 			  {
1112*946379e7Schristos 			    bufmax = 2 * bufmax + 10;
1113*946379e7Schristos 			    buffer = xrealloc (buffer, bufmax);
1114*946379e7Schristos 			  }
1115*946379e7Schristos 			buffer[bufpos++] = c;
1116*946379e7Schristos 			c = phase3_getc ();
1117*946379e7Schristos 		      }
1118*946379e7Schristos 		    while (c != EOF && c != '\n' && c != '\r');
1119*946379e7Schristos 		    /* buffer[0..bufpos-1] now contains the label.  */
1120*946379e7Schristos 
1121*946379e7Schristos 		    /* Now skip the here document.  */
1122*946379e7Schristos 		    for (;;)
1123*946379e7Schristos 		      {
1124*946379e7Schristos 			c = phase1_getc ();
1125*946379e7Schristos 			if (c == EOF)
1126*946379e7Schristos 			  break;
1127*946379e7Schristos 			if (c == '\n' || c == '\r')
1128*946379e7Schristos 			  {
1129*946379e7Schristos 			    int bufidx = 0;
1130*946379e7Schristos 
1131*946379e7Schristos 			    while (bufidx < bufpos)
1132*946379e7Schristos 			      {
1133*946379e7Schristos 				c = phase1_getc ();
1134*946379e7Schristos 				if (c == EOF)
1135*946379e7Schristos 				  break;
1136*946379e7Schristos 				if (c != buffer[bufidx])
1137*946379e7Schristos 				  {
1138*946379e7Schristos 				    phase1_ungetc (c);
1139*946379e7Schristos 				    break;
1140*946379e7Schristos 				  }
1141*946379e7Schristos 				bufidx++;
1142*946379e7Schristos 			      }
1143*946379e7Schristos 			    if (bufidx == bufpos)
1144*946379e7Schristos 			      {
1145*946379e7Schristos 				c = phase1_getc ();
1146*946379e7Schristos 				if (c != ';')
1147*946379e7Schristos 				  phase1_ungetc (c);
1148*946379e7Schristos 				c = phase1_getc ();
1149*946379e7Schristos 				if (c == '\n' || c == '\r')
1150*946379e7Schristos 				  break;
1151*946379e7Schristos 			      }
1152*946379e7Schristos 			  }
1153*946379e7Schristos 		      }
1154*946379e7Schristos 
1155*946379e7Schristos 		    /* FIXME: Ideally we should turn the here document into a
1156*946379e7Schristos 		       string literal if it didn't contain $ substitution.  And
1157*946379e7Schristos 		       we should also respect backslash escape sequences like
1158*946379e7Schristos 		       in double-quoted strings.  */
1159*946379e7Schristos 		    tp->type = token_type_other;
1160*946379e7Schristos 		    return;
1161*946379e7Schristos 		  }
1162*946379e7Schristos 		phase1_ungetc (c3);
1163*946379e7Schristos 	      }
1164*946379e7Schristos 
1165*946379e7Schristos 	    /* < / script > terminates PHP mode and switches back to HTML
1166*946379e7Schristos 	       mode.  */
1167*946379e7Schristos 	    while (c2 == ' ' || c2 == '\t' || c2 == '\n' || c2 == '\r')
1168*946379e7Schristos 	      c2 = phase1_getc ();
1169*946379e7Schristos 	    if (c2 == '/')
1170*946379e7Schristos 	      {
1171*946379e7Schristos 		do
1172*946379e7Schristos 		  c2 = phase1_getc ();
1173*946379e7Schristos 		while (c2 == ' ' || c2 == '\t' || c2 == '\n' || c2 == '\r');
1174*946379e7Schristos 		if (c2 == 's' || c2 == 'S')
1175*946379e7Schristos 		  {
1176*946379e7Schristos 		    c2 = phase1_getc ();
1177*946379e7Schristos 		    if (c2 == 'c' || c2 == 'C')
1178*946379e7Schristos 		      {
1179*946379e7Schristos 			c2 = phase1_getc ();
1180*946379e7Schristos 			if (c2 == 'r' || c2 == 'R')
1181*946379e7Schristos 			  {
1182*946379e7Schristos 			    c2 = phase1_getc ();
1183*946379e7Schristos 			    if (c2 == 'i' || c2 == 'I')
1184*946379e7Schristos 			      {
1185*946379e7Schristos 				c2 = phase1_getc ();
1186*946379e7Schristos 				if (c2 == 'p' || c2 == 'P')
1187*946379e7Schristos 				  {
1188*946379e7Schristos 				    c2 = phase1_getc ();
1189*946379e7Schristos 				    if (c2 == 't' || c2 == 'T')
1190*946379e7Schristos 				      {
1191*946379e7Schristos 					do
1192*946379e7Schristos 					  c2 = phase1_getc ();
1193*946379e7Schristos 					while (c2 == ' ' || c2 == '\t'
1194*946379e7Schristos 					       || c2 == '\n' || c2 == '\r');
1195*946379e7Schristos 					if (c2 == '>')
1196*946379e7Schristos 					  {
1197*946379e7Schristos 					    skip_html ();
1198*946379e7Schristos 					  }
1199*946379e7Schristos 					else
1200*946379e7Schristos 					  phase1_ungetc (c2);
1201*946379e7Schristos 				      }
1202*946379e7Schristos 				    else
1203*946379e7Schristos 				      phase1_ungetc (c2);
1204*946379e7Schristos 				  }
1205*946379e7Schristos 				else
1206*946379e7Schristos 				  phase1_ungetc (c2);
1207*946379e7Schristos 			      }
1208*946379e7Schristos 			    else
1209*946379e7Schristos 			      phase1_ungetc (c2);
1210*946379e7Schristos 			  }
1211*946379e7Schristos 			else
1212*946379e7Schristos 			  phase1_ungetc (c2);
1213*946379e7Schristos 		      }
1214*946379e7Schristos 		    else
1215*946379e7Schristos 		      phase1_ungetc (c2);
1216*946379e7Schristos 		  }
1217*946379e7Schristos 		else
1218*946379e7Schristos 		  phase1_ungetc (c2);
1219*946379e7Schristos 	      }
1220*946379e7Schristos 	    else
1221*946379e7Schristos 	      phase1_ungetc (c2);
1222*946379e7Schristos 
1223*946379e7Schristos 	    tp->type = token_type_other;
1224*946379e7Schristos 	    return;
1225*946379e7Schristos 	  }
1226*946379e7Schristos 
1227*946379e7Schristos 	case '`':
1228*946379e7Schristos 	  /* Execution operator.  */
1229*946379e7Schristos 	default:
1230*946379e7Schristos 	  /* We could carefully recognize each of the 2 and 3 character
1231*946379e7Schristos 	     operators, but it is not necessary, as we only need to recognize
1232*946379e7Schristos 	     gettext invocations.  Don't bother.  */
1233*946379e7Schristos 	  tp->type = token_type_other;
1234*946379e7Schristos 	  return;
1235*946379e7Schristos 	}
1236*946379e7Schristos     }
1237*946379e7Schristos }
1238*946379e7Schristos 
1239*946379e7Schristos 
1240*946379e7Schristos /* ========================= Extracting strings.  ========================== */
1241*946379e7Schristos 
1242*946379e7Schristos 
1243*946379e7Schristos /* Context lookup table.  */
1244*946379e7Schristos static flag_context_list_table_ty *flag_context_list_table;
1245*946379e7Schristos 
1246*946379e7Schristos 
1247*946379e7Schristos /* The file is broken into tokens.  Scan the token stream, looking for
1248*946379e7Schristos    a keyword, followed by a left paren, followed by a string.  When we
1249*946379e7Schristos    see this sequence, we have something to remember.  We assume we are
1250*946379e7Schristos    looking at a valid C or C++ program, and leave the complaints about
1251*946379e7Schristos    the grammar to the compiler.
1252*946379e7Schristos 
1253*946379e7Schristos      Normal handling: Look for
1254*946379e7Schristos        keyword ( ... msgid ... )
1255*946379e7Schristos      Plural handling: Look for
1256*946379e7Schristos        keyword ( ... msgid ... msgid_plural ... )
1257*946379e7Schristos 
1258*946379e7Schristos    We use recursion because the arguments before msgid or between msgid
1259*946379e7Schristos    and msgid_plural can contain subexpressions of the same form.  */
1260*946379e7Schristos 
1261*946379e7Schristos 
1262*946379e7Schristos /* Extract messages until the next balanced closing parenthesis.
1263*946379e7Schristos    Extracted messages are added to MLP.
1264*946379e7Schristos    Return true upon eof, false upon closing parenthesis.  */
1265*946379e7Schristos static bool
extract_parenthesized(message_list_ty * mlp,flag_context_ty outer_context,flag_context_list_iterator_ty context_iter,struct arglist_parser * argparser)1266*946379e7Schristos extract_parenthesized (message_list_ty *mlp,
1267*946379e7Schristos 		       flag_context_ty outer_context,
1268*946379e7Schristos 		       flag_context_list_iterator_ty context_iter,
1269*946379e7Schristos 		       struct arglist_parser *argparser)
1270*946379e7Schristos {
1271*946379e7Schristos   /* Current argument number.  */
1272*946379e7Schristos   int arg = 1;
1273*946379e7Schristos   /* 0 when no keyword has been seen.  1 right after a keyword is seen.  */
1274*946379e7Schristos   int state;
1275*946379e7Schristos   /* Parameters of the keyword just seen.  Defined only in state 1.  */
1276*946379e7Schristos   const struct callshapes *next_shapes = NULL;
1277*946379e7Schristos   /* Context iterator that will be used if the next token is a '('.  */
1278*946379e7Schristos   flag_context_list_iterator_ty next_context_iter =
1279*946379e7Schristos     passthrough_context_list_iterator;
1280*946379e7Schristos   /* Current context.  */
1281*946379e7Schristos   flag_context_ty inner_context =
1282*946379e7Schristos     inherited_context (outer_context,
1283*946379e7Schristos 		       flag_context_list_iterator_advance (&context_iter));
1284*946379e7Schristos 
1285*946379e7Schristos   /* Start state is 0.  */
1286*946379e7Schristos   state = 0;
1287*946379e7Schristos 
1288*946379e7Schristos   for (;;)
1289*946379e7Schristos     {
1290*946379e7Schristos       token_ty token;
1291*946379e7Schristos 
1292*946379e7Schristos       x_php_lex (&token);
1293*946379e7Schristos       switch (token.type)
1294*946379e7Schristos 	{
1295*946379e7Schristos 	case token_type_symbol:
1296*946379e7Schristos 	  {
1297*946379e7Schristos 	    void *keyword_value;
1298*946379e7Schristos 
1299*946379e7Schristos 	    if (hash_find_entry (&keywords, token.string, strlen (token.string),
1300*946379e7Schristos 				 &keyword_value)
1301*946379e7Schristos 		== 0)
1302*946379e7Schristos 	      {
1303*946379e7Schristos 		next_shapes = (const struct callshapes *) keyword_value;
1304*946379e7Schristos 		state = 1;
1305*946379e7Schristos 	      }
1306*946379e7Schristos 	    else
1307*946379e7Schristos 	      state = 0;
1308*946379e7Schristos 	  }
1309*946379e7Schristos 	  next_context_iter =
1310*946379e7Schristos 	    flag_context_list_iterator (
1311*946379e7Schristos 	      flag_context_list_table_lookup (
1312*946379e7Schristos 		flag_context_list_table,
1313*946379e7Schristos 		token.string, strlen (token.string)));
1314*946379e7Schristos 	  free (token.string);
1315*946379e7Schristos 	  continue;
1316*946379e7Schristos 
1317*946379e7Schristos 	case token_type_lparen:
1318*946379e7Schristos 	  if (extract_parenthesized (mlp, inner_context, next_context_iter,
1319*946379e7Schristos 				     arglist_parser_alloc (mlp,
1320*946379e7Schristos 							   state ? next_shapes : NULL)))
1321*946379e7Schristos 	    {
1322*946379e7Schristos 	      arglist_parser_done (argparser, arg);
1323*946379e7Schristos 	      return true;
1324*946379e7Schristos 	    }
1325*946379e7Schristos 	  next_context_iter = null_context_list_iterator;
1326*946379e7Schristos 	  state = 0;
1327*946379e7Schristos 	  continue;
1328*946379e7Schristos 
1329*946379e7Schristos 	case token_type_rparen:
1330*946379e7Schristos 	  arglist_parser_done (argparser, arg);
1331*946379e7Schristos 	  return false;
1332*946379e7Schristos 
1333*946379e7Schristos 	case token_type_comma:
1334*946379e7Schristos 	  arg++;
1335*946379e7Schristos 	  inner_context =
1336*946379e7Schristos 	    inherited_context (outer_context,
1337*946379e7Schristos 			       flag_context_list_iterator_advance (
1338*946379e7Schristos 				 &context_iter));
1339*946379e7Schristos 	  next_context_iter = passthrough_context_list_iterator;
1340*946379e7Schristos 	  state = 0;
1341*946379e7Schristos 	  continue;
1342*946379e7Schristos 
1343*946379e7Schristos 	case token_type_string_literal:
1344*946379e7Schristos 	  {
1345*946379e7Schristos 	    lex_pos_ty pos;
1346*946379e7Schristos 	    pos.file_name = logical_file_name;
1347*946379e7Schristos 	    pos.line_number = token.line_number;
1348*946379e7Schristos 
1349*946379e7Schristos 	    if (extract_all)
1350*946379e7Schristos 	      remember_a_message (mlp, NULL, token.string, inner_context,
1351*946379e7Schristos 				  &pos, savable_comment);
1352*946379e7Schristos 	    else
1353*946379e7Schristos 	      arglist_parser_remember (argparser, arg, token.string,
1354*946379e7Schristos 				       inner_context,
1355*946379e7Schristos 				       pos.file_name, pos.line_number,
1356*946379e7Schristos 				       savable_comment);
1357*946379e7Schristos 	  }
1358*946379e7Schristos 	  next_context_iter = null_context_list_iterator;
1359*946379e7Schristos 	  state = 0;
1360*946379e7Schristos 	  continue;
1361*946379e7Schristos 
1362*946379e7Schristos 	case token_type_other:
1363*946379e7Schristos 	  next_context_iter = null_context_list_iterator;
1364*946379e7Schristos 	  state = 0;
1365*946379e7Schristos 	  continue;
1366*946379e7Schristos 
1367*946379e7Schristos 	case token_type_eof:
1368*946379e7Schristos 	  arglist_parser_done (argparser, arg);
1369*946379e7Schristos 	  return true;
1370*946379e7Schristos 
1371*946379e7Schristos 	default:
1372*946379e7Schristos 	  abort ();
1373*946379e7Schristos 	}
1374*946379e7Schristos     }
1375*946379e7Schristos }
1376*946379e7Schristos 
1377*946379e7Schristos 
1378*946379e7Schristos void
extract_php(FILE * f,const char * real_filename,const char * logical_filename,flag_context_list_table_ty * flag_table,msgdomain_list_ty * mdlp)1379*946379e7Schristos extract_php (FILE *f,
1380*946379e7Schristos 	     const char *real_filename, const char *logical_filename,
1381*946379e7Schristos 	     flag_context_list_table_ty *flag_table,
1382*946379e7Schristos 	     msgdomain_list_ty *mdlp)
1383*946379e7Schristos {
1384*946379e7Schristos   message_list_ty *mlp = mdlp->item[0]->messages;
1385*946379e7Schristos 
1386*946379e7Schristos   fp = f;
1387*946379e7Schristos   real_file_name = real_filename;
1388*946379e7Schristos   logical_file_name = xstrdup (logical_filename);
1389*946379e7Schristos   line_number = 1;
1390*946379e7Schristos 
1391*946379e7Schristos   last_comment_line = -1;
1392*946379e7Schristos   last_non_comment_line = -1;
1393*946379e7Schristos 
1394*946379e7Schristos   flag_context_list_table = flag_table;
1395*946379e7Schristos 
1396*946379e7Schristos   init_keywords ();
1397*946379e7Schristos 
1398*946379e7Schristos   /* Initial mode is HTML mode, not PHP mode.  */
1399*946379e7Schristos   skip_html ();
1400*946379e7Schristos 
1401*946379e7Schristos   /* Eat tokens until eof is seen.  When extract_parenthesized returns
1402*946379e7Schristos      due to an unbalanced closing parenthesis, just restart it.  */
1403*946379e7Schristos   while (!extract_parenthesized (mlp, null_context, null_context_list_iterator,
1404*946379e7Schristos 				 arglist_parser_alloc (mlp, NULL)))
1405*946379e7Schristos     ;
1406*946379e7Schristos 
1407*946379e7Schristos   /* Close scanner.  */
1408*946379e7Schristos   fp = NULL;
1409*946379e7Schristos   real_file_name = NULL;
1410*946379e7Schristos   logical_file_name = NULL;
1411*946379e7Schristos   line_number = 0;
1412*946379e7Schristos }
1413