From a77cfd06157b414a95da3c8c49a2a6d0852ead56 Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Sat, 4 Oct 2008 11:23:41 +0000 Subject: [PATCH] Attach a numeric range to each message. --- gettext-tools/src/ChangeLog | 29 +++++++++++ gettext-tools/src/message.c | 3 ++ gettext-tools/src/message.h | 24 ++++++++- gettext-tools/src/msgl-cat.c | 36 +++++++++++++- gettext-tools/src/msgl-equal.c | 5 +- gettext-tools/src/msgmerge.c | 14 ++++++ gettext-tools/src/read-catalog-abstract.c | 60 ++++++++++++++++++++++- gettext-tools/src/read-catalog-abstract.h | 3 +- gettext-tools/src/read-catalog.c | 9 +++- gettext-tools/src/read-catalog.h | 3 +- gettext-tools/src/write-po.c | 29 ++++++++++- gettext-tools/src/write-po.h | 4 +- gettext-tools/src/write-stringtable.c | 10 ++++ gettext-tools/src/xgettext.c | 25 +++++++++- 14 files changed, 242 insertions(+), 12 deletions(-) diff --git a/gettext-tools/src/ChangeLog b/gettext-tools/src/ChangeLog index 9b66e31f9..a1171cb9b 100644 --- a/gettext-tools/src/ChangeLog +++ b/gettext-tools/src/ChangeLog @@ -1,3 +1,32 @@ +2008-10-04 Bruno Haible + + * 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 . + (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 . + (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 * plural-distrib.h: New file. diff --git a/gettext-tools/src/message.c b/gettext-tools/src/message.c index 75fae5e0b..121240eac 100644 --- a/gettext-tools/src/message.c +++ b/gettext-tools/src/message.c @@ -117,6 +117,8 @@ message_alloc (const char *msgctxt, 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; @@ -221,6 +223,7 @@ message_copy (message_ty *mp) 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) { diff --git a/gettext-tools/src/message.h b/gettext-tools/src/message.h index c052696d1..6142055b2 100644 --- a/gettext-tools/src/message.h +++ b/gettext-tools/src/message.h @@ -85,6 +85,17 @@ extern bool 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 @@ -141,10 +152,21 @@ struct message_ty 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; diff --git a/gettext-tools/src/msgl-cat.c b/gettext-tools/src/msgl-cat.c index 8d2fdc257..560a72191 100644 --- a/gettext-tools/src/msgl-cat.c +++ b/gettext-tools/src/msgl-cat.c @@ -1,5 +1,5 @@ /* 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 , 2001. This program is free software: you can redistribute it and/or modify @@ -24,6 +24,7 @@ /* Specification. */ #include "msgl-cat.h" +#include #include #include #include @@ -304,6 +305,8 @@ domain \"%s\" in input file `%s' doesn't contain a header entry with a charset s 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; @@ -530,6 +533,7 @@ UTF-8 encoded from the beginning, i.e. already in your source code files.\n"), 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; @@ -562,6 +566,21 @@ UTF-8 encoded from the beginning, i.e. already in your source code files.\n"), 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; @@ -599,6 +618,21 @@ UTF-8 encoded from the beginning, i.e. already in your source code files.\n"), 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. */ diff --git a/gettext-tools/src/msgl-equal.c b/gettext-tools/src/msgl-equal.c index c78333bd7..f8d2e9f88 100644 --- a/gettext-tools/src/msgl-equal.c +++ b/gettext-tools/src/msgl-equal.c @@ -1,5 +1,5 @@ /* 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 , 2001. This program is free software: you can redistribute it and/or modify @@ -180,6 +180,9 @@ message_equal (const message_ty *mp1, const message_ty *mp2, 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 diff --git a/gettext-tools/src/msgmerge.c b/gettext-tools/src/msgmerge.c index b5817607b..4ab4c5e0b 100644 --- a/gettext-tools/src/msgmerge.c +++ b/gettext-tools/src/msgmerge.c @@ -1277,6 +1277,20 @@ message_merge (message_ty *def, message_ty *ref, bool force_fuzzy, 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 "#|". diff --git a/gettext-tools/src/read-catalog-abstract.c b/gettext-tools/src/read-catalog-abstract.c index e78a031b9..05a739a46 100644 --- a/gettext-tools/src/read-catalog-abstract.c +++ b/gettext-tools/src/read-catalog-abstract.c @@ -1,5 +1,5 @@ /* 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 @@ -24,6 +24,7 @@ /* Specification. */ #include "read-catalog-abstract.h" +#include #include #include @@ -258,7 +259,7 @@ po_callback_comment_special (const char *s) 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; @@ -332,6 +333,61 @@ po_parse_comment_special (const char *s, continue; } + /* Accept range description "range: ..". */ + 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) { diff --git a/gettext-tools/src/read-catalog-abstract.h b/gettext-tools/src/read-catalog-abstract.h index 1a5b26b11..17af04cf7 100644 --- a/gettext-tools/src/read-catalog-abstract.h +++ b/gettext-tools/src/read-catalog-abstract.h @@ -1,5 +1,5 @@ /* 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 @@ -182,6 +182,7 @@ extern void po_callback_comment_dispatcher (const char *s); /* 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); diff --git a/gettext-tools/src/read-catalog.c b/gettext-tools/src/read-catalog.c index dae03c90a..f35ceb0d0 100644 --- a/gettext-tools/src/read-catalog.c +++ b/gettext-tools/src/read-catalog.c @@ -1,5 +1,5 @@ /* 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 This program is free software: you can redistribute it and/or modify @@ -102,6 +102,8 @@ default_constructor (abstract_catalog_reader_ty *that) 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; } @@ -175,6 +177,7 @@ default_copy_comment_state (default_catalog_reader_ty *this, message_ty *mp) 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; } @@ -209,6 +212,8 @@ default_reset_comment_state (default_catalog_reader_ty *this) 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; } @@ -307,7 +312,7 @@ default_comment_special (abstract_catalog_reader_ty *that, const char *s) { 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); } diff --git a/gettext-tools/src/read-catalog.h b/gettext-tools/src/read-catalog.h index 60fecb280..e6f592d3c 100644 --- a/gettext-tools/src/read-catalog.h +++ b/gettext-tools/src/read-catalog.h @@ -1,5 +1,5 @@ /* 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 . This program is free software: you can redistribute it and/or modify @@ -112,6 +112,7 @@ struct default_catalog_reader_class_ty /* 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; diff --git a/gettext-tools/src/write-po.c b/gettext-tools/src/write-po.c index ceda8205d..d0acdb174 100644 --- a/gettext-tools/src/write-po.c +++ b/gettext-tools/src/write-po.c @@ -120,6 +120,15 @@ has_significant_format_p (const enum is_format is_format[NFORMATS]) } +/* 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 * @@ -373,13 +382,15 @@ message_print_comment_filepos (const message_ty *mp, ostream_t stream, } -/* 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; @@ -419,6 +430,22 @@ message_print_comment_flags (const message_ty *mp, ostream_t stream, bool debug) 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) diff --git a/gettext-tools/src/write-po.h b/gettext-tools/src/write-po.h index 6916c903d..b832b005b 100644 --- a/gettext-tools/src/write-po.h +++ b/gettext-tools/src/write-po.h @@ -1,5 +1,5 @@ /* 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 @@ -37,6 +37,8 @@ extern const char * 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 diff --git a/gettext-tools/src/write-stringtable.c b/gettext-tools/src/write-stringtable.c index ddd03f2cb..348e4224c 100644 --- a/gettext-tools/src/write-stringtable.c +++ b/gettext-tools/src/write-stringtable.c @@ -223,6 +223,16 @@ write_message (ostream_t stream, const message_ty *mp, 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); diff --git a/gettext-tools/src/xgettext.c b/gettext-tools/src/xgettext.c index 241d4d296..640667e61 100644 --- a/gettext-tools/src/xgettext.c +++ b/gettext-tools/src/xgettext.c @@ -2050,6 +2050,7 @@ remember_a_message (message_list_ty *mlp, char *msgctxt, char *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; @@ -2074,6 +2075,8 @@ remember_a_message (message_list_ty *mlp, char *msgctxt, char *msgid, for (i = 0; i < NFORMATS; i++) is_format[i] = undecided; + range.min = -1; + range.max = -1; do_wrap = undecided; if (msgctxt != NULL) @@ -2155,12 +2158,14 @@ meta information, not the empty string.\n"))); { 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++) @@ -2169,6 +2174,11 @@ meta information, not the empty string.\n"))); 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; @@ -2269,6 +2279,19 @@ meta information, not the empty string.\n"))); 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 -- 2.47.2