xref: /netbsd-src/external/gpl2/gettext/dist/gettext-tools/src/x-glade.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
1 /* xgettext glade backend.
2    Copyright (C) 2002-2003, 2005-2006 Free Software Foundation, Inc.
3 
4    This file was written by Bruno Haible <haible@clisp.cons.org>, 2002.
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 <errno.h>
25 #include <stdbool.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #if DYNLOAD_LIBEXPAT
30 # include <dlfcn.h>
31 #else
32 # if HAVE_LIBEXPAT
33 #  include <expat.h>
34 # endif
35 #endif
36 
37 #include "message.h"
38 #include "xgettext.h"
39 #include "x-glade.h"
40 #include "error.h"
41 #include "xerror.h"
42 #include "xvasprintf.h"
43 #include "basename.h"
44 #include "progname.h"
45 #include "xalloc.h"
46 #include "exit.h"
47 #include "hash.h"
48 #include "po-charset.h"
49 #include "gettext.h"
50 
51 #define _(s) gettext(s)
52 
53 
54 /* glade is an XML based format.  Some example files are contained in
55    libglade-0.16.  */
56 
57 
58 /* ====================== Keyword set customization.  ====================== */
59 
60 /* If true extract all strings.  */
61 static bool extract_all = false;
62 
63 static hash_table keywords;
64 static bool default_keywords = true;
65 
66 
67 void
x_glade_extract_all()68 x_glade_extract_all ()
69 {
70   extract_all = true;
71 }
72 
73 
74 void
x_glade_keyword(const char * name)75 x_glade_keyword (const char *name)
76 {
77   if (name == NULL)
78     default_keywords = false;
79   else
80     {
81       if (keywords.table == NULL)
82 	hash_init (&keywords, 100);
83 
84       hash_insert_entry (&keywords, name, strlen (name), NULL);
85     }
86 }
87 
88 /* Finish initializing the keywords hash table.
89    Called after argument processing, before each file is processed.  */
90 static void
init_keywords()91 init_keywords ()
92 {
93   if (default_keywords)
94     {
95       /* When adding new keywords here, also update the documentation in
96 	 xgettext.texi!  */
97       x_glade_keyword ("label");
98       x_glade_keyword ("title");
99       x_glade_keyword ("text");
100       x_glade_keyword ("format");
101       x_glade_keyword ("copyright");
102       x_glade_keyword ("comments");
103       x_glade_keyword ("preview_text");
104       x_glade_keyword ("tooltip");
105       default_keywords = false;
106     }
107 }
108 
109 
110 /* ===================== Dynamic loading of libexpat.  ===================== */
111 
112 #if DYNLOAD_LIBEXPAT
113 
114 typedef void *XML_Parser;
115 typedef char XML_Char;
116 typedef char XML_LChar;
117 enum XML_Error { XML_ERROR_NONE };
118 typedef void (*XML_StartElementHandler) (void *userData, const XML_Char *name, const XML_Char **atts);
119 typedef void (*XML_EndElementHandler) (void *userData, const XML_Char *name);
120 typedef void (*XML_CharacterDataHandler) (void *userData, const XML_Char *s, int len);
121 typedef void (*XML_CommentHandler) (void *userData, const XML_Char *data);
122 
123 static XML_Parser (*p_XML_ParserCreate) (const XML_Char *encoding);
124 static void (*p_XML_SetElementHandler) (XML_Parser parser, XML_StartElementHandler start, XML_EndElementHandler end);
125 static void (*p_XML_SetCharacterDataHandler) (XML_Parser parser, XML_CharacterDataHandler handler);
126 static void (*p_XML_SetCommentHandler) (XML_Parser parser, XML_CommentHandler handler);
127 static int (*p_XML_Parse) (XML_Parser parser, const char *s, int len, int isFinal);
128 static enum XML_Error (*p_XML_GetErrorCode) (XML_Parser parser);
129 #if XML_MAJOR_VERSION >= 2
130 static XML_Size (*p_XML_GetCurrentLineNumber) (XML_Parser parser);
131 static XML_Size (*p_XML_GetCurrentColumnNumber) (XML_Parser parser);
132 #else
133 static int (*p_XML_GetCurrentLineNumber) (XML_Parser parser);
134 static int (*p_XML_GetCurrentColumnNumber) (XML_Parser parser);
135 #endif
136 static void (*p_XML_ParserFree) (XML_Parser parser);
137 static const XML_LChar * (*p_XML_ErrorString) (int code);
138 
139 #define XML_ParserCreate (*p_XML_ParserCreate)
140 #define XML_SetElementHandler (*p_XML_SetElementHandler)
141 #define XML_SetCharacterDataHandler (*p_XML_SetCharacterDataHandler)
142 #define XML_SetCommentHandler (*p_XML_SetCommentHandler)
143 #define XML_Parse (*p_XML_Parse)
144 #define XML_GetErrorCode (*p_XML_GetErrorCode)
145 #define XML_GetCurrentLineNumber (*p_XML_GetCurrentLineNumber)
146 #define XML_GetCurrentColumnNumber (*p_XML_GetCurrentColumnNumber)
147 #define XML_ParserFree (*p_XML_ParserFree)
148 #define XML_ErrorString (*p_XML_ErrorString)
149 
150 static int libexpat_loaded = 0;
151 
152 static bool
load_libexpat()153 load_libexpat ()
154 {
155   if (libexpat_loaded == 0)
156     {
157       void *handle;
158       /* Be careful to use exactly the version of libexpat that matches the
159 	 binary interface declared in <expat.h>.  */
160 #if XML_MAJOR_VERSION >= 2
161       handle = dlopen ("libexpat.so.1", RTLD_LAZY);
162 #else
163       handle = dlopen ("libexpat.so.0", RTLD_LAZY);
164 #endif
165       if (handle != NULL
166 	  && (p_XML_ParserCreate = dlsym (handle, "XML_ParserCreate")) != NULL
167 	  && (p_XML_SetElementHandler = dlsym (handle, "XML_SetElementHandler")) != NULL
168 	  && (p_XML_SetCharacterDataHandler = dlsym (handle, "XML_SetCharacterDataHandler")) != NULL
169 	  && (p_XML_SetCommentHandler = dlsym (handle, "XML_SetCommentHandler")) != NULL
170 	  && (p_XML_Parse = dlsym (handle, "XML_Parse")) != NULL
171 	  && (p_XML_GetErrorCode = dlsym (handle, "XML_GetErrorCode")) != NULL
172 	  && (p_XML_GetCurrentLineNumber = dlsym (handle, "XML_GetCurrentLineNumber")) != NULL
173 	  && (p_XML_GetCurrentColumnNumber = dlsym (handle, "XML_GetCurrentColumnNumber")) != NULL
174 	  && (p_XML_ParserFree = dlsym (handle, "XML_ParserFree")) != NULL
175 	  && (p_XML_ErrorString = dlsym (handle, "XML_ErrorString")) != NULL)
176 	libexpat_loaded = 1;
177       else
178 	libexpat_loaded = -1;
179     }
180   return libexpat_loaded >= 0;
181 }
182 
183 #define LIBEXPAT_AVAILABLE() (load_libexpat ())
184 
185 #elif HAVE_LIBEXPAT
186 
187 #define LIBEXPAT_AVAILABLE() true
188 
189 #endif
190 
191 /* ============================= XML parsing.  ============================= */
192 
193 #if DYNLOAD_LIBEXPAT || HAVE_LIBEXPAT
194 
195 /* Accumulator for the extracted messages.  */
196 static message_list_ty *mlp;
197 
198 /* Logical filename, used to label the extracted messages.  */
199 static char *logical_file_name;
200 
201 /* XML parser.  */
202 static XML_Parser parser;
203 
204 struct element_state
205 {
206   bool extract_string;
207   int lineno;
208   char *buffer;
209   size_t bufmax;
210   size_t buflen;
211 };
212 static struct element_state *stack;
213 static size_t stack_size;
214 
215 /* Ensures stack_size >= size.  */
216 static void
ensure_stack_size(size_t size)217 ensure_stack_size (size_t size)
218 {
219   if (size > stack_size)
220     {
221       stack_size = 2 * stack_size;
222       if (stack_size < size)
223 	stack_size = size;
224       stack =
225 	(struct element_state *)
226 	xrealloc (stack, stack_size * sizeof (struct element_state));
227     }
228 }
229 
230 static size_t stack_depth;
231 
232 /* Callback called when <element> is seen.  */
233 static void
start_element_handler(void * userData,const char * name,const char ** attributes)234 start_element_handler (void *userData, const char *name,
235 		       const char **attributes)
236 {
237   struct element_state *p;
238   void *hash_result;
239 
240   /* Increase stack depth.  */
241   stack_depth++;
242   ensure_stack_size (stack_depth + 1);
243 
244   /* Don't extract a string for the containing element.  */
245   stack[stack_depth - 1].extract_string = false;
246 
247   p = &stack[stack_depth];
248   p->extract_string = extract_all;
249   /* In Glade 1, a few specific elements are translatable.  */
250   if (!p->extract_string)
251     p->extract_string =
252       (hash_find_entry (&keywords, name, strlen (name), &hash_result) == 0);
253   /* In Glade 2, all <property> and <atkproperty> elements are translatable
254      that have the attribute translatable="yes".  */
255   if (!p->extract_string
256       && (strcmp (name, "property") == 0 || strcmp (name, "atkproperty") == 0))
257     {
258       bool has_translatable = false;
259       const char **attp = attributes;
260       while (*attp != NULL)
261 	{
262 	  if (strcmp (attp[0], "translatable") == 0)
263 	    {
264 	      has_translatable = (strcmp (attp[1], "yes") == 0);
265 	      break;
266 	    }
267 	  attp += 2;
268 	}
269       p->extract_string = has_translatable;
270     }
271   if (!p->extract_string
272       && strcmp (name, "atkaction") == 0)
273     {
274       const char **attp = attributes;
275       while (*attp != NULL)
276 	{
277 	  if (strcmp (attp[0], "description") == 0)
278 	    {
279 	      if (strcmp (attp[1], "") != 0)
280 		{
281 		  lex_pos_ty pos;
282 
283 		  pos.file_name = logical_file_name;
284 		  pos.line_number = XML_GetCurrentLineNumber (parser);
285 
286 		  remember_a_message (mlp, NULL, xstrdup (attp[1]),
287 				      null_context, &pos, savable_comment);
288 		}
289 	      break;
290 	    }
291 	  attp += 2;
292 	}
293     }
294   p->lineno = XML_GetCurrentLineNumber (parser);
295   p->buffer = NULL;
296   p->bufmax = 0;
297   p->buflen = 0;
298   if (!p->extract_string)
299     savable_comment_reset ();
300 }
301 
302 /* Callback called when </element> is seen.  */
303 static void
end_element_handler(void * userData,const char * name)304 end_element_handler (void *userData, const char *name)
305 {
306   struct element_state *p = &stack[stack_depth];
307 
308   /* Actually extract string.  */
309   if (p->extract_string)
310     {
311       /* Don't extract the empty string.  */
312       if (p->buflen > 0)
313 	{
314 	  lex_pos_ty pos;
315 
316 	  if (p->buflen == p->bufmax)
317 	    p->buffer = (char *) xrealloc (p->buffer, p->buflen + 1);
318 	  p->buffer[p->buflen] = '\0';
319 
320 	  pos.file_name = logical_file_name;
321 	  pos.line_number = p->lineno;
322 
323 	  remember_a_message (mlp, NULL, p->buffer, null_context, &pos,
324 			      savable_comment);
325 	  p->buffer = NULL;
326 	}
327     }
328 
329   /* Free memory for this stack level.  */
330   if (p->buffer != NULL)
331     free (p->buffer);
332 
333   /* Decrease stack depth.  */
334   stack_depth--;
335 
336   savable_comment_reset ();
337 }
338 
339 /* Callback called when some text is seen.  */
340 static void
character_data_handler(void * userData,const char * s,int len)341 character_data_handler (void *userData, const char *s, int len)
342 {
343   struct element_state *p = &stack[stack_depth];
344 
345   /* Accumulate character data.  */
346   if (len > 0)
347     {
348       if (p->buflen + len > p->bufmax)
349 	{
350 	  p->bufmax = 2 * p->bufmax;
351 	  if (p->bufmax < p->buflen + len)
352 	    p->bufmax = p->buflen + len;
353 	  p->buffer = (char *) xrealloc (p->buffer, p->bufmax);
354 	}
355       memcpy (p->buffer + p->buflen, s, len);
356       p->buflen += len;
357     }
358 }
359 
360 /* Callback called when some comment text is seen.  */
361 static void
comment_handler(void * userData,const char * data)362 comment_handler (void *userData, const char *data)
363 {
364   /* Split multiline comment into lines, and remove leading and trailing
365      whitespace.  */
366   char *copy = xstrdup (data);
367   char *p = copy;
368   char *q;
369 
370   for (p = copy; (q = strchr (p, '\n')) != NULL; p = q + 1)
371     {
372       while (p[0] == ' ' || p[0] == '\t')
373 	p++;
374       while (q > p && (q[-1] == ' ' || q[-1] == '\t'))
375 	q--;
376       *q = '\0';
377       savable_comment_add (p);
378     }
379   q = p + strlen (p);
380   while (p[0] == ' ' || p[0] == '\t')
381     p++;
382   while (q > p && (q[-1] == ' ' || q[-1] == '\t'))
383     q--;
384   *q = '\0';
385   savable_comment_add (p);
386   free (copy);
387 }
388 
389 
390 static void
do_extract_glade(FILE * fp,const char * real_filename,const char * logical_filename,msgdomain_list_ty * mdlp)391 do_extract_glade (FILE *fp,
392 		  const char *real_filename, const char *logical_filename,
393 		  msgdomain_list_ty *mdlp)
394 {
395   mlp = mdlp->item[0]->messages;
396 
397   /* expat feeds us strings in UTF-8 encoding.  */
398   xgettext_current_source_encoding = po_charset_utf8;
399 
400   logical_file_name = xstrdup (logical_filename);
401 
402   init_keywords ();
403 
404   parser = XML_ParserCreate (NULL);
405   if (parser == NULL)
406     error (EXIT_FAILURE, 0, _("memory exhausted"));
407 
408   XML_SetElementHandler (parser, start_element_handler, end_element_handler);
409   XML_SetCharacterDataHandler (parser, character_data_handler);
410   XML_SetCommentHandler (parser, comment_handler);
411 
412   stack_depth = 0;
413 
414   while (!feof (fp))
415     {
416       char buf[4096];
417       int count = fread (buf, 1, sizeof buf, fp);
418 
419       if (count == 0)
420 	{
421 	  if (ferror (fp))
422 	    error (EXIT_FAILURE, errno, _("\
423 error while reading \"%s\""), real_filename);
424 	  /* EOF reached.  */
425 	  break;
426 	}
427 
428       if (XML_Parse (parser, buf, count, 0) == 0)
429 	error (EXIT_FAILURE, 0, _("%s:%lu:%lu: %s"), logical_filename,
430 	       (unsigned long) XML_GetCurrentLineNumber (parser),
431 	       (unsigned long) XML_GetCurrentColumnNumber (parser) + 1,
432 	       XML_ErrorString (XML_GetErrorCode (parser)));
433     }
434 
435   if (XML_Parse (parser, NULL, 0, 1) == 0)
436     error (EXIT_FAILURE, 0, _("%s:%lu:%lu: %s"), logical_filename,
437 	   (unsigned long) XML_GetCurrentLineNumber (parser),
438 	   (unsigned long) XML_GetCurrentColumnNumber (parser) + 1,
439 	   XML_ErrorString (XML_GetErrorCode (parser)));
440 
441   XML_ParserFree (parser);
442 
443   /* Close scanner.  */
444   logical_file_name = NULL;
445   parser = NULL;
446 }
447 
448 #endif
449 
450 void
extract_glade(FILE * fp,const char * real_filename,const char * logical_filename,flag_context_list_table_ty * flag_table,msgdomain_list_ty * mdlp)451 extract_glade (FILE *fp,
452 	       const char *real_filename, const char *logical_filename,
453 	       flag_context_list_table_ty *flag_table,
454 	       msgdomain_list_ty *mdlp)
455 {
456 #if DYNLOAD_LIBEXPAT || HAVE_LIBEXPAT
457   if (LIBEXPAT_AVAILABLE ())
458     do_extract_glade (fp, real_filename, logical_filename, mdlp);
459   else
460 #endif
461     {
462       multiline_error (xstrdup (""),
463 		       xasprintf (_("\
464 Language \"glade\" is not supported. %s relies on expat.\n\
465 This version was built without expat.\n"),
466 				  basename (program_name)));
467       exit (EXIT_FAILURE);
468     }
469 }
470