| /* xgettext PO, JavaProperties, and NXStringTable backends. |
| Copyright (C) 1995-1998, 2000-2003, 2005-2006, 2008-2009, 2014, 2018, 2020 Free Software Foundation, Inc. |
| |
| This file was written by Peter Miller <millerp@canb.auug.org.au> |
| |
| 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 <https://www.gnu.org/licenses/>. */ |
| |
| #ifdef HAVE_CONFIG_H |
| # include <config.h> |
| #endif |
| |
| /* Specification. */ |
| #include "x-po.h" |
| #include "x-properties.h" |
| #include "x-stringtable.h" |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdbool.h> |
| #include <string.h> |
| |
| #include "message.h" |
| #include "xgettext.h" |
| #include "xalloc.h" |
| #include "read-catalog.h" |
| #include "read-po.h" |
| #include "read-properties.h" |
| #include "read-stringtable.h" |
| #include "po-lex.h" |
| #include "gettext.h" |
| |
| /* A convenience macro. I don't like writing gettext() every time. */ |
| #define _(str) gettext (str) |
| |
| |
| /* The charset found in the header entry. */ |
| static char *header_charset; |
| |
| /* Define a subclass extract_catalog_reader_ty of default_catalog_reader_ty. */ |
| |
| static void |
| extract_add_message (default_catalog_reader_ty *this, |
| 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 force_fuzzy, bool obsolete) |
| { |
| /* See whether we shall exclude this message. */ |
| if (exclude != NULL && message_list_search (exclude, msgctxt, msgid) != NULL) |
| goto discard; |
| |
| /* If the msgid is the empty string, it is the old header. Throw it |
| away, we have constructed a new one. Only remember its charset. |
| But if no new one was constructed, keep the old header. This is useful |
| because the old header may contain a charset= directive. */ |
| if (msgctxt == NULL && *msgid == '\0' && !xgettext_omit_header) |
| { |
| { |
| const char *charsetstr = strstr (msgstr, "charset="); |
| |
| if (charsetstr != NULL) |
| { |
| size_t len; |
| char *charset; |
| |
| charsetstr += strlen ("charset="); |
| len = strcspn (charsetstr, " \t\n"); |
| charset = XNMALLOC (len + 1, char); |
| memcpy (charset, charsetstr, len); |
| charset[len] = '\0'; |
| |
| if (header_charset != NULL) |
| free (header_charset); |
| header_charset = charset; |
| } |
| } |
| |
| discard: |
| if (msgctxt != NULL) |
| free (msgctxt); |
| free (msgid); |
| if (msgid_plural != NULL) |
| free (msgid_plural); |
| free (msgstr); |
| if (prev_msgctxt != NULL) |
| free (prev_msgctxt); |
| if (prev_msgid != NULL) |
| free (prev_msgid); |
| if (prev_msgid_plural != NULL) |
| free (prev_msgid_plural); |
| return; |
| } |
| |
| /* Invoke superclass method. */ |
| default_add_message (this, msgctxt, msgid, msgid_pos, msgid_plural, |
| msgstr, msgstr_len, msgstr_pos, |
| prev_msgctxt, prev_msgid, prev_msgid_plural, |
| force_fuzzy, obsolete); |
| } |
| |
| |
| /* So that the one parser can be used for multiple programs, and also |
| use good data hiding and encapsulation practices, an object |
| oriented approach has been taken. An object instance is allocated, |
| and all actions resulting from the parse will be through |
| invocations of method functions of that object. */ |
| |
| static default_catalog_reader_class_ty extract_methods = |
| { |
| { |
| sizeof (default_catalog_reader_ty), |
| default_constructor, |
| default_destructor, |
| default_parse_brief, |
| default_parse_debrief, |
| default_directive_domain, |
| default_directive_message, |
| default_comment, |
| default_comment_dot, |
| default_comment_filepos, |
| default_comment_special |
| }, |
| default_set_domain, /* set_domain */ |
| extract_add_message, /* add_message */ |
| NULL /* frob_new_message */ |
| }; |
| |
| |
| static void |
| extract (FILE *fp, |
| const char *real_filename, const char *logical_filename, |
| catalog_input_format_ty input_syntax, |
| msgdomain_list_ty *mdlp) |
| { |
| default_catalog_reader_ty *pop; |
| |
| header_charset = NULL; |
| |
| pop = default_catalog_reader_alloc (&extract_methods); |
| pop->handle_comments = true; |
| pop->allow_domain_directives = false; |
| pop->allow_duplicates = false; |
| pop->allow_duplicates_if_same_msgstr = true; |
| pop->file_name = real_filename; |
| pop->mdlp = NULL; |
| pop->mlp = mdlp->item[0]->messages; |
| catalog_reader_parse ((abstract_catalog_reader_ty *) pop, fp, real_filename, |
| logical_filename, input_syntax); |
| catalog_reader_free ((abstract_catalog_reader_ty *) pop); |
| |
| if (header_charset != NULL) |
| { |
| if (!xgettext_omit_header) |
| { |
| /* Put the old charset into the freshly constructed header entry. */ |
| message_ty *mp = |
| message_list_search (mdlp->item[0]->messages, NULL, ""); |
| |
| if (mp != NULL && !mp->obsolete) |
| { |
| const char *header = mp->msgstr; |
| |
| if (header != NULL) |
| { |
| const char *charsetstr = strstr (header, "charset="); |
| |
| if (charsetstr != NULL) |
| { |
| size_t len, len1, len2, len3; |
| char *new_header; |
| |
| charsetstr += strlen ("charset="); |
| len = strcspn (charsetstr, " \t\n"); |
| |
| len1 = charsetstr - header; |
| len2 = strlen (header_charset); |
| len3 = (header + strlen (header)) - (charsetstr + len); |
| new_header = XNMALLOC (len1 + len2 + len3 + 1, char); |
| memcpy (new_header, header, len1); |
| memcpy (new_header + len1, header_charset, len2); |
| memcpy (new_header + len1 + len2, charsetstr + len, len3 + 1); |
| mp->msgstr = new_header; |
| mp->msgstr_len = len1 + len2 + len3 + 1; |
| } |
| } |
| } |
| } |
| |
| free (header_charset); |
| } |
| } |
| |
| |
| void |
| extract_po (FILE *fp, |
| const char *real_filename, const char *logical_filename, |
| flag_context_list_table_ty *flag_table, |
| msgdomain_list_ty *mdlp) |
| { |
| extract (fp, real_filename, logical_filename, &input_format_po, mdlp); |
| } |
| |
| |
| void |
| extract_properties (FILE *fp, |
| const char *real_filename, const char *logical_filename, |
| flag_context_list_table_ty *flag_table, |
| msgdomain_list_ty *mdlp) |
| { |
| extract (fp, real_filename, logical_filename, &input_format_properties, |
| mdlp); |
| } |
| |
| |
| void |
| extract_stringtable (FILE *fp, |
| const char *real_filename, const char *logical_filename, |
| flag_context_list_table_ty *flag_table, |
| msgdomain_list_ty *mdlp) |
| { |
| extract (fp, real_filename, logical_filename, &input_format_stringtable, |
| mdlp); |
| } |