+Version 0.11.4 - July 2002
+
+* The tools now know about the ISO C 99 <inttypes.h> format string directive
+ macros PRId64, PRIxMAX etc.
+\f
Version 0.11.3 - July 2002
* New program:
+2002-07-21 Bruno Haible <bruno@clisp.org>
+
+ * gettext.texi (Preparing Strings): Update section about <inttypes.h>
+ 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 <bruno@clisp.org>
* gettext.texi (aclocal): Mention intdiv0.m4.
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{<inttypes.h>} 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<PRId64>\n".
+The translators will provide a translation containing "%0<PRId64>"
+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{<inttypes.h>} 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
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},
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{<inttypes.h>} 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.
+2002-07-21 Bruno Haible <bruno@clisp.org>
+
+ * 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 <bruno@clisp.org>
* gettext-0.11.3 released.
size_t *lengthp;
{
struct loaded_domain *domain;
+ nls_uint32 nstrings;
size_t act;
char *result;
size_t resultlen;
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);
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;
}
size_t top, bottom;
bottom = 0;
- top = domain->nstrings;
+ top = nstrings;
while (bottom < top)
{
int cmp_val;
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
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;
#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;
/* 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
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 */
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__
# undef HAVE_MMAP
#endif
+#if defined HAVE_STDINT_H_WITH_UINTMAX || defined _LIBC
+# include <stdint.h>
+#endif
+#if defined HAVE_INTTYPES_H || defined _LIBC
+# include <inttypes.h>
+#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 <inttypes.h>.
+ 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
# 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 *
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;
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);
_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);
+2002-07-21 Bruno Haible <bruno@clisp.org>
+
+ * 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 <bruno@clisp.org>
* gettext-0.11.3 released.
-/* 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
# define TMP_MAX 238328
#endif
-#if HAVE_STDINT_H || _LIBC
+#if HAVE_STDINT_H_WITH_UINTMAX || _LIBC
# include <stdint.h>
#endif
-#if HAVE_INTTYPES_H || _LIBC
+#if HAVE_INTTYPES_H_WITH_UINTMAX || _LIBC
# include <inttypes.h>
#endif
+2002-07-21 Bruno Haible <bruno@clisp.org>
+
+ * 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 <bruno@clisp.org>
* gettext.m4 (AM_GNU_GETTEXT_VERSION): New macro.
## 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 \
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 <inttypes.h> 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
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], []))
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 <libintl.h>
-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],
LIBS="$LIBS $LIBINTL"
dnl Now see whether libintl exists and does not depend on libiconv.
AC_TRY_LINK([#include <libintl.h>
-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"
if test "$gt_cv_func_gnugettext_libintl" != yes && test -n "$LIBICONV"; then
LIBS="$LIBS $LIBICONV"
AC_TRY_LINK([#include <libintl.h>
-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"
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])
--- /dev/null
+# 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 <inttypes.h> exists and doesn't clash with
+# <sys/types.h>.
+
+AC_DEFUN([gt_HEADER_INTTYPES_H],
+[
+ AC_CACHE_CHECK([for inttypes.h], gt_cv_header_inttypes_h,
+ [
+ AC_TRY_COMPILE(
+ [#include <sys/types.h>
+#include <inttypes.h>],
+ [], 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 <inttypes.h> exists and doesn't clash with <sys/types.h>.])
+ fi
+])
-# 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
dnl From Paul Eggert.
-# Define HAVE_INTTYPES_H if <inttypes.h> exists,
+# Define HAVE_INTTYPES_H_WITH_UINTMAX if <inttypes.h> exists,
# doesn't clash with <sys/types.h>, and declares uintmax_t.
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 <inttypes.h> exists, doesn't clash with <sys/types.h>,
and declares uintmax_t. ])
fi
-# 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
dnl From Paul Eggert.
-# Define HAVE_STDINT_H if <stdint.h> exists,
+# Define HAVE_STDINT_H_WITH_UINTMAX if <stdint.h> exists,
# doesn't clash with <sys/types.h>, and declares uintmax_t.
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 <stdint.h> exists, doesn't clash with <sys/types.h>,
and declares uintmax_t. ])
fi
+2002-07-21 Bruno Haible <bruno@clisp.org>
+
+ * gettextize.in: Add inttypes.m4, inttypes_h.m4, stdint_h.m4,
+ uintmax_t.m4 to m4filelist.
+
2002-07-19 Bruno Haible <bruno@clisp.org>
* gettextize.in: Copy also intdiv0.m4.
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.)
+2002-07-21 Bruno Haible <bruno@clisp.org>
+
+ * 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 <inttypes.h> 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 <schwab@suse.de>
* plural-eval.c: Include <signal.h> if !INTDIV0_RAISES_SIGFPE.
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,
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,
/* 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
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.
spec.allocated = 0;
numbered = NULL;
spec.unnumbered = NULL;
+ spec.c99_directives_count = 0;
+ spec.c99_directives = NULL;
for (; *format != '\0';)
if (*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)
free (numbered);
if (spec.unnumbered != NULL)
free (spec.unnumbered);
+ if (spec.c99_directives != NULL)
+ free (spec.c99_directives);
return NULL;
}
if (spec->unnumbered != NULL)
free (spec->unnumbered);
+ if (spec->c99_directives != NULL)
+ free (spec->c99_directives);
free (spec);
}
};
+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
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;
/* Table of all format string parsers. */
extern struct formatstring_parser *formatstring_parsers[NFORMATS];
+/* Returns an array of the ISO C 99 <inttypes.h> 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 */
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)
{
#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)
{
_("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:
}
}
- /* 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)
#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 <sys/param.h>. */
#ifndef roundup
# if defined __GNUC__ && __GNUC__ >= 2
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 <inttypes.h>
+ 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
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);
}
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,
}
+/* 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 "<PRIdXXX>". */
+ 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). */
phase8_get (tp)
token_ty *tp;
{
- phase6_get (tp);
+ phase8a_get (tp);
if (tp->type != token_type_string_literal)
return;
for (;;)
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);
+2002-07-21 Bruno Haible <bruno@clisp.org>
+
+ * 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 <bruno@clisp.org>
* gettext-0.11.3 released.
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 \
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 \
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
--- /dev/null
+#! /bin/sh
+
+# Test ISO C 99 <inttypes.h> 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 <<EOF > fc3.ok
+#, c-format
+msgid "father of %<PRId8> children"
+msgstr ""
+EOF
+
+: ${DIFF=diff}
+${DIFF} fc3.ok fc3.pot || exit 1
+
+tmpfiles="$tmpfiles de.po"
+cat <<EOF > de.po
+#, c-format
+msgid "father of %<PRId8> children"
+msgstr "Vater von %<PRId8> 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
--- /dev/null
+/* 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 <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#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
+}
--- /dev/null
+#! /bin/sh
+
+# Test ISO C 99 <inttypes.h> 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 <<EOF > fc4.ok
+#, c-format
+msgid "father of %<PRId8> child"
+msgid_plural "father of %<PRId8> children"
+msgstr[0] ""
+msgstr[1] ""
+EOF
+
+: ${DIFF=diff}
+${DIFF} fc4.ok fc4.pot || exit 1
+
+tmpfiles="$tmpfiles de.po"
+cat <<EOF > de.po
+#, c-format
+msgid "father of %<PRId8> child"
+msgid_plural "father of %<PRId8> children"
+msgstr[0] "Vater eines Kindes"
+msgstr[1] "Vater von %<PRId8> 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
--- /dev/null
+/* 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 <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#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
+}
--- /dev/null
+#! /bin/sh
+
+# Test ISO C 99 <inttypes.h> 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: %<PRId64>"
+msgstr "Dateigröße ist: %<PRId64>"
+
+#, c-format
+msgid "File age is %10<PRIdMAX> microseconds"
+msgstr "Datei ist %10<PRIdMAX> Mikrosekunden alt."
+
+msgid "<PRIXFAST16> errors"
+msgstr "<PRIXFAST16> 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 "<PRIXFAST16> errors"
+msgstr "<PRIXFAST16> Fehler"
+
+msgid "File size is: %<PRId64>"
+msgstr "Dateigröße ist: %<PRId64>"
+
+msgid "File age is %10<PRIdMAX> microseconds"
+msgstr "Datei ist %10<PRIdMAX> Mikrosekunden alt."
+EOF
+
+: ${DIFF=diff}
+${DIFF} mf-12.ok mf-12.out
+result=$?
+
+rm -fr $tmpfiles
+
+exit $result
--- /dev/null
+#! /bin/sh
+
+# Test recognition of ISO C 99 <inttypes.h> format string directives.
+
+tmpfiles=""
+trap 'rm -fr $tmpfiles' 1 2 3 15
+
+tmpfiles="$tmpfiles xg-test22.c"
+cat <<EOF > 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 <<EOF > xg-test22.ok
+#, c-format
+msgid "File size is: %<PRId64>"
+msgstr ""
+
+#, c-format
+msgid "File age is %10<PRIdMAX> microseconds"
+msgstr ""
+
+msgid "<PRIXFAST16> errors"
+msgstr ""
+
+msgid " mistakes"
+msgstr ""
+EOF
+
+: ${DIFF=diff}
+${DIFF} xg-test22.ok xg-test22.po
+result=$?
+
+rm -fr $tmpfiles
+
+exit $result