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 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