1 /* xgettext glade backend.
2 Copyright (C) 2002-2003 Free Software Foundation, Inc.
4 This file was written by Bruno Haible <haible@clisp.cons.org>, 2002.
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)
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.
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
47 #include "po-charset.h"
50 #define _(s) gettext(s)
53 /* glade is an XML based format. Some example files are contained in
57 /* ====================== Keyword set customization. ====================== */
59 /* If true extract all strings. */
60 static bool extract_all
= false;
62 static hash_table keywords
;
63 static bool default_keywords
= true;
67 x_glade_extract_all ()
74 x_glade_keyword (const char *name
)
77 default_keywords
= false;
80 if (keywords
.table
== NULL
)
81 init_hash (&keywords
, 100);
83 insert_entry (&keywords
, name
, strlen (name
), NULL
);
87 /* Finish initializing the keywords hash table.
88 Called after argument processing, before each file is processed. */
94 x_glade_keyword ("label");
95 x_glade_keyword ("title");
96 x_glade_keyword ("text");
97 x_glade_keyword ("format");
98 x_glade_keyword ("copyright");
99 x_glade_keyword ("comments");
100 x_glade_keyword ("preview_text");
101 x_glade_keyword ("tooltip");
102 default_keywords
= false;
107 /* ===================== Dynamic loading of libexpat. ===================== */
111 typedef void *XML_Parser
;
112 typedef char XML_Char
;
113 typedef char XML_LChar
;
114 enum XML_Error
{ XML_ERROR_NONE
};
115 typedef void (*XML_StartElementHandler
) (void *userData
, const XML_Char
*name
, const XML_Char
**atts
);
116 typedef void (*XML_EndElementHandler
) (void *userData
, const XML_Char
*name
);
117 typedef void (*XML_CharacterDataHandler
) (void *userData
, const XML_Char
*s
, int len
);
118 typedef void (*XML_CommentHandler
) (void *userData
, const XML_Char
*data
);
120 static XML_Parser (*p_XML_ParserCreate
) (const XML_Char
*encoding
);
121 static void (*p_XML_SetElementHandler
) (XML_Parser parser
, XML_StartElementHandler start
, XML_EndElementHandler end
);
122 static void (*p_XML_SetCharacterDataHandler
) (XML_Parser parser
, XML_CharacterDataHandler handler
);
123 static void (*p_XML_SetCommentHandler
) (XML_Parser parser
, XML_CommentHandler handler
);
124 static int (*p_XML_Parse
) (XML_Parser parser
, const char *s
, int len
, int isFinal
);
125 static enum XML_Error (*p_XML_GetErrorCode
) (XML_Parser parser
);
126 static int (*p_XML_GetCurrentLineNumber
) (XML_Parser parser
);
127 static int (*p_XML_GetCurrentColumnNumber
) (XML_Parser parser
);
128 static void (*p_XML_ParserFree
) (XML_Parser parser
);
129 static const XML_LChar
* (*p_XML_ErrorString
) (int code
);
131 #define XML_ParserCreate (*p_XML_ParserCreate)
132 #define XML_SetElementHandler (*p_XML_SetElementHandler)
133 #define XML_SetCharacterDataHandler (*p_XML_SetCharacterDataHandler)
134 #define XML_SetCommentHandler (*p_XML_SetCommentHandler)
135 #define XML_Parse (*p_XML_Parse)
136 #define XML_GetErrorCode (*p_XML_GetErrorCode)
137 #define XML_GetCurrentLineNumber (*p_XML_GetCurrentLineNumber)
138 #define XML_GetCurrentColumnNumber (*p_XML_GetCurrentColumnNumber)
139 #define XML_ParserFree (*p_XML_ParserFree)
140 #define XML_ErrorString (*p_XML_ErrorString)
142 static int libexpat_loaded
= 0;
147 if (libexpat_loaded
== 0)
149 void *handle
= dlopen ("libexpat.so.0", RTLD_LAZY
);
151 && (p_XML_ParserCreate
= dlsym (handle
, "XML_ParserCreate")) != NULL
152 && (p_XML_SetElementHandler
= dlsym (handle
, "XML_SetElementHandler")) != NULL
153 && (p_XML_SetCharacterDataHandler
= dlsym (handle
, "XML_SetCharacterDataHandler")) != NULL
154 && (p_XML_SetCommentHandler
= dlsym (handle
, "XML_SetCommentHandler")) != NULL
155 && (p_XML_Parse
= dlsym (handle
, "XML_Parse")) != NULL
156 && (p_XML_GetErrorCode
= dlsym (handle
, "XML_GetErrorCode")) != NULL
157 && (p_XML_GetCurrentLineNumber
= dlsym (handle
, "XML_GetCurrentLineNumber")) != NULL
158 && (p_XML_GetCurrentColumnNumber
= dlsym (handle
, "XML_GetCurrentColumnNumber")) != NULL
159 && (p_XML_ParserFree
= dlsym (handle
, "XML_ParserFree")) != NULL
160 && (p_XML_ErrorString
= dlsym (handle
, "XML_ErrorString")) != NULL
)
163 libexpat_loaded
= -1;
165 return libexpat_loaded
>= 0;
168 #define LIBEXPAT_AVAILABLE() (load_libexpat ())
172 #define LIBEXPAT_AVAILABLE() true
176 /* ============================= XML parsing. ============================= */
178 #if DYNLOAD_LIBEXPAT || HAVE_LIBEXPAT
180 /* Accumulator for the extracted messages. */
181 static message_list_ty
*mlp
;
183 /* Logical filename, used to label the extracted messages. */
184 static char *logical_file_name
;
187 static XML_Parser parser
;
197 static struct element_state
*stack
;
198 static size_t stack_size
;
200 /* Ensures stack_size >= size. */
202 ensure_stack_size (size_t size
)
204 if (size
> stack_size
)
206 stack_size
= 2 * stack_size
;
207 if (stack_size
< size
)
210 (struct element_state
*)
211 xrealloc (stack
, stack_size
* sizeof (struct element_state
));
215 static size_t stack_depth
;
217 /* Callback called when <element> is seen. */
219 start_element_handler (void *userData
, const char *name
,
220 const char **attributes
)
222 struct element_state
*p
;
225 /* Increase stack depth. */
227 ensure_stack_size (stack_depth
+ 1);
229 /* Don't extract a string for the containing element. */
230 stack
[stack_depth
- 1].extract_string
= false;
232 p
= &stack
[stack_depth
];
233 p
->extract_string
= extract_all
;
234 /* In Glade 1, a few specific elements are translatable. */
235 if (!p
->extract_string
)
237 (find_entry (&keywords
, name
, strlen (name
), &hash_result
) == 0);
238 /* In Glade 2, all <property> and <atkproperty> elements are translatable
239 that have the attribute translatable="yes". */
240 if (!p
->extract_string
241 && (strcmp (name
, "property") == 0 || strcmp (name
, "atkproperty") == 0))
243 bool has_translatable
= false;
244 const char **attp
= attributes
;
245 while (*attp
!= NULL
)
247 if (strcmp (attp
[0], "translatable") == 0)
249 has_translatable
= (strcmp (attp
[1], "yes") == 0);
254 p
->extract_string
= has_translatable
;
256 if (!p
->extract_string
257 && strcmp (name
, "atkaction") == 0)
259 const char **attp
= attributes
;
260 while (*attp
!= NULL
)
262 if (strcmp (attp
[0], "description") == 0)
264 if (strcmp (attp
[1], "") != 0)
268 pos
.file_name
= logical_file_name
;
269 pos
.line_number
= XML_GetCurrentLineNumber (parser
);
271 remember_a_message (mlp
, xstrdup (attp
[1]),
279 p
->lineno
= XML_GetCurrentLineNumber (parser
);
283 if (!p
->extract_string
)
284 xgettext_comment_reset ();
287 /* Callback called when </element> is seen. */
289 end_element_handler (void *userData
, const char *name
)
291 struct element_state
*p
= &stack
[stack_depth
];
293 /* Actually extract string. */
294 if (p
->extract_string
)
296 /* Don't extract the empty string. */
301 if (p
->buflen
== p
->bufmax
)
302 p
->buffer
= (char *) xrealloc (p
->buffer
, p
->buflen
+ 1);
303 p
->buffer
[p
->buflen
] = '\0';
305 pos
.file_name
= logical_file_name
;
306 pos
.line_number
= p
->lineno
;
308 remember_a_message (mlp
, p
->buffer
, null_context
, &pos
);
313 /* Free memory for this stack level. */
314 if (p
->buffer
!= NULL
)
317 /* Decrease stack depth. */
320 xgettext_comment_reset ();
323 /* Callback called when some text is seen. */
325 character_data_handler (void *userData
, const char *s
, int len
)
327 struct element_state
*p
= &stack
[stack_depth
];
329 /* Accumulate character data. */
332 if (p
->buflen
+ len
> p
->bufmax
)
334 p
->bufmax
= 2 * p
->bufmax
;
335 if (p
->bufmax
< p
->buflen
+ len
)
336 p
->bufmax
= p
->buflen
+ len
;
337 p
->buffer
= (char *) xrealloc (p
->buffer
, p
->bufmax
);
339 memcpy (p
->buffer
+ p
->buflen
, s
, len
);
344 /* Callback called when some comment text is seen. */
346 comment_handler (void *userData
, const char *data
)
348 /* Split multiline comment into lines, and remove leading and trailing
350 char *copy
= xstrdup (data
);
354 for (p
= copy
; (q
= strchr (p
, '\n')) != NULL
; p
= q
+ 1)
356 while (p
[0] == ' ' || p
[0] == '\t')
358 while (q
> p
&& (q
[-1] == ' ' || q
[-1] == '\t'))
361 xgettext_comment_add (p
);
364 while (p
[0] == ' ' || p
[0] == '\t')
366 while (q
> p
&& (q
[-1] == ' ' || q
[-1] == '\t'))
369 xgettext_comment_add (p
);
375 do_extract_glade (FILE *fp
,
376 const char *real_filename
, const char *logical_filename
,
377 msgdomain_list_ty
*mdlp
)
379 mlp
= mdlp
->item
[0]->messages
;
381 /* expat feeds us strings in UTF-8 encoding. */
382 xgettext_current_source_encoding
= po_charset_utf8
;
384 logical_file_name
= xstrdup (logical_filename
);
388 parser
= XML_ParserCreate (NULL
);
390 error (EXIT_FAILURE
, 0, _("memory exhausted"));
392 XML_SetElementHandler (parser
, start_element_handler
, end_element_handler
);
393 XML_SetCharacterDataHandler (parser
, character_data_handler
);
394 XML_SetCommentHandler (parser
, comment_handler
);
401 int count
= fread (buf
, 1, sizeof buf
, fp
);
406 error (EXIT_FAILURE
, errno
, _("\
407 error while reading \"%s\""), real_filename
);
412 if (XML_Parse (parser
, buf
, count
, 0) == 0)
413 error (EXIT_FAILURE
, 0, _("%s:%d:%d: %s"), logical_filename
,
414 XML_GetCurrentLineNumber (parser
),
415 XML_GetCurrentColumnNumber (parser
) + 1,
416 XML_ErrorString (XML_GetErrorCode (parser
)));
419 if (XML_Parse (parser
, NULL
, 0, 1) == 0)
420 error (EXIT_FAILURE
, 0, _("%s:%d:%d: %s"), logical_filename
,
421 XML_GetCurrentLineNumber (parser
),
422 XML_GetCurrentColumnNumber (parser
) + 1,
423 XML_ErrorString (XML_GetErrorCode (parser
)));
425 XML_ParserFree (parser
);
428 logical_file_name
= NULL
;
435 extract_glade (FILE *fp
,
436 const char *real_filename
, const char *logical_filename
,
437 flag_context_list_table_ty
*flag_table
,
438 msgdomain_list_ty
*mdlp
)
440 #if DYNLOAD_LIBEXPAT || HAVE_LIBEXPAT
441 if (LIBEXPAT_AVAILABLE ())
442 do_extract_glade (fp
, real_filename
, logical_filename
, mdlp
);
446 multiline_error (xstrdup (""),
448 Language \"glade\" is not supported. %s relies on expat.\n\
449 This version was built without expat.\n"),
450 basename (program_name
)));