+2008-10-04 Bruno Haible <bruno@clisp.org>
+
+ * message.h (struct argument_range): New type.
+ (has_range_p): New macro.
+ (struct message_ty): Add field 'range'.
+ * message.c (message_alloc): Initialize the 'range' field.
+ (message_copy): Copy the 'range' field.
+ * read-catalog-abstract.h (po_parse_comment_special): Add 'rangep'
+ argument.
+ * read-catalog-abstract.c: Include <limits.h>.
+ (po_parse_comment_special): Add 'rangep' argument. Parse the range
+ description syntax.
+ * read-catalog.h (DEFAULT_CATALOG_READER_TY): Add 'range' field.
+ * read-catalog.c (default_constructor): Initialize the 'range' field.
+ (default_copy_comment_state): Copy the 'range' field into the new
+ message.
+ (default_reset_comment_state): Clear the 'range' field.
+ (default_comment_special): Update.
+ * write-po.h (make_range_description_string): New declaration.
+ * write-po.c (make_range_description_string): New function.
+ (message_print_comment_flags): Also print the range.
+ * write-stringtable.c (write_message): Likewise.
+ * msgl-cat.c: Include <limits.h>.
+ (catenate_msgdomain_list): Fill in the range of the resulting messages.
+ * msgl-equal.c (message_equal): Compare also the ranges.
+ * msgmerge.c (message_merge): Fill in the range of the resulting
+ message. Set it fuzzy if a range was introduced or extended.
+ * xgettext.c (remember_a_message): Set the range of the new message.
+
2008-10-03 Bruno Haible <bruno@clisp.org>
* plural-distrib.h: New file.
mp->is_fuzzy = false;
for (i = 0; i < NFORMATS; i++)
mp->is_format[i] = undecided;
+ mp->range.min = -1;
+ mp->range.max = -1;
mp->do_wrap = undecided;
mp->prev_msgctxt = NULL;
mp->prev_msgid = NULL;
result->is_fuzzy = mp->is_fuzzy;
for (i = 0; i < NFORMATS; i++)
result->is_format[i] = mp->is_format[i];
+ result->range = mp->range;
result->do_wrap = mp->do_wrap;
for (j = 0; j < mp->filepos_count; ++j)
{
possible_format_p (enum is_format);
+/* Range of an unsigned integer argument. */
+struct argument_range
+{
+ int min;
+ int max;
+};
+
+/* Tests whether a range is present. */
+#define has_range_p(range) ((range).min >= 0 && (range).max >= 0)
+
+
/* Is current msgid wrappable? */
#if 0
enum is_wrap
size_t filepos_count;
lex_pos_ty *filepos;
- /* Informations from special comments (e.g. generated by msgmerge). */
+ /* Informations from special comments (#,).
+ Some of them come from extracted comments. They are manipulated by
+ the tools, e.g. msgmerge. */
+
+ /* Fuzzy means "needs translator review". */
bool is_fuzzy;
+
+ /* Designation of format string syntax requirements for specific
+ programming languages. */
enum is_format is_format[NFORMATS];
+ /* Lower and upper bound for the argument whose format directive can be
+ omitted in specific cases of singular or plural. */
+ struct argument_range range;
+
/* Do we want the string to be wrapped in the emitted PO file? */
enum is_wrap do_wrap;
/* Message list concatenation and duplicate handling.
- Copyright (C) 2001-2003, 2005-2007 Free Software Foundation, Inc.
+ Copyright (C) 2001-2003, 2005-2008 Free Software Foundation, Inc.
Written by Bruno Haible <haible@clisp.cons.org>, 2001.
This program is free software: you can redistribute it and/or modify
/* Specification. */
#include "msgl-cat.h"
+#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
tmp->is_fuzzy = true; /* may be set to false later */
for (i = 0; i < NFORMATS; i++)
tmp->is_format[i] = undecided; /* may be set to yes/no later */
+ tmp->range.min = - INT_MAX;
+ tmp->range.max = - INT_MAX;
tmp->do_wrap = yes; /* may be set to no later */
tmp->obsolete = true; /* may be set to false later */
tmp->alternative_count = 0;
tmp->is_fuzzy = mp->is_fuzzy;
for (i = 0; i < NFORMATS; i++)
tmp->is_format[i] = mp->is_format[i];
+ tmp->range = mp->range;
tmp->do_wrap = mp->do_wrap;
tmp->prev_msgctxt = mp->prev_msgctxt;
tmp->prev_msgid = mp->prev_msgid;
for (i = 0; i < NFORMATS; i++)
if (tmp->is_format[i] == undecided)
tmp->is_format[i] = mp->is_format[i];
+ if (tmp->range.min == - INT_MAX
+ && tmp->range.max == - INT_MAX)
+ tmp->range = mp->range;
+ else if (has_range_p (mp->range) && has_range_p (tmp->range))
+ {
+ if (mp->range.min < tmp->range.min)
+ tmp->range.min = mp->range.min;
+ if (mp->range.max > tmp->range.max)
+ tmp->range.max = mp->range.max;
+ }
+ else
+ {
+ tmp->range.min = -1;
+ tmp->range.max = -1;
+ }
if (tmp->do_wrap == undecided)
tmp->do_wrap = mp->do_wrap;
tmp->obsolete = false;
else if (mp->is_format[i] == no
&& tmp->is_format[i] == undecided)
tmp->is_format[i] = no;
+ if (tmp->range.min == - INT_MAX
+ && tmp->range.max == - INT_MAX)
+ tmp->range = mp->range;
+ else if (has_range_p (mp->range) && has_range_p (tmp->range))
+ {
+ if (mp->range.min < tmp->range.min)
+ tmp->range.min = mp->range.min;
+ if (mp->range.max > tmp->range.max)
+ tmp->range.max = mp->range.max;
+ }
+ else
+ {
+ tmp->range.min = -1;
+ tmp->range.max = -1;
+ }
if (mp->do_wrap == no)
tmp->do_wrap = no;
/* Don't fill tmp->prev_msgid in this case. */
/* Message list test for equality.
- Copyright (C) 2001-2002, 2005-2006 Free Software Foundation, Inc.
+ Copyright (C) 2001-2002, 2005-2006, 2008 Free Software Foundation, Inc.
Written by Bruno Haible <haible@clisp.cons.org>, 2001.
This program is free software: you can redistribute it and/or modify
if (mp1->is_format[i] != mp2->is_format[i])
return false;
+ if (!(mp1->range.min == mp2->range.min && mp1->range.max == mp2->range.max))
+ return false;
+
if (!(mp1->prev_msgctxt != NULL
? mp2->prev_msgctxt != NULL
&& strcmp (mp1->prev_msgctxt, mp2->prev_msgctxt) == 0
result->is_fuzzy = true;
}
+ result->range = ref->range;
+ /* If the definition message was assuming a certain range, but the reference
+ message does not specify a range any more or specifies a range that is
+ not the same or a subset, we add a fuzzy marker, because
+ 1. the message needs the translator's attention,
+ 2. msgmerge must not transform a PO file which passes "msgfmt -c"
+ into a PO file which doesn't. */
+ if (!result->is_fuzzy
+ && has_range_p (def->range)
+ && !(has_range_p (ref->range)
+ && ref->range.min >= def->range.min
+ && ref->range.max <= def->range.max))
+ result->is_fuzzy = true;
+
result->do_wrap = ref->do_wrap;
/* Insert previous msgid, commented out with "#|".
/* Reading PO files, abstract class.
- Copyright (C) 1995-1996, 1998, 2000-2007 Free Software Foundation, Inc.
+ Copyright (C) 1995-1996, 1998, 2000-2008 Free Software Foundation, Inc.
This file was written by Peter Miller <millerp@canb.auug.org.au>
/* Specification. */
#include "read-catalog-abstract.h"
+#include <limits.h>
#include <stdlib.h>
#include <string.h>
void
po_parse_comment_special (const char *s,
bool *fuzzyp, enum is_format formatp[NFORMATS],
- enum is_wrap *wrapp)
+ struct argument_range *rangep, enum is_wrap *wrapp)
{
size_t i;
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)
{
/* Reading PO files, abstract class.
- Copyright (C) 1995-1996, 1998, 2000-2003, 2005-2006 Free Software Foundation, Inc.
+ Copyright (C) 1995-1996, 1998, 2000-2003, 2005-2006, 2008 Free Software Foundation, Inc.
This file was written by Peter Miller <millerp@canb.auug.org.au>
/* Parse a special comment and put the result in *fuzzyp, formatp, *wrapp. */
extern void po_parse_comment_special (const char *s, bool *fuzzyp,
enum is_format formatp[NFORMATS],
+ struct argument_range *rangep,
enum is_wrap *wrapp);
/* Reading PO files.
- Copyright (C) 1995-1998, 2000-2003, 2005-2006 Free Software Foundation, Inc.
+ Copyright (C) 1995-1998, 2000-2003, 2005-2006, 2008 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
this->is_fuzzy = false;
for (i = 0; i < NFORMATS; i++)
this->is_format[i] = undecided;
+ this->range.min = -1;
+ this->range.max = -1;
this->do_wrap = undecided;
}
mp->is_fuzzy = this->is_fuzzy;
for (i = 0; i < NFORMATS; i++)
mp->is_format[i] = this->is_format[i];
+ mp->range = this->range;
mp->do_wrap = this->do_wrap;
}
this->is_fuzzy = false;
for (i = 0; i < NFORMATS; i++)
this->is_format[i] = undecided;
+ this->range.min = -1;
+ this->range.max = -1;
this->do_wrap = undecided;
}
{
default_catalog_reader_ty *this = (default_catalog_reader_ty *) that;
- po_parse_comment_special (s, &this->is_fuzzy, this->is_format,
+ po_parse_comment_special (s, &this->is_fuzzy, this->is_format, &this->range,
&this->do_wrap);
}
/* Reading PO files.
- Copyright (C) 1995-1998, 2000-2003, 2005-2006 Free Software Foundation, Inc.
+ Copyright (C) 1995-1998, 2000-2003, 2005-2006, 2008 Free Software Foundation, Inc.
This file was written by Bruno Haible <haible@clisp.cons.org>.
This program is free software: you can redistribute it and/or modify
/* Flags transported in special comments. */ \
bool is_fuzzy; \
enum is_format is_format[NFORMATS]; \
+ struct argument_range range; \
enum is_wrap do_wrap; \
typedef struct default_catalog_reader_ty default_catalog_reader_ty;
}
+/* Convert a RANGE to a freshly allocated string for use in #, flags. */
+
+char *
+make_range_description_string (struct argument_range range)
+{
+ return xasprintf ("range: %d..%d", range.min, range.max);
+}
+
+
/* Convert a wrapping flag DO_WRAP to a string for use in #, flags. */
static const char *
}
-/* Output mp->is_fuzzy, mp->is_format, mp->do_wrap as a comment line. */
+/* Output mp->is_fuzzy, mp->is_format, mp->range, mp->do_wrap as a comment
+ line. */
void
message_print_comment_flags (const message_ty *mp, ostream_t stream, bool debug)
{
if ((mp->is_fuzzy && mp->msgstr[0] != '\0')
|| has_significant_format_p (mp->is_format)
+ || has_range_p (mp->range)
|| mp->do_wrap == no)
{
bool first_flag = true;
first_flag = false;
}
+ if (has_range_p (mp->range))
+ {
+ char *string;
+
+ if (!first_flag)
+ ostream_write_str (stream, ",");
+
+ ostream_write_str (stream, " ");
+ begin_css_class (stream, class_flag);
+ string = make_range_description_string (mp->range);
+ ostream_write_str (stream, string);
+ free (string);
+ end_css_class (stream, class_flag);
+ first_flag = false;
+ }
+
if (mp->do_wrap == no)
{
if (!first_flag)
/* GNU gettext - internationalization aids
- Copyright (C) 1995-1998, 2000-2003, 2006 Free Software Foundation, Inc.
+ Copyright (C) 1995-1998, 2000-2003, 2006, 2008 Free Software Foundation, Inc.
This file was written by Peter Miller <millerp@canb.auug.org.au>
extern bool
significant_format_p (enum is_format is_format);
+extern char *
+ make_range_description_string (struct argument_range range);
/* These functions output parts of a message, as comments. */
extern void
ostream_write_str (stream, " */\n");
}
}
+ if (has_range_p (mp->range))
+ {
+ char *string;
+
+ ostream_write_str (stream, "/* Flag: ");
+ string = make_range_description_string (mp->range);
+ ostream_write_str (stream, string);
+ free (string);
+ ostream_write_str (stream, " */\n");
+ }
/* Now write the untranslated string and the translated string. */
write_escaped_string (stream, mp->msgid);
refcounted_string_list_ty *comment)
{
enum is_format is_format[NFORMATS];
+ struct argument_range range;
enum is_wrap do_wrap;
message_ty *mp;
char *msgstr;
for (i = 0; i < NFORMATS; i++)
is_format[i] = undecided;
+ range.min = -1;
+ range.max = -1;
do_wrap = undecided;
if (msgctxt != NULL)
{
bool tmp_fuzzy;
enum is_format tmp_format[NFORMATS];
+ struct argument_range tmp_range;
enum is_wrap tmp_wrap;
bool interesting;
t += strlen ("xgettext:");
- po_parse_comment_special (t, &tmp_fuzzy, tmp_format, &tmp_wrap);
+ po_parse_comment_special (t, &tmp_fuzzy, tmp_format, &tmp_range,
+ &tmp_wrap);
interesting = false;
for (i = 0; i < NFORMATS; i++)
is_format[i] = tmp_format[i];
interesting = true;
}
+ if (has_range_p (tmp_range))
+ {
+ range = tmp_range;
+ interesting = true;
+ }
if (tmp_wrap != undecided)
{
do_wrap = tmp_wrap;
mp->is_format[i] = is_format[i];
}
+ if (has_range_p (range))
+ {
+ if (has_range_p (mp->range))
+ {
+ if (range.min < mp->range.min)
+ mp->range.min = range.min;
+ if (range.max > mp->range.max)
+ mp->range.max = range.max;
+ }
+ else
+ mp->range = range;
+ }
+
mp->do_wrap = do_wrap == no ? no : yes; /* By default we wrap. */
/* Warn about the use of non-reorderable format strings when the programming