]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
Rename msgexec to msgfilter.
authorBruno Haible <bruno@clisp.org>
Tue, 11 Dec 2001 11:37:25 +0000 (11:37 +0000)
committerBruno Haible <bruno@clisp.org>
Sun, 21 Jun 2009 21:39:43 +0000 (23:39 +0200)
New program msgexec.

19 files changed:
NEWS
doc/ChangeLog
doc/Makefile.am
doc/gettext.texi
doc/msgexec.texi [new file with mode: 0644]
doc/msgfilter.texi
man/ChangeLog
man/Makefile.am
man/msgexec.x [new file with mode: 0644]
man/msgfilter.x
po/ChangeLog
po/Makefile.in.in
po/POTFILES.in
po/Rules-quot
src/.cvsignore
src/ChangeLog
src/FILES
src/Makefile.am
src/msgexec.c [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index c9de033ef8421c9610a73063cec191b99a2b441b..9915f9d7b91fd619d0cb158a09ed9a3c362d761b 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -5,7 +5,8 @@ Version 0.11 - XXX 2001
     msgcat - combines several message catalogs,
     msgconv - character set conversion for message catalog,
     msgen - create English message catalog,
-    msgexec - edit translations of message catalog,
+    msgexec - process translations of message catalog,
+    msgfilter - edit translations of message catalog,
     msggrep - pattern matching on message catalog,
     msginit - initialize a message catalog,
     msguniq - unify duplicate translations in message catalog.
index c433c09e61f97e0c55a93520602e29e9e445f85c..92b7a491a1c091469074eeea71e490cfcb88acbb 100644 (file)
@@ -1,3 +1,12 @@
+2001-12-08  Bruno Haible  <bruno@clisp.org>
+
+       * msgfilter.texi: Renamed from msgexec.texi.
+       * msgexec.texi: New file.
+       * gettext.texi (msgfilter Invocation): Renamed from msgexec Invocation.
+       (msgexec Invocation): New section.
+       (Manipulating): Update. Mention msgexec.
+       * Makefile.am (gettext_TEXINFOS): Add msgfilter.texi.
+
 2001-12-08  Bruno Haible  <bruno@clisp.org>
 
        * msgen.texi: Refer to msginit as an alternative.
index f7560291dbe2fdce155ff8644e5b5b9f44e08cb1..78bd6087cfee251dd3280e8fcaefc629c302c3da 100644 (file)
@@ -34,8 +34,8 @@ info_TEXINFOS = gettext.texi
 # List of texinfo sources @included by gettext.texi, excluding version.texi.
 gettext_TEXINFOS = \
   xgettext.texi msginit.texi msgmerge.texi msgcat.texi msgconv.texi \
-  msggrep.texi msgexec.texi msguniq.texi msgcomm.texi msgcmp.texi \
-  msgattrib.texi msgen.texi msgfmt.texi msgunfmt.texi \
+  msggrep.texi msgfilter.texi msguniq.texi msgcomm.texi msgcmp.texi \
+  msgattrib.texi msgen.texi msgexec.texi msgfmt.texi msgunfmt.texi \
   iso-639.texi iso-3166.texi
 
 EXTRA_DIST = iso-639.sed iso-3166.sed ISO_639 ISO_3166 nls.texi matrix.texi $(EXTRA_DIST_html) texi2html
index 06218bdf88e75ddd165b09bcd6d0dc8554276c2d..344624245c4f4a52259bd2c39cd4ace05edebbe2 100644 (file)
@@ -155,12 +155,13 @@ Manipulating PO Files
 * msgcat Invocation::           Invoking the @code{msgcat} Program
 * msgconv Invocation::          Invoking the @code{msgconv} Program
 * msggrep Invocation::          Invoking the @code{msggrep} Program
-* msgexec Invocation::          Invoking the @code{msgexec} Program
+* msgfilter Invocation::        Invoking the @code{msgfilter} Program
 * msguniq Invocation::          Invoking the @code{msguniq} Program
 * msgcomm Invocation::          Invoking the @code{msgcomm} Program
 * msgcmp Invocation::           Invoking the @code{msgcmp} Program
 * msgattrib Invocation::        Invoking the @code{msgattrib} Program
 * msgen Invocation::            Invoking the @code{msgen} Program
+* msgexec Invocation::          Invoking the @code{msgexec} Program
 
 Producing Binary MO Files
 
@@ -2972,18 +2973,22 @@ When a translator wants to adjust some translation catalog for a special
 dialect or orthography - for example, German as written in Switzerland
 versus German as written in Germany -, she needs to apply some text
 processing to every message in the catalog.  The tool for doing this is
-@samp{msgexec}.
+@samp{msgfilter}.
 
-Another use of @code{msgexec} is to produce approximately the POT file for
+Another use of @code{msgfilter} is to produce approximately the POT file for
 which a given PO file was made.  This can be done through a filter command
-like @samp{msgexec sed -e d | sed -e '/^# /d'}.  Note that the original
+like @samp{msgfilter sed -e d | sed -e '/^# /d'}.  Note that the original
 POT file may have had different comments and different plural message counts,
 that's why it's better to use the original POT file if available.
 
+When a translator wants to check her translations, for example according
+to orthography rules or using a non-interactive spell checker, she can do
+so using the @samp{msgexec} program.
+
 When third party tools create PO or POT files, sometimes duplicates cannot
 be avoided.  But the GNU @code{gettext} tools give an error when they
 encounter duplicate msgids in the same file and in the same domain.
-To merge duplicates, the @code{msguniq} program can be used.
+To merge duplicates, the @samp{msguniq} program can be used.
 
 @samp{msgcomm} is a more general tool for keeping or throwing away
 duplicates, occurring in different files.
@@ -3001,12 +3006,13 @@ catalogs.  It copies each message's msgid to its msgstr.
 * msgcat Invocation::           Invoking the @code{msgcat} Program
 * msgconv Invocation::          Invoking the @code{msgconv} Program
 * msggrep Invocation::          Invoking the @code{msggrep} Program
-* msgexec Invocation::          Invoking the @code{msgexec} Program
+* msgfilter Invocation::        Invoking the @code{msgfilter} Program
 * msguniq Invocation::          Invoking the @code{msguniq} Program
 * msgcomm Invocation::          Invoking the @code{msgcomm} Program
 * msgcmp Invocation::           Invoking the @code{msgcmp} Program
 * msgattrib Invocation::        Invoking the @code{msgattrib} Program
 * msgen Invocation::            Invoking the @code{msgen} Program
+* msgexec Invocation::          Invoking the @code{msgexec} Program
 @end menu
 
 @node msgcat Invocation, msgconv Invocation, Manipulating, Manipulating
@@ -3019,17 +3025,17 @@ catalogs.  It copies each message's msgid to its msgstr.
 
 @include msgconv.texi
 
-@node msggrep Invocation, msgexec Invocation, msgconv Invocation, Manipulating
+@node msggrep Invocation, msgfilter Invocation, msgconv Invocation, Manipulating
 @section Invoking the @code{msggrep} Program
 
 @include msggrep.texi
 
-@node msgexec Invocation, msguniq Invocation, msggrep Invocation, Manipulating
-@section Invoking the @code{msgexec} Program
+@node msgfilter Invocation, msguniq Invocation, msggrep Invocation, Manipulating
+@section Invoking the @code{msgfilter} Program
 
-@include msgexec.texi
+@include msgfilter.texi
 
-@node msguniq Invocation, msgcomm Invocation, msgexec Invocation, Manipulating
+@node msguniq Invocation, msgcomm Invocation, msgfilter Invocation, Manipulating
 @section Invoking the @code{msguniq} Program
 
 @include msguniq.texi
@@ -3049,11 +3055,16 @@ catalogs.  It copies each message's msgid to its msgstr.
 
 @include msgattrib.texi
 
-@node msgen Invocation,  , msgattrib Invocation, Manipulating
+@node msgen Invocation, msgexec Invocation, msgattrib Invocation, Manipulating
 @section Invoking the @code{msgen} Program
 
 @include msgen.texi
 
+@node msgexec Invocation,  , msgen Invocation, Manipulating
+@section Invoking the @code{msgexec} Program
+
+@include msgexec.texi
+
 @node Binaries, Users, Manipulating, Top
 @chapter Producing Binary MO Files
 
diff --git a/doc/msgexec.texi b/doc/msgexec.texi
new file mode 100644 (file)
index 0000000..031c426
--- /dev/null
@@ -0,0 +1,59 @@
+@example
+msgexec [@var{option}] @var{command} [@var{command-option}]
+@end example
+
+The @code{msgexec} program applies a command to all translations of a
+translation catalog.
+The @var{command} can be any program that reads a translation from standard
+input.  It is invoked once for each translation.  Its output becomes
+msgexec's output.  @code{msgexec}'s return code is the maximum return code
+across all invocations.
+
+A special builtin command called @samp{0} outputs the translation, followed
+by a null byte.  The output of @samp{msgexec 0} is suitable as input for
+@samp{xargs -0}.
+
+During each @var{command} invocation, the environment variable
+@code{MSGEXEC_MSGID} is bound to the message's msgid, and the environment
+variable @code{MSGEXEC_LOCATION} is bound to the location in the PO file
+of the message.
+
+Note: It is your responsibility to ensure that the @var{command} can cope
+with input encoded in the translation catalog's encoding.  If the
+@var{command} wants input in a particular encoding, you can in a first step
+convert the translation catalog to that encoding using the @samp{msgconv}
+program, before invoking @samp{msgexec}.  If the @var{command} wants input
+in the locale's encoding, but you want to avoid the locale's encoding, then
+you can first convert the translation catalog to UTF-8 using the
+@samp{msgconv} program and then make @samp{msgexec} work in an UTF-8
+locale, by using the @code{LC_ALL} environment variable.
+
+@subsection Input file location
+
+@table @samp
+@item -i @var{inputfile}
+@itemx --input=@var{inputfile}
+Input PO file.
+
+@item -D @var{directory}
+@itemx --directory=@var{directory}
+Add @var{directory} to the list of directories.  Source files are
+searched relative to this list of directories.  The resulting @file{.po}
+file will be written relative to the current directory, though.
+
+@end table
+
+If no @var{inputfile} is given or if it is @samp{-}, standard input is read.
+
+@subsection Informative output
+
+@table @samp
+@item -h
+@itemx --help
+Display this help and exit.
+
+@item -V
+@itemx --version
+Output version information and exit.
+
+@end table
index 2ced8d5f6d67110c25fb882f6033e8ccb8a482ad..b6dc7bffa68b4ffa3dff8654294f5b62b71359e8 100644 (file)
@@ -1,8 +1,8 @@
 @example
-msgexec [@var{option}] @var{filter} [@var{filter-option}]
+msgfilter [@var{option}] @var{filter} [@var{filter-option}]
 @end example
 
-The @code{msgexec} program applies a filter to all translations of a
+The @code{msgfilter} program applies a filter to all translations of a
 translation catalog.
 
 @subsection Input file location
index 113e5698ea0e3cc957ee790a52c99e2182c47e1f..f4244ed18cd40f10ceee388409f0b35576798d2d 100644 (file)
@@ -1,3 +1,13 @@
+2001-12-08  Bruno Haible  <bruno@clisp.org>
+
+       * msgfilter.x: Renamed from msgexec.x.
+       * msgexec.x: New file.
+       * Makefile.am (man_aux): Add msgfilter.x.
+       (man_MAN1OTHER): Add msgfilter.1.
+       (man_HTMLOTHER): Add msgfilter.1.html.
+       (msgfilter.1): New rule.
+       (msgfilter.1.html): New rule.
+
 2001-10-08  Bruno Haible  <haible@clisp.cons.org>
 
        * msginit.x: New file.
index 8de6601a1644b4a59836893d758d90db54d20fb9..0fe1e5485caeb80b0b3a1d5f7e165b89c4d48393 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 msginit.x msguniq.x
+msgattrib.x msgcat.x msgcomm.x msgconv.x msgen.x msgexec.x msgfilter.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 msginit.1 msguniq.1
+msgattrib.1 msgcat.1 msgcomm.1 msgconv.1 msgen.1 msgexec.1 msgfilter.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 msginit.1.html msguniq.1.html \
+msgattrib.1.html msgcat.1.html msgcomm.1.html msgconv.1.html msgen.1.html msgexec.1.html msgfilter.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)
@@ -120,6 +120,8 @@ msgen.1: msgen.x
        $(SHELL) x-to-1 "$(PERL)" "$(HELP2MAN)" ../src/msgen$(EXEEXT) $(srcdir)/msgen.x msgen.1
 msgexec.1: msgexec.x
        $(SHELL) x-to-1 "$(PERL)" "$(HELP2MAN)" ../src/msgexec$(EXEEXT) $(srcdir)/msgexec.x msgexec.1
+msgfilter.1: msgfilter.x
+       $(SHELL) x-to-1 "$(PERL)" "$(HELP2MAN)" ../src/msgfilter$(EXEEXT) $(srcdir)/msgfilter.x msgfilter.1
 msggrep.1: msggrep.x
        $(SHELL) x-to-1 "$(PERL)" "$(HELP2MAN)" ../src/msggrep$(EXEEXT) $(srcdir)/msggrep.x msggrep.1
 msginit.1: msginit.x
@@ -198,6 +200,9 @@ msgen.1.html: msgen.1
 msgexec.1.html: msgexec.1
        $(MAN2HTML) `if test -f msgexec.1; then echo .; else echo $(srcdir); fi`/msgexec.1 | sed -e '/CreationDate:/d' > t-$@
        mv t-$@ $@
+msgfilter.1.html: msgfilter.1
+       $(MAN2HTML) `if test -f msgfilter.1; then echo .; else echo $(srcdir); fi`/msgfilter.1 | sed -e '/CreationDate:/d' > t-$@
+       mv t-$@ $@
 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-$@ $@
diff --git a/man/msgexec.x b/man/msgexec.x
new file mode 100644 (file)
index 0000000..9a5bdb0
--- /dev/null
@@ -0,0 +1,4 @@
+[NAME]
+msgexec \- process translations of message catalog
+[DESCRIPTION]
+.\" Add any additional description here
index 2175e11cce6a5c94e94cf4e6416d7a904b62094a..0a000afd9f1125f5b09545a5954915bcf37f45a0 100644 (file)
@@ -1,4 +1,4 @@
 [NAME]
-msgexec \- edit translations of message catalog
+msgfilter \- edit translations of message catalog
 [DESCRIPTION]
 .\" Add any additional description here
index f29a22233b23776acf1e107075582a36d20648bc..8d31dc20a3eff46de181746721b1d38ed7002e58 100644 (file)
@@ -1,3 +1,11 @@
+2001-12-08  Bruno Haible  <bruno@clisp.org>
+
+       * Makefile.in.in (MSGEXEC): Remove variable.
+       (MSGFILTER): New variable.
+       * Rules-quot (.insert-header.po-update-en): Use $(MSGFILTER) instead
+       of $(MSGEXEC).
+       * POTFILES.in: Add src/msgfilter.c.
+
 2001-12-06  Bruno Haible  <bruno@clisp.org>
 
        * Makevars (XGETTEXT_OPTIONS): New variable.
index 39305fd1a6700a699349f0f94bf06fe3e4fa249c..7eeb6d5495fa538c044dd8258e2078ee3c197fdf 100644 (file)
@@ -36,7 +36,7 @@ MSGMERGE = msgmerge
 MSGMERGE_UPDATE = @MSGMERGE@ --update
 MSGINIT = msginit
 MSGCONV = msgconv
-MSGEXEC = msgexec
+MSGFILTER = msgfilter
 
 POFILES = @POFILES@
 GMOFILES = @GMOFILES@
index 38196ef0495489a2590d2ef79797c8a109381729..944ac30322bc02d41a2499eb0e76166459b13cac 100644 (file)
@@ -38,6 +38,7 @@ src/msgcomm.c
 src/msgconv.c
 src/msgen.c
 src/msgexec.c
+src/msgfilter.c
 src/msgfmt.c
 src/msggrep.c
 src/msginit.c
index 9324c9c76fdfeb5f38e945f89d697b65607de5b4..132e94fcbca023507119493f8762c746a4aa977f 100644 (file)
@@ -15,7 +15,7 @@ en@boldquot.po-update: en@boldquot.po-update-en
        ll=`echo $$lang | sed -e 's/@.*//'`; \
        LC_ALL=C; export LC_ALL; \
        cd $(srcdir); \
-       if $(MSGINIT) -i $(DOMAIN).pot --no-translator -l $$ll -o - 2>/dev/null | sed -f $$tmpdir/$$lang.insert-header | $(MSGCONV) -t UTF-8 | $(MSGEXEC) sed -f `echo $$lang | sed -e 's/.*@//'`.sed 2>/dev/null > $$tmpdir/$$lang.new.po; then \
+       if $(MSGINIT) -i $(DOMAIN).pot --no-translator -l $$ll -o - 2>/dev/null | sed -f $$tmpdir/$$lang.insert-header | $(MSGCONV) -t UTF-8 | $(MSGFILTER) sed -f `echo $$lang | sed -e 's/.*@//'`.sed 2>/dev/null > $$tmpdir/$$lang.new.po; then \
          if cmp $$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \
            rm -f $$tmpdir/$$lang.new.po; \
          else \
index d6c1eff73184cd9dc030e1af49f291691cd8e909..be539d830ce5e899106c24d221cbd614bbda535b 100644 (file)
@@ -14,6 +14,7 @@ msgcat
 msgconv
 msgen
 msgexec
+msgfilter
 msggrep
 msginit
 msguniq
index db7bdea179f77bd778248e8576c1a21c4be05442..1dd4b4cc46e91a9c9796d26e7866d4408c712a54 100644 (file)
@@ -1,3 +1,11 @@
+2001-12-08  Bruno Haible  <bruno@clisp.org>
+
+       * msgfilter.c: Renamed from msgexec.c.
+       * msgexec.c: New file.
+       * Makefile.am (bin_PROGRAMS): Add msgfilter.
+       (msgfilter_SOURCES): New variable.
+       (msgfilter_LDADD): New variable.
+
 2001-12-07  Bruno Haible  <bruno@clisp.org>
 
        * msgexec.c (keep_header): New variable.
index 336fad862a82545590ae2ccdae908a1d12dae78a..09a7bf65762c02e37f245b3a495bfdc96a004c16 100644 (file)
--- a/src/FILES
+++ b/src/FILES
@@ -111,6 +111,7 @@ msgl-charset.c
                 encoding.
 
 msgexec.c       Main source for the 'msgexec' program.
+msgfilter.c     Main source for the 'msgfilter' program.
 msggrep.c       Main source for the 'msggrep' program.
 
 +-------------- The 'msgunfmt' program
index bf460f97fa66efe7167d1ab39e89fc34f98380f5..f5610bb790279a83aaa4eecc63698cd66280c969 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 msginit msguniq
+msgattrib msgcat msgcomm msgconv msgen msgexec msgfilter msggrep msginit msguniq
 
 noinst_PROGRAMS = hostname urlget
 
@@ -89,6 +89,7 @@ msgcomm_SOURCES = msgcomm.c
 msgconv_SOURCES = msgconv.c
 msgen_SOURCES = msgen.c
 msgexec_SOURCES = msgexec.c
+msgfilter_SOURCES = msgfilter.c
 msggrep_SOURCES = msggrep.c
 msginit_SOURCES = msginit.c
 msguniq_SOURCES = msguniq.c
@@ -116,6 +117,7 @@ msgcomm_LDADD = libgettextsrc.la
 msgconv_LDADD = libgettextsrc.la
 msgen_LDADD = libgettextsrc.la
 msgexec_LDADD = libgettextsrc.la
+msgfilter_LDADD = libgettextsrc.la
 msggrep_LDADD = libgettextsrc.la
 msginit_LDADD = ../intl/localealias.$(OBJEXT) ../intl/localename.$(OBJEXT) \
                 libgettextsrc.la
diff --git a/src/msgexec.c b/src/msgexec.c
new file mode 100644 (file)
index 0000000..40f3a90
--- /dev/null
@@ -0,0 +1,388 @@
+/* Pass translations to a subprocess.
+   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 <sys/types.h>
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include "dir-list.h"
+#include "error.h"
+#include "xerror.h"
+#include "progname.h"
+#include "basename.h"
+#include "message.h"
+#include "read-po.h"
+#include "xmalloc.h"
+#include "system.h"
+#include "full-write.h"
+#include "findprog.h"
+#include "pipe.h"
+#include "wait-process.h"
+#include "setenv.h"
+#include "libgettext.h"
+
+#define _(str) gettext (str)
+
+#ifndef STDOUT_FILENO
+# define STDOUT_FILENO 1
+#endif
+
+
+/* Name of the subprogram.  */
+static const char *sub_name;
+
+/* Pathname of the subprogram.  */
+static const char *sub_path;
+
+/* Argument list for the subprogram.  */
+static char **sub_argv;
+static int sub_argc;
+
+/* Maximum exit code encountered.  */
+static int exitcode;
+
+/* Long options.  */
+static const struct option long_options[] =
+{
+  { "directory", required_argument, NULL, 'D' },
+  { "help", no_argument, NULL, 'h' },
+  { "input", required_argument, NULL, 'i' },
+  { "version", no_argument, NULL, 'V' },
+  { 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));
+#ifdef EINTR
+static inline int nonintr_close PARAMS ((int fd));
+#endif
+static void process_string PARAMS ((const message_ty *mp,
+                                   const char *str, size_t len));
+static void process_message PARAMS ((const message_ty *mp));
+static void process_message_list PARAMS ((const message_list_ty *mlp));
+static void process_msgdomain_list PARAMS ((const msgdomain_list_ty *mdlp));
+
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  int opt;
+  bool do_help;
+  bool do_version;
+  const char *input_file;
+  msgdomain_list_ty *result;
+  size_t i;
+
+  /* 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;
+  input_file = NULL;
+
+  /* The '+' in the options string causes option parsing to terminate when
+     the first non-option, i.e. the subprogram name, is encountered.  */
+  while ((opt = getopt_long (argc, argv, "+D:hi:V", long_options, NULL)) != EOF)
+    switch (opt)
+      {
+      case '\0':               /* Long option.  */
+       break;
+
+      case 'D':
+       dir_list_append (optarg);
+       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 'V':
+       do_version = true;
+       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 the subprogram name.  */
+  if (optind == argc)
+    error (EXIT_FAILURE, 0, _("missing command name"));
+  sub_name = argv[optind];
+
+  /* Build argument list for the program.  */
+  sub_argc = argc - optind;
+  sub_argv = (char **) xmalloc ((sub_argc + 1) * sizeof (char *));
+  for (i = 0; i < sub_argc; i++)
+    sub_argv[i] = argv[optind + i];
+  sub_argv[i] = NULL;
+
+  /* By default, input comes from standard input.  */
+  if (input_file == NULL)
+    input_file = "-";
+
+  /* Read input file.  */
+  result = read_po_file (input_file);
+
+  if (strcmp (sub_name, "0") != 0)
+    {
+      /* Attempt to locate the program.
+        This is an optimization, to avoid that spawn/exec searches the PATH
+        on every call.  */
+      sub_path = find_in_path (sub_name);
+
+      /* Finish argument list for the program.  */
+      sub_argv[0] = (char *) sub_path;
+    }
+
+  exitcode = 0; /* = EXIT_SUCCESS */
+
+  /* Apply the subprogram.  */
+  process_msgdomain_list (result);
+
+  exit (exitcode);
+}
+
+
+/* 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] COMMAND [COMMAND-OPTION]\n\
+"), program_name);
+      printf ("\n");
+      /* xgettext: no-wrap */
+      printf (_("\
+Applies a command to all translations of a translation catalog.\n\
+The COMMAND can be any program that reads a translation from standard\n\
+input.  It is invoked once for each translation.  Its output becomes\n\
+msgexec's output.  msgexec's return code is the maximum return code\n\
+across all invocations.\n\
+"));
+      printf ("\n");
+      /* xgettext: no-wrap */
+      printf (_("\
+A special builtin command called '0' outputs the translation, followed by a\n\
+null byte.  The output of \"msgexec 0\" is suitable as input for \"xargs -0\".\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 PO file\n\
+  -D, --directory=DIRECTORY   add DIRECTORY to list for input files search\n\
+If no input file is given or if it is -, standard input is read.\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\
+"));
+      printf ("\n");
+      fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
+            stdout);
+    }
+
+  exit (status);
+}
+
+
+#ifdef EINTR
+
+/* EINTR handling for close().
+   These functions can return -1/EINTR even though we don't have any
+   signal handlers set up, namely when we get interrupted via SIGSTOP.  */
+
+static inline int
+nonintr_close (fd)
+     int fd;
+{
+  int retval;
+
+  do
+    retval = close (fd);
+  while (retval < 0 && errno == EINTR);
+
+  return retval;
+}
+#define close nonintr_close
+
+#endif
+
+
+/* Pipe a string STR of size LEN bytes to the subprogram.
+   The byte after STR is known to be a '\0' byte.  */
+static void
+process_string (mp, str, len)
+     const message_ty *mp;
+     const char *str;
+     size_t len;
+{
+  ssize_t nwritten;
+
+  if (strcmp (sub_name, "0") == 0)
+    {
+      /* Built-in command "0".  */
+      nwritten = full_write (STDOUT_FILENO, str, len + 1);
+      if (nwritten != (ssize_t) (len + 1))
+       error (EXIT_FAILURE, errno, _("write to stdout failed"));
+    }
+  else
+    {
+      /* General command.  */
+      char *location;
+      pid_t child;
+      int fd[1];
+      int exitstatus;
+
+      /* Set environment variables for the subprocess.  */
+      setenv ("MSGEXEC_MSGID", mp->msgid, 1);
+      location = xasprintf ("%s:%ld", mp->pos.file_name,
+                           (long) mp->pos.line_number);
+      setenv ("MSGEXEC_LOCATION", location, 1);
+      free (location);
+
+      /* Open a pipe to a subprocess.  */
+      child = create_pipe_out (sub_name, sub_path, sub_argv, NULL, false, true,
+                              fd);
+
+      nwritten = full_write (fd[0], str, len);
+      if (nwritten != (ssize_t) len)
+       error (EXIT_FAILURE, errno,
+              _("write to %s subprocess failed"), sub_name);
+
+      close (fd[0]);
+
+      /* Remove zombie process from process list, and retrieve exit status.  */
+      exitstatus = wait_subprocess (child, sub_name, true);
+      if (exitcode < exitstatus)
+       exitcode = exitstatus;
+    }
+}
+
+
+static void
+process_message (mp)
+     const message_ty *mp;
+{
+  const char *msgstr = mp->msgstr;
+  size_t msgstr_len = mp->msgstr_len;
+  const char *p;
+
+  /* Process each NUL delimited substring separately.  */
+  for (p = msgstr; p < msgstr + msgstr_len; )
+    {
+      size_t length = strlen (p);
+
+      process_string (mp, p, length);
+
+      p += length + 1;
+    }
+}
+
+
+static void
+process_message_list (mlp)
+     const message_list_ty *mlp;
+{
+  size_t j;
+
+  for (j = 0; j < mlp->nitems; j++)
+    process_message (mlp->item[j]);
+}
+
+
+static void
+process_msgdomain_list (mdlp)
+     const msgdomain_list_ty *mdlp;
+{
+  size_t k;
+
+  for (k = 0; k < mdlp->nitems; k++)
+    process_message_list (mdlp->item[k]->messages);
+}