summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBruno Haible <bruno@clisp.org>2001-10-25 09:38:30 +0000
committerBruno Haible <bruno@clisp.org>2001-10-25 09:38:30 +0000
commit72d14ef6b0e15298ab77229c71a732e73c741273 (patch)
tree0990acce2cf3ed576569aa37cb32aded422f519b
parent0221e6d7d6aba165d4018a3f094191feee731b6a (diff)
downloadexternal_gettext-72d14ef6b0e15298ab77229c71a732e73c741273.zip
external_gettext-72d14ef6b0e15298ab77229c71a732e73c741273.tar.gz
external_gettext-72d14ef6b0e15298ab77229c71a732e73c741273.tar.bz2
New program 'msginit'.
-rw-r--r--man/ChangeLog9
-rw-r--r--man/Makefile.am11
-rw-r--r--man/msginit.x4
-rw-r--r--src/ChangeLog14
-rw-r--r--src/Makefile.am10
-rw-r--r--src/msginit.c1563
-rwxr-xr-xsrc/project-id67
7 files changed, 1672 insertions, 6 deletions
diff --git a/man/ChangeLog b/man/ChangeLog
index e9b1e81..113e569 100644
--- a/man/ChangeLog
+++ b/man/ChangeLog
@@ -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.
diff --git a/man/Makefile.am b/man/Makefile.am
index 0d66e87..8de6601 100644
--- a/man/Makefile.am
+++ b/man/Makefile.am
@@ -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
index 0000000..126791f
--- /dev/null
+++ b/man/msginit.x
@@ -0,0 +1,4 @@
+[NAME]
+msginit \- initialize a message catalog
+[DESCRIPTION]
+.\" Add any additional description here
diff --git a/src/ChangeLog b/src/ChangeLog
index 3b6f05a..b174aab 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,5 +1,17 @@
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.
* urlget.c: New file.
* Makefile.am (noinst_PROGRAMS): Add urlget.
@@ -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.
diff --git a/src/Makefile.am b/src/Makefile.am
index ba1930a..ce0cf5b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -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
index 0000000..3296a6f
--- /dev/null
+++ b/src/msginit.c
@@ -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
index 0000000..2514a84
--- /dev/null
+++ b/src/project-id
@@ -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