1 /* Reading PO files, abstract class.
2 Copyright (C) 1995-1996, 1998, 2000-2009 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 3 of the License, or
9 (at your option) 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, see <http://www.gnu.org/licenses/>. */
25 #include "read-catalog-abstract.h"
32 #include "xvasprintf.h"
33 #include "po-xerror.h"
37 /* Local variables. */
38 static abstract_catalog_reader_ty *callback_arg;
41 /* ========================================================================= */
42 /* Allocating and freeing instances of abstract_catalog_reader_ty. */
45 abstract_catalog_reader_ty *
46 catalog_reader_alloc (abstract_catalog_reader_class_ty *method_table)
48 abstract_catalog_reader_ty *pop;
50 pop = (abstract_catalog_reader_ty *) xmalloc (method_table->size);
51 pop->methods = method_table;
52 if (method_table->constructor)
53 method_table->constructor (pop);
59 catalog_reader_free (abstract_catalog_reader_ty *pop)
61 if (pop->methods->destructor)
62 pop->methods->destructor (pop);
67 /* ========================================================================= */
68 /* Inline functions to invoke the methods. */
72 call_parse_brief (abstract_catalog_reader_ty *pop)
74 if (pop->methods->parse_brief)
75 pop->methods->parse_brief (pop);
79 call_parse_debrief (abstract_catalog_reader_ty *pop)
81 if (pop->methods->parse_debrief)
82 pop->methods->parse_debrief (pop);
86 call_directive_domain (abstract_catalog_reader_ty *pop, char *name)
88 if (pop->methods->directive_domain)
89 pop->methods->directive_domain (pop, name);
93 call_directive_message (abstract_catalog_reader_ty *pop,
96 lex_pos_ty *msgid_pos,
98 char *msgstr, size_t msgstr_len,
99 lex_pos_ty *msgstr_pos,
102 char *prev_msgid_plural,
103 bool force_fuzzy, bool obsolete)
105 if (pop->methods->directive_message)
106 pop->methods->directive_message (pop, msgctxt,
107 msgid, msgid_pos, msgid_plural,
108 msgstr, msgstr_len, msgstr_pos,
112 force_fuzzy, obsolete);
116 call_comment (abstract_catalog_reader_ty *pop, const char *s)
118 if (pop->methods->comment != NULL)
119 pop->methods->comment (pop, s);
123 call_comment_dot (abstract_catalog_reader_ty *pop, const char *s)
125 if (pop->methods->comment_dot != NULL)
126 pop->methods->comment_dot (pop, s);
130 call_comment_filepos (abstract_catalog_reader_ty *pop, const char *name,
133 if (pop->methods->comment_filepos)
134 pop->methods->comment_filepos (pop, name, line);
138 call_comment_special (abstract_catalog_reader_ty *pop, const char *s)
140 if (pop->methods->comment_special != NULL)
141 pop->methods->comment_special (pop, s);
145 /* ========================================================================= */
146 /* Exported functions. */
150 parse_start (abstract_catalog_reader_ty *pop)
152 /* The parse will call the po_callback_... functions (see below)
153 when the various directive are recognised. The callback_arg
154 variable is used to tell these functions which instance is to
155 have the relevant method invoked. */
158 call_parse_brief (pop);
162 parse_end (abstract_catalog_reader_ty *pop)
164 call_parse_debrief (pop);
170 catalog_reader_parse (abstract_catalog_reader_ty *pop, FILE *fp,
171 const char *real_filename, const char *logical_filename,
172 catalog_input_format_ty input_syntax)
174 /* Parse the stream's content. */
176 input_syntax->parse (pop, fp, real_filename, logical_filename);
179 if (error_message_count > 0)
180 po_xerror (PO_SEVERITY_FATAL_ERROR, NULL,
181 /*real_filename*/ NULL, (size_t)(-1), (size_t)(-1), false,
182 xasprintf (ngettext ("found %d fatal error",
183 "found %d fatal errors",
184 error_message_count),
185 error_message_count));
186 error_message_count = 0;
190 /* ========================================================================= */
191 /* Callbacks used by po-gram.y or po-lex.c, indirectly from
192 catalog_reader_parse. */
195 /* This function is called by po_gram_lex() whenever a domain directive
198 po_callback_domain (char *name)
200 /* assert(callback_arg); */
201 call_directive_domain (callback_arg, name);
205 /* This function is called by po_gram_lex() whenever a message has been
208 po_callback_message (char *msgctxt,
209 char *msgid, lex_pos_ty *msgid_pos, char *msgid_plural,
210 char *msgstr, size_t msgstr_len, lex_pos_ty *msgstr_pos,
213 char *prev_msgid_plural,
214 bool force_fuzzy, bool obsolete)
216 /* assert(callback_arg); */
217 call_directive_message (callback_arg, msgctxt,
218 msgid, msgid_pos, msgid_plural,
219 msgstr, msgstr_len, msgstr_pos,
220 prev_msgctxt, prev_msgid, prev_msgid_plural,
221 force_fuzzy, obsolete);
226 po_callback_comment (const char *s)
228 /* assert(callback_arg); */
229 call_comment (callback_arg, s);
234 po_callback_comment_dot (const char *s)
236 /* assert(callback_arg); */
237 call_comment_dot (callback_arg, s);
241 /* This function is called by po_parse_comment_filepos(), once for each
244 po_callback_comment_filepos (const char *name, size_t line)
246 /* assert(callback_arg); */
247 call_comment_filepos (callback_arg, name, line);
252 po_callback_comment_special (const char *s)
254 /* assert(callback_arg); */
255 call_comment_special (callback_arg, s);
259 /* Parse a special comment and put the result in *fuzzyp, formatp, *rangep,
262 po_parse_comment_special (const char *s,
263 bool *fuzzyp, enum is_format formatp[NFORMATS],
264 struct argument_range *rangep, enum is_wrap *wrapp)
269 for (i = 0; i < NFORMATS; i++)
270 formatp[i] = undecided;
279 /* Skip whitespace. */
280 while (*s != '\0' && strchr ("\n \t\r\f\v,", *s) != NULL)
283 /* Collect a token. */
285 while (*s != '\0' && strchr ("\n \t\r\f\v,", *s) == NULL)
291 /* Accept fuzzy flag. */
292 if (len == 5 && memcmp (t, "fuzzy", 5) == 0)
298 /* Accept format description. */
299 if (len >= 7 && memcmp (t + len - 7, "-format", 7) == 0)
303 enum is_format value;
308 if (n >= 3 && memcmp (p, "no-", 3) == 0)
314 else if (n >= 9 && memcmp (p, "possible-", 9) == 0)
320 else if (n >= 11 && memcmp (p, "impossible-", 11) == 0)
329 for (i = 0; i < NFORMATS; i++)
330 if (strlen (format_language[i]) == n
331 && memcmp (format_language[i], p, n) == 0)
340 /* Accept range description "range: <min>..<max>". */
341 if (len == 6 && memcmp (t, "range:", 6) == 0)
343 /* Skip whitespace. */
344 while (*s != '\0' && strchr ("\n \t\r\f\v,", *s) != NULL)
347 /* Collect a token. */
349 while (*s != '\0' && strchr ("\n \t\r\f\v,", *s) == NULL)
352 if (*t >= '0' && *t <= '9')
354 unsigned int min = 0;
356 for (; *t >= '0' && *t <= '9'; t++)
358 if (min <= INT_MAX / 10)
360 min = 10 * min + (*t - '0');
365 /* Avoid integer overflow. */
370 if (*t >= '0' && *t <= '9')
372 unsigned int max = 0;
373 for (; *t >= '0' && *t <= '9'; t++)
375 if (max <= INT_MAX / 10)
377 max = 10 * max + (*t - '0');
382 /* Avoid integer overflow. */
395 /* Accept wrap description. */
396 if (len == 4 && memcmp (t, "wrap", 4) == 0)
401 if (len == 7 && memcmp (t, "no-wrap", 7) == 0)
407 /* Unknown special comment marker. It may have been generated
408 from a future xgettext version. Ignore it. */
414 /* Parse a GNU style file comment.
415 Syntax: an arbitrary number of
419 The latter style, without line number, occurs in PO files converted e.g.
420 from Pascal .rst files or from OpenOffice resource files.
421 Call po_callback_comment_filepos for each of them. */
423 po_parse_comment_filepos (const char *s)
427 while (*s == ' ' || *s == '\t' || *s == '\n')
431 const char *string_start = s;
435 while (!(*s == '\0' || *s == ' ' || *s == '\t' || *s == '\n'));
437 /* See if there is a COLON and NUMBER after the STRING, separated
438 through optional spaces. */
442 while (*p == ' ' || *p == '\t' || *p == '\n')
449 while (*p == ' ' || *p == '\t' || *p == '\n')
452 if (*p >= '0' && *p <= '9')
454 /* Accumulate a number. */
459 n = n * 10 + (*p - '0');
462 while (*p >= '0' && *p <= '9');
464 if (*p == '\0' || *p == ' ' || *p == '\t' || *p == '\n')
466 /* Parsed a GNU style file comment with spaces. */
467 const char *string_end = s;
468 size_t string_length = string_end - string_start;
469 char *string = XNMALLOC (string_length + 1, char);
471 memcpy (string, string_start, string_length);
472 string[string_length] = '\0';
474 po_callback_comment_filepos (string, n);
485 /* See if there is a COLON at the end of STRING and a NUMBER after
486 it, separated through optional spaces. */
491 while (*p == ' ' || *p == '\t' || *p == '\n')
494 if (*p >= '0' && *p <= '9')
496 /* Accumulate a number. */
501 n = n * 10 + (*p - '0');
504 while (*p >= '0' && *p <= '9');
506 if (*p == '\0' || *p == ' ' || *p == '\t' || *p == '\n')
508 /* Parsed a GNU style file comment with spaces. */
509 const char *string_end = s - 1;
510 size_t string_length = string_end - string_start;
511 char *string = XNMALLOC (string_length + 1, char);
513 memcpy (string, string_start, string_length);
514 string[string_length] = '\0';
516 po_callback_comment_filepos (string, n);
526 /* See if there is a COLON and NUMBER at the end of the STRING,
527 without separating spaces. */
531 while (p > string_start)
534 if (!(*p >= '0' && *p <= '9'))
541 /* p now points to the beginning of the trailing digits segment
542 at the end of STRING. */
545 && p > string_start + 1
548 /* Parsed a GNU style file comment without spaces. */
549 const char *string_end = p - 1;
551 /* Accumulate a number. */
557 n = n * 10 + (*p - '0');
563 size_t string_length = string_end - string_start;
564 char *string = XNMALLOC (string_length + 1, char);
566 memcpy (string, string_start, string_length);
567 string[string_length] = '\0';
569 po_callback_comment_filepos (string, n);
579 /* Parsed a file comment without line number. */
581 const char *string_end = s;
582 size_t string_length = string_end - string_start;
583 char *string = XNMALLOC (string_length + 1, char);
585 memcpy (string, string_start, string_length);
586 string[string_length] = '\0';
588 po_callback_comment_filepos (string, (size_t)(-1));
597 /* Parse a SunOS or Solaris style file comment.
598 Syntax of SunOS style:
599 FILE_KEYWORD COLON STRING COMMA LINE_KEYWORD COLON NUMBER
600 Syntax of Solaris style:
601 FILE_KEYWORD COLON STRING COMMA LINE_KEYWORD NUMBER_KEYWORD COLON NUMBER
603 FILE_KEYWORD ::= "file" | "File"
606 LINE_KEYWORD ::= "line"
607 NUMBER_KEYWORD ::= "number"
609 Return true if parsed, false if not a comment of this form. */
611 po_parse_comment_solaris_filepos (const char *s)
614 && (s[1] == 'F' || s[1] == 'f')
615 && s[2] == 'i' && s[3] == 'l' && s[4] == 'e'
618 const char *string_start;
619 const char *string_end;
622 const char *p = s + 6;
624 while (*p == ' ' || *p == '\t')
629 for (string_end = string_start; *string_end != '\0'; string_end++)
631 const char *p = string_end;
633 while (*p == ' ' || *p == '\t')
640 while (*p == ' ' || *p == '\t')
643 if (p[0] == 'l' && p[1] == 'i' && p[2] == 'n' && p[3] == 'e')
647 while (*p == ' ' || *p == '\t')
650 if (p[0] == 'n' && p[1] == 'u' && p[2] == 'm'
651 && p[3] == 'b' && p[4] == 'e' && p[5] == 'r')
654 while (*p == ' ' || *p == '\t')
662 if (*p >= '0' && *p <= '9')
664 /* Accumulate a number. */
669 n = n * 10 + (*p - '0');
672 while (*p >= '0' && *p <= '9');
674 while (*p == ' ' || *p == '\t' || *p == '\n')
679 /* Parsed a Sun style file comment. */
680 size_t string_length = string_end - string_start;
682 XNMALLOC (string_length + 1, char);
684 memcpy (string, string_start, string_length);
685 string[string_length] = '\0';
687 po_callback_comment_filepos (string, n);
703 /* This function is called by po_gram_lex() whenever a comment is
704 seen. It analyzes the comment to see what sort it is, and then
705 dispatches it to the appropriate method: call_comment, call_comment_dot,
706 call_comment_filepos (via po_parse_comment_filepos), or
707 call_comment_special. */
709 po_callback_comment_dispatcher (const char *s)
714 /* There is usually a space before the comment. People don't
715 consider it part of the comment, therefore remove it here. */
718 po_callback_comment_dot (s);
722 /* Parse the file location string. The appropriate callback will be
724 po_parse_comment_filepos (s + 1);
726 else if (*s == ',' || *s == '!')
728 /* Get all entries in the special comment line. */
729 po_callback_comment_special (s + 1);
733 /* It looks like a plain vanilla comment, but Solaris-style file
734 position lines do, too. Try to parse the lot. If the parse
735 succeeds, the appropriate callback will be invoked. */
736 if (po_parse_comment_solaris_filepos (s))
737 /* Do nothing, it is a Sun-style file pos line. */ ;
740 /* There is usually a space before the comment. People don't
741 consider it part of the comment, therefore remove it here. */
744 po_callback_comment (s);