/* GNU gettext - internationalization aids Copyright (C) 1995-1996, 1998, 2000-2001, 2003, 2005-2006, 2012-2013, 2016, 2020 Free Software Foundation, Inc. This file was written by Peter Miller This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ %{ #ifdef HAVE_CONFIG_H # include "config.h" #endif /* Specification. */ #include "po-gram.h" #include #include #include #include #include "str-list.h" #include "po-lex.h" #include "po-charset.h" #include "error.h" #include "xalloc.h" #include "gettext.h" #include "read-catalog-abstract.h" #define _(str) gettext (str) static long plural_counter; #define check_obsolete(value1,value2) \ if ((value1).obsolete != (value2).obsolete) \ po_gram_error_at_line (&(value2).pos, _("inconsistent use of #~")); 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) { /* Test for header entry. Ignore fuzziness of the header entry. */ if (msgctxt == NULL && msgid[0] == '\0' && !obsolete) po_lex_charset_set (msgstr, gram_pos.file_name); po_callback_message (msgctxt, msgid, msgid_pos, msgid_plural, msgstr, msgstr_len, msgstr_pos, prev_msgctxt, prev_msgid, prev_msgid_plural, false, obsolete); } #define free_message_intro(value) \ if ((value).prev_ctxt != NULL) \ free ((value).prev_ctxt); \ if ((value).prev_id != NULL) \ free ((value).prev_id); \ if ((value).prev_id_plural != NULL) \ free ((value).prev_id_plural); \ if ((value).ctxt != NULL) \ free ((value).ctxt); %} %require "3.0" /* Remap parser interface names, so we can have multiple Bison generated parsers in the same program. */ %define api.prefix {po_gram_} %token COMMENT %token DOMAIN %token JUNK %token PREV_MSGCTXT %token PREV_MSGID %token PREV_MSGID_PLURAL %token PREV_STRING %token MSGCTXT %token MSGID %token MSGID_PLURAL %token MSGSTR %token NAME %token '[' ']' %token NUMBER %token STRING %union { struct { char *string; lex_pos_ty pos; bool obsolete; } string; struct { string_list_ty stringlist; lex_pos_ty pos; bool obsolete; } stringlist; struct { long number; lex_pos_ty pos; bool obsolete; } number; struct { lex_pos_ty pos; bool obsolete; } pos; struct { char *ctxt; char *id; char *id_plural; lex_pos_ty pos; bool obsolete; } prev; struct { char *prev_ctxt; char *prev_id; char *prev_id_plural; char *ctxt; lex_pos_ty pos; bool obsolete; } message_intro; struct { struct msgstr_def rhs; lex_pos_ty pos; bool obsolete; } rhs; } %type STRING PREV_STRING COMMENT NAME msg_intro prev_msg_intro msgid_pluralform prev_msgid_pluralform %type string_list prev_string_list %type NUMBER %type DOMAIN PREV_MSGCTXT PREV_MSGID PREV_MSGID_PLURAL MSGCTXT MSGID MSGID_PLURAL MSGSTR '[' ']' %type prev %type message_intro %type pluralform pluralform_list %right MSGSTR %% po_file : /* empty */ | po_file comment | po_file domain | po_file message | po_file error ; comment : COMMENT { po_callback_comment_dispatcher ($1.string); } ; domain : DOMAIN STRING { po_callback_domain ($2.string); } ; message : message_intro string_list MSGSTR string_list { char *string2 = string_list_concat_destroy (&$2.stringlist); char *string4 = string_list_concat_destroy (&$4.stringlist); check_obsolete ($1, $2); check_obsolete ($1, $3); check_obsolete ($1, $4); if (!$1.obsolete || pass_obsolete_entries) do_callback_message ($1.ctxt, string2, &$1.pos, NULL, string4, strlen (string4) + 1, &$3.pos, $1.prev_ctxt, $1.prev_id, $1.prev_id_plural, $1.obsolete); else { free_message_intro ($1); free (string2); free (string4); } } | message_intro string_list msgid_pluralform pluralform_list { char *string2 = string_list_concat_destroy (&$2.stringlist); check_obsolete ($1, $2); check_obsolete ($1, $3); check_obsolete ($1, $4); if (!$1.obsolete || pass_obsolete_entries) do_callback_message ($1.ctxt, string2, &$1.pos, $3.string, $4.rhs.msgstr, $4.rhs.msgstr_len, &$4.pos, $1.prev_ctxt, $1.prev_id, $1.prev_id_plural, $1.obsolete); else { free_message_intro ($1); free (string2); free ($3.string); free ($4.rhs.msgstr); } } | message_intro string_list msgid_pluralform { check_obsolete ($1, $2); check_obsolete ($1, $3); po_gram_error_at_line (&$1.pos, _("missing 'msgstr[]' section")); free_message_intro ($1); string_list_destroy (&$2.stringlist); free ($3.string); } | message_intro string_list pluralform_list { check_obsolete ($1, $2); check_obsolete ($1, $3); po_gram_error_at_line (&$1.pos, _("missing 'msgid_plural' section")); free_message_intro ($1); string_list_destroy (&$2.stringlist); free ($3.rhs.msgstr); } | message_intro string_list { check_obsolete ($1, $2); po_gram_error_at_line (&$1.pos, _("missing 'msgstr' section")); free_message_intro ($1); string_list_destroy (&$2.stringlist); } ; message_intro : msg_intro { $$.prev_ctxt = NULL; $$.prev_id = NULL; $$.prev_id_plural = NULL; $$.ctxt = $1.string; $$.pos = $1.pos; $$.obsolete = $1.obsolete; } | prev msg_intro { check_obsolete ($1, $2); $$.prev_ctxt = $1.ctxt; $$.prev_id = $1.id; $$.prev_id_plural = $1.id_plural; $$.ctxt = $2.string; $$.pos = $2.pos; $$.obsolete = $2.obsolete; } ; prev : prev_msg_intro prev_string_list { check_obsolete ($1, $2); $$.ctxt = $1.string; $$.id = string_list_concat_destroy (&$2.stringlist); $$.id_plural = NULL; $$.pos = $1.pos; $$.obsolete = $1.obsolete; } | prev_msg_intro prev_string_list prev_msgid_pluralform { check_obsolete ($1, $2); check_obsolete ($1, $3); $$.ctxt = $1.string; $$.id = string_list_concat_destroy (&$2.stringlist); $$.id_plural = $3.string; $$.pos = $1.pos; $$.obsolete = $1.obsolete; } ; msg_intro : MSGID { $$.string = NULL; $$.pos = $1.pos; $$.obsolete = $1.obsolete; } | MSGCTXT string_list MSGID { check_obsolete ($1, $2); check_obsolete ($1, $3); $$.string = string_list_concat_destroy (&$2.stringlist); $$.pos = $3.pos; $$.obsolete = $3.obsolete; } ; prev_msg_intro : PREV_MSGID { $$.string = NULL; $$.pos = $1.pos; $$.obsolete = $1.obsolete; } | PREV_MSGCTXT prev_string_list PREV_MSGID { check_obsolete ($1, $2); check_obsolete ($1, $3); $$.string = string_list_concat_destroy (&$2.stringlist); $$.pos = $3.pos; $$.obsolete = $3.obsolete; } ; msgid_pluralform : MSGID_PLURAL string_list { check_obsolete ($1, $2); plural_counter = 0; $$.string = string_list_concat_destroy (&$2.stringlist); $$.pos = $1.pos; $$.obsolete = $1.obsolete; } ; prev_msgid_pluralform : PREV_MSGID_PLURAL prev_string_list { check_obsolete ($1, $2); $$.string = string_list_concat_destroy (&$2.stringlist); $$.pos = $1.pos; $$.obsolete = $1.obsolete; } ; pluralform_list : pluralform { $$ = $1; } | pluralform_list pluralform { check_obsolete ($1, $2); $$.rhs.msgstr = XNMALLOC ($1.rhs.msgstr_len + $2.rhs.msgstr_len, char); memcpy ($$.rhs.msgstr, $1.rhs.msgstr, $1.rhs.msgstr_len); memcpy ($$.rhs.msgstr + $1.rhs.msgstr_len, $2.rhs.msgstr, $2.rhs.msgstr_len); $$.rhs.msgstr_len = $1.rhs.msgstr_len + $2.rhs.msgstr_len; free ($1.rhs.msgstr); free ($2.rhs.msgstr); $$.pos = $1.pos; $$.obsolete = $1.obsolete; } ; pluralform : MSGSTR '[' NUMBER ']' string_list { check_obsolete ($1, $2); check_obsolete ($1, $3); check_obsolete ($1, $4); check_obsolete ($1, $5); if ($3.number != plural_counter) { if (plural_counter == 0) po_gram_error_at_line (&$1.pos, _("first plural form has nonzero index")); else po_gram_error_at_line (&$1.pos, _("plural form has wrong index")); } plural_counter++; $$.rhs.msgstr = string_list_concat_destroy (&$5.stringlist); $$.rhs.msgstr_len = strlen ($$.rhs.msgstr) + 1; $$.pos = $1.pos; $$.obsolete = $1.obsolete; } ; string_list : STRING { string_list_init (&$$.stringlist); string_list_append (&$$.stringlist, $1.string); free ($1.string); $$.pos = $1.pos; $$.obsolete = $1.obsolete; } | string_list STRING { check_obsolete ($1, $2); $$.stringlist = $1.stringlist; string_list_append (&$$.stringlist, $2.string); free ($2.string); $$.pos = $1.pos; $$.obsolete = $1.obsolete; } ; prev_string_list : PREV_STRING { string_list_init (&$$.stringlist); string_list_append (&$$.stringlist, $1.string); free ($1.string); $$.pos = $1.pos; $$.obsolete = $1.obsolete; } | prev_string_list PREV_STRING { check_obsolete ($1, $2); $$.stringlist = $1.stringlist; string_list_append (&$$.stringlist, $2.string); free ($2.string); $$.pos = $1.pos; $$.obsolete = $1.obsolete; } ;