/src/gettext/gettext-tools/src/read-catalog.c
Line | Count | Source |
1 | | /* Reading textual message catalogs (such as PO files). |
2 | | Copyright (C) 1995-2026 Free Software Foundation, Inc. |
3 | | |
4 | | This program is free software: you can redistribute it and/or modify |
5 | | it under the terms of the GNU General Public License as published by |
6 | | the Free Software Foundation; either version 3 of the License, or |
7 | | (at your option) any later version. |
8 | | |
9 | | This program is distributed in the hope that it will be useful, |
10 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | | GNU General Public License for more details. |
13 | | |
14 | | You should have received a copy of the GNU General Public License |
15 | | along with this program. If not, see <https://www.gnu.org/licenses/>. */ |
16 | | |
17 | | /* Written by Peter Miller, Ulrich Drepper, and Bruno Haible. */ |
18 | | |
19 | | #include <config.h> |
20 | | |
21 | | /* Specification. */ |
22 | | #include "read-catalog.h" |
23 | | |
24 | | #include <stdbool.h> |
25 | | #include <stdlib.h> |
26 | | #include <string.h> |
27 | | |
28 | | #include "po-charset.h" |
29 | | #include "read-po-lex.h" |
30 | | #include "xerror-handler.h" |
31 | | #include "xalloc.h" |
32 | | #include "read-catalog-special.h" |
33 | | #include "gettext.h" |
34 | | |
35 | 131k | #define _(str) gettext (str) |
36 | | |
37 | | |
38 | | /* ========================================================================= */ |
39 | | /* Inline functions to invoke the methods. */ |
40 | | |
41 | | static inline void |
42 | | call_set_domain (struct default_catalog_reader_ty *dcatr, |
43 | | char *name, lex_pos_ty *name_pos) |
44 | 5.22k | { |
45 | 5.22k | default_catalog_reader_class_ty *methods = |
46 | 5.22k | (default_catalog_reader_class_ty *) dcatr->methods; |
47 | | |
48 | 5.22k | if (methods->set_domain) |
49 | 5.22k | methods->set_domain (dcatr, name, name_pos); |
50 | 5.22k | } |
51 | | |
52 | | static inline void |
53 | | call_add_message (struct default_catalog_reader_ty *dcatr, |
54 | | char *msgctxt, |
55 | | char *msgid, lex_pos_ty *msgid_pos, char *msgid_plural, |
56 | | char *msgstr, size_t msgstr_len, lex_pos_ty *msgstr_pos, |
57 | | char *prev_msgctxt, char *prev_msgid, char *prev_msgid_plural, |
58 | | bool force_fuzzy, bool obsolete) |
59 | 97.2k | { |
60 | 97.2k | default_catalog_reader_class_ty *methods = |
61 | 97.2k | (default_catalog_reader_class_ty *) dcatr->methods; |
62 | | |
63 | 97.2k | if (methods->add_message) |
64 | 97.2k | methods->add_message (dcatr, msgctxt, |
65 | 97.2k | msgid, msgid_pos, msgid_plural, |
66 | 97.2k | msgstr, msgstr_len, msgstr_pos, |
67 | 97.2k | prev_msgctxt, prev_msgid, prev_msgid_plural, |
68 | 97.2k | force_fuzzy, obsolete); |
69 | 97.2k | } |
70 | | |
71 | | static inline void |
72 | | call_frob_new_message (struct default_catalog_reader_ty *dcatr, message_ty *mp, |
73 | | const lex_pos_ty *msgid_pos, |
74 | | const lex_pos_ty *msgstr_pos) |
75 | 31.5k | { |
76 | 31.5k | default_catalog_reader_class_ty *methods = |
77 | 31.5k | (default_catalog_reader_class_ty *) dcatr->methods; |
78 | | |
79 | 31.5k | if (methods->frob_new_message) |
80 | 0 | methods->frob_new_message (dcatr, mp, msgid_pos, msgstr_pos); |
81 | 31.5k | } |
82 | | |
83 | | |
84 | | /* ========================================================================= */ |
85 | | /* Implementation of default_catalog_reader_ty's methods. */ |
86 | | |
87 | | |
88 | | /* Implementation of methods declared in the superclass. */ |
89 | | |
90 | | |
91 | | /* Prepare for first message. */ |
92 | | void |
93 | | default_constructor (abstract_catalog_reader_ty *catr) |
94 | 12.0k | { |
95 | 12.0k | default_catalog_reader_ty *dcatr = (default_catalog_reader_ty *) catr; |
96 | | |
97 | 12.0k | dcatr->domain = MESSAGE_DOMAIN_DEFAULT; |
98 | 12.0k | dcatr->comment = NULL; |
99 | 12.0k | dcatr->comment_dot = NULL; |
100 | 12.0k | dcatr->filepos_count = 0; |
101 | 12.0k | dcatr->filepos = NULL; |
102 | 12.0k | dcatr->is_fuzzy = false; |
103 | 456k | for (size_t i = 0; i < NFORMATS; i++) |
104 | 444k | dcatr->is_format[i] = undecided; |
105 | 12.0k | dcatr->range.min = -1; |
106 | 12.0k | dcatr->range.max = -1; |
107 | 12.0k | dcatr->do_wrap = undecided; |
108 | 12.0k | } |
109 | | |
110 | | |
111 | | void |
112 | | default_destructor (abstract_catalog_reader_ty *catr) |
113 | 12.0k | { |
114 | 12.0k | default_catalog_reader_ty *dcatr = (default_catalog_reader_ty *) catr; |
115 | | |
116 | | /* Do not free dcatr->mdlp and dcatr->mlp. */ |
117 | 12.0k | if (dcatr->handle_comments) |
118 | 12.0k | { |
119 | 12.0k | if (dcatr->comment != NULL) |
120 | 3.34k | string_list_free (dcatr->comment); |
121 | 12.0k | if (dcatr->comment_dot != NULL) |
122 | 288 | string_list_free (dcatr->comment_dot); |
123 | 12.0k | } |
124 | | |
125 | 49.8k | for (size_t j = 0; j < dcatr->filepos_count; ++j) |
126 | 37.8k | free ((char *) dcatr->filepos[j].file_name); |
127 | 12.0k | if (dcatr->filepos != NULL) |
128 | 12.0k | free (dcatr->filepos); |
129 | 12.0k | } |
130 | | |
131 | | |
132 | | void |
133 | | default_parse_brief (abstract_catalog_reader_ty *catr) |
134 | 12.0k | { |
135 | | /* We need to parse comments, because even if dcatr->handle_comments |
136 | | is false, we need to know which messages are fuzzy. */ |
137 | 12.0k | catr->pass_comments = true; |
138 | 12.0k | } |
139 | | |
140 | | |
141 | | void |
142 | | default_parse_debrief (abstract_catalog_reader_ty *catr) |
143 | 12.0k | { |
144 | 12.0k | } |
145 | | |
146 | | |
147 | | /* Add the accumulated comments to the message. */ |
148 | | static void |
149 | | default_copy_comment_state (default_catalog_reader_ty *dcatr, message_ty *mp) |
150 | 97.2k | { |
151 | 97.2k | if (dcatr->handle_comments) |
152 | 97.2k | { |
153 | 97.2k | if (dcatr->comment != NULL) |
154 | 360k | for (size_t j = 0; j < dcatr->comment->nitems; ++j) |
155 | 338k | message_comment_append (mp, dcatr->comment->item[j]); |
156 | 97.2k | if (dcatr->comment_dot != NULL) |
157 | 15.4k | for (size_t j = 0; j < dcatr->comment_dot->nitems; ++j) |
158 | 10.4k | message_comment_dot_append (mp, dcatr->comment_dot->item[j]); |
159 | 97.2k | } |
160 | 196k | for (size_t j = 0; j < dcatr->filepos_count; ++j) |
161 | 99.2k | { |
162 | 99.2k | lex_pos_ty *pp = &dcatr->filepos[j]; |
163 | | |
164 | 99.2k | message_comment_filepos (mp, pp->file_name, pp->line_number); |
165 | 99.2k | } |
166 | 97.2k | mp->is_fuzzy = dcatr->is_fuzzy; |
167 | 3.69M | for (size_t i = 0; i < NFORMATS; i++) |
168 | 3.59M | mp->is_format[i] = dcatr->is_format[i]; |
169 | 97.2k | mp->range = dcatr->range; |
170 | 97.2k | mp->do_wrap = dcatr->do_wrap; |
171 | 97.2k | } |
172 | | |
173 | | |
174 | | static void |
175 | | default_reset_comment_state (default_catalog_reader_ty *dcatr) |
176 | 102k | { |
177 | 102k | if (dcatr->handle_comments) |
178 | 102k | { |
179 | 102k | if (dcatr->comment != NULL) |
180 | 22.7k | { |
181 | 22.7k | string_list_free (dcatr->comment); |
182 | 22.7k | dcatr->comment = NULL; |
183 | 22.7k | } |
184 | 102k | if (dcatr->comment_dot != NULL) |
185 | 5.29k | { |
186 | 5.29k | string_list_free (dcatr->comment_dot); |
187 | 5.29k | dcatr->comment_dot = NULL; |
188 | 5.29k | } |
189 | 102k | } |
190 | 203k | for (size_t j = 0; j < dcatr->filepos_count; ++j) |
191 | 102k | free ((char *) dcatr->filepos[j].file_name); |
192 | 102k | if (dcatr->filepos != NULL) |
193 | 102k | free (dcatr->filepos); |
194 | 102k | dcatr->filepos_count = 0; |
195 | 102k | dcatr->filepos = NULL; |
196 | 102k | dcatr->is_fuzzy = false; |
197 | 3.89M | for (size_t i = 0; i < NFORMATS; i++) |
198 | 3.78M | dcatr->is_format[i] = undecided; |
199 | 102k | dcatr->range.min = -1; |
200 | 102k | dcatr->range.max = -1; |
201 | 102k | dcatr->do_wrap = undecided; |
202 | 102k | } |
203 | | |
204 | | |
205 | | /* Process 'domain' directive from .po file. */ |
206 | | void |
207 | | default_directive_domain (abstract_catalog_reader_ty *catr, |
208 | | char *name, lex_pos_ty *name_pos) |
209 | 5.22k | { |
210 | 5.22k | default_catalog_reader_ty *dcatr = (default_catalog_reader_ty *) catr; |
211 | | |
212 | 5.22k | call_set_domain (dcatr, name, name_pos); |
213 | | |
214 | | /* If there are accumulated comments, throw them away, they are |
215 | | probably part of the file header, or about the domain directive, |
216 | | and will be unrelated to the next message. */ |
217 | 5.22k | default_reset_comment_state (dcatr); |
218 | 5.22k | } |
219 | | |
220 | | |
221 | | /* Process ['msgctxt'/]'msgid'/'msgstr' pair from .po file. */ |
222 | | void |
223 | | default_directive_message (abstract_catalog_reader_ty *catr, |
224 | | char *msgctxt, |
225 | | char *msgid, |
226 | | lex_pos_ty *msgid_pos, |
227 | | char *msgid_plural, |
228 | | char *msgstr, size_t msgstr_len, |
229 | | lex_pos_ty *msgstr_pos, |
230 | | char *prev_msgctxt, |
231 | | char *prev_msgid, char *prev_msgid_plural, |
232 | | bool force_fuzzy, bool obsolete) |
233 | 97.2k | { |
234 | 97.2k | default_catalog_reader_ty *dcatr = (default_catalog_reader_ty *) catr; |
235 | | |
236 | 97.2k | call_add_message (dcatr, msgctxt, msgid, msgid_pos, msgid_plural, |
237 | 97.2k | msgstr, msgstr_len, msgstr_pos, |
238 | 97.2k | prev_msgctxt, prev_msgid, prev_msgid_plural, |
239 | 97.2k | force_fuzzy, obsolete); |
240 | | |
241 | | /* Prepare for next message. */ |
242 | 97.2k | default_reset_comment_state (dcatr); |
243 | 97.2k | } |
244 | | |
245 | | |
246 | | void |
247 | | default_comment (abstract_catalog_reader_ty *catr, const char *s) |
248 | 1.16M | { |
249 | 1.16M | default_catalog_reader_ty *dcatr = (default_catalog_reader_ty *) catr; |
250 | | |
251 | 1.16M | if (dcatr->handle_comments) |
252 | 1.16M | { |
253 | 1.16M | if (dcatr->comment == NULL) |
254 | 26.0k | dcatr->comment = string_list_alloc (); |
255 | 1.16M | string_list_append (dcatr->comment, s); |
256 | 1.16M | } |
257 | 1.16M | } |
258 | | |
259 | | |
260 | | void |
261 | | default_comment_dot (abstract_catalog_reader_ty *catr, const char *s) |
262 | 12.2k | { |
263 | 12.2k | default_catalog_reader_ty *dcatr = (default_catalog_reader_ty *) catr; |
264 | | |
265 | 12.2k | if (dcatr->handle_comments) |
266 | 12.2k | { |
267 | 12.2k | if (dcatr->comment_dot == NULL) |
268 | 5.58k | dcatr->comment_dot = string_list_alloc (); |
269 | 12.2k | string_list_append (dcatr->comment_dot, s); |
270 | 12.2k | } |
271 | 12.2k | } |
272 | | |
273 | | |
274 | | void |
275 | | default_comment_filepos (abstract_catalog_reader_ty *catr, |
276 | | const char *file_name, size_t line_number) |
277 | 138k | { |
278 | 138k | default_catalog_reader_ty *dcatr = (default_catalog_reader_ty *) catr; |
279 | | |
280 | 138k | size_t nbytes = (dcatr->filepos_count + 1) * sizeof (dcatr->filepos[0]); |
281 | 138k | dcatr->filepos = xrealloc (dcatr->filepos, nbytes); |
282 | | |
283 | 138k | lex_pos_ty *pp = &dcatr->filepos[dcatr->filepos_count++]; |
284 | 138k | pp->file_name = xstrdup (file_name); |
285 | 138k | pp->line_number = line_number; |
286 | 138k | } |
287 | | |
288 | | |
289 | | /* Test for '#, fuzzy' or '#= fuzzy' comments and warn. */ |
290 | | void |
291 | | default_comment_special (abstract_catalog_reader_ty *catr, const char *s) |
292 | 52.9k | { |
293 | 52.9k | default_catalog_reader_ty *dcatr = (default_catalog_reader_ty *) catr; |
294 | | |
295 | 52.9k | bool tmp_fuzzy; |
296 | 52.9k | enum is_format tmp_format[NFORMATS]; |
297 | 52.9k | struct argument_range tmp_range; |
298 | 52.9k | enum is_wrap tmp_wrap; |
299 | 52.9k | parse_comment_special (s, &tmp_fuzzy, tmp_format, &tmp_range, &tmp_wrap, |
300 | 52.9k | NULL); |
301 | | |
302 | 52.9k | if (tmp_fuzzy) |
303 | 10.5k | dcatr->is_fuzzy = true; |
304 | 2.01M | for (size_t i = 0; i < NFORMATS; i++) |
305 | 1.95M | if (tmp_format[i] != undecided) |
306 | 389 | dcatr->is_format[i] = tmp_format[i]; |
307 | 52.9k | if (has_range_p (tmp_range)) |
308 | 4.23k | { |
309 | 4.23k | if (has_range_p (dcatr->range)) |
310 | 2.59k | { |
311 | 2.59k | if (tmp_range.min < dcatr->range.min) |
312 | 434 | dcatr->range.min = tmp_range.min; |
313 | 2.59k | if (tmp_range.max > dcatr->range.max) |
314 | 804 | dcatr->range.max = tmp_range.max; |
315 | 2.59k | } |
316 | 1.63k | else |
317 | 1.63k | dcatr->range = tmp_range; |
318 | 4.23k | } |
319 | 52.9k | if (tmp_wrap != undecided) |
320 | 826 | dcatr->do_wrap = tmp_wrap; |
321 | 52.9k | } |
322 | | |
323 | | |
324 | | /* Default implementation of methods not inherited from the superclass. */ |
325 | | |
326 | | |
327 | | void |
328 | | default_set_domain (default_catalog_reader_ty *dcatr, |
329 | | char *name, lex_pos_ty *name_pos) |
330 | 5.22k | { |
331 | 5.22k | if (dcatr->allow_domain_directives) |
332 | | /* Override current domain name. Don't free memory. */ |
333 | 5.22k | dcatr->domain = name; |
334 | 0 | else |
335 | 0 | { |
336 | 0 | xerror (dcatr->xeh, CAT_SEVERITY_ERROR, NULL, |
337 | 0 | name_pos->file_name, name_pos->line_number, (size_t)(-1), |
338 | 0 | false, |
339 | 0 | _("this file may not contain domain directives")); |
340 | | |
341 | | /* NAME was allocated in read-po-gram.y but is not used anywhere. */ |
342 | 0 | free (name); |
343 | 0 | } |
344 | 5.22k | } |
345 | | |
346 | | void |
347 | | default_add_message (default_catalog_reader_ty *dcatr, |
348 | | char *msgctxt, |
349 | | char *msgid, |
350 | | lex_pos_ty *msgid_pos, |
351 | | char *msgid_plural, |
352 | | char *msgstr, size_t msgstr_len, |
353 | | lex_pos_ty *msgstr_pos, |
354 | | char *prev_msgctxt, |
355 | | char *prev_msgid, |
356 | | char *prev_msgid_plural, |
357 | | bool force_fuzzy, bool obsolete) |
358 | 97.2k | { |
359 | 97.2k | if (dcatr->mdlp != NULL) |
360 | | /* Select the appropriate sublist of dcatr->mdlp. */ |
361 | 97.2k | dcatr->mlp = msgdomain_list_sublist (dcatr->mdlp, dcatr->domain, true); |
362 | | |
363 | 97.2k | message_ty *mp; |
364 | 97.2k | if (dcatr->allow_duplicates && msgid[0] != '\0') |
365 | | /* Doesn't matter if this message ID has been seen before. */ |
366 | 0 | mp = NULL; |
367 | 97.2k | else |
368 | | /* See if this message ID has been seen before. */ |
369 | 97.2k | mp = message_list_search (dcatr->mlp, msgctxt, msgid); |
370 | | |
371 | 97.2k | if (mp) |
372 | 65.6k | { |
373 | 65.6k | if (!(dcatr->allow_duplicates_if_same_msgstr |
374 | 0 | && msgstr_len == mp->msgstr_len |
375 | 0 | && memeq (msgstr, mp->msgstr, msgstr_len))) |
376 | 65.6k | { |
377 | | /* We give a fatal error about this, regardless whether the |
378 | | translations are equal or different. This is for consistency |
379 | | with msgmerge, msgcat and others. The user can use the |
380 | | msguniq program to get rid of duplicates. */ |
381 | 65.6k | xerror2 (dcatr->xeh, CAT_SEVERITY_ERROR, |
382 | 65.6k | NULL, |
383 | 65.6k | msgid_pos->file_name, msgid_pos->line_number, (size_t)(-1), |
384 | 65.6k | false, |
385 | 65.6k | _("duplicate message definition"), |
386 | 65.6k | mp, |
387 | 65.6k | NULL, 0, 0, |
388 | 65.6k | false, |
389 | 65.6k | _("this is the location of the first definition")); |
390 | 65.6k | } |
391 | | /* We don't need the just constructed entries' parameter string |
392 | | (allocated in read-po-gram.y). */ |
393 | 65.6k | free (msgid); |
394 | 65.6k | if (msgid_plural != NULL) |
395 | 65.6k | free (msgid_plural); |
396 | 65.6k | free (msgstr); |
397 | 65.6k | if (msgctxt != NULL) |
398 | 65.6k | free (msgctxt); |
399 | 65.6k | if (prev_msgctxt != NULL) |
400 | 65.6k | free (prev_msgctxt); |
401 | 65.6k | if (prev_msgid != NULL) |
402 | 65.6k | free (prev_msgid); |
403 | 65.6k | if (prev_msgid_plural != NULL) |
404 | 65.6k | free (prev_msgid_plural); |
405 | | |
406 | | /* Add the accumulated comments to the message. */ |
407 | 65.6k | default_copy_comment_state (dcatr, mp); |
408 | 65.6k | } |
409 | 31.5k | else |
410 | 31.5k | { |
411 | | /* Construct message to add to the list. |
412 | | Obsolete message go into the list at least for duplicate checking. |
413 | | It's the caller's responsibility to ignore obsolete messages when |
414 | | appropriate. */ |
415 | 31.5k | mp = message_alloc (msgctxt, msgid, msgid_plural, msgstr, msgstr_len, |
416 | 31.5k | msgstr_pos); |
417 | 31.5k | if (msgid_plural != NULL) |
418 | 31.5k | free (msgid_plural); |
419 | 31.5k | mp->prev_msgctxt = prev_msgctxt; |
420 | 31.5k | mp->prev_msgid = prev_msgid; |
421 | 31.5k | mp->prev_msgid_plural = prev_msgid_plural; |
422 | 31.5k | mp->obsolete = obsolete; |
423 | 31.5k | default_copy_comment_state (dcatr, mp); |
424 | 31.5k | if (force_fuzzy) |
425 | 0 | mp->is_fuzzy = true; |
426 | | |
427 | 31.5k | call_frob_new_message (dcatr, mp, msgid_pos, msgstr_pos); |
428 | | |
429 | 31.5k | message_list_append (dcatr->mlp, mp); |
430 | 31.5k | } |
431 | 97.2k | } |
432 | | |
433 | | |
434 | | /* So that the one parser can be used for multiple programs, and also |
435 | | use good data hiding and encapsulation practices, an object |
436 | | oriented approach has been taken. An object instance is allocated, |
437 | | and all actions resulting from the parse will be through |
438 | | invocations of method functions of that object. */ |
439 | | |
440 | | static default_catalog_reader_class_ty default_methods = |
441 | | { |
442 | | { |
443 | | sizeof (default_catalog_reader_ty), |
444 | | default_constructor, |
445 | | default_destructor, |
446 | | default_parse_brief, |
447 | | default_parse_debrief, |
448 | | default_directive_domain, |
449 | | default_directive_message, |
450 | | default_comment, |
451 | | default_comment_dot, |
452 | | default_comment_filepos, |
453 | | default_comment_special |
454 | | }, |
455 | | default_set_domain, /* set_domain */ |
456 | | default_add_message, /* add_message */ |
457 | | NULL /* frob_new_message */ |
458 | | }; |
459 | | |
460 | | |
461 | | default_catalog_reader_ty * |
462 | | default_catalog_reader_alloc (default_catalog_reader_class_ty *method_table, |
463 | | xerror_handler_ty xerror_handler) |
464 | 12.0k | { |
465 | 12.0k | return |
466 | 12.0k | (default_catalog_reader_ty *) |
467 | 12.0k | catalog_reader_alloc (&method_table->super, xerror_handler); |
468 | 12.0k | } |
469 | | |
470 | | |
471 | | /* ========================================================================= */ |
472 | | /* Exported functions. */ |
473 | | |
474 | | |
475 | | /* If false, duplicate msgids in the same domain and file generate an error. |
476 | | If true, such msgids are allowed; the caller should treat them |
477 | | appropriately. Defaults to false. */ |
478 | | bool allow_duplicates = false; |
479 | | |
480 | | |
481 | | msgdomain_list_ty * |
482 | | read_catalog_stream (FILE *fp, const char *real_filename, |
483 | | const char *logical_filename, |
484 | | catalog_input_format_ty input_syntax, |
485 | | xerror_handler_ty xerror_handler, |
486 | | string_list_ty *arena) |
487 | 12.0k | { |
488 | 12.0k | default_catalog_reader_ty *dcatr = |
489 | 12.0k | default_catalog_reader_alloc (&default_methods, xerror_handler); |
490 | 12.0k | dcatr->pass_obsolete_entries = true; |
491 | 12.0k | dcatr->handle_comments = true; |
492 | 12.0k | dcatr->allow_domain_directives = true; |
493 | 12.0k | dcatr->allow_duplicates = allow_duplicates; |
494 | 12.0k | dcatr->allow_duplicates_if_same_msgstr = false; |
495 | 12.0k | dcatr->file_name = real_filename; |
496 | 12.0k | dcatr->mdlp = msgdomain_list_alloc (!dcatr->allow_duplicates); |
497 | 12.0k | dcatr->mlp = msgdomain_list_sublist (dcatr->mdlp, dcatr->domain, true); |
498 | 12.0k | if (input_syntax->produces_utf8) |
499 | | /* We know a priori that input_syntax->parse convert strings to UTF-8. */ |
500 | 0 | dcatr->mdlp->encoding = po_charset_utf8; |
501 | | |
502 | 12.0k | catalog_reader_parse ((abstract_catalog_reader_ty *) dcatr, fp, real_filename, |
503 | 12.0k | logical_filename, false, input_syntax, arena); |
504 | | |
505 | 12.0k | msgdomain_list_ty *mdlp = dcatr->mdlp; |
506 | 12.0k | catalog_reader_free ((abstract_catalog_reader_ty *) dcatr); |
507 | 12.0k | return mdlp; |
508 | 12.0k | } |