1 /* GNU gettext - internationalization aids
2 Copyright (C) 1995-1996, 1998, 2000-2001, 2003, 2005-2006 Free Software Foundation, Inc.
3
4 This file was written by Peter Miller <pmiller@agso.gov.au>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software Foundation,
18 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
19
20 %{
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 /* Specification. */
26 #include "po-gram.h"
27
28 #include <stdbool.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include "str-list.h"
34 #include "po-lex.h"
35 #include "po-charset.h"
36 #include "error.h"
37 #include "xalloc.h"
38 #include "gettext.h"
39 #include "read-catalog-abstract.h"
40
41 #define _(str) gettext (str)
42
43 /* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc),
44 as well as gratuitiously global symbol names, so we can have multiple
45 yacc generated parsers in the same program. Note that these are only
46 the variables produced by yacc. If other parser generators (bison,
47 byacc, etc) produce additional global names that conflict at link time,
48 then those parser generators need to be fixed instead of adding those
49 names to this list. */
50
51 #define yymaxdepth po_gram_maxdepth
52 #define yyparse po_gram_parse
53 #define yylex po_gram_lex
54 #define yyerror po_gram_error
55 #define yylval po_gram_lval
56 #define yychar po_gram_char
57 #define yydebug po_gram_debug
58 #define yypact po_gram_pact
59 #define yyr1 po_gram_r1
60 #define yyr2 po_gram_r2
61 #define yydef po_gram_def
62 #define yychk po_gram_chk
63 #define yypgo po_gram_pgo
64 #define yyact po_gram_act
65 #define yyexca po_gram_exca
66 #define yyerrflag po_gram_errflag
67 #define yynerrs po_gram_nerrs
68 #define yyps po_gram_ps
69 #define yypv po_gram_pv
70 #define yys po_gram_s
71 #define yy_yys po_gram_yys
72 #define yystate po_gram_state
73 #define yytmp po_gram_tmp
74 #define yyv po_gram_v
75 #define yy_yyv po_gram_yyv
76 #define yyval po_gram_val
77 #define yylloc po_gram_lloc
78 #define yyreds po_gram_reds /* With YYDEBUG defined */
79 #define yytoks po_gram_toks /* With YYDEBUG defined */
80 #define yylhs po_gram_yylhs
81 #define yylen po_gram_yylen
82 #define yydefred po_gram_yydefred
83 #define yydgoto po_gram_yydgoto
84 #define yysindex po_gram_yysindex
85 #define yyrindex po_gram_yyrindex
86 #define yygindex po_gram_yygindex
87 #define yytable po_gram_yytable
88 #define yycheck po_gram_yycheck
89
90 static long plural_counter;
91
92 #define check_obsolete(value1,value2) \
93 if ((value1).obsolete != (value2).obsolete) \
94 po_gram_error_at_line (&(value2).pos, _("inconsistent use of #~"));
95
96 static inline void
do_callback_message(char * msgctxt,char * msgid,lex_pos_ty * msgid_pos,char * msgid_plural,char * msgstr,size_t msgstr_len,lex_pos_ty * msgstr_pos,char * prev_msgctxt,char * prev_msgid,char * prev_msgid_plural,bool obsolete)97 do_callback_message (char *msgctxt,
98 char *msgid, lex_pos_ty *msgid_pos, char *msgid_plural,
99 char *msgstr, size_t msgstr_len, lex_pos_ty *msgstr_pos,
100 char *prev_msgctxt,
101 char *prev_msgid, char *prev_msgid_plural,
102 bool obsolete)
103 {
104 /* Test for header entry. Ignore fuzziness of the header entry. */
105 if (msgctxt == NULL && msgid[0] == '\0' && !obsolete)
106 po_lex_charset_set (msgstr, gram_pos.file_name);
107
108 po_callback_message (msgctxt,
109 msgid, msgid_pos, msgid_plural,
110 msgstr, msgstr_len, msgstr_pos,
111 prev_msgctxt, prev_msgid, prev_msgid_plural,
112 false, obsolete);
113 }
114
115 #define free_message_intro(value) \
116 if ((value).prev_ctxt != NULL) \
117 free ((value).prev_ctxt); \
118 if ((value).prev_id != NULL) \
119 free ((value).prev_id); \
120 if ((value).prev_id_plural != NULL) \
121 free ((value).prev_id_plural); \
122 if ((value).ctxt != NULL) \
123 free ((value).ctxt);
124
125 %}
126
127 %token COMMENT
128 %token DOMAIN
129 %token JUNK
130 %token PREV_MSGCTXT
131 %token PREV_MSGID
132 %token PREV_MSGID_PLURAL
133 %token PREV_STRING
134 %token MSGCTXT
135 %token MSGID
136 %token MSGID_PLURAL
137 %token MSGSTR
138 %token NAME
139 %token '[' ']'
140 %token NUMBER
141 %token STRING
142
143 %union
144 {
145 struct { char *string; lex_pos_ty pos; bool obsolete; } string;
146 struct { string_list_ty stringlist; lex_pos_ty pos; bool obsolete; } stringlist;
147 struct { long number; lex_pos_ty pos; bool obsolete; } number;
148 struct { lex_pos_ty pos; bool obsolete; } pos;
149 struct { char *ctxt; char *id; char *id_plural; lex_pos_ty pos; bool obsolete; } prev;
150 struct { char *prev_ctxt; char *prev_id; char *prev_id_plural; char *ctxt; lex_pos_ty pos; bool obsolete; } message_intro;
151 struct { struct msgstr_def rhs; lex_pos_ty pos; bool obsolete; } rhs;
152 }
153
154 %type <string> STRING PREV_STRING COMMENT NAME
155 msg_intro prev_msg_intro msgid_pluralform prev_msgid_pluralform
156 %type <stringlist> string_list prev_string_list
157 %type <number> NUMBER
158 %type <pos> DOMAIN
159 PREV_MSGCTXT PREV_MSGID PREV_MSGID_PLURAL
160 MSGCTXT MSGID MSGID_PLURAL MSGSTR '[' ']'
161 %type <prev> prev
162 %type <message_intro> message_intro
163 %type <rhs> pluralform pluralform_list
164
165 %right MSGSTR
166
167 %%
168
169 po_file
170 : /* empty */
171 | po_file comment
172 | po_file domain
173 | po_file message
174 | po_file error
175 ;
176
177
178 comment
179 : COMMENT
180 {
181 po_callback_comment_dispatcher ($1.string);
182 }
183 ;
184
185
186 domain
187 : DOMAIN STRING
188 {
189 po_callback_domain ($2.string);
190 }
191 ;
192
193
194 message
195 : message_intro string_list MSGSTR string_list
196 {
197 char *string2 = string_list_concat_destroy (&$2.stringlist);
198 char *string4 = string_list_concat_destroy (&$4.stringlist);
199
200 check_obsolete ($1, $2);
201 check_obsolete ($1, $3);
202 check_obsolete ($1, $4);
203 if (!$1.obsolete || pass_obsolete_entries)
204 do_callback_message ($1.ctxt, string2, &$1.pos, NULL,
205 string4, strlen (string4) + 1, &$3.pos,
206 $1.prev_ctxt,
207 $1.prev_id, $1.prev_id_plural,
208 $1.obsolete);
209 else
210 {
211 free_message_intro ($1);
212 free (string2);
213 free (string4);
214 }
215 }
216 | message_intro string_list msgid_pluralform pluralform_list
217 {
218 char *string2 = string_list_concat_destroy (&$2.stringlist);
219
220 check_obsolete ($1, $2);
221 check_obsolete ($1, $3);
222 check_obsolete ($1, $4);
223 if (!$1.obsolete || pass_obsolete_entries)
224 do_callback_message ($1.ctxt, string2, &$1.pos, $3.string,
225 $4.rhs.msgstr, $4.rhs.msgstr_len, &$4.pos,
226 $1.prev_ctxt,
227 $1.prev_id, $1.prev_id_plural,
228 $1.obsolete);
229 else
230 {
231 free_message_intro ($1);
232 free (string2);
233 free ($3.string);
234 free ($4.rhs.msgstr);
235 }
236 }
237 | message_intro string_list msgid_pluralform
238 {
239 check_obsolete ($1, $2);
240 check_obsolete ($1, $3);
241 po_gram_error_at_line (&$1.pos, _("missing `msgstr[]' section"));
242 free_message_intro ($1);
243 string_list_destroy (&$2.stringlist);
244 free ($3.string);
245 }
246 | message_intro string_list pluralform_list
247 {
248 check_obsolete ($1, $2);
249 check_obsolete ($1, $3);
250 po_gram_error_at_line (&$1.pos, _("missing `msgid_plural' section"));
251 free_message_intro ($1);
252 string_list_destroy (&$2.stringlist);
253 free ($3.rhs.msgstr);
254 }
255 | message_intro string_list
256 {
257 check_obsolete ($1, $2);
258 po_gram_error_at_line (&$1.pos, _("missing `msgstr' section"));
259 free_message_intro ($1);
260 string_list_destroy (&$2.stringlist);
261 }
262 ;
263
264
265 message_intro
266 : msg_intro
267 {
268 $$.prev_ctxt = NULL;
269 $$.prev_id = NULL;
270 $$.prev_id_plural = NULL;
271 $$.ctxt = $1.string;
272 $$.pos = $1.pos;
273 $$.obsolete = $1.obsolete;
274 }
275 | prev msg_intro
276 {
277 check_obsolete ($1, $2);
278 $$.prev_ctxt = $1.ctxt;
279 $$.prev_id = $1.id;
280 $$.prev_id_plural = $1.id_plural;
281 $$.ctxt = $2.string;
282 $$.pos = $2.pos;
283 $$.obsolete = $2.obsolete;
284 }
285 ;
286
287
288 prev
289 : prev_msg_intro prev_string_list
290 {
291 check_obsolete ($1, $2);
292 $$.ctxt = $1.string;
293 $$.id = string_list_concat_destroy (&$2.stringlist);
294 $$.id_plural = NULL;
295 $$.pos = $1.pos;
296 $$.obsolete = $1.obsolete;
297 }
298 | prev_msg_intro prev_string_list prev_msgid_pluralform
299 {
300 check_obsolete ($1, $2);
301 check_obsolete ($1, $3);
302 $$.ctxt = $1.string;
303 $$.id = string_list_concat_destroy (&$2.stringlist);
304 $$.id_plural = $3.string;
305 $$.pos = $1.pos;
306 $$.obsolete = $1.obsolete;
307 }
308 ;
309
310
311 msg_intro
312 : MSGID
313 {
314 $$.string = NULL;
315 $$.pos = $1.pos;
316 $$.obsolete = $1.obsolete;
317 }
318 | MSGCTXT string_list MSGID
319 {
320 check_obsolete ($1, $2);
321 check_obsolete ($1, $3);
322 $$.string = string_list_concat_destroy (&$2.stringlist);
323 $$.pos = $3.pos;
324 $$.obsolete = $3.obsolete;
325 }
326 ;
327
328 prev_msg_intro
329 : PREV_MSGID
330 {
331 $$.string = NULL;
332 $$.pos = $1.pos;
333 $$.obsolete = $1.obsolete;
334 }
335 | PREV_MSGCTXT prev_string_list PREV_MSGID
336 {
337 check_obsolete ($1, $2);
338 check_obsolete ($1, $3);
339 $$.string = string_list_concat_destroy (&$2.stringlist);
340 $$.pos = $3.pos;
341 $$.obsolete = $3.obsolete;
342 }
343 ;
344
345
346 msgid_pluralform
347 : MSGID_PLURAL string_list
348 {
349 check_obsolete ($1, $2);
350 plural_counter = 0;
351 $$.string = string_list_concat_destroy (&$2.stringlist);
352 $$.pos = $1.pos;
353 $$.obsolete = $1.obsolete;
354 }
355 ;
356
357 prev_msgid_pluralform
358 : PREV_MSGID_PLURAL prev_string_list
359 {
360 check_obsolete ($1, $2);
361 $$.string = string_list_concat_destroy (&$2.stringlist);
362 $$.pos = $1.pos;
363 $$.obsolete = $1.obsolete;
364 }
365 ;
366
367
368 pluralform_list
369 : pluralform
370 {
371 $$ = $1;
372 }
373 | pluralform_list pluralform
374 {
375 check_obsolete ($1, $2);
376 $$.rhs.msgstr = (char *) xmalloc ($1.rhs.msgstr_len + $2.rhs.msgstr_len);
377 memcpy ($$.rhs.msgstr, $1.rhs.msgstr, $1.rhs.msgstr_len);
378 memcpy ($$.rhs.msgstr + $1.rhs.msgstr_len, $2.rhs.msgstr, $2.rhs.msgstr_len);
379 $$.rhs.msgstr_len = $1.rhs.msgstr_len + $2.rhs.msgstr_len;
380 free ($1.rhs.msgstr);
381 free ($2.rhs.msgstr);
382 $$.pos = $1.pos;
383 $$.obsolete = $1.obsolete;
384 }
385 ;
386
387 pluralform
388 : MSGSTR '[' NUMBER ']' string_list
389 {
390 check_obsolete ($1, $2);
391 check_obsolete ($1, $3);
392 check_obsolete ($1, $4);
393 check_obsolete ($1, $5);
394 if ($3.number != plural_counter)
395 {
396 if (plural_counter == 0)
397 po_gram_error_at_line (&$1.pos, _("first plural form has nonzero index"));
398 else
399 po_gram_error_at_line (&$1.pos, _("plural form has wrong index"));
400 }
401 plural_counter++;
402 $$.rhs.msgstr = string_list_concat_destroy (&$5.stringlist);
403 $$.rhs.msgstr_len = strlen ($$.rhs.msgstr) + 1;
404 $$.pos = $1.pos;
405 $$.obsolete = $1.obsolete;
406 }
407 ;
408
409
410 string_list
411 : STRING
412 {
413 string_list_init (&$$.stringlist);
414 string_list_append (&$$.stringlist, $1.string);
415 $$.pos = $1.pos;
416 $$.obsolete = $1.obsolete;
417 }
418 | string_list STRING
419 {
420 check_obsolete ($1, $2);
421 $$.stringlist = $1.stringlist;
422 string_list_append (&$$.stringlist, $2.string);
423 $$.pos = $1.pos;
424 $$.obsolete = $1.obsolete;
425 }
426 ;
427
428 prev_string_list
429 : PREV_STRING
430 {
431 string_list_init (&$$.stringlist);
432 string_list_append (&$$.stringlist, $1.string);
433 $$.pos = $1.pos;
434 $$.obsolete = $1.obsolete;
435 }
436 | prev_string_list PREV_STRING
437 {
438 check_obsolete ($1, $2);
439 $$.stringlist = $1.stringlist;
440 string_list_append (&$$.stringlist, $2.string);
441 $$.pos = $1.pos;
442 $$.obsolete = $1.obsolete;
443 }
444 ;
445