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