]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
Add support for ISO C 99 <inttypes.h> format string directive macros.
authorBruno Haible <bruno@clisp.org>
Tue, 23 Jul 2002 12:33:13 +0000 (12:33 +0000)
committerBruno Haible <bruno@clisp.org>
Tue, 23 Jun 2009 10:08:41 +0000 (12:08 +0200)
34 files changed:
NEWS
doc/ChangeLog
doc/gettext.texi
intl/ChangeLog
intl/dcigettext.c
intl/gettextP.h
intl/gmo.h
intl/libgnuintl.h
intl/loadmsgcat.c
lib/ChangeLog
lib/mkdtemp.c
m4/ChangeLog
m4/Makefile.am
m4/gettext.m4
m4/inttypes.m4 [new file with mode: 0644]
m4/inttypes_h.m4
m4/stdint_h.m4
misc/ChangeLog
misc/gettextize.in
src/ChangeLog
src/format-c.c
src/format.h
src/msgfmt.c
src/read-mo.c
src/write-mo.c
src/x-c.c
tests/ChangeLog
tests/Makefile.am
tests/format-c-3 [new file with mode: 0755]
tests/format-c-3-prg.c [new file with mode: 0644]
tests/format-c-4 [new file with mode: 0755]
tests/format-c-4-prg.c [new file with mode: 0644]
tests/msgfmt-12 [new file with mode: 0755]
tests/xgettext-22 [new file with mode: 0755]

diff --git a/NEWS b/NEWS
index 3ae7fe18541f60ca2fb7998bb947cd2dc9090f88..577f15e08d8145a7a6363e2c9d09d49e972d1091 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,8 @@
+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:
index b34b307a46a0a0a68a3b2afdfe5e821a8d2abaa5..7809d66c0eb91b63181bc60dc7e14a34c1c722dd 100644 (file)
@@ -1,3 +1,11 @@
+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.
index 1f6e0de289ca54345164197639c59095620880c9..cc699b0f9827f32c11959c4c97dd648d452490d9 100644 (file)
@@ -1885,34 +1885,31 @@ integer through @code{printf}. It expands to a constant string, usually
 Assume you have code like
 
 @example
-printf ("The amount is %0" PRId64 "\n"), number);
+printf ("The amount is %0" PRId64 "\n", number);
 @end example
 
 @noindent
-After marking, this cannot become
+The @code{gettext} tools and library have special support for these
+@code{<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
 
@@ -5972,8 +5969,10 @@ AC_CONFIG_AUX_DIR([@var{subdir}])
 If you do not have an @file{aclocal.m4} file in your distribution,
 the simplest is to concatenate the files @file{codeset.m4},
 @file{gettext.m4}, @file{glibc21.m4}, @file{iconv.m4}, @file{intdiv0.m4},
-@file{isc-posix.m4}, @file{lcmessage.m4}, @file{lib-ld.m4}, @file{lib-link.m4},
-@file{lib-prefix.m4}, @file{progtest.m4} from GNU @code{gettext}'s
+@file{inttypes.m4}, @file{inttypes_h.m4}, @file{isc-posix.m4},
+@file{lcmessage.m4}, @file{lib-ld.m4}, @file{lib-link.m4},
+@file{lib-prefix.m4}, @file{progtest.m4}, @file{stdint_h.m4},
+@file{uintmax_t.m4} from GNU @code{gettext}'s
 @file{m4/} directory into a single file.  If you have suppressed the
 @file{intl/} directory, only @file{gettext.m4}, @file{iconv.m4},
 @file{lib-ld.m4}, @file{lib-link.m4}, @file{lib-prefix.m4},
@@ -6326,7 +6325,13 @@ presence of @code{AM_DISABLE_SHARED}). If @var{intlsymbol} is
 
 If @var{needsymbol} is specified and is @samp{need-ngettext}, then GNU
 gettext implementations (in libc or libintl) without the @code{ngettext()}
-function will be ignored.
+function will be ignored.  If @var{needsymbol} is specified and is
+@samp{need-formatstring-macros}, then GNU gettext implementations that don't
+support the ISO C 99 @file{<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.
index bc2120434322d376ff347db4fc740ab98751a0a8..af4cfd3f9e7da6705026cb8d8da8c41d403ad78a 100644 (file)
@@ -1,3 +1,26 @@
+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.
index bec5dd33e3d5a93e37421d5ada2137aee9e3c3c0..f2f01524c03c667818cb752380a7122b470f124d 100644 (file)
@@ -697,6 +697,7 @@ _nl_find_msg (domain_file, domainbinding, msgid, lengthp)
      size_t *lengthp;
 {
   struct loaded_domain *domain;
+  nls_uint32 nstrings;
   size_t act;
   char *result;
   size_t resultlen;
@@ -709,8 +710,10 @@ _nl_find_msg (domain_file, domainbinding, msgid, lengthp)
 
   domain = (struct loaded_domain *) domain_file->data;
 
+  nstrings = domain->nstrings;
+
   /* Locate the MSGID and its translation.  */
-  if (domain->hash_size > 2 && domain->hash_tab != NULL)
+  if (domain->hash_tab != NULL)
     {
       /* Use the hashing table.  */
       nls_uint32 len = strlen (msgid);
@@ -720,22 +723,30 @@ _nl_find_msg (domain_file, domainbinding, msgid, lengthp)
 
       while (1)
        {
-         nls_uint32 nstr = W (domain->must_swap, domain->hash_tab[idx]);
+         nls_uint32 nstr =
+           W (domain->must_swap_hash_tab, domain->hash_tab[idx]);
 
          if (nstr == 0)
            /* Hash table entry is empty.  */
            return NULL;
 
-         /* Compare msgid with the original string at index nstr-1.
+         nstr--;
+
+         /* Compare msgid with the original string at index nstr.
             We compare the lengths with >=, not ==, because plural entries
             are represented by strings with an embedded NUL.  */
-         if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) >= len
-             && (strcmp (msgid,
-                         domain->data + W (domain->must_swap,
-                                           domain->orig_tab[nstr - 1].offset))
-                 == 0))
+         if (nstr < nstrings
+             ? W (domain->must_swap, domain->orig_tab[nstr].length) >= len
+               && (strcmp (msgid,
+                           domain->data + W (domain->must_swap,
+                                             domain->orig_tab[nstr].offset))
+                   == 0)
+             : domain->orig_sysdep_tab[nstr - nstrings].length > len
+               && (strcmp (msgid,
+                           domain->orig_sysdep_tab[nstr - nstrings].pointer)
+                   == 0))
            {
-             act = nstr - 1;
+             act = nstr;
              goto found;
            }
 
@@ -753,7 +764,7 @@ _nl_find_msg (domain_file, domainbinding, msgid, lengthp)
       size_t top, bottom;
 
       bottom = 0;
-      top = domain->nstrings;
+      top = nstrings;
       while (bottom < top)
        {
          int cmp_val;
@@ -776,9 +787,17 @@ _nl_find_msg (domain_file, domainbinding, msgid, lengthp)
  found:
   /* The translation was found at index ACT.  If we have to convert the
      string to use a different character set, this is the time.  */
-  result = ((char *) domain->data
-           + W (domain->must_swap, domain->trans_tab[act].offset));
-  resultlen = W (domain->must_swap, domain->trans_tab[act].length) + 1;
+  if (act < nstrings)
+    {
+      result = (char *)
+       (domain->data + W (domain->must_swap, domain->trans_tab[act].offset));
+      resultlen = W (domain->must_swap, domain->trans_tab[act].length) + 1;
+    }
+  else
+    {
+      result = (char *) domain->trans_sysdep_tab[act - nstrings].pointer;
+      resultlen = domain->trans_sysdep_tab[act - nstrings].length;
+    }
 
 #if defined _LIBC || HAVE_ICONV
   if (domain->codeset_cntr
@@ -811,8 +830,9 @@ _nl_find_msg (domain_file, domainbinding, msgid, lengthp)
         NULs.  */
 
       if (domain->conv_tab == NULL
-         && ((domain->conv_tab = (char **) calloc (domain->nstrings,
-                                                   sizeof (char *)))
+         && ((domain->conv_tab =
+                (char **) calloc (nstrings + domain->n_sysdep_strings,
+                                  sizeof (char *)))
              == NULL))
        /* Mark that we didn't succeed allocating a table.  */
        domain->conv_tab = (char **) -1;
index 0fff5a9e9b2d608bc97098d666ed6cd56bf06fde..f085c59bb963e95d1ca7f21cc5907bb233a6a4a6 100644 (file)
@@ -76,18 +76,50 @@ SWAP (i)
 #endif
 
 
+/* In-memory representation of system dependent string.  */
+struct sysdep_string_desc
+{
+  /* Length of addressed string, including the trailing NUL.  */
+  size_t length;
+  /* Pointer to addressed string.  */
+  const char *pointer;
+};
+
 /* The representation of an opened message catalog.  */
 struct loaded_domain
 {
+  /* Pointer to memory containing the .mo file.  */
   const char *data;
+  /* 1 if the memory is mmap()ed, 0 if the memory is malloc()ed.  */
   int use_mmap;
+  /* Size of mmap()ed memory.  */
   size_t mmap_size;
+  /* 1 if the .mo file uses a different endianness than this machine.  */
   int must_swap;
+  /* Pointer to additional malloc()ed memory.  */
+  void *malloced;
+
+  /* Number of static strings pairs.  */
   nls_uint32 nstrings;
-  struct string_desc *orig_tab;
-  struct string_desc *trans_tab;
+  /* Pointer to descriptors of original strings in the file.  */
+  const struct string_desc *orig_tab;
+  /* Pointer to descriptors of translated strings in the file.  */
+  const struct string_desc *trans_tab;
+
+  /* Number of system dependent strings pairs.  */
+  nls_uint32 n_sysdep_strings;
+  /* Pointer to descriptors of original sysdep strings.  */
+  const struct sysdep_string_desc *orig_sysdep_tab;
+  /* Pointer to descriptors of translated sysdep strings.  */
+  const struct sysdep_string_desc *trans_sysdep_tab;
+
+  /* Size of hash table.  */
   nls_uint32 hash_size;
-  nls_uint32 *hash_tab;
+  /* Pointer to hash table.  */
+  const nls_uint32 *hash_tab;
+  /* 1 if the hash table uses a different endianness than this machine.  */
+  int must_swap_hash_tab;
+
   int codeset_cntr;
 #ifdef _LIBC
   __gconv_t conv;
index f05ae470c72901f9507835c2493e64ef47ebcdca..d1fe4d6b8fd4a23eac6a13b8cd6114629d7c1ffe 100644 (file)
@@ -1,5 +1,5 @@
 /* Description of GNU message catalog format: general file layout.
-   Copyright (C) 1995, 1997, 2000, 2001 Free Software Foundation, Inc.
+   Copyright (C) 1995, 1997, 2000-2002 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU Library General Public License as published
@@ -75,26 +75,74 @@ struct mo_file_header
   nls_uint32 magic;
   /* The revision number of the file format.  */
   nls_uint32 revision;
+
+  /* The following are only used in .mo files with major revision 0.  */
+
   /* The number of strings pairs.  */
   nls_uint32 nstrings;
   /* Offset of table with start offsets of original strings.  */
   nls_uint32 orig_tab_offset;
-  /* Offset of table with start offsets of translation strings.  */
+  /* Offset of table with start offsets of translated strings.  */
   nls_uint32 trans_tab_offset;
-  /* Size of hashing table.  */
+  /* Size of hash table.  */
   nls_uint32 hash_tab_size;
-  /* Offset of first hashing entry.  */
+  /* Offset of first hash table entry.  */
   nls_uint32 hash_tab_offset;
+
+  /* The following are only used in .mo files with minor revision >= 1.  */
+
+  /* The number of system dependent segments.  */
+  nls_uint32 n_sysdep_segments;
+  /* Offset of table describing system dependent segments.  */
+  nls_uint32 sysdep_segments_offset;
+  /* The number of system dependent strings pairs.  */
+  nls_uint32 n_sysdep_strings;
+  /* Offset of table with start offsets of original sysdep strings.  */
+  nls_uint32 orig_sysdep_tab_offset;
+  /* Offset of table with start offsets of translated sysdep strings.  */
+  nls_uint32 trans_sysdep_tab_offset;
 };
 
+/* Descriptor for static string contained in the binary .mo file.  */
 struct string_desc
 {
-  /* Length of addressed string.  */
+  /* Length of addressed string, not including the trailing NUL.  */
   nls_uint32 length;
   /* Offset of string in file.  */
   nls_uint32 offset;
 };
 
+/* The following are only used in .mo files with minor revision >= 1.  */
+
+/* Descriptor for system dependent string segment.  */
+struct sysdep_segment
+{
+  /* Length of addressed string, including the trailing NUL.  */
+  nls_uint32 length;
+  /* Offset of string in file.  */
+  nls_uint32 offset;
+};
+
+/* Descriptor for system dependent string.  */
+struct sysdep_string
+{
+  /* Offset of static string segments in file.  */
+  nls_uint32 offset;
+  /* Alternating sequence of static and system dependent segments.
+     The last segment is a static segment, including the trailing NUL.  */
+  struct segment_pair
+  {
+    /* Size of static segment.  */
+    nls_uint32 segsize;
+    /* Reference to system dependent string segment, or ~0 at the end.  */
+    nls_uint32 sysdepref;
+  } segments[1];
+};
+
+/* Marker for the end of the segments[] array.  This has the value 0xFFFFFFFF,
+   regardless whether 'int' is 16 bit, 32 bit, or 64 bit.  */
+#define SEGMENTS_END ((nls_uint32) ~0)
+
 /* @@ begin of epilog @@ */
 
 #endif /* gettext.h  */
index bbb336106e557adf6c6bb133acbf089df1773ed9..ed25fe9f5a38b9e0e6a13c2147cba5d5b8582a20 100644 (file)
    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__
index c01451b2390a6a3a782b9caceaef32a25f7d09dd..a97faad9d6d26d5382ada00ffd2dff9d1566a6ba 100644 (file)
@@ -71,14 +71,296 @@ char *alloca ();
 # 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
@@ -118,12 +400,274 @@ char *alloca ();
 # define O_BINARY 0
 #endif
 
+
+/* Prototypes for local functions.  Needed to ensure compiler checking of
+   function argument counts despite of K&R C function definition syntax.  */
+static const char *get_sysdep_segment_value PARAMS ((const char *name));
+
+
 /* We need a sign, whether a new catalog was loaded, which can be associated
    with all translations.  This is important if the translations are
    cached by one of GCC's features.  */
 int _nl_msg_cat_cntr;
 
 
+/* Expand a system dependent string segment.  Return NULL if unsupported.  */
+static const char *
+get_sysdep_segment_value (name)
+     const char *name;
+{
+  /* Test for an ISO C 99 section 7.8.1 format string directive.
+     Syntax:
+     P R I { d | i | o | u | x | X }
+     { { | LEAST | FAST } { 8 | 16 | 32 | 64 } | MAX | PTR }  */
+  /* We don't use a table of 14 times 6 'const char *' strings here, because
+     data relocations cost startup time.  */
+  if (name[0] == 'P' && name[1] == 'R' && name[2] == 'I')
+    {
+      if (name[3] == 'd' || name[3] == 'i' || name[3] == 'o' || name[3] == 'u'
+         || name[3] == 'x' || name[3] == 'X')
+       {
+         if (name[4] == '8' && name[5] == '\0')
+           {
+             if (name[3] == 'd')
+               return PRId8;
+             if (name[3] == 'i')
+               return PRIi8;
+             if (name[3] == 'o')
+               return PRIo8;
+             if (name[3] == 'u')
+               return PRIu8;
+             if (name[3] == 'x')
+               return PRIx8;
+             if (name[3] == 'X')
+               return PRIX8;
+             abort ();
+           }
+         if (name[4] == '1' && name[5] == '6' && name[6] == '\0')
+           {
+             if (name[3] == 'd')
+               return PRId16;
+             if (name[3] == 'i')
+               return PRIi16;
+             if (name[3] == 'o')
+               return PRIo16;
+             if (name[3] == 'u')
+               return PRIu16;
+             if (name[3] == 'x')
+               return PRIx16;
+             if (name[3] == 'X')
+               return PRIX16;
+             abort ();
+           }
+         if (name[4] == '3' && name[5] == '2' && name[6] == '\0')
+           {
+             if (name[3] == 'd')
+               return PRId32;
+             if (name[3] == 'i')
+               return PRIi32;
+             if (name[3] == 'o')
+               return PRIo32;
+             if (name[3] == 'u')
+               return PRIu32;
+             if (name[3] == 'x')
+               return PRIx32;
+             if (name[3] == 'X')
+               return PRIX32;
+             abort ();
+           }
+         if (name[4] == '6' && name[5] == '4' && name[6] == '\0')
+           {
+             if (name[3] == 'd')
+               return PRId64;
+             if (name[3] == 'i')
+               return PRIi64;
+             if (name[3] == 'o')
+               return PRIo64;
+             if (name[3] == 'u')
+               return PRIu64;
+             if (name[3] == 'x')
+               return PRIx64;
+             if (name[3] == 'X')
+               return PRIX64;
+             abort ();
+           }
+         if (name[4] == 'L' && name[5] == 'E' && name[6] == 'A'
+             && name[7] == 'S' && name[8] == 'T')
+           {
+             if (name[9] == '8' && name[10] == '\0')
+               {
+                 if (name[3] == 'd')
+                   return PRIdLEAST8;
+                 if (name[3] == 'i')
+                   return PRIiLEAST8;
+                 if (name[3] == 'o')
+                   return PRIoLEAST8;
+                 if (name[3] == 'u')
+                   return PRIuLEAST8;
+                 if (name[3] == 'x')
+                   return PRIxLEAST8;
+                 if (name[3] == 'X')
+                   return PRIXLEAST8;
+                 abort ();
+               }
+             if (name[9] == '1' && name[10] == '6' && name[11] == '\0')
+               {
+                 if (name[3] == 'd')
+                   return PRIdLEAST16;
+                 if (name[3] == 'i')
+                   return PRIiLEAST16;
+                 if (name[3] == 'o')
+                   return PRIoLEAST16;
+                 if (name[3] == 'u')
+                   return PRIuLEAST16;
+                 if (name[3] == 'x')
+                   return PRIxLEAST16;
+                 if (name[3] == 'X')
+                   return PRIXLEAST16;
+                 abort ();
+               }
+             if (name[9] == '3' && name[10] == '2' && name[11] == '\0')
+               {
+                 if (name[3] == 'd')
+                   return PRIdLEAST32;
+                 if (name[3] == 'i')
+                   return PRIiLEAST32;
+                 if (name[3] == 'o')
+                   return PRIoLEAST32;
+                 if (name[3] == 'u')
+                   return PRIuLEAST32;
+                 if (name[3] == 'x')
+                   return PRIxLEAST32;
+                 if (name[3] == 'X')
+                   return PRIXLEAST32;
+                 abort ();
+               }
+             if (name[9] == '6' && name[10] == '4' && name[11] == '\0')
+               {
+                 if (name[3] == 'd')
+                   return PRIdLEAST64;
+                 if (name[3] == 'i')
+                   return PRIiLEAST64;
+                 if (name[3] == 'o')
+                   return PRIoLEAST64;
+                 if (name[3] == 'u')
+                   return PRIuLEAST64;
+                 if (name[3] == 'x')
+                   return PRIxLEAST64;
+                 if (name[3] == 'X')
+                   return PRIXLEAST64;
+                 abort ();
+               }
+           }
+         if (name[4] == 'F' && name[5] == 'A' && name[6] == 'S'
+             && name[7] == 'T')
+           {
+             if (name[8] == '8' && name[9] == '\0')
+               {
+                 if (name[3] == 'd')
+                   return PRIdFAST8;
+                 if (name[3] == 'i')
+                   return PRIiFAST8;
+                 if (name[3] == 'o')
+                   return PRIoFAST8;
+                 if (name[3] == 'u')
+                   return PRIuFAST8;
+                 if (name[3] == 'x')
+                   return PRIxFAST8;
+                 if (name[3] == 'X')
+                   return PRIXFAST8;
+                 abort ();
+               }
+             if (name[8] == '1' && name[9] == '6' && name[10] == '\0')
+               {
+                 if (name[3] == 'd')
+                   return PRIdFAST16;
+                 if (name[3] == 'i')
+                   return PRIiFAST16;
+                 if (name[3] == 'o')
+                   return PRIoFAST16;
+                 if (name[3] == 'u')
+                   return PRIuFAST16;
+                 if (name[3] == 'x')
+                   return PRIxFAST16;
+                 if (name[3] == 'X')
+                   return PRIXFAST16;
+                 abort ();
+               }
+             if (name[8] == '3' && name[9] == '2' && name[10] == '\0')
+               {
+                 if (name[3] == 'd')
+                   return PRIdFAST32;
+                 if (name[3] == 'i')
+                   return PRIiFAST32;
+                 if (name[3] == 'o')
+                   return PRIoFAST32;
+                 if (name[3] == 'u')
+                   return PRIuFAST32;
+                 if (name[3] == 'x')
+                   return PRIxFAST32;
+                 if (name[3] == 'X')
+                   return PRIXFAST32;
+                 abort ();
+               }
+             if (name[8] == '6' && name[9] == '4' && name[10] == '\0')
+               {
+                 if (name[3] == 'd')
+                   return PRIdFAST64;
+                 if (name[3] == 'i')
+                   return PRIiFAST64;
+                 if (name[3] == 'o')
+                   return PRIoFAST64;
+                 if (name[3] == 'u')
+                   return PRIuFAST64;
+                 if (name[3] == 'x')
+                   return PRIxFAST64;
+                 if (name[3] == 'X')
+                   return PRIXFAST64;
+                 abort ();
+               }
+           }
+         if (name[4] == 'M' && name[5] == 'A' && name[6] == 'X'
+             && name[7] == '\0')
+           {
+             if (name[3] == 'd')
+               return PRIdMAX;
+             if (name[3] == 'i')
+               return PRIiMAX;
+             if (name[3] == 'o')
+               return PRIoMAX;
+             if (name[3] == 'u')
+               return PRIuMAX;
+             if (name[3] == 'x')
+               return PRIxMAX;
+             if (name[3] == 'X')
+               return PRIXMAX;
+             abort ();
+           }
+         if (name[4] == 'P' && name[5] == 'T' && name[6] == 'R'
+             && name[7] == '\0')
+           {
+             if (name[3] == 'd')
+               return PRIdPTR;
+             if (name[3] == 'i')
+               return PRIiPTR;
+             if (name[3] == 'o')
+               return PRIoPTR;
+             if (name[3] == 'u')
+               return PRIuPTR;
+             if (name[3] == 'x')
+               return PRIxPTR;
+             if (name[3] == 'X')
+               return PRIXPTR;
+             abort ();
+           }
+       }
+    }
+  /* Other system dependent strings are not valid.  */
+  return NULL;
+}
+
 /* Initialize the codeset dependent parts of an opened message catalog.
    Return the header entry.  */
 const char *
@@ -282,6 +826,7 @@ _nl_load_domain (domain_file, domainbinding)
   struct mo_file_header *data = (struct mo_file_header *) -1;
   int use_mmap = 0;
   struct loaded_domain *domain;
+  int revision;
   const char *nullentry;
 
   domain_file->decided = 1;
@@ -389,22 +934,257 @@ _nl_load_domain (domain_file, domainbinding)
   domain->use_mmap = use_mmap;
   domain->mmap_size = size;
   domain->must_swap = data->magic != _MAGIC;
+  domain->malloced = NULL;
 
   /* Fill in the information about the available tables.  */
-  switch (W (domain->must_swap, data->revision))
+  revision = W (domain->must_swap, data->revision);
+  /* We support only the major revision 0.  */
+  switch (revision >> 16)
     {
     case 0:
       domain->nstrings = W (domain->must_swap, data->nstrings);
-      domain->orig_tab = (struct string_desc *)
+      domain->orig_tab = (const struct string_desc *)
        ((char *) data + W (domain->must_swap, data->orig_tab_offset));
-      domain->trans_tab = (struct string_desc *)
+      domain->trans_tab = (const struct string_desc *)
        ((char *) data + W (domain->must_swap, data->trans_tab_offset));
       domain->hash_size = W (domain->must_swap, data->hash_tab_size);
-      domain->hash_tab = (nls_uint32 *)
-       ((char *) data + W (domain->must_swap, data->hash_tab_offset));
+      domain->hash_tab =
+       (domain->hash_size > 2
+        ? (const nls_uint32 *)
+          ((char *) data + W (domain->must_swap, data->hash_tab_offset))
+        : NULL);
+      domain->must_swap_hash_tab = domain->must_swap;
+
+      /* Now dispatch on the minor revision.  */
+      switch (revision & 0xffff)
+       {
+       case 0:
+         domain->n_sysdep_strings = 0;
+         domain->orig_sysdep_tab = NULL;
+         domain->trans_sysdep_tab = NULL;
+         break;
+       case 1:
+       default:
+         {
+           nls_uint32 n_sysdep_strings;
+
+           if (domain->hash_tab == NULL)
+             /* This is invalid.  These minor revisions need a hash table.  */
+             goto invalid;
+
+           n_sysdep_strings =
+             W (domain->must_swap, data->n_sysdep_strings);
+           if (n_sysdep_strings > 0)
+             {
+               nls_uint32 n_sysdep_segments;
+               const struct sysdep_segment *sysdep_segments;
+               const char **sysdep_segment_values;
+               const nls_uint32 *orig_sysdep_tab;
+               const nls_uint32 *trans_sysdep_tab;
+               size_t memneed;
+               char *mem;
+               struct sysdep_string_desc *inmem_orig_sysdep_tab;
+               struct sysdep_string_desc *inmem_trans_sysdep_tab;
+               nls_uint32 *inmem_hash_tab;
+               unsigned int i;
+
+               /* Get the values of the system dependent segments.  */
+               n_sysdep_segments =
+                 W (domain->must_swap, data->n_sysdep_segments);
+               sysdep_segments = (const struct sysdep_segment *)
+                 ((char *) data
+                  + W (domain->must_swap, data->sysdep_segments_offset));
+               sysdep_segment_values =
+                 alloca (n_sysdep_segments * sizeof (const char *));
+               for (i = 0; i < n_sysdep_segments; i++)
+                 {
+                   const char *name =
+                     (char *) data
+                     + W (domain->must_swap, sysdep_segments[i].offset);
+                   nls_uint32 namelen =
+                     W (domain->must_swap, sysdep_segments[i].length);
+
+                   if (!(namelen > 0 && name[namelen - 1] == '\0'))
+                     {
+                       freea (sysdep_segment_values);
+                       goto invalid;
+                     }
+
+                   sysdep_segment_values[i] = get_sysdep_segment_value (name);
+                 }
+
+               orig_sysdep_tab = (const nls_uint32 *)
+                 ((char *) data
+                  + W (domain->must_swap, data->orig_sysdep_tab_offset));
+               trans_sysdep_tab = (const nls_uint32 *)
+                 ((char *) data
+                  + W (domain->must_swap, data->trans_sysdep_tab_offset));
+
+               /* Compute the amount of additional memory needed for the
+                  system dependent strings and the augmented hash table.  */
+               memneed = 2 * n_sysdep_strings
+                         * sizeof (struct sysdep_string_desc)
+                         + domain->hash_size * sizeof (nls_uint32);
+               for (i = 0; i < 2 * n_sysdep_strings; i++)
+                 {
+                   const struct sysdep_string *sysdep_string =
+                     (const struct sysdep_string *)
+                     ((char *) data
+                      + W (domain->must_swap,
+                           i < n_sysdep_strings
+                           ? orig_sysdep_tab[i]
+                           : trans_sysdep_tab[i - n_sysdep_strings]));
+                   size_t need = 0;
+                   const struct segment_pair *p = sysdep_string->segments;
+
+                   if (W (domain->must_swap, p->sysdepref) != SEGMENTS_END)
+                     for (p = sysdep_string->segments;; p++)
+                       {
+                         nls_uint32 sysdepref;
+
+                         need += W (domain->must_swap, p->segsize);
+
+                         sysdepref = W (domain->must_swap, p->sysdepref);
+                         if (sysdepref == SEGMENTS_END)
+                           break;
+
+                         if (sysdepref >= n_sysdep_segments)
+                           {
+                             /* Invalid.  */
+                             freea (sysdep_segment_values);
+                             goto invalid;
+                           }
+
+                         need += strlen (sysdep_segment_values[sysdepref]);
+                       }
+
+                   memneed += need;
+                 }
+
+               /* Allocate additional memory.  */
+               mem = (char *) malloc (memneed);
+               if (mem == NULL)
+                 goto invalid;
+
+               domain->malloced = mem;
+               inmem_orig_sysdep_tab = (struct sysdep_string_desc *) mem;
+               mem += n_sysdep_strings * sizeof (struct sysdep_string_desc);
+               inmem_trans_sysdep_tab = (struct sysdep_string_desc *) mem;
+               mem += n_sysdep_strings * sizeof (struct sysdep_string_desc);
+               inmem_hash_tab = (nls_uint32 *) mem;
+               mem += domain->hash_size * sizeof (nls_uint32);
+
+               /* Compute the system dependent strings.  */
+               for (i = 0; i < 2 * n_sysdep_strings; i++)
+                 {
+                   const struct sysdep_string *sysdep_string =
+                     (const struct sysdep_string *)
+                     ((char *) data
+                      + W (domain->must_swap,
+                           i < n_sysdep_strings
+                           ? orig_sysdep_tab[i]
+                           : trans_sysdep_tab[i - n_sysdep_strings]));
+                   const char *static_segments =
+                     (char *) data
+                     + W (domain->must_swap, sysdep_string->offset);
+                   const struct segment_pair *p = sysdep_string->segments;
+
+                   /* Concatenate the segments, and fill
+                      inmem_orig_sysdep_tab[i] (for i < n_sysdep_strings) and
+                      inmem_trans_sysdep_tab[i-n_sysdep_strings] (for
+                      i >= n_sysdep_strings).  */
+
+                   if (W (domain->must_swap, p->sysdepref) == SEGMENTS_END)
+                     {
+                       /* Only one static segment.  */
+                       inmem_orig_sysdep_tab[i].length =
+                         W (domain->must_swap, p->segsize);
+                       inmem_orig_sysdep_tab[i].pointer = static_segments;
+                     }
+                   else
+                     {
+                       inmem_orig_sysdep_tab[i].pointer = mem;
+
+                       for (p = sysdep_string->segments;; p++)
+                         {
+                           nls_uint32 segsize =
+                             W (domain->must_swap, p->segsize);
+                           nls_uint32 sysdepref =
+                             W (domain->must_swap, p->sysdepref);
+                           size_t n;
+
+                           if (segsize > 0)
+                             {
+                               memcpy (mem, static_segments, segsize);
+                               mem += segsize;
+                               static_segments += segsize;
+                             }
+
+                           if (sysdepref == SEGMENTS_END)
+                             break;
+
+                           n = strlen (sysdep_segment_values[sysdepref]);
+                           memcpy (mem, sysdep_segment_values[sysdepref], n);
+                           mem += n;
+                         }
+
+                       inmem_orig_sysdep_tab[i].length =
+                         mem - inmem_orig_sysdep_tab[i].pointer;
+                     }
+                 }
+
+               /* Compute the augmented hash table.  */
+               for (i = 0; i < domain->hash_size; i++)
+                 inmem_hash_tab[i] =
+                   W (domain->must_swap_hash_tab, domain->hash_tab[i]);
+               for (i = 0; i < n_sysdep_strings; i++)
+                 {
+                   const char *msgid = inmem_orig_sysdep_tab[i].pointer;
+                   nls_uint32 hash_val = hash_string (msgid);
+                   nls_uint32 idx = hash_val % domain->hash_size;
+                   nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2));
+
+                   for (;;)
+                     {
+                       if (inmem_hash_tab[idx] == 0)
+                         {
+                           /* Hash table entry is empty.  Use it.  */
+                           inmem_hash_tab[idx] = 1 + domain->nstrings + i;
+                           break;
+                         }
+
+                       if (idx >= domain->hash_size - incr)
+                         idx -= domain->hash_size - incr;
+                       else
+                         idx += incr;
+                     }
+                 }
+
+               freea (sysdep_segment_values);
+
+               domain->n_sysdep_strings = n_sysdep_strings;
+               domain->orig_sysdep_tab = inmem_orig_sysdep_tab;
+               domain->trans_sysdep_tab = inmem_trans_sysdep_tab;
+
+               domain->hash_tab = inmem_hash_tab;
+               domain->must_swap_hash_tab = 0;
+             }
+           else
+             {
+               domain->n_sysdep_strings = 0;
+               domain->orig_sysdep_tab = NULL;
+               domain->trans_sysdep_tab = NULL;
+             }
+         }
+         break;
+       }
       break;
     default:
       /* This is an invalid revision.  */
+    invalid:
+      /* This is an invalid .mo file.  */
+      if (domain->malloced)
+       free (domain->malloced);
 #ifdef HAVE_MMAP
       if (use_mmap)
        munmap ((caddr_t) data, size);
@@ -437,6 +1217,9 @@ _nl_unload_domain (domain)
 
   _nl_free_domain_conv (domain);
 
+  if (domain->malloced)
+    free (domain->malloced);
+
 # ifdef _POSIX_MAPPED_FILES
   if (domain->use_mmap)
     munmap ((caddr_t) domain->data, domain->mmap_size);
index d033ff13fd9c4a5c31655e8bac65d4b20719d926..dc51e93ccb4a52c46c27af77c5ae6687c24f94e9 100644 (file)
@@ -1,3 +1,8 @@
+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.
index dcdfdc1501bfc3f863cbcbb214f950e0877e17e9..15081020dd4a63e13ed398758271e1d7663925a4 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 1999, 2001 Free Software Foundation, Inc.
+/* Copyright (C) 1999, 2001-2002 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
 # 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
 
index 1a37b3cd77778bb28551d3a42dc82af34ecb741d..f558c3a6b6e5fa48266c2ea85875b92ab8090a76 100644 (file)
@@ -1,3 +1,17 @@
+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.
index 53203cab4feae4832a44b99202d19fff80351808..c81386b629847de7dfc1e7709c3aa771ae26dee6 100644 (file)
@@ -1,14 +1,14 @@
 ## Process this file with automake to produce Makefile.in -*-Makefile-*-
 
 aclocaldir = @aclocaldir@
-aclocal_DATA = codeset.m4 gettext.m4 glibc21.m4 iconv.m4 intdiv0.m4 isc-posix.m4 lib-ld.m4 lib-link.m4 lib-prefix.m4 lcmessage.m4 progtest.m4
+aclocal_DATA = codeset.m4 gettext.m4 glibc21.m4 iconv.m4 intdiv0.m4 inttypes.m4 inttypes_h.m4 isc-posix.m4 lib-ld.m4 lib-link.m4 lib-prefix.m4 lcmessage.m4 progtest.m4 stdint_h.m4 uintmax_t.m4
 
 # Generate this list with
 # find . -type f -name '*.m4' -printf '%f\n'|sort |fmt |tr '\012' @ \
 #   |sed 's/@$/%/;s/@/ \\@/g' |tr @% '\012\012'
 EXTRA_DIST = README \
 backupfile.m4 c-bs-a.m4 codeset.m4 error.m4 flex.m4 fnmatch.m4 gcj.m4 \
-getline.m4 gettext.m4 glibc21.m4 hostname.m4 iconv.m4 intdiv0.m4 \
+getline.m4 gettext.m4 glibc21.m4 hostname.m4 iconv.m4 intdiv0.m4 inttypes.m4 \
 inttypes_h.m4 isc-posix.m4 javacomp.m4 javaexec.m4 lcmessage.m4 lib-ld.m4 \
 lib-link.m4 lib-prefix.m4 libtool.m4 mbrtowc.m4 mbstate_t.m4 mbswidth.m4 \
 mkdtemp.m4 progtest.m4 setenv.m4 setlocale.m4 siginfo.m4 signalblocking.m4 \
index bfa15a22858a105fe787f3d5eced4d8a26da0d34..f2b3a1f84d26b8045531244352813c1a6cc55484 100644 (file)
@@ -33,7 +33,9 @@ dnl    AM-DISABLE-SHARED). If INTLSYMBOL is 'no-libtool', a static library
 dnl    $(top_builddir)/intl/libintl.a will be created.
 dnl If NEEDSYMBOL is specified and is 'need-ngettext', then GNU gettext
 dnl    implementations (in libc or libintl) without the ngettext() function
-dnl    will be ignored.
+dnl    will be ignored.  If NEEDSYMBOL is specified and is
+dnl    'need-formatstring-macros', then GNU gettext implementations that don't
+dnl    support the ISO C 99 <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
@@ -60,9 +62,9 @@ AC_DEFUN([AM_GNU_GETTEXT],
   ifelse([$1], [], , [ifelse([$1], [external], , [ifelse([$1], [no-libtool], , [ifelse([$1], [use-libtool], ,
     [errprint([ERROR: invalid first argument to AM_GNU_GETTEXT
 ])])])])])
-  ifelse([$2], [], , [ifelse([$2], [need-ngettext], ,
+  ifelse([$2], [], , [ifelse([$2], [need-ngettext], , [ifelse([$2], [need-formatstring-macros], ,
     [errprint([ERROR: invalid second argument to AM_GNU_GETTEXT
-])])])
+])])])])
   define(gt_included_intl, ifelse([$1], [external], [no], [yes]))
   define(gt_libtool_suffix_prefix, ifelse([$1], [use-libtool], [l], []))
 
@@ -125,13 +127,20 @@ AC_DEFUN([AM_GNU_GETTEXT],
         dnl to fall back to GNU NLS library.
 
         dnl Add a version number to the cache macros.
-        define([gt_api_version], ifelse([$2], [need-ngettext], 2, 1))
+        define([gt_api_version], ifelse([$2], [need-formatstring-macros], 3, ifelse([$2], [need-ngettext], 2, 1)))
         define([gt_cv_func_gnugettext_libc], [gt_cv_func_gnugettext]gt_api_version[_libc])
         define([gt_cv_func_gnugettext_libintl], [gt_cv_func_gnugettext]gt_api_version[_libintl])
 
         AC_CACHE_CHECK([for GNU gettext in libc], gt_cv_func_gnugettext_libc,
          [AC_TRY_LINK([#include <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],
@@ -156,7 +165,14 @@ return (int) gettext ("")]ifelse([$2], [need-ngettext], [ + (int) ngettext ("",
             LIBS="$LIBS $LIBINTL"
             dnl Now see whether libintl exists and does not depend on libiconv.
             AC_TRY_LINK([#include <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"
@@ -170,7 +186,14 @@ return (int) gettext ("")]ifelse([$2], [need-ngettext], [ + (int) ngettext ("",
             if test "$gt_cv_func_gnugettext_libintl" != yes && test -n "$LIBICONV"; then
               LIBS="$LIBS $LIBICONV"
               AC_TRY_LINK([#include <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"
@@ -495,6 +518,8 @@ AC_DEFUN([AM_INTL_SUBDIR],
   AC_REQUIRE([AC_FUNC_MMAP])dnl
   AC_REQUIRE([jm_GLIBC21])dnl
   AC_REQUIRE([gt_INTDIV0])dnl
+  AC_REQUIRE([jm_AC_TYPE_UINTMAX_T])dnl
+  AC_REQUIRE([gt_HEADER_INTTYPES_H])dnl
 
   AC_CHECK_HEADERS([argz.h limits.h locale.h nl_types.h malloc.h stddef.h \
 stdlib.h string.h unistd.h sys/param.h])
diff --git a/m4/inttypes.m4 b/m4/inttypes.m4
new file mode 100644 (file)
index 0000000..ab370ff
--- /dev/null
@@ -0,0 +1,27 @@
+# inttypes.m4 serial 1 (gettext-0.11.4)
+dnl Copyright (C) 1997-2002 Free Software Foundation, Inc.
+dnl This file is free software, distributed under the terms of the GNU
+dnl General Public License.  As a special exception to the GNU General
+dnl Public License, this file may be distributed as part of a program
+dnl that contains a configuration script generated by Autoconf, under
+dnl the same distribution terms as the rest of that program.
+
+dnl From Paul Eggert.
+
+# Define HAVE_INTTYPES_H if <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
+])
index 75b04a6c01ca688450a4692502562b873e3c2cb3..400a11184e5552d46b0083dec97e701e3a689686 100644 (file)
@@ -1,4 +1,4 @@
-# inttypes_h.m4 serial 3 (gettext-0.10.40)
+# inttypes_h.m4 serial 4 (gettext-0.11.4)
 dnl Copyright (C) 1997-2002 Free Software Foundation, Inc.
 dnl This file is free software, distributed under the terms of the GNU
 dnl General Public License.  As a special exception to the GNU General
@@ -8,7 +8,7 @@ dnl the same distribution terms as the rest of that program.
 
 dnl From Paul Eggert.
 
-# Define HAVE_INTTYPES_H if <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],
@@ -21,7 +21,7 @@ AC_DEFUN([jm_AC_HEADER_INTTYPES_H],
     jm_ac_cv_header_inttypes_h=yes,
     jm_ac_cv_header_inttypes_h=no)])
   if test $jm_ac_cv_header_inttypes_h = yes; then
-    AC_DEFINE_UNQUOTED(HAVE_INTTYPES_H, 1,
+    AC_DEFINE_UNQUOTED(HAVE_INTTYPES_H_WITH_UINTMAX, 1,
 [Define if <inttypes.h> exists, doesn't clash with <sys/types.h>,
    and declares uintmax_t. ])
   fi
index b5fd1f0f8593eb008422160d5d8bfe91879dba71..4b5a4ac6021320882534bed4dc07ce673891fc9d 100644 (file)
@@ -1,4 +1,4 @@
-# stdint_h.m4 serial 1 (gettext-0.11)
+# stdint_h.m4 serial 2 (gettext-0.11.4)
 dnl Copyright (C) 1997-2002 Free Software Foundation, Inc.
 dnl This file is free software, distributed under the terms of the GNU
 dnl General Public License.  As a special exception to the GNU General
@@ -8,7 +8,7 @@ dnl the same distribution terms as the rest of that program.
 
 dnl From Paul Eggert.
 
-# Define HAVE_STDINT_H if <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],
@@ -21,7 +21,7 @@ AC_DEFUN([jm_AC_HEADER_STDINT_H],
     jm_ac_cv_header_stdint_h=yes,
     jm_ac_cv_header_stdint_h=no)])
   if test $jm_ac_cv_header_stdint_h = yes; then
-    AC_DEFINE_UNQUOTED(HAVE_STDINT_H, 1,
+    AC_DEFINE_UNQUOTED(HAVE_STDINT_H_WITH_UINTMAX, 1,
 [Define if <stdint.h> exists, doesn't clash with <sys/types.h>,
    and declares uintmax_t. ])
   fi
index 4c6d97373ba21f936472a724adf27b20bf524a44..badf7ebea9a94b5004006ff9e1c5917ef8bdeea7 100644 (file)
@@ -1,3 +1,8 @@
+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.
index ce570502e8af2d7b498565a904dd997dd6acdce2..32aaa7f397210ce1482ed3dfd1ce80fac8cb2b25 100644 (file)
@@ -559,9 +559,11 @@ if test -f "$srcdir/po/stamp-cat-id"; then
 fi
 $do_changelog && func_poChangeLog_finish
 
-m4filelist='  codeset.m4 gettext.m4 glibc21.m4 iconv.m4 intdiv0.m4 isc-posix.m4
-  lcmessage.m4 lib-ld.m4 lib-link.m4 lib-prefix.m4 progtest.m4'
-# We cannot omit codeset.m4, glibc21.m4, intdiv0.m4, isc-posix.m4, lcmessage.m4
+m4filelist='  codeset.m4 gettext.m4 glibc21.m4 iconv.m4 intdiv0.m4 inttypes.m4
+  inttypes_h.m4 isc-posix.m4 lcmessage.m4 lib-ld.m4 lib-link.m4 lib-prefix.m4
+  progtest.m4 stdint_h.m4 uintmax_t.m4'
+# We cannot omit codeset.m4, glibc21.m4, intdiv0.m4, inttypes.m4,
+# inttypes_h.m4, isc-posix.m4, lcmessage.m4, stdint_h.m4, uintmax_t.m4
 # if test -z "$intldir", otherwise "aclocal -I m4" might give an error.
 # (aclocal doesn't know which macros are really needed, it looks which macros
 # are potentially needed.)
index 48d0fca84ee3f1e8831091a43337b85f7a1cb60c..c766517fe9d3cfbf6502a6ac67493c92e88b3bd7 100644 (file)
@@ -1,3 +1,39 @@
+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.
index 04deaff1ea48c168f4ee9c5cf10f3b508c6454cd..2101874d272ee626ca5533223b092452545a6413 100644 (file)
      or '*m$' or a nonempty digit sequence,
    - is optionally followed by '.' and a precision specification: '*' (reads
      an argument) or '*m$' or a nonempty digit sequence,
-   - is optionally followed by a size specifier, one of 'hh' 'h' 'l' 'll' 'L'
-     'q' 'j' 'z' 't',
-   - is finished by a specifier
-       - '%', that needs no argument,
-       - 'c', 'C', that need a character argument,
-       - 's', 'S', that need a string argument,
-       - 'i', 'd', that need a signed integer argument,
-       - 'o', 'u', 'x', 'X', that need an unsigned integer argument,
-       - 'e', 'E', 'f', 'F', 'g', 'G', 'a', 'A', that need a floating-point
-         argument,
-       - 'p', that needs a 'void *' argument,
-       - 'n', that needs a pointer to integer.
+   - is either continued like this:
+       - is optionally followed by a size specifier, one of 'hh' 'h' 'l' 'll'
+         'L' 'q' 'j' 'z' 't',
+       - is finished by a specifier
+           - '%', that needs no argument,
+           - 'c', 'C', that need a character argument,
+           - 's', 'S', that need a string argument,
+           - 'i', 'd', that need a signed integer argument,
+           - 'o', 'u', 'x', 'X', that need an unsigned integer argument,
+           - 'e', 'E', 'f', 'F', 'g', 'G', 'a', 'A', that need a floating-point
+             argument,
+           - 'p', that needs a 'void *' argument,
+           - 'n', that needs a pointer to integer.
+     or is finished by a specifier '<' inttypes-macro '>' where inttypes-macro
+     is an ISO C 99 section 7.8.1 format directive.
    Numbered ('%m$' or '*m$') and unnumbered argument specifications cannot
    be used in the same string.  When numbered argument specifications are
    used, specifying the Nth argument requires that all the leading arguments,
@@ -75,9 +78,22 @@ enum format_arg_type
   FAT_SIZE_CHAR                = 2 << 4,
   FAT_SIZE_LONG                = 1 << 6,
   FAT_SIZE_LONGLONG    = 2 << 6,
-  FAT_SIZE_INTMAX_T    = 1 << 8,
-  FAT_SIZE_SIZE_T      = 1 << 9,
-  FAT_SIZE_PTRDIFF_T   = 1 << 10,
+  FAT_SIZE_8_T         = 1 << 8,
+  FAT_SIZE_16_T                = 1 << 9,
+  FAT_SIZE_32_T                = 1 << 10,
+  FAT_SIZE_64_T                = 1 << 11,
+  FAT_SIZE_LEAST8_T    = 1 << 12,
+  FAT_SIZE_LEAST16_T   = 1 << 13,
+  FAT_SIZE_LEAST32_T   = 1 << 14,
+  FAT_SIZE_LEAST64_T   = 1 << 15,
+  FAT_SIZE_FAST8_T     = 1 << 16,
+  FAT_SIZE_FAST16_T    = 1 << 17,
+  FAT_SIZE_FAST32_T    = 1 << 18,
+  FAT_SIZE_FAST64_T    = 1 << 19,
+  FAT_SIZE_INTMAX_T    = 1 << 20,
+  FAT_SIZE_INTPTR_T    = 1 << 21,
+  FAT_SIZE_SIZE_T      = 1 << 22,
+  FAT_SIZE_PTRDIFF_T   = 1 << 23,
   FAT_WIDE             = FAT_SIZE_LONG,
   /* Meaningful combinations of basic types and flags:
   'signed char'                        = FAT_INTEGER | FAT_SIZE_CHAR,
@@ -106,8 +122,14 @@ enum format_arg_type
   /* Bitmasks */
   FAT_SIZE_MASK                = (FAT_SIZE_SHORT | FAT_SIZE_CHAR
                           | FAT_SIZE_LONG | FAT_SIZE_LONGLONG
-                          | FAT_SIZE_INTMAX_T | FAT_SIZE_SIZE_T
-                          | FAT_SIZE_PTRDIFF_T)
+                          | FAT_SIZE_8_T | FAT_SIZE_16_T
+                          | FAT_SIZE_32_T | FAT_SIZE_64_T
+                          | FAT_SIZE_LEAST8_T | FAT_SIZE_LEAST16_T
+                          | FAT_SIZE_LEAST32_T | FAT_SIZE_LEAST64_T
+                          | FAT_SIZE_FAST8_T | FAT_SIZE_FAST16_T
+                          | FAT_SIZE_FAST32_T | FAT_SIZE_FAST64_T
+                          | FAT_SIZE_INTMAX_T | FAT_SIZE_INTPTR_T
+                          | FAT_SIZE_SIZE_T | FAT_SIZE_PTRDIFF_T)
 };
 
 struct numbered_arg
@@ -127,6 +149,8 @@ struct spec
   unsigned int unnumbered_arg_count;
   unsigned int allocated;
   struct unnumbered_arg *unnumbered;
+  unsigned int c99_directives_count;
+  const char **c99_directives;
 };
 
 /* Locale independent test for a decimal digit.
@@ -174,6 +198,8 @@ format_parse (format)
   spec.allocated = 0;
   numbered = NULL;
   spec.unnumbered = NULL;
+  spec.c99_directives_count = 0;
+  spec.c99_directives = NULL;
 
   for (; *format != '\0';)
     if (*format++ == '%')
@@ -350,84 +376,225 @@ format_parse (format)
              }
          }
 
-       /* Parse size.  */
-       size = 0;
-       for (;; format++)
+       if (*format == '<')
          {
-           if (*format == 'h')
+           spec.c99_directives =
+             (const char **)
+             xrealloc (spec.c99_directives,
+                       2 * (spec.c99_directives_count + 1)
+                       * sizeof (const char *));
+           spec.c99_directives[2 * spec.c99_directives_count] = format;
+
+           format++;
+           /* Parse ISO C 99 section 7.8.1 format string directive.
+              Syntax:
+              P R I { d | i | o | u | x | X }
+              { { | LEAST | FAST } { 8 | 16 | 32 | 64 } | MAX | PTR }  */
+           if (*format != 'P')
+             goto bad_format;
+           format++;
+           if (*format != 'R')
+             goto bad_format;
+           format++;
+           if (*format != 'I')
+             goto bad_format;
+           format++;
+
+           switch (*format)
+             {
+             case 'i': case 'd':
+               type = FAT_INTEGER;
+               break;
+             case 'u': case 'o': case 'x': case 'X':
+               type = FAT_INTEGER | FAT_UNSIGNED;
+               break;
+             default:
+               goto bad_format;
+             }
+           format++;
+
+           if (format[0] == 'M' && format[1] == 'A' && format[2] == 'X')
+             {
+               type |= FAT_SIZE_INTMAX_T;
+               format += 3;
+             }
+           else if (format[0] == 'P' && format[1] == 'T' && format[2] == 'R')
              {
-               if (size & (FAT_SIZE_SHORT | FAT_SIZE_CHAR))
-                 size = FAT_SIZE_CHAR;
+               type |= FAT_SIZE_INTPTR_T;
+               format += 3;
+             }
+           else
+             {
+               if (format[0] == 'L' && format[1] == 'E' && format[2] == 'A'
+                   && format[3] == 'S' && format[4] == 'T')
+                 {
+                   format += 5;
+                   if (format[0] == '8')
+                     {
+                       type |= FAT_SIZE_LEAST8_T;
+                       format++;
+                     }
+                   else if (format[0] == '1' && format[1] == '6')
+                     {
+                       type |= FAT_SIZE_LEAST16_T;
+                       format += 2;
+                     }
+                   else if (format[0] == '3' && format[1] == '2')
+                     {
+                       type |= FAT_SIZE_LEAST32_T;
+                       format += 2;
+                     }
+                   else if (format[0] == '6' && format[1] == '4')
+                     {
+                       type |= FAT_SIZE_LEAST64_T;
+                       format += 2;
+                     }
+                   else
+                     goto bad_format;
+                 }
+               else if (format[0] == 'F' && format[1] == 'A'
+                        && format[2] == 'S' && format[3] == 'T')
+                 {
+                   format += 4;
+                   if (format[0] == '8')
+                     {
+                       type |= FAT_SIZE_FAST8_T;
+                       format++;
+                     }
+                   else if (format[0] == '1' && format[1] == '6')
+                     {
+                       type |= FAT_SIZE_FAST16_T;
+                       format += 2;
+                     }
+                   else if (format[0] == '3' && format[1] == '2')
+                     {
+                       type |= FAT_SIZE_FAST32_T;
+                       format += 2;
+                     }
+                   else if (format[0] == '6' && format[1] == '4')
+                     {
+                       type |= FAT_SIZE_FAST64_T;
+                       format += 2;
+                     }
+                   else
+                     goto bad_format;
+                 }
                else
-                 size = FAT_SIZE_SHORT;
+                 {
+                   if (format[0] == '8')
+                     {
+                       type |= FAT_SIZE_8_T;
+                       format++;
+                     }
+                   else if (format[0] == '1' && format[1] == '6')
+                     {
+                       type |= FAT_SIZE_16_T;
+                       format += 2;
+                     }
+                   else if (format[0] == '3' && format[1] == '2')
+                     {
+                       type |= FAT_SIZE_32_T;
+                       format += 2;
+                     }
+                   else if (format[0] == '6' && format[1] == '4')
+                     {
+                       type |= FAT_SIZE_64_T;
+                       format += 2;
+                     }
+                   else
+                     goto bad_format;
+                 }
              }
-           else if (*format == 'l')
+
+           if (*format != '>')
+             goto bad_format;
+
+           spec.c99_directives[2 * spec.c99_directives_count + 1] = format;
+           spec.c99_directives_count++;
+         }
+       else
+         {
+           /* Parse size.  */
+           size = 0;
+           for (;; format++)
              {
-               if (size & (FAT_SIZE_LONG | FAT_SIZE_LONGLONG))
+               if (*format == 'h')
+                 {
+                   if (size & (FAT_SIZE_SHORT | FAT_SIZE_CHAR))
+                     size = FAT_SIZE_CHAR;
+                   else
+                     size = FAT_SIZE_SHORT;
+                 }
+               else if (*format == 'l')
+                 {
+                   if (size & (FAT_SIZE_LONG | FAT_SIZE_LONGLONG))
+                     size = FAT_SIZE_LONGLONG;
+                   else
+                     size = FAT_SIZE_LONG;
+                 }
+               else if (*format == 'L')
                  size = FAT_SIZE_LONGLONG;
+               else if (*format == 'q')
+                 /* Old BSD 4.4 convention.  */
+                 size = FAT_SIZE_LONGLONG;
+               else if (*format == 'j')
+                 size = FAT_SIZE_INTMAX_T;
+               else if (*format == 'z' || *format == 'Z')
+                 /* 'z' is standardized in ISO C 99, but glibc uses 'Z'
+                    because the warning facility in gcc-2.95.2 understands
+                    only 'Z' (see gcc-2.95.2/gcc/c-common.c:1784).  */
+                 size = FAT_SIZE_SIZE_T;
+               else if (*format == 't')
+                 size = FAT_SIZE_PTRDIFF_T;
                else
-                 size = FAT_SIZE_LONG;
+                 break;
              }
-           else if (*format == 'L')
-             size = FAT_SIZE_LONGLONG;
-           else if (*format == 'q')
-             /* Old BSD 4.4 convention.  */
-             size = FAT_SIZE_LONGLONG;
-           else if (*format == 'j')
-             size = FAT_SIZE_INTMAX_T;
-           else if (*format == 'z' || *format == 'Z')
-             /* 'z' is standardized in ISO C 99, but glibc uses 'Z' because
-                the warning facility in gcc-2.95.2 understands only 'Z'
-                (see gcc-2.95.2/gcc/c-common.c:1784).  */
-             size = FAT_SIZE_SIZE_T;
-           else if (*format == 't')
-             size = FAT_SIZE_PTRDIFF_T;
-           else
-             break;
-         }
 
-       switch (*format)
-         {
-         case '%':
-         case 'm': /* glibc extension */
-           type = FAT_NONE;
-           break;
-         case 'c':
-           type = FAT_CHAR;
-           type |= (size & (FAT_SIZE_LONG | FAT_SIZE_LONGLONG) ? FAT_WIDE : 0);
-           break;
-         case 'C': /* obsolete */
-           type = FAT_CHAR | FAT_WIDE;
-           break;
-         case 's':
-           type = FAT_STRING;
-           type |= (size & (FAT_SIZE_LONG | FAT_SIZE_LONGLONG) ? FAT_WIDE : 0);
-           break;
-         case 'S': /* obsolete */
-           type = FAT_STRING | FAT_WIDE;
-           break;
-         case 'i': case 'd':
-           type = FAT_INTEGER;
-           type |= (size & FAT_SIZE_MASK);
-           break;
-         case 'u': case 'o': case 'x': case 'X':
-           type = FAT_INTEGER | FAT_UNSIGNED;
-           type |= (size & FAT_SIZE_MASK);
-           break;
-         case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
-         case 'a': case 'A':
-           type = FAT_DOUBLE;
-           type |= (size & FAT_SIZE_LONGLONG);
-           break;
-         case 'p':
-           type = FAT_POINTER;
-           break;
-         case 'n':
-           type = FAT_COUNT_POINTER;
-           type |= (size & FAT_SIZE_MASK);
-           break;
-         default:
-           goto bad_format;
+           switch (*format)
+             {
+             case '%':
+             case 'm': /* glibc extension */
+               type = FAT_NONE;
+               break;
+             case 'c':
+               type = FAT_CHAR;
+               type |= (size & (FAT_SIZE_LONG | FAT_SIZE_LONGLONG)
+                        ? FAT_WIDE : 0);
+               break;
+             case 'C': /* obsolete */
+               type = FAT_CHAR | FAT_WIDE;
+               break;
+             case 's':
+               type = FAT_STRING;
+               type |= (size & (FAT_SIZE_LONG | FAT_SIZE_LONGLONG)
+                        ? FAT_WIDE : 0);
+               break;
+             case 'S': /* obsolete */
+               type = FAT_STRING | FAT_WIDE;
+               break;
+             case 'i': case 'd':
+               type = FAT_INTEGER;
+               type |= (size & FAT_SIZE_MASK);
+               break;
+             case 'u': case 'o': case 'x': case 'X':
+               type = FAT_INTEGER | FAT_UNSIGNED;
+               type |= (size & FAT_SIZE_MASK);
+               break;
+             case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
+             case 'a': case 'A':
+               type = FAT_DOUBLE;
+               type |= (size & FAT_SIZE_LONGLONG);
+               break;
+             case 'p':
+               type = FAT_POINTER;
+               break;
+             case 'n':
+               type = FAT_COUNT_POINTER;
+               type |= (size & FAT_SIZE_MASK);
+               break;
+             default:
+               goto bad_format;
+             }
          }
 
        if (type != FAT_NONE)
@@ -540,6 +707,8 @@ format_parse (format)
     free (numbered);
   if (spec.unnumbered != NULL)
     free (spec.unnumbered);
+  if (spec.c99_directives != NULL)
+    free (spec.c99_directives);
   return NULL;
 }
 
@@ -551,6 +720,8 @@ format_free (descr)
 
   if (spec->unnumbered != NULL)
     free (spec->unnumbered);
+  if (spec->c99_directives != NULL)
+    free (spec->c99_directives);
   free (spec);
 }
 
@@ -620,6 +791,37 @@ struct formatstring_parser formatstring_c =
 };
 
 
+void
+get_c99_format_directives (string, intervalsp, lengthp)
+     const char *string;
+     struct interval **intervalsp;
+     size_t *lengthp;
+{
+  struct spec *descr = (struct spec *) format_parse (string);
+
+  if (descr != NULL && descr->c99_directives_count > 0)
+    {
+      unsigned int n = descr->c99_directives_count;
+      struct interval *intervals =
+       (struct interval *) xmalloc (n * sizeof (struct interval));
+      unsigned int i;
+
+      for (i = 0; i < n; i++)
+       {
+         intervals[i].startpos = descr->c99_directives[2 * i] - string;
+         intervals[i].endpos = descr->c99_directives[2 * i + 1] - string;
+       }
+      *intervalsp = intervals;
+      *lengthp = n;
+    }
+  else
+    {
+      *intervalsp = NULL;
+      *lengthp = 0;
+    }
+}
+
+
 #ifdef TEST
 
 /* Test program: Print the argument list specification returned by
@@ -664,9 +866,48 @@ format_print (descr)
        case FAT_SIZE_LONGLONG:
          printf ("[long long]");
          break;
+       case FAT_SIZE_8_T:
+         printf ("[int8_t]");
+         break;
+       case FAT_SIZE_16_T:
+         printf ("[int16_t]");
+         break;
+       case FAT_SIZE_32_T:
+         printf ("[int32_t]");
+         break;
+       case FAT_SIZE_64_T:
+         printf ("[int64_t]");
+         break;
+       case FAT_SIZE_LEAST8_T:
+         printf ("[int_least8_t]");
+         break;
+       case FAT_SIZE_LEAST16_T:
+         printf ("[int_least16_t]");
+         break;
+       case FAT_SIZE_LEAST32_T:
+         printf ("[int_least32_t]");
+         break;
+       case FAT_SIZE_LEAST64_T:
+         printf ("[int_least64_t]");
+         break;
+       case FAT_SIZE_FAST8_T:
+         printf ("[int_fast8_t]");
+         break;
+       case FAT_SIZE_FAST16_T:
+         printf ("[int_fast16_t]");
+         break;
+       case FAT_SIZE_FAST32_T:
+         printf ("[int_fast32_t]");
+         break;
+       case FAT_SIZE_FAST64_T:
+         printf ("[int_fast64_t]");
+         break;
        case FAT_SIZE_INTMAX_T:
          printf ("[intmax_t]");
          break;
+       case FAT_SIZE_INTPTR_T:
+         printf ("[intptr_t]");
+         break;
        case FAT_SIZE_SIZE_T:
          printf ("[size_t]");
          break;
index 37f05647a0a5b29bd4c5e971245c3d47180e9e47..f19634f5c0af7ab69b72a6da4c088a5012a96d6d 100644 (file)
@@ -69,4 +69,15 @@ extern struct formatstring_parser formatstring_tcl;
 /* Table of all format string parsers.  */
 extern struct formatstring_parser *formatstring_parsers[NFORMATS];
 
+/* Returns an array of the ISO C 99 <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 */
index 058178d27e64dbd0d6088f964c0c9abd2f2d08a1..9f4091f0656623346aafd1ac9097e281f816b72d 100644 (file)
@@ -1464,6 +1464,8 @@ format_directive_message (that, msgid_string, msgid_pos, msgid_plural,
       mp->msgid = msgid_string;
       mp->msgid_plural = msgid_plural;
       mp->obsolete = obsolete;
+      for (i = 0; i < NFORMATS; i++)
+       mp->is_format[i] = this->is_format[i];
 
       if (!obsolete)
        {
index 590f1856577d00976584e7ca5e5d36e6c5793ea5..4d2ebb004759918d16c49698358cefd38bc74c2b 100644 (file)
 #define _(str) gettext (str)
 
 
-/* This defines the byte order within the file.  It needs to be set
-   appropriately once we have the file open.  */
-static enum { MO_LITTLE_ENDIAN, MO_BIG_ENDIAN } endian;
+/* We read the file completely into memory.  This is more efficient than
+   lots of lseek().  This struct represents the .mo file in memory.  */
+struct binary_mo_file
+{
+  const char *filename;
+  char *data;
+  size_t size;
+  enum { MO_LITTLE_ENDIAN, MO_BIG_ENDIAN } endian;
+};
 
 
 /* Prototypes for local functions.  Needed to ensure compiler checking of
    function argument counts despite of K&R C function definition syntax.  */
-static nls_uint32 read32 PARAMS ((FILE *fp, const char *fn));
-static void seek32 PARAMS ((FILE *fp, const char *fn, long offset));
-static char *string32 PARAMS ((FILE *fp, const char *fn, long offset,
-                              size_t *lengthp));
-
-
-/* This function reads a 32-bit number from the file, and assembles it
-   according to the current ``endian'' setting.  */
-static nls_uint32
-read32 (fp, fn)
+static void read_binary_mo_file PARAMS ((struct binary_mo_file *bfp,
+                                        FILE *fp, const char *filename));
+static nls_uint32 get_uint32 PARAMS ((const struct binary_mo_file *bfp,
+                                     size_t offset));
+static char * get_string PARAMS ((const struct binary_mo_file *bfp,
+                                 size_t offset, size_t *lengthp));
+static char * get_sysdep_string PARAMS ((const struct binary_mo_file *bfp,
+                                        size_t offset,
+                                        const struct mo_file_header *header,
+                                        size_t *lengthp));
+
+
+/* Read the contents of the given input stream.  */
+static void
+read_binary_mo_file (bfp, fp, filename)
+     struct binary_mo_file *bfp;
      FILE *fp;
-     const char *fn;
+     const char *filename;
 {
-  int c1, c2, c3, c4;
+  char *buf = NULL;
+  size_t alloc = 0;
+  size_t size = 0;
+  size_t count;
 
-  c1 = getc (fp);
-  if (c1 == EOF)
+  while (!feof (fp))
     {
-    bomb:
-      if (ferror (fp))
-       error (EXIT_FAILURE, errno, _("error while reading \"%s\""), fn);
-      error (EXIT_FAILURE, 0, _("file \"%s\" truncated"), fn);
+      const size_t increment = 4096;
+      if (size + increment > alloc)
+       {
+         alloc = alloc + alloc / 2;
+         if (alloc < size + increment)
+           alloc = size + increment;
+         buf = (char *) xrealloc (buf, alloc);
+       }
+      count = fread (buf + size, 1, increment, fp);
+      if (count == 0)
+       {
+         if (ferror (fp))
+           error (EXIT_FAILURE, errno, _("error while reading \"%s\""),
+                  filename);
+       }
+      else
+       size += count;
     }
-  c2 = getc (fp);
-  if (c2 == EOF)
-    goto bomb;
-  c3 = getc (fp);
-  if (c3 == EOF)
-    goto bomb;
-  c4 = getc (fp);
-  if (c4 == EOF)
-    goto bomb;
-  if (endian == MO_LITTLE_ENDIAN)
-    return (((nls_uint32) c1)
-           | ((nls_uint32) c2 << 8)
-           | ((nls_uint32) c3 << 16)
-           | ((nls_uint32) c4 << 24));
-
-  return (((nls_uint32) c1 << 24)
-         | ((nls_uint32) c2 << 16)
-         | ((nls_uint32) c3 << 8)
-         | ((nls_uint32) c4));
+  buf = (char *) xrealloc (buf, size);
+  bfp->filename = filename;
+  bfp->data = buf;
+  bfp->size = size;
 }
 
-
-static void
-seek32 (fp, fn, offset)
-     FILE *fp;
-     const char *fn;
-     long offset;
+/* Get a 32-bit number from the file, at the given file position.  */
+static nls_uint32
+get_uint32 (bfp, offset)
+     const struct binary_mo_file *bfp;
+     size_t offset;
 {
-  if (fseek (fp, offset, 0) < 0)
-    error (EXIT_FAILURE, errno, _("seek \"%s\" offset %ld failed"),
-          fn, offset);
+  nls_uint32 b0, b1, b2, b3;
+
+  if (offset + 4 > bfp->size)
+    error (EXIT_FAILURE, 0, _("file \"%s\" is truncated"), bfp->filename);
+
+  b0 = *(unsigned char *) (bfp->data + offset + 0);
+  b1 = *(unsigned char *) (bfp->data + offset + 1);
+  b2 = *(unsigned char *) (bfp->data + offset + 2);
+  b3 = *(unsigned char *) (bfp->data + offset + 3);
+  if (bfp->endian == MO_LITTLE_ENDIAN)
+    return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24);
+  else
+    return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
 }
 
+/* Get a static string from the file, at the given file position.  */
+static char *
+get_string (bfp, offset, lengthp)
+     const struct binary_mo_file *bfp;
+     size_t offset;
+     size_t *lengthp;
+{
+  /* See 'struct string_desc'.  */
+  nls_uint32 s_length = get_uint32 (bfp, offset);
+  nls_uint32 s_offset = get_uint32 (bfp, offset + 4);
+
+  if (s_offset + s_length + 1 > bfp->size)
+    error (EXIT_FAILURE, 0, _("file \"%s\" is truncated"), bfp->filename);
+  if (bfp->data[s_offset + s_length] != '\0')
+    error (EXIT_FAILURE, 0,
+          _("file \"%s\" contains a not NUL terminated string"),
+          bfp->filename);
+
+  *lengthp = s_length + 1;
+  return bfp->data + s_offset;
+}
 
+/* Get a system dependent string from the file, at the given file position.  */
 static char *
-string32 (fp, fn, offset, lengthp)
-     FILE *fp;
-     const char *fn;
-     long offset;
+get_sysdep_string (bfp, offset, header, lengthp)
+     const struct binary_mo_file *bfp;
+     size_t offset;
+     const struct mo_file_header *header;
      size_t *lengthp;
 {
-  long length;
-  char *buffer;
-  long n;
-
-  /* Read the string_desc structure, describing where in the file to
-     find the string.  */
-  seek32 (fp, fn, offset);
-  length = read32 (fp, fn);
-  offset = read32 (fp, fn);
-
-  /* Allocate memory for the string to be read into.  Leave space for
-     the NUL on the end.  */
-  buffer = (char *) xmalloc (length + 1);
-
-  /* Read in the string.  Complain if there is an error or it comes up
-     short.  Add the NUL ourselves.  */
-  seek32 (fp, fn, offset);
-  n = fread (buffer, 1, length + 1, fp);
-  if (n != length + 1)
+  /* See 'struct sysdep_string'.  */
+  size_t length;
+  char *string;
+  size_t i;
+  char *p;
+  nls_uint32 s_offset;
+
+  /* Compute the length.  */
+  length = 0;
+  for (i = 4; ; i += 8)
     {
-      if (ferror (fp))
-       error (EXIT_FAILURE, errno, _("error while reading \"%s\""), fn);
-      error (EXIT_FAILURE, 0, _("file \"%s\" truncated"), fn);
+      nls_uint32 segsize = get_uint32 (bfp, offset + i);
+      nls_uint32 sysdepref = get_uint32 (bfp, offset + i + 4);
+      nls_uint32 sysdep_segment_offset;
+      nls_uint32 ss_length;
+      nls_uint32 ss_offset;
+
+      length += segsize;
+
+      if (sysdepref == SEGMENTS_END)
+       break;
+      if (sysdepref >= header->n_sysdep_segments)
+       /* Invalid.  */
+         error (EXIT_FAILURE, 0, _("file \"%s\" is not in GNU .mo format"),
+                bfp->filename);
+      /* See 'struct sysdep_segment'.  */
+      sysdep_segment_offset = header->sysdep_segments_offset + sysdepref * 8;
+      ss_length = get_uint32 (bfp, sysdep_segment_offset);
+      ss_offset = get_uint32 (bfp, sysdep_segment_offset + 4);
+      if (ss_offset + ss_length > bfp->size)
+       error (EXIT_FAILURE, 0, _("file \"%s\" is truncated"), bfp->filename);
+      if (!(ss_length > 0 && bfp->data[ss_offset + ss_length - 1] == '\0'))
+       error (EXIT_FAILURE, 0,
+              _("file \"%s\" contains a not NUL terminated sysdep segment"),
+              bfp->filename);
+      length += 1 + strlen (bfp->data + ss_offset) + 1;
     }
-  if (buffer[length] != '\0')
+
+  /* Allocate and fill the string.  */
+  string = (char *) xmalloc (length);
+  p = string;
+  s_offset = get_uint32 (bfp, offset);
+  for (i = 4; ; i += 8)
     {
-      error (EXIT_FAILURE, 0,
-            _("file \"%s\" contains a not NUL terminated string"), fn);
+      nls_uint32 segsize = get_uint32 (bfp, offset + i);
+      nls_uint32 sysdepref = get_uint32 (bfp, offset + i + 4);
+      nls_uint32 sysdep_segment_offset;
+      nls_uint32 ss_length;
+      nls_uint32 ss_offset;
+      size_t n;
+
+      if (s_offset + segsize > bfp->size)
+       error (EXIT_FAILURE, 0, _("file \"%s\" is truncated"), bfp->filename);
+      memcpy (p, bfp->data + s_offset, segsize);
+      p += segsize;
+      s_offset += segsize;
+
+      if (sysdepref == SEGMENTS_END)
+       break;
+      if (sysdepref >= header->n_sysdep_segments)
+       abort ();
+      /* See 'struct sysdep_segment'.  */
+      sysdep_segment_offset = header->sysdep_segments_offset + sysdepref * 8;
+      ss_length = get_uint32 (bfp, sysdep_segment_offset);
+      ss_offset = get_uint32 (bfp, sysdep_segment_offset + 4);
+      if (ss_offset + ss_length > bfp->size)
+       abort ();
+      if (!(ss_length > 0 && bfp->data[ss_offset + ss_length - 1] == '\0'))
+       abort();
+      n = strlen (bfp->data + ss_offset);
+      *p++ = '<';
+      memcpy (p, bfp->data + ss_offset, n);
+      p += n;
+      *p++ = '>';
     }
 
-  /* Return the string to the caller.  */
-  *lengthp = length + 1;
-  return buffer;
-}
+  if (p != string + length)
+    abort ();
 
+  *lengthp = length;
+  return string;
+}
 
-/* This function reads an existing .mo file.  */
+/* Reads an existing .mo file and adds the messages to mlp.  */
 void
 read_mo_file (mlp, fn)
      message_list_ty *mlp;
      const char *fn;
 {
   FILE *fp;
+  struct binary_mo_file bf;
   struct mo_file_header header;
-  unsigned int j;
+  unsigned int i;
+  static lex_pos_ty pos = { __FILE__, __LINE__ };
 
   if (strcmp (fn, "-") == 0 || strcmp (fn, "/dev/stdin") == 0)
     {
@@ -171,16 +263,22 @@ read_mo_file (mlp, fn)
               _("error while opening \"%s\" for reading"), fn);
     }
 
+  /* Read the file contents into memory.  */
+  read_binary_mo_file (&bf, fp, fn);
+
+  /* Get a 32-bit number from the file header.  */
+# define GET_HEADER_FIELD(field) \
+    get_uint32 (&bf, offsetof (struct mo_file_header, field))
+
   /* We must grope the file to determine which endian it is.
      Perversity of the universe tends towards maximum, so it will
      probably not match the currently executing architecture.  */
-  endian = MO_BIG_ENDIAN;
-  header.magic = read32 (fp, fn);
+  bf.endian = MO_BIG_ENDIAN;
+  header.magic = GET_HEADER_FIELD (magic);
   if (header.magic != _MAGIC)
     {
-      endian = MO_LITTLE_ENDIAN;
-      seek32 (fp, fn, 0L);
-      header.magic = read32 (fp, fn);
+      bf.endian = MO_LITTLE_ENDIAN;
+      header.magic = GET_HEADER_FIELD (magic);
       if (header.magic != _MAGIC)
        {
        unrecognised:
@@ -189,37 +287,91 @@ read_mo_file (mlp, fn)
        }
     }
 
-  /* Fill the structure describing the header.  */
-  header.revision = read32 (fp, fn);
-  if (header.revision != MO_REVISION_NUMBER)
-    goto unrecognised;
-  header.nstrings = read32 (fp, fn);
-  header.orig_tab_offset = read32 (fp, fn);
-  header.trans_tab_offset = read32 (fp, fn);
-  header.hash_tab_size = read32 (fp, fn);
-  header.hash_tab_offset = read32 (fp, fn);
-
-  for (j = 0; j < header.nstrings; ++j)
+  header.revision = GET_HEADER_FIELD (revision);
+
+  /* We support only the major revision 0.  */
+  switch (header.revision >> 16)
     {
-      static lex_pos_ty pos = { __FILE__, __LINE__ };
-      message_ty *mp;
-      char *msgid;
-      size_t msgid_len;
-      char *msgstr;
-      size_t msgstr_len;
-
-      /* Read the msgid.  */
-      msgid = string32 (fp, fn, header.orig_tab_offset + j * 8, &msgid_len);
-
-      /* Read the msgstr.  */
-      msgstr = string32 (fp, fn, header.trans_tab_offset + j * 8, &msgstr_len);
-
-      mp = message_alloc (msgid,
-                         (strlen (msgid) + 1 < msgid_len
-                          ? msgid + strlen (msgid) + 1
-                          : NULL),
-                         msgstr, msgstr_len, &pos);
-      message_list_append (mlp, mp);
+    case 0:
+      /* Fill the header parts that apply to major revision 0.  */
+      header.nstrings = GET_HEADER_FIELD (nstrings);
+      header.orig_tab_offset = GET_HEADER_FIELD (orig_tab_offset);
+      header.trans_tab_offset = GET_HEADER_FIELD (trans_tab_offset);
+      header.hash_tab_size = GET_HEADER_FIELD (hash_tab_size);
+      header.hash_tab_offset = GET_HEADER_FIELD (hash_tab_offset);
+
+      for (i = 0; i < header.nstrings; i++)
+       {
+         message_ty *mp;
+         char *msgid;
+         size_t msgid_len;
+         char *msgstr;
+         size_t msgstr_len;
+
+         /* Read the msgid.  */
+         msgid = get_string (&bf, header.orig_tab_offset + i * 8,
+                             &msgid_len);
+
+         /* Read the msgstr.  */
+         msgstr = get_string (&bf, header.trans_tab_offset + i * 8,
+                              &msgstr_len);
+
+         mp = message_alloc (msgid,
+                             (strlen (msgid) + 1 < msgid_len
+                              ? msgid + strlen (msgid) + 1
+                              : NULL),
+                             msgstr, msgstr_len,
+                             &pos);
+         message_list_append (mlp, mp);
+       }
+
+      switch (header.revision & 0xffff)
+       {
+       case 0:
+         break;
+       case 1:
+       default:
+         /* Fill the header parts that apply to minor revision >= 1.  */
+         header.n_sysdep_segments = GET_HEADER_FIELD (n_sysdep_segments);
+         header.sysdep_segments_offset =
+           GET_HEADER_FIELD (sysdep_segments_offset);
+         header.n_sysdep_strings = GET_HEADER_FIELD (n_sysdep_strings);
+         header.orig_sysdep_tab_offset =
+           GET_HEADER_FIELD (orig_sysdep_tab_offset);
+         header.trans_sysdep_tab_offset =
+           GET_HEADER_FIELD (trans_sysdep_tab_offset);
+
+         for (i = 0; i < header.n_sysdep_strings; i++)
+           {
+             message_ty *mp;
+             char *msgid;
+             size_t msgid_len;
+             char *msgstr;
+             size_t msgstr_len;
+             nls_uint32 offset;
+
+             /* Read the msgid.  */
+             offset = get_uint32 (&bf, header.orig_sysdep_tab_offset + i * 4);
+             msgid = get_sysdep_string (&bf, offset, &header, &msgid_len);
+
+             /* Read the msgstr.  */
+             offset = get_uint32 (&bf, header.trans_sysdep_tab_offset + i * 4);
+             msgstr = get_sysdep_string (&bf, offset, &header, &msgstr_len);
+
+             mp = message_alloc (msgid,
+                                 (strlen (msgid) + 1 < msgid_len
+                                  ? msgid + strlen (msgid) + 1
+                                  : NULL),
+                                 msgstr, msgstr_len,
+                                 &pos);
+             message_list_append (mlp, mp);
+           }
+         break;
+       }
+      break;
+
+    default:
+      goto unrecognised;
     }
 
   if (fp != stdin)
index 0127d47c1318833cc706f46c04c2d7126b53ef70..9db7f180a531fd192c7506332dd2f55b36cf9151 100644 (file)
 #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
@@ -68,35 +72,266 @@ static int compare_id PARAMS ((const void *pval1, const void *pval2));
 static void write_table PARAMS ((FILE *output_file, message_list_ty *mlp));
 
 
-/* Define the data structure which we need to represent the data to
-   be written out.  */
-struct id_str_pair
+/* Indices into the strings contained in 'struct pre_message' and
+   'struct pre_sysdep_message'.  */
+enum
+{
+  M_ID = 0,    /* msgid - the original string */
+  M_STR = 1    /* msgstr - the translated string */
+};
+
+/* An intermediate data structure representing a 'struct string_desc'.  */
+struct pre_string
+{
+  size_t length;
+  const char *pointer;
+};
+
+/* An intermediate data structure representing a message.  */
+struct pre_message
 {
-  const char *id;
-  size_t id_len;
+  struct pre_string str[2];
   const char *id_plural;
   size_t id_plural_len;
-  const char *str;
-  size_t str_len;
 };
 
-
 static int
 compare_id (pval1, pval2)
      const void *pval1;
      const void *pval2;
 {
-  return strcmp (((struct id_str_pair *) pval1)->id,
-                ((struct id_str_pair *) pval2)->id);
+  return strcmp (((struct pre_message *) pval1)->str[M_ID].pointer,
+                ((struct pre_message *) pval2)->str[M_ID].pointer);
 }
 
 
+/* An intermediate data structure representing a 'struct sysdep_segment'.  */
+struct pre_sysdep_segment
+{
+  size_t length;
+  const char *pointer;
+};
+
+/* An intermediate data structure representing a 'struct segment_pair'.  */
+struct pre_segment_pair
+{
+  size_t segsize;
+  const char *segptr;
+  size_t sysdepref;
+};
+
+/* An intermediate data structure representing a 'struct sysdep_string'.  */
+struct pre_sysdep_string
+{
+  unsigned int segmentcount;
+  struct pre_segment_pair segments[1];
+};
+
+/* An intermediate data structure representing a message with system dependent
+   strings.  */
+struct pre_sysdep_message
+{
+  struct pre_sysdep_string *str[2];
+  const char *id_plural;
+  size_t id_plural_len;
+};
+
+/* Write the message list to the given open file.  */
 static void
 write_table (output_file, mlp)
      FILE *output_file;
      message_list_ty *mlp;
 {
-  static char null = '\0';
+  size_t nstrings;
+  struct pre_message *msg_arr;
+  size_t n_sysdep_strings;
+  struct pre_sysdep_message *sysdep_msg_arr;
+  size_t n_sysdep_segments;
+  struct pre_sysdep_segment *sysdep_segments;
+  int minor_revision;
+  bool omit_hash_table;
+  nls_uint32 hash_tab_size;
+  struct mo_file_header header;        /* Header of the .mo file to be written.  */
+  size_t header_size;
+  size_t offset;
+  struct string_desc *orig_tab;
+  struct string_desc *trans_tab;
+  size_t sysdep_tab_offset = 0;
+  size_t end_offset;
+  char *null;
+  size_t j, m;
+
+  /* First pass: Move the static string pairs into an array, for sorting,
+     and at the same time, compute the segments of the system dependent
+     strings.  */
+  nstrings = 0;
+  msg_arr =
+    (struct pre_message *)
+    alloca (mlp->nitems * sizeof (struct pre_message));
+  n_sysdep_strings = 0;
+  sysdep_msg_arr =
+    (struct pre_sysdep_message *)
+    alloca (mlp->nitems * sizeof (struct pre_sysdep_message));
+  n_sysdep_segments = 0;
+  sysdep_segments = NULL;
+  for (j = 0; j < mlp->nitems; j++)
+    {
+      message_ty *mp = mlp->item[j];
+      struct interval *intervals[2];
+      size_t nintervals[2];
+
+      intervals[M_ID] = NULL;
+      nintervals[M_ID] = 0;
+      intervals[M_STR] = NULL;
+      nintervals[M_STR] = 0;
+
+      /* Test if mp contains system dependent strings and thus
+        requires the use of the .mo file minor revision 1.  */
+      if (possible_format_p (mp->is_format[format_c]))
+       {
+         /* Check whether msgid or msgstr contain ISO C 99 <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
@@ -114,142 +349,331 @@ write_table (output_file, mlp)
      If we now choose M to be the next prime bigger than 4 / 3 * N,
      we get the values
                         4   and   1.85  resp.
-     Because unsuccesful searches are unlikely this is a good value.
+     Because unsuccessful searches are unlikely this is a good value.
      Formulas: [Knuth, The Art of Computer Programming, Volume 3,
                Sorting and Searching, 1973, Addison Wesley]  */
-  nls_uint32 hash_tab_size =
-    (no_hash_table ? 0 : next_prime ((mlp->nitems * 4) / 3));
-  nls_uint32 *hash_tab;
-
-  /* Header of the .mo file to be written.  */
-  struct mo_file_header header;
-  struct id_str_pair *msg_arr;
-  size_t cnt, j;
-  message_ty *entry;
-  struct string_desc sd;
-
-  /* Fill the structure describing the header.  */
-  header.magic = _MAGIC;               /* Magic number.  */
-  header.revision = MO_REVISION_NUMBER;        /* Revision number of file format.  */
-  header.nstrings = mlp->nitems;       /* Number of strings.  */
-  header.orig_tab_offset = sizeof (header);
-                       /* Offset of table for original string offsets.  */
-  header.trans_tab_offset = sizeof (header)
-                           + mlp->nitems * sizeof (struct string_desc);
-                       /* Offset of table for translation string offsets.  */
-  header.hash_tab_size = hash_tab_size;        /* Size of used hashing table.  */
-  header.hash_tab_offset =
-       no_hash_table ? 0 : sizeof (header)
-                           + 2 * (mlp->nitems * sizeof (struct string_desc));
-                       /* Offset of hashing table.  */
+  if (!omit_hash_table)
+    {
+      hash_tab_size = next_prime ((mlp->nitems * 4) / 3);
+      /* Ensure M > 2.  */
+      if (hash_tab_size <= 2)
+       hash_tab_size = 3;
+    }
+  else
+    hash_tab_size = 0;
+
+
+  /* Second pass: Fill the structure describing the header.  At the same time,
+     compute the sizes and offsets of the non-string parts of the file.  */
+
+  /* Magic number.  */
+  header.magic = _MAGIC;
+  /* Revision number of file format.  */
+  header.revision = (MO_REVISION_NUMBER << 16) + minor_revision;
+
+  header_size =
+    (minor_revision == 0
+     ? offsetof (struct mo_file_header, n_sysdep_segments)
+     : sizeof (struct mo_file_header));
+  offset = header_size;
+
+  /* Number of static string pairs.  */
+  header.nstrings = nstrings;
+
+  /* Offset of table for original string offsets.  */
+  header.orig_tab_offset = offset;
+  offset += nstrings * sizeof (struct string_desc);
+  orig_tab =
+    (struct string_desc *) alloca (nstrings * sizeof (struct string_desc));
+
+  /* Offset of table for translated string offsets.  */
+  header.trans_tab_offset = offset;
+  offset += nstrings * sizeof (struct string_desc);
+  trans_tab =
+    (struct string_desc *) alloca (nstrings * sizeof (struct string_desc));
+
+  /* Size of hash table.  */
+  header.hash_tab_size = hash_tab_size;
+  /* Offset of hash table.  */
+  header.hash_tab_offset = offset;
+  offset += hash_tab_size * sizeof (nls_uint32);
+
+  if (minor_revision >= 1)
+    {
+      /* Size of table describing system dependent segments.  */
+      header.n_sysdep_segments = n_sysdep_segments;
+      /* Offset of table describing system dependent segments.  */
+      header.sysdep_segments_offset = offset;
+      offset += n_sysdep_segments * sizeof (struct sysdep_segment);
+
+      /* Number of system dependent string pairs.  */
+      header.n_sysdep_strings = n_sysdep_strings;
+
+      /* Offset of table for original sysdep string offsets.  */
+      header.orig_sysdep_tab_offset = offset;
+      offset += n_sysdep_strings * sizeof (nls_uint32);
+
+      /* Offset of table for translated sysdep string offsets.  */
+      header.trans_sysdep_tab_offset = offset;
+      offset += n_sysdep_strings * sizeof (nls_uint32);
+
+      /* System dependent string descriptors.  */
+      sysdep_tab_offset = offset;
+      for (m = 0; m < 2; m++)
+       for (j = 0; j < n_sysdep_strings; j++)
+         offset += sizeof (struct sysdep_string)
+                   + sysdep_msg_arr[j].str[m]->segmentcount
+                     * sizeof (struct segment_pair);
+    }
 
-  /* Write the header out.  */
-  fwrite (&header, sizeof (header), 1, output_file);
+  end_offset = offset;
 
-  /* Allocate table for the all elements of the hashing table.  */
-  msg_arr = (struct id_str_pair *) alloca (mlp->nitems * sizeof (msg_arr[0]));
 
-  /* Read values from list into array.  */
-  for (j = 0; j < mlp->nitems; j++)
-    {
-      entry = mlp->item[j];
-
-      msg_arr[j].id = entry->msgid;
-      msg_arr[j].id_len = strlen (entry->msgid) + 1;
-      msg_arr[j].id_plural = entry->msgid_plural;
-      msg_arr[j].id_plural_len =
-       (entry->msgid_plural != NULL ? strlen (entry->msgid_plural) + 1 : 0);
-      msg_arr[j].str = entry->msgstr;
-      msg_arr[j].str_len = entry->msgstr_len;
-    }
+  /* Third pass: Write the non-string parts of the file.  At the same time,
+     compute the offsets of each string, including the proper alignment.  */
 
-  /* Sort the table according to original string.  */
-  qsort (msg_arr, mlp->nitems, sizeof (msg_arr[0]), compare_id);
+  /* Write the header out.  */
+  fwrite (&header, header_size, 1, output_file);
 
-  /* Set offset to first byte after all the tables.  */
-  sd.offset = roundup (sizeof (header)
-                      + mlp->nitems * sizeof (sd)
-                      + mlp->nitems * sizeof (sd)
-                      + hash_tab_size * sizeof (nls_uint32),
-                      alignment);
+  /* Table for original string offsets.  */
+  /* Here output_file is at position header.orig_tab_offset.  */
 
-  /* Write out length and starting offset for all original strings.  */
-  for (cnt = 0; cnt < mlp->nitems; ++cnt)
+  for (j = 0; j < nstrings; j++)
     {
+      offset = roundup (offset, alignment);
+      orig_tab[j].length =
+       msg_arr[j].str[M_ID].length + msg_arr[j].id_plural_len;
+      orig_tab[j].offset = offset;
+      offset += orig_tab[j].length;
       /* Subtract 1 because of the terminating NUL.  */
-      sd.length = msg_arr[cnt].id_len + msg_arr[cnt].id_plural_len - 1;
-      fwrite (&sd, sizeof (sd), 1, output_file);
-      sd.offset += roundup (sd.length + 1, alignment);
+      orig_tab[j].length--;
     }
+  fwrite (orig_tab, nstrings * sizeof (struct string_desc), 1, output_file);
+
+  /* Table for translated string offsets.  */
+  /* Here output_file is at position header.trans_tab_offset.  */
 
-  /* Write out length and starting offset for all translation strings.  */
-  for (cnt = 0; cnt < mlp->nitems; ++cnt)
+  for (j = 0; j < nstrings; j++)
     {
+      offset = roundup (offset, alignment);
+      trans_tab[j].length = msg_arr[j].str[M_STR].length;
+      trans_tab[j].offset = offset;
+      offset += trans_tab[j].length;
       /* Subtract 1 because of the terminating NUL.  */
-      sd.length = msg_arr[cnt].str_len - 1;
-      fwrite (&sd, sizeof (sd), 1, output_file);
-      sd.offset += roundup (sd.length + 1, alignment);
+      trans_tab[j].length--;
     }
+  fwrite (trans_tab, nstrings * sizeof (struct string_desc), 1, output_file);
 
   /* Skip this part when no hash table is needed.  */
-  if (!no_hash_table)
+  if (!omit_hash_table)
     {
+      nls_uint32 *hash_tab;
+      unsigned int j;
+
+      /* Here output_file is at position header.hash_tab_offset.  */
+
       /* Allocate room for the hashing table to be written out.  */
       hash_tab = (nls_uint32 *) alloca (hash_tab_size * sizeof (nls_uint32));
       memset (hash_tab, '\0', hash_tab_size * sizeof (nls_uint32));
 
       /* Insert all value in the hash table, following the algorithm described
         above.  */
-      for (cnt = 0; cnt < mlp->nitems; ++cnt)
+      for (j = 0; j < nstrings; j++)
        {
-         nls_uint32 hash_val = hash_string (msg_arr[cnt].id);
+         nls_uint32 hash_val = hash_string (msg_arr[j].str[M_ID].pointer);
          nls_uint32 idx = hash_val % hash_tab_size;
 
          if (hash_tab[idx] != 0)
            {
              /* We need the second hashing function.  */
-             nls_uint32 c = 1 + (hash_val % (hash_tab_size - 2));
+             nls_uint32 incr = 1 + (hash_val % (hash_tab_size - 2));
 
              do
-               if (idx >= hash_tab_size - c)
-                 idx -= hash_tab_size - c;
+               if (idx >= hash_tab_size - incr)
+                 idx -= hash_tab_size - incr;
                else
-                 idx += c;
+                 idx += incr;
              while (hash_tab[idx] != 0);
            }
 
-         hash_tab[idx] = cnt + 1;
+         hash_tab[idx] = j + 1;
        }
 
       /* Write the hash table out.  */
-      fwrite (hash_tab, sizeof (nls_uint32), hash_tab_size, output_file);
+      fwrite (hash_tab, hash_tab_size * sizeof (nls_uint32), 1, output_file);
+
+      freea (hash_tab);
     }
 
-  /* Write bytes to make first string to be aligned.  */
-  cnt = sizeof (header) + 2 * mlp->nitems * sizeof (sd)
-       + hash_tab_size * sizeof (nls_uint32);
-  fwrite (&null, 1, roundup (cnt, alignment) - cnt, output_file);
+  if (minor_revision >= 1)
+    {
+      struct sysdep_segment *sysdep_segments_tab;
+      nls_uint32 *sysdep_tab;
+      size_t stoffset;
+      unsigned int i;
+
+      /* Here output_file is at position header.sysdep_segments_offset.  */
+
+      sysdep_segments_tab =
+       (struct sysdep_segment *)
+       alloca (n_sysdep_segments * sizeof (struct sysdep_segment));
+      for (i = 0; i < n_sysdep_segments; i++)
+       {
+         offset = roundup (offset, alignment);
+         /* The "+ 1" accounts for the trailing NUL byte.  */
+         sysdep_segments_tab[i].length = sysdep_segments[i].length + 1;
+         sysdep_segments_tab[i].offset = offset;
+         offset += sysdep_segments_tab[i].length;
+       }
+
+      fwrite (sysdep_segments_tab,
+             n_sysdep_segments * sizeof (struct sysdep_segment), 1,
+             output_file);
+
+      freea (sysdep_segments_tab);
+
+      sysdep_tab =
+       (nls_uint32 *) alloca (n_sysdep_strings * sizeof (nls_uint32));
+      stoffset = sysdep_tab_offset;
+
+      for (m = 0; m < 2; m++)
+       {
+         /* Here output_file is at position
+            m == M_ID  -> header.orig_sysdep_tab_offset,
+            m == M_STR -> header.trans_sysdep_tab_offset.  */
+
+         for (j = 0; j < n_sysdep_strings; j++)
+           {
+             sysdep_tab[j] = stoffset;
+             stoffset += sizeof (struct sysdep_string)
+                         + sysdep_msg_arr[j].str[m]->segmentcount
+                           * sizeof (struct segment_pair);
+           }
+         /* Write the table for original/translated sysdep string offsets.  */
+         fwrite (sysdep_tab, n_sysdep_strings * sizeof (nls_uint32), 1,
+                 output_file);
+       }
+
+      freea (sysdep_tab);
+
+      /* Here output_file is at position sysdep_tab_offset.  */
+
+      for (m = 0; m < 2; m++)
+       for (j = 0; j < n_sysdep_strings; j++)
+         {
+           struct pre_sysdep_message *msg = &sysdep_msg_arr[j];
+           struct pre_sysdep_string *pre = msg->str[m];
+           struct sysdep_string *str =
+             (struct sysdep_string *)
+             alloca (sizeof (struct sysdep_string)
+                     + pre->segmentcount * sizeof (struct segment_pair));
+           unsigned int i;
+
+           offset = roundup (offset, alignment);
+           str->offset = offset;
+           for (i = 0; i <= pre->segmentcount; i++)
+             {
+               str->segments[i].segsize = pre->segments[i].segsize;
+               str->segments[i].sysdepref = pre->segments[i].sysdepref;
+               offset += str->segments[i].segsize;
+             }
+           if (m == M_ID && msg->id_plural_len > 0)
+             {
+               str->segments[pre->segmentcount].segsize += msg->id_plural_len;
+               offset += msg->id_plural_len;
+             }
+           fwrite (str,
+                   sizeof (struct sysdep_string)
+                   + pre->segmentcount * sizeof (struct segment_pair),
+                   1, output_file);
+
+           freea (str);
+         }
+    }
+
+  /* Here output_file is at position end_offset.  */
+
+  freea (trans_tab);
+  freea (orig_tab);
+
+
+  /* Fourth pass: Write the strings.  */
+
+  offset = end_offset;
+
+  /* A few zero bytes for padding.  */
+  null = alloca (alignment);
+  memset (null, '\0', alignment);
 
   /* Now write the original strings.  */
-  for (cnt = 0; cnt < mlp->nitems; ++cnt)
+  for (j = 0; j < nstrings; j++)
     {
-      size_t len = msg_arr[cnt].id_len + msg_arr[cnt].id_plural_len;
+      fwrite (null, roundup (offset, alignment) - offset, 1, output_file);
+      offset = roundup (offset, alignment);
 
-      fwrite (msg_arr[cnt].id, msg_arr[cnt].id_len, 1, output_file);
-      if (msg_arr[cnt].id_plural_len > 0)
-       fwrite (msg_arr[cnt].id_plural, msg_arr[cnt].id_plural_len, 1,
+      fwrite (msg_arr[j].str[M_ID].pointer, msg_arr[j].str[M_ID].length, 1,
+             output_file);
+      if (msg_arr[j].id_plural_len > 0)
+       fwrite (msg_arr[j].id_plural, msg_arr[j].id_plural_len, 1,
                output_file);
-      fwrite (&null, 1, roundup (len, alignment) - len, output_file);
+      offset += msg_arr[j].str[M_ID].length + msg_arr[j].id_plural_len;
     }
 
-  /* Now write the translation strings.  */
-  for (cnt = 0; cnt < mlp->nitems; ++cnt)
+  /* Now write the translated strings.  */
+  for (j = 0; j < nstrings; j++)
     {
-      size_t len = msg_arr[cnt].str_len;
+      fwrite (null, roundup (offset, alignment) - offset, 1, output_file);
+      offset = roundup (offset, alignment);
 
-      fwrite (msg_arr[cnt].str, len, 1, output_file);
-      fwrite (&null, 1, roundup (len, alignment) - len, output_file);
+      fwrite (msg_arr[j].str[M_STR].pointer, msg_arr[j].str[M_STR].length, 1,
+             output_file);
+      offset += msg_arr[j].str[M_STR].length;
     }
+
+  if (minor_revision >= 1)
+    {
+      unsigned int i;
+
+      for (i = 0; i < n_sysdep_segments; i++)
+       {
+         fwrite (null, roundup (offset, alignment) - offset, 1, output_file);
+         offset = roundup (offset, alignment);
+
+         fwrite (sysdep_segments[i].pointer, sysdep_segments[i].length, 1,
+                 output_file);
+         fwrite (null, 1, 1, output_file);
+         offset += sysdep_segments[i].length + 1;
+       }
+
+      for (m = 0; m < 2; m++)
+       for (j = 0; j < n_sysdep_strings; j++)
+         {
+           struct pre_sysdep_message *msg = &sysdep_msg_arr[j];
+           struct pre_sysdep_string *pre = msg->str[m];
+
+           fwrite (null, roundup (offset, alignment) - offset, 1,
+                   output_file);
+           offset = roundup (offset, alignment);
+
+           for (i = 0; i <= pre->segmentcount; i++)
+             {
+               fwrite (pre->segments[i].segptr, pre->segments[i].segsize, 1,
+                       output_file);
+               offset += pre->segments[i].segsize;
+             }
+           if (m == M_ID && msg->id_plural_len > 0)
+             {
+               fwrite (msg->id_plural, msg->id_plural_len, 1, output_file);
+               offset += msg->id_plural_len;
+             }
+
+           freea (pre);
+         }
+    }
+
+  freea (null);
+  freea (sysdep_msg_arr);
+  freea (msg_arr);
 }
 
 
index 494552f8d7195747e338aca1fa4250050d93dc4a..8f359718e91ba1c5cc1706c65f5ca47e661cc213 100644 (file)
--- a/src/x-c.c
+++ b/src/x-c.c
@@ -159,6 +159,9 @@ static void phase5_unget PARAMS ((token_ty *tp));
 static void phaseX_get PARAMS ((token_ty *tp));
 static void phase6_get PARAMS ((token_ty *tp));
 static void phase6_unget PARAMS ((token_ty *tp));
+static bool is_inttypes_macro PARAMS ((const char *name));
+static void phase8a_get PARAMS ((token_ty *tp));
+static void phase8a_unget PARAMS ((token_ty *tp));
 static void phase8_get PARAMS ((token_ty *tp));
 static void x_c_lex PARAMS ((xgettext_token_ty *tp));
 static bool extract_parenthesized PARAMS ((message_list_ty *mlp,
@@ -1176,6 +1179,77 @@ phase6_unget (tp)
 }
 
 
+/* 8a. Convert ISO C 99 section 7.8.1 format string directives to string
+   literal placeholders.  */
+
+/* Test for an ISO C 99 section 7.8.1 format string directive.  */
+static bool
+is_inttypes_macro (name)
+     const char *name;
+{
+  /* Syntax:
+     P R I { d | i | o | u | x | X }
+     { { | LEAST | FAST } { 8 | 16 | 32 | 64 } | MAX | PTR }  */
+  if (name[0] == 'P' && name[1] == 'R' && name[2] == 'I')
+    {
+      name += 3;
+      if (name[0] == 'd' || name[0] == 'i' || name[0] == 'o' || name[0] == 'u'
+         || name[0] == 'x' || name[0] == 'X')
+       {
+         name += 1;
+         if (name[0] == 'M' && name[1] == 'A' && name[2] == 'X'
+             && name[3] == '\0')
+           return true;
+         if (name[0] == 'P' && name[1] == 'T' && name[2] == 'R'
+             && name[3] == '\0')
+           return true;
+         if (name[0] == 'L' && name[1] == 'E' && name[2] == 'A'
+             && name[3] == 'S' && name[4] == 'T')
+           name += 5;
+         else if (name[0] == 'F' && name[1] == 'A' && name[2] == 'S'
+                  && name[3] == 'T')
+           name += 4;
+         if (name[0] == '8' && name[1] == '\0')
+           return true;
+         if (name[0] == '1' && name[1] == '6' && name[2] == '\0')
+           return true;
+         if (name[0] == '3' && name[1] == '2' && name[2] == '\0')
+           return true;
+         if (name[0] == '6' && name[1] == '4' && name[2] == '\0')
+           return true;
+       }
+    }
+  return false;
+}
+
+static void
+phase8a_get (tp)
+     token_ty *tp;
+{
+  phase6_get (tp);
+  if (tp->type == token_type_name && is_inttypes_macro (tp->string))
+    {
+      /* Turn PRIdXXX into "<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).  */
@@ -1184,7 +1258,7 @@ static void
 phase8_get (tp)
      token_ty *tp;
 {
-  phase6_get (tp);
+  phase8a_get (tp);
   if (tp->type != token_type_string_literal)
     return;
   for (;;)
@@ -1192,14 +1266,14 @@ phase8_get (tp)
       token_ty tmp;
       size_t len;
 
-      phase6_get (&tmp);
+      phase8a_get (&tmp);
       if (tmp.type == token_type_white_space)
        continue;
       if (tmp.type == token_type_eoln)
        continue;
       if (tmp.type != token_type_string_literal)
        {
-         phase6_unget (&tmp);
+         phase8a_unget (&tmp);
          return;
        }
       len = strlen (tp->string);
index 36461dd0464f342cbc9e8e193fc3f55411c858f4..774d3e76dd99562db5d99ec3ad14e427a503ab4f 100644 (file)
@@ -1,3 +1,16 @@
+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.
index 65bd5fc00096e649bd6a1d51a442201af1c7a9ac..925fe30ffa58ab773e59501444db650cab8a731e 100644 (file)
@@ -34,7 +34,7 @@ TESTS = gettext-1 gettext-2 \
        msgexec-1 msgexec-2 \
        msgfilter-1 msgfilter-2 \
        msgfmt-1 msgfmt-2 msgfmt-3 msgfmt-4 msgfmt-5 msgfmt-6 msgfmt-7 \
-       msgfmt-8 msgfmt-9 msgfmt-10 msgfmt-11 \
+       msgfmt-8 msgfmt-9 msgfmt-10 msgfmt-11 msgfmt-12 \
        msggrep-1 msggrep-2 msggrep-3 msggrep-4 msggrep-5 \
        msgmerge-1 msgmerge-2 msgmerge-3 msgmerge-4 msgmerge-5 msgmerge-6 \
        msgmerge-7 msgmerge-8 msgmerge-9 msgmerge-10 msgmerge-11 msgmerge-12 \
@@ -45,9 +45,9 @@ TESTS = gettext-1 gettext-2 \
        xgettext-1 xgettext-2 xgettext-3 xgettext-4 xgettext-5 xgettext-6 \
        xgettext-7 xgettext-8 xgettext-9 xgettext-10 xgettext-11 xgettext-12 \
        xgettext-13 xgettext-14 xgettext-15 xgettext-16 xgettext-17 \
-       xgettext-18 xgettext-19 xgettext-20 xgettext-21 \
+       xgettext-18 xgettext-19 xgettext-20 xgettext-21 xgettext-22 \
        format-awk-1 format-awk-2 \
-       format-c-1 format-c-2 \
+       format-c-1 format-c-2 format-c-3 format-c-4 \
        format-elisp-1 format-elisp-2 \
        format-java-1 format-java-2 \
        format-librep-1 format-librep-2 \
@@ -136,13 +136,17 @@ DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@
 LDADD = $(LDADD_@USE_INCLUDED_LIBINTL@)
 LDADD_yes = ../intl/libintl.la
 LDADD_no = ../intl/libgnuintl.la @LTLIBINTL@
-noinst_PROGRAMS = tstgettext tstngettext cake
+noinst_PROGRAMS = tstgettext tstngettext cake fc3 fc4
 tstgettext_SOURCES = tstgettext.c setlocale.c
 tstgettext_LDADD = ../lib/libgettextlib.la $(LDADD)
 tstngettext_SOURCES = tstngettext.c setlocale.c
 tstngettext_LDADD = ../lib/libgettextlib.la $(LDADD)
 cake_SOURCES = plural-1-prg.c setlocale.c
 cake_LDADD = ../lib/libgettextlib.la $(LDADD)
+fc3_SOURCES = format-c-3-prg.c setlocale.c
+fc3_LDADD = ../lib/libgettextlib.la $(LDADD)
+fc4_SOURCES = format-c-4-prg.c setlocale.c
+fc4_LDADD = ../lib/libgettextlib.la $(LDADD)
 
 # Help maintaining config.rpath.
 rpathcfg: rpathcfg.sh
diff --git a/tests/format-c-3 b/tests/format-c-3
new file mode 100755 (executable)
index 0000000..7afd902
--- /dev/null
@@ -0,0 +1,51 @@
+#! /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
diff --git a/tests/format-c-3-prg.c b/tests/format-c-3-prg.c
new file mode 100644 (file)
index 0000000..8ff0e09
--- /dev/null
@@ -0,0 +1,89 @@
+/* Test program, used by the format-c-3 test.
+   Copyright (C) 2002 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <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
+}
diff --git a/tests/format-c-4 b/tests/format-c-4
new file mode 100755 (executable)
index 0000000..2359a24
--- /dev/null
@@ -0,0 +1,55 @@
+#! /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
diff --git a/tests/format-c-4-prg.c b/tests/format-c-4-prg.c
new file mode 100644 (file)
index 0000000..c764aac
--- /dev/null
@@ -0,0 +1,89 @@
+/* Test program, used by the format-c-4 test.
+   Copyright (C) 2002 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <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
+}
diff --git a/tests/msgfmt-12 b/tests/msgfmt-12
new file mode 100755 (executable)
index 0000000..427b833
--- /dev/null
@@ -0,0 +1,56 @@
+#! /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
diff --git a/tests/xgettext-22 b/tests/xgettext-22
new file mode 100755 (executable)
index 0000000..a250a7c
--- /dev/null
@@ -0,0 +1,47 @@
+#! /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