summaryrefslogtreecommitdiff
path: root/gettext-tools/src/read-catalog-abstract.c
diff options
context:
space:
mode:
Diffstat (limited to 'gettext-tools/src/read-catalog-abstract.c')
-rw-r--r--gettext-tools/src/read-catalog-abstract.c748
1 files changed, 748 insertions, 0 deletions
diff --git a/gettext-tools/src/read-catalog-abstract.c b/gettext-tools/src/read-catalog-abstract.c
new file mode 100644
index 0000000..d4e98ee
--- /dev/null
+++ b/gettext-tools/src/read-catalog-abstract.c
@@ -0,0 +1,748 @@
+/* Reading PO files, abstract class.
+ Copyright (C) 1995-1996, 1998, 2000-2009 Free Software Foundation, Inc.
+
+ This file was written by Peter Miller <millerp@canb.auug.org.au>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "read-catalog-abstract.h"
+
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "xalloc.h"
+#include "xvasprintf.h"
+#include "po-xerror.h"
+#include "error.h"
+#include "gettext.h"
+
+/* Local variables. */
+static abstract_catalog_reader_ty *callback_arg;
+
+
+/* ========================================================================= */
+/* Allocating and freeing instances of abstract_catalog_reader_ty. */
+
+
+abstract_catalog_reader_ty *
+catalog_reader_alloc (abstract_catalog_reader_class_ty *method_table)
+{
+ abstract_catalog_reader_ty *pop;
+
+ pop = (abstract_catalog_reader_ty *) xmalloc (method_table->size);
+ pop->methods = method_table;
+ if (method_table->constructor)
+ method_table->constructor (pop);
+ return pop;
+}
+
+
+void
+catalog_reader_free (abstract_catalog_reader_ty *pop)
+{
+ if (pop->methods->destructor)
+ pop->methods->destructor (pop);
+ free (pop);
+}
+
+
+/* ========================================================================= */
+/* Inline functions to invoke the methods. */
+
+
+static inline void
+call_parse_brief (abstract_catalog_reader_ty *pop)
+{
+ if (pop->methods->parse_brief)
+ pop->methods->parse_brief (pop);
+}
+
+static inline void
+call_parse_debrief (abstract_catalog_reader_ty *pop)
+{
+ if (pop->methods->parse_debrief)
+ pop->methods->parse_debrief (pop);
+}
+
+static inline void
+call_directive_domain (abstract_catalog_reader_ty *pop, char *name)
+{
+ if (pop->methods->directive_domain)
+ pop->methods->directive_domain (pop, name);
+}
+
+static inline void
+call_directive_message (abstract_catalog_reader_ty *pop,
+ char *msgctxt,
+ char *msgid,
+ lex_pos_ty *msgid_pos,
+ char *msgid_plural,
+ char *msgstr, size_t msgstr_len,
+ lex_pos_ty *msgstr_pos,
+ char *prev_msgctxt,
+ char *prev_msgid,
+ char *prev_msgid_plural,
+ bool force_fuzzy, bool obsolete)
+{
+ if (pop->methods->directive_message)
+ pop->methods->directive_message (pop, msgctxt,
+ msgid, msgid_pos, msgid_plural,
+ msgstr, msgstr_len, msgstr_pos,
+ prev_msgctxt,
+ prev_msgid,
+ prev_msgid_plural,
+ force_fuzzy, obsolete);
+}
+
+static inline void
+call_comment (abstract_catalog_reader_ty *pop, const char *s)
+{
+ if (pop->methods->comment != NULL)
+ pop->methods->comment (pop, s);
+}
+
+static inline void
+call_comment_dot (abstract_catalog_reader_ty *pop, const char *s)
+{
+ if (pop->methods->comment_dot != NULL)
+ pop->methods->comment_dot (pop, s);
+}
+
+static inline void
+call_comment_filepos (abstract_catalog_reader_ty *pop, const char *name,
+ size_t line)
+{
+ if (pop->methods->comment_filepos)
+ pop->methods->comment_filepos (pop, name, line);
+}
+
+static inline void
+call_comment_special (abstract_catalog_reader_ty *pop, const char *s)
+{
+ if (pop->methods->comment_special != NULL)
+ pop->methods->comment_special (pop, s);
+}
+
+
+/* ========================================================================= */
+/* Exported functions. */
+
+
+static inline void
+parse_start (abstract_catalog_reader_ty *pop)
+{
+ /* The parse will call the po_callback_... functions (see below)
+ when the various directive are recognised. The callback_arg
+ variable is used to tell these functions which instance is to
+ have the relevant method invoked. */
+ callback_arg = pop;
+
+ call_parse_brief (pop);
+}
+
+static inline void
+parse_end (abstract_catalog_reader_ty *pop)
+{
+ call_parse_debrief (pop);
+ callback_arg = NULL;
+}
+
+
+void
+catalog_reader_parse (abstract_catalog_reader_ty *pop, FILE *fp,
+ const char *real_filename, const char *logical_filename,
+ catalog_input_format_ty input_syntax)
+{
+ error_message_count = 0;
+
+ /* Parse the stream's content. */
+ parse_start (pop);
+ input_syntax->parse (pop, fp, real_filename, logical_filename);
+ parse_end (pop);
+
+ if (error_message_count > 0)
+ po_xerror (PO_SEVERITY_FATAL_ERROR, NULL,
+ /*real_filename*/ NULL, (size_t)(-1), (size_t)(-1), false,
+ xasprintf (ngettext ("found %d fatal error",
+ "found %d fatal errors",
+ error_message_count),
+ error_message_count));
+}
+
+
+/* ========================================================================= */
+/* Callbacks used by po-gram.y or po-lex.c, indirectly from
+ catalog_reader_parse. */
+
+
+/* This function is called by po_gram_lex() whenever a domain directive
+ has been seen. */
+void
+po_callback_domain (char *name)
+{
+ /* assert(callback_arg); */
+ call_directive_domain (callback_arg, name);
+}
+
+
+/* This function is called by po_gram_lex() whenever a message has been
+ seen. */
+void
+po_callback_message (char *msgctxt,
+ char *msgid, lex_pos_ty *msgid_pos, char *msgid_plural,
+ char *msgstr, size_t msgstr_len, lex_pos_ty *msgstr_pos,
+ char *prev_msgctxt,
+ char *prev_msgid,
+ char *prev_msgid_plural,
+ bool force_fuzzy, bool obsolete)
+{
+ /* assert(callback_arg); */
+ call_directive_message (callback_arg, msgctxt,
+ msgid, msgid_pos, msgid_plural,
+ msgstr, msgstr_len, msgstr_pos,
+ prev_msgctxt, prev_msgid, prev_msgid_plural,
+ force_fuzzy, obsolete);
+}
+
+
+void
+po_callback_comment (const char *s)
+{
+ /* assert(callback_arg); */
+ call_comment (callback_arg, s);
+}
+
+
+void
+po_callback_comment_dot (const char *s)
+{
+ /* assert(callback_arg); */
+ call_comment_dot (callback_arg, s);
+}
+
+
+/* This function is called by po_parse_comment_filepos(), once for each
+ filename. */
+void
+po_callback_comment_filepos (const char *name, size_t line)
+{
+ /* assert(callback_arg); */
+ call_comment_filepos (callback_arg, name, line);
+}
+
+
+void
+po_callback_comment_special (const char *s)
+{
+ /* assert(callback_arg); */
+ call_comment_special (callback_arg, s);
+}
+
+
+/* Parse a special comment and put the result in *fuzzyp, formatp, *rangep,
+ *wrapp. */
+void
+po_parse_comment_special (const char *s,
+ bool *fuzzyp, enum is_format formatp[NFORMATS],
+ struct argument_range *rangep, enum is_wrap *wrapp)
+{
+ size_t i;
+
+ *fuzzyp = false;
+ for (i = 0; i < NFORMATS; i++)
+ formatp[i] = undecided;
+ rangep->min = -1;
+ rangep->max = -1;
+ *wrapp = undecided;
+
+ while (*s != '\0')
+ {
+ const char *t;
+
+ /* Skip whitespace. */
+ while (*s != '\0' && strchr ("\n \t\r\f\v,", *s) != NULL)
+ s++;
+
+ /* Collect a token. */
+ t = s;
+ while (*s != '\0' && strchr ("\n \t\r\f\v,", *s) == NULL)
+ s++;
+ if (s != t)
+ {
+ size_t len = s - t;
+
+ /* Accept fuzzy flag. */
+ if (len == 5 && memcmp (t, "fuzzy", 5) == 0)
+ {
+ *fuzzyp = true;
+ continue;
+ }
+
+ /* Accept format description. */
+ if (len >= 7 && memcmp (t + len - 7, "-format", 7) == 0)
+ {
+ const char *p;
+ size_t n;
+ enum is_format value;
+
+ p = t;
+ n = len - 7;
+
+ if (n >= 3 && memcmp (p, "no-", 3) == 0)
+ {
+ p += 3;
+ n -= 3;
+ value = no;
+ }
+ else if (n >= 9 && memcmp (p, "possible-", 9) == 0)
+ {
+ p += 9;
+ n -= 9;
+ value = possible;
+ }
+ else if (n >= 11 && memcmp (p, "impossible-", 11) == 0)
+ {
+ p += 11;
+ n -= 11;
+ value = impossible;
+ }
+ else
+ value = yes;
+
+ for (i = 0; i < NFORMATS; i++)
+ if (strlen (format_language[i]) == n
+ && memcmp (format_language[i], p, n) == 0)
+ {
+ formatp[i] = value;
+ break;
+ }
+ if (i < NFORMATS)
+ continue;
+ }
+
+ /* Accept range description "range: <min>..<max>". */
+ if (len == 6 && memcmp (t, "range:", 6) == 0)
+ {
+ /* Skip whitespace. */
+ while (*s != '\0' && strchr ("\n \t\r\f\v,", *s) != NULL)
+ s++;
+
+ /* Collect a token. */
+ t = s;
+ while (*s != '\0' && strchr ("\n \t\r\f\v,", *s) == NULL)
+ s++;
+ /* Parse it. */
+ if (*t >= '0' && *t <= '9')
+ {
+ unsigned int min = 0;
+
+ for (; *t >= '0' && *t <= '9'; t++)
+ {
+ if (min <= INT_MAX / 10)
+ {
+ min = 10 * min + (*t - '0');
+ if (min > INT_MAX)
+ min = INT_MAX;
+ }
+ else
+ /* Avoid integer overflow. */
+ min = INT_MAX;
+ }
+ if (*t++ == '.')
+ if (*t++ == '.')
+ if (*t >= '0' && *t <= '9')
+ {
+ unsigned int max = 0;
+ for (; *t >= '0' && *t <= '9'; t++)
+ {
+ if (max <= INT_MAX / 10)
+ {
+ max = 10 * max + (*t - '0');
+ if (max > INT_MAX)
+ max = INT_MAX;
+ }
+ else
+ /* Avoid integer overflow. */
+ max = INT_MAX;
+ }
+ if (min <= max)
+ {
+ rangep->min = min;
+ rangep->max = max;
+ continue;
+ }
+ }
+ }
+ }
+
+ /* Accept wrap description. */
+ if (len == 4 && memcmp (t, "wrap", 4) == 0)
+ {
+ *wrapp = yes;
+ continue;
+ }
+ if (len == 7 && memcmp (t, "no-wrap", 7) == 0)
+ {
+ *wrapp = no;
+ continue;
+ }
+
+ /* Unknown special comment marker. It may have been generated
+ from a future xgettext version. Ignore it. */
+ }
+ }
+}
+
+
+/* Parse a GNU style file comment.
+ Syntax: an arbitrary number of
+ STRING COLON NUMBER
+ or
+ STRING
+ The latter style, without line number, occurs in PO files converted e.g.
+ from Pascal .rst files or from OpenOffice resource files.
+ Call po_callback_comment_filepos for each of them. */
+static void
+po_parse_comment_filepos (const char *s)
+{
+ while (*s != '\0')
+ {
+ while (*s == ' ' || *s == '\t' || *s == '\n')
+ s++;
+ if (*s != '\0')
+ {
+ const char *string_start = s;
+
+ do
+ s++;
+ while (!(*s == '\0' || *s == ' ' || *s == '\t' || *s == '\n'));
+
+ /* See if there is a COLON and NUMBER after the STRING, separated
+ through optional spaces. */
+ {
+ const char *p = s;
+
+ while (*p == ' ' || *p == '\t' || *p == '\n')
+ p++;
+
+ if (*p == ':')
+ {
+ p++;
+
+ while (*p == ' ' || *p == '\t' || *p == '\n')
+ p++;
+
+ if (*p >= '0' && *p <= '9')
+ {
+ /* Accumulate a number. */
+ size_t n = 0;
+
+ do
+ {
+ n = n * 10 + (*p - '0');
+ p++;
+ }
+ while (*p >= '0' && *p <= '9');
+
+ if (*p == '\0' || *p == ' ' || *p == '\t' || *p == '\n')
+ {
+ /* Parsed a GNU style file comment with spaces. */
+ const char *string_end = s;
+ size_t string_length = string_end - string_start;
+ char *string = XNMALLOC (string_length + 1, char);
+
+ memcpy (string, string_start, string_length);
+ string[string_length] = '\0';
+
+ po_callback_comment_filepos (string, n);
+
+ free (string);
+
+ s = p;
+ continue;
+ }
+ }
+ }
+ }
+
+ /* See if there is a COLON at the end of STRING and a NUMBER after
+ it, separated through optional spaces. */
+ if (s[-1] == ':')
+ {
+ const char *p = s;
+
+ while (*p == ' ' || *p == '\t' || *p == '\n')
+ p++;
+
+ if (*p >= '0' && *p <= '9')
+ {
+ /* Accumulate a number. */
+ size_t n = 0;
+
+ do
+ {
+ n = n * 10 + (*p - '0');
+ p++;
+ }
+ while (*p >= '0' && *p <= '9');
+
+ if (*p == '\0' || *p == ' ' || *p == '\t' || *p == '\n')
+ {
+ /* Parsed a GNU style file comment with spaces. */
+ const char *string_end = s - 1;
+ size_t string_length = string_end - string_start;
+ char *string = XNMALLOC (string_length + 1, char);
+
+ memcpy (string, string_start, string_length);
+ string[string_length] = '\0';
+
+ po_callback_comment_filepos (string, n);
+
+ free (string);
+
+ s = p;
+ continue;
+ }
+ }
+ }
+
+ /* See if there is a COLON and NUMBER at the end of the STRING,
+ without separating spaces. */
+ {
+ const char *p = s;
+
+ while (p > string_start)
+ {
+ p--;
+ if (!(*p >= '0' && *p <= '9'))
+ {
+ p++;
+ break;
+ }
+ }
+
+ /* p now points to the beginning of the trailing digits segment
+ at the end of STRING. */
+
+ if (p < s
+ && p > string_start + 1
+ && p[-1] == ':')
+ {
+ /* Parsed a GNU style file comment without spaces. */
+ const char *string_end = p - 1;
+
+ /* Accumulate a number. */
+ {
+ size_t n = 0;
+
+ do
+ {
+ n = n * 10 + (*p - '0');
+ p++;
+ }
+ while (p < s);
+
+ {
+ size_t string_length = string_end - string_start;
+ char *string = XNMALLOC (string_length + 1, char);
+
+ memcpy (string, string_start, string_length);
+ string[string_length] = '\0';
+
+ po_callback_comment_filepos (string, n);
+
+ free (string);
+
+ continue;
+ }
+ }
+ }
+ }
+
+ /* Parsed a file comment without line number. */
+ {
+ const char *string_end = s;
+ size_t string_length = string_end - string_start;
+ char *string = XNMALLOC (string_length + 1, char);
+
+ memcpy (string, string_start, string_length);
+ string[string_length] = '\0';
+
+ po_callback_comment_filepos (string, (size_t)(-1));
+
+ free (string);
+ }
+ }
+ }
+}
+
+
+/* Parse a SunOS or Solaris style file comment.
+ Syntax of SunOS style:
+ FILE_KEYWORD COLON STRING COMMA LINE_KEYWORD COLON NUMBER
+ Syntax of Solaris style:
+ FILE_KEYWORD COLON STRING COMMA LINE_KEYWORD NUMBER_KEYWORD COLON NUMBER
+ where
+ FILE_KEYWORD ::= "file" | "File"
+ COLON ::= ":"
+ COMMA ::= ","
+ LINE_KEYWORD ::= "line"
+ NUMBER_KEYWORD ::= "number"
+ NUMBER ::= [0-9]+
+ Return true if parsed, false if not a comment of this form. */
+static bool
+po_parse_comment_solaris_filepos (const char *s)
+{
+ if (s[0] == ' '
+ && (s[1] == 'F' || s[1] == 'f')
+ && s[2] == 'i' && s[3] == 'l' && s[4] == 'e'
+ && s[5] == ':')
+ {
+ const char *string_start;
+ const char *string_end;
+
+ {
+ const char *p = s + 6;
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+ string_start = p;
+ }
+
+ for (string_end = string_start; *string_end != '\0'; string_end++)
+ {
+ const char *p = string_end;
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ if (*p == ',')
+ {
+ p++;
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ if (p[0] == 'l' && p[1] == 'i' && p[2] == 'n' && p[3] == 'e')
+ {
+ p += 4;
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ if (p[0] == 'n' && p[1] == 'u' && p[2] == 'm'
+ && p[3] == 'b' && p[4] == 'e' && p[5] == 'r')
+ {
+ p += 6;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ }
+
+ if (*p == ':')
+ {
+ p++;
+
+ if (*p >= '0' && *p <= '9')
+ {
+ /* Accumulate a number. */
+ size_t n = 0;
+
+ do
+ {
+ n = n * 10 + (*p - '0');
+ p++;
+ }
+ while (*p >= '0' && *p <= '9');
+
+ while (*p == ' ' || *p == '\t' || *p == '\n')
+ p++;
+
+ if (*p == '\0')
+ {
+ /* Parsed a Sun style file comment. */
+ size_t string_length = string_end - string_start;
+ char *string =
+ XNMALLOC (string_length + 1, char);
+
+ memcpy (string, string_start, string_length);
+ string[string_length] = '\0';
+
+ po_callback_comment_filepos (string, n);
+
+ free (string);
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+
+/* This function is called by po_gram_lex() whenever a comment is
+ seen. It analyzes the comment to see what sort it is, and then
+ dispatches it to the appropriate method: call_comment, call_comment_dot,
+ call_comment_filepos (via po_parse_comment_filepos), or
+ call_comment_special. */
+void
+po_callback_comment_dispatcher (const char *s)
+{
+ if (*s == '.')
+ {
+ s++;
+ /* There is usually a space before the comment. People don't
+ consider it part of the comment, therefore remove it here. */
+ if (*s == ' ')
+ s++;
+ po_callback_comment_dot (s);
+ }
+ else if (*s == ':')
+ {
+ /* Parse the file location string. The appropriate callback will be
+ invoked. */
+ po_parse_comment_filepos (s + 1);
+ }
+ else if (*s == ',' || *s == '!')
+ {
+ /* Get all entries in the special comment line. */
+ po_callback_comment_special (s + 1);
+ }
+ else
+ {
+ /* It looks like a plain vanilla comment, but Solaris-style file
+ position lines do, too. Try to parse the lot. If the parse
+ succeeds, the appropriate callback will be invoked. */
+ if (po_parse_comment_solaris_filepos (s))
+ /* Do nothing, it is a Sun-style file pos line. */ ;
+ else
+ {
+ /* There is usually a space before the comment. People don't
+ consider it part of the comment, therefore remove it here. */
+ if (*s == ' ')
+ s++;
+ po_callback_comment (s);
+ }
+ }
+}