1 /* Pass translations to a subprocess.
2 Copyright (C) 2001-2012 Free Software Foundation, Inc.
3 Written by Bruno Haible <haible@clisp.cons.org>, 2001.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
31 #include <sys/types.h>
37 #include "xvasprintf.h"
38 #include "error-progname.h"
40 #include "relocatable.h"
43 #include "read-catalog.h"
45 #include "read-properties.h"
46 #include "read-stringtable.h"
47 #include "msgl-charset.h"
49 #include "full-write.h"
51 #include "spawn-pipe.h"
52 #include "wait-process.h"
54 #include "propername.h"
57 #define _(str) gettext (str)
60 # define STDOUT_FILENO 1
64 /* Name of the subprogram. */
65 static const char *sub_name;
67 /* Pathname of the subprogram. */
68 static const char *sub_path;
70 /* Argument list for the subprogram. */
71 static char **sub_argv;
74 /* Maximum exit code encountered. */
78 static const struct option long_options[] =
80 { "directory", required_argument, NULL, 'D' },
81 { "help", no_argument, NULL, 'h' },
82 { "input", required_argument, NULL, 'i' },
83 { "properties-input", no_argument, NULL, 'P' },
84 { "stringtable-input", no_argument, NULL, CHAR_MAX + 1 },
85 { "version", no_argument, NULL, 'V' },
90 /* Forward declaration of local functions. */
91 static void usage (int status)
92 #if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
93 __attribute__ ((noreturn))
96 static void process_msgdomain_list (const msgdomain_list_ty *mdlp);
100 main (int argc, char **argv)
105 const char *input_file;
106 msgdomain_list_ty *result;
107 catalog_input_format_ty input_syntax = &input_format_po;
110 /* Set program name for messages. */
111 set_program_name (argv[0]);
112 error_print_progname = maybe_print_progname;
114 #ifdef HAVE_SETLOCALE
115 /* Set locale via LC_ALL. */
116 setlocale (LC_ALL, "");
119 /* Set the text message domain. */
120 bindtextdomain (PACKAGE, relocate (LOCALEDIR));
121 bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR));
122 textdomain (PACKAGE);
124 /* Ensure that write errors on stdout are detected. */
125 atexit (close_stdout);
127 /* Set default values for variables. */
132 /* The '+' in the options string causes option parsing to terminate when
133 the first non-option, i.e. the subprogram name, is encountered. */
134 while ((opt = getopt_long (argc, argv, "+D:hi:PV", long_options, NULL))
138 case '\0': /* Long option. */
142 dir_list_append (optarg);
150 if (input_file != NULL)
152 error (EXIT_SUCCESS, 0, _("at most one input file allowed"));
153 usage (EXIT_FAILURE);
159 input_syntax = &input_format_properties;
166 case CHAR_MAX + 1: /* --stringtable-input */
167 input_syntax = &input_format_stringtable;
171 usage (EXIT_FAILURE);
175 /* Version information is requested. */
178 printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
179 /* xgettext: no-wrap */
180 printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
181 License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
182 This is free software: you are free to change and redistribute it.\n\
183 There is NO WARRANTY, to the extent permitted by law.\n\
186 printf (_("Written by %s.\n"), proper_name ("Bruno Haible"));
190 /* Help is requested. */
192 usage (EXIT_SUCCESS);
194 /* Test for the subprogram name. */
196 error (EXIT_FAILURE, 0, _("missing command name"));
197 sub_name = argv[optind];
199 /* Build argument list for the program. */
200 sub_argc = argc - optind;
201 sub_argv = XNMALLOC (sub_argc + 1, char *);
202 for (i = 0; i < sub_argc; i++)
203 sub_argv[i] = argv[optind + i];
206 /* By default, input comes from standard input. */
207 if (input_file == NULL)
210 /* Read input file. */
211 result = read_catalog_file (input_file, input_syntax);
213 if (strcmp (sub_name, "0") != 0)
215 /* Warn if the current locale is not suitable for this PO file. */
216 compare_po_locale_charsets (result);
218 /* Block SIGPIPE for this process and for the subprocesses.
219 The subprogram may have side effects (additionally to producing some
220 output), therefore if there are no readers on stdout, processing of the
221 strings must continue nevertheless. */
223 sigset_t sigpipe_set;
225 sigemptyset (&sigpipe_set);
226 sigaddset (&sigpipe_set, SIGPIPE);
227 sigprocmask (SIG_UNBLOCK, &sigpipe_set, NULL);
230 /* Attempt to locate the program.
231 This is an optimization, to avoid that spawn/exec searches the PATH
233 sub_path = find_in_path (sub_name);
235 /* Finish argument list for the program. */
236 sub_argv[0] = (char *) sub_path;
239 exitcode = 0; /* = EXIT_SUCCESS */
241 /* Apply the subprogram. */
242 process_msgdomain_list (result);
248 /* Display usage information and exit. */
252 if (status != EXIT_SUCCESS)
253 fprintf (stderr, _("Try '%s --help' for more information.\n"),
258 Usage: %s [OPTION] COMMAND [COMMAND-OPTION]\n\
261 /* xgettext: no-wrap */
263 Applies a command to all translations of a translation catalog.\n\
264 The COMMAND can be any program that reads a translation from standard\n\
265 input. It is invoked once for each translation. Its output becomes\n\
266 msgexec's output. msgexec's return code is the maximum return code\n\
267 across all invocations.\n\
270 /* xgettext: no-wrap */
272 A special builtin command called '0' outputs the translation, followed by a\n\
273 null byte. The output of \"msgexec 0\" is suitable as input for \"xargs -0\".\n\
277 Mandatory arguments to long options are mandatory for short options too.\n"));
280 Input file location:\n"));
282 -i, --input=INPUTFILE input PO file\n"));
284 -D, --directory=DIRECTORY add DIRECTORY to list for input files search\n"));
286 If no input file is given or if it is -, standard input is read.\n"));
289 Input file syntax:\n"));
291 -P, --properties-input input file is in Java .properties syntax\n"));
293 --stringtable-input input file is in NeXTstep/GNUstep .strings syntax\n"));
296 Informative output:\n"));
298 -h, --help display this help and exit\n"));
300 -V, --version output version information and exit\n"));
302 /* TRANSLATORS: The placeholder indicates the bug-reporting address
303 for this package. Please add _another line_ saying
304 "Report translation bugs to <...>\n" with the address for translation
305 bugs (typically your translation team's web or email address). */
306 fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
316 /* EINTR handling for close().
317 These functions can return -1/EINTR even though we don't have any
318 signal handlers set up, namely when we get interrupted via SIGSTOP. */
321 nonintr_close (int fd)
327 while (retval < 0 && errno == EINTR);
331 #define close nonintr_close
336 /* Pipe a string STR of size LEN bytes to the subprogram.
337 The byte after STR is known to be a '\0' byte. */
339 process_string (const message_ty *mp, const char *str, size_t len)
341 if (strcmp (sub_name, "0") == 0)
343 /* Built-in command "0". */
344 if (full_write (STDOUT_FILENO, str, len + 1) < len + 1)
345 error (EXIT_FAILURE, errno, _("write to stdout failed"));
349 /* General command. */
353 void (*orig_sigpipe_handler)(int);
356 /* Set environment variables for the subprocess.
357 Note: These environment variables, especially MSGEXEC_MSGCTXT and
358 MSGEXEC_MSGCTXT, may contain non-ASCII characters. The subprocess
359 may not interpret these values correctly if the locale encoding is
360 different from the PO file's encoding. We want about this situation,
362 On Unix, this problem is often harmless. On Windows, however, - both
363 native Windows and Cygwin - the values of environment variables *must*
364 be in the encoding that is the value of GetACP(), because the system
365 may convert the environment from char** to wchar_t** before spawning
366 the subprocess and back from wchar_t** to char** in the subprocess,
367 and it does so using the GetACP() codepage. */
368 if (mp->msgctxt != NULL)
369 xsetenv ("MSGEXEC_MSGCTXT", mp->msgctxt, 1);
371 unsetenv ("MSGEXEC_MSGCTXT");
372 xsetenv ("MSGEXEC_MSGID", mp->msgid, 1);
373 location = xasprintf ("%s:%ld", mp->pos.file_name,
374 (long) mp->pos.line_number);
375 xsetenv ("MSGEXEC_LOCATION", location, 1);
378 /* Open a pipe to a subprocess. */
379 child = create_pipe_out (sub_name, sub_path, sub_argv, NULL, false, true,
382 /* Ignore SIGPIPE here. We don't care if the subprocesses terminates
383 successfully without having read all of the input that we feed it. */
384 orig_sigpipe_handler = signal (SIGPIPE, SIG_IGN);
386 if (full_write (fd[0], str, len) < len)
388 error (EXIT_FAILURE, errno,
389 _("write to %s subprocess failed"), sub_name);
393 signal (SIGPIPE, orig_sigpipe_handler);
395 /* Remove zombie process from process list, and retrieve exit status. */
396 /* FIXME: Should ignore_sigpipe be set to true here? It depends on the
397 semantics of the subprogram... */
399 wait_subprocess (child, sub_name, false, false, true, true, NULL);
400 if (exitcode < exitstatus)
401 exitcode = exitstatus;
407 process_message (const message_ty *mp)
409 const char *msgstr = mp->msgstr;
410 size_t msgstr_len = mp->msgstr_len;
413 /* Process each NUL delimited substring separately. */
414 for (p = msgstr; p < msgstr + msgstr_len; )
416 size_t length = strlen (p);
418 process_string (mp, p, length);
426 process_message_list (const message_list_ty *mlp)
430 for (j = 0; j < mlp->nitems; j++)
431 process_message (mlp->item[j]);
436 process_msgdomain_list (const msgdomain_list_ty *mdlp)
440 for (k = 0; k < mdlp->nitems; k++)
441 process_message_list (mdlp->item[k]->messages);