New program msgexec.
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.
+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.
# 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
* 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
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.
* 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
@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
@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
--- /dev/null
+@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
@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
+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.
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.
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
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)
$(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
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-$@ $@
--- /dev/null
+[NAME]
+msgexec \- process translations of message catalog
+[DESCRIPTION]
+.\" Add any additional description here
[NAME]
-msgexec \- edit translations of message catalog
+msgfilter \- edit translations of message catalog
[DESCRIPTION]
.\" Add any additional description here
+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.
MSGMERGE_UPDATE = @MSGMERGE@ --update
MSGINIT = msginit
MSGCONV = msgconv
-MSGEXEC = msgexec
+MSGFILTER = msgfilter
POFILES = @POFILES@
GMOFILES = @GMOFILES@
src/msgconv.c
src/msgen.c
src/msgexec.c
+src/msgfilter.c
src/msgfmt.c
src/msggrep.c
src/msginit.c
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 \
msgconv
msgen
msgexec
+msgfilter
msggrep
msginit
msguniq
+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.
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
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
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
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
--- /dev/null
+/* 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);
+}