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