1 /* Pass translations to a subprocess.
2 Copyright (C) 2001-2005 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 2, or (at your option)
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, write to the Free Software Foundation,
17 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
31 #include <sys/types.h>
41 #include "error-progname.h"
43 #include "relocatable.h"
49 #include "full-write.h"
52 #include "wait-process.h"
56 #define _(str) gettext (str)
59 # define STDOUT_FILENO 1
63 /* Name of the subprogram. */
64 static const char *sub_name
;
66 /* Pathname of the subprogram. */
67 static const char *sub_path
;
69 /* Argument list for the subprogram. */
70 static char **sub_argv
;
73 /* Maximum exit code encountered. */
77 static const struct option long_options
[] =
79 { "directory", required_argument
, NULL
, 'D' },
80 { "help", no_argument
, NULL
, 'h' },
81 { "input", required_argument
, NULL
, 'i' },
82 { "properties-input", no_argument
, NULL
, 'P' },
83 { "stringtable-input", no_argument
, NULL
, CHAR_MAX
+ 1 },
84 { "version", no_argument
, NULL
, 'V' },
89 /* Forward declaration of local functions. */
90 static void usage (int status
)
91 #if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
92 __attribute__ ((noreturn
))
95 static void process_msgdomain_list (const msgdomain_list_ty
*mdlp
);
99 main (int argc
, char **argv
)
104 const char *input_file
;
105 msgdomain_list_ty
*result
;
108 /* Set program name for messages. */
109 set_program_name (argv
[0]);
110 error_print_progname
= maybe_print_progname
;
112 #ifdef HAVE_SETLOCALE
113 /* Set locale via LC_ALL. */
114 setlocale (LC_ALL
, "");
117 /* Set the text message domain. */
118 bindtextdomain (PACKAGE
, relocate (LOCALEDIR
));
119 textdomain (PACKAGE
);
121 /* Ensure that write errors on stdout are detected. */
122 atexit (close_stdout
);
124 /* Set default values for variables. */
129 /* The '+' in the options string causes option parsing to terminate when
130 the first non-option, i.e. the subprogram name, is encountered. */
131 while ((opt
= getopt_long (argc
, argv
, "+D:hi:PV", long_options
, NULL
))
135 case '\0': /* Long option. */
139 dir_list_append (optarg
);
147 if (input_file
!= NULL
)
149 error (EXIT_SUCCESS
, 0, _("at most one input file allowed"));
150 usage (EXIT_FAILURE
);
156 input_syntax
= syntax_properties
;
163 case CHAR_MAX
+ 1: /* --stringtable-input */
164 input_syntax
= syntax_stringtable
;
168 usage (EXIT_FAILURE
);
172 /* Version information is requested. */
175 printf ("%s (GNU %s) %s\n", basename (program_name
), PACKAGE
, VERSION
);
176 /* xgettext: no-wrap */
177 printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
178 This is free software; see the source for copying conditions. There is NO\n\
179 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
182 printf (_("Written by %s.\n"), "Bruno Haible");
186 /* Help is requested. */
188 usage (EXIT_SUCCESS
);
190 /* Test for the subprogram name. */
192 error (EXIT_FAILURE
, 0, _("missing command name"));
193 sub_name
= argv
[optind
];
195 /* Build argument list for the program. */
196 sub_argc
= argc
- optind
;
197 sub_argv
= (char **) xmalloc ((sub_argc
+ 1) * sizeof (char *));
198 for (i
= 0; i
< sub_argc
; i
++)
199 sub_argv
[i
] = argv
[optind
+ i
];
202 /* By default, input comes from standard input. */
203 if (input_file
== NULL
)
206 /* Read input file. */
207 result
= read_po_file (input_file
);
209 if (strcmp (sub_name
, "0") != 0)
211 /* Attempt to locate the program.
212 This is an optimization, to avoid that spawn/exec searches the PATH
214 sub_path
= find_in_path (sub_name
);
216 /* Finish argument list for the program. */
217 sub_argv
[0] = (char *) sub_path
;
220 exitcode
= 0; /* = EXIT_SUCCESS */
222 /* Apply the subprogram. */
223 process_msgdomain_list (result
);
229 /* Display usage information and exit. */
233 if (status
!= EXIT_SUCCESS
)
234 fprintf (stderr
, _("Try `%s --help' for more information.\n"),
239 Usage: %s [OPTION] COMMAND [COMMAND-OPTION]\n\
242 /* xgettext: no-wrap */
244 Applies a command to all translations of a translation catalog.\n\
245 The COMMAND can be any program that reads a translation from standard\n\
246 input. It is invoked once for each translation. Its output becomes\n\
247 msgexec's output. msgexec's return code is the maximum return code\n\
248 across all invocations.\n\
251 /* xgettext: no-wrap */
253 A special builtin command called '0' outputs the translation, followed by a\n\
254 null byte. The output of \"msgexec 0\" is suitable as input for \"xargs -0\".\n\
258 Mandatory arguments to long options are mandatory for short options too.\n"));
261 Input file location:\n"));
263 -i, --input=INPUTFILE input PO file\n"));
265 -D, --directory=DIRECTORY add DIRECTORY to list for input files search\n"));
267 If no input file is given or if it is -, standard input is read.\n"));
270 Input file syntax:\n"));
272 -P, --properties-input input file is in Java .properties syntax\n"));
274 --stringtable-input input file is in NeXTstep/GNUstep .strings syntax\n"));
277 Informative output:\n"));
279 -h, --help display this help and exit\n"));
281 -V, --version output version information and exit\n"));
283 fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
293 /* EINTR handling for close().
294 These functions can return -1/EINTR even though we don't have any
295 signal handlers set up, namely when we get interrupted via SIGSTOP. */
298 nonintr_close (int fd
)
304 while (retval
< 0 && errno
== EINTR
);
308 #define close nonintr_close
313 /* Pipe a string STR of size LEN bytes to the subprogram.
314 The byte after STR is known to be a '\0' byte. */
316 process_string (const message_ty
*mp
, const char *str
, size_t len
)
318 if (strcmp (sub_name
, "0") == 0)
320 /* Built-in command "0". */
321 if (full_write (STDOUT_FILENO
, str
, len
+ 1) < len
+ 1)
322 error (EXIT_FAILURE
, errno
, _("write to stdout failed"));
326 /* General command. */
332 /* Set environment variables for the subprocess. */
333 xsetenv ("MSGEXEC_MSGID", mp
->msgid
, 1);
334 location
= xasprintf ("%s:%ld", mp
->pos
.file_name
,
335 (long) mp
->pos
.line_number
);
336 xsetenv ("MSGEXEC_LOCATION", location
, 1);
339 /* Open a pipe to a subprocess. */
340 child
= create_pipe_out (sub_name
, sub_path
, sub_argv
, NULL
, false, true,
343 if (full_write (fd
[0], str
, len
) < len
)
344 error (EXIT_FAILURE
, errno
,
345 _("write to %s subprocess failed"), sub_name
);
349 /* Remove zombie process from process list, and retrieve exit status. */
350 /* FIXME: Should ignore_sigpipe be set to true here? It depends on the
351 semantics of the subprogram... */
352 exitstatus
= wait_subprocess (child
, sub_name
, false, false, true, true);
353 if (exitcode
< exitstatus
)
354 exitcode
= exitstatus
;
360 process_message (const message_ty
*mp
)
362 const char *msgstr
= mp
->msgstr
;
363 size_t msgstr_len
= mp
->msgstr_len
;
366 /* Process each NUL delimited substring separately. */
367 for (p
= msgstr
; p
< msgstr
+ msgstr_len
; )
369 size_t length
= strlen (p
);
371 process_string (mp
, p
, length
);
379 process_message_list (const message_list_ty
*mlp
)
383 for (j
= 0; j
< mlp
->nitems
; j
++)
384 process_message (mlp
->item
[j
]);
389 process_msgdomain_list (const msgdomain_list_ty
*mdlp
)
393 for (k
= 0; k
< mdlp
->nitems
; k
++)
394 process_message_list (mdlp
->item
[k
]->messages
);