From: Bruno Haible Date: Tue, 11 Dec 2001 11:37:25 +0000 (+0000) Subject: Rename msgexec to msgfilter. X-Git-Tag: v0.11~212 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4bd88cb1c4e87dc873ea1263b87d1925af7511e1;p=thirdparty%2Fgettext.git Rename msgexec to msgfilter. New program msgexec. --- diff --git a/NEWS b/NEWS index c9de033ef..9915f9d7b 100644 --- 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. diff --git a/doc/ChangeLog b/doc/ChangeLog index c433c09e6..92b7a491a 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,12 @@ +2001-12-08 Bruno Haible + + * 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 * msgen.texi: Refer to msginit as an alternative. diff --git a/doc/Makefile.am b/doc/Makefile.am index f7560291d..78bd6087c 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -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 diff --git a/doc/gettext.texi b/doc/gettext.texi index 06218bdf8..344624245 100644 --- a/doc/gettext.texi +++ b/doc/gettext.texi @@ -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 index 000000000..031c42609 --- /dev/null +++ b/doc/msgexec.texi @@ -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 diff --git a/doc/msgfilter.texi b/doc/msgfilter.texi index 2ced8d5f6..b6dc7bffa 100644 --- a/doc/msgfilter.texi +++ b/doc/msgfilter.texi @@ -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 diff --git a/man/ChangeLog b/man/ChangeLog index 113e5698e..f4244ed18 100644 --- a/man/ChangeLog +++ b/man/ChangeLog @@ -1,3 +1,13 @@ +2001-12-08 Bruno Haible + + * 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 * msginit.x: New file. diff --git a/man/Makefile.am b/man/Makefile.am index 8de6601a1..0fe1e5485 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 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 index 000000000..9a5bdb020 --- /dev/null +++ b/man/msgexec.x @@ -0,0 +1,4 @@ +[NAME] +msgexec \- process translations of message catalog +[DESCRIPTION] +.\" Add any additional description here diff --git a/man/msgfilter.x b/man/msgfilter.x index 2175e11cc..0a000afd9 100644 --- a/man/msgfilter.x +++ b/man/msgfilter.x @@ -1,4 +1,4 @@ [NAME] -msgexec \- edit translations of message catalog +msgfilter \- edit translations of message catalog [DESCRIPTION] .\" Add any additional description here diff --git a/po/ChangeLog b/po/ChangeLog index f29a22233..8d31dc20a 100644 --- a/po/ChangeLog +++ b/po/ChangeLog @@ -1,3 +1,11 @@ +2001-12-08 Bruno Haible + + * 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 * Makevars (XGETTEXT_OPTIONS): New variable. diff --git a/po/Makefile.in.in b/po/Makefile.in.in index 39305fd1a..7eeb6d549 100644 --- a/po/Makefile.in.in +++ b/po/Makefile.in.in @@ -36,7 +36,7 @@ MSGMERGE = msgmerge MSGMERGE_UPDATE = @MSGMERGE@ --update MSGINIT = msginit MSGCONV = msgconv -MSGEXEC = msgexec +MSGFILTER = msgfilter POFILES = @POFILES@ GMOFILES = @GMOFILES@ diff --git a/po/POTFILES.in b/po/POTFILES.in index 38196ef04..944ac3032 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -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 diff --git a/po/Rules-quot b/po/Rules-quot index 9324c9c76..132e94fcb 100644 --- a/po/Rules-quot +++ b/po/Rules-quot @@ -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 \ diff --git a/src/.cvsignore b/src/.cvsignore index d6c1eff73..be539d830 100644 --- a/src/.cvsignore +++ b/src/.cvsignore @@ -14,6 +14,7 @@ msgcat msgconv msgen msgexec +msgfilter msggrep msginit msguniq diff --git a/src/ChangeLog b/src/ChangeLog index db7bdea17..1dd4b4cc4 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,11 @@ +2001-12-08 Bruno Haible + + * 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 * msgexec.c (keep_header): New variable. diff --git a/src/FILES b/src/FILES index 336fad862..09a7bf657 100644 --- 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 diff --git a/src/Makefile.am b/src/Makefile.am index bf460f97f..f5610bb79 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 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 index 000000000..40f3a9009 --- /dev/null +++ b/src/msgexec.c @@ -0,0 +1,388 @@ +/* Pass translations to a subprocess. + Copyright (C) 2001 Free Software Foundation, Inc. + Written by Bruno Haible , 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 +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_UNISTD_H +# include +#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 .\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); +}