Make fdwalk work when /proc isn't mounted
[kopensolaris-gnu/glibc.git] / catgets / gencat.c
index ccc6585..d8ce7af 100644 (file)
@@ -1,41 +1,45 @@
-/* Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
+/* Copyright (C) 1996-2005, 2006, 2007 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
-   Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1996.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 1996.
 
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Library General Public License as
-   published by the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
+   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; version 2 of the License, or
+   (at your option) any later version.
 
-   The GNU C Library is distributed in the hope that it will be useful,
+   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
-   Library General Public License for more details.
+   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 Library General Public
-   License along with the GNU C Library; see the file COPYING.LIB.  If not,
-   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.  */
+   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>
+# include "config.h"
 #endif
 
 #include <argp.h>
+#include <assert.h>
 #include <ctype.h>
 #include <endian.h>
 #include <errno.h>
 #include <error.h>
 #include <fcntl.h>
+#include <iconv.h>
+#include <langinfo.h>
 #include <locale.h>
 #include <libintl.h>
 #include <limits.h>
 #include <nl_types.h>
 #include <obstack.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <wchar.h>
 
 #include "version.h"
 
@@ -78,7 +82,7 @@ struct catalog
   struct set_list *all_sets;
   struct set_list *current_set;
   size_t total_messages;
-  char quote_char;
+  wint_t quote_char;
   int last_set;
 
   struct obstack mem_pool;
@@ -136,6 +140,8 @@ static struct argp argp =
 /* Wrapper functions with error checking for standard functions.  */
 extern void *xmalloc (size_t n);
 extern void *xcalloc (size_t n, size_t s);
+extern void *xrealloc (void *o, size_t n);
+extern char *xstrdup (const char *);
 
 /* Prototypes for local functions.  */
 static void error_print (void);
@@ -144,9 +150,12 @@ static struct catalog *read_input_file (struct catalog *current,
 static void write_out (struct catalog *result, const char *output_name,
                       const char *header_name);
 static struct set_list *find_set (struct catalog *current, int number);
-static void normalize_line (const char *fname, size_t line, char *string,
-                           char quote_char);
+static void normalize_line (const char *fname, size_t line, iconv_t cd,
+                           wchar_t *string, wchar_t quote_char,
+                           wchar_t escape_char);
 static void read_old (struct catalog *catalog, const char *file_name);
+static int open_conversion (const char *codesetp, iconv_t *cd_towcp,
+                           iconv_t *cd_tombp, wchar_t *escape_charp);
 
 
 int
@@ -187,7 +196,7 @@ main (int argc, char *argv[])
   if (result != NULL)
     write_out (result, output_name, header_name);
 
-  exit (EXIT_SUCCESS);
+  return error_message_count != 0;
 }
 
 
@@ -221,7 +230,8 @@ more_help (int key, const char *text, void *input)
     case ARGP_KEY_HELP_EXTRA:
       /* We print some extra information.  */
       return strdup (gettext ("\
-Report bugs using the `glibcbug' script to <bugs@gnu.org>.\n"));
+For bug reporting instructions, please see:\n\
+<http://www.gnu.org/software/libc/bugs.html>.\n"));
     default:
       break;
     }
@@ -237,7 +247,7 @@ print_version (FILE *stream, struct argp_state *state)
 Copyright (C) %s Free Software Foundation, Inc.\n\
 This is free software; see the source for copying conditions.  There is NO\n\
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
-"), "1999");
+"), "2007");
   fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
 }
 
@@ -259,6 +269,12 @@ read_input_file (struct catalog *current, const char *fname)
   char *buf;
   size_t len;
   size_t line_number;
+  wchar_t *wbuf;
+  size_t wbufsize;
+  iconv_t cd_towc = (iconv_t) -1;
+  iconv_t cd_tomb = (iconv_t) -1;
+  wchar_t escape_char = L'\\';
+  char *codeset = NULL;
 
   if (strcmp (fname, "-") == 0 || strcmp (fname, "/dev/stdin") == 0)
     {
@@ -288,6 +304,10 @@ read_input_file (struct catalog *current, const char *fname)
   buf = NULL;
   len = 0;
   line_number = 0;
+
+  wbufsize = 1024;
+  wbuf = (wchar_t *) xmalloc (wbufsize);
+
   while (!feof (fp))
     {
       int continued;
@@ -305,15 +325,29 @@ read_input_file (struct catalog *current, const char *fname)
          ++line_number;
 
          /* It the line continued?  */
+         continued = 0;
          if (buf[act_len - 1] == '\n')
            {
              --act_len;
-             continued = buf[act_len - 1] == '\\';
-             if (continued)
-               --act_len;
+
+             /* There might be more than one backslash at the end of
+                the line.  Only if there is an odd number of them is
+                the line continued.  */
+             if (act_len > 0 && buf[act_len - 1] == '\\')
+               {
+                 int temp_act_len = act_len;
+
+                 do
+                   {
+                     --temp_act_len;
+                     continued = !continued;
+                   }
+                 while (temp_act_len > 0 && buf[temp_act_len - 1] == '\\');
+
+                 if (continued)
+                   --act_len;
+               }
            }
-         else
-           continued = 0;
 
          /* Append to currently selected line.  */
          obstack_grow (&current->mem_pool, buf, act_len);
@@ -326,8 +360,30 @@ read_input_file (struct catalog *current, const char *fname)
       used = 0;
       if (this_line[0] == '$')
        {
-         if (isspace (this_line[1]))
-           /* This is a comment line.  Do nothing.  */;
+         if (isblank (this_line[1]))
+           {
+             int cnt = 1;
+             while (isblank (this_line[cnt]))
+               ++cnt;
+             if (strncmp (&this_line[cnt], "codeset=", 8) != 0)
+               /* This is a comment line. Do nothing.  */;
+             else if (codeset != NULL)
+               /* Ignore multiple codeset. */;
+             else
+               {
+                 int start = cnt + 8;
+                 cnt = start;
+                 while (this_line[cnt] != '\0' && !isspace (this_line[cnt]))
+                   ++cnt;
+                 if (cnt != start)
+                   {
+                     int len = cnt - start;
+                     codeset = xmalloc (len + 1);
+                     *((char *) mempcpy (codeset, &this_line[start], len))
+                       = '\0';
+                   }
+               }
+           }
          else if (strncmp (&this_line[1], "set", 3) == 0)
            {
              int cnt = sizeof ("set");
@@ -469,12 +525,45 @@ this is the first definition"));
            }
          else if (strncmp (&this_line[1], "quote", 5) == 0)
            {
-             int cnt = sizeof ("quote");
+             char buf[2];
+             char *bufptr;
+             size_t buflen;
+             char *wbufptr;
+             size_t wbuflen;
+             int cnt;
+
+             cnt = sizeof ("quote");
              while (isspace (this_line[cnt]))
                ++cnt;
+
+             /* We need the conversion.  */
+             if (cd_towc == (iconv_t) -1
+                 && open_conversion (codeset, &cd_towc, &cd_tomb,
+                                     &escape_char) != 0)
+               /* Something is wrong.  */
+               goto out;
+
              /* Yes, the quote char can be '\0'; this means no quote
-                char.  */
-             current->quote_char = this_line[cnt];
+                char.  The function using the information works on
+                wide characters so we have to convert it here.  */
+             buf[0] = this_line[cnt];
+             buf[1] = '\0';
+             bufptr = buf;
+             buflen = 2;
+
+             wbufptr = (char *) wbuf;
+             wbuflen = wbufsize;
+
+             /* Flush the state.  */
+             iconv (cd_towc, NULL, NULL, NULL, NULL);
+
+             iconv (cd_towc, &bufptr, &buflen, &wbufptr, &wbuflen);
+             if (buflen != 0 || (wchar_t *) wbufptr != &wbuf[2])
+               error_at_line (0, 0, fname, start_line,
+                              gettext ("invalid quote character"));
+             else
+               /* Use the converted wide character.  */
+               current->quote_char = wbuf[0];
            }
          else
            {
@@ -491,40 +580,73 @@ this is the first definition"));
       else if (isalnum (this_line[0]) || this_line[0] == '_')
        {
          const char *ident = this_line;
+         char *line = this_line;
          int message_number;
 
          do
-           ++this_line;
-         while (this_line[0] != '\0' && !isspace (this_line[0]));;
-         this_line[0] = '\0';  /* Terminate the identifier.  */
+           ++line;
+         while (line[0] != '\0' && !isspace (line[0]));
+         if (line[0] != '\0')
+           *line++ = '\0';     /* Terminate the identifier.  */
 
-         do
-           ++this_line;
-         while (isspace (this_line[0]));
          /* Now we found the beginning of the message itself.  */
 
          if (isdigit (ident[0]))
            {
              struct message_list *runp;
+             struct message_list *lastp;
 
              message_number = atoi (ident);
 
              /* Find location to insert the new message.  */
              runp = current->current_set->messages;
+             lastp = NULL;
              while (runp != NULL)
                if (runp->number == message_number)
                  break;
                else
-                 runp = runp->next;
+                 {
+                   lastp = runp;
+                   runp = runp->next;
+                 }
              if (runp != NULL)
                {
                  /* Oh, oh.  There is already a message with this
-                    number is the message set.  */
-                 error_at_line (0, 0, fname, start_line,
-                                gettext ("duplicated message number"));
-                 error_at_line (0, 0, runp->fname, runp->line,
-                                gettext ("this is the first definition"));
-                 message_number = 0;
+                    number in the message set.  */
+                 if (runp->symbol == NULL)
+                   {
+                     /* The existing message had its number specified
+                        by the user.  Fatal collision type uh, oh.  */
+                     error_at_line (0, 0, fname, start_line,
+                                    gettext ("duplicated message number"));
+                     error_at_line (0, 0, runp->fname, runp->line,
+                                    gettext ("this is the first definition"));
+                     message_number = 0;
+                   }
+                 else
+                   {
+                     /* Collision was with number auto-assigned to a
+                        symbolic.  Change existing symbolic number
+                        and move to end the list (if not already there).  */
+                     runp->number = ++current->current_set->last_message;
+
+                     if (runp->next != NULL)
+                       {
+                         struct message_list *endp;
+
+                         if (lastp == NULL)
+                           current->current_set->messages=runp->next;
+                         else
+                           lastp->next=runp->next;
+
+                         endp = runp->next;
+                         while (endp->next != NULL)
+                           endp = endp->next;
+
+                         endp->next = runp;
+                         runp->next = NULL;
+                       }
+                   }
                }
              ident = NULL;     /* We don't have a symbol.  */
 
@@ -535,10 +657,12 @@ this is the first definition"));
          else if (ident[0] != '\0')
            {
              struct message_list *runp;
-             runp = current->current_set->messages;
+             struct message_list *lastp;
 
              /* Test whether the symbolic name was not used for
                 another message in this message set.  */
+             runp = current->current_set->messages;
+             lastp = NULL;
              while (runp != NULL)
                if (runp->symbol != NULL && strcmp (ident, runp->symbol) == 0)
                  break;
@@ -547,8 +671,8 @@ this is the first definition"));
              if (runp != NULL)
                {
                  /* The name is already used.  */
-                 error_at_line (0, 0, fname, start_line,
-                                gettext ("duplicated message identifier"));
+                 error_at_line (0, 0, fname, start_line, gettext ("\
+duplicated message identifier"));
                  error_at_line (0, 0, runp->fname, runp->line,
                                 gettext ("this is the first definition"));
                  message_number = 0;
@@ -562,20 +686,105 @@ this is the first definition"));
 
          if (message_number != 0)
            {
+             char *inbuf;
+             size_t inlen;
+             char *outbuf;
+             size_t outlen;
              struct message_list *newp;
+             size_t line_len = strlen (line) + 1;
+             size_t ident_len = 0;
+
+             /* We need the conversion.  */
+             if (cd_towc == (iconv_t) -1
+                 && open_conversion (codeset, &cd_towc, &cd_tomb,
+                                     &escape_char) != 0)
+               /* Something is wrong.  */
+               goto out;
+
+             /* Convert to a wide character string.  We have to
+                interpret escape sequences which will be impossible
+                without doing the conversion if the codeset of the
+                message is stateful.  */
+             while (1)
+               {
+                 inbuf = line;
+                 inlen = line_len;
+                 outbuf = (char *) wbuf;
+                 outlen = wbufsize;
 
-             used = 1; /* Yes, we use the line.  */
+                 /* Flush the state.  */
+                 iconv (cd_towc, NULL, NULL, NULL, NULL);
+
+                 iconv (cd_towc, &inbuf, &inlen, &outbuf, &outlen);
+                 if (inlen == 0)
+                   {
+                     /* The string is converted.  */
+                     assert (outlen < wbufsize);
+                     assert (wbuf[(wbufsize - outlen) / sizeof (wchar_t) - 1]
+                             == L'\0');
+                     break;
+                   }
+
+                 if (outlen != 0)
+                   {
+                     /* Something is wrong with this string, we ignore it.  */
+                     error_at_line (0, 0, fname, start_line, gettext ("\
+invalid character: message ignored"));
+                     goto ignore;
+                   }
+
+                 /* The output buffer is too small.  */
+                 wbufsize *= 2;
+                 wbuf = (wchar_t *) xrealloc (wbuf, wbufsize);
+               }
 
              /* Strip quote characters, change escape sequences into
                 correct characters etc.  */
-             normalize_line (fname, start_line, this_line,
-                             current->quote_char);
+             normalize_line (fname, start_line, cd_towc, wbuf,
+                             current->quote_char, escape_char);
+
+             if (ident)
+               ident_len = line - this_line;
+
+             /* Now the string is free of escape sequences.  Convert it
+                back into a multibyte character string.  First free the
+                memory allocated for the original string.  */
+             obstack_free (&current->mem_pool, this_line);
+
+             used = 1; /* Yes, we use the line.  */
+
+             /* Now fill in the new string.  It should never happen that
+                the replaced string is longer than the original.  */
+             inbuf = (char *) wbuf;
+             inlen = (wcslen (wbuf) + 1) * sizeof (wchar_t);
+
+             outlen = obstack_room (&current->mem_pool);
+             obstack_blank (&current->mem_pool, outlen);
+             this_line = (char *) obstack_base (&current->mem_pool);
+             outbuf = this_line + ident_len;
+             outlen -= ident_len;
+
+             /* Flush the state.  */
+             iconv (cd_tomb, NULL, NULL, NULL, NULL);
+
+             iconv (cd_tomb, &inbuf, &inlen, &outbuf, &outlen);
+             if (inlen != 0)
+               {
+                 error_at_line (0, 0, fname, start_line,
+                                gettext ("invalid line"));
+                 goto ignore;
+               }
+             assert (outbuf[-1] == '\0');
+
+             /* Free the memory in the obstack we don't use.  */
+             obstack_blank (&current->mem_pool, -(int) outlen);
+             line = obstack_finish (&current->mem_pool);
 
              newp = (struct message_list *) xmalloc (sizeof (*newp));
              newp->number = message_number;
-             newp->message = this_line;
+             newp->message = line + ident_len;
              /* Remember symbolic name; is NULL if no is given.  */
-             newp->symbol = ident;
+             newp->symbol = ident ? line : NULL;
              /* Remember where we found the character.  */
              newp->fname = fname;
              newp->line = start_line;
@@ -619,11 +828,20 @@ this is the first definition"));
                           gettext ("malformed line ignored"));
        }
 
+    ignore:
       /* We can save the memory for the line if it was not used.  */
       if (!used)
        obstack_free (&current->mem_pool, this_line);
     }
 
+  /* Close the conversion modules.  */
+  iconv_close (cd_towc);
+  iconv_close (cd_tomb);
+  free (codeset);
+
+ out:
+  free (wbuf);
+
   if (fp != stdin)
     fclose (fp);
   return current;
@@ -642,7 +860,7 @@ write_out (struct catalog *catalog, const char *output_name,
   struct obstack string_pool;
   const char *strings;
   size_t strings_size;
-  u_int32_t *array1, *array2;
+  uint32_t *array1, *array2;
   size_t cnt;
   int fd;
 
@@ -720,10 +938,10 @@ write_out (struct catalog *catalog, const char *output_name,
 
   /* Allocate room for all needed arrays.  */
   array1 =
-    (u_int32_t *) alloca (best_size * best_depth * sizeof (u_int32_t) * 3);
-  memset (array1, '\0', best_size * best_depth * sizeof (u_int32_t) * 3);
+    (uint32_t *) alloca (best_size * best_depth * sizeof (uint32_t) * 3);
+  memset (array1, '\0', best_size * best_depth * sizeof (uint32_t) * 3);
   array2
-    = (u_int32_t *) alloca (best_size * best_depth * sizeof (u_int32_t) * 3);
+    = (uint32_t *) alloca (best_size * best_depth * sizeof (uint32_t) * 3);
   obstack_init (&string_pool);
 
   set_run = catalog->all_sets;
@@ -781,11 +999,11 @@ write_out (struct catalog *catalog, const char *output_name,
   /* We always write out the little endian version of the index
      arrays.  */
 #if __BYTE_ORDER == __LITTLE_ENDIAN
-  write (fd, array1, best_size * best_depth * sizeof (u_int32_t) * 3);
-  write (fd, array2, best_size * best_depth * sizeof (u_int32_t) * 3);
+  write (fd, array1, best_size * best_depth * sizeof (uint32_t) * 3);
+  write (fd, array2, best_size * best_depth * sizeof (uint32_t) * 3);
 #elif __BYTE_ORDER == __BIG_ENDIAN
-  write (fd, array2, best_size * best_depth * sizeof (u_int32_t) * 3);
-  write (fd, array1, best_size * best_depth * sizeof (u_int32_t) * 3);
+  write (fd, array2, best_size * best_depth * sizeof (uint32_t) * 3);
+  write (fd, array1, best_size * best_depth * sizeof (uint32_t) * 3);
 #else
 # error Cannot handle __BYTE_ORDER byte order
 #endif
@@ -889,13 +1107,14 @@ find_set (struct catalog *current, int number)
 /* Normalize given string *in*place* by processing escape sequences
    and quote characters.  */
 static void
-normalize_line (const char *fname, size_t line, char *string, char quote_char)
+normalize_line (const char *fname, size_t line, iconv_t cd, wchar_t *string,
+               wchar_t quote_char, wchar_t escape_char)
 {
   int is_quoted;
-  char *rp = string;
-  char *wp = string;
+  wchar_t *rp = string;
+  wchar_t *wp = string;
 
-  if (quote_char != '\0' && *rp == quote_char)
+  if (quote_char != L'\0' && *rp == quote_char)
     {
       is_quoted = 1;
       ++rp;
@@ -903,62 +1122,89 @@ normalize_line (const char *fname, size_t line, char *string, char quote_char)
   else
     is_quoted = 0;
 
-  while (*rp != '\0')
+  while (*rp != L'\0')
     if (*rp == quote_char)
       /* We simply end the string when we find the first time an
         not-escaped quote character.  */
        break;
-    else if (*rp == '\\')
+    else if (*rp == escape_char)
       {
        ++rp;
-       if (quote_char != '\0' && *rp == quote_char)
+       if (quote_char != L'\0' && *rp == quote_char)
          /* This is an extension to XPG.  */
          *wp++ = *rp++;
        else
          /* Recognize escape sequences.  */
          switch (*rp)
            {
-           case 'n':
-             *wp++ = '\n';
+           case L'n':
+             *wp++ = L'\n';
              ++rp;
              break;
-           case 't':
-             *wp++ = '\t';
+           case L't':
+             *wp++ = L'\t';
              ++rp;
              break;
-           case 'v':
-             *wp++ = '\v';
+           case L'v':
+             *wp++ = L'\v';
              ++rp;
              break;
-           case 'b':
-             *wp++ = '\b';
+           case L'b':
+             *wp++ = L'\b';
              ++rp;
              break;
-           case 'r':
-             *wp++ = '\r';
+           case L'r':
+             *wp++ = L'\r';
              ++rp;
              break;
-           case 'f':
-             *wp++ = '\f';
+           case L'f':
+             *wp++ = L'\f';
              ++rp;
              break;
-           case '\\':
-             *wp++ = '\\';
-             ++rp;
-             break;
-           case '0' ... '7':
+           case L'0' ... L'7':
              {
-               int number = *rp++ - '0';
-               while (number <= (255 / 8) && *rp >= '0' && *rp <= '7')
+               int number;
+               char cbuf[2];
+               char *cbufptr;
+               size_t cbufin;
+               wchar_t wcbuf[2];
+               char *wcbufptr;
+               size_t wcbufin;
+
+               number = *rp++ - L'0';
+               while (number <= (255 / 8) && *rp >= L'0' && *rp <= L'7')
                  {
                    number *= 8;
-                   number += *rp++ - '0';
+                   number += *rp++ - L'0';
                  }
-               *wp++ = (char) number;
+
+               cbuf[0] = (char) number;
+               cbuf[1] = '\0';
+               cbufptr = cbuf;
+               cbufin = 2;
+
+               wcbufptr = (char *) wcbuf;
+               wcbufin = sizeof (wcbuf);
+
+               /* Flush the state.  */
+               iconv (cd, NULL, NULL, NULL, NULL);
+
+               iconv (cd, &cbufptr, &cbufin, &wcbufptr, &wcbufin);
+               if (cbufptr != &cbuf[2] || (wchar_t *) wcbufptr != &wcbuf[2])
+                 error_at_line (0, 0, fname, line,
+                                gettext ("invalid escape sequence"));
+               else
+                 *wp++ = wcbuf[0];
              }
              break;
            default:
-             /* Simply ignore the backslash character.  */
+             if (*rp == escape_char)
+               {
+                 *wp++ = escape_char;
+                 ++rp;
+               }
+             else
+               /* Simply ignore the backslash character.  */;
              break;
            }
       }
@@ -968,10 +1214,10 @@ normalize_line (const char *fname, size_t line, char *string, char quote_char)
   /* If we saw a quote character at the beginning we expect another
      one at the end.  */
   if (is_quoted && *rp != quote_char)
-    error (0, 0, fname, line, gettext ("unterminated message"));
+    error_at_line (0, 0, fname, line, gettext ("unterminated message"));
 
   /* Terminate string.  */
-  *wp = '\0';
+  *wp = L'\0';
   return;
 }
 
@@ -984,26 +1230,21 @@ read_old (struct catalog *catalog, const char *file_name)
   int last_set = -1;
   size_t cnt;
 
-  old_cat_obj.status = closed;
-  old_cat_obj.cat_name = file_name;
-  old_cat_obj.nlspath = NULL;
-  __libc_lock_init (old_cat_obj.lock);
-
   /* Try to open catalog, but don't look through the NLSPATH.  */
-  __open_catalog (&old_cat_obj);
-
-  if (old_cat_obj.status != mmapped && old_cat_obj.status != malloced)
+  if (__open_catalog (file_name, NULL, NULL, &old_cat_obj) != 0)
     {
       if (errno == ENOENT)
        /* No problem, the catalog simply does not exist.  */
        return;
       else
-       error (EXIT_FAILURE, errno, gettext ("while opening old catalog file"));
+       error (EXIT_FAILURE, errno,
+              gettext ("while opening old catalog file"));
     }
 
   /* OK, we have the catalog loaded.  Now read all messages and merge
      them.  When set and message number clash for any message the new
-     one is used.  */
+     one is used.  If the new one is empty it indicates that the
+     message should be deleted.  */
   for (cnt = 0; cnt < old_cat_obj.plane_size * old_cat_obj.plane_depth; ++cnt)
     {
       struct message_list *message, *last;
@@ -1012,7 +1253,7 @@ read_old (struct catalog *catalog, const char *file_name)
        /* No message in this slot.  */
        continue;
 
-      if (old_cat_obj.name_ptr[cnt * 3 + 0] - 1 != (u_int32_t) last_set)
+      if (old_cat_obj.name_ptr[cnt * 3 + 0] - 1 != (uint32_t) last_set)
        {
          last_set = old_cat_obj.name_ptr[cnt * 3 + 0] - 1;
          set = find_set (catalog, old_cat_obj.name_ptr[cnt * 3 + 0] - 1);
@@ -1022,14 +1263,14 @@ read_old (struct catalog *catalog, const char *file_name)
       message = set->messages;
       while (message != NULL)
        {
-         if ((u_int32_t) message->number >= old_cat_obj.name_ptr[cnt * 3 + 1])
+         if ((uint32_t) message->number >= old_cat_obj.name_ptr[cnt * 3 + 1])
            break;
          last = message;
          message = message->next;
        }
 
       if (message == NULL
-         || (u_int32_t) message->number > old_cat_obj.name_ptr[cnt * 3 + 1])
+         || (uint32_t) message->number > old_cat_obj.name_ptr[cnt * 3 + 1])
        {
          /* We have found a message which is not yet in the catalog.
             Insert it at the right position.  */
@@ -1051,5 +1292,74 @@ read_old (struct catalog *catalog, const char *file_name)
 
          ++catalog->total_messages;
        }
+      else if (*message->message == '\0')
+       {
+         /* The new empty message has overridden the old one thus
+            "deleting" it as required.  Now remove the empty remains. */
+         if (last == NULL)
+           set->messages = message->next;
+         else
+           last->next = message->next;
+       }
+    }
+}
+
+
+static int
+open_conversion (const char *codeset, iconv_t *cd_towcp, iconv_t *cd_tombp,
+                wchar_t *escape_charp)
+{
+  char buf[2];
+  char *bufptr;
+  size_t bufsize;
+  wchar_t wbuf[2];
+  char *wbufptr;
+  size_t wbufsize;
+
+  /* If the input file does not specify the codeset use the locale's.  */
+  if (codeset == NULL)
+    {
+      setlocale (LC_ALL, "");
+      codeset = nl_langinfo (CODESET);
+      setlocale (LC_ALL, "C");
+    }
+
+  /* Get the conversion modules.  */
+  *cd_towcp = iconv_open ("WCHAR_T", codeset);
+  *cd_tombp = iconv_open (codeset, "WCHAR_T");
+  if (*cd_towcp == (iconv_t) -1 || *cd_tombp == (iconv_t) -1)
+    {
+      error (0, 0, gettext ("conversion modules not available"));
+      if (*cd_towcp != (iconv_t) -1)
+       iconv_close (*cd_towcp);
+
+      return 1;
     }
+
+  /* One special case for historical reasons is the backslash
+     character.  In some codesets the byte value 0x5c is not mapped to
+     U005c in Unicode.  These charsets then don't have a backslash
+     character at all.  Therefore we have to live with whatever the
+     codeset provides and recognize, instead of the U005c, the character
+     the byte value 0x5c is mapped to.  */
+  buf[0] = '\\';
+  buf[1] = '\0';
+  bufptr = buf;
+  bufsize = 2;
+
+  wbufptr = (char *) wbuf;
+  wbufsize = sizeof (wbuf);
+
+  iconv (*cd_towcp, &bufptr, &bufsize, &wbufptr, &wbufsize);
+  if (bufsize != 0 || wbufsize != 0)
+    {
+      /* Something went wrong, we couldn't convert the byte 0x5c.  Go
+        on with using U005c.  */
+      error (0, 0, gettext ("cannot determine escape character"));
+      *escape_charp = L'\\';
+    }
+  else
+    *escape_charp = wbuf[0];
+
+  return 0;
 }