From: Bruno Haible Date: Tue, 23 Jul 2002 12:33:13 +0000 (+0000) Subject: Add support for ISO C 99 format string directive macros. X-Git-Tag: v0.11.4~8 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8b45c5df18e1976a1275297db73978bfe6144c7e;p=thirdparty%2Fgettext.git Add support for ISO C 99 format string directive macros. --- diff --git a/NEWS b/NEWS index 3ae7fe185..577f15e08 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,8 @@ +Version 0.11.4 - July 2002 + +* The tools now know about the ISO C 99 format string directive + macros PRId64, PRIxMAX etc. + Version 0.11.3 - July 2002 * New program: diff --git a/doc/ChangeLog b/doc/ChangeLog index b34b307a4..7809d66c0 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,11 @@ +2002-07-21 Bruno Haible + + * gettext.texi (Preparing Strings): Update section about + macros. + (aclocal): Add inttypes.m4, inttypes_h.m4, stdint_h.m4, uintmax_t.m4 + to the list of m4 files. + (AM_GNU_GETTEXT): Document needsymbol value 'need-formatstring-macros'. + 2002-07-19 Bruno Haible * gettext.texi (aclocal): Mention intdiv0.m4. diff --git a/doc/gettext.texi b/doc/gettext.texi index 1f6e0de28..cc699b0f9 100644 --- a/doc/gettext.texi +++ b/doc/gettext.texi @@ -1885,34 +1885,31 @@ integer through @code{printf}. It expands to a constant string, usually Assume you have code like @example -printf ("The amount is %0" PRId64 "\n"), number); +printf ("The amount is %0" PRId64 "\n", number); @end example @noindent -After marking, this cannot become +The @code{gettext} tools and library have special support for these +@code{} macros. You can therefore simply write @example -printf (gettext ("The amount is %0") PRId64 "\n"), number); +printf (gettext ("The amount is %0" PRId64 "\n"), number); @end example @noindent -because it would simply be invalid C syntax. It cannot become +The PO file will contain the string "The amount is %0\n". +The translators will provide a translation containing "%0" +as well, and at runtime the @code{gettext} function's result will +contain the appropriate constant string, "d" or "ld" or "lld". -@example -printf (gettext ("The amount is %0" PRId64 "\n")), number); -@end example - -@noindent -because the value of @code{PRId64} is not known to @code{xgettext}, and -even if were, there would be three or more possibilities, and the -translator would have to translate three or more strings that differ in -a single letter. - -The solution for this problem is to change the code like this: +This works only for the predefined @code{} macros. If +you have defined your own similar macros, let's say @samp{MYPRId64}, +that are not known to @code{xgettext}, the solution for this problem +is to change the code like this: @example char buf1[100]; -sprintf (buf1, "%0" PRId64, number); +sprintf (buf1, "%0" MYPRId64, number); printf (gettext ("The amount is %s\n"), buf1); @end example @@ -5972,8 +5969,10 @@ AC_CONFIG_AUX_DIR([@var{subdir}]) If you do not have an @file{aclocal.m4} file in your distribution, the simplest is to concatenate the files @file{codeset.m4}, @file{gettext.m4}, @file{glibc21.m4}, @file{iconv.m4}, @file{intdiv0.m4}, -@file{isc-posix.m4}, @file{lcmessage.m4}, @file{lib-ld.m4}, @file{lib-link.m4}, -@file{lib-prefix.m4}, @file{progtest.m4} from GNU @code{gettext}'s +@file{inttypes.m4}, @file{inttypes_h.m4}, @file{isc-posix.m4}, +@file{lcmessage.m4}, @file{lib-ld.m4}, @file{lib-link.m4}, +@file{lib-prefix.m4}, @file{progtest.m4}, @file{stdint_h.m4}, +@file{uintmax_t.m4} from GNU @code{gettext}'s @file{m4/} directory into a single file. If you have suppressed the @file{intl/} directory, only @file{gettext.m4}, @file{iconv.m4}, @file{lib-ld.m4}, @file{lib-link.m4}, @file{lib-prefix.m4}, @@ -6326,7 +6325,13 @@ presence of @code{AM_DISABLE_SHARED}). If @var{intlsymbol} is If @var{needsymbol} is specified and is @samp{need-ngettext}, then GNU gettext implementations (in libc or libintl) without the @code{ngettext()} -function will be ignored. +function will be ignored. If @var{needsymbol} is specified and is +@samp{need-formatstring-macros}, then GNU gettext implementations that don't +support the ISO C 99 @file{} formatstring macros will be ignored. +Only one @var{needsymbol} can be specified. To specify more than one +requirement, just specify the strongest one among them. The hierarchy among +the various alternatives is as follows: @samp{need-formatstring-macros} +implies @samp{need-ngettext}. @var{intldir} is used to find the intl libraries. If empty, the value @samp{$(top_builddir)/intl/} is used. diff --git a/intl/ChangeLog b/intl/ChangeLog index bc2120434..af4cfd3f9 100644 --- a/intl/ChangeLog +++ b/intl/ChangeLog @@ -1,3 +1,26 @@ +2002-07-21 Bruno Haible + + * libgnuintl.h (__GNU_GETTEXT_SUPPORTED_REVISION): New macro. + * gmo.h (struct mo_file_header): New fields n_sysdep_segments, + sysdep_segments_offset, n_sysdep_strings, orig_sysdep_tab_offset, + trans_sysdep_tab_offset. + (struct sysdep_segment): New type. + (struct sysdep_string): New type. + (SEGMENTS_END): New macro. + * gettextP.h (struct sysdep_string_desc): New type. + (struct loaded_domain): New fields malloced, n_sysdep_strings, + orig_sysdep_tab, trans_sysdep_tab, must_swap_hash_tab. Make fields + orig_tab, trans_tab, hash_tab to const pointers because they point + into read-only memory. + * loadmsgcat.c: Include stdint.h, inttypes.h, hash-string.h. + (PRI*): Define fallback values. + (get_sysdep_segment_value): New function. + (_nl_load_domain): Distinguish major and minor revision parts. Add + support for minor revision 1 with system dependent strings. + (_nl_unload_domain): Also free the 'malloced' field. + * dcigettext.c (_nl_find_msg): Remove test for domain->hash_size, now + done in loadmsgcat.c. Add support for system dependent strings. + 2002-07-17 Bruno Haible * gettext-0.11.3 released. diff --git a/intl/dcigettext.c b/intl/dcigettext.c index bec5dd33e..f2f01524c 100644 --- a/intl/dcigettext.c +++ b/intl/dcigettext.c @@ -697,6 +697,7 @@ _nl_find_msg (domain_file, domainbinding, msgid, lengthp) size_t *lengthp; { struct loaded_domain *domain; + nls_uint32 nstrings; size_t act; char *result; size_t resultlen; @@ -709,8 +710,10 @@ _nl_find_msg (domain_file, domainbinding, msgid, lengthp) domain = (struct loaded_domain *) domain_file->data; + nstrings = domain->nstrings; + /* Locate the MSGID and its translation. */ - if (domain->hash_size > 2 && domain->hash_tab != NULL) + if (domain->hash_tab != NULL) { /* Use the hashing table. */ nls_uint32 len = strlen (msgid); @@ -720,22 +723,30 @@ _nl_find_msg (domain_file, domainbinding, msgid, lengthp) while (1) { - nls_uint32 nstr = W (domain->must_swap, domain->hash_tab[idx]); + nls_uint32 nstr = + W (domain->must_swap_hash_tab, domain->hash_tab[idx]); if (nstr == 0) /* Hash table entry is empty. */ return NULL; - /* Compare msgid with the original string at index nstr-1. + nstr--; + + /* Compare msgid with the original string at index nstr. We compare the lengths with >=, not ==, because plural entries are represented by strings with an embedded NUL. */ - if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) >= len - && (strcmp (msgid, - domain->data + W (domain->must_swap, - domain->orig_tab[nstr - 1].offset)) - == 0)) + if (nstr < nstrings + ? W (domain->must_swap, domain->orig_tab[nstr].length) >= len + && (strcmp (msgid, + domain->data + W (domain->must_swap, + domain->orig_tab[nstr].offset)) + == 0) + : domain->orig_sysdep_tab[nstr - nstrings].length > len + && (strcmp (msgid, + domain->orig_sysdep_tab[nstr - nstrings].pointer) + == 0)) { - act = nstr - 1; + act = nstr; goto found; } @@ -753,7 +764,7 @@ _nl_find_msg (domain_file, domainbinding, msgid, lengthp) size_t top, bottom; bottom = 0; - top = domain->nstrings; + top = nstrings; while (bottom < top) { int cmp_val; @@ -776,9 +787,17 @@ _nl_find_msg (domain_file, domainbinding, msgid, lengthp) found: /* The translation was found at index ACT. If we have to convert the string to use a different character set, this is the time. */ - result = ((char *) domain->data - + W (domain->must_swap, domain->trans_tab[act].offset)); - resultlen = W (domain->must_swap, domain->trans_tab[act].length) + 1; + if (act < nstrings) + { + result = (char *) + (domain->data + W (domain->must_swap, domain->trans_tab[act].offset)); + resultlen = W (domain->must_swap, domain->trans_tab[act].length) + 1; + } + else + { + result = (char *) domain->trans_sysdep_tab[act - nstrings].pointer; + resultlen = domain->trans_sysdep_tab[act - nstrings].length; + } #if defined _LIBC || HAVE_ICONV if (domain->codeset_cntr @@ -811,8 +830,9 @@ _nl_find_msg (domain_file, domainbinding, msgid, lengthp) NULs. */ if (domain->conv_tab == NULL - && ((domain->conv_tab = (char **) calloc (domain->nstrings, - sizeof (char *))) + && ((domain->conv_tab = + (char **) calloc (nstrings + domain->n_sysdep_strings, + sizeof (char *))) == NULL)) /* Mark that we didn't succeed allocating a table. */ domain->conv_tab = (char **) -1; diff --git a/intl/gettextP.h b/intl/gettextP.h index 0fff5a9e9..f085c59bb 100644 --- a/intl/gettextP.h +++ b/intl/gettextP.h @@ -76,18 +76,50 @@ SWAP (i) #endif +/* In-memory representation of system dependent string. */ +struct sysdep_string_desc +{ + /* Length of addressed string, including the trailing NUL. */ + size_t length; + /* Pointer to addressed string. */ + const char *pointer; +}; + /* The representation of an opened message catalog. */ struct loaded_domain { + /* Pointer to memory containing the .mo file. */ const char *data; + /* 1 if the memory is mmap()ed, 0 if the memory is malloc()ed. */ int use_mmap; + /* Size of mmap()ed memory. */ size_t mmap_size; + /* 1 if the .mo file uses a different endianness than this machine. */ int must_swap; + /* Pointer to additional malloc()ed memory. */ + void *malloced; + + /* Number of static strings pairs. */ nls_uint32 nstrings; - struct string_desc *orig_tab; - struct string_desc *trans_tab; + /* Pointer to descriptors of original strings in the file. */ + const struct string_desc *orig_tab; + /* Pointer to descriptors of translated strings in the file. */ + const struct string_desc *trans_tab; + + /* Number of system dependent strings pairs. */ + nls_uint32 n_sysdep_strings; + /* Pointer to descriptors of original sysdep strings. */ + const struct sysdep_string_desc *orig_sysdep_tab; + /* Pointer to descriptors of translated sysdep strings. */ + const struct sysdep_string_desc *trans_sysdep_tab; + + /* Size of hash table. */ nls_uint32 hash_size; - nls_uint32 *hash_tab; + /* Pointer to hash table. */ + const nls_uint32 *hash_tab; + /* 1 if the hash table uses a different endianness than this machine. */ + int must_swap_hash_tab; + int codeset_cntr; #ifdef _LIBC __gconv_t conv; diff --git a/intl/gmo.h b/intl/gmo.h index f05ae470c..d1fe4d6b8 100644 --- a/intl/gmo.h +++ b/intl/gmo.h @@ -1,5 +1,5 @@ /* Description of GNU message catalog format: general file layout. - Copyright (C) 1995, 1997, 2000, 2001 Free Software Foundation, Inc. + Copyright (C) 1995, 1997, 2000-2002 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published @@ -75,26 +75,74 @@ struct mo_file_header nls_uint32 magic; /* The revision number of the file format. */ nls_uint32 revision; + + /* The following are only used in .mo files with major revision 0. */ + /* The number of strings pairs. */ nls_uint32 nstrings; /* Offset of table with start offsets of original strings. */ nls_uint32 orig_tab_offset; - /* Offset of table with start offsets of translation strings. */ + /* Offset of table with start offsets of translated strings. */ nls_uint32 trans_tab_offset; - /* Size of hashing table. */ + /* Size of hash table. */ nls_uint32 hash_tab_size; - /* Offset of first hashing entry. */ + /* Offset of first hash table entry. */ nls_uint32 hash_tab_offset; + + /* The following are only used in .mo files with minor revision >= 1. */ + + /* The number of system dependent segments. */ + nls_uint32 n_sysdep_segments; + /* Offset of table describing system dependent segments. */ + nls_uint32 sysdep_segments_offset; + /* The number of system dependent strings pairs. */ + nls_uint32 n_sysdep_strings; + /* Offset of table with start offsets of original sysdep strings. */ + nls_uint32 orig_sysdep_tab_offset; + /* Offset of table with start offsets of translated sysdep strings. */ + nls_uint32 trans_sysdep_tab_offset; }; +/* Descriptor for static string contained in the binary .mo file. */ struct string_desc { - /* Length of addressed string. */ + /* Length of addressed string, not including the trailing NUL. */ nls_uint32 length; /* Offset of string in file. */ nls_uint32 offset; }; +/* The following are only used in .mo files with minor revision >= 1. */ + +/* Descriptor for system dependent string segment. */ +struct sysdep_segment +{ + /* Length of addressed string, including the trailing NUL. */ + nls_uint32 length; + /* Offset of string in file. */ + nls_uint32 offset; +}; + +/* Descriptor for system dependent string. */ +struct sysdep_string +{ + /* Offset of static string segments in file. */ + nls_uint32 offset; + /* Alternating sequence of static and system dependent segments. + The last segment is a static segment, including the trailing NUL. */ + struct segment_pair + { + /* Size of static segment. */ + nls_uint32 segsize; + /* Reference to system dependent string segment, or ~0 at the end. */ + nls_uint32 sysdepref; + } segments[1]; +}; + +/* Marker for the end of the segments[] array. This has the value 0xFFFFFFFF, + regardless whether 'int' is 16 bit, 32 bit, or 64 bit. */ +#define SEGMENTS_END ((nls_uint32) ~0) + /* @@ begin of epilog @@ */ #endif /* gettext.h */ diff --git a/intl/libgnuintl.h b/intl/libgnuintl.h index bbb336106..ed25fe9f5 100644 --- a/intl/libgnuintl.h +++ b/intl/libgnuintl.h @@ -36,6 +36,11 @@ implementation of gettext. */ #define __USE_GNU_GETTEXT 1 +/* Provide information about the supported file formats. Returns the + maximum minor revision number supported for a given major revision. */ +#define __GNU_GETTEXT_SUPPORTED_REVISION(major) \ + ((major) == 0 ? 1 : -1) + /* Resolve a platform specific conflict on DJGPP. GNU gettext takes precedence over _conio_gettext. */ #ifdef __DJGPP__ diff --git a/intl/loadmsgcat.c b/intl/loadmsgcat.c index c01451b23..a97faad9d 100644 --- a/intl/loadmsgcat.c +++ b/intl/loadmsgcat.c @@ -71,14 +71,296 @@ char *alloca (); # undef HAVE_MMAP #endif +#if defined HAVE_STDINT_H_WITH_UINTMAX || defined _LIBC +# include +#endif +#if defined HAVE_INTTYPES_H || defined _LIBC +# include +#endif + #include "gmo.h" #include "gettextP.h" +#include "hash-string.h" #include "plural-exp.h" #ifdef _LIBC # include "../locale/localeinfo.h" #endif +/* Provide fallback values for macros that ought to be defined in . + Note that our fallback values need not be literal strings, because we don't + use them with preprocessor string concatenation. */ +#ifndef PRId8 +# define PRId8 "d" +#endif +#ifndef PRIi8 +# define PRIi8 "i" +#endif +#ifndef PRIo8 +# define PRIo8 "o" +#endif +#ifndef PRIu8 +# define PRIu8 "u" +#endif +#ifndef PRIx8 +# define PRIx8 "x" +#endif +#ifndef PRIX8 +# define PRIX8 "X" +#endif +#ifndef PRId16 +# define PRId16 "d" +#endif +#ifndef PRIi16 +# define PRIi16 "i" +#endif +#ifndef PRIo16 +# define PRIo16 "o" +#endif +#ifndef PRIu16 +# define PRIu16 "u" +#endif +#ifndef PRIx16 +# define PRIx16 "x" +#endif +#ifndef PRIX16 +# define PRIX16 "X" +#endif +#ifndef PRId32 +# define PRId32 "d" +#endif +#ifndef PRIi32 +# define PRIi32 "i" +#endif +#ifndef PRIo32 +# define PRIo32 "o" +#endif +#ifndef PRIu32 +# define PRIu32 "u" +#endif +#ifndef PRIx32 +# define PRIx32 "x" +#endif +#ifndef PRIX32 +# define PRIX32 "X" +#endif +#ifndef PRId64 +# define PRId64 (sizeof (long) == 8 ? "ld" : "lld") +#endif +#ifndef PRIi64 +# define PRIi64 (sizeof (long) == 8 ? "li" : "lli") +#endif +#ifndef PRIo64 +# define PRIo64 (sizeof (long) == 8 ? "lo" : "llo") +#endif +#ifndef PRIu64 +# define PRIu64 (sizeof (long) == 8 ? "lu" : "llu") +#endif +#ifndef PRIx64 +# define PRIx64 (sizeof (long) == 8 ? "lx" : "llx") +#endif +#ifndef PRIX64 +# define PRIX64 (sizeof (long) == 8 ? "lX" : "llX") +#endif +#ifndef PRIdLEAST8 +# define PRIdLEAST8 "d" +#endif +#ifndef PRIiLEAST8 +# define PRIiLEAST8 "i" +#endif +#ifndef PRIoLEAST8 +# define PRIoLEAST8 "o" +#endif +#ifndef PRIuLEAST8 +# define PRIuLEAST8 "u" +#endif +#ifndef PRIxLEAST8 +# define PRIxLEAST8 "x" +#endif +#ifndef PRIXLEAST8 +# define PRIXLEAST8 "X" +#endif +#ifndef PRIdLEAST16 +# define PRIdLEAST16 "d" +#endif +#ifndef PRIiLEAST16 +# define PRIiLEAST16 "i" +#endif +#ifndef PRIoLEAST16 +# define PRIoLEAST16 "o" +#endif +#ifndef PRIuLEAST16 +# define PRIuLEAST16 "u" +#endif +#ifndef PRIxLEAST16 +# define PRIxLEAST16 "x" +#endif +#ifndef PRIXLEAST16 +# define PRIXLEAST16 "X" +#endif +#ifndef PRIdLEAST32 +# define PRIdLEAST32 "d" +#endif +#ifndef PRIiLEAST32 +# define PRIiLEAST32 "i" +#endif +#ifndef PRIoLEAST32 +# define PRIoLEAST32 "o" +#endif +#ifndef PRIuLEAST32 +# define PRIuLEAST32 "u" +#endif +#ifndef PRIxLEAST32 +# define PRIxLEAST32 "x" +#endif +#ifndef PRIXLEAST32 +# define PRIXLEAST32 "X" +#endif +#ifndef PRIdLEAST64 +# define PRIdLEAST64 PRId64 +#endif +#ifndef PRIiLEAST64 +# define PRIiLEAST64 PRIi64 +#endif +#ifndef PRIoLEAST64 +# define PRIoLEAST64 PRIo64 +#endif +#ifndef PRIuLEAST64 +# define PRIuLEAST64 PRIu64 +#endif +#ifndef PRIxLEAST64 +# define PRIxLEAST64 PRIx64 +#endif +#ifndef PRIXLEAST64 +# define PRIXLEAST64 PRIX64 +#endif +#ifndef PRIdFAST8 +# define PRIdFAST8 "d" +#endif +#ifndef PRIiFAST8 +# define PRIiFAST8 "i" +#endif +#ifndef PRIoFAST8 +# define PRIoFAST8 "o" +#endif +#ifndef PRIuFAST8 +# define PRIuFAST8 "u" +#endif +#ifndef PRIxFAST8 +# define PRIxFAST8 "x" +#endif +#ifndef PRIXFAST8 +# define PRIXFAST8 "X" +#endif +#ifndef PRIdFAST16 +# define PRIdFAST16 "d" +#endif +#ifndef PRIiFAST16 +# define PRIiFAST16 "i" +#endif +#ifndef PRIoFAST16 +# define PRIoFAST16 "o" +#endif +#ifndef PRIuFAST16 +# define PRIuFAST16 "u" +#endif +#ifndef PRIxFAST16 +# define PRIxFAST16 "x" +#endif +#ifndef PRIXFAST16 +# define PRIXFAST16 "X" +#endif +#ifndef PRIdFAST32 +# define PRIdFAST32 "d" +#endif +#ifndef PRIiFAST32 +# define PRIiFAST32 "i" +#endif +#ifndef PRIoFAST32 +# define PRIoFAST32 "o" +#endif +#ifndef PRIuFAST32 +# define PRIuFAST32 "u" +#endif +#ifndef PRIxFAST32 +# define PRIxFAST32 "x" +#endif +#ifndef PRIXFAST32 +# define PRIXFAST32 "X" +#endif +#ifndef PRIdFAST64 +# define PRIdFAST64 PRId64 +#endif +#ifndef PRIiFAST64 +# define PRIiFAST64 PRIi64 +#endif +#ifndef PRIoFAST64 +# define PRIoFAST64 PRIo64 +#endif +#ifndef PRIuFAST64 +# define PRIuFAST64 PRIu64 +#endif +#ifndef PRIxFAST64 +# define PRIxFAST64 PRIx64 +#endif +#ifndef PRIXFAST64 +# define PRIXFAST64 PRIX64 +#endif +#ifndef PRIdMAX +# define PRIdMAX (sizeof (uintmax_t) == sizeof (long) ? "ld" : "lld") +#endif +#ifndef PRIiMAX +# define PRIiMAX (sizeof (uintmax_t) == sizeof (long) ? "li" : "lli") +#endif +#ifndef PRIoMAX +# define PRIoMAX (sizeof (uintmax_t) == sizeof (long) ? "lo" : "llo") +#endif +#ifndef PRIuMAX +# define PRIuMAX (sizeof (uintmax_t) == sizeof (long) ? "lu" : "llu") +#endif +#ifndef PRIxMAX +# define PRIxMAX (sizeof (uintmax_t) == sizeof (long) ? "lx" : "llx") +#endif +#ifndef PRIXMAX +# define PRIXMAX (sizeof (uintmax_t) == sizeof (long) ? "lX" : "llX") +#endif +#ifndef PRIdPTR +# define PRIdPTR \ + (sizeof (void *) == sizeof (long) ? "ld" : \ + sizeof (void *) == sizeof (int) ? "d" : \ + "lld") +#endif +#ifndef PRIiPTR +# define PRIiPTR \ + (sizeof (void *) == sizeof (long) ? "li" : \ + sizeof (void *) == sizeof (int) ? "i" : \ + "lli") +#endif +#ifndef PRIoPTR +# define PRIoPTR \ + (sizeof (void *) == sizeof (long) ? "lo" : \ + sizeof (void *) == sizeof (int) ? "o" : \ + "llo") +#endif +#ifndef PRIuPTR +# define PRIuPTR \ + (sizeof (void *) == sizeof (long) ? "lu" : \ + sizeof (void *) == sizeof (int) ? "u" : \ + "llu") +#endif +#ifndef PRIxPTR +# define PRIxPTR \ + (sizeof (void *) == sizeof (long) ? "lx" : \ + sizeof (void *) == sizeof (int) ? "x" : \ + "llx") +#endif +#ifndef PRIXPTR +# define PRIXPTR \ + (sizeof (void *) == sizeof (long) ? "lX" : \ + sizeof (void *) == sizeof (int) ? "X" : \ + "llX") +#endif + /* @@ end of prolog @@ */ #ifdef _LIBC @@ -118,12 +400,274 @@ char *alloca (); # define O_BINARY 0 #endif + +/* Prototypes for local functions. Needed to ensure compiler checking of + function argument counts despite of K&R C function definition syntax. */ +static const char *get_sysdep_segment_value PARAMS ((const char *name)); + + /* We need a sign, whether a new catalog was loaded, which can be associated with all translations. This is important if the translations are cached by one of GCC's features. */ int _nl_msg_cat_cntr; +/* Expand a system dependent string segment. Return NULL if unsupported. */ +static const char * +get_sysdep_segment_value (name) + const char *name; +{ + /* Test for an ISO C 99 section 7.8.1 format string directive. + Syntax: + P R I { d | i | o | u | x | X } + { { | LEAST | FAST } { 8 | 16 | 32 | 64 } | MAX | PTR } */ + /* We don't use a table of 14 times 6 'const char *' strings here, because + data relocations cost startup time. */ + if (name[0] == 'P' && name[1] == 'R' && name[2] == 'I') + { + if (name[3] == 'd' || name[3] == 'i' || name[3] == 'o' || name[3] == 'u' + || name[3] == 'x' || name[3] == 'X') + { + if (name[4] == '8' && name[5] == '\0') + { + if (name[3] == 'd') + return PRId8; + if (name[3] == 'i') + return PRIi8; + if (name[3] == 'o') + return PRIo8; + if (name[3] == 'u') + return PRIu8; + if (name[3] == 'x') + return PRIx8; + if (name[3] == 'X') + return PRIX8; + abort (); + } + if (name[4] == '1' && name[5] == '6' && name[6] == '\0') + { + if (name[3] == 'd') + return PRId16; + if (name[3] == 'i') + return PRIi16; + if (name[3] == 'o') + return PRIo16; + if (name[3] == 'u') + return PRIu16; + if (name[3] == 'x') + return PRIx16; + if (name[3] == 'X') + return PRIX16; + abort (); + } + if (name[4] == '3' && name[5] == '2' && name[6] == '\0') + { + if (name[3] == 'd') + return PRId32; + if (name[3] == 'i') + return PRIi32; + if (name[3] == 'o') + return PRIo32; + if (name[3] == 'u') + return PRIu32; + if (name[3] == 'x') + return PRIx32; + if (name[3] == 'X') + return PRIX32; + abort (); + } + if (name[4] == '6' && name[5] == '4' && name[6] == '\0') + { + if (name[3] == 'd') + return PRId64; + if (name[3] == 'i') + return PRIi64; + if (name[3] == 'o') + return PRIo64; + if (name[3] == 'u') + return PRIu64; + if (name[3] == 'x') + return PRIx64; + if (name[3] == 'X') + return PRIX64; + abort (); + } + if (name[4] == 'L' && name[5] == 'E' && name[6] == 'A' + && name[7] == 'S' && name[8] == 'T') + { + if (name[9] == '8' && name[10] == '\0') + { + if (name[3] == 'd') + return PRIdLEAST8; + if (name[3] == 'i') + return PRIiLEAST8; + if (name[3] == 'o') + return PRIoLEAST8; + if (name[3] == 'u') + return PRIuLEAST8; + if (name[3] == 'x') + return PRIxLEAST8; + if (name[3] == 'X') + return PRIXLEAST8; + abort (); + } + if (name[9] == '1' && name[10] == '6' && name[11] == '\0') + { + if (name[3] == 'd') + return PRIdLEAST16; + if (name[3] == 'i') + return PRIiLEAST16; + if (name[3] == 'o') + return PRIoLEAST16; + if (name[3] == 'u') + return PRIuLEAST16; + if (name[3] == 'x') + return PRIxLEAST16; + if (name[3] == 'X') + return PRIXLEAST16; + abort (); + } + if (name[9] == '3' && name[10] == '2' && name[11] == '\0') + { + if (name[3] == 'd') + return PRIdLEAST32; + if (name[3] == 'i') + return PRIiLEAST32; + if (name[3] == 'o') + return PRIoLEAST32; + if (name[3] == 'u') + return PRIuLEAST32; + if (name[3] == 'x') + return PRIxLEAST32; + if (name[3] == 'X') + return PRIXLEAST32; + abort (); + } + if (name[9] == '6' && name[10] == '4' && name[11] == '\0') + { + if (name[3] == 'd') + return PRIdLEAST64; + if (name[3] == 'i') + return PRIiLEAST64; + if (name[3] == 'o') + return PRIoLEAST64; + if (name[3] == 'u') + return PRIuLEAST64; + if (name[3] == 'x') + return PRIxLEAST64; + if (name[3] == 'X') + return PRIXLEAST64; + abort (); + } + } + if (name[4] == 'F' && name[5] == 'A' && name[6] == 'S' + && name[7] == 'T') + { + if (name[8] == '8' && name[9] == '\0') + { + if (name[3] == 'd') + return PRIdFAST8; + if (name[3] == 'i') + return PRIiFAST8; + if (name[3] == 'o') + return PRIoFAST8; + if (name[3] == 'u') + return PRIuFAST8; + if (name[3] == 'x') + return PRIxFAST8; + if (name[3] == 'X') + return PRIXFAST8; + abort (); + } + if (name[8] == '1' && name[9] == '6' && name[10] == '\0') + { + if (name[3] == 'd') + return PRIdFAST16; + if (name[3] == 'i') + return PRIiFAST16; + if (name[3] == 'o') + return PRIoFAST16; + if (name[3] == 'u') + return PRIuFAST16; + if (name[3] == 'x') + return PRIxFAST16; + if (name[3] == 'X') + return PRIXFAST16; + abort (); + } + if (name[8] == '3' && name[9] == '2' && name[10] == '\0') + { + if (name[3] == 'd') + return PRIdFAST32; + if (name[3] == 'i') + return PRIiFAST32; + if (name[3] == 'o') + return PRIoFAST32; + if (name[3] == 'u') + return PRIuFAST32; + if (name[3] == 'x') + return PRIxFAST32; + if (name[3] == 'X') + return PRIXFAST32; + abort (); + } + if (name[8] == '6' && name[9] == '4' && name[10] == '\0') + { + if (name[3] == 'd') + return PRIdFAST64; + if (name[3] == 'i') + return PRIiFAST64; + if (name[3] == 'o') + return PRIoFAST64; + if (name[3] == 'u') + return PRIuFAST64; + if (name[3] == 'x') + return PRIxFAST64; + if (name[3] == 'X') + return PRIXFAST64; + abort (); + } + } + if (name[4] == 'M' && name[5] == 'A' && name[6] == 'X' + && name[7] == '\0') + { + if (name[3] == 'd') + return PRIdMAX; + if (name[3] == 'i') + return PRIiMAX; + if (name[3] == 'o') + return PRIoMAX; + if (name[3] == 'u') + return PRIuMAX; + if (name[3] == 'x') + return PRIxMAX; + if (name[3] == 'X') + return PRIXMAX; + abort (); + } + if (name[4] == 'P' && name[5] == 'T' && name[6] == 'R' + && name[7] == '\0') + { + if (name[3] == 'd') + return PRIdPTR; + if (name[3] == 'i') + return PRIiPTR; + if (name[3] == 'o') + return PRIoPTR; + if (name[3] == 'u') + return PRIuPTR; + if (name[3] == 'x') + return PRIxPTR; + if (name[3] == 'X') + return PRIXPTR; + abort (); + } + } + } + /* Other system dependent strings are not valid. */ + return NULL; +} + /* Initialize the codeset dependent parts of an opened message catalog. Return the header entry. */ const char * @@ -282,6 +826,7 @@ _nl_load_domain (domain_file, domainbinding) struct mo_file_header *data = (struct mo_file_header *) -1; int use_mmap = 0; struct loaded_domain *domain; + int revision; const char *nullentry; domain_file->decided = 1; @@ -389,22 +934,257 @@ _nl_load_domain (domain_file, domainbinding) domain->use_mmap = use_mmap; domain->mmap_size = size; domain->must_swap = data->magic != _MAGIC; + domain->malloced = NULL; /* Fill in the information about the available tables. */ - switch (W (domain->must_swap, data->revision)) + revision = W (domain->must_swap, data->revision); + /* We support only the major revision 0. */ + switch (revision >> 16) { case 0: domain->nstrings = W (domain->must_swap, data->nstrings); - domain->orig_tab = (struct string_desc *) + domain->orig_tab = (const struct string_desc *) ((char *) data + W (domain->must_swap, data->orig_tab_offset)); - domain->trans_tab = (struct string_desc *) + domain->trans_tab = (const struct string_desc *) ((char *) data + W (domain->must_swap, data->trans_tab_offset)); domain->hash_size = W (domain->must_swap, data->hash_tab_size); - domain->hash_tab = (nls_uint32 *) - ((char *) data + W (domain->must_swap, data->hash_tab_offset)); + domain->hash_tab = + (domain->hash_size > 2 + ? (const nls_uint32 *) + ((char *) data + W (domain->must_swap, data->hash_tab_offset)) + : NULL); + domain->must_swap_hash_tab = domain->must_swap; + + /* Now dispatch on the minor revision. */ + switch (revision & 0xffff) + { + case 0: + domain->n_sysdep_strings = 0; + domain->orig_sysdep_tab = NULL; + domain->trans_sysdep_tab = NULL; + break; + case 1: + default: + { + nls_uint32 n_sysdep_strings; + + if (domain->hash_tab == NULL) + /* This is invalid. These minor revisions need a hash table. */ + goto invalid; + + n_sysdep_strings = + W (domain->must_swap, data->n_sysdep_strings); + if (n_sysdep_strings > 0) + { + nls_uint32 n_sysdep_segments; + const struct sysdep_segment *sysdep_segments; + const char **sysdep_segment_values; + const nls_uint32 *orig_sysdep_tab; + const nls_uint32 *trans_sysdep_tab; + size_t memneed; + char *mem; + struct sysdep_string_desc *inmem_orig_sysdep_tab; + struct sysdep_string_desc *inmem_trans_sysdep_tab; + nls_uint32 *inmem_hash_tab; + unsigned int i; + + /* Get the values of the system dependent segments. */ + n_sysdep_segments = + W (domain->must_swap, data->n_sysdep_segments); + sysdep_segments = (const struct sysdep_segment *) + ((char *) data + + W (domain->must_swap, data->sysdep_segments_offset)); + sysdep_segment_values = + alloca (n_sysdep_segments * sizeof (const char *)); + for (i = 0; i < n_sysdep_segments; i++) + { + const char *name = + (char *) data + + W (domain->must_swap, sysdep_segments[i].offset); + nls_uint32 namelen = + W (domain->must_swap, sysdep_segments[i].length); + + if (!(namelen > 0 && name[namelen - 1] == '\0')) + { + freea (sysdep_segment_values); + goto invalid; + } + + sysdep_segment_values[i] = get_sysdep_segment_value (name); + } + + orig_sysdep_tab = (const nls_uint32 *) + ((char *) data + + W (domain->must_swap, data->orig_sysdep_tab_offset)); + trans_sysdep_tab = (const nls_uint32 *) + ((char *) data + + W (domain->must_swap, data->trans_sysdep_tab_offset)); + + /* Compute the amount of additional memory needed for the + system dependent strings and the augmented hash table. */ + memneed = 2 * n_sysdep_strings + * sizeof (struct sysdep_string_desc) + + domain->hash_size * sizeof (nls_uint32); + for (i = 0; i < 2 * n_sysdep_strings; i++) + { + const struct sysdep_string *sysdep_string = + (const struct sysdep_string *) + ((char *) data + + W (domain->must_swap, + i < n_sysdep_strings + ? orig_sysdep_tab[i] + : trans_sysdep_tab[i - n_sysdep_strings])); + size_t need = 0; + const struct segment_pair *p = sysdep_string->segments; + + if (W (domain->must_swap, p->sysdepref) != SEGMENTS_END) + for (p = sysdep_string->segments;; p++) + { + nls_uint32 sysdepref; + + need += W (domain->must_swap, p->segsize); + + sysdepref = W (domain->must_swap, p->sysdepref); + if (sysdepref == SEGMENTS_END) + break; + + if (sysdepref >= n_sysdep_segments) + { + /* Invalid. */ + freea (sysdep_segment_values); + goto invalid; + } + + need += strlen (sysdep_segment_values[sysdepref]); + } + + memneed += need; + } + + /* Allocate additional memory. */ + mem = (char *) malloc (memneed); + if (mem == NULL) + goto invalid; + + domain->malloced = mem; + inmem_orig_sysdep_tab = (struct sysdep_string_desc *) mem; + mem += n_sysdep_strings * sizeof (struct sysdep_string_desc); + inmem_trans_sysdep_tab = (struct sysdep_string_desc *) mem; + mem += n_sysdep_strings * sizeof (struct sysdep_string_desc); + inmem_hash_tab = (nls_uint32 *) mem; + mem += domain->hash_size * sizeof (nls_uint32); + + /* Compute the system dependent strings. */ + for (i = 0; i < 2 * n_sysdep_strings; i++) + { + const struct sysdep_string *sysdep_string = + (const struct sysdep_string *) + ((char *) data + + W (domain->must_swap, + i < n_sysdep_strings + ? orig_sysdep_tab[i] + : trans_sysdep_tab[i - n_sysdep_strings])); + const char *static_segments = + (char *) data + + W (domain->must_swap, sysdep_string->offset); + const struct segment_pair *p = sysdep_string->segments; + + /* Concatenate the segments, and fill + inmem_orig_sysdep_tab[i] (for i < n_sysdep_strings) and + inmem_trans_sysdep_tab[i-n_sysdep_strings] (for + i >= n_sysdep_strings). */ + + if (W (domain->must_swap, p->sysdepref) == SEGMENTS_END) + { + /* Only one static segment. */ + inmem_orig_sysdep_tab[i].length = + W (domain->must_swap, p->segsize); + inmem_orig_sysdep_tab[i].pointer = static_segments; + } + else + { + inmem_orig_sysdep_tab[i].pointer = mem; + + for (p = sysdep_string->segments;; p++) + { + nls_uint32 segsize = + W (domain->must_swap, p->segsize); + nls_uint32 sysdepref = + W (domain->must_swap, p->sysdepref); + size_t n; + + if (segsize > 0) + { + memcpy (mem, static_segments, segsize); + mem += segsize; + static_segments += segsize; + } + + if (sysdepref == SEGMENTS_END) + break; + + n = strlen (sysdep_segment_values[sysdepref]); + memcpy (mem, sysdep_segment_values[sysdepref], n); + mem += n; + } + + inmem_orig_sysdep_tab[i].length = + mem - inmem_orig_sysdep_tab[i].pointer; + } + } + + /* Compute the augmented hash table. */ + for (i = 0; i < domain->hash_size; i++) + inmem_hash_tab[i] = + W (domain->must_swap_hash_tab, domain->hash_tab[i]); + for (i = 0; i < n_sysdep_strings; i++) + { + const char *msgid = inmem_orig_sysdep_tab[i].pointer; + nls_uint32 hash_val = hash_string (msgid); + nls_uint32 idx = hash_val % domain->hash_size; + nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2)); + + for (;;) + { + if (inmem_hash_tab[idx] == 0) + { + /* Hash table entry is empty. Use it. */ + inmem_hash_tab[idx] = 1 + domain->nstrings + i; + break; + } + + if (idx >= domain->hash_size - incr) + idx -= domain->hash_size - incr; + else + idx += incr; + } + } + + freea (sysdep_segment_values); + + domain->n_sysdep_strings = n_sysdep_strings; + domain->orig_sysdep_tab = inmem_orig_sysdep_tab; + domain->trans_sysdep_tab = inmem_trans_sysdep_tab; + + domain->hash_tab = inmem_hash_tab; + domain->must_swap_hash_tab = 0; + } + else + { + domain->n_sysdep_strings = 0; + domain->orig_sysdep_tab = NULL; + domain->trans_sysdep_tab = NULL; + } + } + break; + } break; default: /* This is an invalid revision. */ + invalid: + /* This is an invalid .mo file. */ + if (domain->malloced) + free (domain->malloced); #ifdef HAVE_MMAP if (use_mmap) munmap ((caddr_t) data, size); @@ -437,6 +1217,9 @@ _nl_unload_domain (domain) _nl_free_domain_conv (domain); + if (domain->malloced) + free (domain->malloced); + # ifdef _POSIX_MAPPED_FILES if (domain->use_mmap) munmap ((caddr_t) domain->data, domain->mmap_size); diff --git a/lib/ChangeLog b/lib/ChangeLog index d033ff13f..dc51e93cc 100644 --- a/lib/ChangeLog +++ b/lib/ChangeLog @@ -1,3 +1,8 @@ +2002-07-21 Bruno Haible + + * mkdtemp.c: Test HAVE_STDINT_H_WITH_UINTMAX instead of HAVE_STDINT_H. + Test HAVE_INTTYPES_H_WITH_UINTMAX instead of HAVE_INTTYPES_H. + 2002-07-17 Bruno Haible * gettext-0.11.3 released. diff --git a/lib/mkdtemp.c b/lib/mkdtemp.c index dcdfdc150..15081020d 100644 --- a/lib/mkdtemp.c +++ b/lib/mkdtemp.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1999, 2001 Free Software Foundation, Inc. +/* Copyright (C) 1999, 2001-2002 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -39,11 +39,11 @@ # define TMP_MAX 238328 #endif -#if HAVE_STDINT_H || _LIBC +#if HAVE_STDINT_H_WITH_UINTMAX || _LIBC # include #endif -#if HAVE_INTTYPES_H || _LIBC +#if HAVE_INTTYPES_H_WITH_UINTMAX || _LIBC # include #endif diff --git a/m4/ChangeLog b/m4/ChangeLog index 1a37b3cd7..f558c3a6b 100644 --- a/m4/ChangeLog +++ b/m4/ChangeLog @@ -1,3 +1,17 @@ +2002-07-21 Bruno Haible + + * stdint_h.m4 (jm_AC_HEADER_STDINT_H): Define + HAVE_STDINT_H_WITH_UINTMAX instead of HAVE_STDINT_H. + * inttypes_h.m4 (jm_AC_HEADER_INTTYPES_H): Define + HAVE_INTTYPES_H_WITH_UINTMAX instead of HAVE_INTTYPES_H. + * inttypes.m4: New file. + * gettext.m4 (AM_GNU_GETTEXT): Accept needsymbol = + need-formatstring-macros. + (AM_INTL_SUBDIR): Require jm_AC_TYPE_UINTMAX_T, gt_HEADER_INTTYPES_H. + * Makefile.am (aclocal_DATA): Add inttypes.m4, inttypes_h.m4, + stdint_h.m4, uintmax_t.m4. + (EXTRA_DIST): Add inttypes.m4. + 2002-07-19 Bruno Haible * gettext.m4 (AM_GNU_GETTEXT_VERSION): New macro. diff --git a/m4/Makefile.am b/m4/Makefile.am index 53203cab4..c81386b62 100644 --- a/m4/Makefile.am +++ b/m4/Makefile.am @@ -1,14 +1,14 @@ ## Process this file with automake to produce Makefile.in -*-Makefile-*- aclocaldir = @aclocaldir@ -aclocal_DATA = codeset.m4 gettext.m4 glibc21.m4 iconv.m4 intdiv0.m4 isc-posix.m4 lib-ld.m4 lib-link.m4 lib-prefix.m4 lcmessage.m4 progtest.m4 +aclocal_DATA = codeset.m4 gettext.m4 glibc21.m4 iconv.m4 intdiv0.m4 inttypes.m4 inttypes_h.m4 isc-posix.m4 lib-ld.m4 lib-link.m4 lib-prefix.m4 lcmessage.m4 progtest.m4 stdint_h.m4 uintmax_t.m4 # Generate this list with # find . -type f -name '*.m4' -printf '%f\n'|sort |fmt |tr '\012' @ \ # |sed 's/@$/%/;s/@/ \\@/g' |tr @% '\012\012' EXTRA_DIST = README \ backupfile.m4 c-bs-a.m4 codeset.m4 error.m4 flex.m4 fnmatch.m4 gcj.m4 \ -getline.m4 gettext.m4 glibc21.m4 hostname.m4 iconv.m4 intdiv0.m4 \ +getline.m4 gettext.m4 glibc21.m4 hostname.m4 iconv.m4 intdiv0.m4 inttypes.m4 \ inttypes_h.m4 isc-posix.m4 javacomp.m4 javaexec.m4 lcmessage.m4 lib-ld.m4 \ lib-link.m4 lib-prefix.m4 libtool.m4 mbrtowc.m4 mbstate_t.m4 mbswidth.m4 \ mkdtemp.m4 progtest.m4 setenv.m4 setlocale.m4 siginfo.m4 signalblocking.m4 \ diff --git a/m4/gettext.m4 b/m4/gettext.m4 index bfa15a228..f2b3a1f84 100644 --- a/m4/gettext.m4 +++ b/m4/gettext.m4 @@ -33,7 +33,9 @@ dnl AM-DISABLE-SHARED). If INTLSYMBOL is 'no-libtool', a static library dnl $(top_builddir)/intl/libintl.a will be created. dnl If NEEDSYMBOL is specified and is 'need-ngettext', then GNU gettext dnl implementations (in libc or libintl) without the ngettext() function -dnl will be ignored. +dnl will be ignored. If NEEDSYMBOL is specified and is +dnl 'need-formatstring-macros', then GNU gettext implementations that don't +dnl support the ISO C 99 formatstring macros will be ignored. dnl INTLDIR is used to find the intl libraries. If empty, dnl the value `$(top_builddir)/intl/' is used. dnl @@ -60,9 +62,9 @@ AC_DEFUN([AM_GNU_GETTEXT], ifelse([$1], [], , [ifelse([$1], [external], , [ifelse([$1], [no-libtool], , [ifelse([$1], [use-libtool], , [errprint([ERROR: invalid first argument to AM_GNU_GETTEXT ])])])])]) - ifelse([$2], [], , [ifelse([$2], [need-ngettext], , + ifelse([$2], [], , [ifelse([$2], [need-ngettext], , [ifelse([$2], [need-formatstring-macros], , [errprint([ERROR: invalid second argument to AM_GNU_GETTEXT -])])]) +])])])]) define(gt_included_intl, ifelse([$1], [external], [no], [yes])) define(gt_libtool_suffix_prefix, ifelse([$1], [use-libtool], [l], [])) @@ -125,13 +127,20 @@ AC_DEFUN([AM_GNU_GETTEXT], dnl to fall back to GNU NLS library. dnl Add a version number to the cache macros. - define([gt_api_version], ifelse([$2], [need-ngettext], 2, 1)) + define([gt_api_version], ifelse([$2], [need-formatstring-macros], 3, ifelse([$2], [need-ngettext], 2, 1))) define([gt_cv_func_gnugettext_libc], [gt_cv_func_gnugettext]gt_api_version[_libc]) define([gt_cv_func_gnugettext_libintl], [gt_cv_func_gnugettext]gt_api_version[_libintl]) AC_CACHE_CHECK([for GNU gettext in libc], gt_cv_func_gnugettext_libc, [AC_TRY_LINK([#include -extern int _nl_msg_cat_cntr; +]ifelse([$2], [need-formatstring-macros], +[#ifndef __GNU_GETTEXT_SUPPORTED_REVISION +#define __GNU_GETTEXT_SUPPORTED_REVISION(major) ((major) == 0 ? 0 : -1) +#endif +changequote(,)dnl +typedef int array [2 * (__GNU_GETTEXT_SUPPORTED_REVISION(0) >= 1) - 1]; +changequote([,])dnl +], [])[extern int _nl_msg_cat_cntr; extern int *_nl_domain_bindings;], [bindtextdomain ("", ""); return (int) gettext ("")]ifelse([$2], [need-ngettext], [ + (int) ngettext ("", "", 0)], [])[ + _nl_msg_cat_cntr + *_nl_domain_bindings], @@ -156,7 +165,14 @@ return (int) gettext ("")]ifelse([$2], [need-ngettext], [ + (int) ngettext ("", LIBS="$LIBS $LIBINTL" dnl Now see whether libintl exists and does not depend on libiconv. AC_TRY_LINK([#include -extern int _nl_msg_cat_cntr; +]ifelse([$2], [need-formatstring-macros], +[#ifndef __GNU_GETTEXT_SUPPORTED_REVISION +#define __GNU_GETTEXT_SUPPORTED_REVISION(major) ((major) == 0 ? 0 : -1) +#endif +changequote(,)dnl +typedef int array [2 * (__GNU_GETTEXT_SUPPORTED_REVISION(0) >= 1) - 1]; +changequote([,])dnl +], [])[extern int _nl_msg_cat_cntr; extern #ifdef __cplusplus "C" @@ -170,7 +186,14 @@ return (int) gettext ("")]ifelse([$2], [need-ngettext], [ + (int) ngettext ("", if test "$gt_cv_func_gnugettext_libintl" != yes && test -n "$LIBICONV"; then LIBS="$LIBS $LIBICONV" AC_TRY_LINK([#include -extern int _nl_msg_cat_cntr; +]ifelse([$2], [need-formatstring-macros], +[#ifndef __GNU_GETTEXT_SUPPORTED_REVISION +#define __GNU_GETTEXT_SUPPORTED_REVISION(major) ((major) == 0 ? 0 : -1) +#endif +changequote(,)dnl +typedef int array [2 * (__GNU_GETTEXT_SUPPORTED_REVISION(0) >= 1) - 1]; +changequote([,])dnl +], [])[extern int _nl_msg_cat_cntr; extern #ifdef __cplusplus "C" @@ -495,6 +518,8 @@ AC_DEFUN([AM_INTL_SUBDIR], AC_REQUIRE([AC_FUNC_MMAP])dnl AC_REQUIRE([jm_GLIBC21])dnl AC_REQUIRE([gt_INTDIV0])dnl + AC_REQUIRE([jm_AC_TYPE_UINTMAX_T])dnl + AC_REQUIRE([gt_HEADER_INTTYPES_H])dnl AC_CHECK_HEADERS([argz.h limits.h locale.h nl_types.h malloc.h stddef.h \ stdlib.h string.h unistd.h sys/param.h]) diff --git a/m4/inttypes.m4 b/m4/inttypes.m4 new file mode 100644 index 000000000..ab370ffe0 --- /dev/null +++ b/m4/inttypes.m4 @@ -0,0 +1,27 @@ +# inttypes.m4 serial 1 (gettext-0.11.4) +dnl Copyright (C) 1997-2002 Free Software Foundation, Inc. +dnl This file is free software, distributed under the terms of the GNU +dnl General Public License. As a special exception to the GNU General +dnl Public License, this file may be distributed as part of a program +dnl that contains a configuration script generated by Autoconf, under +dnl the same distribution terms as the rest of that program. + +dnl From Paul Eggert. + +# Define HAVE_INTTYPES_H if exists and doesn't clash with +# . + +AC_DEFUN([gt_HEADER_INTTYPES_H], +[ + AC_CACHE_CHECK([for inttypes.h], gt_cv_header_inttypes_h, + [ + AC_TRY_COMPILE( + [#include +#include ], + [], gt_cv_header_inttypes_h=yes, gt_cv_header_inttypes_h=no) + ]) + if test $gt_cv_header_inttypes_h = yes; then + AC_DEFINE_UNQUOTED(HAVE_INTTYPES_H, 1, + [Define if exists and doesn't clash with .]) + fi +]) diff --git a/m4/inttypes_h.m4 b/m4/inttypes_h.m4 index 75b04a6c0..400a11184 100644 --- a/m4/inttypes_h.m4 +++ b/m4/inttypes_h.m4 @@ -1,4 +1,4 @@ -# inttypes_h.m4 serial 3 (gettext-0.10.40) +# inttypes_h.m4 serial 4 (gettext-0.11.4) dnl Copyright (C) 1997-2002 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General @@ -8,7 +8,7 @@ dnl the same distribution terms as the rest of that program. dnl From Paul Eggert. -# Define HAVE_INTTYPES_H if exists, +# Define HAVE_INTTYPES_H_WITH_UINTMAX if exists, # doesn't clash with , and declares uintmax_t. AC_DEFUN([jm_AC_HEADER_INTTYPES_H], @@ -21,7 +21,7 @@ AC_DEFUN([jm_AC_HEADER_INTTYPES_H], jm_ac_cv_header_inttypes_h=yes, jm_ac_cv_header_inttypes_h=no)]) if test $jm_ac_cv_header_inttypes_h = yes; then - AC_DEFINE_UNQUOTED(HAVE_INTTYPES_H, 1, + AC_DEFINE_UNQUOTED(HAVE_INTTYPES_H_WITH_UINTMAX, 1, [Define if exists, doesn't clash with , and declares uintmax_t. ]) fi diff --git a/m4/stdint_h.m4 b/m4/stdint_h.m4 index b5fd1f0f8..4b5a4ac60 100644 --- a/m4/stdint_h.m4 +++ b/m4/stdint_h.m4 @@ -1,4 +1,4 @@ -# stdint_h.m4 serial 1 (gettext-0.11) +# stdint_h.m4 serial 2 (gettext-0.11.4) dnl Copyright (C) 1997-2002 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General @@ -8,7 +8,7 @@ dnl the same distribution terms as the rest of that program. dnl From Paul Eggert. -# Define HAVE_STDINT_H if exists, +# Define HAVE_STDINT_H_WITH_UINTMAX if exists, # doesn't clash with , and declares uintmax_t. AC_DEFUN([jm_AC_HEADER_STDINT_H], @@ -21,7 +21,7 @@ AC_DEFUN([jm_AC_HEADER_STDINT_H], jm_ac_cv_header_stdint_h=yes, jm_ac_cv_header_stdint_h=no)]) if test $jm_ac_cv_header_stdint_h = yes; then - AC_DEFINE_UNQUOTED(HAVE_STDINT_H, 1, + AC_DEFINE_UNQUOTED(HAVE_STDINT_H_WITH_UINTMAX, 1, [Define if exists, doesn't clash with , and declares uintmax_t. ]) fi diff --git a/misc/ChangeLog b/misc/ChangeLog index 4c6d97373..badf7ebea 100644 --- a/misc/ChangeLog +++ b/misc/ChangeLog @@ -1,3 +1,8 @@ +2002-07-21 Bruno Haible + + * gettextize.in: Add inttypes.m4, inttypes_h.m4, stdint_h.m4, + uintmax_t.m4 to m4filelist. + 2002-07-19 Bruno Haible * gettextize.in: Copy also intdiv0.m4. diff --git a/misc/gettextize.in b/misc/gettextize.in index ce570502e..32aaa7f39 100644 --- a/misc/gettextize.in +++ b/misc/gettextize.in @@ -559,9 +559,11 @@ if test -f "$srcdir/po/stamp-cat-id"; then fi $do_changelog && func_poChangeLog_finish -m4filelist=' codeset.m4 gettext.m4 glibc21.m4 iconv.m4 intdiv0.m4 isc-posix.m4 - lcmessage.m4 lib-ld.m4 lib-link.m4 lib-prefix.m4 progtest.m4' -# We cannot omit codeset.m4, glibc21.m4, intdiv0.m4, isc-posix.m4, lcmessage.m4 +m4filelist=' codeset.m4 gettext.m4 glibc21.m4 iconv.m4 intdiv0.m4 inttypes.m4 + inttypes_h.m4 isc-posix.m4 lcmessage.m4 lib-ld.m4 lib-link.m4 lib-prefix.m4 + progtest.m4 stdint_h.m4 uintmax_t.m4' +# We cannot omit codeset.m4, glibc21.m4, intdiv0.m4, inttypes.m4, +# inttypes_h.m4, isc-posix.m4, lcmessage.m4, stdint_h.m4, uintmax_t.m4 # if test -z "$intldir", otherwise "aclocal -I m4" might give an error. # (aclocal doesn't know which macros are really needed, it looks which macros # are potentially needed.) diff --git a/src/ChangeLog b/src/ChangeLog index 48d0fca84..c766517fe 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,39 @@ +2002-07-21 Bruno Haible + + * format.h (struct interval): New type. + (get_c99_format_directives): New declaration. + * format-c.c (FAT_SIZE_8_T, FAT_SIZE_16_T, FAT_SIZE_32_T, + FAT_SIZE_64_T, FAT_SIZE_LEAST8_T, FAT_SIZE_LEAST16_T, + FAT_SIZE_LEAST32_T, FAT_SIZE_LEAST64_T, FAT_SIZE_FAST8_T, + FAT_SIZE_FAST16_T, FAT_SIZE_FAST32_T, FAT_SIZE_FAST64_T, + FAT_SIZE_INTPTR_T): New enum values. + (FAT_SIZE_MASK): Update. + (struct spec): New fields c99_directives_count, c99_directives. + (format_parse): Recognize C 99 macros. Set the new + fields c99_directives_count, c99_directives in the result. + (get_c99_format_directives): New function. + (format_print): Update. + * x-c.c (is_inttypes_macro, phase8a_get, phase8a_unget): New functions. + (phase8_get): Use phase8a_get instead of phase6_get. + * write-mo.c: Include format.h, xmalloc.h. + (freea): New macro. + (struct pre_string): New type. + (struct pre_message): Renamed from struct id_str_pair. + (compare_id): Update. + (struct pre_sysdep_segment): New type. + (struct pre_segment_pair): New type. + (struct pre_sysdep_string): New type. + (struct pre_sysdep_message): New type. + (write_table): Rewritten. + * read-mo.c (endian): Remove variable. + (read32, seek32, string32): Remove functions. + (struct binary_mo_file): New type. + (read_binary_mo_file, get_uint32, get_string, get_sysdep_string): New + functions. + (read_mo_file): Reworked to work in-memory, without fseek/lseek calls. + * msgfmt.c (format_directive_message): Copy the is_format array. + Needed by write-mo.c. + 2002-07-20 Andreas Schwab * plural-eval.c: Include if !INTDIV0_RAISES_SIGFPE. diff --git a/src/format-c.c b/src/format-c.c index 04deaff1e..2101874d2 100644 --- a/src/format-c.c +++ b/src/format-c.c @@ -41,18 +41,21 @@ or '*m$' or a nonempty digit sequence, - is optionally followed by '.' and a precision specification: '*' (reads an argument) or '*m$' or a nonempty digit sequence, - - is optionally followed by a size specifier, one of 'hh' 'h' 'l' 'll' 'L' - 'q' 'j' 'z' 't', - - is finished by a specifier - - '%', that needs no argument, - - 'c', 'C', that need a character argument, - - 's', 'S', that need a string argument, - - 'i', 'd', that need a signed integer argument, - - 'o', 'u', 'x', 'X', that need an unsigned integer argument, - - 'e', 'E', 'f', 'F', 'g', 'G', 'a', 'A', that need a floating-point - argument, - - 'p', that needs a 'void *' argument, - - 'n', that needs a pointer to integer. + - is either continued like this: + - is optionally followed by a size specifier, one of 'hh' 'h' 'l' 'll' + 'L' 'q' 'j' 'z' 't', + - is finished by a specifier + - '%', that needs no argument, + - 'c', 'C', that need a character argument, + - 's', 'S', that need a string argument, + - 'i', 'd', that need a signed integer argument, + - 'o', 'u', 'x', 'X', that need an unsigned integer argument, + - 'e', 'E', 'f', 'F', 'g', 'G', 'a', 'A', that need a floating-point + argument, + - 'p', that needs a 'void *' argument, + - 'n', that needs a pointer to integer. + or is finished by a specifier '<' inttypes-macro '>' where inttypes-macro + is an ISO C 99 section 7.8.1 format directive. Numbered ('%m$' or '*m$') and unnumbered argument specifications cannot be used in the same string. When numbered argument specifications are used, specifying the Nth argument requires that all the leading arguments, @@ -75,9 +78,22 @@ enum format_arg_type FAT_SIZE_CHAR = 2 << 4, FAT_SIZE_LONG = 1 << 6, FAT_SIZE_LONGLONG = 2 << 6, - FAT_SIZE_INTMAX_T = 1 << 8, - FAT_SIZE_SIZE_T = 1 << 9, - FAT_SIZE_PTRDIFF_T = 1 << 10, + FAT_SIZE_8_T = 1 << 8, + FAT_SIZE_16_T = 1 << 9, + FAT_SIZE_32_T = 1 << 10, + FAT_SIZE_64_T = 1 << 11, + FAT_SIZE_LEAST8_T = 1 << 12, + FAT_SIZE_LEAST16_T = 1 << 13, + FAT_SIZE_LEAST32_T = 1 << 14, + FAT_SIZE_LEAST64_T = 1 << 15, + FAT_SIZE_FAST8_T = 1 << 16, + FAT_SIZE_FAST16_T = 1 << 17, + FAT_SIZE_FAST32_T = 1 << 18, + FAT_SIZE_FAST64_T = 1 << 19, + FAT_SIZE_INTMAX_T = 1 << 20, + FAT_SIZE_INTPTR_T = 1 << 21, + FAT_SIZE_SIZE_T = 1 << 22, + FAT_SIZE_PTRDIFF_T = 1 << 23, FAT_WIDE = FAT_SIZE_LONG, /* Meaningful combinations of basic types and flags: 'signed char' = FAT_INTEGER | FAT_SIZE_CHAR, @@ -106,8 +122,14 @@ enum format_arg_type /* Bitmasks */ FAT_SIZE_MASK = (FAT_SIZE_SHORT | FAT_SIZE_CHAR | FAT_SIZE_LONG | FAT_SIZE_LONGLONG - | FAT_SIZE_INTMAX_T | FAT_SIZE_SIZE_T - | FAT_SIZE_PTRDIFF_T) + | FAT_SIZE_8_T | FAT_SIZE_16_T + | FAT_SIZE_32_T | FAT_SIZE_64_T + | FAT_SIZE_LEAST8_T | FAT_SIZE_LEAST16_T + | FAT_SIZE_LEAST32_T | FAT_SIZE_LEAST64_T + | FAT_SIZE_FAST8_T | FAT_SIZE_FAST16_T + | FAT_SIZE_FAST32_T | FAT_SIZE_FAST64_T + | FAT_SIZE_INTMAX_T | FAT_SIZE_INTPTR_T + | FAT_SIZE_SIZE_T | FAT_SIZE_PTRDIFF_T) }; struct numbered_arg @@ -127,6 +149,8 @@ struct spec unsigned int unnumbered_arg_count; unsigned int allocated; struct unnumbered_arg *unnumbered; + unsigned int c99_directives_count; + const char **c99_directives; }; /* Locale independent test for a decimal digit. @@ -174,6 +198,8 @@ format_parse (format) spec.allocated = 0; numbered = NULL; spec.unnumbered = NULL; + spec.c99_directives_count = 0; + spec.c99_directives = NULL; for (; *format != '\0';) if (*format++ == '%') @@ -350,84 +376,225 @@ format_parse (format) } } - /* Parse size. */ - size = 0; - for (;; format++) + if (*format == '<') { - if (*format == 'h') + spec.c99_directives = + (const char **) + xrealloc (spec.c99_directives, + 2 * (spec.c99_directives_count + 1) + * sizeof (const char *)); + spec.c99_directives[2 * spec.c99_directives_count] = format; + + format++; + /* Parse ISO C 99 section 7.8.1 format string directive. + Syntax: + P R I { d | i | o | u | x | X } + { { | LEAST | FAST } { 8 | 16 | 32 | 64 } | MAX | PTR } */ + if (*format != 'P') + goto bad_format; + format++; + if (*format != 'R') + goto bad_format; + format++; + if (*format != 'I') + goto bad_format; + format++; + + switch (*format) + { + case 'i': case 'd': + type = FAT_INTEGER; + break; + case 'u': case 'o': case 'x': case 'X': + type = FAT_INTEGER | FAT_UNSIGNED; + break; + default: + goto bad_format; + } + format++; + + if (format[0] == 'M' && format[1] == 'A' && format[2] == 'X') + { + type |= FAT_SIZE_INTMAX_T; + format += 3; + } + else if (format[0] == 'P' && format[1] == 'T' && format[2] == 'R') { - if (size & (FAT_SIZE_SHORT | FAT_SIZE_CHAR)) - size = FAT_SIZE_CHAR; + type |= FAT_SIZE_INTPTR_T; + format += 3; + } + else + { + if (format[0] == 'L' && format[1] == 'E' && format[2] == 'A' + && format[3] == 'S' && format[4] == 'T') + { + format += 5; + if (format[0] == '8') + { + type |= FAT_SIZE_LEAST8_T; + format++; + } + else if (format[0] == '1' && format[1] == '6') + { + type |= FAT_SIZE_LEAST16_T; + format += 2; + } + else if (format[0] == '3' && format[1] == '2') + { + type |= FAT_SIZE_LEAST32_T; + format += 2; + } + else if (format[0] == '6' && format[1] == '4') + { + type |= FAT_SIZE_LEAST64_T; + format += 2; + } + else + goto bad_format; + } + else if (format[0] == 'F' && format[1] == 'A' + && format[2] == 'S' && format[3] == 'T') + { + format += 4; + if (format[0] == '8') + { + type |= FAT_SIZE_FAST8_T; + format++; + } + else if (format[0] == '1' && format[1] == '6') + { + type |= FAT_SIZE_FAST16_T; + format += 2; + } + else if (format[0] == '3' && format[1] == '2') + { + type |= FAT_SIZE_FAST32_T; + format += 2; + } + else if (format[0] == '6' && format[1] == '4') + { + type |= FAT_SIZE_FAST64_T; + format += 2; + } + else + goto bad_format; + } else - size = FAT_SIZE_SHORT; + { + if (format[0] == '8') + { + type |= FAT_SIZE_8_T; + format++; + } + else if (format[0] == '1' && format[1] == '6') + { + type |= FAT_SIZE_16_T; + format += 2; + } + else if (format[0] == '3' && format[1] == '2') + { + type |= FAT_SIZE_32_T; + format += 2; + } + else if (format[0] == '6' && format[1] == '4') + { + type |= FAT_SIZE_64_T; + format += 2; + } + else + goto bad_format; + } } - else if (*format == 'l') + + if (*format != '>') + goto bad_format; + + spec.c99_directives[2 * spec.c99_directives_count + 1] = format; + spec.c99_directives_count++; + } + else + { + /* Parse size. */ + size = 0; + for (;; format++) { - if (size & (FAT_SIZE_LONG | FAT_SIZE_LONGLONG)) + if (*format == 'h') + { + if (size & (FAT_SIZE_SHORT | FAT_SIZE_CHAR)) + size = FAT_SIZE_CHAR; + else + size = FAT_SIZE_SHORT; + } + else if (*format == 'l') + { + if (size & (FAT_SIZE_LONG | FAT_SIZE_LONGLONG)) + size = FAT_SIZE_LONGLONG; + else + size = FAT_SIZE_LONG; + } + else if (*format == 'L') size = FAT_SIZE_LONGLONG; + else if (*format == 'q') + /* Old BSD 4.4 convention. */ + size = FAT_SIZE_LONGLONG; + else if (*format == 'j') + size = FAT_SIZE_INTMAX_T; + else if (*format == 'z' || *format == 'Z') + /* 'z' is standardized in ISO C 99, but glibc uses 'Z' + because the warning facility in gcc-2.95.2 understands + only 'Z' (see gcc-2.95.2/gcc/c-common.c:1784). */ + size = FAT_SIZE_SIZE_T; + else if (*format == 't') + size = FAT_SIZE_PTRDIFF_T; else - size = FAT_SIZE_LONG; + break; } - else if (*format == 'L') - size = FAT_SIZE_LONGLONG; - else if (*format == 'q') - /* Old BSD 4.4 convention. */ - size = FAT_SIZE_LONGLONG; - else if (*format == 'j') - size = FAT_SIZE_INTMAX_T; - else if (*format == 'z' || *format == 'Z') - /* 'z' is standardized in ISO C 99, but glibc uses 'Z' because - the warning facility in gcc-2.95.2 understands only 'Z' - (see gcc-2.95.2/gcc/c-common.c:1784). */ - size = FAT_SIZE_SIZE_T; - else if (*format == 't') - size = FAT_SIZE_PTRDIFF_T; - else - break; - } - switch (*format) - { - case '%': - case 'm': /* glibc extension */ - type = FAT_NONE; - break; - case 'c': - type = FAT_CHAR; - type |= (size & (FAT_SIZE_LONG | FAT_SIZE_LONGLONG) ? FAT_WIDE : 0); - break; - case 'C': /* obsolete */ - type = FAT_CHAR | FAT_WIDE; - break; - case 's': - type = FAT_STRING; - type |= (size & (FAT_SIZE_LONG | FAT_SIZE_LONGLONG) ? FAT_WIDE : 0); - break; - case 'S': /* obsolete */ - type = FAT_STRING | FAT_WIDE; - break; - case 'i': case 'd': - type = FAT_INTEGER; - type |= (size & FAT_SIZE_MASK); - break; - case 'u': case 'o': case 'x': case 'X': - type = FAT_INTEGER | FAT_UNSIGNED; - type |= (size & FAT_SIZE_MASK); - break; - case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': - case 'a': case 'A': - type = FAT_DOUBLE; - type |= (size & FAT_SIZE_LONGLONG); - break; - case 'p': - type = FAT_POINTER; - break; - case 'n': - type = FAT_COUNT_POINTER; - type |= (size & FAT_SIZE_MASK); - break; - default: - goto bad_format; + switch (*format) + { + case '%': + case 'm': /* glibc extension */ + type = FAT_NONE; + break; + case 'c': + type = FAT_CHAR; + type |= (size & (FAT_SIZE_LONG | FAT_SIZE_LONGLONG) + ? FAT_WIDE : 0); + break; + case 'C': /* obsolete */ + type = FAT_CHAR | FAT_WIDE; + break; + case 's': + type = FAT_STRING; + type |= (size & (FAT_SIZE_LONG | FAT_SIZE_LONGLONG) + ? FAT_WIDE : 0); + break; + case 'S': /* obsolete */ + type = FAT_STRING | FAT_WIDE; + break; + case 'i': case 'd': + type = FAT_INTEGER; + type |= (size & FAT_SIZE_MASK); + break; + case 'u': case 'o': case 'x': case 'X': + type = FAT_INTEGER | FAT_UNSIGNED; + type |= (size & FAT_SIZE_MASK); + break; + case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': + case 'a': case 'A': + type = FAT_DOUBLE; + type |= (size & FAT_SIZE_LONGLONG); + break; + case 'p': + type = FAT_POINTER; + break; + case 'n': + type = FAT_COUNT_POINTER; + type |= (size & FAT_SIZE_MASK); + break; + default: + goto bad_format; + } } if (type != FAT_NONE) @@ -540,6 +707,8 @@ format_parse (format) free (numbered); if (spec.unnumbered != NULL) free (spec.unnumbered); + if (spec.c99_directives != NULL) + free (spec.c99_directives); return NULL; } @@ -551,6 +720,8 @@ format_free (descr) if (spec->unnumbered != NULL) free (spec->unnumbered); + if (spec->c99_directives != NULL) + free (spec->c99_directives); free (spec); } @@ -620,6 +791,37 @@ struct formatstring_parser formatstring_c = }; +void +get_c99_format_directives (string, intervalsp, lengthp) + const char *string; + struct interval **intervalsp; + size_t *lengthp; +{ + struct spec *descr = (struct spec *) format_parse (string); + + if (descr != NULL && descr->c99_directives_count > 0) + { + unsigned int n = descr->c99_directives_count; + struct interval *intervals = + (struct interval *) xmalloc (n * sizeof (struct interval)); + unsigned int i; + + for (i = 0; i < n; i++) + { + intervals[i].startpos = descr->c99_directives[2 * i] - string; + intervals[i].endpos = descr->c99_directives[2 * i + 1] - string; + } + *intervalsp = intervals; + *lengthp = n; + } + else + { + *intervalsp = NULL; + *lengthp = 0; + } +} + + #ifdef TEST /* Test program: Print the argument list specification returned by @@ -664,9 +866,48 @@ format_print (descr) case FAT_SIZE_LONGLONG: printf ("[long long]"); break; + case FAT_SIZE_8_T: + printf ("[int8_t]"); + break; + case FAT_SIZE_16_T: + printf ("[int16_t]"); + break; + case FAT_SIZE_32_T: + printf ("[int32_t]"); + break; + case FAT_SIZE_64_T: + printf ("[int64_t]"); + break; + case FAT_SIZE_LEAST8_T: + printf ("[int_least8_t]"); + break; + case FAT_SIZE_LEAST16_T: + printf ("[int_least16_t]"); + break; + case FAT_SIZE_LEAST32_T: + printf ("[int_least32_t]"); + break; + case FAT_SIZE_LEAST64_T: + printf ("[int_least64_t]"); + break; + case FAT_SIZE_FAST8_T: + printf ("[int_fast8_t]"); + break; + case FAT_SIZE_FAST16_T: + printf ("[int_fast16_t]"); + break; + case FAT_SIZE_FAST32_T: + printf ("[int_fast32_t]"); + break; + case FAT_SIZE_FAST64_T: + printf ("[int_fast64_t]"); + break; case FAT_SIZE_INTMAX_T: printf ("[intmax_t]"); break; + case FAT_SIZE_INTPTR_T: + printf ("[intptr_t]"); + break; case FAT_SIZE_SIZE_T: printf ("[size_t]"); break; diff --git a/src/format.h b/src/format.h index 37f05647a..f19634f5c 100644 --- a/src/format.h +++ b/src/format.h @@ -69,4 +69,15 @@ extern struct formatstring_parser formatstring_tcl; /* Table of all format string parsers. */ extern struct formatstring_parser *formatstring_parsers[NFORMATS]; +/* Returns an array of the ISO C 99 format directives + contained in the argument string. *intervalsp is assigned to a freshly + allocated array of intervals (startpos pointing to '<', endpos to '>'), + and *lengthp is assigned to the number of intervals in this array. */ +struct interval +{ + size_t startpos; + size_t endpos; +}; +extern void get_c99_format_directives PARAMS ((const char *string, struct interval **intervalsp, size_t *lengthp)); + #endif /* _FORMAT_H */ diff --git a/src/msgfmt.c b/src/msgfmt.c index 058178d27..9f4091f06 100644 --- a/src/msgfmt.c +++ b/src/msgfmt.c @@ -1464,6 +1464,8 @@ format_directive_message (that, msgid_string, msgid_pos, msgid_plural, mp->msgid = msgid_string; mp->msgid_plural = msgid_plural; mp->obsolete = obsolete; + for (i = 0; i < NFORMATS; i++) + mp->is_format[i] = this->is_format[i]; if (!obsolete) { diff --git a/src/read-mo.c b/src/read-mo.c index 590f18565..4d2ebb004 100644 --- a/src/read-mo.c +++ b/src/read-mo.c @@ -41,122 +41,214 @@ #define _(str) gettext (str) -/* This defines the byte order within the file. It needs to be set - appropriately once we have the file open. */ -static enum { MO_LITTLE_ENDIAN, MO_BIG_ENDIAN } endian; +/* We read the file completely into memory. This is more efficient than + lots of lseek(). This struct represents the .mo file in memory. */ +struct binary_mo_file +{ + const char *filename; + char *data; + size_t size; + enum { MO_LITTLE_ENDIAN, MO_BIG_ENDIAN } endian; +}; /* Prototypes for local functions. Needed to ensure compiler checking of function argument counts despite of K&R C function definition syntax. */ -static nls_uint32 read32 PARAMS ((FILE *fp, const char *fn)); -static void seek32 PARAMS ((FILE *fp, const char *fn, long offset)); -static char *string32 PARAMS ((FILE *fp, const char *fn, long offset, - size_t *lengthp)); - - -/* This function reads a 32-bit number from the file, and assembles it - according to the current ``endian'' setting. */ -static nls_uint32 -read32 (fp, fn) +static void read_binary_mo_file PARAMS ((struct binary_mo_file *bfp, + FILE *fp, const char *filename)); +static nls_uint32 get_uint32 PARAMS ((const struct binary_mo_file *bfp, + size_t offset)); +static char * get_string PARAMS ((const struct binary_mo_file *bfp, + size_t offset, size_t *lengthp)); +static char * get_sysdep_string PARAMS ((const struct binary_mo_file *bfp, + size_t offset, + const struct mo_file_header *header, + size_t *lengthp)); + + +/* Read the contents of the given input stream. */ +static void +read_binary_mo_file (bfp, fp, filename) + struct binary_mo_file *bfp; FILE *fp; - const char *fn; + const char *filename; { - int c1, c2, c3, c4; + char *buf = NULL; + size_t alloc = 0; + size_t size = 0; + size_t count; - c1 = getc (fp); - if (c1 == EOF) + while (!feof (fp)) { - bomb: - if (ferror (fp)) - error (EXIT_FAILURE, errno, _("error while reading \"%s\""), fn); - error (EXIT_FAILURE, 0, _("file \"%s\" truncated"), fn); + const size_t increment = 4096; + if (size + increment > alloc) + { + alloc = alloc + alloc / 2; + if (alloc < size + increment) + alloc = size + increment; + buf = (char *) xrealloc (buf, alloc); + } + count = fread (buf + size, 1, increment, fp); + if (count == 0) + { + if (ferror (fp)) + error (EXIT_FAILURE, errno, _("error while reading \"%s\""), + filename); + } + else + size += count; } - c2 = getc (fp); - if (c2 == EOF) - goto bomb; - c3 = getc (fp); - if (c3 == EOF) - goto bomb; - c4 = getc (fp); - if (c4 == EOF) - goto bomb; - if (endian == MO_LITTLE_ENDIAN) - return (((nls_uint32) c1) - | ((nls_uint32) c2 << 8) - | ((nls_uint32) c3 << 16) - | ((nls_uint32) c4 << 24)); - - return (((nls_uint32) c1 << 24) - | ((nls_uint32) c2 << 16) - | ((nls_uint32) c3 << 8) - | ((nls_uint32) c4)); + buf = (char *) xrealloc (buf, size); + bfp->filename = filename; + bfp->data = buf; + bfp->size = size; } - -static void -seek32 (fp, fn, offset) - FILE *fp; - const char *fn; - long offset; +/* Get a 32-bit number from the file, at the given file position. */ +static nls_uint32 +get_uint32 (bfp, offset) + const struct binary_mo_file *bfp; + size_t offset; { - if (fseek (fp, offset, 0) < 0) - error (EXIT_FAILURE, errno, _("seek \"%s\" offset %ld failed"), - fn, offset); + nls_uint32 b0, b1, b2, b3; + + if (offset + 4 > bfp->size) + error (EXIT_FAILURE, 0, _("file \"%s\" is truncated"), bfp->filename); + + b0 = *(unsigned char *) (bfp->data + offset + 0); + b1 = *(unsigned char *) (bfp->data + offset + 1); + b2 = *(unsigned char *) (bfp->data + offset + 2); + b3 = *(unsigned char *) (bfp->data + offset + 3); + if (bfp->endian == MO_LITTLE_ENDIAN) + return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24); + else + return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; } +/* Get a static string from the file, at the given file position. */ +static char * +get_string (bfp, offset, lengthp) + const struct binary_mo_file *bfp; + size_t offset; + size_t *lengthp; +{ + /* See 'struct string_desc'. */ + nls_uint32 s_length = get_uint32 (bfp, offset); + nls_uint32 s_offset = get_uint32 (bfp, offset + 4); + + if (s_offset + s_length + 1 > bfp->size) + error (EXIT_FAILURE, 0, _("file \"%s\" is truncated"), bfp->filename); + if (bfp->data[s_offset + s_length] != '\0') + error (EXIT_FAILURE, 0, + _("file \"%s\" contains a not NUL terminated string"), + bfp->filename); + + *lengthp = s_length + 1; + return bfp->data + s_offset; +} +/* Get a system dependent string from the file, at the given file position. */ static char * -string32 (fp, fn, offset, lengthp) - FILE *fp; - const char *fn; - long offset; +get_sysdep_string (bfp, offset, header, lengthp) + const struct binary_mo_file *bfp; + size_t offset; + const struct mo_file_header *header; size_t *lengthp; { - long length; - char *buffer; - long n; - - /* Read the string_desc structure, describing where in the file to - find the string. */ - seek32 (fp, fn, offset); - length = read32 (fp, fn); - offset = read32 (fp, fn); - - /* Allocate memory for the string to be read into. Leave space for - the NUL on the end. */ - buffer = (char *) xmalloc (length + 1); - - /* Read in the string. Complain if there is an error or it comes up - short. Add the NUL ourselves. */ - seek32 (fp, fn, offset); - n = fread (buffer, 1, length + 1, fp); - if (n != length + 1) + /* See 'struct sysdep_string'. */ + size_t length; + char *string; + size_t i; + char *p; + nls_uint32 s_offset; + + /* Compute the length. */ + length = 0; + for (i = 4; ; i += 8) { - if (ferror (fp)) - error (EXIT_FAILURE, errno, _("error while reading \"%s\""), fn); - error (EXIT_FAILURE, 0, _("file \"%s\" truncated"), fn); + nls_uint32 segsize = get_uint32 (bfp, offset + i); + nls_uint32 sysdepref = get_uint32 (bfp, offset + i + 4); + nls_uint32 sysdep_segment_offset; + nls_uint32 ss_length; + nls_uint32 ss_offset; + + length += segsize; + + if (sysdepref == SEGMENTS_END) + break; + if (sysdepref >= header->n_sysdep_segments) + /* Invalid. */ + error (EXIT_FAILURE, 0, _("file \"%s\" is not in GNU .mo format"), + bfp->filename); + /* See 'struct sysdep_segment'. */ + sysdep_segment_offset = header->sysdep_segments_offset + sysdepref * 8; + ss_length = get_uint32 (bfp, sysdep_segment_offset); + ss_offset = get_uint32 (bfp, sysdep_segment_offset + 4); + if (ss_offset + ss_length > bfp->size) + error (EXIT_FAILURE, 0, _("file \"%s\" is truncated"), bfp->filename); + if (!(ss_length > 0 && bfp->data[ss_offset + ss_length - 1] == '\0')) + error (EXIT_FAILURE, 0, + _("file \"%s\" contains a not NUL terminated sysdep segment"), + bfp->filename); + length += 1 + strlen (bfp->data + ss_offset) + 1; } - if (buffer[length] != '\0') + + /* Allocate and fill the string. */ + string = (char *) xmalloc (length); + p = string; + s_offset = get_uint32 (bfp, offset); + for (i = 4; ; i += 8) { - error (EXIT_FAILURE, 0, - _("file \"%s\" contains a not NUL terminated string"), fn); + nls_uint32 segsize = get_uint32 (bfp, offset + i); + nls_uint32 sysdepref = get_uint32 (bfp, offset + i + 4); + nls_uint32 sysdep_segment_offset; + nls_uint32 ss_length; + nls_uint32 ss_offset; + size_t n; + + if (s_offset + segsize > bfp->size) + error (EXIT_FAILURE, 0, _("file \"%s\" is truncated"), bfp->filename); + memcpy (p, bfp->data + s_offset, segsize); + p += segsize; + s_offset += segsize; + + if (sysdepref == SEGMENTS_END) + break; + if (sysdepref >= header->n_sysdep_segments) + abort (); + /* See 'struct sysdep_segment'. */ + sysdep_segment_offset = header->sysdep_segments_offset + sysdepref * 8; + ss_length = get_uint32 (bfp, sysdep_segment_offset); + ss_offset = get_uint32 (bfp, sysdep_segment_offset + 4); + if (ss_offset + ss_length > bfp->size) + abort (); + if (!(ss_length > 0 && bfp->data[ss_offset + ss_length - 1] == '\0')) + abort(); + n = strlen (bfp->data + ss_offset); + *p++ = '<'; + memcpy (p, bfp->data + ss_offset, n); + p += n; + *p++ = '>'; } - /* Return the string to the caller. */ - *lengthp = length + 1; - return buffer; -} + if (p != string + length) + abort (); + *lengthp = length; + return string; +} -/* This function reads an existing .mo file. */ +/* Reads an existing .mo file and adds the messages to mlp. */ void read_mo_file (mlp, fn) message_list_ty *mlp; const char *fn; { FILE *fp; + struct binary_mo_file bf; struct mo_file_header header; - unsigned int j; + unsigned int i; + static lex_pos_ty pos = { __FILE__, __LINE__ }; if (strcmp (fn, "-") == 0 || strcmp (fn, "/dev/stdin") == 0) { @@ -171,16 +263,22 @@ read_mo_file (mlp, fn) _("error while opening \"%s\" for reading"), fn); } + /* Read the file contents into memory. */ + read_binary_mo_file (&bf, fp, fn); + + /* Get a 32-bit number from the file header. */ +# define GET_HEADER_FIELD(field) \ + get_uint32 (&bf, offsetof (struct mo_file_header, field)) + /* We must grope the file to determine which endian it is. Perversity of the universe tends towards maximum, so it will probably not match the currently executing architecture. */ - endian = MO_BIG_ENDIAN; - header.magic = read32 (fp, fn); + bf.endian = MO_BIG_ENDIAN; + header.magic = GET_HEADER_FIELD (magic); if (header.magic != _MAGIC) { - endian = MO_LITTLE_ENDIAN; - seek32 (fp, fn, 0L); - header.magic = read32 (fp, fn); + bf.endian = MO_LITTLE_ENDIAN; + header.magic = GET_HEADER_FIELD (magic); if (header.magic != _MAGIC) { unrecognised: @@ -189,37 +287,91 @@ read_mo_file (mlp, fn) } } - /* Fill the structure describing the header. */ - header.revision = read32 (fp, fn); - if (header.revision != MO_REVISION_NUMBER) - goto unrecognised; - header.nstrings = read32 (fp, fn); - header.orig_tab_offset = read32 (fp, fn); - header.trans_tab_offset = read32 (fp, fn); - header.hash_tab_size = read32 (fp, fn); - header.hash_tab_offset = read32 (fp, fn); - - for (j = 0; j < header.nstrings; ++j) + header.revision = GET_HEADER_FIELD (revision); + + /* We support only the major revision 0. */ + switch (header.revision >> 16) { - static lex_pos_ty pos = { __FILE__, __LINE__ }; - message_ty *mp; - char *msgid; - size_t msgid_len; - char *msgstr; - size_t msgstr_len; - - /* Read the msgid. */ - msgid = string32 (fp, fn, header.orig_tab_offset + j * 8, &msgid_len); - - /* Read the msgstr. */ - msgstr = string32 (fp, fn, header.trans_tab_offset + j * 8, &msgstr_len); - - mp = message_alloc (msgid, - (strlen (msgid) + 1 < msgid_len - ? msgid + strlen (msgid) + 1 - : NULL), - msgstr, msgstr_len, &pos); - message_list_append (mlp, mp); + case 0: + /* Fill the header parts that apply to major revision 0. */ + header.nstrings = GET_HEADER_FIELD (nstrings); + header.orig_tab_offset = GET_HEADER_FIELD (orig_tab_offset); + header.trans_tab_offset = GET_HEADER_FIELD (trans_tab_offset); + header.hash_tab_size = GET_HEADER_FIELD (hash_tab_size); + header.hash_tab_offset = GET_HEADER_FIELD (hash_tab_offset); + + for (i = 0; i < header.nstrings; i++) + { + message_ty *mp; + char *msgid; + size_t msgid_len; + char *msgstr; + size_t msgstr_len; + + /* Read the msgid. */ + msgid = get_string (&bf, header.orig_tab_offset + i * 8, + &msgid_len); + + /* Read the msgstr. */ + msgstr = get_string (&bf, header.trans_tab_offset + i * 8, + &msgstr_len); + + mp = message_alloc (msgid, + (strlen (msgid) + 1 < msgid_len + ? msgid + strlen (msgid) + 1 + : NULL), + msgstr, msgstr_len, + &pos); + message_list_append (mlp, mp); + } + + switch (header.revision & 0xffff) + { + case 0: + break; + case 1: + default: + /* Fill the header parts that apply to minor revision >= 1. */ + header.n_sysdep_segments = GET_HEADER_FIELD (n_sysdep_segments); + header.sysdep_segments_offset = + GET_HEADER_FIELD (sysdep_segments_offset); + header.n_sysdep_strings = GET_HEADER_FIELD (n_sysdep_strings); + header.orig_sysdep_tab_offset = + GET_HEADER_FIELD (orig_sysdep_tab_offset); + header.trans_sysdep_tab_offset = + GET_HEADER_FIELD (trans_sysdep_tab_offset); + + for (i = 0; i < header.n_sysdep_strings; i++) + { + message_ty *mp; + char *msgid; + size_t msgid_len; + char *msgstr; + size_t msgstr_len; + nls_uint32 offset; + + /* Read the msgid. */ + offset = get_uint32 (&bf, header.orig_sysdep_tab_offset + i * 4); + msgid = get_sysdep_string (&bf, offset, &header, &msgid_len); + + /* Read the msgstr. */ + offset = get_uint32 (&bf, header.trans_sysdep_tab_offset + i * 4); + msgstr = get_sysdep_string (&bf, offset, &header, &msgstr_len); + + mp = message_alloc (msgid, + (strlen (msgid) + 1 < msgid_len + ? msgid + strlen (msgid) + 1 + : NULL), + msgstr, msgstr_len, + &pos); + message_list_append (mlp, mp); + } + break; + } + break; + + default: + goto unrecognised; } if (fp != stdin) diff --git a/src/write-mo.c b/src/write-mo.c index 0127d47c1..9db7f180a 100644 --- a/src/write-mo.c +++ b/src/write-mo.c @@ -38,12 +38,16 @@ #include "error.h" #include "hash.h" #include "message.h" +#include "format.h" +#include "xmalloc.h" #include "binary-io.h" #include "exit.h" #include "gettext.h" #define _(str) gettext (str) +#define freea(p) /* nothing */ + /* Usually defined in . */ #ifndef roundup # if defined __GNUC__ && __GNUC__ >= 2 @@ -68,35 +72,266 @@ static int compare_id PARAMS ((const void *pval1, const void *pval2)); static void write_table PARAMS ((FILE *output_file, message_list_ty *mlp)); -/* Define the data structure which we need to represent the data to - be written out. */ -struct id_str_pair +/* Indices into the strings contained in 'struct pre_message' and + 'struct pre_sysdep_message'. */ +enum +{ + M_ID = 0, /* msgid - the original string */ + M_STR = 1 /* msgstr - the translated string */ +}; + +/* An intermediate data structure representing a 'struct string_desc'. */ +struct pre_string +{ + size_t length; + const char *pointer; +}; + +/* An intermediate data structure representing a message. */ +struct pre_message { - const char *id; - size_t id_len; + struct pre_string str[2]; const char *id_plural; size_t id_plural_len; - const char *str; - size_t str_len; }; - static int compare_id (pval1, pval2) const void *pval1; const void *pval2; { - return strcmp (((struct id_str_pair *) pval1)->id, - ((struct id_str_pair *) pval2)->id); + return strcmp (((struct pre_message *) pval1)->str[M_ID].pointer, + ((struct pre_message *) pval2)->str[M_ID].pointer); } +/* An intermediate data structure representing a 'struct sysdep_segment'. */ +struct pre_sysdep_segment +{ + size_t length; + const char *pointer; +}; + +/* An intermediate data structure representing a 'struct segment_pair'. */ +struct pre_segment_pair +{ + size_t segsize; + const char *segptr; + size_t sysdepref; +}; + +/* An intermediate data structure representing a 'struct sysdep_string'. */ +struct pre_sysdep_string +{ + unsigned int segmentcount; + struct pre_segment_pair segments[1]; +}; + +/* An intermediate data structure representing a message with system dependent + strings. */ +struct pre_sysdep_message +{ + struct pre_sysdep_string *str[2]; + const char *id_plural; + size_t id_plural_len; +}; + +/* Write the message list to the given open file. */ static void write_table (output_file, mlp) FILE *output_file; message_list_ty *mlp; { - static char null = '\0'; + size_t nstrings; + struct pre_message *msg_arr; + size_t n_sysdep_strings; + struct pre_sysdep_message *sysdep_msg_arr; + size_t n_sysdep_segments; + struct pre_sysdep_segment *sysdep_segments; + int minor_revision; + bool omit_hash_table; + nls_uint32 hash_tab_size; + struct mo_file_header header; /* Header of the .mo file to be written. */ + size_t header_size; + size_t offset; + struct string_desc *orig_tab; + struct string_desc *trans_tab; + size_t sysdep_tab_offset = 0; + size_t end_offset; + char *null; + size_t j, m; + + /* First pass: Move the static string pairs into an array, for sorting, + and at the same time, compute the segments of the system dependent + strings. */ + nstrings = 0; + msg_arr = + (struct pre_message *) + alloca (mlp->nitems * sizeof (struct pre_message)); + n_sysdep_strings = 0; + sysdep_msg_arr = + (struct pre_sysdep_message *) + alloca (mlp->nitems * sizeof (struct pre_sysdep_message)); + n_sysdep_segments = 0; + sysdep_segments = NULL; + for (j = 0; j < mlp->nitems; j++) + { + message_ty *mp = mlp->item[j]; + struct interval *intervals[2]; + size_t nintervals[2]; + + intervals[M_ID] = NULL; + nintervals[M_ID] = 0; + intervals[M_STR] = NULL; + nintervals[M_STR] = 0; + + /* Test if mp contains system dependent strings and thus + requires the use of the .mo file minor revision 1. */ + if (possible_format_p (mp->is_format[format_c])) + { + /* Check whether msgid or msgstr contain ISO C 99 + format string directives. No need to check msgid_plural, because + it is not accessed by the [n]gettext() function family. */ + const char *p_end; + const char *p; + + get_c99_format_directives (mp->msgid, + &intervals[M_ID], &nintervals[M_ID]); + + p_end = mp->msgstr + mp->msgstr_len; + for (p = mp->msgstr; p < p_end; p += strlen (p) + 1) + { + struct interval *part_intervals; + size_t part_nintervals; + + get_c99_format_directives (p, &part_intervals, &part_nintervals); + if (part_nintervals > 0) + { + size_t d = p - mp->msgstr; + unsigned int i; + + intervals[M_STR] = + (struct interval *) + xrealloc (intervals[M_STR], + (nintervals[M_STR] + part_nintervals) + * sizeof (struct interval)); + for (i = 0; i < part_nintervals; i++) + { + intervals[M_STR][nintervals[M_STR] + i].startpos = + d + part_intervals[i].startpos; + intervals[M_STR][nintervals[M_STR] + i].endpos = + d + part_intervals[i].endpos; + } + nintervals[M_STR] += part_nintervals; + } + } + } + + if (nintervals[M_ID] > 0 || nintervals[M_STR] > 0) + { + /* System dependent string pair. */ + for (m = 0; m < 2; m++) + { + struct pre_sysdep_string *pre = + (struct pre_sysdep_string *) + alloca (sizeof (struct pre_sysdep_string) + + nintervals[m] * sizeof (struct pre_segment_pair)); + const char *str; + size_t str_len; + size_t lastpos; + unsigned int i; + + if (m == M_ID) + { + str = mp->msgid; + str_len = strlen (mp->msgid) + 1; + } + else + { + str = mp->msgstr; + str_len = mp->msgstr_len; + } + + lastpos = 0; + pre->segmentcount = nintervals[m]; + for (i = 0; i < nintervals[m]; i++) + { + size_t length; + const char *pointer; + size_t r; + + pre->segments[i].segptr = str + lastpos; + pre->segments[i].segsize = intervals[m][i].startpos - lastpos; + + /* The "+ 1" skips the '<' marker. */ + length = + intervals[m][i].endpos - (intervals[m][i].startpos + 1); + pointer = str + (intervals[m][i].startpos + 1); + + for (r = 0; r < n_sysdep_segments; r++) + if (sysdep_segments[r].length == length + && memcmp (sysdep_segments[r].pointer, pointer, length) + == 0) + break; + if (r == n_sysdep_segments) + { + n_sysdep_segments++; + sysdep_segments = + (struct pre_sysdep_segment *) + xrealloc (sysdep_segments, + n_sysdep_segments + * sizeof (struct pre_sysdep_segment)); + sysdep_segments[r].length = length; + sysdep_segments[r].pointer = pointer; + } + + pre->segments[i].sysdepref = r; + + /* The "+ 1" skips the '>' marker. */ + lastpos = intervals[m][i].endpos + 1; + } + pre->segments[i].segptr = str + lastpos; + pre->segments[i].segsize = str_len - lastpos; + pre->segments[i].sysdepref = SEGMENTS_END; + + sysdep_msg_arr[n_sysdep_strings].str[m] = pre; + } + + sysdep_msg_arr[n_sysdep_strings].id_plural = mp->msgid_plural; + sysdep_msg_arr[n_sysdep_strings].id_plural_len = + (mp->msgid_plural != NULL ? strlen (mp->msgid_plural) + 1 : 0); + n_sysdep_strings++; + } + else + { + /* Static string pair. */ + msg_arr[nstrings].str[M_ID].pointer = mp->msgid; + msg_arr[nstrings].str[M_ID].length = strlen (mp->msgid) + 1; + msg_arr[nstrings].str[M_STR].pointer = mp->msgstr; + msg_arr[nstrings].str[M_STR].length = mp->msgstr_len; + msg_arr[nstrings].id_plural = mp->msgid_plural; + msg_arr[nstrings].id_plural_len = + (mp->msgid_plural != NULL ? strlen (mp->msgid_plural) + 1 : 0); + nstrings++; + } + + for (m = 0; m < 2; m++) + if (intervals[m] != NULL) + free (intervals[m]); + } + + /* Sort the table according to original string. */ + if (nstrings > 0) + qsort (msg_arr, nstrings, sizeof (struct pre_message), compare_id); + + /* We need minor revision 1 if there are system dependent strings. + Otherwise we choose minor revision 0 because it's supported by older + versions of libintl and revision 1 isn't. */ + minor_revision = (n_sysdep_strings > 0 ? 1 : 0); + + /* In minor revision >= 1, the hash table is obligatory. */ + omit_hash_table = (no_hash_table && minor_revision == 0); + /* This should be explained: Each string has an associate hashing value V, computed by a fixed function. To locate the string we use open addressing with double @@ -114,142 +349,331 @@ write_table (output_file, mlp) If we now choose M to be the next prime bigger than 4 / 3 * N, we get the values 4 and 1.85 resp. - Because unsuccesful searches are unlikely this is a good value. + Because unsuccessful searches are unlikely this is a good value. Formulas: [Knuth, The Art of Computer Programming, Volume 3, Sorting and Searching, 1973, Addison Wesley] */ - nls_uint32 hash_tab_size = - (no_hash_table ? 0 : next_prime ((mlp->nitems * 4) / 3)); - nls_uint32 *hash_tab; - - /* Header of the .mo file to be written. */ - struct mo_file_header header; - struct id_str_pair *msg_arr; - size_t cnt, j; - message_ty *entry; - struct string_desc sd; - - /* Fill the structure describing the header. */ - header.magic = _MAGIC; /* Magic number. */ - header.revision = MO_REVISION_NUMBER; /* Revision number of file format. */ - header.nstrings = mlp->nitems; /* Number of strings. */ - header.orig_tab_offset = sizeof (header); - /* Offset of table for original string offsets. */ - header.trans_tab_offset = sizeof (header) - + mlp->nitems * sizeof (struct string_desc); - /* Offset of table for translation string offsets. */ - header.hash_tab_size = hash_tab_size; /* Size of used hashing table. */ - header.hash_tab_offset = - no_hash_table ? 0 : sizeof (header) - + 2 * (mlp->nitems * sizeof (struct string_desc)); - /* Offset of hashing table. */ + if (!omit_hash_table) + { + hash_tab_size = next_prime ((mlp->nitems * 4) / 3); + /* Ensure M > 2. */ + if (hash_tab_size <= 2) + hash_tab_size = 3; + } + else + hash_tab_size = 0; + + + /* Second pass: Fill the structure describing the header. At the same time, + compute the sizes and offsets of the non-string parts of the file. */ + + /* Magic number. */ + header.magic = _MAGIC; + /* Revision number of file format. */ + header.revision = (MO_REVISION_NUMBER << 16) + minor_revision; + + header_size = + (minor_revision == 0 + ? offsetof (struct mo_file_header, n_sysdep_segments) + : sizeof (struct mo_file_header)); + offset = header_size; + + /* Number of static string pairs. */ + header.nstrings = nstrings; + + /* Offset of table for original string offsets. */ + header.orig_tab_offset = offset; + offset += nstrings * sizeof (struct string_desc); + orig_tab = + (struct string_desc *) alloca (nstrings * sizeof (struct string_desc)); + + /* Offset of table for translated string offsets. */ + header.trans_tab_offset = offset; + offset += nstrings * sizeof (struct string_desc); + trans_tab = + (struct string_desc *) alloca (nstrings * sizeof (struct string_desc)); + + /* Size of hash table. */ + header.hash_tab_size = hash_tab_size; + /* Offset of hash table. */ + header.hash_tab_offset = offset; + offset += hash_tab_size * sizeof (nls_uint32); + + if (minor_revision >= 1) + { + /* Size of table describing system dependent segments. */ + header.n_sysdep_segments = n_sysdep_segments; + /* Offset of table describing system dependent segments. */ + header.sysdep_segments_offset = offset; + offset += n_sysdep_segments * sizeof (struct sysdep_segment); + + /* Number of system dependent string pairs. */ + header.n_sysdep_strings = n_sysdep_strings; + + /* Offset of table for original sysdep string offsets. */ + header.orig_sysdep_tab_offset = offset; + offset += n_sysdep_strings * sizeof (nls_uint32); + + /* Offset of table for translated sysdep string offsets. */ + header.trans_sysdep_tab_offset = offset; + offset += n_sysdep_strings * sizeof (nls_uint32); + + /* System dependent string descriptors. */ + sysdep_tab_offset = offset; + for (m = 0; m < 2; m++) + for (j = 0; j < n_sysdep_strings; j++) + offset += sizeof (struct sysdep_string) + + sysdep_msg_arr[j].str[m]->segmentcount + * sizeof (struct segment_pair); + } - /* Write the header out. */ - fwrite (&header, sizeof (header), 1, output_file); + end_offset = offset; - /* Allocate table for the all elements of the hashing table. */ - msg_arr = (struct id_str_pair *) alloca (mlp->nitems * sizeof (msg_arr[0])); - /* Read values from list into array. */ - for (j = 0; j < mlp->nitems; j++) - { - entry = mlp->item[j]; - - msg_arr[j].id = entry->msgid; - msg_arr[j].id_len = strlen (entry->msgid) + 1; - msg_arr[j].id_plural = entry->msgid_plural; - msg_arr[j].id_plural_len = - (entry->msgid_plural != NULL ? strlen (entry->msgid_plural) + 1 : 0); - msg_arr[j].str = entry->msgstr; - msg_arr[j].str_len = entry->msgstr_len; - } + /* Third pass: Write the non-string parts of the file. At the same time, + compute the offsets of each string, including the proper alignment. */ - /* Sort the table according to original string. */ - qsort (msg_arr, mlp->nitems, sizeof (msg_arr[0]), compare_id); + /* Write the header out. */ + fwrite (&header, header_size, 1, output_file); - /* Set offset to first byte after all the tables. */ - sd.offset = roundup (sizeof (header) - + mlp->nitems * sizeof (sd) - + mlp->nitems * sizeof (sd) - + hash_tab_size * sizeof (nls_uint32), - alignment); + /* Table for original string offsets. */ + /* Here output_file is at position header.orig_tab_offset. */ - /* Write out length and starting offset for all original strings. */ - for (cnt = 0; cnt < mlp->nitems; ++cnt) + for (j = 0; j < nstrings; j++) { + offset = roundup (offset, alignment); + orig_tab[j].length = + msg_arr[j].str[M_ID].length + msg_arr[j].id_plural_len; + orig_tab[j].offset = offset; + offset += orig_tab[j].length; /* Subtract 1 because of the terminating NUL. */ - sd.length = msg_arr[cnt].id_len + msg_arr[cnt].id_plural_len - 1; - fwrite (&sd, sizeof (sd), 1, output_file); - sd.offset += roundup (sd.length + 1, alignment); + orig_tab[j].length--; } + fwrite (orig_tab, nstrings * sizeof (struct string_desc), 1, output_file); + + /* Table for translated string offsets. */ + /* Here output_file is at position header.trans_tab_offset. */ - /* Write out length and starting offset for all translation strings. */ - for (cnt = 0; cnt < mlp->nitems; ++cnt) + for (j = 0; j < nstrings; j++) { + offset = roundup (offset, alignment); + trans_tab[j].length = msg_arr[j].str[M_STR].length; + trans_tab[j].offset = offset; + offset += trans_tab[j].length; /* Subtract 1 because of the terminating NUL. */ - sd.length = msg_arr[cnt].str_len - 1; - fwrite (&sd, sizeof (sd), 1, output_file); - sd.offset += roundup (sd.length + 1, alignment); + trans_tab[j].length--; } + fwrite (trans_tab, nstrings * sizeof (struct string_desc), 1, output_file); /* Skip this part when no hash table is needed. */ - if (!no_hash_table) + if (!omit_hash_table) { + nls_uint32 *hash_tab; + unsigned int j; + + /* Here output_file is at position header.hash_tab_offset. */ + /* Allocate room for the hashing table to be written out. */ hash_tab = (nls_uint32 *) alloca (hash_tab_size * sizeof (nls_uint32)); memset (hash_tab, '\0', hash_tab_size * sizeof (nls_uint32)); /* Insert all value in the hash table, following the algorithm described above. */ - for (cnt = 0; cnt < mlp->nitems; ++cnt) + for (j = 0; j < nstrings; j++) { - nls_uint32 hash_val = hash_string (msg_arr[cnt].id); + nls_uint32 hash_val = hash_string (msg_arr[j].str[M_ID].pointer); nls_uint32 idx = hash_val % hash_tab_size; if (hash_tab[idx] != 0) { /* We need the second hashing function. */ - nls_uint32 c = 1 + (hash_val % (hash_tab_size - 2)); + nls_uint32 incr = 1 + (hash_val % (hash_tab_size - 2)); do - if (idx >= hash_tab_size - c) - idx -= hash_tab_size - c; + if (idx >= hash_tab_size - incr) + idx -= hash_tab_size - incr; else - idx += c; + idx += incr; while (hash_tab[idx] != 0); } - hash_tab[idx] = cnt + 1; + hash_tab[idx] = j + 1; } /* Write the hash table out. */ - fwrite (hash_tab, sizeof (nls_uint32), hash_tab_size, output_file); + fwrite (hash_tab, hash_tab_size * sizeof (nls_uint32), 1, output_file); + + freea (hash_tab); } - /* Write bytes to make first string to be aligned. */ - cnt = sizeof (header) + 2 * mlp->nitems * sizeof (sd) - + hash_tab_size * sizeof (nls_uint32); - fwrite (&null, 1, roundup (cnt, alignment) - cnt, output_file); + if (minor_revision >= 1) + { + struct sysdep_segment *sysdep_segments_tab; + nls_uint32 *sysdep_tab; + size_t stoffset; + unsigned int i; + + /* Here output_file is at position header.sysdep_segments_offset. */ + + sysdep_segments_tab = + (struct sysdep_segment *) + alloca (n_sysdep_segments * sizeof (struct sysdep_segment)); + for (i = 0; i < n_sysdep_segments; i++) + { + offset = roundup (offset, alignment); + /* The "+ 1" accounts for the trailing NUL byte. */ + sysdep_segments_tab[i].length = sysdep_segments[i].length + 1; + sysdep_segments_tab[i].offset = offset; + offset += sysdep_segments_tab[i].length; + } + + fwrite (sysdep_segments_tab, + n_sysdep_segments * sizeof (struct sysdep_segment), 1, + output_file); + + freea (sysdep_segments_tab); + + sysdep_tab = + (nls_uint32 *) alloca (n_sysdep_strings * sizeof (nls_uint32)); + stoffset = sysdep_tab_offset; + + for (m = 0; m < 2; m++) + { + /* Here output_file is at position + m == M_ID -> header.orig_sysdep_tab_offset, + m == M_STR -> header.trans_sysdep_tab_offset. */ + + for (j = 0; j < n_sysdep_strings; j++) + { + sysdep_tab[j] = stoffset; + stoffset += sizeof (struct sysdep_string) + + sysdep_msg_arr[j].str[m]->segmentcount + * sizeof (struct segment_pair); + } + /* Write the table for original/translated sysdep string offsets. */ + fwrite (sysdep_tab, n_sysdep_strings * sizeof (nls_uint32), 1, + output_file); + } + + freea (sysdep_tab); + + /* Here output_file is at position sysdep_tab_offset. */ + + for (m = 0; m < 2; m++) + for (j = 0; j < n_sysdep_strings; j++) + { + struct pre_sysdep_message *msg = &sysdep_msg_arr[j]; + struct pre_sysdep_string *pre = msg->str[m]; + struct sysdep_string *str = + (struct sysdep_string *) + alloca (sizeof (struct sysdep_string) + + pre->segmentcount * sizeof (struct segment_pair)); + unsigned int i; + + offset = roundup (offset, alignment); + str->offset = offset; + for (i = 0; i <= pre->segmentcount; i++) + { + str->segments[i].segsize = pre->segments[i].segsize; + str->segments[i].sysdepref = pre->segments[i].sysdepref; + offset += str->segments[i].segsize; + } + if (m == M_ID && msg->id_plural_len > 0) + { + str->segments[pre->segmentcount].segsize += msg->id_plural_len; + offset += msg->id_plural_len; + } + fwrite (str, + sizeof (struct sysdep_string) + + pre->segmentcount * sizeof (struct segment_pair), + 1, output_file); + + freea (str); + } + } + + /* Here output_file is at position end_offset. */ + + freea (trans_tab); + freea (orig_tab); + + + /* Fourth pass: Write the strings. */ + + offset = end_offset; + + /* A few zero bytes for padding. */ + null = alloca (alignment); + memset (null, '\0', alignment); /* Now write the original strings. */ - for (cnt = 0; cnt < mlp->nitems; ++cnt) + for (j = 0; j < nstrings; j++) { - size_t len = msg_arr[cnt].id_len + msg_arr[cnt].id_plural_len; + fwrite (null, roundup (offset, alignment) - offset, 1, output_file); + offset = roundup (offset, alignment); - fwrite (msg_arr[cnt].id, msg_arr[cnt].id_len, 1, output_file); - if (msg_arr[cnt].id_plural_len > 0) - fwrite (msg_arr[cnt].id_plural, msg_arr[cnt].id_plural_len, 1, + fwrite (msg_arr[j].str[M_ID].pointer, msg_arr[j].str[M_ID].length, 1, + output_file); + if (msg_arr[j].id_plural_len > 0) + fwrite (msg_arr[j].id_plural, msg_arr[j].id_plural_len, 1, output_file); - fwrite (&null, 1, roundup (len, alignment) - len, output_file); + offset += msg_arr[j].str[M_ID].length + msg_arr[j].id_plural_len; } - /* Now write the translation strings. */ - for (cnt = 0; cnt < mlp->nitems; ++cnt) + /* Now write the translated strings. */ + for (j = 0; j < nstrings; j++) { - size_t len = msg_arr[cnt].str_len; + fwrite (null, roundup (offset, alignment) - offset, 1, output_file); + offset = roundup (offset, alignment); - fwrite (msg_arr[cnt].str, len, 1, output_file); - fwrite (&null, 1, roundup (len, alignment) - len, output_file); + fwrite (msg_arr[j].str[M_STR].pointer, msg_arr[j].str[M_STR].length, 1, + output_file); + offset += msg_arr[j].str[M_STR].length; } + + if (minor_revision >= 1) + { + unsigned int i; + + for (i = 0; i < n_sysdep_segments; i++) + { + fwrite (null, roundup (offset, alignment) - offset, 1, output_file); + offset = roundup (offset, alignment); + + fwrite (sysdep_segments[i].pointer, sysdep_segments[i].length, 1, + output_file); + fwrite (null, 1, 1, output_file); + offset += sysdep_segments[i].length + 1; + } + + for (m = 0; m < 2; m++) + for (j = 0; j < n_sysdep_strings; j++) + { + struct pre_sysdep_message *msg = &sysdep_msg_arr[j]; + struct pre_sysdep_string *pre = msg->str[m]; + + fwrite (null, roundup (offset, alignment) - offset, 1, + output_file); + offset = roundup (offset, alignment); + + for (i = 0; i <= pre->segmentcount; i++) + { + fwrite (pre->segments[i].segptr, pre->segments[i].segsize, 1, + output_file); + offset += pre->segments[i].segsize; + } + if (m == M_ID && msg->id_plural_len > 0) + { + fwrite (msg->id_plural, msg->id_plural_len, 1, output_file); + offset += msg->id_plural_len; + } + + freea (pre); + } + } + + freea (null); + freea (sysdep_msg_arr); + freea (msg_arr); } diff --git a/src/x-c.c b/src/x-c.c index 494552f8d..8f359718e 100644 --- a/src/x-c.c +++ b/src/x-c.c @@ -159,6 +159,9 @@ static void phase5_unget PARAMS ((token_ty *tp)); static void phaseX_get PARAMS ((token_ty *tp)); static void phase6_get PARAMS ((token_ty *tp)); static void phase6_unget PARAMS ((token_ty *tp)); +static bool is_inttypes_macro PARAMS ((const char *name)); +static void phase8a_get PARAMS ((token_ty *tp)); +static void phase8a_unget PARAMS ((token_ty *tp)); static void phase8_get PARAMS ((token_ty *tp)); static void x_c_lex PARAMS ((xgettext_token_ty *tp)); static bool extract_parenthesized PARAMS ((message_list_ty *mlp, @@ -1176,6 +1179,77 @@ phase6_unget (tp) } +/* 8a. Convert ISO C 99 section 7.8.1 format string directives to string + literal placeholders. */ + +/* Test for an ISO C 99 section 7.8.1 format string directive. */ +static bool +is_inttypes_macro (name) + const char *name; +{ + /* Syntax: + P R I { d | i | o | u | x | X } + { { | LEAST | FAST } { 8 | 16 | 32 | 64 } | MAX | PTR } */ + if (name[0] == 'P' && name[1] == 'R' && name[2] == 'I') + { + name += 3; + if (name[0] == 'd' || name[0] == 'i' || name[0] == 'o' || name[0] == 'u' + || name[0] == 'x' || name[0] == 'X') + { + name += 1; + if (name[0] == 'M' && name[1] == 'A' && name[2] == 'X' + && name[3] == '\0') + return true; + if (name[0] == 'P' && name[1] == 'T' && name[2] == 'R' + && name[3] == '\0') + return true; + if (name[0] == 'L' && name[1] == 'E' && name[2] == 'A' + && name[3] == 'S' && name[4] == 'T') + name += 5; + else if (name[0] == 'F' && name[1] == 'A' && name[2] == 'S' + && name[3] == 'T') + name += 4; + if (name[0] == '8' && name[1] == '\0') + return true; + if (name[0] == '1' && name[1] == '6' && name[2] == '\0') + return true; + if (name[0] == '3' && name[1] == '2' && name[2] == '\0') + return true; + if (name[0] == '6' && name[1] == '4' && name[2] == '\0') + return true; + } + } + return false; +} + +static void +phase8a_get (tp) + token_ty *tp; +{ + phase6_get (tp); + if (tp->type == token_type_name && is_inttypes_macro (tp->string)) + { + /* Turn PRIdXXX into "". */ + size_t len = strlen (tp->string); + char *new_string = (char *) xmalloc (len + 3); + new_string[0] = '<'; + memcpy (new_string + 1, tp->string, len); + new_string[len + 1] = '>'; + new_string[len + 2] = '\0'; + free (tp->string); + tp->string = new_string; + tp->type = token_type_string_literal; + } +} + +static void +phase8a_unget (tp) + token_ty *tp; +{ + phase6_unget (tp); +} + + /* 8. Concatenate adjacent string literals to form single string literals (because we don't expand macros, there are a few things we will miss). */ @@ -1184,7 +1258,7 @@ static void phase8_get (tp) token_ty *tp; { - phase6_get (tp); + phase8a_get (tp); if (tp->type != token_type_string_literal) return; for (;;) @@ -1192,14 +1266,14 @@ phase8_get (tp) token_ty tmp; size_t len; - phase6_get (&tmp); + phase8a_get (&tmp); if (tmp.type == token_type_white_space) continue; if (tmp.type == token_type_eoln) continue; if (tmp.type != token_type_string_literal) { - phase6_unget (&tmp); + phase8a_unget (&tmp); return; } len = strlen (tp->string); diff --git a/tests/ChangeLog b/tests/ChangeLog index 36461dd04..774d3e76d 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,16 @@ +2002-07-21 Bruno Haible + + * format-c-3: New file. + * format-c-3-prg.c: New file. + * format-c-4: New file. + * format-c-4-prg.c: New file. + * msgfmt-12: New file. + * xgettext-22: New file. + * Makefile.am (TESTS): Add msgfmt-12, xgettext-22, format-c-3, + format-c-4. + (noinst_PROGRAMS): Add fc3, fc4. + (fc3_SOURCES, fc3_LDADD, fc4_SOURCES, fc4_LDADD): New variables. + 2002-07-17 Bruno Haible * gettext-0.11.3 released. diff --git a/tests/Makefile.am b/tests/Makefile.am index 65bd5fc00..925fe30ff 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -34,7 +34,7 @@ TESTS = gettext-1 gettext-2 \ msgexec-1 msgexec-2 \ msgfilter-1 msgfilter-2 \ msgfmt-1 msgfmt-2 msgfmt-3 msgfmt-4 msgfmt-5 msgfmt-6 msgfmt-7 \ - msgfmt-8 msgfmt-9 msgfmt-10 msgfmt-11 \ + msgfmt-8 msgfmt-9 msgfmt-10 msgfmt-11 msgfmt-12 \ msggrep-1 msggrep-2 msggrep-3 msggrep-4 msggrep-5 \ msgmerge-1 msgmerge-2 msgmerge-3 msgmerge-4 msgmerge-5 msgmerge-6 \ msgmerge-7 msgmerge-8 msgmerge-9 msgmerge-10 msgmerge-11 msgmerge-12 \ @@ -45,9 +45,9 @@ TESTS = gettext-1 gettext-2 \ xgettext-1 xgettext-2 xgettext-3 xgettext-4 xgettext-5 xgettext-6 \ xgettext-7 xgettext-8 xgettext-9 xgettext-10 xgettext-11 xgettext-12 \ xgettext-13 xgettext-14 xgettext-15 xgettext-16 xgettext-17 \ - xgettext-18 xgettext-19 xgettext-20 xgettext-21 \ + xgettext-18 xgettext-19 xgettext-20 xgettext-21 xgettext-22 \ format-awk-1 format-awk-2 \ - format-c-1 format-c-2 \ + format-c-1 format-c-2 format-c-3 format-c-4 \ format-elisp-1 format-elisp-2 \ format-java-1 format-java-2 \ format-librep-1 format-librep-2 \ @@ -136,13 +136,17 @@ DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@ LDADD = $(LDADD_@USE_INCLUDED_LIBINTL@) LDADD_yes = ../intl/libintl.la LDADD_no = ../intl/libgnuintl.la @LTLIBINTL@ -noinst_PROGRAMS = tstgettext tstngettext cake +noinst_PROGRAMS = tstgettext tstngettext cake fc3 fc4 tstgettext_SOURCES = tstgettext.c setlocale.c tstgettext_LDADD = ../lib/libgettextlib.la $(LDADD) tstngettext_SOURCES = tstngettext.c setlocale.c tstngettext_LDADD = ../lib/libgettextlib.la $(LDADD) cake_SOURCES = plural-1-prg.c setlocale.c cake_LDADD = ../lib/libgettextlib.la $(LDADD) +fc3_SOURCES = format-c-3-prg.c setlocale.c +fc3_LDADD = ../lib/libgettextlib.la $(LDADD) +fc4_SOURCES = format-c-4-prg.c setlocale.c +fc4_LDADD = ../lib/libgettextlib.la $(LDADD) # Help maintaining config.rpath. rpathcfg: rpathcfg.sh diff --git a/tests/format-c-3 b/tests/format-c-3 new file mode 100755 index 000000000..7afd902f5 --- /dev/null +++ b/tests/format-c-3 @@ -0,0 +1,51 @@ +#! /bin/sh + +# Test ISO C 99 format string directives. + +tmpfiles="" +trap 'rm -fr $tmpfiles' 1 2 3 15 + +tmpfiles="$tmpfiles fc3.pot" +: ${XGETTEXT=xgettext} +${XGETTEXT} -o fc3.pot --omit-header --no-location format-c-3-prg.c + +tmpfiles="$tmpfiles fc3.ok" +cat < fc3.ok +#, c-format +msgid "father of % children" +msgstr "" +EOF + +: ${DIFF=diff} +${DIFF} fc3.ok fc3.pot || exit 1 + +tmpfiles="$tmpfiles de.po" +cat < de.po +#, c-format +msgid "father of % children" +msgstr "Vater von % Kindern" +EOF + +tmpfiles="$tmpfiles de" +test -d de || mkdir de +test -d de/LC_MESSAGES || mkdir de/LC_MESSAGES + +: ${MSGFMT=msgfmt} +${MSGFMT} -o de/LC_MESSAGES/fc3.mo de.po + +tmpfiles="$tmpfiles de.po.tmp" +: ${MSGUNFMT=msgunfmt} +${MSGUNFMT} de/LC_MESSAGES/fc3.mo -o de.po.tmp + +tmpfiles="$tmpfiles de.po.strip" +sed 1d < de.po > de.po.strip + +: ${DIFF=diff} +${DIFF} de.po.strip de.po.tmp || exit 1 + +LANGUAGE= ./fc3 de_DE +result=$? + +rm -fr $tmpfiles + +exit $result diff --git a/tests/format-c-3-prg.c b/tests/format-c-3-prg.c new file mode 100644 index 000000000..8ff0e09c6 --- /dev/null +++ b/tests/format-c-3-prg.c @@ -0,0 +1,89 @@ +/* Test program, used by the format-c-3 test. + Copyright (C) 2002 Free Software Foundation, Inc. + + 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 2, 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, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#ifdef HAVE_INTTYPES_H +# include +#endif +#include "xsetenv.h" + +/* Make sure we use the included libintl, not the system's one. */ +#undef _LIBINTL_H +#include "libgnuintl.h" + +#define _(string) gettext (string) + +/* Fallback definition. */ +#ifndef PRId8 +# define PRId8 "d" +#endif + +int main (argc, argv) + int argc; + char *argv[]; +{ +/* This test requires ANSI C string concatenation. */ +#ifdef __STDC__ + unsigned char n = 5; + const char *s; + const char *c1; + const char *c2; + char buf[100]; + + xsetenv ("LC_ALL", argv[1], 1); + if (setlocale (LC_ALL, "") == NULL) + { + fprintf (stderr, "Couldn't set locale.\n"); + exit (1); + } + + textdomain ("fc3"); + bindtextdomain ("fc3", "."); + + s = gettext ("father of %"PRId8" children"); + c1 = "Vater von %"; c2 = " Kindern"; + + if (!(strlen (s) > strlen (c1) + strlen (c2) + && memcmp (s, c1, strlen (c1)) == 0 + && memcmp (s + strlen (s) - strlen (c2), c2, strlen (c2)) == 0)) + { + fprintf (stderr, "String not translated.\n"); + exit (1); + } + if (strchr (s, '<') != NULL || strchr (s, '>') != NULL) + { + fprintf (stderr, "Translation contains <...> markers.\n"); + exit (1); + } + sprintf (buf, s, n); + if (strcmp (buf, "Vater von 5 Kindern") != 0) + { + fprintf (stderr, "printf of translation wrong.\n"); + exit (1); + } + return 0; +#else + exit (77); +#endif +} diff --git a/tests/format-c-4 b/tests/format-c-4 new file mode 100755 index 000000000..2359a241e --- /dev/null +++ b/tests/format-c-4 @@ -0,0 +1,55 @@ +#! /bin/sh + +# Test ISO C 99 format string directives with plural forms. + +tmpfiles="" +trap 'rm -fr $tmpfiles' 1 2 3 15 + +tmpfiles="$tmpfiles fc4.pot" +: ${XGETTEXT=xgettext} +${XGETTEXT} -o fc4.pot --omit-header --no-location format-c-4-prg.c + +tmpfiles="$tmpfiles fc4.ok" +cat < fc4.ok +#, c-format +msgid "father of % child" +msgid_plural "father of % children" +msgstr[0] "" +msgstr[1] "" +EOF + +: ${DIFF=diff} +${DIFF} fc4.ok fc4.pot || exit 1 + +tmpfiles="$tmpfiles de.po" +cat < de.po +#, c-format +msgid "father of % child" +msgid_plural "father of % children" +msgstr[0] "Vater eines Kindes" +msgstr[1] "Vater von % Kindern" +EOF + +tmpfiles="$tmpfiles de" +test -d de || mkdir de +test -d de/LC_MESSAGES || mkdir de/LC_MESSAGES + +: ${MSGFMT=msgfmt} +${MSGFMT} -o de/LC_MESSAGES/fc4.mo de.po + +tmpfiles="$tmpfiles de.po.tmp" +: ${MSGUNFMT=msgunfmt} +${MSGUNFMT} de/LC_MESSAGES/fc4.mo -o de.po.tmp + +tmpfiles="$tmpfiles de.po.strip" +sed 1d < de.po > de.po.strip + +: ${DIFF=diff} +${DIFF} de.po.strip de.po.tmp || exit 1 + +LANGUAGE= ./fc4 de_DE +result=$? + +rm -fr $tmpfiles + +exit $result diff --git a/tests/format-c-4-prg.c b/tests/format-c-4-prg.c new file mode 100644 index 000000000..c764aac80 --- /dev/null +++ b/tests/format-c-4-prg.c @@ -0,0 +1,89 @@ +/* Test program, used by the format-c-4 test. + Copyright (C) 2002 Free Software Foundation, Inc. + + 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 2, 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, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#ifdef HAVE_INTTYPES_H +# include +#endif +#include "xsetenv.h" + +/* Make sure we use the included libintl, not the system's one. */ +#undef _LIBINTL_H +#include "libgnuintl.h" + +#define _(string) gettext (string) + +/* Fallback definition. */ +#ifndef PRId8 +# define PRId8 "d" +#endif + +int main (argc, argv) + int argc; + char *argv[]; +{ +/* This test requires ANSI C string concatenation. */ +#ifdef __STDC__ + unsigned char n = 5; + const char *s; + const char *c1; + const char *c2; + char buf[100]; + + xsetenv ("LC_ALL", argv[1], 1); + if (setlocale (LC_ALL, "") == NULL) + { + fprintf (stderr, "Couldn't set locale.\n"); + exit (1); + } + + textdomain ("fc4"); + bindtextdomain ("fc4", "."); + + s = ngettext ("father of %"PRId8" child", "father of %"PRId8" children", n); + c1 = "Vater von %"; c2 = " Kindern"; + + if (!(strlen (s) > strlen (c1) + strlen (c2) + && memcmp (s, c1, strlen (c1)) == 0 + && memcmp (s + strlen (s) - strlen (c2), c2, strlen (c2)) == 0)) + { + fprintf (stderr, "String not translated.\n"); + exit (1); + } + if (strchr (s, '<') != NULL || strchr (s, '>') != NULL) + { + fprintf (stderr, "Translation contains <...> markers.\n"); + exit (1); + } + sprintf (buf, s, n); + if (strcmp (buf, "Vater von 5 Kindern") != 0) + { + fprintf (stderr, "printf of translation wrong.\n"); + exit (1); + } + return 0; +#else + exit (77); +#endif +} diff --git a/tests/msgfmt-12 b/tests/msgfmt-12 new file mode 100755 index 000000000..427b83333 --- /dev/null +++ b/tests/msgfmt-12 @@ -0,0 +1,56 @@ +#! /bin/sh + +# Test ISO C 99 format string directives. + +tmpfiles="" +trap 'rm -fr $tmpfiles' 1 2 3 15 + +tmpfiles="$tmpfiles mf-12.po" +cat <<\EOF > mf-12.po +msgid "" +msgstr "Content-Type: text/plain; charset=ISO-8859-1\n" + +#, c-format +msgid "File size is: %" +msgstr "Dateigröße ist: %" + +#, c-format +msgid "File age is %10 microseconds" +msgstr "Datei ist %10 Mikrosekunden alt." + +msgid " errors" +msgstr " Fehler" +EOF + +tmpfiles="$tmpfiles mf-12.mo" +: ${MSGFMT=msgfmt} +${MSGFMT} -o mf-12.mo mf-12.po +test $? = 0 || { rm -fr $tmpfiles; exit 1; } + +tmpfiles="$tmpfiles mf-12.out" +: ${MSGUNFMT=msgunfmt} +${MSGUNFMT} mf-12.mo -o mf-12.out +test $? = 0 || { rm -fr $tmpfiles; exit 1; } + +tmpfiles="$tmpfiles mf-12.ok" +cat <<\EOF > mf-12.ok +msgid "" +msgstr "Content-Type: text/plain; charset=ISO-8859-1\n" + +msgid " errors" +msgstr " Fehler" + +msgid "File size is: %" +msgstr "Dateigröße ist: %" + +msgid "File age is %10 microseconds" +msgstr "Datei ist %10 Mikrosekunden alt." +EOF + +: ${DIFF=diff} +${DIFF} mf-12.ok mf-12.out +result=$? + +rm -fr $tmpfiles + +exit $result diff --git a/tests/xgettext-22 b/tests/xgettext-22 new file mode 100755 index 000000000..a250a7cda --- /dev/null +++ b/tests/xgettext-22 @@ -0,0 +1,47 @@ +#! /bin/sh + +# Test recognition of ISO C 99 format string directives. + +tmpfiles="" +trap 'rm -fr $tmpfiles' 1 2 3 15 + +tmpfiles="$tmpfiles xg-test22.c" +cat < xg-test22.c +void foo () +{ + printf (_("File size is: %" PRId64), size); + printf (_("File age is %10" PRIdMAX " microseconds"), age); + printf (_(PRIXFAST16 " errors"), nerrs); + printf (_(PRIXFAT16 " mistakes"), nerrs); +} +EOF + +tmpfiles="$tmpfiles xg-test22.po" +: ${XGETTEXT=xgettext} +${XGETTEXT} --omit-header --no-location -k_ -o xg-test22.po xg-test22.c +test $? = 0 || { rm -fr $tmpfiles; exit 1; } + +tmpfiles="$tmpfiles xg-test22.ok" +cat < xg-test22.ok +#, c-format +msgid "File size is: %" +msgstr "" + +#, c-format +msgid "File age is %10 microseconds" +msgstr "" + +msgid " errors" +msgstr "" + +msgid " mistakes" +msgstr "" +EOF + +: ${DIFF=diff} +${DIFF} xg-test22.ok xg-test22.po +result=$? + +rm -fr $tmpfiles + +exit $result