]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
New program 'msginit'.
authorBruno Haible <bruno@clisp.org>
Thu, 25 Oct 2001 09:38:30 +0000 (09:38 +0000)
committerBruno Haible <bruno@clisp.org>
Thu, 25 Oct 2001 09:38:30 +0000 (09:38 +0000)
man/ChangeLog
man/Makefile.am
man/msginit.x [new file with mode: 0644]
src/ChangeLog
src/Makefile.am
src/msginit.c [new file with mode: 0644]
src/project-id [new file with mode: 0755]

index e9b1e81d3f18b6e978a9229dce5b46234c491bdd..113e5698ea0e3cc957ee790a52c99e2182c47e1f 100644 (file)
@@ -1,3 +1,12 @@
+2001-10-08  Bruno Haible  <haible@clisp.cons.org>
+
+       * msginit.x: New file.
+       * Makefile.am (man_aux): Add msginit.x.
+       (man_MAN1OTHER): Add msginit.1.
+       (man_HTMLOTHER): Add msginit.1.html.
+       (msginit.1): New rule.
+       (msginit.1.html): New rule.
+
 2001-09-08  Bruno Haible  <haible@clisp.cons.org>
 
        * msgattrib.x: New file.
index 0d66e87e5ee746c75b9d128ea674a4f2356d9098..8de6601a1644b4a59836893d758d90db54d20fb9 100644 (file)
@@ -30,7 +30,7 @@ AUTOMAKE_OPTIONS = 1.2 gnits
 
 man_aux  = gettext.x ngettext.x \
 msgcmp.x msgfmt.x msgmerge.x msgunfmt.x xgettext.x \
-msgattrib.x msgcat.x msgcomm.x msgconv.x msgen.x msgexec.x msggrep.x msguniq.x
+msgattrib.x msgcat.x msgcomm.x msgconv.x msgen.x msgexec.x msggrep.x msginit.x msguniq.x
 
 # Likewise, plus additional manual pages for the libintl functions.
 
@@ -38,7 +38,7 @@ man_MAN1GEN = gettext.1 ngettext.1
 man_MAN1IN = gettext.1.in ngettext.1.in
 man_MAN1OTHER = \
 msgcmp.1 msgfmt.1 msgmerge.1 msgunfmt.1 xgettext.1 \
-msgattrib.1 msgcat.1 msgcomm.1 msgconv.1 msgen.1 msgexec.1 msggrep.1 msguniq.1
+msgattrib.1 msgcat.1 msgcomm.1 msgconv.1 msgen.1 msgexec.1 msggrep.1 msginit.1 msguniq.1
 man_MAN1 = $(man_MAN1GEN) $(man_MAN1OTHER)
 man_MAN3 = gettext.3 ngettext.3 \
 textdomain.3 bindtextdomain.3 bind_textdomain_codeset.3
@@ -51,7 +51,7 @@ man_HTMLGEN = gettext.1.html ngettext.1.html
 man_HTMLIN = gettext.1.html.in ngettext.1.html.in
 man_HTMLOTHER = \
 msgcmp.1.html msgfmt.1.html msgmerge.1.html msgunfmt.1.html xgettext.1.html \
-msgattrib.1.html msgcat.1.html msgcomm.1.html msgconv.1.html msgen.1.html msgexec.1.html msggrep.1.html msguniq.1.html \
+msgattrib.1.html msgcat.1.html msgcomm.1.html msgconv.1.html msgen.1.html msgexec.1.html msggrep.1.html msginit.1.html msguniq.1.html \
 gettext.3.html ngettext.3.html \
 textdomain.3.html bindtextdomain.3.html bind_textdomain_codeset.3.html
 man_HTML = $(man_HTMLGEN) $(man_HTMLOTHER)
@@ -122,6 +122,8 @@ msgexec.1: msgexec.x
        $(SHELL) x-to-1 "$(PERL)" "$(HELP2MAN)" ../src/msgexec$(EXEEXT) $(srcdir)/msgexec.x msgexec.1
 msggrep.1: msggrep.x
        $(SHELL) x-to-1 "$(PERL)" "$(HELP2MAN)" ../src/msggrep$(EXEEXT) $(srcdir)/msggrep.x msggrep.1
+msginit.1: msginit.x
+       $(SHELL) x-to-1 "$(PERL)" "$(HELP2MAN)" ../src/msginit$(EXEEXT) $(srcdir)/msginit.x msginit.1
 msguniq.1: msguniq.x
        $(SHELL) x-to-1 "$(PERL)" "$(HELP2MAN)" ../src/msguniq$(EXEEXT) $(srcdir)/msguniq.x msguniq.1
 
@@ -199,6 +201,9 @@ msgexec.1.html: msgexec.1
 msggrep.1.html: msggrep.1
        $(MAN2HTML) `if test -f msggrep.1; then echo .; else echo $(srcdir); fi`/msggrep.1 | sed -e '/CreationDate:/d' > t-$@
        mv t-$@ $@
+msginit.1.html: msginit.1
+       $(MAN2HTML) `if test -f msginit.1; then echo .; else echo $(srcdir); fi`/msginit.1 | sed -e '/CreationDate:/d' > t-$@
+       mv t-$@ $@
 msguniq.1.html: msguniq.1
        $(MAN2HTML) `if test -f msguniq.1; then echo .; else echo $(srcdir); fi`/msguniq.1 | sed -e '/CreationDate:/d' > t-$@
        mv t-$@ $@
diff --git a/man/msginit.x b/man/msginit.x
new file mode 100644 (file)
index 0000000..126791f
--- /dev/null
@@ -0,0 +1,4 @@
+[NAME]
+msginit \- initialize a message catalog
+[DESCRIPTION]
+.\" Add any additional description here
index 3b6f05a2ba23801a4bc3b7af9720b3ab6e5037ea..b174aabf714eac040eb5b12ebf9935525c7e770c 100644 (file)
@@ -1,3 +1,15 @@
+2001-10-21  Bruno Haible  <haible@clisp.cons.org>
+
+       * msginit.c: New file.
+       * project-id: New file.
+       * Makefile.am (bin_PROGRAMS): Add msginit.
+       (projectsdir): New variable.
+       (DEFS): Add -DLIBDIR and -DPROJECTSDIR.
+       (msginit_SOURCES): New variable.
+       (msginit_LDADD): New variable.
+       (install-exec-local): Also install project-id.
+       (uninstall-local): Also uninstall project-id.
+
 2001-10-21  Bruno Haible  <haible@clisp.cons.org>
 
        * gnu/gettext/GetURL.java: New file.
@@ -51,7 +63,7 @@
        * read-java.c (execute_and_read_po_output): Update for changed
        create_pipe_in(), wait_subprocess().
 
-2001-01-08  Bruno Haible  <haible@clisp.cons.org>
+2001-10-08  Bruno Haible  <haible@clisp.cons.org>
 
        * xgettext.c (remember_a_message): When the comment tag is seen,
        remember all remaining comment lines, not just one.
index ba1930a8c6893f3514cff504f41e925997516c84..ce0cf5b5545bf087e32ec9b75cc8c5b4c558cc5f 100644 (file)
@@ -21,7 +21,7 @@ AUTOMAKE_OPTIONS = 1.5 gnits no-dependencies
 
 bin_PROGRAMS = gettext ngettext \
 msgcmp msgfmt msgmerge msgunfmt xgettext \
-msgattrib msgcat msgcomm msgconv msgen msgexec msggrep msguniq
+msgattrib msgcat msgcomm msgconv msgen msgexec msggrep msginit msguniq
 
 noinst_PROGRAMS = hostname urlget
 
@@ -35,11 +35,12 @@ EXTRA_DIST = FILES
 
 localedir = $(datadir)/locale
 jardir = $(datadir)/gettext
+projectsdir = $(pkgdatadir)/projects
 
 INCLUDES = -I. -I$(srcdir) -I.. -I$(top_srcdir)/lib -I../intl \
 -I$(top_srcdir)/intl
 DEFS = -DLOCALEDIR=\"$(localedir)\" -DGETTEXTJAR=\"$(jardir)/gettext.jar\" \
-@DEFS@
+-DLIBDIR=\"$(libdir)\" -DPROJECTSDIR=\"$(projectsdir)\" @DEFS@
 LDADD = ../lib/libnlsut.a @INTLLIBS@
 
 SED = sed
@@ -79,6 +80,7 @@ msgconv_SOURCES   = msgconv.c   $(COMMON_SOURCE) msgl-ascii.c write-po.c read-po
 msgen_SOURCES     = msgen.c     $(COMMON_SOURCE) msgl-ascii.c write-po.c read-po.c msgl-english.c
 msgexec_SOURCES   = msgexec.c   $(COMMON_SOURCE) msgl-ascii.c write-po.c read-po.c msgl-charset.c
 msggrep_SOURCES   = msggrep.c   $(COMMON_SOURCE) msgl-ascii.c write-po.c read-po.c msgl-charset.c
+msginit_SOURCES   = msginit.c   $(COMMON_SOURCE) msgl-ascii.c write-po.c read-po.c msgl-english.c po-time.c
 msguniq_SOURCES   = msguniq.c   $(COMMON_SOURCE) msgl-ascii.c write-po.c read-po.c msgl-iconv.c msgl-cat.c
 hostname_SOURCES  = hostname.c
 urlget_SOURCES    = urlget.c
@@ -98,6 +100,8 @@ msgconv_LDADD = ../lib/libnlsut.a @INTLLIBS@ @LIBICONV@
 msgen_LDADD = ../lib/libnlsut.a @INTLLIBS@ @LIBICONV@
 msgexec_LDADD = ../lib/libnlsut.a @INTLLIBS@ @LIBICONV@
 msggrep_LDADD = ../lib/libnlsut.a @INTLLIBS@ @LIBICONV@
+msginit_LDADD = ../intl/localealias.$(OBJEXT) ../intl/localename.$(OBJEXT) \
+                ../lib/libnlsut.a @INTLLIBS@ @LIBICONV@
 msguniq_LDADD = ../lib/libnlsut.a @INTLLIBS@ @LIBICONV@
 
 
@@ -124,6 +128,7 @@ install-exec-local:
        $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(INSTALL_PROGRAM) hostname$(EXEEXT) $(DESTDIR)$(libdir)/$(PACKAGE)/hostname$(EXEEXT)
        $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(INSTALL_PROGRAM) urlget$(EXEEXT) $(DESTDIR)$(libdir)/$(PACKAGE)/urlget$(EXEEXT)
        $(INSTALL_SCRIPT) user-email $(DESTDIR)$(libdir)/$(PACKAGE)/user-email
+       $(INSTALL_SCRIPT) $(srcdir)/project-id $(DESTDIR)$(libdir)/$(PACKAGE)/project-id
 
 installdirs-local:
        $(mkinstalldirs) $(DESTDIR)$(libdir)/$(PACKAGE)
@@ -132,6 +137,7 @@ uninstall-local:
        $(RM) $(DESTDIR)$(libdir)/$(PACKAGE)/hostname$(EXEEXT)
        $(RM) $(DESTDIR)$(libdir)/$(PACKAGE)/urlget$(EXEEXT)
        $(RM) $(DESTDIR)$(libdir)/$(PACKAGE)/user-email
+       $(RM) $(DESTDIR)$(libdir)/$(PACKAGE)/project-id
 
 
 # Special rules for Java compilation.
diff --git a/src/msginit.c b/src/msginit.c
new file mode 100644 (file)
index 0000000..3296a6f
--- /dev/null
@@ -0,0 +1,1563 @@
+/* Initializes a new PO file.
+   Copyright (C) 2001 Free Software Foundation, Inc.
+   Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+   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 <errno.h>
+#include <getopt.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <pwd.h>
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#if HAVE_DIRENT_H
+# include <dirent.h>
+#else
+# define dirent direct
+# if HAVE_SYS_NDIR_H
+#  include <sys/ndir.h>
+# endif
+# if HAVE_SYS_DIR_H
+#  include <sys/dir.h>
+# endif
+# if HAVE_NDIR_H
+#  include <ndir.h>
+# endif
+#endif
+
+#if CLOSEDIR_VOID
+/* Fake a return value. */
+# define CLOSEDIR(d) (closedir (d), 0)
+#else
+# define CLOSEDIR(d) closedir (d)
+#endif
+
+#if HAVE_DIRENT_H || HAVE_NDIR_H || HAVE_SYS_DIR_H || HAVE_SYS_NDIR_H
+# define HAVE_DIR 1
+#else
+# define HAVE_DIR 0
+#endif
+
+#include "error.h"
+#include "progname.h"
+#include "basename.h"
+#include "strpbrk.h"
+#include "message.h"
+#include "read-po.h"
+#include "write-po.h"
+#include "po-charset.h"
+#include "po-time.h"
+#include "xmalloc.h"
+#include "system.h"
+#include "xerror.h"
+#include "msgl-english.h"
+#include "pipe.h"
+#include "wait-process.h"
+#include "getline.h"
+#include "setenv.h"
+#include "str-list.h"
+#include "libgettext.h"
+
+#define _(str) gettext (str)
+#define N_(str) (str)
+
+#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
+
+extern const char * locale_charset PARAMS ((void));
+extern const char * _nl_locale_name PARAMS ((int category,
+                                            const char *categoryname));
+extern const char * _nl_expand_alias PARAMS ((const char *name));
+
+/* Locale name.  */
+static const char *locale;
+
+/* Language (ISO-639 code) and optional territory (ISO-3166 code).  */
+static const char *catalogname;
+
+/* Language (ISO-639 code).  */
+static const char *language;
+
+/* Verbosity control.  */
+static bool verbose;
+
+/* Long options.  */
+static const struct option long_options[] =
+{
+  { "help", no_argument, NULL, 'h' },
+  { "input", required_argument, NULL, 'i' },
+  { "locale", required_argument, NULL, 'l' },
+  { "output-file", required_argument, NULL, 'o' },
+  { "verbose", no_argument, NULL, 'v' },
+  { "version", no_argument, NULL, 'V' },
+  { "width", required_argument, NULL, 'w' },
+  { NULL, 0, NULL, 0 }
+};
+
+/* Prototypes for local functions.  Needed to ensure compiler checking of
+   function argument counts despite of K&R C function definition syntax.  */
+static void usage PARAMS ((int status))
+#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
+     __attribute__ ((noreturn))
+#endif 
+;  
+static const char *find_pot PARAMS ((void));
+static const char *catalogname_for_locale PARAMS ((const char *locale));
+static const char *language_of_locale PARAMS ((const char *locale));
+static const char *canonical_locale_charset PARAMS ((void));
+static const char *englishname_of_language PARAMS ((void));
+static const char *project_id PARAMS ((void));
+static const char *project_id_version PARAMS ((void));
+static const char *po_revision_date PARAMS ((void));
+static struct passwd *get_user_pwd PARAMS ((void));
+static const char *get_user_fullname PARAMS ((void));
+static const char *get_user_email PARAMS ((void));
+static const char *last_translator PARAMS ((void));
+static const char *language_team_address PARAMS ((void));
+static const char *language_team PARAMS ((void));
+static const char *mime_version PARAMS ((void));
+static const char *content_type PARAMS ((void));
+static const char *content_transfer_encoding PARAMS ((void));
+static const char *plural_forms PARAMS ((void));
+#ifdef unused
+static char *get_field PARAMS ((const char *header, const char *field));
+#endif
+static char *put_field PARAMS ((const char *old_header, const char *field,
+                               const char *value));
+static const char *get_title PARAMS ((void));
+static const char *subst_string PARAMS ((const char *str,
+                                        unsigned int nsubst,
+                                        const char *(*subst)[2]));
+static void subst_string_list PARAMS ((string_list_ty *slp,
+                                      unsigned int nsubst,
+                                      const char *(*subst)[2]));
+static msgdomain_list_ty *fill_header PARAMS ((msgdomain_list_ty *mdlp));
+
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  int opt;
+  bool do_help;
+  bool do_version;
+  char *output_file;
+  const char *input_file;
+  msgdomain_list_ty *result;
+
+  /* Set program name for messages.  */
+  set_program_name (argv[0]);
+  error_print_progname = maybe_print_progname;
+
+#ifdef HAVE_SETLOCALE
+  /* Set locale via LC_ALL.  */
+  setlocale (LC_ALL, "");
+#endif
+
+  /* Set the text message domain.  */
+  bindtextdomain (PACKAGE, LOCALEDIR);
+  textdomain (PACKAGE);
+
+  /* Set default values for variables.  */
+  do_help = false;
+  do_version = false;
+  output_file = NULL;
+  input_file = NULL;
+  locale = NULL;
+
+  while ((opt = getopt_long (argc, argv, "hi:l:o:vVw:", long_options, NULL))
+        != EOF)
+    switch (opt)
+      {
+      case '\0':               /* Long option.  */
+       break;
+
+      case 'h':
+       do_help = true;
+       break;
+
+      case 'i':
+       if (input_file != NULL)
+         {
+           error (EXIT_SUCCESS, 0, _("at most one input file allowed"));
+           usage (EXIT_FAILURE);
+         }
+       input_file = optarg;
+       break;
+
+      case 'l':
+       locale = optarg;
+       break;
+
+      case 'o':
+       output_file = optarg;
+       break;
+
+      case 'v':
+       verbose = true;
+       break;
+
+      case 'V':
+       do_version = true;
+       break;
+
+      case 'w':
+       {
+         int value;
+         char *endp;
+         value = strtol (optarg, &endp, 10);
+         if (endp != optarg)
+           message_page_width_set (value);
+       }
+       break;
+
+      default:
+       usage (EXIT_FAILURE);
+       break;
+      }
+
+  /* Version information is requested.  */
+  if (do_version)
+    {
+      printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
+      /* xgettext: no-wrap */
+      printf (_("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\
+"),
+             "2001");
+      printf (_("Written by %s.\n"), "Bruno Haible");
+      exit (EXIT_SUCCESS);
+    }
+
+  /* Help is requested.  */
+  if (do_help)
+    usage (EXIT_SUCCESS);
+
+  /* Test for extraneous arguments.  */
+  if (optind != argc)
+    error (EXIT_FAILURE, 0, _("too many arguments"));
+
+  /* Search for the input file.  */
+  if (input_file == NULL)
+    input_file = find_pot ();
+
+  /* Determine target locale.  */
+  if (locale == NULL)
+    {
+      locale = _nl_locale_name (LC_MESSAGES, "LC_MESSAGES");
+      if (strcmp (locale, "C") == 0)
+       {
+         multiline_error (xstrdup (""),
+                          xstrdup (_("\
+You are in a language indifferent environment.  Please set\n\
+your LANG environment variable, as described in the ABOUT-NLS\n\
+file.  This is necessary so you can test your translations.\n")));
+         exit (EXIT_FAILURE);
+       }
+    }
+  {
+    const char *alias = _nl_expand_alias (locale);
+    if (alias != NULL)
+      locale = alias;
+  }
+  catalogname = catalogname_for_locale (locale);
+  language = language_of_locale (locale);
+
+  /* Default output file name is CATALOGNAME.po.  */
+  if (output_file == NULL)
+    {
+      size_t cnlen = strlen (catalogname);
+
+      output_file = (char *) xmalloc (cnlen + 3 + 1);
+      memcpy (output_file, catalogname, cnlen);
+      memcpy (output_file + cnlen, ".po", 3 + 1);
+
+      /* But don't overwrite existing PO files.  */
+      if (access (output_file, F_OK) == 0)
+       {
+         multiline_error (xstrdup (""),
+                          xasprintf (_("\
+Output file %s already exists.\n\
+Please specify the locale through the --locale option or\n\
+the output .po file through the --output-file option.\n"),
+                                     output_file));
+         exit (EXIT_FAILURE);
+       }
+    }
+
+  /* Read input file.  */
+  result = read_po_file (input_file);
+
+  /* Fill the header entry.  */
+  result = fill_header (result);
+
+  /* Initialize translations.  */
+  if (strcmp (language, "en") == 0)
+    result = msgdomain_list_english (result);
+
+  /* Write the modified message list out.  */
+  msgdomain_list_print (result, output_file, true, false);
+
+  if (verbose)
+    fprintf (stderr, "Created %s.\n", output_file);
+
+  exit (EXIT_SUCCESS);
+}
+
+
+/* Display usage information and exit.  */
+static void
+usage (status)
+     int status;
+{
+  if (status != EXIT_SUCCESS)
+    fprintf (stderr, _("Try `%s --help' for more information.\n"),
+            program_name);
+  else
+    {
+      /* xgettext: no-wrap */
+      printf (_("\
+Usage: %s [OPTION]\n\
+"), program_name);
+      printf ("\n");
+      /* xgettext: no-wrap */
+      printf (_("\
+Creates a new PO file, initializing the meta information with values from the\n\
+user's environment.\n\
+"));
+      printf ("\n");
+      /* xgettext: no-wrap */
+      printf (_("\
+Mandatory arguments to long options are mandatory for short options too.\n\
+"));
+      printf ("\n");
+      /* xgettext: no-wrap */
+      printf (_("\
+Input file location:\n\
+  -i, --input=INPUTFILE       input POT file\n\
+If no input file is given, the current directory is searched for the POT file.\n\
+If it is -, standard input is read.\n\
+"));
+      printf ("\n");
+      /* xgettext: no-wrap */
+      printf (_("\
+Output file location:\n\
+  -o, --output-file=FILE      write output to specified PO file\n\
+If no output file is given, it depends on the --locale option or the user's\n\
+locale setting.  If it is -, the results are written to standard output.\n\
+"));
+      printf ("\n");
+      /* xgettext: no-wrap */
+      printf (_("\
+Output details:\n\
+  -l, --locale=LL_CC          set target locale\n\
+  -w, --width=NUMBER          set output page width\n\
+"));
+      printf ("\n");
+      /* xgettext: no-wrap */
+      printf (_("\
+Informative output:\n\
+  -h, --help                  display this help and exit\n\
+  -V, --version               output version information and exit\n\
+  -v, --verbose               increase verbosity level\n\
+"));
+      printf ("\n");
+      fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
+            stdout);
+    }
+
+  exit (status);
+}
+
+
+/* Search for the POT file and return its name.  */
+static const char *
+find_pot ()
+{
+#if HAVE_DIR
+  DIR *dirp;
+  char *found = NULL;
+
+  dirp = opendir (".");
+  if (dirp != NULL)
+    {
+      for (;;)
+       {
+         struct dirent *dp;
+
+         errno = 0;
+         dp = readdir (dirp);
+         if (dp != NULL)
+           {
+             const char *name = dp->d_name;
+             size_t namlen = strlen (name);
+
+             if (namlen > 4 && memcmp (name + namlen - 4, ".pot", 4) == 0)
+               {
+                 if (found == NULL)
+                   found = xstrdup (name);
+                 else
+                   {
+                     multiline_error (xstrdup (""),
+                                      xstrdup (_("\
+Found more than one .pot file.\n\
+Please specify the input .pot file through the --input option.\n")));
+                     usage (EXIT_FAILURE);
+                   }
+               }
+           }
+         else if (errno != 0)
+           error (EXIT_FAILURE, errno, _("error reading current directory"));
+         else
+           break;
+       }
+      if (CLOSEDIR (dirp))
+       error (EXIT_FAILURE, errno, _("error reading current directory"));
+
+      if (found != NULL)
+       return found;
+    }
+#endif
+
+  multiline_error (xstrdup (""),
+                  xstrdup (_("\
+Found no .pot file in the current directory.\n\
+Please specify the input .pot file through the --input option.\n")));
+  usage (EXIT_FAILURE);
+}
+
+
+/* Return the gettext catalog name corresponding to a locale.  If the locale
+   consists of a language and a territory, and the language is mainly spoken
+   in that territory, the territory is removed from the locale name.
+   For example, "de_DE" or "de_DE.ISO-8859-1" are simplified to "de",
+   because the resulting catalog can be used as a default for all "de_XX",
+   such as "de_AT".  */
+static const char *
+catalogname_for_locale (locale)
+     const char *locale;
+{
+  static const char *locales_with_principal_territory[] = {
+               /* Language     Main territory */
+    "af_ZA",   /* Afrikaans    South Africa */
+    "am_ET",   /* Amharic      Ethiopia */
+    "as_IN",   /* Assamese     India */
+    "az_AZ",   /* Azerbaijani  Azerbaijan */
+    "be_BY",   /* Belarusian   Belarus */
+    "bg_BG",   /* Bulgarian    Bulgaria */
+    "bn_IN",   /* Bengali      India */
+    "bo_CN",   /* Tibetan      China */
+    "br_FR",   /* Breton       France */
+    "bs_BA",   /* Bosnian      Bosnia */
+    "ca_ES",   /* Catalan      Spain */
+    "ce_RU",   /* Chechen      Russia */
+    "co_FR",   /* Corsican     France */
+    "cs_CZ",   /* Czech        Czech Republic */
+    "cy_GB",   /* Welsh        Britain */
+    "da_DK",   /* Danish       Denmark */
+    "de_DE",   /* German       Germany */
+    "dz_BT",   /* Dzongkha     Bhutan */
+    "el_GR",   /* Greek        Greece */
+    /* Don't put "en_GB" or "en_US" here.  That would be asking for fruitless
+       political discussion.  */
+    "es_ES",   /* Spanish      Spain */
+    "et_EE",   /* Estonian     Estonia */
+    "fa_IR",   /* Persian      Iran */
+    "fi_FI",   /* Finnish      Finland */
+    "fj_FJ",   /* Fijian       Fiji */
+    "fo_FO",   /* Faroese      Faeroe Islands */
+    "fr_FR",   /* French       France */
+    "ga_IE",   /* Irish        Ireland */
+    "gd_GB",   /* Scots        Britain */
+    "gu_IN",   /* Gujarati     India */
+    "he_IL",   /* Hebrew       Israel */
+    "hi_IN",   /* Hindi        India */
+    "hr_HR",   /* Croatian     Croatia */
+    "hu_HU",   /* Hungarian    Hungary */
+    "hy_AM",   /* Armenian     Armenia */
+    "id_ID",   /* Indonesian   Indonesia */
+    "is_IS",   /* Icelandic    Iceland */
+    "it_IT",   /* Italian      Italy */
+    "ja_JP",   /* Japanese     Japan */
+    "jw_ID",   /* Javanese     Indonesia */
+    "ka_GE",   /* Georgian     Georgia */
+    "kk_KZ",   /* Kazakh       Kazakhstan */
+    "kl_GL",   /* Kalaallisut  Greenland */
+    "km_KH",   /* Khmer        Cambodia */
+    "kn_IN",   /* Kannada      India */
+    "ko_KR",   /* Korean       Korea (South) */
+    "kok_IN",  /* Konkani      India */
+    "lo_LA",   /* Laotian      Laos */
+    "lt_LT",   /* Lithuanian   Lithuania */
+    "lv_LV",   /* Latvian      Latvia */
+    "mg_MG",   /* Malagasy     Madagascar */
+    "mk_MK",   /* Macedonian   Macedonia */
+    "ml_IN",   /* Malayalam    India */
+    "mn_MN",   /* Mongolian    Mongolia */
+    "mr_IN",   /* Marathi      India */
+    "ms_MY",   /* Malay        Malaysia */
+    "mt_MT",   /* Maltese      Malta */
+    "my_MM",   /* Burmese      Myanmar */
+    "mni_IN",  /* Manipuri     India */
+    "na_NR",   /* Nauru        Nauru */
+    "nb_NO",   /* Norwegian BokmÃ¥l    Norway */
+    "ne_NP",   /* Nepali       Nepal */
+    "nl_NL",   /* Dutch        Netherlands */
+    "nn_NO",   /* Norwegian Nynorsk    Norway */
+    "no_NO",   /* Norwegian    Norway */
+    "oc_FR",   /* Occitan      France */
+    "or_IN",   /* Oriya        India */
+    "pa_IN",   /* Punjabi      India */
+    "pl_PL",   /* Polish       Poland */
+    "ps_AF",   /* Pashto       Afghanistan */
+    "pt_PT",   /* Portuguese   Portugal */
+    "rm_CH",   /* Rhaeto-Roman Switzerland */
+    "rn_BI",   /* Kirundi      Burundi */
+    "ro_RO",   /* Romanian     Romania */
+    "ru_RU",   /* Russian      Russia */
+    "sa_IN",   /* Sanskrit     India */
+    "sc_IT",   /* Sardinian    Italy */
+    "sg_CF",   /* Sango        Central African Rep. */
+    "si_LK",   /* Sinhalese    Sri Lanka */
+    "sk_SK",   /* Slovak       Slovakia */
+    "sl_SI",   /* Slovenian    Slovenia */
+    "so_SO",   /* Somali       Somalia */
+    "sq_AL",   /* Albanian     Albania */
+    "sr_YU",   /* Serbian      Yugoslavia */
+    "sv_SE",   /* Swedish      Sweden */
+    "te_IN",   /* Telugu       India */
+    "tg_TJ",   /* Tajik        Tajikistan */
+    "th_TH",   /* Thai         Thailand */
+    "tk_TM",   /* Turkmen      Turkmenistan */
+    "tl_PH",   /* Tagalog      Philippines */
+    "to_TO",   /* Tonga        Tonga */
+    "tr_TR",   /* Turkish      Turkey */
+    "uk_UA",   /* Ukrainian    Ukraine */
+    "ur_PK",   /* Urdu         Pakistan */
+    "uz_UZ",   /* Uzbek        Uzbekistan */
+    "vi_VN",   /* Vietnamese   Vietnam */
+    "wen_DE"   /* Sorbian      Germany */
+  };
+  const char *dot;
+  size_t i;
+
+  /* Remove the ".codeset" part from the locale.  */
+  dot = strchr (locale, '.');
+  if (dot != NULL)
+    {
+      const char *codeset_end;
+      char *shorter_locale;
+
+      codeset_end = strpbrk (dot + 1, "_@+,");
+      if (codeset_end == NULL)
+       codeset_end = dot + strlen (dot);
+
+      shorter_locale = (char *) xmalloc (strlen (locale));
+      memcpy (shorter_locale, locale, dot - locale);
+      strcpy (shorter_locale + (dot - locale), codeset_end);
+      locale = shorter_locale;
+    }
+
+  /* If the territory is the language's principal territory, drop it.  */
+  for (i = 0; i < SIZEOF (locales_with_principal_territory); i++)
+    if (strcmp (locale, locales_with_principal_territory[i]) == 0)
+      {
+       const char *language_end;
+       size_t len;
+       char *shorter_locale;
+
+       language_end = strchr (locale, '_');
+       if (language_end == NULL)
+         abort ();
+
+       len = language_end - locale;
+       shorter_locale = (char *) xmalloc (len + 1);
+       memcpy (shorter_locale, locale, len);
+       shorter_locale[len] = '\0';
+       locale = shorter_locale;
+       break;
+      }
+
+  return locale;
+}
+
+
+/* Return the language of a locale.  */
+static const char *
+language_of_locale (locale)
+     const char *locale;
+{
+  const char *language_end;
+
+  language_end = strpbrk (locale, "_.@+,");
+  if (language_end != NULL)
+    {
+      size_t len;
+      char *result;
+
+      len = language_end - locale;
+      result = (char *) xmalloc (len + 1);
+      memcpy (result, locale, len);
+      result[len] = '\0';
+
+      return result;
+    }
+  else
+    return locale;
+}
+
+
+/* Return the most likely desired charset for the PO file, as a portable
+   charset name.  */
+static const char *
+canonical_locale_charset ()
+{
+  const char *charset;
+
+  /* Get the current locale's charset and canonicalize it.  */
+  charset = locale_charset ();
+  charset = po_charset_canonicalize (charset);
+  if (charset == NULL)
+    charset = po_charset_ascii;
+
+  return charset;
+}
+
+
+/* Return the English name of the language.  */
+static const char *
+englishname_of_language ()
+{
+  /* Derived from ISO 639.  */
+  static struct { const char *code; const char *english; } table[] = {
+    { "aa", "Afar" },
+    { "ab", "Abkhazian" },
+    { "ae", "Avestan" },
+    { "af", "Afrikaans" },
+    { "am", "Amharic" },
+    { "ar", "Arabic" },
+    { "as", "Assamese" },
+    { "ay", "Aymara" },
+    { "az", "Azerbaijani" },
+    { "ba", "Bashkir" },
+    { "be", "Belarusian" },
+    { "bg", "Bulgarian" },
+    { "bh", "Bihari" },
+    { "bi", "Bislama" },
+    { "bn", "Bengali" },
+    { "bo", "Tibetan" },
+    { "br", "Breton" },
+    { "bs", "Bosnian" },
+    { "ca", "Catalan" },
+    { "ce", "Chechen" },
+    { "ch", "Chamorro" },
+    { "co", "Corsican" },
+    { "cs", "Czech" },
+    { "cu", "Church Slavic" },
+    { "cv", "Chuvash" },
+    { "cy", "Welsh" },
+    { "da", "Danish" },
+    { "de", "German" },
+    { "dz", "Dzongkha" },
+    { "el", "Greek" },
+    { "en", "English" },
+    { "eo", "Esperanto" },
+    { "es", "Spanish" },
+    { "et", "Estonian" },
+    { "eu", "Basque" },
+    { "fa", "Persian" },
+    { "fi", "Finnish" },
+    { "fj", "Fijian" },
+    { "fo", "Faroese" },
+    { "fr", "French" },
+    { "fy", "Frisian" },
+    { "ga", "Irish" },
+    { "gd", "Scots" },
+    { "gl", "Galician" },
+    { "gn", "Guarani" },
+    { "gu", "Gujarati" },
+    { "gv", "Manx" },
+    { "ha", "Hausa" },
+    { "he", "Hebrew" },
+    { "hi", "Hindi" },
+    { "ho", "Hiri Motu" },
+    { "hr", "Croatian" },
+    { "hu", "Hungarian" },
+    { "hy", "Armenian" },
+    { "hz", "Herero" },
+    { "ia", "Interlingua" },
+    { "id", "Indonesian" },
+    { "ie", "Interlingue" },
+    { "ik", "Inupiak" },
+    { "is", "Icelandic" },
+    { "it", "Italian" },
+    { "iu", "Inuktitut" },
+    { "ja", "Japanese" },
+    { "jw", "Javanese" },
+    { "ka", "Georgian" },
+    { "ki", "Kikuyu" },
+    { "kj", "Kuanyama" },
+    { "kk", "Kazakh" },
+    { "kl", "Kalaallisut" },
+    { "km", "Khmer" },
+    { "kn", "Kannada" },
+    { "ko", "Korean" },
+    { "ks", "Kashmiri" },
+    { "ku", "Kurdish" },
+    { "kv", "Komi" },
+    { "kw", "Cornish" },
+    { "ky", "Kirghiz" },
+    { "kok", "Konkani" },
+    { "la", "Latin" },
+    { "lb", "Letzeburgesch" },
+    { "ln", "Lingala" },
+    { "lo", "Laotian" },
+    { "lt", "Lithuanian" },
+    { "lv", "Latvian" },
+    { "mg", "Malagasy" },
+    { "mh", "Marshall" },
+    { "mi", "Maori" },
+    { "mk", "Macedonian" },
+    { "ml", "Malayalam" },
+    { "mn", "Mongolian" },
+    { "mo", "Moldavian" },
+    { "mr", "Marathi" },
+    { "ms", "Malay" },
+    { "mt", "Maltese" },
+    { "my", "Burmese" },
+    { "mni", "Manipuri" },
+    { "na", "Nauru" },
+    { "nb", "Norwegian Bokmal" },
+    { "nd", "North Ndebele" },
+    { "ne", "Nepali" },
+    { "ng", "Ndonga" },
+    { "nl", "Dutch" },
+    { "nn", "Norwegian Nynorsk" },
+    { "no", "Norwegian" },
+    { "nr", "South Ndebele" },
+    { "nv", "Navajo" },
+    { "ny", "Nyanja" },
+    { "oc", "Occitan" },
+    { "om", "(Afan) Oromo" },
+    { "or", "Oriya" },
+    { "os", "Ossetian" },
+    { "pa", "Punjabi" },
+    { "pi", "Pali" },
+    { "pl", "Polish" },
+    { "ps", "Pashto" },
+    { "pt", "Portuguese" },
+    { "qu", "Quechua" },
+    { "rm", "Rhaeto-Roman" },
+    { "rn", "Kirundi" },
+    { "ro", "Romanian" },
+    { "ru", "Russian" },
+    { "rw", "Kinyarwanda" },
+    { "sa", "Sanskrit" },
+    { "sc", "Sardinian" },
+    { "sd", "Sindhi" },
+    { "se", "Northern Sami" },
+    { "sg", "Sango" },
+    { "si", "Sinhalese" },
+    { "sk", "Slovak" },
+    { "sl", "Slovenian" },
+    { "sm", "Samoan" },
+    { "sn", "Shona" },
+    { "so", "Somali" },
+    { "sq", "Albanian" },
+    { "sr", "Serbian" },
+    { "ss", "Siswati" },
+    { "st", "Sesotho" },
+    { "su", "Sundanese" },
+    { "sv", "Swedish" },
+    { "sw", "Swahili" },
+    { "ta", "Tamil" },
+    { "te", "Telugu" },
+    { "tg", "Tajik" },
+    { "th", "Thai" },
+    { "ti", "Tigrinya" },
+    { "tk", "Turkmen" },
+    { "tl", "Tagalog" },
+    { "tn", "Setswana" },
+    { "to", "Tonga" },
+    { "tr", "Turkish" },
+    { "ts", "Tsonga" },
+    { "tt", "Tatar" },
+    { "tw", "Twi" },
+    { "ty", "Tahitian" },
+    { "ug", "Uighur" },
+    { "uk", "Ukrainian" },
+    { "ur", "Urdu" },
+    { "uz", "Uzbek" },
+    { "vi", "Vietnamese" },
+    { "vo", "Volapuk" },
+    { "wo", "Wolof" },
+    { "wen", "Sorbian" },
+    { "xh", "Xhosa" },
+    { "yi", "Yiddish" },
+    { "yo", "Yoruba" },
+    { "za", "Zhuang" },
+    { "zh", "Chinese" },
+    { "zu", "Zulu" }
+  };
+  size_t i;
+
+  for (i = 0; i < SIZEOF (table); i ++)
+    if (strcmp (table[i].code, language) == 0)
+      return table[i].english;
+
+  return xasprintf ("Language %s", language);
+}
+
+
+/* Construct the value for the PACKAGE name.  */
+static const char *
+project_id ()
+{
+  char *prog = concatenated_pathname (LIBDIR, "gettext/project-id", NULL);
+  char *argv[3];
+  pid_t child;
+  int fd[1];
+  FILE *fp;
+  char *line;
+  size_t linesize;
+  size_t linelen;
+  int exitstatus;
+
+  /* Call the project-id shell script.  */
+  argv[0] = "/bin/sh";
+  argv[1] = prog;
+  argv[2] = NULL;
+  child = create_pipe_in (prog, "/bin/sh", argv, "/dev/null", false, true, fd);
+
+  /* Retrieve its result.  */
+  fp = fdopen (fd[0], "r");
+  if (fp == NULL)
+    error (EXIT_FAILURE, errno, _("fdopen() failed"));
+
+  line = NULL; linesize = 0;
+  linelen = getline (&line, &linesize, fp);
+  if (linelen == (size_t)(-1))
+    error (EXIT_FAILURE, 0, _("%s subprocess I/O error"), prog);
+  if (linelen > 0 && line[linelen - 1] == '\n')
+    line[linelen - 1] = '\0';
+
+  fclose (fp);
+
+  /* Remove zombie process from process list, and retrieve exit status.  */
+  exitstatus = wait_subprocess (child, prog, true);
+  if (exitstatus != 0)
+    error (EXIT_FAILURE, 0, _("%s subprocess failed with exit code %d"),
+          prog, exitstatus);
+
+  return line;
+}
+
+
+/* Construct the value for the Project-Id-Version field.  */
+static const char *
+project_id_version ()
+{
+  char *prog = concatenated_pathname (LIBDIR, "gettext/project-id", NULL);
+  char *argv[4];
+  pid_t child;
+  int fd[1];
+  FILE *fp;
+  char *line;
+  size_t linesize;
+  size_t linelen;
+  int exitstatus;
+
+  /* Call the project-id shell script.  */
+  argv[0] = "/bin/sh";
+  argv[1] = prog;
+  argv[2] = "yes";
+  argv[3] = NULL;
+  child = create_pipe_in (prog, "/bin/sh", argv, "/dev/null", false, true, fd);
+
+  /* Retrieve its result.  */
+  fp = fdopen (fd[0], "r");
+  if (fp == NULL)
+    error (EXIT_FAILURE, errno, _("fdopen() failed"));
+
+  line = NULL; linesize = 0;
+  linelen = getline (&line, &linesize, fp);
+  if (linelen == (size_t)(-1))
+    error (EXIT_FAILURE, 0, _("%s subprocess I/O error"), prog);
+  if (linelen > 0 && line[linelen - 1] == '\n')
+    line[linelen - 1] = '\0';
+
+  fclose (fp);
+
+  /* Remove zombie process from process list, and retrieve exit status.  */
+  exitstatus = wait_subprocess (child, prog, true);
+  if (exitstatus != 0)
+    error (EXIT_FAILURE, 0, _("%s subprocess failed with exit code %d"),
+          prog, exitstatus);
+
+  return line;
+}
+
+
+/* Construct the value for the PO-Revision-Date field.  */
+static const char *
+po_revision_date ()
+{
+  time_t now;
+
+  time (&now);
+  return po_strftime (&now);
+}
+
+
+/* Returns the struct passwd entry for the current user.  */
+static struct passwd *
+get_user_pwd ()
+{
+  const char *username;
+  struct passwd *userpasswd;
+
+  /* 1. attempt: getpwnam(getenv("USER"))  */
+  username = getenv ("USER");
+  if (username != NULL)
+    {
+      errno = 0;
+      userpasswd = getpwnam (username);
+      if (userpasswd != NULL)
+       return userpasswd;
+      if (errno != 0)
+       error (EXIT_FAILURE, errno, "getpwnam(\"%s\")", username);
+    }
+
+  /* 2. attempt: getpwnam(getlogin())  */
+  username = getlogin ();
+  if (username != NULL)
+    {
+      errno = 0;
+      userpasswd = getpwnam (username);
+      if (userpasswd != NULL)
+       return userpasswd;
+      if (errno != 0)
+       error (EXIT_FAILURE, errno, "getpwnam(\"%s\")", username);
+    }
+
+  /* 3. attempt: getpwuid(getuid())  */
+  errno = 0;
+  userpasswd = getpwuid (getuid ());
+  if (userpasswd != NULL)
+    return userpasswd;
+  if (errno != 0)
+    error (EXIT_FAILURE, errno, "getpwuid(\"%d\")", getuid ());
+
+  return NULL;
+}
+
+
+/* Return the user's full name.  */
+static const char *
+get_user_fullname ()
+{
+  struct passwd *pwd;
+  const char *fullname;
+  const char *fullname_end;
+  char *result;
+
+  pwd = get_user_pwd ();
+  if (pwd == NULL)
+    return NULL;
+
+  /* Return the pw_gecos field, upto the first comma (if any).  */
+  fullname = pwd->pw_gecos;
+  fullname_end = strchr (fullname, ',');
+  if (fullname_end == NULL)
+    fullname_end = fullname + strlen (fullname);
+
+  result = (char *) xmalloc (fullname_end - fullname + 1);
+  memcpy (result, fullname, fullname_end - fullname);
+  result[fullname_end - fullname] = '\0';
+
+  return result;
+}
+
+
+/* Return the user's email address.  */
+static const char *
+get_user_email ()
+{
+  char *prog = concatenated_pathname (LIBDIR, "gettext/user-email", NULL);
+  char *argv[4];
+  pid_t child;
+  int fd[1];
+  FILE *fp;
+  char *line;
+  size_t linesize;
+  size_t linelen;
+  int exitstatus;
+
+  /* Ask the user for his email address.  */
+  argv[0] = "/bin/sh";
+  argv[1] = prog;
+  argv[2] = _("\
+The new message catalog should contain your email address, so that users can\n\
+give you feedback about the translations, and so that maintainers can contact\n\
+you in case of unexpected technical problems.\n");
+  argv[3] = NULL;
+  child = create_pipe_in (prog, "/bin/sh", argv, "/dev/null", false, true, fd);
+
+  /* Retrieve his answer.  */
+  fp = fdopen (fd[0], "r");
+  if (fp == NULL)
+    error (EXIT_FAILURE, errno, _("fdopen() failed"));
+
+  line = NULL; linesize = 0;
+  linelen = getline (&line, &linesize, fp);
+  if (linelen == (size_t)(-1))
+    error (EXIT_FAILURE, 0, _("%s subprocess I/O error"), prog);
+  if (linelen > 0 && line[linelen - 1] == '\n')
+    line[linelen - 1] = '\0';
+
+  fclose (fp);
+
+  /* Remove zombie process from process list, and retrieve exit status.  */
+  exitstatus = wait_subprocess (child, prog, true);
+  if (exitstatus != 0)
+    error (EXIT_FAILURE, 0, _("%s subprocess failed with exit code %d"),
+          prog, exitstatus);
+
+  return line;
+}
+
+
+/* Construct the value for the Last-Translator field.  */
+static const char *
+last_translator ()
+{
+  const char *fullname = get_user_fullname ();
+  const char *email = get_user_email ();
+
+  if (fullname != NULL)
+    return xasprintf ("%s <%s>", fullname, email);
+  else
+    return xasprintf ("<%s>", email);
+}
+
+
+/* Return the language team's mailing list address or homepage URL.  */
+static const char *
+language_team_address ()
+{
+  char *prog = concatenated_pathname (PROJECTSDIR, "team-address", NULL);
+  char *argv[8];
+  pid_t child;
+  int fd[1];
+  FILE *fp;
+  char *line;
+  size_t linesize;
+  size_t linelen;
+  int exitstatus;
+
+  /* Call the team-address shell script.  */
+  argv[0] = "/bin/sh";
+  argv[1] = prog;
+  argv[2] = PROJECTSDIR;
+  argv[3] = concatenated_pathname (LIBDIR, "gettext", NULL);
+  argv[4] = (char *) catalogname;
+  argv[5] = (char *) language;
+  argv[6] = (verbose ? "yes" : "");
+  argv[7] = NULL;
+  child = create_pipe_in (prog, "/bin/sh", argv, "/dev/null", false, true, fd);
+
+  /* Retrieve its result.  */
+  fp = fdopen (fd[0], "r");
+  if (fp == NULL)
+    error (EXIT_FAILURE, errno, _("fdopen() failed"));
+
+  line = NULL; linesize = 0;
+  linelen = getline (&line, &linesize, fp);
+  if (linelen == (size_t)(-1))
+    line = "";
+  else if (linelen > 0 && line[linelen - 1] == '\n')
+    line[linelen - 1] = '\0';
+
+  fclose (fp);
+
+  /* Remove zombie process from process list, and retrieve exit status.  */
+  exitstatus = wait_subprocess (child, prog, true);
+  if (exitstatus != 0)
+    error (EXIT_FAILURE, 0, _("%s subprocess failed with exit code %d"),
+          prog, exitstatus);
+
+  return line;
+}
+
+
+/* Construct the value for the Language-Team field.  */
+static const char *
+language_team ()
+{
+  const char *englishname = englishname_of_language ();
+  const char *address = language_team_address ();
+
+  if (address != NULL && address[0] != '\0')
+    return xasprintf ("%s %s", englishname, address);
+  else
+    return englishname;
+}
+
+
+/* Construct the value for the MIME-Version field.  */
+static const char *
+mime_version ()
+{
+  return "1.0";
+}
+
+
+/* Construct the value for the Content-Type field.  */
+static const char *
+content_type ()
+{
+  return xasprintf ("text/plain; charset=%s", canonical_locale_charset ());
+}
+
+
+/* Construct the value for the Content-Transfer-Encoding field.  */
+static const char *
+content_transfer_encoding ()
+{
+  return "8bit";
+}
+
+
+/* Construct the value for the Plural-Forms field.  */
+static const char *
+plural_forms ()
+{
+  /* Formulas taken from the documentation, node "Plural forms".  */
+  static struct { const char *lang; const char *value; } table[] =
+    {
+      { "hu", "nplurals=1; plural=0;" },
+      { "ja", "nplurals=1; plural=0;" },
+      { "ko", "nplurals=1; plural=0;" },
+      { "tr", "nplurals=1; plural=0;" },
+      { "da", "nplurals=2; plural=(n != 1);" },
+      { "nl", "nplurals=2; plural=(n != 1);" },
+      { "en", "nplurals=2; plural=(n != 1);" },
+      { "de", "nplurals=2; plural=(n != 1);" },
+      { "nb", "nplurals=2; plural=(n != 1);" },
+      { "no", "nplurals=2; plural=(n != 1);" },
+      { "nn", "nplurals=2; plural=(n != 1);" },
+      { "sv", "nplurals=2; plural=(n != 1);" },
+      { "et", "nplurals=2; plural=(n != 1);" },
+      { "fi", "nplurals=2; plural=(n != 1);" },
+      { "el", "nplurals=2; plural=(n != 1);" },
+      { "he", "nplurals=2; plural=(n != 1);" },
+      { "it", "nplurals=2; plural=(n != 1);" },
+      { "pt", "nplurals=2; plural=(n != 1);" },
+      { "es", "nplurals=2; plural=(n != 1);" },
+      { "eo", "nplurals=2; plural=(n != 1);" },
+      { "fr", "nplurals=2; plural=(n > 1);" },
+      { "pt_BR", "nplurals=2; plural=(n > 1);" },
+      { "lv", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);" },
+      { "ga", "nplurals=3; plural=n==1 ? 0 : n==2 ? 1 : 2;" },
+      { "lt", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2);" },
+      { "hr", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" },
+      { "cs", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" },
+      { "ru", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" },
+      { "sk", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" },
+      { "uk", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" },
+      { "pl", "nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" },
+      { "sl", "nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);" }
+    };
+  size_t i;
+
+  /* Search for a formula depending on the catalogname.  */
+  for (i = 0; i < SIZEOF (table); i++)
+    if (strcmp (table[i].lang, catalogname) == 0)
+      return table[i].value;
+
+  /* Search for a formula depending on the language only.  */
+  for (i = 0; i < SIZEOF (table); i++)
+    if (strcmp (table[i].lang, language) == 0)
+      return table[i].value;
+
+  return NULL;
+}
+
+
+static struct { const char *name; const char * (*getter) PARAMS ((void)); }
+fields[] =
+  {
+    { "Project-Id-Version", project_id_version },
+    { "PO-Revision-Date", po_revision_date },
+    { "Last-Translator", last_translator },
+    { "Language-Team", language_team },
+    { "MIME-Version", mime_version },
+    { "Content-Type", content_type },
+    { "Content-Transfer-Encoding", content_transfer_encoding },
+    { "Plural-Forms", plural_forms }
+  };
+
+#define NFIELDS SIZEOF (fields)
+#define FIELD_LAST_TRANSLATOR 2
+
+
+#ifdef unused
+/* Retrieve a freshly allocated copy of a field's value.  */
+static char *
+get_field (header, field)
+     const char *header;
+     const char *field;
+{
+  size_t len = strlen (field);
+  const char *line;
+
+  for (line = header;;)
+    {
+      if (strncmp (line, field, len) == 0
+         && line[len] == ':' && line[len + 1] == ' ')
+       {
+         const char *value_start;
+         const char *value_end;
+         char *value;
+
+         value_start = line + len + 2;
+         value_end = strchr (value_start, '\n');
+         if (value_end == NULL)
+           value_end = value_start + strlen (value_start);
+
+         value = (char *) xmalloc (value_end - value_start + 1);
+         memcpy (value, value_start, value_end - value_start);
+         value[value_end - value_start] = '\0';
+
+         return value;
+       }
+
+      line = strchr (line, '\n');
+      if (line != NULL)
+       line++;
+      else
+       break;
+    }
+
+  return NULL;
+}
+#endif
+
+/* Add a field with value to a header, and return the new header.  */
+static char *
+put_field (old_header, field, value)
+     const char *old_header;
+     const char *field;
+     const char *value;
+{
+  size_t len = strlen (field);
+  const char *line;
+  char *new_header;
+  char *p;
+
+  for (line = old_header;;)
+    {
+      if (strncmp (line, field, len) == 0
+         && line[len] == ':' && line[len + 1] == ' ')
+       {
+         const char *value_start;
+         const char *value_end;
+
+         value_start = line + len + 2;
+         value_end = strchr (value_start, '\n');
+         if (value_end == NULL)
+           value_end = value_start + strlen (value_start);
+
+         new_header = (char *) xmalloc (strlen (old_header)
+                                        - (value_end - value_start)
+                                        + strlen (value)
+                                        + (*value_end != '\n' ? 1 : 0)
+                                        + 1);
+         p = new_header;
+         memcpy (p, old_header, value_start - old_header);
+         p += value_start - old_header;
+         memcpy (p, value, strlen (value));
+         p += strlen (value);
+         if (*value_end != '\n')
+           *p++ = '\n';
+         strcpy (p, value_end);
+
+         return new_header;
+       }
+
+      line = strchr (line, '\n');
+      if (line != NULL)
+       line++;
+      else
+       break;
+    }
+
+  new_header = (char *) xmalloc (strlen (old_header) + 1
+                                + len + 2 + strlen (value) + 1
+                                + 1);
+  p = new_header;
+  memcpy (p, old_header, strlen (old_header));
+  p += strlen (old_header);
+  if (p > new_header && p[-1] != '\n')
+    *p++ = '\n';
+  memcpy (p, field, len);
+  p += len;
+  *p++ = ':';
+  *p++ = ' ';
+  memcpy (p, value, strlen (value));
+  p += strlen (value);
+  *p++ = '\n';
+  *p = '\0';
+
+  return new_header;
+}
+
+
+/* Return the title format string.  */
+static const char *
+get_title ()
+{
+  /* This is tricky.  We want the translation in the given locale specified by
+     the command line, not the current locale.  But we want it in the encoding
+     that we put into the header entry, not the encoding of that locale.
+     We could avoid the use of OUTPUT_CHARSET by using a separate message
+     catalog and bind_textdomain_codeset(), but that doesn't seem worth the
+     trouble for one single message.  */
+  const char *encoding;
+  const char *tmp;
+  char *old_LC_ALL;
+  char *old_LANGUAGE;
+  char *old_OUTPUT_CHARSET;
+  const char *msgid;
+  const char *result;
+
+  encoding = canonical_locale_charset ();
+
+  /* Save LC_ALL, LANGUAGE, OUTPUT_CHARSET environment variables.  */
+
+  tmp = getenv ("LC_ALL");
+  old_LC_ALL = (tmp != NULL ? xstrdup (tmp) : NULL);
+
+  tmp = getenv ("LANGUAGE");
+  old_LANGUAGE = (tmp != NULL ? xstrdup (tmp) : NULL);
+
+  tmp = getenv ("OUTPUT_CHARSET");
+  old_OUTPUT_CHARSET = (tmp != NULL ? xstrdup (tmp) : NULL);
+
+  setenv ("LC_ALL", locale, 1);
+  unsetenv ("LANGUAGE");
+  setenv ("OUTPUT_CHARSET", encoding, 1);
+
+#ifdef HAVE_SETLOCALE
+  setlocale (LC_ALL, "");
+#endif
+
+  /* Fetch the translation.  */
+  /* TRANSLATORS: "English" needs to be replaced by your language.
+     For example in it.po write "Traduzioni italiani ...",
+     *not* "Traduzioni inglesi ...".  */
+  msgid = N_("English translations for %s package");
+  result = gettext (msgid);
+  if (result == msgid)
+    /* No translation found.  */
+    result = xasprintf ("%s translations for %%s package",
+                       englishname_of_language ());
+
+  /* Restore LC_ALL, LANGUAGE, OUTPUT_CHARSET environment variables.  */
+
+  if (old_LC_ALL != NULL)
+    setenv ("LC_ALL", old_LC_ALL, 1), free (old_LC_ALL);
+  else
+    unsetenv ("LC_ALL");
+
+  if (old_LANGUAGE != NULL)
+    setenv ("LANGUAGE", old_LANGUAGE, 1), free (old_LANGUAGE);
+  else
+    unsetenv ("LANGUAGE");
+
+  if (old_OUTPUT_CHARSET != NULL)
+    setenv ("OUTPUT_CHARSET", old_OUTPUT_CHARSET, 1), free (old_OUTPUT_CHARSET);
+  else
+    unsetenv ("OUTPUT_CHARSET");
+
+#ifdef HAVE_SETLOCALE
+  setlocale (LC_ALL, "");
+#endif
+
+  return result;
+}
+
+
+/* Perform a set of substitutions in a string and return the resulting
+   string.  When subst[j][0] found, it is replaced with subst[j][1].
+   subst[j][0] must not be the empty string.  */
+static const char *
+subst_string (str, nsubst, subst)
+     const char *str;
+     unsigned int nsubst;
+     const char *(*subst)[2];
+{
+  if (nsubst > 0)
+    {
+      char *malloced = NULL;
+      size_t *substlen;
+      size_t i;
+      unsigned int j;
+
+      substlen = (size_t *) alloca (nsubst * sizeof (size_t));
+      for (j = 0; j < nsubst; j++)
+       {
+         substlen[j] = strlen (subst[j][0]);
+         if (substlen[j] == 0)
+           abort ();
+       }
+
+      for (i = 0;;)
+       {
+         if (str[i] == '\0')
+           break;
+         for (j = 0; j < nsubst; j++)
+           if (*(str + i) == *subst[j][0]
+               && strncmp (str + i, subst[j][0], substlen[j]) == 0)
+             {
+               size_t replacement_len = strlen (subst[j][1]);
+               size_t new_len = strlen (str) - substlen[j] + replacement_len;
+               char *new_str = (char *) xmalloc (new_len + 1);
+               memcpy (new_str, str, i);
+               memcpy (new_str + i, subst[j][1], replacement_len);
+               strcpy (new_str + i + replacement_len, str + i + substlen[j]);
+               if (malloced != NULL)
+                 free (malloced);
+               str = new_str;
+               malloced = new_str;
+               i += replacement_len;
+               break;
+             }
+         if (j == nsubst)
+           i++;
+       }
+    }
+
+  return str;
+}
+
+/* Perform a set of substitutions on each string of a string list.
+   When subst[j][0] found, it is replaced with subst[j][1].  subst[j][0]
+   must not be the empty string.  */
+static void
+subst_string_list (slp, nsubst, subst)
+     string_list_ty *slp;
+     unsigned int nsubst;
+     const char *(*subst)[2];
+{
+  size_t j;
+
+  for (j = 0; j < slp->nitems; j++)
+    slp->item[j] = subst_string (slp->item[j], nsubst, subst);
+}
+
+
+/* Fill the templates in all fields of the header entry.  */
+static msgdomain_list_ty *
+fill_header (mdlp)
+     msgdomain_list_ty *mdlp;
+{
+  /* Cache the strings filled in, for use when there are multiple domains
+     and a header entry for each domain.  */
+  const char *field_value[NFIELDS];
+  size_t k, j, i;
+
+  for (i = 0; i < NFIELDS; i++)
+    field_value[i] = NULL;
+
+  for (k = 0; k < mdlp->nitems; k++)
+    {
+      message_list_ty *mlp = mdlp->item[k]->messages;
+
+      if (mlp->nitems > 0)
+       {
+         message_ty *header_mp = NULL;
+         char *header;
+         const char *subst[3][2];
+         time_t now;
+
+         /* Search the header entry.  */
+         for (j = 0; j < mlp->nitems; j++)
+           if (mlp->item[j]->msgid[0] == '\0' && !mlp->item[j]->obsolete)
+             {
+               header_mp = mlp->item[j];
+               break;
+             }
+
+         /* If it wasn't found, provide one.  */
+         if (header_mp == NULL)
+           {
+             static lex_pos_ty pos = { __FILE__, __LINE__ };
+
+             header_mp = message_alloc ("", NULL, "", 1, &pos);
+             message_list_prepend (mlp, header_mp);
+           }
+
+         header = xstrdup (header_mp->msgstr);
+
+         /* Fill in the fields.  */
+         for (i = 0; i < NFIELDS; i++)
+           {
+             if (field_value[i] == NULL)
+               field_value[i] = fields[i].getter ();
+
+             if (field_value[i] != NULL)
+               {
+                 char *old_header = header;
+                 header = put_field (header, fields[i].name, field_value[i]);
+                 free (old_header);
+               }
+           }
+
+         /* Replace the old translation in the header entry.  */
+         header_mp->msgstr = header;
+         header_mp->msgstr_len = strlen (header) + 1;
+
+         /* Update the comments in the header entry.  */
+         subst[0][0] = "SOME DESCRIPTIVE TITLE";
+         subst[0][1] = xasprintf (get_title (), project_id ());
+         subst[1][0] = "FIRST AUTHOR <EMAIL@ADDRESS>";
+         subst[1][1] = field_value[FIELD_LAST_TRANSLATOR];
+         subst[2][0] = "YEAR";
+         subst[2][1] =
+           xasprintf ("%d", (time (&now), (localtime (&now))->tm_year + 1900));
+         subst_string_list (header_mp->comment, SIZEOF (subst), subst);
+
+         /* Finally remove the fuzzy attribute.  */
+         header_mp->is_fuzzy = false;
+       }
+    }
+
+  return mdlp;
+}
diff --git a/src/project-id b/src/project-id
new file mode 100755 (executable)
index 0000000..2514a84
--- /dev/null
@@ -0,0 +1,67 @@
+#!/bin/sh
+# Prints a package's identification PACKAGE VERSION or PACKAGE.
+
+want_version="$1"
+while true; do
+  if test -f configure; then
+    package=`grep '^PACKAGE=' configure | sed -e '1q' | sed -e 's/^PACKAGE=//'`
+    case "$package" in
+      *[\"\$\`\{\}]*)
+        # Some packages (gcal) retrieve the package name dynamically.
+        package=
+        ;;
+    esac
+    if test -n "$package"; then
+      is_gnu=`LC_ALL=C grep "GNU $package" * 2>/dev/null`
+      if test -n "$is_gnu"; then
+        package="GNU $package"
+      fi
+      if test -n "$want_version"; then
+        version=`grep '^VERSION=' configure | sed -e '1q' | sed -e 's/^VERSION=//'`
+        case "$version" in
+          *[\"\$\`\{\}]*)
+            # Some packages (gcal, gcc, clisp) retrieve the version dynamically.
+            version=
+            ;;
+        esac
+        if test -n "$version"; then
+          echo "$package $version"
+        else
+          echo "$package"
+        fi
+      else
+        echo "$package"
+      fi
+      exit 0
+    fi
+  fi
+  dir=`basename \`pwd\``
+  case "$dir" in
+    i18n)
+      # This directory name, used in GNU make, is not the top level directory.
+      ;;
+    *[A-Za-z]*[0-9]*)
+      package=`echo "$dir" | sed -e 's/^\([^0-9]*\)[0-9].*$/\1/' -e 's/[-_]$//'`
+      if test -n "$want_version"; then
+        version=`echo "$dir" | sed -e 's/^[^0-9]*\([0-9].*\)$/\1/'`
+        echo "$package $version"
+      else
+        echo "$package"
+      fi
+      exit 0
+      ;;
+  esac
+  # Go to parent directory
+  last=`/bin/pwd`
+  cd ..
+  curr=`/bin/pwd`
+  if test "$last" = "$curr"; then
+    # Oops, didn't find the package name.
+    if test -n "$want_version"; then
+      echo "PACKAGE VERSION"
+    else
+      echo "PACKAGE"
+    fi
+    exit 0
+  fi
+done