No empty .Rs/.Re
[netbsd-mini2440.git] / gnu / dist / gettext / gettext-tools / src / read-po-abstract.c
blob01e0a7882bcdd12dc756e6f131e6076d67f80439
1 /* Reading PO files, abstract class.
2 Copyright (C) 1995-1996, 1998, 2000-2005 Free Software Foundation, Inc.
4 This file was written by Peter Miller <millerp@canb.auug.org.au>
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.
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. */
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
25 /* Specification. */
26 #include "read-po-abstract.h"
28 #include <stdlib.h>
29 #include <string.h>
31 #include "po-gram.h"
32 #include "read-properties.h"
33 #include "read-stringtable.h"
34 #include "xalloc.h"
35 #include "gettext.h"
37 /* Local variables. */
38 static abstract_po_reader_ty *callback_arg;
41 /* ========================================================================= */
42 /* Allocating and freeing instances of abstract_po_reader_ty. */
45 abstract_po_reader_ty *
46 po_reader_alloc (abstract_po_reader_class_ty *method_table)
48 abstract_po_reader_ty *pop;
50 pop = (abstract_po_reader_ty *) xmalloc (method_table->size);
51 pop->methods = method_table;
52 if (method_table->constructor)
53 method_table->constructor (pop);
54 return pop;
58 void
59 po_reader_free (abstract_po_reader_ty *pop)
61 if (pop->methods->destructor)
62 pop->methods->destructor (pop);
63 free (pop);
67 /* ========================================================================= */
68 /* Inline functions to invoke the methods. */
71 static inline void
72 call_parse_brief (abstract_po_reader_ty *pop)
74 if (pop->methods->parse_brief)
75 pop->methods->parse_brief (pop);
78 static inline void
79 call_parse_debrief (abstract_po_reader_ty *pop)
81 if (pop->methods->parse_debrief)
82 pop->methods->parse_debrief (pop);
85 static inline void
86 call_directive_domain (abstract_po_reader_ty *pop, char *name)
88 if (pop->methods->directive_domain)
89 pop->methods->directive_domain (pop, name);
92 static inline void
93 call_directive_message (abstract_po_reader_ty *pop,
94 char *msgid,
95 lex_pos_ty *msgid_pos,
96 char *msgid_plural,
97 char *msgstr, size_t msgstr_len,
98 lex_pos_ty *msgstr_pos,
99 bool force_fuzzy, bool obsolete)
101 if (pop->methods->directive_message)
102 pop->methods->directive_message (pop, msgid, msgid_pos, msgid_plural,
103 msgstr, msgstr_len, msgstr_pos,
104 force_fuzzy, obsolete);
107 static inline void
108 call_comment (abstract_po_reader_ty *pop, const char *s)
110 if (pop->methods->comment != NULL)
111 pop->methods->comment (pop, s);
114 static inline void
115 call_comment_dot (abstract_po_reader_ty *pop, const char *s)
117 if (pop->methods->comment_dot != NULL)
118 pop->methods->comment_dot (pop, s);
121 static inline void
122 call_comment_filepos (abstract_po_reader_ty *pop, const char *name, size_t line)
124 if (pop->methods->comment_filepos)
125 pop->methods->comment_filepos (pop, name, line);
128 static inline void
129 call_comment_special (abstract_po_reader_ty *pop, const char *s)
131 if (pop->methods->comment_special != NULL)
132 pop->methods->comment_special (pop, s);
136 /* ========================================================================= */
137 /* Exported functions. */
140 static inline void
141 po_scan_start (abstract_po_reader_ty *pop)
143 /* The parse will call the po_callback_... functions (see below)
144 when the various directive are recognised. The callback_arg
145 variable is used to tell these functions which instance is to
146 have the relevant method invoked. */
147 callback_arg = pop;
149 call_parse_brief (pop);
152 static inline void
153 po_scan_end (abstract_po_reader_ty *pop)
155 call_parse_debrief (pop);
156 callback_arg = NULL;
160 void
161 po_scan (abstract_po_reader_ty *pop, FILE *fp,
162 const char *real_filename, const char *logical_filename,
163 input_syntax_ty syntax)
165 /* Parse the stream's content. */
166 switch (syntax)
168 case syntax_po:
169 lex_start (fp, real_filename, logical_filename);
170 po_scan_start (pop);
171 po_gram_parse ();
172 po_scan_end (pop);
173 lex_end ();
174 break;
175 case syntax_properties:
176 po_scan_start (pop);
177 properties_parse (pop, fp, real_filename, logical_filename);
178 po_scan_end (pop);
179 break;
180 case syntax_stringtable:
181 po_scan_start (pop);
182 stringtable_parse (pop, fp, real_filename, logical_filename);
183 po_scan_end (pop);
184 break;
185 default:
186 abort ();
189 if (error_message_count > 0)
190 po_error (EXIT_FAILURE, 0,
191 ngettext ("found %d fatal error", "found %d fatal errors",
192 error_message_count),
193 error_message_count);
194 error_message_count = 0;
198 /* ========================================================================= */
199 /* Callbacks used by po-gram.y or po-lex.c, indirectly from po_scan. */
202 /* This function is called by po_gram_lex() whenever a domain directive
203 has been seen. */
204 void
205 po_callback_domain (char *name)
207 /* assert(callback_arg); */
208 call_directive_domain (callback_arg, name);
212 /* This function is called by po_gram_lex() whenever a message has been
213 seen. */
214 void
215 po_callback_message (char *msgid, lex_pos_ty *msgid_pos, char *msgid_plural,
216 char *msgstr, size_t msgstr_len, lex_pos_ty *msgstr_pos,
217 bool force_fuzzy, bool obsolete)
219 /* assert(callback_arg); */
220 call_directive_message (callback_arg, msgid, msgid_pos, msgid_plural,
221 msgstr, msgstr_len, msgstr_pos,
222 force_fuzzy, obsolete);
226 void
227 po_callback_comment (const char *s)
229 /* assert(callback_arg); */
230 call_comment (callback_arg, s);
234 void
235 po_callback_comment_dot (const char *s)
237 /* assert(callback_arg); */
238 call_comment_dot (callback_arg, s);
242 /* This function is called by po_parse_comment_filepos(), once for each
243 filename. */
244 void
245 po_callback_comment_filepos (const char *name, size_t line)
247 /* assert(callback_arg); */
248 call_comment_filepos (callback_arg, name, line);
252 void
253 po_callback_comment_special (const char *s)
255 /* assert(callback_arg); */
256 call_comment_special (callback_arg, s);
260 /* Parse a special comment and put the result in *fuzzyp, formatp, *wrapp. */
261 void
262 po_parse_comment_special (const char *s,
263 bool *fuzzyp, enum is_format formatp[NFORMATS],
264 enum is_wrap *wrapp)
266 size_t i;
268 *fuzzyp = false;
269 for (i = 0; i < NFORMATS; i++)
270 formatp[i] = undecided;
271 *wrapp = undecided;
273 while (*s != '\0')
275 const char *t;
277 /* Skip whitespace. */
278 while (*s != '\0' && strchr ("\n \t\r\f\v,", *s) != NULL)
279 s++;
281 /* Collect a token. */
282 t = s;
283 while (*s != '\0' && strchr ("\n \t\r\f\v,", *s) == NULL)
284 s++;
285 if (s != t)
287 size_t len = s - t;
289 /* Accept fuzzy flag. */
290 if (len == 5 && memcmp (t, "fuzzy", 5) == 0)
292 *fuzzyp = true;
293 continue;
296 /* Accept format description. */
297 if (len >= 7 && memcmp (t + len - 7, "-format", 7) == 0)
299 const char *p;
300 size_t n;
301 enum is_format value;
303 p = t;
304 n = len - 7;
306 if (n >= 3 && memcmp (p, "no-", 3) == 0)
308 p += 3;
309 n -= 3;
310 value = no;
312 else if (n >= 9 && memcmp (p, "possible-", 9) == 0)
314 p += 9;
315 n -= 9;
316 value = possible;
318 else if (n >= 11 && memcmp (p, "impossible-", 11) == 0)
320 p += 11;
321 n -= 11;
322 value = impossible;
324 else
325 value = yes;
327 for (i = 0; i < NFORMATS; i++)
328 if (strlen (format_language[i]) == n
329 && memcmp (format_language[i], p, n) == 0)
331 formatp[i] = value;
332 break;
334 if (i < NFORMATS)
335 continue;
338 /* Accept wrap description. */
339 if (len == 4 && memcmp (t, "wrap", 4) == 0)
341 *wrapp = yes;
342 continue;
344 if (len == 7 && memcmp (t, "no-wrap", 7) == 0)
346 *wrapp = no;
347 continue;
350 /* Unknown special comment marker. It may have been generated
351 from a future xgettext version. Ignore it. */
357 /* Parse a GNU style file comment.
358 Syntax: an arbitrary number of
359 STRING COLON NUMBER
361 STRING
362 The latter style, without line number, occurs in PO files converted e.g.
363 from Pascal .rst files or from OpenOffice resource files.
364 Call po_callback_comment_filepos for each of them. */
365 static void
366 po_parse_comment_filepos (const char *s)
368 while (*s != '\0')
370 while (*s == ' ' || *s == '\t' || *s == '\n')
371 s++;
372 if (*s != '\0')
374 const char *string_start = s;
377 s++;
378 while (!(*s == '\0' || *s == ' ' || *s == '\t' || *s == '\n'));
380 /* See if there is a COLON and NUMBER after the STRING, separated
381 through optional spaces. */
383 const char *p = s;
385 while (*p == ' ' || *p == '\t' || *p == '\n')
386 p++;
388 if (*p == ':')
390 p++;
392 while (*p == ' ' || *p == '\t' || *p == '\n')
393 p++;
395 if (*p >= '0' && *p <= '9')
397 /* Accumulate a number. */
398 size_t n = 0;
402 n = n * 10 + (*p - '0');
403 p++;
405 while (*p >= '0' && *p <= '9');
407 if (*p == '\0' || *p == ' ' || *p == '\t' || *p == '\n')
409 /* Parsed a GNU style file comment with spaces. */
410 const char *string_end = s;
411 size_t string_length = string_end - string_start;
412 char *string = (char *) xmalloc (string_length + 1);
414 memcpy (string, string_start, string_length);
415 string[string_length] = '\0';
417 po_callback_comment_filepos (string, n);
419 free (string);
421 s = p;
422 continue;
428 /* See if there is a COLON at the end of STRING and a NUMBER after
429 it, separated through optional spaces. */
430 if (s[-1] == ':')
432 const char *p = s;
434 while (*p == ' ' || *p == '\t' || *p == '\n')
435 p++;
437 if (*p >= '0' && *p <= '9')
439 /* Accumulate a number. */
440 size_t n = 0;
444 n = n * 10 + (*p - '0');
445 p++;
447 while (*p >= '0' && *p <= '9');
449 if (*p == '\0' || *p == ' ' || *p == '\t' || *p == '\n')
451 /* Parsed a GNU style file comment with spaces. */
452 const char *string_end = s - 1;
453 size_t string_length = string_end - string_start;
454 char *string = (char *) xmalloc (string_length + 1);
456 memcpy (string, string_start, string_length);
457 string[string_length] = '\0';
459 po_callback_comment_filepos (string, n);
461 free (string);
463 s = p;
464 continue;
469 /* See if there is a COLON and NUMBER at the end of the STRING,
470 without separating spaces. */
472 const char *p = s;
474 while (p > string_start)
476 p--;
477 if (!(*p >= '0' && *p <= '9'))
479 p++;
480 break;
484 /* p now points to the beginning of the trailing digits segment
485 at the end of STRING. */
487 if (p < s
488 && p > string_start + 1
489 && p[-1] == ':')
491 /* Parsed a GNU style file comment without spaces. */
492 const char *string_end = p - 1;
494 /* Accumulate a number. */
496 size_t n = 0;
500 n = n * 10 + (*p - '0');
501 p++;
503 while (p < s);
506 size_t string_length = string_end - string_start;
507 char *string = (char *) xmalloc (string_length + 1);
509 memcpy (string, string_start, string_length);
510 string[string_length] = '\0';
512 po_callback_comment_filepos (string, n);
514 free (string);
516 continue;
522 /* Parsed a file comment without line number. */
524 const char *string_end = s;
525 size_t string_length = string_end - string_start;
526 char *string = (char *) xmalloc (string_length + 1);
528 memcpy (string, string_start, string_length);
529 string[string_length] = '\0';
531 po_callback_comment_filepos (string, (size_t)(-1));
533 free (string);
540 /* Parse a SunOS or Solaris style file comment.
541 Syntax of SunOS style:
542 FILE_KEYWORD COLON STRING COMMA LINE_KEYWORD COLON NUMBER
543 Syntax of Solaris style:
544 FILE_KEYWORD COLON STRING COMMA LINE_KEYWORD NUMBER_KEYWORD COLON NUMBER
545 where
546 FILE_KEYWORD ::= "file" | "File"
547 COLON ::= ":"
548 COMMA ::= ","
549 LINE_KEYWORD ::= "line"
550 NUMBER_KEYWORD ::= "number"
551 NUMBER ::= [0-9]+
552 Return true if parsed, false if not a comment of this form. */
553 static bool
554 po_parse_comment_solaris_filepos (const char *s)
556 if (s[0] == ' '
557 && (s[1] == 'F' || s[1] == 'f')
558 && s[2] == 'i' && s[3] == 'l' && s[4] == 'e'
559 && s[5] == ':')
561 const char *string_start;
562 const char *string_end;
565 const char *p = s + 6;
567 while (*p == ' ' || *p == '\t')
568 p++;
569 string_start = p;
572 for (string_end = string_start; *string_end != '\0'; string_end++)
574 const char *p = string_end;
576 while (*p == ' ' || *p == '\t')
577 p++;
579 if (*p == ',')
581 p++;
583 while (*p == ' ' || *p == '\t')
584 p++;
586 if (p[0] == 'l' && p[1] == 'i' && p[2] == 'n' && p[3] == 'e')
588 p += 4;
590 while (*p == ' ' || *p == '\t')
591 p++;
593 if (p[0] == 'n' && p[1] == 'u' && p[2] == 'm'
594 && p[3] == 'b' && p[4] == 'e' && p[5] == 'r')
596 p += 6;
597 while (*p == ' ' || *p == '\t')
598 p++;
601 if (*p == ':')
603 p++;
605 if (*p >= '0' && *p <= '9')
607 /* Accumulate a number. */
608 size_t n = 0;
612 n = n * 10 + (*p - '0');
613 p++;
615 while (*p >= '0' && *p <= '9');
617 while (*p == ' ' || *p == '\t' || *p == '\n')
618 p++;
620 if (*p == '\0')
622 /* Parsed a Sun style file comment. */
623 size_t string_length = string_end - string_start;
624 char *string =
625 (char *) xmalloc (string_length + 1);
627 memcpy (string, string_start, string_length);
628 string[string_length] = '\0';
630 po_callback_comment_filepos (string, n);
632 free (string);
633 return true;
642 return false;
646 /* This function is called by po_gram_lex() whenever a comment is
647 seen. It analyzes the comment to see what sort it is, and then
648 dispatches it to the appropriate method: call_comment, call_comment_dot,
649 call_comment_filepos (via po_parse_comment_filepos), or
650 call_comment_special. */
651 void
652 po_callback_comment_dispatcher (const char *s)
654 if (*s == '.')
655 po_callback_comment_dot (s + 1);
656 else if (*s == ':')
658 /* Parse the file location string. The appropriate callback will be
659 invoked. */
660 po_parse_comment_filepos (s + 1);
662 else if (*s == ',' || *s == '!')
664 /* Get all entries in the special comment line. */
665 po_callback_comment_special (s + 1);
667 else
669 /* It looks like a plain vanilla comment, but Solaris-style file
670 position lines do, too. Try to parse the lot. If the parse
671 succeeds, the appropriate callback will be invoked. */
672 if (po_parse_comment_solaris_filepos (s))
673 /* Do nothing, it is a Sun-style file pos line. */ ;
674 else
675 po_callback_comment (s);