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