xref: /netbsd-src/external/gpl2/gettext/dist/gettext-tools/src/x-c.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
1*946379e7Schristos /* xgettext C/C++/ObjectiveC backend.
2*946379e7Schristos    Copyright (C) 1995-1998, 2000-2006 Free Software Foundation, Inc.
3*946379e7Schristos 
4*946379e7Schristos    This file was written by Peter Miller <millerp@canb.auug.org.au>
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 #include <string.h>
29*946379e7Schristos 
30*946379e7Schristos #include "message.h"
31*946379e7Schristos #include "xgettext.h"
32*946379e7Schristos #include "x-c.h"
33*946379e7Schristos #include "error.h"
34*946379e7Schristos #include "error-progname.h"
35*946379e7Schristos #include "xalloc.h"
36*946379e7Schristos #include "xvasprintf.h"
37*946379e7Schristos #include "exit.h"
38*946379e7Schristos #include "hash.h"
39*946379e7Schristos #include "gettext.h"
40*946379e7Schristos 
41*946379e7Schristos #define _(s) gettext(s)
42*946379e7Schristos 
43*946379e7Schristos #define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
44*946379e7Schristos 
45*946379e7Schristos 
46*946379e7Schristos /* The ANSI C standard defines several phases of translation:
47*946379e7Schristos 
48*946379e7Schristos    1. Terminate line by \n, regardless of the external representation
49*946379e7Schristos       of a text line.  Stdio does this for us.
50*946379e7Schristos 
51*946379e7Schristos    2. Convert trigraphs to their single character equivalents.
52*946379e7Schristos 
53*946379e7Schristos    3. Concatenate each line ending in backslash (\) with the following
54*946379e7Schristos       line.
55*946379e7Schristos 
56*946379e7Schristos    4. Replace each comment with a space character.
57*946379e7Schristos 
58*946379e7Schristos    5. Parse each resulting logical line as preprocessing tokens a
59*946379e7Schristos       white space.
60*946379e7Schristos 
61*946379e7Schristos    6. Recognize and carry out directives (it also expands macros on
62*946379e7Schristos       non-directive lines, which we do not do here).
63*946379e7Schristos 
64*946379e7Schristos    7. Replaces escape sequences within character strings with their
65*946379e7Schristos       single character equivalents (we do this in step 5, because we
66*946379e7Schristos       don't have to worry about the #include argument).
67*946379e7Schristos 
68*946379e7Schristos    8. Concatenates adjacent string literals to form single string
69*946379e7Schristos       literals (because we don't expand macros, there are a few things
70*946379e7Schristos       we will miss).
71*946379e7Schristos 
72*946379e7Schristos    9. Converts the remaining preprocessing tokens to C tokens and
73*946379e7Schristos       discards any white space from the translation unit.
74*946379e7Schristos 
75*946379e7Schristos    This lexer implements the above, and presents the scanner (in
76*946379e7Schristos    xgettext.c) with a stream of C tokens.  The comments are
77*946379e7Schristos    accumulated in a buffer, and given to xgettext when asked for.  */
78*946379e7Schristos 
79*946379e7Schristos 
80*946379e7Schristos /* ========================= Lexer customization.  ========================= */
81*946379e7Schristos 
82*946379e7Schristos static bool trigraphs = false;
83*946379e7Schristos 
84*946379e7Schristos void
x_c_trigraphs()85*946379e7Schristos x_c_trigraphs ()
86*946379e7Schristos {
87*946379e7Schristos   trigraphs = true;
88*946379e7Schristos }
89*946379e7Schristos 
90*946379e7Schristos 
91*946379e7Schristos /* ====================== Keyword set customization.  ====================== */
92*946379e7Schristos 
93*946379e7Schristos /* If true extract all strings.  */
94*946379e7Schristos static bool extract_all = false;
95*946379e7Schristos 
96*946379e7Schristos static hash_table c_keywords;
97*946379e7Schristos static hash_table objc_keywords;
98*946379e7Schristos static bool default_keywords = true;
99*946379e7Schristos 
100*946379e7Schristos 
101*946379e7Schristos void
x_c_extract_all()102*946379e7Schristos x_c_extract_all ()
103*946379e7Schristos {
104*946379e7Schristos   extract_all = true;
105*946379e7Schristos }
106*946379e7Schristos 
107*946379e7Schristos 
108*946379e7Schristos static void
add_keyword(const char * name,hash_table * keywords)109*946379e7Schristos add_keyword (const char *name, hash_table *keywords)
110*946379e7Schristos {
111*946379e7Schristos   if (name == NULL)
112*946379e7Schristos     default_keywords = false;
113*946379e7Schristos   else
114*946379e7Schristos     {
115*946379e7Schristos       const char *end;
116*946379e7Schristos       struct callshape shape;
117*946379e7Schristos       const char *colon;
118*946379e7Schristos 
119*946379e7Schristos       if (keywords->table == NULL)
120*946379e7Schristos 	hash_init (keywords, 100);
121*946379e7Schristos 
122*946379e7Schristos       split_keywordspec (name, &end, &shape);
123*946379e7Schristos 
124*946379e7Schristos       /* The characters between name and end should form a valid C identifier.
125*946379e7Schristos 	 A colon means an invalid parse in split_keywordspec().  */
126*946379e7Schristos       colon = strchr (name, ':');
127*946379e7Schristos       if (colon == NULL || colon >= end)
128*946379e7Schristos 	insert_keyword_callshape (keywords, name, end - name, &shape);
129*946379e7Schristos     }
130*946379e7Schristos }
131*946379e7Schristos 
132*946379e7Schristos void
x_c_keyword(const char * name)133*946379e7Schristos x_c_keyword (const char *name)
134*946379e7Schristos {
135*946379e7Schristos   add_keyword (name, &c_keywords);
136*946379e7Schristos }
137*946379e7Schristos 
138*946379e7Schristos void
x_objc_keyword(const char * name)139*946379e7Schristos x_objc_keyword (const char *name)
140*946379e7Schristos {
141*946379e7Schristos   add_keyword (name, &objc_keywords);
142*946379e7Schristos }
143*946379e7Schristos 
144*946379e7Schristos /* Finish initializing the keywords hash tables.
145*946379e7Schristos    Called after argument processing, before each file is processed.  */
146*946379e7Schristos static void
init_keywords()147*946379e7Schristos init_keywords ()
148*946379e7Schristos {
149*946379e7Schristos   if (default_keywords)
150*946379e7Schristos     {
151*946379e7Schristos       /* When adding new keywords here, also update the documentation in
152*946379e7Schristos 	 xgettext.texi!  */
153*946379e7Schristos       x_c_keyword ("gettext");
154*946379e7Schristos       x_c_keyword ("dgettext:2");
155*946379e7Schristos       x_c_keyword ("dcgettext:2");
156*946379e7Schristos       x_c_keyword ("ngettext:1,2");
157*946379e7Schristos       x_c_keyword ("dngettext:2,3");
158*946379e7Schristos       x_c_keyword ("dcngettext:2,3");
159*946379e7Schristos       x_c_keyword ("gettext_noop");
160*946379e7Schristos       x_c_keyword ("pgettext:1c,2");
161*946379e7Schristos       x_c_keyword ("dpgettext:2c,3");
162*946379e7Schristos       x_c_keyword ("dcpgettext:2c,3");
163*946379e7Schristos       x_c_keyword ("npgettext:1c,2,3");
164*946379e7Schristos       x_c_keyword ("dnpgettext:2c,3,4");
165*946379e7Schristos       x_c_keyword ("dcnpgettext:2c,3,4");
166*946379e7Schristos 
167*946379e7Schristos       x_objc_keyword ("gettext");
168*946379e7Schristos       x_objc_keyword ("dgettext:2");
169*946379e7Schristos       x_objc_keyword ("dcgettext:2");
170*946379e7Schristos       x_objc_keyword ("ngettext:1,2");
171*946379e7Schristos       x_objc_keyword ("dngettext:2,3");
172*946379e7Schristos       x_objc_keyword ("dcngettext:2,3");
173*946379e7Schristos       x_objc_keyword ("gettext_noop");
174*946379e7Schristos       x_objc_keyword ("pgettext:1c,2");
175*946379e7Schristos       x_objc_keyword ("dpgettext:2c,3");
176*946379e7Schristos       x_objc_keyword ("dcpgettext:2c,3");
177*946379e7Schristos       x_objc_keyword ("npgettext:1c,2,3");
178*946379e7Schristos       x_objc_keyword ("dnpgettext:2c,3,4");
179*946379e7Schristos       x_objc_keyword ("dcnpgettext:2c,3,4");
180*946379e7Schristos       x_objc_keyword ("NSLocalizedString");	  /* similar to gettext */
181*946379e7Schristos       x_objc_keyword ("_");			  /* similar to gettext */
182*946379e7Schristos       x_objc_keyword ("NSLocalizedStaticString"); /* similar to gettext_noop */
183*946379e7Schristos       x_objc_keyword ("__");			  /* similar to gettext_noop */
184*946379e7Schristos 
185*946379e7Schristos       default_keywords = false;
186*946379e7Schristos     }
187*946379e7Schristos }
188*946379e7Schristos 
189*946379e7Schristos void
init_flag_table_c()190*946379e7Schristos init_flag_table_c ()
191*946379e7Schristos {
192*946379e7Schristos   xgettext_record_flag ("gettext:1:pass-c-format");
193*946379e7Schristos   xgettext_record_flag ("dgettext:2:pass-c-format");
194*946379e7Schristos   xgettext_record_flag ("dcgettext:2:pass-c-format");
195*946379e7Schristos   xgettext_record_flag ("ngettext:1:pass-c-format");
196*946379e7Schristos   xgettext_record_flag ("ngettext:2:pass-c-format");
197*946379e7Schristos   xgettext_record_flag ("dngettext:2:pass-c-format");
198*946379e7Schristos   xgettext_record_flag ("dngettext:3:pass-c-format");
199*946379e7Schristos   xgettext_record_flag ("dcngettext:2:pass-c-format");
200*946379e7Schristos   xgettext_record_flag ("dcngettext:3:pass-c-format");
201*946379e7Schristos   xgettext_record_flag ("gettext_noop:1:pass-c-format");
202*946379e7Schristos   xgettext_record_flag ("pgettext:2:pass-c-format");
203*946379e7Schristos   xgettext_record_flag ("dpgettext:3:pass-c-format");
204*946379e7Schristos   xgettext_record_flag ("dcpgettext:3:pass-c-format");
205*946379e7Schristos   xgettext_record_flag ("npgettext:2:pass-c-format");
206*946379e7Schristos   xgettext_record_flag ("npgettext:3:pass-c-format");
207*946379e7Schristos   xgettext_record_flag ("dnpgettext:3:pass-c-format");
208*946379e7Schristos   xgettext_record_flag ("dnpgettext:4:pass-c-format");
209*946379e7Schristos   xgettext_record_flag ("dcnpgettext:3:pass-c-format");
210*946379e7Schristos   xgettext_record_flag ("dcnpgettext:4:pass-c-format");
211*946379e7Schristos 
212*946379e7Schristos   /* <stdio.h> */
213*946379e7Schristos   xgettext_record_flag ("fprintf:2:c-format");
214*946379e7Schristos   xgettext_record_flag ("vfprintf:2:c-format");
215*946379e7Schristos   xgettext_record_flag ("printf:1:c-format");
216*946379e7Schristos   xgettext_record_flag ("vprintf:1:c-format");
217*946379e7Schristos   xgettext_record_flag ("sprintf:2:c-format");
218*946379e7Schristos   xgettext_record_flag ("vsprintf:2:c-format");
219*946379e7Schristos   xgettext_record_flag ("snprintf:3:c-format");
220*946379e7Schristos   xgettext_record_flag ("vsnprintf:3:c-format");
221*946379e7Schristos #if 0 /* These functions are not standard.  */
222*946379e7Schristos   /* <stdio.h> */
223*946379e7Schristos   xgettext_record_flag ("asprintf:2:c-format");
224*946379e7Schristos   xgettext_record_flag ("vasprintf:2:c-format");
225*946379e7Schristos   xgettext_record_flag ("dprintf:2:c-format");
226*946379e7Schristos   xgettext_record_flag ("vdprintf:2:c-format");
227*946379e7Schristos   xgettext_record_flag ("obstack_printf:2:c-format");
228*946379e7Schristos   xgettext_record_flag ("obstack_vprintf:2:c-format");
229*946379e7Schristos   /* <error.h> */
230*946379e7Schristos   xgettext_record_flag ("error:3:c-format");
231*946379e7Schristos   xgettext_record_flag ("error_at_line:5:c-format");
232*946379e7Schristos   /* <argp.h> */
233*946379e7Schristos   xgettext_record_flag ("argp_error:2:c-format");
234*946379e7Schristos   xgettext_record_flag ("argp_failure:2:c-format");
235*946379e7Schristos #endif
236*946379e7Schristos 
237*946379e7Schristos   xgettext_record_flag ("gettext:1:pass-boost-format");
238*946379e7Schristos   xgettext_record_flag ("dgettext:2:pass-boost-format");
239*946379e7Schristos   xgettext_record_flag ("dcgettext:2:pass-boost-format");
240*946379e7Schristos   xgettext_record_flag ("ngettext:1:pass-boost-format");
241*946379e7Schristos   xgettext_record_flag ("ngettext:2:pass-boost-format");
242*946379e7Schristos   xgettext_record_flag ("dngettext:2:pass-boost-format");
243*946379e7Schristos   xgettext_record_flag ("dngettext:3:pass-boost-format");
244*946379e7Schristos   xgettext_record_flag ("dcngettext:2:pass-boost-format");
245*946379e7Schristos   xgettext_record_flag ("dcngettext:3:pass-boost-format");
246*946379e7Schristos   xgettext_record_flag ("gettext_noop:1:pass-boost-format");
247*946379e7Schristos   xgettext_record_flag ("pgettext:2:pass-boost-format");
248*946379e7Schristos   xgettext_record_flag ("dpgettext:3:pass-boost-format");
249*946379e7Schristos   xgettext_record_flag ("dcpgettext:3:pass-boost-format");
250*946379e7Schristos   xgettext_record_flag ("npgettext:2:pass-boost-format");
251*946379e7Schristos   xgettext_record_flag ("npgettext:3:pass-boost-format");
252*946379e7Schristos   xgettext_record_flag ("dnpgettext:3:pass-boost-format");
253*946379e7Schristos   xgettext_record_flag ("dnpgettext:4:pass-boost-format");
254*946379e7Schristos   xgettext_record_flag ("dcnpgettext:3:pass-boost-format");
255*946379e7Schristos   xgettext_record_flag ("dcnpgettext:4:pass-boost-format");
256*946379e7Schristos 
257*946379e7Schristos   /* <boost/format.hpp> */
258*946379e7Schristos   xgettext_record_flag ("format:1:boost-format");
259*946379e7Schristos }
260*946379e7Schristos 
261*946379e7Schristos void
init_flag_table_objc()262*946379e7Schristos init_flag_table_objc ()
263*946379e7Schristos {
264*946379e7Schristos   /* Since the settings done in init_flag_table_c() also have an effect for
265*946379e7Schristos      the ObjectiveC parser, we don't have to repeat them here.  */
266*946379e7Schristos   xgettext_record_flag ("gettext:1:pass-objc-format");
267*946379e7Schristos   xgettext_record_flag ("dgettext:2:pass-objc-format");
268*946379e7Schristos   xgettext_record_flag ("dcgettext:2:pass-objc-format");
269*946379e7Schristos   xgettext_record_flag ("ngettext:1:pass-objc-format");
270*946379e7Schristos   xgettext_record_flag ("ngettext:2:pass-objc-format");
271*946379e7Schristos   xgettext_record_flag ("dngettext:2:pass-objc-format");
272*946379e7Schristos   xgettext_record_flag ("dngettext:3:pass-objc-format");
273*946379e7Schristos   xgettext_record_flag ("dcngettext:2:pass-objc-format");
274*946379e7Schristos   xgettext_record_flag ("dcngettext:3:pass-objc-format");
275*946379e7Schristos   xgettext_record_flag ("gettext_noop:1:pass-objc-format");
276*946379e7Schristos   xgettext_record_flag ("pgettext:2:pass-objc-format");
277*946379e7Schristos   xgettext_record_flag ("dpgettext:3:pass-objc-format");
278*946379e7Schristos   xgettext_record_flag ("dcpgettext:3:pass-objc-format");
279*946379e7Schristos   xgettext_record_flag ("npgettext:2:pass-objc-format");
280*946379e7Schristos   xgettext_record_flag ("npgettext:3:pass-objc-format");
281*946379e7Schristos   xgettext_record_flag ("dnpgettext:3:pass-objc-format");
282*946379e7Schristos   xgettext_record_flag ("dnpgettext:4:pass-objc-format");
283*946379e7Schristos   xgettext_record_flag ("dcnpgettext:3:pass-objc-format");
284*946379e7Schristos   xgettext_record_flag ("dcnpgettext:4:pass-objc-format");
285*946379e7Schristos   xgettext_record_flag ("NSLocalizedString:1:pass-c-format");
286*946379e7Schristos   xgettext_record_flag ("NSLocalizedString:1:pass-objc-format");
287*946379e7Schristos   xgettext_record_flag ("_:1:pass-c-format");
288*946379e7Schristos   xgettext_record_flag ("_:1:pass-objc-format");
289*946379e7Schristos   xgettext_record_flag ("stringWithFormat::1:objc-format");
290*946379e7Schristos   xgettext_record_flag ("initWithFormat::1:objc-format");
291*946379e7Schristos   xgettext_record_flag ("stringByAppendingFormat::1:objc-format");
292*946379e7Schristos   xgettext_record_flag ("localizedStringWithFormat::1:objc-format");
293*946379e7Schristos   xgettext_record_flag ("appendFormat::1:objc-format");
294*946379e7Schristos }
295*946379e7Schristos 
296*946379e7Schristos void
init_flag_table_gcc_internal()297*946379e7Schristos init_flag_table_gcc_internal ()
298*946379e7Schristos {
299*946379e7Schristos   xgettext_record_flag ("gettext:1:pass-gcc-internal-format");
300*946379e7Schristos   xgettext_record_flag ("dgettext:2:pass-gcc-internal-format");
301*946379e7Schristos   xgettext_record_flag ("dcgettext:2:pass-gcc-internal-format");
302*946379e7Schristos   xgettext_record_flag ("ngettext:1:pass-gcc-internal-format");
303*946379e7Schristos   xgettext_record_flag ("ngettext:2:pass-gcc-internal-format");
304*946379e7Schristos   xgettext_record_flag ("dngettext:2:pass-gcc-internal-format");
305*946379e7Schristos   xgettext_record_flag ("dngettext:3:pass-gcc-internal-format");
306*946379e7Schristos   xgettext_record_flag ("dcngettext:2:pass-gcc-internal-format");
307*946379e7Schristos   xgettext_record_flag ("dcngettext:3:pass-gcc-internal-format");
308*946379e7Schristos   xgettext_record_flag ("gettext_noop:1:pass-gcc-internal-format");
309*946379e7Schristos   xgettext_record_flag ("pgettext:2:pass-gcc-internal-format");
310*946379e7Schristos   xgettext_record_flag ("dpgettext:3:pass-gcc-internal-format");
311*946379e7Schristos   xgettext_record_flag ("dcpgettext:3:pass-gcc-internal-format");
312*946379e7Schristos   xgettext_record_flag ("npgettext:2:pass-gcc-internal-format");
313*946379e7Schristos   xgettext_record_flag ("npgettext:3:pass-gcc-internal-format");
314*946379e7Schristos   xgettext_record_flag ("dnpgettext:3:pass-gcc-internal-format");
315*946379e7Schristos   xgettext_record_flag ("dnpgettext:4:pass-gcc-internal-format");
316*946379e7Schristos   xgettext_record_flag ("dcnpgettext:3:pass-gcc-internal-format");
317*946379e7Schristos   xgettext_record_flag ("dcnpgettext:4:pass-gcc-internal-format");
318*946379e7Schristos #if 0 /* This should better be done inside GCC.  */
319*946379e7Schristos   /* grepping for ATTRIBUTE_PRINTF in gcc-3.3/gcc/?*.h */
320*946379e7Schristos   /* c-format.c */
321*946379e7Schristos   xgettext_record_flag ("status_warning:2:gcc-internal-format");
322*946379e7Schristos   /* c-tree.h */
323*946379e7Schristos   xgettext_record_flag ("pedwarn_c99:1:pass-gcc-internal-format");
324*946379e7Schristos   /* collect2.h */
325*946379e7Schristos   //xgettext_record_flag ("error:1:c-format"); // 3 different versions
326*946379e7Schristos   xgettext_record_flag ("notice:1:c-format");
327*946379e7Schristos   //xgettext_record_flag ("fatal:1:c-format"); // 2 different versions
328*946379e7Schristos   xgettext_record_flag ("fatal_perror:1:c-format");
329*946379e7Schristos   /* cpplib.h */
330*946379e7Schristos   xgettext_record_flag ("cpp_error:3:c-format");
331*946379e7Schristos   xgettext_record_flag ("cpp_error_with_line:5:c-format");
332*946379e7Schristos   /* diagnostic.h */
333*946379e7Schristos   xgettext_record_flag ("diagnostic_set_info:2:pass-gcc-internal-format");
334*946379e7Schristos   xgettext_record_flag ("output_printf:2:gcc-internal-format");
335*946379e7Schristos   xgettext_record_flag ("output_verbatim:2:pass-gcc-internal-format");
336*946379e7Schristos   xgettext_record_flag ("verbatim:1:gcc-internal-format");
337*946379e7Schristos   xgettext_record_flag ("inform:1:pass-gcc-internal-format");
338*946379e7Schristos   /* gcc.h */
339*946379e7Schristos   //xgettext_record_flag ("fatal:1:c-format"); // 2 different versions
340*946379e7Schristos   //xgettext_record_flag ("error:1:c-format"); // 3 different versions
341*946379e7Schristos   /* genattrtab.h */
342*946379e7Schristos   xgettext_record_flag ("attr_printf:2:pass-c-format");
343*946379e7Schristos   /* gengtype.h */
344*946379e7Schristos   xgettext_record_flag ("error_at_line:2:pass-c-format");
345*946379e7Schristos   xgettext_record_flag ("xvasprintf:2:pass-c-format");
346*946379e7Schristos   xgettext_record_flag ("xasprintf:1:pass-c-format");
347*946379e7Schristos   xgettext_record_flag ("oprintf:2:pass-c-format");
348*946379e7Schristos   /* gensupport.h */
349*946379e7Schristos   xgettext_record_flag ("message_with_line:2:pass-c-format");
350*946379e7Schristos   /* output.h */
351*946379e7Schristos   xgettext_record_flag ("output_operand_lossage:1:c-format");
352*946379e7Schristos   /* ra.h */
353*946379e7Schristos    xgettext_record_flag ("ra_debug_msg:2:pass-c-format");
354*946379e7Schristos   /* toplev.h */
355*946379e7Schristos   xgettext_record_flag ("fnotice:2:c-format");
356*946379e7Schristos   xgettext_record_flag ("fatal_io_error:2:gcc-internal-format");
357*946379e7Schristos   xgettext_record_flag ("error_for_asm:2:pass-gcc-internal-format");
358*946379e7Schristos   xgettext_record_flag ("warning_for_asm:2:pass-gcc-internal-format");
359*946379e7Schristos   xgettext_record_flag ("error_with_file_and_line:3:pass-gcc-internal-format");
360*946379e7Schristos   xgettext_record_flag ("error_with_decl:2:pass-gcc-internal-format");
361*946379e7Schristos   xgettext_record_flag ("pedwarn:1:gcc-internal-format");
362*946379e7Schristos   xgettext_record_flag ("pedwarn_with_file_and_line:3:gcc-internal-format");
363*946379e7Schristos   xgettext_record_flag ("pedwarn_with_decl:2:gcc-internal-format");
364*946379e7Schristos   xgettext_record_flag ("sorry:1:gcc-internal-format");
365*946379e7Schristos   xgettext_record_flag ("error:1:pass-gcc-internal-format");
366*946379e7Schristos   xgettext_record_flag ("fatal_error:1:pass-gcc-internal-format");
367*946379e7Schristos   xgettext_record_flag ("internal_error:1:pass-gcc-internal-format");
368*946379e7Schristos   xgettext_record_flag ("warning:1:pass-gcc-internal-format");
369*946379e7Schristos   xgettext_record_flag ("warning_with_file_and_line:3:pass-gcc-internal-format");
370*946379e7Schristos   xgettext_record_flag ("warning_with_decl:2:pass-gcc-internal-format");
371*946379e7Schristos   /* f/com.h */
372*946379e7Schristos   xgettext_record_flag ("ffecom_get_invented_identifier:1:pass-c-format");
373*946379e7Schristos   /* f/sts.h */
374*946379e7Schristos   xgettext_record_flag ("ffests_printf:2:pass-c-format");
375*946379e7Schristos   /* java/java-tree.h */
376*946379e7Schristos   xgettext_record_flag ("parse_error_context:2:pass-c-format");
377*946379e7Schristos #endif
378*946379e7Schristos }
379*946379e7Schristos 
380*946379e7Schristos 
381*946379e7Schristos /* ======================== Reading of characters.  ======================== */
382*946379e7Schristos 
383*946379e7Schristos /* Real filename, used in error messages about the input file.  */
384*946379e7Schristos static const char *real_file_name;
385*946379e7Schristos 
386*946379e7Schristos /* Logical filename and line number, used to label the extracted messages.  */
387*946379e7Schristos static char *logical_file_name;
388*946379e7Schristos static int line_number;
389*946379e7Schristos 
390*946379e7Schristos /* The input file stream.  */
391*946379e7Schristos static FILE *fp;
392*946379e7Schristos 
393*946379e7Schristos 
394*946379e7Schristos /* 0. Terminate line by \n, regardless whether the external representation of
395*946379e7Schristos    a line terminator is LF (Unix), CR (Mac) or CR/LF (DOS/Windows).
396*946379e7Schristos    It is debatable whether supporting CR/LF line terminators in C sources
397*946379e7Schristos    on Unix is ISO C or POSIX compliant, but since GCC 3.3 now supports it
398*946379e7Schristos    unconditionally, it must be OK.
399*946379e7Schristos    The so-called "text mode" in stdio on DOS/Windows translates CR/LF to \n
400*946379e7Schristos    automatically, but here we also need this conversion on Unix.  As a side
401*946379e7Schristos    effect, on DOS/Windows we also parse CR/CR/LF into a single \n, but this
402*946379e7Schristos    is not a problem.  */
403*946379e7Schristos 
404*946379e7Schristos 
405*946379e7Schristos static int
phase0_getc()406*946379e7Schristos phase0_getc ()
407*946379e7Schristos {
408*946379e7Schristos   int c;
409*946379e7Schristos 
410*946379e7Schristos   c = getc (fp);
411*946379e7Schristos   if (c == EOF)
412*946379e7Schristos     {
413*946379e7Schristos       if (ferror (fp))
414*946379e7Schristos 	error (EXIT_FAILURE, errno, _("error while reading \"%s\""),
415*946379e7Schristos 	       real_file_name);
416*946379e7Schristos       return EOF;
417*946379e7Schristos     }
418*946379e7Schristos 
419*946379e7Schristos   if (c == '\r')
420*946379e7Schristos     {
421*946379e7Schristos       int c1 = getc (fp);
422*946379e7Schristos 
423*946379e7Schristos       if (c1 != EOF && c1 != '\n')
424*946379e7Schristos 	ungetc (c1, fp);
425*946379e7Schristos 
426*946379e7Schristos       /* Seen line terminator CR or CR/LF.  */
427*946379e7Schristos       return '\n';
428*946379e7Schristos     }
429*946379e7Schristos 
430*946379e7Schristos   return c;
431*946379e7Schristos }
432*946379e7Schristos 
433*946379e7Schristos 
434*946379e7Schristos /* Supports only one pushback character, and not '\n'.  */
435*946379e7Schristos static inline void
phase0_ungetc(int c)436*946379e7Schristos phase0_ungetc (int c)
437*946379e7Schristos {
438*946379e7Schristos   if (c != EOF)
439*946379e7Schristos     ungetc (c, fp);
440*946379e7Schristos }
441*946379e7Schristos 
442*946379e7Schristos 
443*946379e7Schristos /* 1. line_number handling.  Combine backslash-newline to nothing.  */
444*946379e7Schristos 
445*946379e7Schristos static unsigned char phase1_pushback[2];
446*946379e7Schristos static int phase1_pushback_length;
447*946379e7Schristos 
448*946379e7Schristos 
449*946379e7Schristos static int
phase1_getc()450*946379e7Schristos phase1_getc ()
451*946379e7Schristos {
452*946379e7Schristos   int c;
453*946379e7Schristos 
454*946379e7Schristos   if (phase1_pushback_length)
455*946379e7Schristos     {
456*946379e7Schristos       c = phase1_pushback[--phase1_pushback_length];
457*946379e7Schristos       if (c == '\n')
458*946379e7Schristos 	++line_number;
459*946379e7Schristos       return c;
460*946379e7Schristos     }
461*946379e7Schristos   for (;;)
462*946379e7Schristos     {
463*946379e7Schristos       c = phase0_getc ();
464*946379e7Schristos       switch (c)
465*946379e7Schristos 	{
466*946379e7Schristos 	case '\n':
467*946379e7Schristos 	  ++line_number;
468*946379e7Schristos 	  return '\n';
469*946379e7Schristos 
470*946379e7Schristos 	case '\\':
471*946379e7Schristos 	  c = phase0_getc ();
472*946379e7Schristos 	  if (c != '\n')
473*946379e7Schristos 	    {
474*946379e7Schristos 	      phase0_ungetc (c);
475*946379e7Schristos 	      return '\\';
476*946379e7Schristos 	    }
477*946379e7Schristos 	  ++line_number;
478*946379e7Schristos 	  break;
479*946379e7Schristos 
480*946379e7Schristos 	default:
481*946379e7Schristos 	  return c;
482*946379e7Schristos 	}
483*946379e7Schristos     }
484*946379e7Schristos }
485*946379e7Schristos 
486*946379e7Schristos 
487*946379e7Schristos /* Supports 2 characters of pushback.  */
488*946379e7Schristos static void
phase1_ungetc(int c)489*946379e7Schristos phase1_ungetc (int c)
490*946379e7Schristos {
491*946379e7Schristos   switch (c)
492*946379e7Schristos     {
493*946379e7Schristos     case EOF:
494*946379e7Schristos       break;
495*946379e7Schristos 
496*946379e7Schristos     case '\n':
497*946379e7Schristos       --line_number;
498*946379e7Schristos       /* FALLTHROUGH */
499*946379e7Schristos 
500*946379e7Schristos     default:
501*946379e7Schristos       if (phase1_pushback_length == SIZEOF (phase1_pushback))
502*946379e7Schristos 	abort ();
503*946379e7Schristos       phase1_pushback[phase1_pushback_length++] = c;
504*946379e7Schristos       break;
505*946379e7Schristos     }
506*946379e7Schristos }
507*946379e7Schristos 
508*946379e7Schristos 
509*946379e7Schristos /* 2. Convert trigraphs to their single character equivalents.  Most
510*946379e7Schristos    sane human beings vomit copiously at the mention of trigraphs, which
511*946379e7Schristos    is why they are an option.  */
512*946379e7Schristos 
513*946379e7Schristos static unsigned char phase2_pushback[1];
514*946379e7Schristos static int phase2_pushback_length;
515*946379e7Schristos 
516*946379e7Schristos 
517*946379e7Schristos static int
phase2_getc()518*946379e7Schristos phase2_getc ()
519*946379e7Schristos {
520*946379e7Schristos   int c;
521*946379e7Schristos 
522*946379e7Schristos   if (phase2_pushback_length)
523*946379e7Schristos     return phase2_pushback[--phase2_pushback_length];
524*946379e7Schristos   if (!trigraphs)
525*946379e7Schristos     return phase1_getc ();
526*946379e7Schristos 
527*946379e7Schristos   c = phase1_getc ();
528*946379e7Schristos   if (c != '?')
529*946379e7Schristos     return c;
530*946379e7Schristos   c = phase1_getc ();
531*946379e7Schristos   if (c != '?')
532*946379e7Schristos     {
533*946379e7Schristos       phase1_ungetc (c);
534*946379e7Schristos       return '?';
535*946379e7Schristos     }
536*946379e7Schristos   c = phase1_getc ();
537*946379e7Schristos   switch (c)
538*946379e7Schristos     {
539*946379e7Schristos     case '(':
540*946379e7Schristos       return '[';
541*946379e7Schristos     case '/':
542*946379e7Schristos       return '\\';
543*946379e7Schristos     case ')':
544*946379e7Schristos       return ']';
545*946379e7Schristos     case '\'':
546*946379e7Schristos       return '^';
547*946379e7Schristos     case '<':
548*946379e7Schristos       return '{';
549*946379e7Schristos     case '!':
550*946379e7Schristos       return '|';
551*946379e7Schristos     case '>':
552*946379e7Schristos       return '}';
553*946379e7Schristos     case '-':
554*946379e7Schristos       return '~';
555*946379e7Schristos     case '#':
556*946379e7Schristos       return '=';
557*946379e7Schristos     }
558*946379e7Schristos   phase1_ungetc (c);
559*946379e7Schristos   phase1_ungetc ('?');
560*946379e7Schristos   return '?';
561*946379e7Schristos }
562*946379e7Schristos 
563*946379e7Schristos 
564*946379e7Schristos /* Supports only one pushback character.  */
565*946379e7Schristos static void
phase2_ungetc(int c)566*946379e7Schristos phase2_ungetc (int c)
567*946379e7Schristos {
568*946379e7Schristos   if (c != EOF)
569*946379e7Schristos     {
570*946379e7Schristos       if (phase2_pushback_length == SIZEOF (phase2_pushback))
571*946379e7Schristos 	abort ();
572*946379e7Schristos       phase2_pushback[phase2_pushback_length++] = c;
573*946379e7Schristos     }
574*946379e7Schristos }
575*946379e7Schristos 
576*946379e7Schristos 
577*946379e7Schristos /* 3. Concatenate each line ending in backslash (\) with the following
578*946379e7Schristos    line.  Basically, all you need to do is elide "\\\n" sequences from
579*946379e7Schristos    the input.  */
580*946379e7Schristos 
581*946379e7Schristos static unsigned char phase3_pushback[2];
582*946379e7Schristos static int phase3_pushback_length;
583*946379e7Schristos 
584*946379e7Schristos 
585*946379e7Schristos static int
phase3_getc()586*946379e7Schristos phase3_getc ()
587*946379e7Schristos {
588*946379e7Schristos   if (phase3_pushback_length)
589*946379e7Schristos     return phase3_pushback[--phase3_pushback_length];
590*946379e7Schristos   for (;;)
591*946379e7Schristos     {
592*946379e7Schristos       int c = phase2_getc ();
593*946379e7Schristos       if (c != '\\')
594*946379e7Schristos 	return c;
595*946379e7Schristos       c = phase2_getc ();
596*946379e7Schristos       if (c != '\n')
597*946379e7Schristos 	{
598*946379e7Schristos 	  phase2_ungetc (c);
599*946379e7Schristos 	  return '\\';
600*946379e7Schristos 	}
601*946379e7Schristos     }
602*946379e7Schristos }
603*946379e7Schristos 
604*946379e7Schristos 
605*946379e7Schristos /* Supports 2 characters of pushback.  */
606*946379e7Schristos static void
phase3_ungetc(int c)607*946379e7Schristos phase3_ungetc (int c)
608*946379e7Schristos {
609*946379e7Schristos   if (c != EOF)
610*946379e7Schristos     {
611*946379e7Schristos       if (phase3_pushback_length == SIZEOF (phase3_pushback))
612*946379e7Schristos 	abort ();
613*946379e7Schristos       phase3_pushback[phase3_pushback_length++] = c;
614*946379e7Schristos     }
615*946379e7Schristos }
616*946379e7Schristos 
617*946379e7Schristos 
618*946379e7Schristos /* Accumulating comments.  */
619*946379e7Schristos 
620*946379e7Schristos static char *buffer;
621*946379e7Schristos static size_t bufmax;
622*946379e7Schristos static size_t buflen;
623*946379e7Schristos 
624*946379e7Schristos static inline void
comment_start()625*946379e7Schristos comment_start ()
626*946379e7Schristos {
627*946379e7Schristos   buflen = 0;
628*946379e7Schristos }
629*946379e7Schristos 
630*946379e7Schristos static inline void
comment_add(int c)631*946379e7Schristos comment_add (int c)
632*946379e7Schristos {
633*946379e7Schristos   if (buflen >= bufmax)
634*946379e7Schristos     {
635*946379e7Schristos       bufmax = 2 * bufmax + 10;
636*946379e7Schristos       buffer = xrealloc (buffer, bufmax);
637*946379e7Schristos     }
638*946379e7Schristos   buffer[buflen++] = c;
639*946379e7Schristos }
640*946379e7Schristos 
641*946379e7Schristos static inline void
comment_line_end(size_t chars_to_remove)642*946379e7Schristos comment_line_end (size_t chars_to_remove)
643*946379e7Schristos {
644*946379e7Schristos   buflen -= chars_to_remove;
645*946379e7Schristos   while (buflen >= 1
646*946379e7Schristos 	 && (buffer[buflen - 1] == ' ' || buffer[buflen - 1] == '\t'))
647*946379e7Schristos     --buflen;
648*946379e7Schristos   if (chars_to_remove == 0 && buflen >= bufmax)
649*946379e7Schristos     {
650*946379e7Schristos       bufmax = 2 * bufmax + 10;
651*946379e7Schristos       buffer = xrealloc (buffer, bufmax);
652*946379e7Schristos     }
653*946379e7Schristos   buffer[buflen] = '\0';
654*946379e7Schristos   savable_comment_add (buffer);
655*946379e7Schristos }
656*946379e7Schristos 
657*946379e7Schristos 
658*946379e7Schristos /* These are for tracking whether comments count as immediately before
659*946379e7Schristos    keyword.  */
660*946379e7Schristos static int last_comment_line;
661*946379e7Schristos static int last_non_comment_line;
662*946379e7Schristos static int newline_count;
663*946379e7Schristos 
664*946379e7Schristos 
665*946379e7Schristos /* 4. Replace each comment that is not inside a character constant or
666*946379e7Schristos    string literal with a space character.  We need to remember the
667*946379e7Schristos    comment for later, because it may be attached to a keyword string.
668*946379e7Schristos    We also optionally understand C++ comments.  */
669*946379e7Schristos 
670*946379e7Schristos static int
phase4_getc()671*946379e7Schristos phase4_getc ()
672*946379e7Schristos {
673*946379e7Schristos   int c;
674*946379e7Schristos   bool last_was_star;
675*946379e7Schristos 
676*946379e7Schristos   c = phase3_getc ();
677*946379e7Schristos   if (c != '/')
678*946379e7Schristos     return c;
679*946379e7Schristos   c = phase3_getc ();
680*946379e7Schristos   switch (c)
681*946379e7Schristos     {
682*946379e7Schristos     default:
683*946379e7Schristos       phase3_ungetc (c);
684*946379e7Schristos       return '/';
685*946379e7Schristos 
686*946379e7Schristos     case '*':
687*946379e7Schristos       /* C comment.  */
688*946379e7Schristos       comment_start ();
689*946379e7Schristos       last_was_star = false;
690*946379e7Schristos       for (;;)
691*946379e7Schristos 	{
692*946379e7Schristos 	  c = phase3_getc ();
693*946379e7Schristos 	  if (c == EOF)
694*946379e7Schristos 	    break;
695*946379e7Schristos 	  /* We skip all leading white space, but not EOLs.  */
696*946379e7Schristos 	  if (!(buflen == 0 && (c == ' ' || c == '\t')))
697*946379e7Schristos 	    comment_add (c);
698*946379e7Schristos 	  switch (c)
699*946379e7Schristos 	    {
700*946379e7Schristos 	    case '\n':
701*946379e7Schristos 	      comment_line_end (1);
702*946379e7Schristos 	      comment_start ();
703*946379e7Schristos 	      last_was_star = false;
704*946379e7Schristos 	      continue;
705*946379e7Schristos 
706*946379e7Schristos 	    case '*':
707*946379e7Schristos 	      last_was_star = true;
708*946379e7Schristos 	      continue;
709*946379e7Schristos 
710*946379e7Schristos 	    case '/':
711*946379e7Schristos 	      if (last_was_star)
712*946379e7Schristos 		{
713*946379e7Schristos 		  comment_line_end (2);
714*946379e7Schristos 		  break;
715*946379e7Schristos 		}
716*946379e7Schristos 	      /* FALLTHROUGH */
717*946379e7Schristos 
718*946379e7Schristos 	    default:
719*946379e7Schristos 	      last_was_star = false;
720*946379e7Schristos 	      continue;
721*946379e7Schristos 	    }
722*946379e7Schristos 	  break;
723*946379e7Schristos 	}
724*946379e7Schristos       last_comment_line = newline_count;
725*946379e7Schristos       return ' ';
726*946379e7Schristos 
727*946379e7Schristos     case '/':
728*946379e7Schristos       /* C++ or ISO C 99 comment.  */
729*946379e7Schristos       comment_start ();
730*946379e7Schristos       for (;;)
731*946379e7Schristos 	{
732*946379e7Schristos 	  c = phase3_getc ();
733*946379e7Schristos 	  if (c == '\n' || c == EOF)
734*946379e7Schristos 	    break;
735*946379e7Schristos 	  /* We skip all leading white space, but not EOLs.  */
736*946379e7Schristos 	  if (!(buflen == 0 && (c == ' ' || c == '\t')))
737*946379e7Schristos 	    comment_add (c);
738*946379e7Schristos 	}
739*946379e7Schristos       comment_line_end (0);
740*946379e7Schristos       last_comment_line = newline_count;
741*946379e7Schristos       return '\n';
742*946379e7Schristos     }
743*946379e7Schristos }
744*946379e7Schristos 
745*946379e7Schristos 
746*946379e7Schristos /* Supports only one pushback character.  */
747*946379e7Schristos static void
phase4_ungetc(int c)748*946379e7Schristos phase4_ungetc (int c)
749*946379e7Schristos {
750*946379e7Schristos   phase3_ungetc (c);
751*946379e7Schristos }
752*946379e7Schristos 
753*946379e7Schristos 
754*946379e7Schristos /* ========================== Reading of tokens.  ========================== */
755*946379e7Schristos 
756*946379e7Schristos 
757*946379e7Schristos /* True if ObjectiveC extensions are recognized.  */
758*946379e7Schristos static bool objc_extensions;
759*946379e7Schristos 
760*946379e7Schristos enum token_type_ty
761*946379e7Schristos {
762*946379e7Schristos   token_type_character_constant,	/* 'x' */
763*946379e7Schristos   token_type_eof,
764*946379e7Schristos   token_type_eoln,
765*946379e7Schristos   token_type_hash,			/* # */
766*946379e7Schristos   token_type_lparen,			/* ( */
767*946379e7Schristos   token_type_rparen,			/* ) */
768*946379e7Schristos   token_type_comma,			/* , */
769*946379e7Schristos   token_type_colon,			/* : */
770*946379e7Schristos   token_type_name,			/* abc */
771*946379e7Schristos   token_type_number,			/* 2.7 */
772*946379e7Schristos   token_type_string_literal,		/* "abc" */
773*946379e7Schristos   token_type_symbol,			/* < > = etc. */
774*946379e7Schristos   token_type_objc_special,		/* @ */
775*946379e7Schristos   token_type_white_space
776*946379e7Schristos };
777*946379e7Schristos typedef enum token_type_ty token_type_ty;
778*946379e7Schristos 
779*946379e7Schristos typedef struct token_ty token_ty;
780*946379e7Schristos struct token_ty
781*946379e7Schristos {
782*946379e7Schristos   token_type_ty type;
783*946379e7Schristos   char *string;		/* for token_type_name, token_type_string_literal */
784*946379e7Schristos   refcounted_string_list_ty *comment;	/* for token_type_string_literal,
785*946379e7Schristos 					   token_type_objc_special */
786*946379e7Schristos   long number;
787*946379e7Schristos   int line_number;
788*946379e7Schristos };
789*946379e7Schristos 
790*946379e7Schristos 
791*946379e7Schristos /* 7. Replace escape sequences within character strings with their
792*946379e7Schristos    single character equivalents.  This is called from phase 5, because
793*946379e7Schristos    we don't have to worry about the #include argument.  There are
794*946379e7Schristos    pathological cases which could bite us (like the DOS directory
795*946379e7Schristos    separator), but just pretend it can't happen.  */
796*946379e7Schristos 
797*946379e7Schristos #define P7_QUOTES (1000 + '"')
798*946379e7Schristos #define P7_QUOTE (1000 + '\'')
799*946379e7Schristos #define P7_NEWLINE (1000 + '\n')
800*946379e7Schristos 
801*946379e7Schristos static int
phase7_getc()802*946379e7Schristos phase7_getc ()
803*946379e7Schristos {
804*946379e7Schristos   int c, n, j;
805*946379e7Schristos 
806*946379e7Schristos   /* Use phase 3, because phase 4 elides comments.  */
807*946379e7Schristos   c = phase3_getc ();
808*946379e7Schristos 
809*946379e7Schristos   /* Return a magic newline indicator, so that we can distinguish
810*946379e7Schristos      between the user requesting a newline in the string (e.g. using
811*946379e7Schristos      "\n" or "\012") from the user failing to terminate the string or
812*946379e7Schristos      character constant.  The ANSI C standard says: 3.1.3.4 Character
813*946379e7Schristos      Constants contain ``any character except single quote, backslash or
814*946379e7Schristos      newline; or an escape sequence'' and 3.1.4 String Literals contain
815*946379e7Schristos      ``any character except double quote, backslash or newline; or an
816*946379e7Schristos      escape sequence''.
817*946379e7Schristos 
818*946379e7Schristos      Most compilers give a fatal error in this case, however gcc is
819*946379e7Schristos      stupidly silent, even though this is a very common typo.  OK, so
820*946379e7Schristos      gcc --pedantic will tell me, but that gripes about too much other
821*946379e7Schristos      stuff.  Could I have a ``gcc -Wnewline-in-string'' option, or
822*946379e7Schristos      better yet a ``gcc -fno-newline-in-string'' option, please?  Gcc is
823*946379e7Schristos      also inconsistent between string literals and character constants:
824*946379e7Schristos      you may not embed newlines in character constants; try it, you get
825*946379e7Schristos      a useful diagnostic.  --PMiller  */
826*946379e7Schristos   if (c == '\n')
827*946379e7Schristos     return P7_NEWLINE;
828*946379e7Schristos 
829*946379e7Schristos   if (c == '"')
830*946379e7Schristos     return P7_QUOTES;
831*946379e7Schristos   if (c == '\'')
832*946379e7Schristos     return P7_QUOTE;
833*946379e7Schristos   if (c != '\\')
834*946379e7Schristos     return c;
835*946379e7Schristos   c = phase3_getc ();
836*946379e7Schristos   switch (c)
837*946379e7Schristos     {
838*946379e7Schristos     default:
839*946379e7Schristos       /* Unknown escape sequences really should be an error, but just
840*946379e7Schristos 	 ignore them, and let the real compiler complain.  */
841*946379e7Schristos       phase3_ungetc (c);
842*946379e7Schristos       return '\\';
843*946379e7Schristos 
844*946379e7Schristos     case '"':
845*946379e7Schristos     case '\'':
846*946379e7Schristos     case '?':
847*946379e7Schristos     case '\\':
848*946379e7Schristos       return c;
849*946379e7Schristos 
850*946379e7Schristos     case 'a':
851*946379e7Schristos       return '\a';
852*946379e7Schristos     case 'b':
853*946379e7Schristos       return '\b';
854*946379e7Schristos 
855*946379e7Schristos       /* The \e escape is preculiar to gcc, and assumes an ASCII
856*946379e7Schristos 	 character set (or superset).  We don't provide support for it
857*946379e7Schristos 	 here.  */
858*946379e7Schristos 
859*946379e7Schristos     case 'f':
860*946379e7Schristos       return '\f';
861*946379e7Schristos     case 'n':
862*946379e7Schristos       return '\n';
863*946379e7Schristos     case 'r':
864*946379e7Schristos       return '\r';
865*946379e7Schristos     case 't':
866*946379e7Schristos       return '\t';
867*946379e7Schristos     case 'v':
868*946379e7Schristos       return '\v';
869*946379e7Schristos 
870*946379e7Schristos     case 'x':
871*946379e7Schristos       c = phase3_getc ();
872*946379e7Schristos       switch (c)
873*946379e7Schristos 	{
874*946379e7Schristos 	default:
875*946379e7Schristos 	  phase3_ungetc (c);
876*946379e7Schristos 	  phase3_ungetc ('x');
877*946379e7Schristos 	  return '\\';
878*946379e7Schristos 
879*946379e7Schristos 	case '0': case '1': case '2': case '3': case '4':
880*946379e7Schristos 	case '5': case '6': case '7': case '8': case '9':
881*946379e7Schristos 	case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
882*946379e7Schristos 	case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
883*946379e7Schristos 	  break;
884*946379e7Schristos 	}
885*946379e7Schristos       n = 0;
886*946379e7Schristos       for (;;)
887*946379e7Schristos 	{
888*946379e7Schristos 	  switch (c)
889*946379e7Schristos 	    {
890*946379e7Schristos 	    default:
891*946379e7Schristos 	      phase3_ungetc (c);
892*946379e7Schristos 	      return n;
893*946379e7Schristos 
894*946379e7Schristos 	    case '0': case '1': case '2': case '3': case '4':
895*946379e7Schristos 	    case '5': case '6': case '7': case '8': case '9':
896*946379e7Schristos 	      n = n * 16 + c - '0';
897*946379e7Schristos 	      break;
898*946379e7Schristos 
899*946379e7Schristos 	    case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
900*946379e7Schristos 	      n = n * 16 + 10 + c - 'A';
901*946379e7Schristos 	      break;
902*946379e7Schristos 
903*946379e7Schristos 	    case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
904*946379e7Schristos 	      n = n * 16 + 10 + c - 'a';
905*946379e7Schristos 	      break;
906*946379e7Schristos 	    }
907*946379e7Schristos 	  c = phase3_getc ();
908*946379e7Schristos 	}
909*946379e7Schristos       return n;
910*946379e7Schristos 
911*946379e7Schristos     case '0': case '1': case '2': case '3':
912*946379e7Schristos     case '4': case '5': case '6': case '7':
913*946379e7Schristos       n = 0;
914*946379e7Schristos       for (j = 0; j < 3; ++j)
915*946379e7Schristos 	{
916*946379e7Schristos 	  n = n * 8 + c - '0';
917*946379e7Schristos 	  c = phase3_getc ();
918*946379e7Schristos 	  switch (c)
919*946379e7Schristos 	    {
920*946379e7Schristos 	    default:
921*946379e7Schristos 	      break;
922*946379e7Schristos 
923*946379e7Schristos 	    case '0': case '1': case '2': case '3':
924*946379e7Schristos 	    case '4': case '5': case '6': case '7':
925*946379e7Schristos 	      continue;
926*946379e7Schristos 	    }
927*946379e7Schristos 	  break;
928*946379e7Schristos 	}
929*946379e7Schristos       phase3_ungetc (c);
930*946379e7Schristos       return n;
931*946379e7Schristos     }
932*946379e7Schristos }
933*946379e7Schristos 
934*946379e7Schristos 
935*946379e7Schristos static void
phase7_ungetc(int c)936*946379e7Schristos phase7_ungetc (int c)
937*946379e7Schristos {
938*946379e7Schristos   phase3_ungetc (c);
939*946379e7Schristos }
940*946379e7Schristos 
941*946379e7Schristos 
942*946379e7Schristos /* Free the memory pointed to by a 'struct token_ty'.  */
943*946379e7Schristos static inline void
free_token(token_ty * tp)944*946379e7Schristos free_token (token_ty *tp)
945*946379e7Schristos {
946*946379e7Schristos   if (tp->type == token_type_name || tp->type == token_type_string_literal)
947*946379e7Schristos     free (tp->string);
948*946379e7Schristos   if (tp->type == token_type_string_literal
949*946379e7Schristos       || tp->type == token_type_objc_special)
950*946379e7Schristos     drop_reference (tp->comment);
951*946379e7Schristos }
952*946379e7Schristos 
953*946379e7Schristos 
954*946379e7Schristos /* 5. Parse each resulting logical line as preprocessing tokens and
955*946379e7Schristos    white space.  Preprocessing tokens and C tokens don't always match.  */
956*946379e7Schristos 
957*946379e7Schristos static token_ty phase5_pushback[1];
958*946379e7Schristos static int phase5_pushback_length;
959*946379e7Schristos 
960*946379e7Schristos 
961*946379e7Schristos static void
phase5_get(token_ty * tp)962*946379e7Schristos phase5_get (token_ty *tp)
963*946379e7Schristos {
964*946379e7Schristos   static char *buffer;
965*946379e7Schristos   static int bufmax;
966*946379e7Schristos   int bufpos;
967*946379e7Schristos   int c;
968*946379e7Schristos 
969*946379e7Schristos   if (phase5_pushback_length)
970*946379e7Schristos     {
971*946379e7Schristos       *tp = phase5_pushback[--phase5_pushback_length];
972*946379e7Schristos       return;
973*946379e7Schristos     }
974*946379e7Schristos   tp->string = NULL;
975*946379e7Schristos   tp->number = 0;
976*946379e7Schristos   tp->line_number = line_number;
977*946379e7Schristos   c = phase4_getc ();
978*946379e7Schristos   switch (c)
979*946379e7Schristos     {
980*946379e7Schristos     case EOF:
981*946379e7Schristos       tp->type = token_type_eof;
982*946379e7Schristos       return;
983*946379e7Schristos 
984*946379e7Schristos     case '\n':
985*946379e7Schristos       tp->type = token_type_eoln;
986*946379e7Schristos       return;
987*946379e7Schristos 
988*946379e7Schristos     case ' ':
989*946379e7Schristos     case '\f':
990*946379e7Schristos     case '\t':
991*946379e7Schristos       for (;;)
992*946379e7Schristos 	{
993*946379e7Schristos 	  c = phase4_getc ();
994*946379e7Schristos 	  switch (c)
995*946379e7Schristos 	    {
996*946379e7Schristos 	    case ' ':
997*946379e7Schristos 	    case '\f':
998*946379e7Schristos 	    case '\t':
999*946379e7Schristos 	      continue;
1000*946379e7Schristos 
1001*946379e7Schristos 	    default:
1002*946379e7Schristos 	      phase4_ungetc (c);
1003*946379e7Schristos 	      break;
1004*946379e7Schristos 	    }
1005*946379e7Schristos 	  break;
1006*946379e7Schristos 	}
1007*946379e7Schristos       tp->type = token_type_white_space;
1008*946379e7Schristos       return;
1009*946379e7Schristos 
1010*946379e7Schristos     case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
1011*946379e7Schristos     case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
1012*946379e7Schristos     case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
1013*946379e7Schristos     case 'V': case 'W': case 'X': case 'Y': case 'Z':
1014*946379e7Schristos     case '_':
1015*946379e7Schristos     case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
1016*946379e7Schristos     case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
1017*946379e7Schristos     case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
1018*946379e7Schristos     case 'v': case 'w': case 'x': case 'y': case 'z':
1019*946379e7Schristos       bufpos = 0;
1020*946379e7Schristos       for (;;)
1021*946379e7Schristos 	{
1022*946379e7Schristos 	  if (bufpos >= bufmax)
1023*946379e7Schristos 	    {
1024*946379e7Schristos 	      bufmax = 2 * bufmax + 10;
1025*946379e7Schristos 	      buffer = xrealloc (buffer, bufmax);
1026*946379e7Schristos 	    }
1027*946379e7Schristos 	  buffer[bufpos++] = c;
1028*946379e7Schristos 	  c = phase4_getc ();
1029*946379e7Schristos 	  switch (c)
1030*946379e7Schristos 	    {
1031*946379e7Schristos 	    case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
1032*946379e7Schristos 	    case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
1033*946379e7Schristos 	    case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
1034*946379e7Schristos 	    case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
1035*946379e7Schristos 	    case 'Y': case 'Z':
1036*946379e7Schristos 	    case '_':
1037*946379e7Schristos 	    case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
1038*946379e7Schristos 	    case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
1039*946379e7Schristos 	    case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
1040*946379e7Schristos 	    case 's': case 't': case 'u': case 'v': case 'w': case 'x':
1041*946379e7Schristos 	    case 'y': case 'z':
1042*946379e7Schristos 	    case '0': case '1': case '2': case '3': case '4':
1043*946379e7Schristos 	    case '5': case '6': case '7': case '8': case '9':
1044*946379e7Schristos 	      continue;
1045*946379e7Schristos 
1046*946379e7Schristos 	    default:
1047*946379e7Schristos 	      phase4_ungetc (c);
1048*946379e7Schristos 	      break;
1049*946379e7Schristos 	    }
1050*946379e7Schristos 	  break;
1051*946379e7Schristos 	}
1052*946379e7Schristos       if (bufpos >= bufmax)
1053*946379e7Schristos 	{
1054*946379e7Schristos 	  bufmax = 2 * bufmax + 10;
1055*946379e7Schristos 	  buffer = xrealloc (buffer, bufmax);
1056*946379e7Schristos 	}
1057*946379e7Schristos       buffer[bufpos] = 0;
1058*946379e7Schristos       tp->string = xstrdup (buffer);
1059*946379e7Schristos       tp->type = token_type_name;
1060*946379e7Schristos       return;
1061*946379e7Schristos 
1062*946379e7Schristos     case '.':
1063*946379e7Schristos       c = phase4_getc ();
1064*946379e7Schristos       phase4_ungetc (c);
1065*946379e7Schristos       switch (c)
1066*946379e7Schristos 	{
1067*946379e7Schristos 	default:
1068*946379e7Schristos 	  tp->type = token_type_symbol;
1069*946379e7Schristos 	  return;
1070*946379e7Schristos 
1071*946379e7Schristos 	case '0': case '1': case '2': case '3': case '4':
1072*946379e7Schristos 	case '5': case '6': case '7': case '8': case '9':
1073*946379e7Schristos 	  c = '.';
1074*946379e7Schristos 	  break;
1075*946379e7Schristos 	}
1076*946379e7Schristos       /* FALLTHROUGH */
1077*946379e7Schristos 
1078*946379e7Schristos     case '0': case '1': case '2': case '3': case '4':
1079*946379e7Schristos     case '5': case '6': case '7': case '8': case '9':
1080*946379e7Schristos       /* The preprocessing number token is more "generous" than the C
1081*946379e7Schristos 	 number tokens.  This is mostly due to token pasting (another
1082*946379e7Schristos 	 thing we can ignore here).  */
1083*946379e7Schristos       bufpos = 0;
1084*946379e7Schristos       for (;;)
1085*946379e7Schristos 	{
1086*946379e7Schristos 	  if (bufpos >= bufmax)
1087*946379e7Schristos 	    {
1088*946379e7Schristos 	      bufmax = 2 * bufmax + 10;
1089*946379e7Schristos 	      buffer = xrealloc (buffer, bufmax);
1090*946379e7Schristos 	    }
1091*946379e7Schristos 	  buffer[bufpos++] = c;
1092*946379e7Schristos 	  c = phase4_getc ();
1093*946379e7Schristos 	  switch (c)
1094*946379e7Schristos 	    {
1095*946379e7Schristos 	    case 'e':
1096*946379e7Schristos 	    case 'E':
1097*946379e7Schristos 	      if (bufpos >= bufmax)
1098*946379e7Schristos 		{
1099*946379e7Schristos 		  bufmax = 2 * bufmax + 10;
1100*946379e7Schristos 		  buffer = xrealloc (buffer, bufmax);
1101*946379e7Schristos 		}
1102*946379e7Schristos 	      buffer[bufpos++] = c;
1103*946379e7Schristos 	      c = phase4_getc ();
1104*946379e7Schristos 	      if (c != '+' || c != '-')
1105*946379e7Schristos 		{
1106*946379e7Schristos 		  phase4_ungetc (c);
1107*946379e7Schristos 		  break;
1108*946379e7Schristos 		}
1109*946379e7Schristos 	      continue;
1110*946379e7Schristos 
1111*946379e7Schristos 	    case 'A': case 'B': case 'C': case 'D':           case 'F':
1112*946379e7Schristos 	    case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
1113*946379e7Schristos 	    case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
1114*946379e7Schristos 	    case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
1115*946379e7Schristos 	    case 'Y': case 'Z':
1116*946379e7Schristos 	    case 'a': case 'b': case 'c': case 'd':           case 'f':
1117*946379e7Schristos 	    case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
1118*946379e7Schristos 	    case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
1119*946379e7Schristos 	    case 's': case 't': case 'u': case 'v': case 'w': case 'x':
1120*946379e7Schristos 	    case 'y': case 'z':
1121*946379e7Schristos 	    case '0': case '1': case '2': case '3': case '4':
1122*946379e7Schristos 	    case '5': case '6': case '7': case '8': case '9':
1123*946379e7Schristos 	    case '.':
1124*946379e7Schristos 	      continue;
1125*946379e7Schristos 
1126*946379e7Schristos 	    default:
1127*946379e7Schristos 	      phase4_ungetc (c);
1128*946379e7Schristos 	      break;
1129*946379e7Schristos 	    }
1130*946379e7Schristos 	  break;
1131*946379e7Schristos 	}
1132*946379e7Schristos       if (bufpos >= bufmax)
1133*946379e7Schristos 	{
1134*946379e7Schristos 	  bufmax = 2 * bufmax + 10;
1135*946379e7Schristos 	  buffer = xrealloc (buffer, bufmax);
1136*946379e7Schristos 	}
1137*946379e7Schristos       buffer[bufpos] = 0;
1138*946379e7Schristos       tp->type = token_type_number;
1139*946379e7Schristos       tp->number = atol (buffer);
1140*946379e7Schristos       return;
1141*946379e7Schristos 
1142*946379e7Schristos     case '\'':
1143*946379e7Schristos       /* We could worry about the 'L' before wide character constants,
1144*946379e7Schristos 	 but ignoring it has no effect unless one of the keywords is
1145*946379e7Schristos 	 "L".  Just pretend it won't happen.  Also, we don't need to
1146*946379e7Schristos 	 remember the character constant.  */
1147*946379e7Schristos       for (;;)
1148*946379e7Schristos 	{
1149*946379e7Schristos 	  c = phase7_getc ();
1150*946379e7Schristos 	  if (c == P7_NEWLINE)
1151*946379e7Schristos 	    {
1152*946379e7Schristos 	      error_with_progname = false;
1153*946379e7Schristos 	      error (0, 0, _("%s:%d: warning: unterminated character constant"),
1154*946379e7Schristos 		     logical_file_name, line_number - 1);
1155*946379e7Schristos 	      error_with_progname = true;
1156*946379e7Schristos 	      phase7_ungetc ('\n');
1157*946379e7Schristos 	      break;
1158*946379e7Schristos 	    }
1159*946379e7Schristos 	  if (c == EOF || c == P7_QUOTE)
1160*946379e7Schristos 	    break;
1161*946379e7Schristos 	}
1162*946379e7Schristos       tp->type = token_type_character_constant;
1163*946379e7Schristos       return;
1164*946379e7Schristos 
1165*946379e7Schristos     case '"':
1166*946379e7Schristos       /* We could worry about the 'L' before wide string constants,
1167*946379e7Schristos 	 but since gettext's argument is not a wide character string,
1168*946379e7Schristos 	 let the compiler complain about the argument not matching the
1169*946379e7Schristos 	 prototype.  Just pretend it won't happen.  */
1170*946379e7Schristos       bufpos = 0;
1171*946379e7Schristos       for (;;)
1172*946379e7Schristos 	{
1173*946379e7Schristos 	  c = phase7_getc ();
1174*946379e7Schristos 	  if (c == P7_NEWLINE)
1175*946379e7Schristos 	    {
1176*946379e7Schristos 	      error_with_progname = false;
1177*946379e7Schristos 	      error (0, 0, _("%s:%d: warning: unterminated string literal"),
1178*946379e7Schristos 		     logical_file_name, line_number - 1);
1179*946379e7Schristos 	      error_with_progname = true;
1180*946379e7Schristos 	      phase7_ungetc ('\n');
1181*946379e7Schristos 	      break;
1182*946379e7Schristos 	    }
1183*946379e7Schristos 	  if (c == EOF || c == P7_QUOTES)
1184*946379e7Schristos 	    break;
1185*946379e7Schristos 	  if (c == P7_QUOTE)
1186*946379e7Schristos 	    c = '\'';
1187*946379e7Schristos 	  if (bufpos >= bufmax)
1188*946379e7Schristos 	    {
1189*946379e7Schristos 	      bufmax = 2 * bufmax + 10;
1190*946379e7Schristos 	      buffer = xrealloc (buffer, bufmax);
1191*946379e7Schristos 	    }
1192*946379e7Schristos 	  buffer[bufpos++] = c;
1193*946379e7Schristos 	}
1194*946379e7Schristos       if (bufpos >= bufmax)
1195*946379e7Schristos 	{
1196*946379e7Schristos 	  bufmax = 2 * bufmax + 10;
1197*946379e7Schristos 	  buffer = xrealloc (buffer, bufmax);
1198*946379e7Schristos 	}
1199*946379e7Schristos       buffer[bufpos] = 0;
1200*946379e7Schristos       tp->type = token_type_string_literal;
1201*946379e7Schristos       tp->string = xstrdup (buffer);
1202*946379e7Schristos       tp->comment = add_reference (savable_comment);
1203*946379e7Schristos       return;
1204*946379e7Schristos 
1205*946379e7Schristos     case '(':
1206*946379e7Schristos       tp->type = token_type_lparen;
1207*946379e7Schristos       return;
1208*946379e7Schristos 
1209*946379e7Schristos     case ')':
1210*946379e7Schristos       tp->type = token_type_rparen;
1211*946379e7Schristos       return;
1212*946379e7Schristos 
1213*946379e7Schristos     case ',':
1214*946379e7Schristos       tp->type = token_type_comma;
1215*946379e7Schristos       return;
1216*946379e7Schristos 
1217*946379e7Schristos     case '#':
1218*946379e7Schristos       tp->type = token_type_hash;
1219*946379e7Schristos       return;
1220*946379e7Schristos 
1221*946379e7Schristos     case ':':
1222*946379e7Schristos       tp->type = token_type_colon;
1223*946379e7Schristos       return;
1224*946379e7Schristos 
1225*946379e7Schristos     case '@':
1226*946379e7Schristos       if (objc_extensions)
1227*946379e7Schristos 	{
1228*946379e7Schristos 	  tp->type = token_type_objc_special;
1229*946379e7Schristos 	  tp->comment = add_reference (savable_comment);
1230*946379e7Schristos 	  return;
1231*946379e7Schristos 	}
1232*946379e7Schristos       /* FALLTHROUGH */
1233*946379e7Schristos 
1234*946379e7Schristos     default:
1235*946379e7Schristos       /* We could carefully recognize each of the 2 and 3 character
1236*946379e7Schristos 	operators, but it is not necessary, as we only need to recognize
1237*946379e7Schristos 	gettext invocations.  Don't bother.  */
1238*946379e7Schristos       tp->type = token_type_symbol;
1239*946379e7Schristos       return;
1240*946379e7Schristos     }
1241*946379e7Schristos }
1242*946379e7Schristos 
1243*946379e7Schristos 
1244*946379e7Schristos /* Supports only one pushback token.  */
1245*946379e7Schristos static void
phase5_unget(token_ty * tp)1246*946379e7Schristos phase5_unget (token_ty *tp)
1247*946379e7Schristos {
1248*946379e7Schristos   if (tp->type != token_type_eof)
1249*946379e7Schristos     {
1250*946379e7Schristos       if (phase5_pushback_length == SIZEOF (phase5_pushback))
1251*946379e7Schristos 	abort ();
1252*946379e7Schristos       phase5_pushback[phase5_pushback_length++] = *tp;
1253*946379e7Schristos     }
1254*946379e7Schristos }
1255*946379e7Schristos 
1256*946379e7Schristos 
1257*946379e7Schristos /* X. Recognize a leading # symbol.  Leave leading hash as a hash, but
1258*946379e7Schristos    turn hash in the middle of a line into a plain symbol token.  This
1259*946379e7Schristos    makes the phase 6 easier.  */
1260*946379e7Schristos 
1261*946379e7Schristos static void
phaseX_get(token_ty * tp)1262*946379e7Schristos phaseX_get (token_ty *tp)
1263*946379e7Schristos {
1264*946379e7Schristos   static bool middle;	/* false at the beginning of a line, true otherwise.  */
1265*946379e7Schristos 
1266*946379e7Schristos   phase5_get (tp);
1267*946379e7Schristos 
1268*946379e7Schristos   if (tp->type == token_type_eoln || tp->type == token_type_eof)
1269*946379e7Schristos     middle = false;
1270*946379e7Schristos   else
1271*946379e7Schristos     {
1272*946379e7Schristos       if (middle)
1273*946379e7Schristos 	{
1274*946379e7Schristos 	  /* Turn hash in the middle of a line into a plain symbol token.  */
1275*946379e7Schristos 	  if (tp->type == token_type_hash)
1276*946379e7Schristos 	    tp->type = token_type_symbol;
1277*946379e7Schristos 	}
1278*946379e7Schristos       else
1279*946379e7Schristos 	{
1280*946379e7Schristos 	  /* When we see leading whitespace followed by a hash sign,
1281*946379e7Schristos 	     discard the leading white space token.  The hash is all
1282*946379e7Schristos 	     phase 6 is interested in.  */
1283*946379e7Schristos 	  if (tp->type == token_type_white_space)
1284*946379e7Schristos 	    {
1285*946379e7Schristos 	      token_ty next;
1286*946379e7Schristos 
1287*946379e7Schristos 	      phase5_get (&next);
1288*946379e7Schristos 	      if (next.type == token_type_hash)
1289*946379e7Schristos 		*tp = next;
1290*946379e7Schristos 	      else
1291*946379e7Schristos 		phase5_unget (&next);
1292*946379e7Schristos 	    }
1293*946379e7Schristos 	  middle = true;
1294*946379e7Schristos 	}
1295*946379e7Schristos     }
1296*946379e7Schristos }
1297*946379e7Schristos 
1298*946379e7Schristos 
1299*946379e7Schristos /* 6. Recognize and carry out directives (it also expands macros on
1300*946379e7Schristos    non-directive lines, which we do not do here).  The only directive
1301*946379e7Schristos    we care about are the #line and #define directive.  We throw all the
1302*946379e7Schristos    others away.  */
1303*946379e7Schristos 
1304*946379e7Schristos static token_ty phase6_pushback[2];
1305*946379e7Schristos static int phase6_pushback_length;
1306*946379e7Schristos 
1307*946379e7Schristos 
1308*946379e7Schristos static void
phase6_get(token_ty * tp)1309*946379e7Schristos phase6_get (token_ty *tp)
1310*946379e7Schristos {
1311*946379e7Schristos   static token_ty *buf;
1312*946379e7Schristos   static int bufmax;
1313*946379e7Schristos   int bufpos;
1314*946379e7Schristos   int j;
1315*946379e7Schristos 
1316*946379e7Schristos   if (phase6_pushback_length)
1317*946379e7Schristos     {
1318*946379e7Schristos       *tp = phase6_pushback[--phase6_pushback_length];
1319*946379e7Schristos       return;
1320*946379e7Schristos     }
1321*946379e7Schristos   for (;;)
1322*946379e7Schristos     {
1323*946379e7Schristos       /* Get the next token.  If it is not a '#' at the beginning of a
1324*946379e7Schristos 	 line (ignoring whitespace), return immediately.  */
1325*946379e7Schristos       phaseX_get (tp);
1326*946379e7Schristos       if (tp->type != token_type_hash)
1327*946379e7Schristos 	return;
1328*946379e7Schristos 
1329*946379e7Schristos       /* Accumulate the rest of the directive in a buffer, until the
1330*946379e7Schristos 	 "define" keyword is seen or until end of line.  */
1331*946379e7Schristos       bufpos = 0;
1332*946379e7Schristos       for (;;)
1333*946379e7Schristos 	{
1334*946379e7Schristos 	  phaseX_get (tp);
1335*946379e7Schristos 	  if (tp->type == token_type_eoln || tp->type == token_type_eof)
1336*946379e7Schristos 	    break;
1337*946379e7Schristos 
1338*946379e7Schristos 	  /* Before the "define" keyword and inside other directives
1339*946379e7Schristos 	     white space is irrelevant.  So just throw it away.  */
1340*946379e7Schristos 	  if (tp->type != token_type_white_space)
1341*946379e7Schristos 	    {
1342*946379e7Schristos 	      /* If it is a #define directive, return immediately,
1343*946379e7Schristos 		 thus treating the body of the #define directive like
1344*946379e7Schristos 		 normal input.  */
1345*946379e7Schristos 	      if (bufpos == 0
1346*946379e7Schristos 		  && tp->type == token_type_name
1347*946379e7Schristos 		  && strcmp (tp->string, "define") == 0)
1348*946379e7Schristos 		return;
1349*946379e7Schristos 
1350*946379e7Schristos 	      /* Accumulate.  */
1351*946379e7Schristos 	      if (bufpos >= bufmax)
1352*946379e7Schristos 		{
1353*946379e7Schristos 		  bufmax = 2 * bufmax + 10;
1354*946379e7Schristos 		  buf = xrealloc (buf, bufmax * sizeof (buf[0]));
1355*946379e7Schristos 		}
1356*946379e7Schristos 	      buf[bufpos++] = *tp;
1357*946379e7Schristos 	    }
1358*946379e7Schristos 	}
1359*946379e7Schristos 
1360*946379e7Schristos       /* If it is a #line directive, with no macros to expand, act on
1361*946379e7Schristos 	 it.  Ignore all other directives.  */
1362*946379e7Schristos       if (bufpos >= 3 && buf[0].type == token_type_name
1363*946379e7Schristos 	  && strcmp (buf[0].string, "line") == 0
1364*946379e7Schristos 	  && buf[1].type == token_type_number
1365*946379e7Schristos 	  && buf[2].type == token_type_string_literal)
1366*946379e7Schristos 	{
1367*946379e7Schristos 	  logical_file_name = xstrdup (buf[2].string);
1368*946379e7Schristos 	  line_number = buf[1].number;
1369*946379e7Schristos 	}
1370*946379e7Schristos       if (bufpos >= 2 && buf[0].type == token_type_number
1371*946379e7Schristos 	  && buf[1].type == token_type_string_literal)
1372*946379e7Schristos 	{
1373*946379e7Schristos 	  logical_file_name = xstrdup (buf[1].string);
1374*946379e7Schristos 	  line_number = buf[0].number;
1375*946379e7Schristos 	}
1376*946379e7Schristos 
1377*946379e7Schristos       /* Release the storage held by the directive.  */
1378*946379e7Schristos       for (j = 0; j < bufpos; ++j)
1379*946379e7Schristos 	free_token (&buf[j]);
1380*946379e7Schristos 
1381*946379e7Schristos       /* We must reset the selected comments.  */
1382*946379e7Schristos       savable_comment_reset ();
1383*946379e7Schristos     }
1384*946379e7Schristos }
1385*946379e7Schristos 
1386*946379e7Schristos 
1387*946379e7Schristos /* Supports 2 tokens of pushback.  */
1388*946379e7Schristos static void
phase6_unget(token_ty * tp)1389*946379e7Schristos phase6_unget (token_ty *tp)
1390*946379e7Schristos {
1391*946379e7Schristos   if (tp->type != token_type_eof)
1392*946379e7Schristos     {
1393*946379e7Schristos       if (phase6_pushback_length == SIZEOF (phase6_pushback))
1394*946379e7Schristos 	abort ();
1395*946379e7Schristos       phase6_pushback[phase6_pushback_length++] = *tp;
1396*946379e7Schristos     }
1397*946379e7Schristos }
1398*946379e7Schristos 
1399*946379e7Schristos 
1400*946379e7Schristos /* 8a. Convert ISO C 99 section 7.8.1 format string directives to string
1401*946379e7Schristos    literal placeholders.  */
1402*946379e7Schristos 
1403*946379e7Schristos /* Test for an ISO C 99 section 7.8.1 format string directive.  */
1404*946379e7Schristos static bool
is_inttypes_macro(const char * name)1405*946379e7Schristos is_inttypes_macro (const char *name)
1406*946379e7Schristos {
1407*946379e7Schristos   /* Syntax:
1408*946379e7Schristos      P R I { d | i | o | u | x | X }
1409*946379e7Schristos      { { | LEAST | FAST } { 8 | 16 | 32 | 64 } | MAX | PTR }  */
1410*946379e7Schristos   if (name[0] == 'P' && name[1] == 'R' && name[2] == 'I')
1411*946379e7Schristos     {
1412*946379e7Schristos       name += 3;
1413*946379e7Schristos       if (name[0] == 'd' || name[0] == 'i' || name[0] == 'o' || name[0] == 'u'
1414*946379e7Schristos 	  || name[0] == 'x' || name[0] == 'X')
1415*946379e7Schristos 	{
1416*946379e7Schristos 	  name += 1;
1417*946379e7Schristos 	  if (name[0] == 'M' && name[1] == 'A' && name[2] == 'X'
1418*946379e7Schristos 	      && name[3] == '\0')
1419*946379e7Schristos 	    return true;
1420*946379e7Schristos 	  if (name[0] == 'P' && name[1] == 'T' && name[2] == 'R'
1421*946379e7Schristos 	      && name[3] == '\0')
1422*946379e7Schristos 	    return true;
1423*946379e7Schristos 	  if (name[0] == 'L' && name[1] == 'E' && name[2] == 'A'
1424*946379e7Schristos 	      && name[3] == 'S' && name[4] == 'T')
1425*946379e7Schristos 	    name += 5;
1426*946379e7Schristos 	  else if (name[0] == 'F' && name[1] == 'A' && name[2] == 'S'
1427*946379e7Schristos 		   && name[3] == 'T')
1428*946379e7Schristos 	    name += 4;
1429*946379e7Schristos 	  if (name[0] == '8' && name[1] == '\0')
1430*946379e7Schristos 	    return true;
1431*946379e7Schristos 	  if (name[0] == '1' && name[1] == '6' && name[2] == '\0')
1432*946379e7Schristos 	    return true;
1433*946379e7Schristos 	  if (name[0] == '3' && name[1] == '2' && name[2] == '\0')
1434*946379e7Schristos 	    return true;
1435*946379e7Schristos 	  if (name[0] == '6' && name[1] == '4' && name[2] == '\0')
1436*946379e7Schristos 	    return true;
1437*946379e7Schristos 	}
1438*946379e7Schristos     }
1439*946379e7Schristos   return false;
1440*946379e7Schristos }
1441*946379e7Schristos 
1442*946379e7Schristos static void
phase8a_get(token_ty * tp)1443*946379e7Schristos phase8a_get (token_ty *tp)
1444*946379e7Schristos {
1445*946379e7Schristos   phase6_get (tp);
1446*946379e7Schristos   if (tp->type == token_type_name && is_inttypes_macro (tp->string))
1447*946379e7Schristos     {
1448*946379e7Schristos       /* Turn PRIdXXX into "<PRIdXXX>".  */
1449*946379e7Schristos       char *new_string = xasprintf ("<%s>", tp->string);
1450*946379e7Schristos       free (tp->string);
1451*946379e7Schristos       tp->string = new_string;
1452*946379e7Schristos       tp->comment = add_reference (savable_comment);
1453*946379e7Schristos       tp->type = token_type_string_literal;
1454*946379e7Schristos     }
1455*946379e7Schristos }
1456*946379e7Schristos 
1457*946379e7Schristos /* Supports 2 tokens of pushback.  */
1458*946379e7Schristos static inline void
phase8a_unget(token_ty * tp)1459*946379e7Schristos phase8a_unget (token_ty *tp)
1460*946379e7Schristos {
1461*946379e7Schristos   phase6_unget (tp);
1462*946379e7Schristos }
1463*946379e7Schristos 
1464*946379e7Schristos 
1465*946379e7Schristos /* 8b. Drop whitespace.  */
1466*946379e7Schristos static void
phase8b_get(token_ty * tp)1467*946379e7Schristos phase8b_get (token_ty *tp)
1468*946379e7Schristos {
1469*946379e7Schristos   for (;;)
1470*946379e7Schristos     {
1471*946379e7Schristos       phase8a_get (tp);
1472*946379e7Schristos 
1473*946379e7Schristos       if (tp->type == token_type_white_space)
1474*946379e7Schristos 	continue;
1475*946379e7Schristos       if (tp->type == token_type_eoln)
1476*946379e7Schristos 	{
1477*946379e7Schristos 	  /* We have to track the last occurrence of a string.  One
1478*946379e7Schristos 	     mode of xgettext allows to group an extracted message
1479*946379e7Schristos 	     with a comment for documentation.  The rule which states
1480*946379e7Schristos 	     which comment is assumed to be grouped with the message
1481*946379e7Schristos 	     says it should immediately precede it.  Our
1482*946379e7Schristos 	     interpretation: between the last line of the comment and
1483*946379e7Schristos 	     the line in which the keyword is found must be no line
1484*946379e7Schristos 	     with non-white space tokens.  */
1485*946379e7Schristos 	  ++newline_count;
1486*946379e7Schristos 	  if (last_non_comment_line > last_comment_line)
1487*946379e7Schristos 	    savable_comment_reset ();
1488*946379e7Schristos 	  continue;
1489*946379e7Schristos 	}
1490*946379e7Schristos       break;
1491*946379e7Schristos     }
1492*946379e7Schristos }
1493*946379e7Schristos 
1494*946379e7Schristos /* Supports 2 tokens of pushback.  */
1495*946379e7Schristos static inline void
phase8b_unget(token_ty * tp)1496*946379e7Schristos phase8b_unget (token_ty *tp)
1497*946379e7Schristos {
1498*946379e7Schristos   phase8a_unget (tp);
1499*946379e7Schristos }
1500*946379e7Schristos 
1501*946379e7Schristos 
1502*946379e7Schristos /* 8c. In ObjectiveC mode, drop '@' before a literal string.  We need to
1503*946379e7Schristos    do this before performing concatenation of adjacent string literals.  */
1504*946379e7Schristos static void
phase8c_get(token_ty * tp)1505*946379e7Schristos phase8c_get (token_ty *tp)
1506*946379e7Schristos {
1507*946379e7Schristos   token_ty tmp;
1508*946379e7Schristos 
1509*946379e7Schristos   phase8b_get (tp);
1510*946379e7Schristos   if (tp->type != token_type_objc_special)
1511*946379e7Schristos     return;
1512*946379e7Schristos   phase8b_get (&tmp);
1513*946379e7Schristos   if (tmp.type != token_type_string_literal)
1514*946379e7Schristos     {
1515*946379e7Schristos       phase8b_unget (&tmp);
1516*946379e7Schristos       return;
1517*946379e7Schristos     }
1518*946379e7Schristos   /* Drop the '@' token and return immediately the following string.  */
1519*946379e7Schristos   drop_reference (tmp.comment);
1520*946379e7Schristos   tmp.comment = tp->comment;
1521*946379e7Schristos   *tp = tmp;
1522*946379e7Schristos }
1523*946379e7Schristos 
1524*946379e7Schristos /* Supports only one pushback token.  */
1525*946379e7Schristos static inline void
phase8c_unget(token_ty * tp)1526*946379e7Schristos phase8c_unget (token_ty *tp)
1527*946379e7Schristos {
1528*946379e7Schristos   phase8b_unget (tp);
1529*946379e7Schristos }
1530*946379e7Schristos 
1531*946379e7Schristos 
1532*946379e7Schristos /* 8. Concatenate adjacent string literals to form single string
1533*946379e7Schristos    literals (because we don't expand macros, there are a few things we
1534*946379e7Schristos    will miss).  */
1535*946379e7Schristos 
1536*946379e7Schristos static void
phase8_get(token_ty * tp)1537*946379e7Schristos phase8_get (token_ty *tp)
1538*946379e7Schristos {
1539*946379e7Schristos   phase8c_get (tp);
1540*946379e7Schristos   if (tp->type != token_type_string_literal)
1541*946379e7Schristos     return;
1542*946379e7Schristos   for (;;)
1543*946379e7Schristos     {
1544*946379e7Schristos       token_ty tmp;
1545*946379e7Schristos       size_t len;
1546*946379e7Schristos 
1547*946379e7Schristos       phase8c_get (&tmp);
1548*946379e7Schristos       if (tmp.type != token_type_string_literal)
1549*946379e7Schristos 	{
1550*946379e7Schristos 	  phase8c_unget (&tmp);
1551*946379e7Schristos 	  return;
1552*946379e7Schristos 	}
1553*946379e7Schristos       len = strlen (tp->string);
1554*946379e7Schristos       tp->string = xrealloc (tp->string, len + strlen (tmp.string) + 1);
1555*946379e7Schristos       strcpy (tp->string + len, tmp.string);
1556*946379e7Schristos       free (tmp.string);
1557*946379e7Schristos     }
1558*946379e7Schristos }
1559*946379e7Schristos 
1560*946379e7Schristos 
1561*946379e7Schristos /* ===================== Reading of high-level tokens.  ==================== */
1562*946379e7Schristos 
1563*946379e7Schristos 
1564*946379e7Schristos enum xgettext_token_type_ty
1565*946379e7Schristos {
1566*946379e7Schristos   xgettext_token_type_eof,
1567*946379e7Schristos   xgettext_token_type_keyword,
1568*946379e7Schristos   xgettext_token_type_symbol,
1569*946379e7Schristos   xgettext_token_type_lparen,
1570*946379e7Schristos   xgettext_token_type_rparen,
1571*946379e7Schristos   xgettext_token_type_comma,
1572*946379e7Schristos   xgettext_token_type_colon,
1573*946379e7Schristos   xgettext_token_type_string_literal,
1574*946379e7Schristos   xgettext_token_type_other
1575*946379e7Schristos };
1576*946379e7Schristos typedef enum xgettext_token_type_ty xgettext_token_type_ty;
1577*946379e7Schristos 
1578*946379e7Schristos typedef struct xgettext_token_ty xgettext_token_ty;
1579*946379e7Schristos struct xgettext_token_ty
1580*946379e7Schristos {
1581*946379e7Schristos   xgettext_token_type_ty type;
1582*946379e7Schristos 
1583*946379e7Schristos   /* This field is used only for xgettext_token_type_keyword.  */
1584*946379e7Schristos   const struct callshapes *shapes;
1585*946379e7Schristos 
1586*946379e7Schristos   /* This field is used only for xgettext_token_type_string_literal,
1587*946379e7Schristos      xgettext_token_type_keyword, xgettext_token_type_symbol.  */
1588*946379e7Schristos   char *string;
1589*946379e7Schristos 
1590*946379e7Schristos   /* This field is used only for xgettext_token_type_string_literal.  */
1591*946379e7Schristos   refcounted_string_list_ty *comment;
1592*946379e7Schristos 
1593*946379e7Schristos   /* These fields are only for
1594*946379e7Schristos        xgettext_token_type_keyword,
1595*946379e7Schristos        xgettext_token_type_string_literal.  */
1596*946379e7Schristos   lex_pos_ty pos;
1597*946379e7Schristos };
1598*946379e7Schristos 
1599*946379e7Schristos 
1600*946379e7Schristos /* 9. Convert the remaining preprocessing tokens to C tokens and
1601*946379e7Schristos    discards any white space from the translation unit.  */
1602*946379e7Schristos 
1603*946379e7Schristos static void
x_c_lex(xgettext_token_ty * tp)1604*946379e7Schristos x_c_lex (xgettext_token_ty *tp)
1605*946379e7Schristos {
1606*946379e7Schristos   for (;;)
1607*946379e7Schristos     {
1608*946379e7Schristos       token_ty token;
1609*946379e7Schristos       void *keyword_value;
1610*946379e7Schristos 
1611*946379e7Schristos       phase8_get (&token);
1612*946379e7Schristos       switch (token.type)
1613*946379e7Schristos 	{
1614*946379e7Schristos 	case token_type_eof:
1615*946379e7Schristos 	  tp->type = xgettext_token_type_eof;
1616*946379e7Schristos 	  return;
1617*946379e7Schristos 
1618*946379e7Schristos 	case token_type_name:
1619*946379e7Schristos 	  last_non_comment_line = newline_count;
1620*946379e7Schristos 
1621*946379e7Schristos 	  if (hash_find_entry (objc_extensions ? &objc_keywords : &c_keywords,
1622*946379e7Schristos 			       token.string, strlen (token.string),
1623*946379e7Schristos 			       &keyword_value)
1624*946379e7Schristos 	      == 0)
1625*946379e7Schristos 	    {
1626*946379e7Schristos 	      tp->type = xgettext_token_type_keyword;
1627*946379e7Schristos 	      tp->shapes = (const struct callshapes *) keyword_value;
1628*946379e7Schristos 	      tp->pos.file_name = logical_file_name;
1629*946379e7Schristos 	      tp->pos.line_number = token.line_number;
1630*946379e7Schristos 	    }
1631*946379e7Schristos 	  else
1632*946379e7Schristos 	    tp->type = xgettext_token_type_symbol;
1633*946379e7Schristos 	  tp->string = token.string;
1634*946379e7Schristos 	  return;
1635*946379e7Schristos 
1636*946379e7Schristos 	case token_type_lparen:
1637*946379e7Schristos 	  last_non_comment_line = newline_count;
1638*946379e7Schristos 
1639*946379e7Schristos 	  tp->type = xgettext_token_type_lparen;
1640*946379e7Schristos 	  return;
1641*946379e7Schristos 
1642*946379e7Schristos 	case token_type_rparen:
1643*946379e7Schristos 	  last_non_comment_line = newline_count;
1644*946379e7Schristos 
1645*946379e7Schristos 	  tp->type = xgettext_token_type_rparen;
1646*946379e7Schristos 	  return;
1647*946379e7Schristos 
1648*946379e7Schristos 	case token_type_comma:
1649*946379e7Schristos 	  last_non_comment_line = newline_count;
1650*946379e7Schristos 
1651*946379e7Schristos 	  tp->type = xgettext_token_type_comma;
1652*946379e7Schristos 	  return;
1653*946379e7Schristos 
1654*946379e7Schristos 	case token_type_colon:
1655*946379e7Schristos 	  last_non_comment_line = newline_count;
1656*946379e7Schristos 
1657*946379e7Schristos 	  tp->type = xgettext_token_type_colon;
1658*946379e7Schristos 	  return;
1659*946379e7Schristos 
1660*946379e7Schristos 	case token_type_string_literal:
1661*946379e7Schristos 	  last_non_comment_line = newline_count;
1662*946379e7Schristos 
1663*946379e7Schristos 	  tp->type = xgettext_token_type_string_literal;
1664*946379e7Schristos 	  tp->string = token.string;
1665*946379e7Schristos 	  tp->comment = token.comment;
1666*946379e7Schristos 	  tp->pos.file_name = logical_file_name;
1667*946379e7Schristos 	  tp->pos.line_number = token.line_number;
1668*946379e7Schristos 	  return;
1669*946379e7Schristos 
1670*946379e7Schristos 	case token_type_objc_special:
1671*946379e7Schristos 	  drop_reference (token.comment);
1672*946379e7Schristos 	  /* FALLTHROUGH */
1673*946379e7Schristos 
1674*946379e7Schristos 	default:
1675*946379e7Schristos 	  last_non_comment_line = newline_count;
1676*946379e7Schristos 
1677*946379e7Schristos 	  tp->type = xgettext_token_type_other;
1678*946379e7Schristos 	  return;
1679*946379e7Schristos 	}
1680*946379e7Schristos     }
1681*946379e7Schristos }
1682*946379e7Schristos 
1683*946379e7Schristos 
1684*946379e7Schristos /* ========================= Extracting strings.  ========================== */
1685*946379e7Schristos 
1686*946379e7Schristos 
1687*946379e7Schristos /* Context lookup table.  */
1688*946379e7Schristos static flag_context_list_table_ty *flag_context_list_table;
1689*946379e7Schristos 
1690*946379e7Schristos 
1691*946379e7Schristos /* The file is broken into tokens.  Scan the token stream, looking for
1692*946379e7Schristos    a keyword, followed by a left paren, followed by a string.  When we
1693*946379e7Schristos    see this sequence, we have something to remember.  We assume we are
1694*946379e7Schristos    looking at a valid C or C++ program, and leave the complaints about
1695*946379e7Schristos    the grammar to the compiler.
1696*946379e7Schristos 
1697*946379e7Schristos      Normal handling: Look for
1698*946379e7Schristos        keyword ( ... msgid ... )
1699*946379e7Schristos      Plural handling: Look for
1700*946379e7Schristos        keyword ( ... msgid ... msgid_plural ... )
1701*946379e7Schristos 
1702*946379e7Schristos    We use recursion because the arguments before msgid or between msgid
1703*946379e7Schristos    and msgid_plural can contain subexpressions of the same form.  */
1704*946379e7Schristos 
1705*946379e7Schristos 
1706*946379e7Schristos /* Extract messages until the next balanced closing parenthesis.
1707*946379e7Schristos    Extracted messages are added to MLP.
1708*946379e7Schristos    Return true upon eof, false upon closing parenthesis.  */
1709*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)1710*946379e7Schristos extract_parenthesized (message_list_ty *mlp,
1711*946379e7Schristos 		       flag_context_ty outer_context,
1712*946379e7Schristos 		       flag_context_list_iterator_ty context_iter,
1713*946379e7Schristos 		       struct arglist_parser *argparser)
1714*946379e7Schristos {
1715*946379e7Schristos   /* Current argument number.  */
1716*946379e7Schristos   int arg = 1;
1717*946379e7Schristos   /* 0 when no keyword has been seen.  1 right after a keyword is seen.  */
1718*946379e7Schristos   int state;
1719*946379e7Schristos   /* Parameters of the keyword just seen.  Defined only in state 1.  */
1720*946379e7Schristos   const struct callshapes *next_shapes = NULL;
1721*946379e7Schristos   /* Context iterator that will be used if the next token is a '('.  */
1722*946379e7Schristos   flag_context_list_iterator_ty next_context_iter =
1723*946379e7Schristos     passthrough_context_list_iterator;
1724*946379e7Schristos   /* Context iterator that will be used if the next token is a ':'.
1725*946379e7Schristos      (Objective C selector syntax.)  */
1726*946379e7Schristos   flag_context_list_iterator_ty selectorcall_context_iter =
1727*946379e7Schristos     passthrough_context_list_iterator;
1728*946379e7Schristos   /* Current context.  */
1729*946379e7Schristos   flag_context_ty inner_context =
1730*946379e7Schristos     inherited_context (outer_context,
1731*946379e7Schristos 		       flag_context_list_iterator_advance (&context_iter));
1732*946379e7Schristos 
1733*946379e7Schristos   /* Start state is 0.  */
1734*946379e7Schristos   state = 0;
1735*946379e7Schristos 
1736*946379e7Schristos   for (;;)
1737*946379e7Schristos     {
1738*946379e7Schristos       xgettext_token_ty token;
1739*946379e7Schristos 
1740*946379e7Schristos       x_c_lex (&token);
1741*946379e7Schristos       switch (token.type)
1742*946379e7Schristos 	{
1743*946379e7Schristos 	case xgettext_token_type_keyword:
1744*946379e7Schristos 	  next_shapes = token.shapes;
1745*946379e7Schristos 	  state = 1;
1746*946379e7Schristos 	  goto keyword_or_symbol;
1747*946379e7Schristos 
1748*946379e7Schristos 	case xgettext_token_type_symbol:
1749*946379e7Schristos 	  state = 0;
1750*946379e7Schristos 	keyword_or_symbol:
1751*946379e7Schristos 	  next_context_iter =
1752*946379e7Schristos 	    flag_context_list_iterator (
1753*946379e7Schristos 	      flag_context_list_table_lookup (
1754*946379e7Schristos 		flag_context_list_table,
1755*946379e7Schristos 		token.string, strlen (token.string)));
1756*946379e7Schristos 	  if (objc_extensions)
1757*946379e7Schristos 	    {
1758*946379e7Schristos 	      size_t token_string_len = strlen (token.string);
1759*946379e7Schristos 	      token.string = xrealloc (token.string, token_string_len + 2);
1760*946379e7Schristos 	      token.string[token_string_len] = ':';
1761*946379e7Schristos 	      token.string[token_string_len + 1] = '\0';
1762*946379e7Schristos 	      selectorcall_context_iter =
1763*946379e7Schristos 		flag_context_list_iterator (
1764*946379e7Schristos 		  flag_context_list_table_lookup (
1765*946379e7Schristos 		    flag_context_list_table,
1766*946379e7Schristos 		    token.string, token_string_len + 1));
1767*946379e7Schristos 	    }
1768*946379e7Schristos 	  free (token.string);
1769*946379e7Schristos 	  continue;
1770*946379e7Schristos 
1771*946379e7Schristos 	case xgettext_token_type_lparen:
1772*946379e7Schristos 	  if (extract_parenthesized (mlp, inner_context, next_context_iter,
1773*946379e7Schristos 				     arglist_parser_alloc (mlp,
1774*946379e7Schristos 							   state ? next_shapes : NULL)))
1775*946379e7Schristos 	    {
1776*946379e7Schristos 	      arglist_parser_done (argparser, arg);
1777*946379e7Schristos 	      return true;
1778*946379e7Schristos 	    }
1779*946379e7Schristos 	  next_context_iter = null_context_list_iterator;
1780*946379e7Schristos 	  selectorcall_context_iter = null_context_list_iterator;
1781*946379e7Schristos 	  state = 0;
1782*946379e7Schristos 	  continue;
1783*946379e7Schristos 
1784*946379e7Schristos 	case xgettext_token_type_rparen:
1785*946379e7Schristos 	  arglist_parser_done (argparser, arg);
1786*946379e7Schristos 	  return false;
1787*946379e7Schristos 
1788*946379e7Schristos 	case xgettext_token_type_comma:
1789*946379e7Schristos 	  arg++;
1790*946379e7Schristos 	  inner_context =
1791*946379e7Schristos 	    inherited_context (outer_context,
1792*946379e7Schristos 			       flag_context_list_iterator_advance (
1793*946379e7Schristos 				 &context_iter));
1794*946379e7Schristos 	  next_context_iter = passthrough_context_list_iterator;
1795*946379e7Schristos 	  selectorcall_context_iter = passthrough_context_list_iterator;
1796*946379e7Schristos 	  state = 0;
1797*946379e7Schristos 	  continue;
1798*946379e7Schristos 
1799*946379e7Schristos 	case xgettext_token_type_colon:
1800*946379e7Schristos 	  if (objc_extensions)
1801*946379e7Schristos 	    {
1802*946379e7Schristos 	      context_iter = selectorcall_context_iter;
1803*946379e7Schristos 	      inner_context =
1804*946379e7Schristos 		inherited_context (inner_context,
1805*946379e7Schristos 				   flag_context_list_iterator_advance (
1806*946379e7Schristos 				     &context_iter));
1807*946379e7Schristos 	      next_context_iter = passthrough_context_list_iterator;
1808*946379e7Schristos 	      selectorcall_context_iter = passthrough_context_list_iterator;
1809*946379e7Schristos 	    }
1810*946379e7Schristos 	  else
1811*946379e7Schristos 	    {
1812*946379e7Schristos 	      next_context_iter = null_context_list_iterator;
1813*946379e7Schristos 	      selectorcall_context_iter = null_context_list_iterator;
1814*946379e7Schristos 	    }
1815*946379e7Schristos 	  state = 0;
1816*946379e7Schristos 	  continue;
1817*946379e7Schristos 
1818*946379e7Schristos 	case xgettext_token_type_string_literal:
1819*946379e7Schristos 	  if (extract_all)
1820*946379e7Schristos 	    remember_a_message (mlp, NULL, token.string, inner_context,
1821*946379e7Schristos 				&token.pos, token.comment);
1822*946379e7Schristos 	  else
1823*946379e7Schristos 	    arglist_parser_remember (argparser, arg, token.string,
1824*946379e7Schristos 				     inner_context,
1825*946379e7Schristos 				     token.pos.file_name, token.pos.line_number,
1826*946379e7Schristos 				     token.comment);
1827*946379e7Schristos 	  drop_reference (token.comment);
1828*946379e7Schristos 	  next_context_iter = null_context_list_iterator;
1829*946379e7Schristos 	  selectorcall_context_iter = null_context_list_iterator;
1830*946379e7Schristos 	  state = 0;
1831*946379e7Schristos 	  continue;
1832*946379e7Schristos 
1833*946379e7Schristos 	case xgettext_token_type_other:
1834*946379e7Schristos 	  next_context_iter = null_context_list_iterator;
1835*946379e7Schristos 	  selectorcall_context_iter = null_context_list_iterator;
1836*946379e7Schristos 	  state = 0;
1837*946379e7Schristos 	  continue;
1838*946379e7Schristos 
1839*946379e7Schristos 	case xgettext_token_type_eof:
1840*946379e7Schristos 	  arglist_parser_done (argparser, arg);
1841*946379e7Schristos 	  return true;
1842*946379e7Schristos 
1843*946379e7Schristos 	default:
1844*946379e7Schristos 	  abort ();
1845*946379e7Schristos 	}
1846*946379e7Schristos     }
1847*946379e7Schristos }
1848*946379e7Schristos 
1849*946379e7Schristos 
1850*946379e7Schristos static void
extract_whole_file(FILE * f,const char * real_filename,const char * logical_filename,flag_context_list_table_ty * flag_table,msgdomain_list_ty * mdlp)1851*946379e7Schristos extract_whole_file (FILE *f,
1852*946379e7Schristos 		    const char *real_filename, const char *logical_filename,
1853*946379e7Schristos 		    flag_context_list_table_ty *flag_table,
1854*946379e7Schristos 		    msgdomain_list_ty *mdlp)
1855*946379e7Schristos {
1856*946379e7Schristos   message_list_ty *mlp = mdlp->item[0]->messages;
1857*946379e7Schristos 
1858*946379e7Schristos   fp = f;
1859*946379e7Schristos   real_file_name = real_filename;
1860*946379e7Schristos   logical_file_name = xstrdup (logical_filename);
1861*946379e7Schristos   line_number = 1;
1862*946379e7Schristos 
1863*946379e7Schristos   newline_count = 0;
1864*946379e7Schristos   last_comment_line = -1;
1865*946379e7Schristos   last_non_comment_line = -1;
1866*946379e7Schristos 
1867*946379e7Schristos   flag_context_list_table = flag_table;
1868*946379e7Schristos 
1869*946379e7Schristos   init_keywords ();
1870*946379e7Schristos 
1871*946379e7Schristos   /* Eat tokens until eof is seen.  When extract_parenthesized returns
1872*946379e7Schristos      due to an unbalanced closing parenthesis, just restart it.  */
1873*946379e7Schristos   while (!extract_parenthesized (mlp, null_context, null_context_list_iterator,
1874*946379e7Schristos 				 arglist_parser_alloc (mlp, NULL)))
1875*946379e7Schristos     ;
1876*946379e7Schristos 
1877*946379e7Schristos   /* Close scanner.  */
1878*946379e7Schristos   fp = NULL;
1879*946379e7Schristos   real_file_name = NULL;
1880*946379e7Schristos   logical_file_name = NULL;
1881*946379e7Schristos   line_number = 0;
1882*946379e7Schristos }
1883*946379e7Schristos 
1884*946379e7Schristos 
1885*946379e7Schristos void
extract_c(FILE * f,const char * real_filename,const char * logical_filename,flag_context_list_table_ty * flag_table,msgdomain_list_ty * mdlp)1886*946379e7Schristos extract_c (FILE *f,
1887*946379e7Schristos 	   const char *real_filename, const char *logical_filename,
1888*946379e7Schristos 	   flag_context_list_table_ty *flag_table,
1889*946379e7Schristos 	   msgdomain_list_ty *mdlp)
1890*946379e7Schristos {
1891*946379e7Schristos   objc_extensions = false;
1892*946379e7Schristos   extract_whole_file (f, real_filename, logical_filename, flag_table, mdlp);
1893*946379e7Schristos }
1894*946379e7Schristos 
1895*946379e7Schristos void
extract_objc(FILE * f,const char * real_filename,const char * logical_filename,flag_context_list_table_ty * flag_table,msgdomain_list_ty * mdlp)1896*946379e7Schristos extract_objc (FILE *f,
1897*946379e7Schristos 	      const char *real_filename, const char *logical_filename,
1898*946379e7Schristos 	      flag_context_list_table_ty *flag_table,
1899*946379e7Schristos 	      msgdomain_list_ty *mdlp)
1900*946379e7Schristos {
1901*946379e7Schristos   objc_extensions = true;
1902*946379e7Schristos   extract_whole_file (f, real_filename, logical_filename, flag_table, mdlp);
1903*946379e7Schristos }
1904