No empty .Rs/.Re
[netbsd-mini2440.git] / gnu / dist / gettext / gettext-tools / src / read-po.c
blobf8735aae6d3a0854aa3f0b8ec9f403fb94d7322a
1 /* Reading PO files.
2 Copyright (C) 1995-1998, 2000-2003 Free Software Foundation, Inc.
3 This file was written by Peter Miller <millerp@canb.auug.org.au>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
23 /* Specification. */
24 #include "read-po.h"
26 #include <stdbool.h>
27 #include <stdlib.h>
28 #include <string.h>
30 #include "open-po.h"
31 #include "po-charset.h"
32 #include "xalloc.h"
33 #include "gettext.h"
35 #define _(str) gettext (str)
38 /* ========================================================================= */
39 /* Inline functions to invoke the methods. */
41 static inline void
42 call_set_domain (struct default_po_reader_ty *this, char *name)
44 default_po_reader_class_ty *methods =
45 (default_po_reader_class_ty *) this->methods;
47 if (methods->set_domain)
48 methods->set_domain (this, name);
51 static inline void
52 call_add_message (struct default_po_reader_ty *this,
53 char *msgid, lex_pos_ty *msgid_pos, char *msgid_plural,
54 char *msgstr, size_t msgstr_len, lex_pos_ty *msgstr_pos,
55 bool force_fuzzy, bool obsolete)
57 default_po_reader_class_ty *methods =
58 (default_po_reader_class_ty *) this->methods;
60 if (methods->add_message)
61 methods->add_message (this, msgid, msgid_pos, msgid_plural,
62 msgstr, msgstr_len, msgstr_pos,
63 force_fuzzy, obsolete);
66 static inline void
67 call_frob_new_message (struct default_po_reader_ty *this, message_ty *mp,
68 const lex_pos_ty *msgid_pos,
69 const lex_pos_ty *msgstr_pos)
71 default_po_reader_class_ty *methods =
72 (default_po_reader_class_ty *) this->methods;
74 if (methods->frob_new_message)
75 methods->frob_new_message (this, mp, msgid_pos, msgstr_pos);
79 /* ========================================================================= */
80 /* Implementation of default_po_reader_ty's methods. */
83 /* Implementation of methods declared in the superclass. */
86 /* Prepare for first message. */
87 void
88 default_constructor (abstract_po_reader_ty *that)
90 default_po_reader_ty *this = (default_po_reader_ty *) that;
91 size_t i;
93 this->domain = MESSAGE_DOMAIN_DEFAULT;
94 this->comment = NULL;
95 this->comment_dot = NULL;
96 this->filepos_count = 0;
97 this->filepos = NULL;
98 this->is_fuzzy = false;
99 for (i = 0; i < NFORMATS; i++)
100 this->is_format[i] = undecided;
101 this->do_wrap = undecided;
105 void
106 default_destructor (abstract_po_reader_ty *that)
108 default_po_reader_ty *this = (default_po_reader_ty *) that;
110 /* Do not free this->mdlp and this->mlp. */
111 if (this->handle_comments)
113 if (this->comment != NULL)
114 string_list_free (this->comment);
115 if (this->comment_dot != NULL)
116 string_list_free (this->comment_dot);
118 if (this->handle_filepos_comments)
120 size_t j;
122 for (j = 0; j < this->filepos_count; ++j)
123 free (this->filepos[j].file_name);
124 if (this->filepos != NULL)
125 free (this->filepos);
130 void
131 default_parse_brief (abstract_po_reader_ty *that)
133 /* We need to parse comments, because even if this->handle_comments and
134 this->handle_filepos_comments are false, we need to know which messages
135 are fuzzy. */
136 po_lex_pass_comments (true);
140 void
141 default_parse_debrief (abstract_po_reader_ty *that)
146 /* Add the accumulated comments to the message. */
147 static void
148 default_copy_comment_state (default_po_reader_ty *this, message_ty *mp)
150 size_t j, i;
152 if (this->handle_comments)
154 if (this->comment != NULL)
155 for (j = 0; j < this->comment->nitems; ++j)
156 message_comment_append (mp, this->comment->item[j]);
157 if (this->comment_dot != NULL)
158 for (j = 0; j < this->comment_dot->nitems; ++j)
159 message_comment_dot_append (mp, this->comment_dot->item[j]);
161 if (this->handle_filepos_comments)
163 for (j = 0; j < this->filepos_count; ++j)
165 lex_pos_ty *pp;
167 pp = &this->filepos[j];
168 message_comment_filepos (mp, pp->file_name, pp->line_number);
171 mp->is_fuzzy = this->is_fuzzy;
172 for (i = 0; i < NFORMATS; i++)
173 mp->is_format[i] = this->is_format[i];
174 mp->do_wrap = this->do_wrap;
178 static void
179 default_reset_comment_state (default_po_reader_ty *this)
181 size_t j, i;
183 if (this->handle_comments)
185 if (this->comment != NULL)
187 string_list_free (this->comment);
188 this->comment = NULL;
190 if (this->comment_dot != NULL)
192 string_list_free (this->comment_dot);
193 this->comment_dot = NULL;
196 if (this->handle_filepos_comments)
198 for (j = 0; j < this->filepos_count; ++j)
199 free (this->filepos[j].file_name);
200 if (this->filepos != NULL)
201 free (this->filepos);
202 this->filepos_count = 0;
203 this->filepos = NULL;
205 this->is_fuzzy = false;
206 for (i = 0; i < NFORMATS; i++)
207 this->is_format[i] = undecided;
208 this->do_wrap = undecided;
212 /* Process 'domain' directive from .po file. */
213 void
214 default_directive_domain (abstract_po_reader_ty *that, char *name)
216 default_po_reader_ty *this = (default_po_reader_ty *) that;
218 call_set_domain (this, name);
220 /* If there are accumulated comments, throw them away, they are
221 probably part of the file header, or about the domain directive,
222 and will be unrelated to the next message. */
223 default_reset_comment_state (this);
227 /* Process 'msgid'/'msgstr' pair from .po file. */
228 void
229 default_directive_message (abstract_po_reader_ty *that,
230 char *msgid,
231 lex_pos_ty *msgid_pos,
232 char *msgid_plural,
233 char *msgstr, size_t msgstr_len,
234 lex_pos_ty *msgstr_pos,
235 bool force_fuzzy, bool obsolete)
237 default_po_reader_ty *this = (default_po_reader_ty *) that;
239 call_add_message (this, msgid, msgid_pos, msgid_plural,
240 msgstr, msgstr_len, msgstr_pos, force_fuzzy, obsolete);
242 /* Prepare for next message. */
243 default_reset_comment_state (this);
247 void
248 default_comment (abstract_po_reader_ty *that, const char *s)
250 default_po_reader_ty *this = (default_po_reader_ty *) that;
252 if (this->handle_comments)
254 if (this->comment == NULL)
255 this->comment = string_list_alloc ();
256 string_list_append (this->comment, s);
261 void
262 default_comment_dot (abstract_po_reader_ty *that, const char *s)
264 default_po_reader_ty *this = (default_po_reader_ty *) that;
266 if (this->handle_comments)
268 if (this->comment_dot == NULL)
269 this->comment_dot = string_list_alloc ();
270 string_list_append (this->comment_dot, s);
275 void
276 default_comment_filepos (abstract_po_reader_ty *that,
277 const char *name, size_t line)
279 default_po_reader_ty *this = (default_po_reader_ty *) that;
281 if (this->handle_filepos_comments)
283 size_t nbytes;
284 lex_pos_ty *pp;
286 nbytes = (this->filepos_count + 1) * sizeof (this->filepos[0]);
287 this->filepos = xrealloc (this->filepos, nbytes);
288 pp = &this->filepos[this->filepos_count++];
289 pp->file_name = xstrdup (name);
290 pp->line_number = line;
295 /* Test for '#, fuzzy' comments and warn. */
296 void
297 default_comment_special (abstract_po_reader_ty *that, const char *s)
299 default_po_reader_ty *this = (default_po_reader_ty *) that;
301 po_parse_comment_special (s, &this->is_fuzzy, this->is_format,
302 &this->do_wrap);
306 /* Default implementation of methods not inherited from the superclass. */
309 void
310 default_set_domain (default_po_reader_ty *this, char *name)
312 if (this->allow_domain_directives)
313 /* Override current domain name. Don't free memory. */
314 this->domain = name;
315 else
317 po_gram_error_at_line (&gram_pos,
318 _("this file may not contain domain directives"));
320 /* NAME was allocated in po-gram-gen.y but is not used anywhere. */
321 free (name);
325 void
326 default_add_message (default_po_reader_ty *this,
327 char *msgid,
328 lex_pos_ty *msgid_pos,
329 char *msgid_plural,
330 char *msgstr, size_t msgstr_len,
331 lex_pos_ty *msgstr_pos,
332 bool force_fuzzy, bool obsolete)
334 message_ty *mp;
336 if (this->mdlp != NULL)
337 /* Select the appropriate sublist of this->mdlp. */
338 this->mlp = msgdomain_list_sublist (this->mdlp, this->domain, true);
340 if (this->allow_duplicates && msgid[0] != '\0')
341 /* Doesn't matter if this message ID has been seen before. */
342 mp = NULL;
343 else
344 /* See if this message ID has been seen before. */
345 mp = message_list_search (this->mlp, msgid);
347 if (mp)
349 if (!(this->allow_duplicates_if_same_msgstr
350 && msgstr_len == mp->msgstr_len
351 && memcmp (msgstr, mp->msgstr, msgstr_len) == 0))
353 /* We give a fatal error about this, regardless whether the
354 translations are equal or different. This is for consistency
355 with msgmerge, msgcat and others. The user can use the
356 msguniq program to get rid of duplicates. */
357 po_gram_error_at_line (msgid_pos, _("duplicate message definition"));
358 po_gram_error_at_line (&mp->pos, _("\
359 ...this is the location of the first definition"));
361 /* We don't need the just constructed entries' parameter string
362 (allocated in po-gram-gen.y). */
363 free (msgstr);
364 free (msgid);
366 /* Add the accumulated comments to the message. */
367 default_copy_comment_state (this, mp);
369 else
371 /* Construct message to add to the list.
372 Obsolete message go into the list at least for duplicate checking.
373 It's the caller's responsibility to ignore obsolete messages when
374 appropriate. */
375 mp = message_alloc (msgid, msgid_plural, msgstr, msgstr_len, msgstr_pos);
376 mp->obsolete = obsolete;
377 default_copy_comment_state (this, mp);
378 if (force_fuzzy)
379 mp->is_fuzzy = true;
381 call_frob_new_message (this, mp, msgid_pos, msgstr_pos);
383 message_list_append (this->mlp, mp);
388 /* So that the one parser can be used for multiple programs, and also
389 use good data hiding and encapsulation practices, an object
390 oriented approach has been taken. An object instance is allocated,
391 and all actions resulting from the parse will be through
392 invocations of method functions of that object. */
394 static default_po_reader_class_ty default_methods =
397 sizeof (default_po_reader_ty),
398 default_constructor,
399 default_destructor,
400 default_parse_brief,
401 default_parse_debrief,
402 default_directive_domain,
403 default_directive_message,
404 default_comment,
405 default_comment_dot,
406 default_comment_filepos,
407 default_comment_special
409 default_set_domain, /* set_domain */
410 default_add_message, /* add_message */
411 NULL /* frob_new_message */
415 default_po_reader_ty *
416 default_po_reader_alloc (default_po_reader_class_ty *method_table)
418 return (default_po_reader_ty *) po_reader_alloc (&method_table->super);
422 /* ========================================================================= */
423 /* Exported functions. */
426 /* If nonzero, remember comments for file name and line number for each
427 msgid, if present in the reference input. Defaults to true. */
428 int line_comment = 1;
430 /* If false, duplicate msgids in the same domain and file generate an error.
431 If true, such msgids are allowed; the caller should treat them
432 appropriately. Defaults to false. */
433 bool allow_duplicates = false;
435 /* Expected syntax of the input files. */
436 input_syntax_ty input_syntax = syntax_po;
439 msgdomain_list_ty *
440 read_po (FILE *fp, const char *real_filename, const char *logical_filename)
442 default_po_reader_ty *pop;
443 msgdomain_list_ty *mdlp;
445 pop = default_po_reader_alloc (&default_methods);
446 pop->handle_comments = true;
447 pop->handle_filepos_comments = (line_comment != 0);
448 pop->allow_domain_directives = true;
449 pop->allow_duplicates = allow_duplicates;
450 pop->allow_duplicates_if_same_msgstr = false;
451 pop->mdlp = msgdomain_list_alloc (!pop->allow_duplicates);
452 pop->mlp = msgdomain_list_sublist (pop->mdlp, pop->domain, true);
453 if (input_syntax == syntax_properties || input_syntax == syntax_stringtable)
454 /* We know a priori that properties_parse() and stringtable_parse()
455 convert strings to UTF-8. */
456 pop->mdlp->encoding = po_charset_utf8;
457 po_lex_pass_obsolete_entries (true);
458 po_scan ((abstract_po_reader_ty *) pop, fp, real_filename, logical_filename,
459 input_syntax);
460 mdlp = pop->mdlp;
461 po_reader_free ((abstract_po_reader_ty *) pop);
462 return mdlp;
466 msgdomain_list_ty *
467 read_po_file (const char *filename)
469 char *real_filename;
470 FILE *fp = open_po_file (filename, &real_filename, true);
471 msgdomain_list_ty *result;
473 result = read_po (fp, real_filename, filename);
475 if (fp != stdin)
476 fclose (fp);
478 return result;