Imported Upstream version 0.19.7
[platform/upstream/gettext.git] / gettext-tools / src / msgexec.c
1 /* Pass translations to a subprocess.
2    Copyright (C) 2001-2015 Free Software Foundation, Inc.
3    Written by Bruno Haible <haible@clisp.cons.org>, 2001.
4
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.
9
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.
14
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/>.  */
17
18
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22
23 #include <errno.h>
24 #include <getopt.h>
25 #include <limits.h>
26 #include <locale.h>
27 #include <signal.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/types.h>
32 #include <unistd.h>
33
34 #include "closeout.h"
35 #include "dir-list.h"
36 #include "error.h"
37 #include "xvasprintf.h"
38 #include "error-progname.h"
39 #include "progname.h"
40 #include "relocatable.h"
41 #include "basename.h"
42 #include "message.h"
43 #include "read-catalog.h"
44 #include "read-po.h"
45 #include "read-properties.h"
46 #include "read-stringtable.h"
47 #include "msgl-charset.h"
48 #include "xalloc.h"
49 #include "full-write.h"
50 #include "findprog.h"
51 #include "spawn-pipe.h"
52 #include "wait-process.h"
53 #include "xsetenv.h"
54 #include "propername.h"
55 #include "gettext.h"
56
57 #define _(str) gettext (str)
58
59 #ifndef STDOUT_FILENO
60 # define STDOUT_FILENO 1
61 #endif
62
63
64 /* Name of the subprogram.  */
65 static const char *sub_name;
66
67 /* Pathname of the subprogram.  */
68 static const char *sub_path;
69
70 /* Argument list for the subprogram.  */
71 static char **sub_argv;
72 static int sub_argc;
73
74 static bool newline;
75
76 /* Maximum exit code encountered.  */
77 static int exitcode;
78
79 /* Long options.  */
80 static const struct option long_options[] =
81 {
82   { "directory", required_argument, NULL, 'D' },
83   { "help", no_argument, NULL, 'h' },
84   { "input", required_argument, NULL, 'i' },
85   { "newline", no_argument, NULL, CHAR_MAX + 2 },
86   { "properties-input", no_argument, NULL, 'P' },
87   { "stringtable-input", no_argument, NULL, CHAR_MAX + 1 },
88   { "version", no_argument, NULL, 'V' },
89   { NULL, 0, NULL, 0 }
90 };
91
92
93 /* Forward declaration of local functions.  */
94 static void usage (int status)
95 #if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
96         __attribute__ ((noreturn))
97 #endif
98 ;
99 static void process_msgdomain_list (const msgdomain_list_ty *mdlp);
100
101
102 int
103 main (int argc, char **argv)
104 {
105   int opt;
106   bool do_help;
107   bool do_version;
108   const char *input_file;
109   msgdomain_list_ty *result;
110   catalog_input_format_ty input_syntax = &input_format_po;
111   size_t i;
112
113   /* Set program name for messages.  */
114   set_program_name (argv[0]);
115   error_print_progname = maybe_print_progname;
116
117 #ifdef HAVE_SETLOCALE
118   /* Set locale via LC_ALL.  */
119   setlocale (LC_ALL, "");
120 #endif
121
122   /* Set the text message domain.  */
123   bindtextdomain (PACKAGE, relocate (LOCALEDIR));
124   bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR));
125   textdomain (PACKAGE);
126
127   /* Ensure that write errors on stdout are detected.  */
128   atexit (close_stdout);
129
130   /* Set default values for variables.  */
131   do_help = false;
132   do_version = false;
133   input_file = NULL;
134
135   /* The '+' in the options string causes option parsing to terminate when
136      the first non-option, i.e. the subprogram name, is encountered.  */
137   while ((opt = getopt_long (argc, argv, "+D:hi:PV", long_options, NULL))
138          != EOF)
139     switch (opt)
140       {
141       case '\0':                /* Long option.  */
142         break;
143
144       case 'D':
145         dir_list_append (optarg);
146         break;
147
148       case 'h':
149         do_help = true;
150         break;
151
152       case 'i':
153         if (input_file != NULL)
154           {
155             error (EXIT_SUCCESS, 0, _("at most one input file allowed"));
156             usage (EXIT_FAILURE);
157           }
158         input_file = optarg;
159         break;
160
161       case 'P':
162         input_syntax = &input_format_properties;
163         break;
164
165       case 'V':
166         do_version = true;
167         break;
168
169       case CHAR_MAX + 1: /* --stringtable-input */
170         input_syntax = &input_format_stringtable;
171         break;
172
173       case CHAR_MAX + 2: /* --newline */
174         newline = true;
175         break;
176
177       default:
178         usage (EXIT_FAILURE);
179         break;
180       }
181
182   /* Version information is requested.  */
183   if (do_version)
184     {
185       printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
186       /* xgettext: no-wrap */
187       printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
188 License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
189 This is free software: you are free to change and redistribute it.\n\
190 There is NO WARRANTY, to the extent permitted by law.\n\
191 "),
192               "2001-2010");
193       printf (_("Written by %s.\n"), proper_name ("Bruno Haible"));
194       exit (EXIT_SUCCESS);
195     }
196
197   /* Help is requested.  */
198   if (do_help)
199     usage (EXIT_SUCCESS);
200
201   /* Test for the subprogram name.  */
202   if (optind == argc)
203     error (EXIT_FAILURE, 0, _("missing command name"));
204   sub_name = argv[optind];
205
206   /* Build argument list for the program.  */
207   sub_argc = argc - optind;
208   sub_argv = XNMALLOC (sub_argc + 1, char *);
209   for (i = 0; i < sub_argc; i++)
210     sub_argv[i] = argv[optind + i];
211   sub_argv[i] = NULL;
212
213   /* By default, input comes from standard input.  */
214   if (input_file == NULL)
215     input_file = "-";
216
217   /* Read input file.  */
218   result = read_catalog_file (input_file, input_syntax);
219
220   if (strcmp (sub_name, "0") != 0)
221     {
222       /* Warn if the current locale is not suitable for this PO file.  */
223       compare_po_locale_charsets (result);
224
225       /* Block SIGPIPE for this process and for the subprocesses.
226          The subprogram may have side effects (additionally to producing some
227          output), therefore if there are no readers on stdout, processing of the
228          strings must continue nevertheless.  */
229       {
230         sigset_t sigpipe_set;
231
232         sigemptyset (&sigpipe_set);
233         sigaddset (&sigpipe_set, SIGPIPE);
234         sigprocmask (SIG_UNBLOCK, &sigpipe_set, NULL);
235       }
236
237       /* Attempt to locate the program.
238          This is an optimization, to avoid that spawn/exec searches the PATH
239          on every call.  */
240       sub_path = find_in_path (sub_name);
241
242       /* Finish argument list for the program.  */
243       sub_argv[0] = (char *) sub_path;
244     }
245
246   exitcode = 0; /* = EXIT_SUCCESS */
247
248   /* Apply the subprogram.  */
249   process_msgdomain_list (result);
250
251   exit (exitcode);
252 }
253
254
255 /* Display usage information and exit.  */
256 static void
257 usage (int status)
258 {
259   if (status != EXIT_SUCCESS)
260     fprintf (stderr, _("Try '%s --help' for more information.\n"),
261              program_name);
262   else
263     {
264       printf (_("\
265 Usage: %s [OPTION] COMMAND [COMMAND-OPTION]\n\
266 "), program_name);
267       printf ("\n");
268       /* xgettext: no-wrap */
269       printf (_("\
270 Applies a command to all translations of a translation catalog.\n\
271 The COMMAND can be any program that reads a translation from standard\n\
272 input.  It is invoked once for each translation.  Its output becomes\n\
273 msgexec's output.  msgexec's return code is the maximum return code\n\
274 across all invocations.\n\
275 "));
276       printf ("\n");
277       /* xgettext: no-wrap */
278       printf (_("\
279 A special builtin command called '0' outputs the translation, followed by a\n\
280 null byte.  The output of \"msgexec 0\" is suitable as input for \"xargs -0\".\n\
281 "));
282       printf ("\n");
283       printf (_("\
284 Command input:\n"));
285       printf (_("\
286   --newline                   add newline at the end of input\n"));
287       printf ("\n");
288       printf (_("\
289 Mandatory arguments to long options are mandatory for short options too.\n"));
290       printf ("\n");
291       printf (_("\
292 Input file location:\n"));
293       printf (_("\
294   -i, --input=INPUTFILE       input PO file\n"));
295       printf (_("\
296   -D, --directory=DIRECTORY   add DIRECTORY to list for input files search\n"));
297       printf (_("\
298 If no input file is given or if it is -, standard input is read.\n"));
299       printf ("\n");
300       printf (_("\
301 Input file syntax:\n"));
302       printf (_("\
303   -P, --properties-input      input file is in Java .properties syntax\n"));
304       printf (_("\
305       --stringtable-input     input file is in NeXTstep/GNUstep .strings syntax\n"));
306       printf ("\n");
307       printf (_("\
308 Informative output:\n"));
309       printf (_("\
310   -h, --help                  display this help and exit\n"));
311       printf (_("\
312   -V, --version               output version information and exit\n"));
313       printf ("\n");
314       /* TRANSLATORS: The placeholder indicates the bug-reporting address
315          for this package.  Please add _another line_ saying
316          "Report translation bugs to <...>\n" with the address for translation
317          bugs (typically your translation team's web or email address).  */
318       fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
319              stdout);
320     }
321
322   exit (status);
323 }
324
325
326 #ifdef EINTR
327
328 /* EINTR handling for close().
329    These functions can return -1/EINTR even though we don't have any
330    signal handlers set up, namely when we get interrupted via SIGSTOP.  */
331
332 static inline int
333 nonintr_close (int fd)
334 {
335   int retval;
336
337   do
338     retval = close (fd);
339   while (retval < 0 && errno == EINTR);
340
341   return retval;
342 }
343 #define close nonintr_close
344
345 #endif
346
347
348 /* Pipe a string STR of size LEN bytes to the subprogram.
349    The byte after STR is known to be a '\0' byte.  */
350 static void
351 process_string (const message_ty *mp, const char *str, size_t len)
352 {
353   if (strcmp (sub_name, "0") == 0)
354     {
355       /* Built-in command "0".  */
356       if (full_write (STDOUT_FILENO, str, len + 1) < len + 1)
357         error (EXIT_FAILURE, errno, _("write to stdout failed"));
358     }
359   else
360     {
361       /* General command.  */
362       char *location;
363       pid_t child;
364       int fd[1];
365       void (*orig_sigpipe_handler)(int);
366       int exitstatus;
367       char *newstr;
368
369       /* Set environment variables for the subprocess.
370          Note: These environment variables, especially MSGEXEC_MSGCTXT and
371          MSGEXEC_MSGID, may contain non-ASCII characters.  The subprocess
372          may not interpret these values correctly if the locale encoding is
373          different from the PO file's encoding.  We want about this situation,
374          above.
375          On Unix, this problem is often harmless.  On Windows, however, - both
376          native Windows and Cygwin - the values of environment variables *must*
377          be in the encoding that is the value of GetACP(), because the system
378          may convert the environment from char** to wchar_t** before spawning
379          the subprocess and back from wchar_t** to char** in the subprocess,
380          and it does so using the GetACP() codepage.  */
381       if (mp->msgctxt != NULL)
382         xsetenv ("MSGEXEC_MSGCTXT", mp->msgctxt, 1);
383       else
384         unsetenv ("MSGEXEC_MSGCTXT");
385       xsetenv ("MSGEXEC_MSGID", mp->msgid, 1);
386       if (mp->msgid_plural != NULL)
387         xsetenv ("MSGEXEC_MSGID_PLURAL", mp->msgid_plural, 1);
388       else
389         unsetenv ("MSGEXEC_MSGID_PLURAL");
390       location = xasprintf ("%s:%ld", mp->pos.file_name,
391                             (long) mp->pos.line_number);
392       xsetenv ("MSGEXEC_LOCATION", location, 1);
393       free (location);
394       if (mp->prev_msgctxt != NULL)
395         xsetenv ("MSGEXEC_PREV_MSGCTXT", mp->prev_msgctxt, 1);
396       else
397         unsetenv ("MSGEXEC_PREV_MSGCTXT");
398       if (mp->prev_msgid != NULL)
399         xsetenv ("MSGEXEC_PREV_MSGID", mp->prev_msgid, 1);
400       else
401         unsetenv ("MSGEXEC_PREV_MSGID");
402       if (mp->prev_msgid_plural != NULL)
403         xsetenv ("MSGEXEC_PREV_MSGID_PLURAL", mp->prev_msgid_plural, 1);
404       else
405         unsetenv ("MSGEXEC_PREV_MSGID_PLURAL");
406
407       /* Open a pipe to a subprocess.  */
408       child = create_pipe_out (sub_name, sub_path, sub_argv, NULL, false, true,
409                                true, fd);
410
411       /* Ignore SIGPIPE here.  We don't care if the subprocesses terminates
412          successfully without having read all of the input that we feed it.  */
413       orig_sigpipe_handler = signal (SIGPIPE, SIG_IGN);
414
415       if (newline)
416         {
417           newstr = XNMALLOC (len + 1, char);
418           memcpy (newstr, str, len);
419           newstr[len++] = '\n';
420         }
421       else
422         newstr = (char *) str;
423
424       if (full_write (fd[0], newstr, len) < len)
425         if (errno != EPIPE)
426           error (EXIT_FAILURE, errno,
427                  _("write to %s subprocess failed"), sub_name);
428
429       if (newstr != str)
430         free (newstr);
431
432       close (fd[0]);
433
434       signal (SIGPIPE, orig_sigpipe_handler);
435
436       /* Remove zombie process from process list, and retrieve exit status.  */
437       /* FIXME: Should ignore_sigpipe be set to true here? It depends on the
438          semantics of the subprogram...  */
439       exitstatus =
440         wait_subprocess (child, sub_name, false, false, true, true, NULL);
441       if (exitcode < exitstatus)
442         exitcode = exitstatus;
443     }
444 }
445
446
447 static void
448 process_message (const message_ty *mp)
449 {
450   const char *msgstr = mp->msgstr;
451   size_t msgstr_len = mp->msgstr_len;
452   const char *p;
453   size_t k;
454
455   /* Process each NUL delimited substring separately.  */
456   for (p = msgstr, k = 0; p < msgstr + msgstr_len; k++)
457     {
458       size_t length = strlen (p);
459
460       if (mp->msgid_plural != NULL)
461         {
462           char *plural_form_string = xasprintf ("%zu", k);
463
464           xsetenv ("MSGEXEC_PLURAL_FORM", plural_form_string, 1);
465           free (plural_form_string);
466         }
467       else
468         unsetenv ("MSGEXEC_PLURAL_FORM");
469       process_string (mp, p, length);
470
471       p += length + 1;
472     }
473 }
474
475
476 static void
477 process_message_list (const message_list_ty *mlp)
478 {
479   size_t j;
480
481   for (j = 0; j < mlp->nitems; j++)
482     process_message (mlp->item[j]);
483 }
484
485
486 static void
487 process_msgdomain_list (const msgdomain_list_ty *mdlp)
488 {
489   size_t k;
490
491   for (k = 0; k < mdlp->nitems; k++)
492     process_message_list (mdlp->item[k]->messages);
493 }