• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/timemachine/gettext-0.17/gettext-tools/src/
1/* Pass translations to a subprocess.
2   Copyright (C) 2001-2007 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 <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <sys/types.h>
31#include <unistd.h>
32
33#include "closeout.h"
34#include "dir-list.h"
35#include "error.h"
36#include "xvasprintf.h"
37#include "error-progname.h"
38#include "progname.h"
39#include "relocatable.h"
40#include "basename.h"
41#include "message.h"
42#include "read-catalog.h"
43#include "read-po.h"
44#include "read-properties.h"
45#include "read-stringtable.h"
46#include "xalloc.h"
47#include "full-write.h"
48#include "findprog.h"
49#include "pipe.h"
50#include "wait-process.h"
51#include "xsetenv.h"
52#include "propername.h"
53#include "gettext.h"
54
55#define _(str) gettext (str)
56
57#ifndef STDOUT_FILENO
58# define STDOUT_FILENO 1
59#endif
60
61
62/* Name of the subprogram.  */
63static const char *sub_name;
64
65/* Pathname of the subprogram.  */
66static const char *sub_path;
67
68/* Argument list for the subprogram.  */
69static char **sub_argv;
70static int sub_argc;
71
72/* Maximum exit code encountered.  */
73static int exitcode;
74
75/* Long options.  */
76static const struct option long_options[] =
77{
78  { "directory", required_argument, NULL, 'D' },
79  { "help", no_argument, NULL, 'h' },
80  { "input", required_argument, NULL, 'i' },
81  { "properties-input", no_argument, NULL, 'P' },
82  { "stringtable-input", no_argument, NULL, CHAR_MAX + 1 },
83  { "version", no_argument, NULL, 'V' },
84  { NULL, 0, NULL, 0 }
85};
86
87
88/* Forward declaration of local functions.  */
89static void usage (int status)
90#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
91	__attribute__ ((noreturn))
92#endif
93;
94static void process_msgdomain_list (const msgdomain_list_ty *mdlp);
95
96
97int
98main (int argc, char **argv)
99{
100  int opt;
101  bool do_help;
102  bool do_version;
103  const char *input_file;
104  msgdomain_list_ty *result;
105  catalog_input_format_ty input_syntax = &input_format_po;
106  size_t i;
107
108  /* Set program name for messages.  */
109  set_program_name (argv[0]);
110  error_print_progname = maybe_print_progname;
111
112#ifdef HAVE_SETLOCALE
113  /* Set locale via LC_ALL.  */
114  setlocale (LC_ALL, "");
115#endif
116
117  /* Set the text message domain.  */
118  bindtextdomain (PACKAGE, relocate (LOCALEDIR));
119  bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR));
120  textdomain (PACKAGE);
121
122  /* Ensure that write errors on stdout are detected.  */
123  atexit (close_stdout);
124
125  /* Set default values for variables.  */
126  do_help = false;
127  do_version = false;
128  input_file = NULL;
129
130  /* The '+' in the options string causes option parsing to terminate when
131     the first non-option, i.e. the subprogram name, is encountered.  */
132  while ((opt = getopt_long (argc, argv, "+D:hi:PV", long_options, NULL))
133	 != EOF)
134    switch (opt)
135      {
136      case '\0':		/* Long option.  */
137	break;
138
139      case 'D':
140	dir_list_append (optarg);
141	break;
142
143      case 'h':
144	do_help = true;
145	break;
146
147      case 'i':
148	if (input_file != NULL)
149	  {
150	    error (EXIT_SUCCESS, 0, _("at most one input file allowed"));
151	    usage (EXIT_FAILURE);
152	  }
153	input_file = optarg;
154	break;
155
156      case 'P':
157	input_syntax = &input_format_properties;
158	break;
159
160      case 'V':
161	do_version = true;
162	break;
163
164      case CHAR_MAX + 1: /* --stringtable-input */
165	input_syntax = &input_format_stringtable;
166	break;
167
168      default:
169	usage (EXIT_FAILURE);
170	break;
171      }
172
173  /* Version information is requested.  */
174  if (do_version)
175    {
176      printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
177      /* xgettext: no-wrap */
178      printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
179License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
180This is free software: you are free to change and redistribute it.\n\
181There is NO WARRANTY, to the extent permitted by law.\n\
182"),
183	      "2001-2007");
184      printf (_("Written by %s.\n"), proper_name ("Bruno Haible"));
185      exit (EXIT_SUCCESS);
186    }
187
188  /* Help is requested.  */
189  if (do_help)
190    usage (EXIT_SUCCESS);
191
192  /* Test for the subprogram name.  */
193  if (optind == argc)
194    error (EXIT_FAILURE, 0, _("missing command name"));
195  sub_name = argv[optind];
196
197  /* Build argument list for the program.  */
198  sub_argc = argc - optind;
199  sub_argv = XNMALLOC (sub_argc + 1, char *);
200  for (i = 0; i < sub_argc; i++)
201    sub_argv[i] = argv[optind + i];
202  sub_argv[i] = NULL;
203
204  /* By default, input comes from standard input.  */
205  if (input_file == NULL)
206    input_file = "-";
207
208  /* Read input file.  */
209  result = read_catalog_file (input_file, input_syntax);
210
211  if (strcmp (sub_name, "0") != 0)
212    {
213      /* Attempt to locate the program.
214	 This is an optimization, to avoid that spawn/exec searches the PATH
215	 on every call.  */
216      sub_path = find_in_path (sub_name);
217
218      /* Finish argument list for the program.  */
219      sub_argv[0] = (char *) sub_path;
220    }
221
222  exitcode = 0; /* = EXIT_SUCCESS */
223
224  /* Apply the subprogram.  */
225  process_msgdomain_list (result);
226
227  exit (exitcode);
228}
229
230
231/* Display usage information and exit.  */
232static void
233usage (int status)
234{
235  if (status != EXIT_SUCCESS)
236    fprintf (stderr, _("Try `%s --help' for more information.\n"),
237	     program_name);
238  else
239    {
240      printf (_("\
241Usage: %s [OPTION] COMMAND [COMMAND-OPTION]\n\
242"), program_name);
243      printf ("\n");
244      /* xgettext: no-wrap */
245      printf (_("\
246Applies a command to all translations of a translation catalog.\n\
247The COMMAND can be any program that reads a translation from standard\n\
248input.  It is invoked once for each translation.  Its output becomes\n\
249msgexec's output.  msgexec's return code is the maximum return code\n\
250across all invocations.\n\
251"));
252      printf ("\n");
253      /* xgettext: no-wrap */
254      printf (_("\
255A special builtin command called '0' outputs the translation, followed by a\n\
256null byte.  The output of \"msgexec 0\" is suitable as input for \"xargs -0\".\n\
257"));
258      printf ("\n");
259      printf (_("\
260Mandatory arguments to long options are mandatory for short options too.\n"));
261      printf ("\n");
262      printf (_("\
263Input file location:\n"));
264      printf (_("\
265  -i, --input=INPUTFILE       input PO file\n"));
266      printf (_("\
267  -D, --directory=DIRECTORY   add DIRECTORY to list for input files search\n"));
268      printf (_("\
269If no input file is given or if it is -, standard input is read.\n"));
270      printf ("\n");
271      printf (_("\
272Input file syntax:\n"));
273      printf (_("\
274  -P, --properties-input      input file is in Java .properties syntax\n"));
275      printf (_("\
276      --stringtable-input     input file is in NeXTstep/GNUstep .strings syntax\n"));
277      printf ("\n");
278      printf (_("\
279Informative output:\n"));
280      printf (_("\
281  -h, --help                  display this help and exit\n"));
282      printf (_("\
283  -V, --version               output version information and exit\n"));
284      printf ("\n");
285      /* TRANSLATORS: The placeholder indicates the bug-reporting address
286         for this package.  Please add _another line_ saying
287         "Report translation bugs to <...>\n" with the address for translation
288         bugs (typically your translation team's web or email address).  */
289      fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
290	     stdout);
291    }
292
293  exit (status);
294}
295
296
297#ifdef EINTR
298
299/* EINTR handling for close().
300   These functions can return -1/EINTR even though we don't have any
301   signal handlers set up, namely when we get interrupted via SIGSTOP.  */
302
303static inline int
304nonintr_close (int fd)
305{
306  int retval;
307
308  do
309    retval = close (fd);
310  while (retval < 0 && errno == EINTR);
311
312  return retval;
313}
314#define close nonintr_close
315
316#endif
317
318
319/* Pipe a string STR of size LEN bytes to the subprogram.
320   The byte after STR is known to be a '\0' byte.  */
321static void
322process_string (const message_ty *mp, const char *str, size_t len)
323{
324  if (strcmp (sub_name, "0") == 0)
325    {
326      /* Built-in command "0".  */
327      if (full_write (STDOUT_FILENO, str, len + 1) < len + 1)
328	error (EXIT_FAILURE, errno, _("write to stdout failed"));
329    }
330  else
331    {
332      /* General command.  */
333      char *location;
334      pid_t child;
335      int fd[1];
336      int exitstatus;
337
338      /* Set environment variables for the subprocess.  */
339      if (mp->msgctxt != NULL)
340	xsetenv ("MSGEXEC_MSGCTXT", mp->msgctxt, 1);
341      else
342	unsetenv ("MSGEXEC_MSGCTXT");
343      xsetenv ("MSGEXEC_MSGID", mp->msgid, 1);
344      location = xasprintf ("%s:%ld", mp->pos.file_name,
345			    (long) mp->pos.line_number);
346      xsetenv ("MSGEXEC_LOCATION", location, 1);
347      free (location);
348
349      /* Open a pipe to a subprocess.  */
350      child = create_pipe_out (sub_name, sub_path, sub_argv, NULL, false, true,
351			       true, fd);
352
353      if (full_write (fd[0], str, len) < len)
354	error (EXIT_FAILURE, errno,
355	       _("write to %s subprocess failed"), sub_name);
356
357      close (fd[0]);
358
359      /* Remove zombie process from process list, and retrieve exit status.  */
360      /* FIXME: Should ignore_sigpipe be set to true here? It depends on the
361	 semantics of the subprogram...  */
362      exitstatus = wait_subprocess (child, sub_name, false, false, true, true);
363      if (exitcode < exitstatus)
364	exitcode = exitstatus;
365    }
366}
367
368
369static void
370process_message (const message_ty *mp)
371{
372  const char *msgstr = mp->msgstr;
373  size_t msgstr_len = mp->msgstr_len;
374  const char *p;
375
376  /* Process each NUL delimited substring separately.  */
377  for (p = msgstr; p < msgstr + msgstr_len; )
378    {
379      size_t length = strlen (p);
380
381      process_string (mp, p, length);
382
383      p += length + 1;
384    }
385}
386
387
388static void
389process_message_list (const message_list_ty *mlp)
390{
391  size_t j;
392
393  for (j = 0; j < mlp->nitems; j++)
394    process_message (mlp->item[j]);
395}
396
397
398static void
399process_msgdomain_list (const msgdomain_list_ty *mdlp)
400{
401  size_t k;
402
403  for (k = 0; k < mdlp->nitems; k++)
404    process_message_list (mdlp->item[k]->messages);
405}
406