1 /* Reading PO files, abstract class.
2 Copyright (C) 1995-1996, 1998, 2000-2009, 2015 Free Software
5 This file was written by Peter Miller <millerp@canb.auug.org.au>
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
26 #include "read-catalog-abstract.h"
33 #include "xvasprintf.h"
34 #include "po-xerror.h"
38 /* Local variables. */
39 static abstract_catalog_reader_ty *callback_arg;
42 /* ========================================================================= */
43 /* Allocating and freeing instances of abstract_catalog_reader_ty. */
46 abstract_catalog_reader_ty *
47 catalog_reader_alloc (abstract_catalog_reader_class_ty *method_table)
49 abstract_catalog_reader_ty *pop;
51 pop = (abstract_catalog_reader_ty *) xmalloc (method_table->size);
52 pop->methods = method_table;
53 if (method_table->constructor)
54 method_table->constructor (pop);
60 catalog_reader_free (abstract_catalog_reader_ty *pop)
62 if (pop->methods->destructor)
63 pop->methods->destructor (pop);
68 /* ========================================================================= */
69 /* Inline functions to invoke the methods. */
73 call_parse_brief (abstract_catalog_reader_ty *pop)
75 if (pop->methods->parse_brief)
76 pop->methods->parse_brief (pop);
80 call_parse_debrief (abstract_catalog_reader_ty *pop)
82 if (pop->methods->parse_debrief)
83 pop->methods->parse_debrief (pop);
87 call_directive_domain (abstract_catalog_reader_ty *pop, char *name)
89 if (pop->methods->directive_domain)
90 pop->methods->directive_domain (pop, name);
94 call_directive_message (abstract_catalog_reader_ty *pop,
97 lex_pos_ty *msgid_pos,
99 char *msgstr, size_t msgstr_len,
100 lex_pos_ty *msgstr_pos,
103 char *prev_msgid_plural,
104 bool force_fuzzy, bool obsolete)
106 if (pop->methods->directive_message)
107 pop->methods->directive_message (pop, msgctxt,
108 msgid, msgid_pos, msgid_plural,
109 msgstr, msgstr_len, msgstr_pos,
113 force_fuzzy, obsolete);
117 call_comment (abstract_catalog_reader_ty *pop, const char *s)
119 if (pop->methods->comment != NULL)
120 pop->methods->comment (pop, s);
124 call_comment_dot (abstract_catalog_reader_ty *pop, const char *s)
126 if (pop->methods->comment_dot != NULL)
127 pop->methods->comment_dot (pop, s);
131 call_comment_filepos (abstract_catalog_reader_ty *pop, const char *name,
134 if (pop->methods->comment_filepos)
135 pop->methods->comment_filepos (pop, name, line);
139 call_comment_special (abstract_catalog_reader_ty *pop, const char *s)
141 if (pop->methods->comment_special != NULL)
142 pop->methods->comment_special (pop, s);
146 /* ========================================================================= */
147 /* Exported functions. */
151 parse_start (abstract_catalog_reader_ty *pop)
153 /* The parse will call the po_callback_... functions (see below)
154 when the various directive are recognised. The callback_arg
155 variable is used to tell these functions which instance is to
156 have the relevant method invoked. */
159 call_parse_brief (pop);
163 parse_end (abstract_catalog_reader_ty *pop)
165 call_parse_debrief (pop);
171 catalog_reader_parse (abstract_catalog_reader_ty *pop, FILE *fp,
172 const char *real_filename, const char *logical_filename,
173 catalog_input_format_ty input_syntax)
175 error_message_count = 0;
177 /* Parse the stream's content. */
179 input_syntax->parse (pop, fp, real_filename, logical_filename);
182 if (error_message_count > 0)
183 po_xerror (PO_SEVERITY_FATAL_ERROR, NULL,
184 /*real_filename*/ NULL, (size_t)(-1), (size_t)(-1), false,
185 xasprintf (ngettext ("found %d fatal error",
186 "found %d fatal errors",
187 error_message_count),
188 error_message_count));
192 /* ========================================================================= */
193 /* Callbacks used by po-gram.y or po-lex.c, indirectly from
194 catalog_reader_parse. */
197 /* This function is called by po_gram_lex() whenever a domain directive
200 po_callback_domain (char *name)
202 /* assert(callback_arg); */
203 call_directive_domain (callback_arg, name);
207 /* This function is called by po_gram_lex() whenever a message has been
210 po_callback_message (char *msgctxt,
211 char *msgid, lex_pos_ty *msgid_pos, char *msgid_plural,
212 char *msgstr, size_t msgstr_len, lex_pos_ty *msgstr_pos,
215 char *prev_msgid_plural,
216 bool force_fuzzy, bool obsolete)
218 /* assert(callback_arg); */
219 call_directive_message (callback_arg, msgctxt,
220 msgid, msgid_pos, msgid_plural,
221 msgstr, msgstr_len, msgstr_pos,
222 prev_msgctxt, prev_msgid, prev_msgid_plural,
223 force_fuzzy, obsolete);
228 po_callback_comment (const char *s)
230 /* assert(callback_arg); */
231 call_comment (callback_arg, s);
236 po_callback_comment_dot (const char *s)
238 /* assert(callback_arg); */
239 call_comment_dot (callback_arg, s);
243 /* This function is called by po_parse_comment_filepos(), once for each
246 po_callback_comment_filepos (const char *name, size_t line)
248 /* assert(callback_arg); */
249 call_comment_filepos (callback_arg, name, line);
254 po_callback_comment_special (const char *s)
256 /* assert(callback_arg); */
257 call_comment_special (callback_arg, s);
261 /* Parse a special comment and put the result in *fuzzyp, formatp, *rangep,
264 po_parse_comment_special (const char *s,
265 bool *fuzzyp, enum is_format formatp[NFORMATS],
266 struct argument_range *rangep, enum is_wrap *wrapp,
267 enum is_syntax_check scp[NSYNTAXCHECKS])
272 for (i = 0; i < NFORMATS; i++)
273 formatp[i] = undecided;
277 for (i = 0; i < NSYNTAXCHECKS; i++)
284 /* Skip whitespace. */
285 while (*s != '\0' && strchr ("\n \t\r\f\v,", *s) != NULL)
288 /* Collect a token. */
290 while (*s != '\0' && strchr ("\n \t\r\f\v,", *s) == NULL)
296 /* Accept fuzzy flag. */
297 if (len == 5 && memcmp (t, "fuzzy", 5) == 0)
303 /* Accept format description. */
304 if (len >= 7 && memcmp (t + len - 7, "-format", 7) == 0)
308 enum is_format value;
313 if (n >= 3 && memcmp (p, "no-", 3) == 0)
319 else if (n >= 9 && memcmp (p, "possible-", 9) == 0)
325 else if (n >= 11 && memcmp (p, "impossible-", 11) == 0)
334 for (i = 0; i < NFORMATS; i++)
335 if (strlen (format_language[i]) == n
336 && memcmp (format_language[i], p, n) == 0)
345 /* Accept range description "range: <min>..<max>". */
346 if (len == 6 && memcmp (t, "range:", 6) == 0)
348 /* Skip whitespace. */
349 while (*s != '\0' && strchr ("\n \t\r\f\v,", *s) != NULL)
352 /* Collect a token. */
354 while (*s != '\0' && strchr ("\n \t\r\f\v,", *s) == NULL)
357 if (*t >= '0' && *t <= '9')
359 unsigned int min = 0;
361 for (; *t >= '0' && *t <= '9'; t++)
363 if (min <= INT_MAX / 10)
365 min = 10 * min + (*t - '0');
370 /* Avoid integer overflow. */
375 if (*t >= '0' && *t <= '9')
377 unsigned int max = 0;
378 for (; *t >= '0' && *t <= '9'; t++)
380 if (max <= INT_MAX / 10)
382 max = 10 * max + (*t - '0');
387 /* Avoid integer overflow. */
400 /* Accept wrap description. */
401 if (len == 4 && memcmp (t, "wrap", 4) == 0)
406 if (len == 7 && memcmp (t, "no-wrap", 7) == 0)
412 /* Accept syntax check description. */
413 if (len >= 6 && memcmp (t + len - 6, "-check", 6) == 0)
417 enum is_syntax_check value;
422 if (n >= 3 && memcmp (p, "no-", 3) == 0)
431 for (i = 0; i < NSYNTAXCHECKS; i++)
432 if (strlen (syntax_check_name[i]) == n
433 && memcmp (syntax_check_name[i], p, n) == 0)
438 if (i < NSYNTAXCHECKS)
442 /* Unknown special comment marker. It may have been generated
443 from a future xgettext version. Ignore it. */
449 /* Parse a GNU style file comment.
450 Syntax: an arbitrary number of
454 The latter style, without line number, occurs in PO files converted e.g.
455 from Pascal .rst files or from OpenOffice resource files.
456 Call po_callback_comment_filepos for each of them. */
458 po_parse_comment_filepos (const char *s)
462 while (*s == ' ' || *s == '\t' || *s == '\n')
466 const char *string_start = s;
470 while (!(*s == '\0' || *s == ' ' || *s == '\t' || *s == '\n'));
472 /* See if there is a COLON and NUMBER after the STRING, separated
473 through optional spaces. */
477 while (*p == ' ' || *p == '\t' || *p == '\n')
484 while (*p == ' ' || *p == '\t' || *p == '\n')
487 if (*p >= '0' && *p <= '9')
489 /* Accumulate a number. */
494 n = n * 10 + (*p - '0');
497 while (*p >= '0' && *p <= '9');
499 if (*p == '\0' || *p == ' ' || *p == '\t' || *p == '\n')
501 /* Parsed a GNU style file comment with spaces. */
502 const char *string_end = s;
503 size_t string_length = string_end - string_start;
504 char *string = XNMALLOC (string_length + 1, char);
506 memcpy (string, string_start, string_length);
507 string[string_length] = '\0';
509 po_callback_comment_filepos (string, n);
520 /* See if there is a COLON at the end of STRING and a NUMBER after
521 it, separated through optional spaces. */
526 while (*p == ' ' || *p == '\t' || *p == '\n')
529 if (*p >= '0' && *p <= '9')
531 /* Accumulate a number. */
536 n = n * 10 + (*p - '0');
539 while (*p >= '0' && *p <= '9');
541 if (*p == '\0' || *p == ' ' || *p == '\t' || *p == '\n')
543 /* Parsed a GNU style file comment with spaces. */
544 const char *string_end = s - 1;
545 size_t string_length = string_end - string_start;
546 char *string = XNMALLOC (string_length + 1, char);
548 memcpy (string, string_start, string_length);
549 string[string_length] = '\0';
551 po_callback_comment_filepos (string, n);
561 /* See if there is a COLON and NUMBER at the end of the STRING,
562 without separating spaces. */
566 while (p > string_start)
569 if (!(*p >= '0' && *p <= '9'))
576 /* p now points to the beginning of the trailing digits segment
577 at the end of STRING. */
580 && p > string_start + 1
583 /* Parsed a GNU style file comment without spaces. */
584 const char *string_end = p - 1;
586 /* Accumulate a number. */
592 n = n * 10 + (*p - '0');
598 size_t string_length = string_end - string_start;
599 char *string = XNMALLOC (string_length + 1, char);
601 memcpy (string, string_start, string_length);
602 string[string_length] = '\0';
604 po_callback_comment_filepos (string, n);
614 /* Parsed a file comment without line number. */
616 const char *string_end = s;
617 size_t string_length = string_end - string_start;
618 char *string = XNMALLOC (string_length + 1, char);
620 memcpy (string, string_start, string_length);
621 string[string_length] = '\0';
623 po_callback_comment_filepos (string, (size_t)(-1));
632 /* Parse a SunOS or Solaris style file comment.
633 Syntax of SunOS style:
634 FILE_KEYWORD COLON STRING COMMA LINE_KEYWORD COLON NUMBER
635 Syntax of Solaris style:
636 FILE_KEYWORD COLON STRING COMMA LINE_KEYWORD NUMBER_KEYWORD COLON NUMBER
638 FILE_KEYWORD ::= "file" | "File"
641 LINE_KEYWORD ::= "line"
642 NUMBER_KEYWORD ::= "number"
644 Return true if parsed, false if not a comment of this form. */
646 po_parse_comment_solaris_filepos (const char *s)
649 && (s[1] == 'F' || s[1] == 'f')
650 && s[2] == 'i' && s[3] == 'l' && s[4] == 'e'
653 const char *string_start;
654 const char *string_end;
657 const char *p = s + 6;
659 while (*p == ' ' || *p == '\t')
664 for (string_end = string_start; *string_end != '\0'; string_end++)
666 const char *p = string_end;
668 while (*p == ' ' || *p == '\t')
675 while (*p == ' ' || *p == '\t')
678 if (p[0] == 'l' && p[1] == 'i' && p[2] == 'n' && p[3] == 'e')
682 while (*p == ' ' || *p == '\t')
685 if (p[0] == 'n' && p[1] == 'u' && p[2] == 'm'
686 && p[3] == 'b' && p[4] == 'e' && p[5] == 'r')
689 while (*p == ' ' || *p == '\t')
697 if (*p >= '0' && *p <= '9')
699 /* Accumulate a number. */
704 n = n * 10 + (*p - '0');
707 while (*p >= '0' && *p <= '9');
709 while (*p == ' ' || *p == '\t' || *p == '\n')
714 /* Parsed a Sun style file comment. */
715 size_t string_length = string_end - string_start;
717 XNMALLOC (string_length + 1, char);
719 memcpy (string, string_start, string_length);
720 string[string_length] = '\0';
722 po_callback_comment_filepos (string, n);
738 /* This function is called by po_gram_lex() whenever a comment is
739 seen. It analyzes the comment to see what sort it is, and then
740 dispatches it to the appropriate method: call_comment, call_comment_dot,
741 call_comment_filepos (via po_parse_comment_filepos), or
742 call_comment_special. */
744 po_callback_comment_dispatcher (const char *s)
749 /* There is usually a space before the comment. People don't
750 consider it part of the comment, therefore remove it here. */
753 po_callback_comment_dot (s);
757 /* Parse the file location string. The appropriate callback will be
759 po_parse_comment_filepos (s + 1);
761 else if (*s == ',' || *s == '!')
763 /* Get all entries in the special comment line. */
764 po_callback_comment_special (s + 1);
768 /* It looks like a plain vanilla comment, but Solaris-style file
769 position lines do, too. Try to parse the lot. If the parse
770 succeeds, the appropriate callback will be invoked. */
771 if (po_parse_comment_solaris_filepos (s))
772 /* Do nothing, it is a Sun-style file pos line. */ ;
775 /* There is usually a space before the comment. People don't
776 consider it part of the comment, therefore remove it here. */
779 po_callback_comment (s);