1 /* xgettext PO, JavaProperties, and NXStringTable backends.
2 Copyright (C) 1995-1998, 2000-2003, 2005-2006, 2008-2009, 2014, 2018, 2020 Free Software Foundation, Inc.
3
4 This file was written by Peter Miller <millerp@canb.auug.org.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 <https://www.gnu.org/licenses/>. */
18
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 /* Specification. */
24 #include "x-po.h"
25 #include "x-properties.h"
26 #include "x-stringtable.h"
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <stdbool.h>
31 #include <string.h>
32
33 #include "message.h"
34 #include "xgettext.h"
35 #include "xalloc.h"
36 #include "read-catalog.h"
37 #include "read-po.h"
38 #include "read-properties.h"
39 #include "read-stringtable.h"
40 #include "po-lex.h"
41 #include "gettext.h"
42
43 /* A convenience macro. I don't like writing gettext() every time. */
44 #define _(str) gettext (str)
45
46
47 /* The charset found in the header entry. */
48 static char *header_charset;
49
50 /* Define a subclass extract_catalog_reader_ty of default_catalog_reader_ty. */
51
52 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)53 extract_add_message (default_catalog_reader_ty *this,
54 char *msgctxt,
55 char *msgid,
56 lex_pos_ty *msgid_pos,
57 char *msgid_plural,
58 char *msgstr, size_t msgstr_len,
59 lex_pos_ty *msgstr_pos,
60 char *prev_msgctxt,
61 char *prev_msgid,
62 char *prev_msgid_plural,
63 bool force_fuzzy, bool obsolete)
64 {
65 /* See whether we shall exclude this message. */
66 if (exclude != NULL && message_list_search (exclude, msgctxt, msgid) != NULL)
67 goto discard;
68
69 /* If the msgid is the empty string, it is the old header. Throw it
70 away, we have constructed a new one. Only remember its charset.
71 But if no new one was constructed, keep the old header. This is useful
72 because the old header may contain a charset= directive. */
73 if (msgctxt == NULL && *msgid == '\0' && !xgettext_omit_header)
74 {
75 {
76 const char *charsetstr = strstr (msgstr, "charset=");
77
78 if (charsetstr != NULL)
79 {
80 size_t len;
81 char *charset;
82
83 charsetstr += strlen ("charset=");
84 len = strcspn (charsetstr, " \t\n");
85 charset = XNMALLOC (len + 1, char);
86 memcpy (charset, charsetstr, len);
87 charset[len] = '\0';
88
89 if (header_charset != NULL)
90 free (header_charset);
91 header_charset = charset;
92 }
93 }
94
95 discard:
96 if (msgctxt != NULL)
97 free (msgctxt);
98 free (msgid);
99 if (msgid_plural != NULL)
100 free (msgid_plural);
101 free (msgstr);
102 if (prev_msgctxt != NULL)
103 free (prev_msgctxt);
104 if (prev_msgid != NULL)
105 free (prev_msgid);
106 if (prev_msgid_plural != NULL)
107 free (prev_msgid_plural);
108 return;
109 }
110
111 /* Invoke superclass method. */
112 default_add_message (this, msgctxt, msgid, msgid_pos, msgid_plural,
113 msgstr, msgstr_len, msgstr_pos,
114 prev_msgctxt, prev_msgid, prev_msgid_plural,
115 force_fuzzy, obsolete);
116 }
117
118
119 /* So that the one parser can be used for multiple programs, and also
120 use good data hiding and encapsulation practices, an object
121 oriented approach has been taken. An object instance is allocated,
122 and all actions resulting from the parse will be through
123 invocations of method functions of that object. */
124
125 static default_catalog_reader_class_ty extract_methods =
126 {
127 {
128 sizeof (default_catalog_reader_ty),
129 default_constructor,
130 default_destructor,
131 default_parse_brief,
132 default_parse_debrief,
133 default_directive_domain,
134 default_directive_message,
135 default_comment,
136 default_comment_dot,
137 default_comment_filepos,
138 default_comment_special
139 },
140 default_set_domain, /* set_domain */
141 extract_add_message, /* add_message */
142 NULL /* frob_new_message */
143 };
144
145
146 static void
extract(FILE * fp,const char * real_filename,const char * logical_filename,catalog_input_format_ty input_syntax,msgdomain_list_ty * mdlp)147 extract (FILE *fp,
148 const char *real_filename, const char *logical_filename,
149 catalog_input_format_ty input_syntax,
150 msgdomain_list_ty *mdlp)
151 {
152 default_catalog_reader_ty *pop;
153
154 header_charset = NULL;
155
156 pop = default_catalog_reader_alloc (&extract_methods);
157 pop->handle_comments = true;
158 pop->allow_domain_directives = false;
159 pop->allow_duplicates = false;
160 pop->allow_duplicates_if_same_msgstr = true;
161 pop->file_name = real_filename;
162 pop->mdlp = NULL;
163 pop->mlp = mdlp->item[0]->messages;
164 catalog_reader_parse ((abstract_catalog_reader_ty *) pop, fp, real_filename,
165 logical_filename, input_syntax);
166 catalog_reader_free ((abstract_catalog_reader_ty *) pop);
167
168 if (header_charset != NULL)
169 {
170 if (!xgettext_omit_header)
171 {
172 /* Put the old charset into the freshly constructed header entry. */
173 message_ty *mp =
174 message_list_search (mdlp->item[0]->messages, NULL, "");
175
176 if (mp != NULL && !mp->obsolete)
177 {
178 const char *header = mp->msgstr;
179
180 if (header != NULL)
181 {
182 const char *charsetstr = strstr (header, "charset=");
183
184 if (charsetstr != NULL)
185 {
186 size_t len, len1, len2, len3;
187 char *new_header;
188
189 charsetstr += strlen ("charset=");
190 len = strcspn (charsetstr, " \t\n");
191
192 len1 = charsetstr - header;
193 len2 = strlen (header_charset);
194 len3 = (header + strlen (header)) - (charsetstr + len);
195 new_header = XNMALLOC (len1 + len2 + len3 + 1, char);
196 memcpy (new_header, header, len1);
197 memcpy (new_header + len1, header_charset, len2);
198 memcpy (new_header + len1 + len2, charsetstr + len, len3 + 1);
199 mp->msgstr = new_header;
200 mp->msgstr_len = len1 + len2 + len3 + 1;
201 }
202 }
203 }
204 }
205
206 free (header_charset);
207 }
208 }
209
210
211 void
extract_po(FILE * fp,const char * real_filename,const char * logical_filename,flag_context_list_table_ty * flag_table,msgdomain_list_ty * mdlp)212 extract_po (FILE *fp,
213 const char *real_filename, const char *logical_filename,
214 flag_context_list_table_ty *flag_table,
215 msgdomain_list_ty *mdlp)
216 {
217 extract (fp, real_filename, logical_filename, &input_format_po, mdlp);
218 }
219
220
221 void
extract_properties(FILE * fp,const char * real_filename,const char * logical_filename,flag_context_list_table_ty * flag_table,msgdomain_list_ty * mdlp)222 extract_properties (FILE *fp,
223 const char *real_filename, const char *logical_filename,
224 flag_context_list_table_ty *flag_table,
225 msgdomain_list_ty *mdlp)
226 {
227 extract (fp, real_filename, logical_filename, &input_format_properties,
228 mdlp);
229 }
230
231
232 void
extract_stringtable(FILE * fp,const char * real_filename,const char * logical_filename,flag_context_list_table_ty * flag_table,msgdomain_list_ty * mdlp)233 extract_stringtable (FILE *fp,
234 const char *real_filename, const char *logical_filename,
235 flag_context_list_table_ty *flag_table,
236 msgdomain_list_ty *mdlp)
237 {
238 extract (fp, real_filename, logical_filename, &input_format_stringtable,
239 mdlp);
240 }
241