No empty .Rs/.Re
[netbsd-mini2440.git] / gnu / dist / gettext / gettext-tools / src / msgexec.c
blob8dc89318368d6b97a10e468fb51ac489c5b32d80
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)
8 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, write to the Free Software Foundation,
17 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
24 #include <errno.h>
25 #include <getopt.h>
26 #include <limits.h>
27 #include <locale.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/types.h>
33 #ifdef HAVE_UNISTD_H
34 # include <unistd.h>
35 #endif
37 #include "closeout.h"
38 #include "dir-list.h"
39 #include "error.h"
40 #include "xerror.h"
41 #include "error-progname.h"
42 #include "progname.h"
43 #include "relocatable.h"
44 #include "basename.h"
45 #include "message.h"
46 #include "read-po.h"
47 #include "xalloc.h"
48 #include "exit.h"
49 #include "full-write.h"
50 #include "findprog.h"
51 #include "pipe.h"
52 #include "wait-process.h"
53 #include "xsetenv.h"
54 #include "gettext.h"
56 #define _(str) gettext (str)
58 #ifndef STDOUT_FILENO
59 # define STDOUT_FILENO 1
60 #endif
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;
71 static int sub_argc;
73 /* Maximum exit code encountered. */
74 static int exitcode;
76 /* Long options. */
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' },
85 { NULL, 0, NULL, 0 }
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))
93 #endif
95 static void process_msgdomain_list (const msgdomain_list_ty *mdlp);
98 int
99 main (int argc, char **argv)
101 int opt;
102 bool do_help;
103 bool do_version;
104 const char *input_file;
105 msgdomain_list_ty *result;
106 size_t i;
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, "");
115 #endif
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. */
125 do_help = false;
126 do_version = false;
127 input_file = NULL;
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))
132 != EOF)
133 switch (opt)
135 case '\0': /* Long option. */
136 break;
138 case 'D':
139 dir_list_append (optarg);
140 break;
142 case 'h':
143 do_help = true;
144 break;
146 case 'i':
147 if (input_file != NULL)
149 error (EXIT_SUCCESS, 0, _("at most one input file allowed"));
150 usage (EXIT_FAILURE);
152 input_file = optarg;
153 break;
155 case 'P':
156 input_syntax = syntax_properties;
157 break;
159 case 'V':
160 do_version = true;
161 break;
163 case CHAR_MAX + 1: /* --stringtable-input */
164 input_syntax = syntax_stringtable;
165 break;
167 default:
168 usage (EXIT_FAILURE);
169 break;
172 /* Version information is requested. */
173 if (do_version)
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\
181 "2001-2005");
182 printf (_("Written by %s.\n"), "Bruno Haible");
183 exit (EXIT_SUCCESS);
186 /* Help is requested. */
187 if (do_help)
188 usage (EXIT_SUCCESS);
190 /* Test for the subprogram name. */
191 if (optind == argc)
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];
200 sub_argv[i] = NULL;
202 /* By default, input comes from standard input. */
203 if (input_file == NULL)
204 input_file = "-";
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
213 on every call. */
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);
225 exit (exitcode);
229 /* Display usage information and exit. */
230 static void
231 usage (int status)
233 if (status != EXIT_SUCCESS)
234 fprintf (stderr, _("Try `%s --help' for more information.\n"),
235 program_name);
236 else
238 printf (_("\
239 Usage: %s [OPTION] COMMAND [COMMAND-OPTION]\n\
240 "), program_name);
241 printf ("\n");
242 /* xgettext: no-wrap */
243 printf (_("\
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\
249 "));
250 printf ("\n");
251 /* xgettext: no-wrap */
252 printf (_("\
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\
255 "));
256 printf ("\n");
257 printf (_("\
258 Mandatory arguments to long options are mandatory for short options too.\n"));
259 printf ("\n");
260 printf (_("\
261 Input file location:\n"));
262 printf (_("\
263 -i, --input=INPUTFILE input PO file\n"));
264 printf (_("\
265 -D, --directory=DIRECTORY add DIRECTORY to list for input files search\n"));
266 printf (_("\
267 If no input file is given or if it is -, standard input is read.\n"));
268 printf ("\n");
269 printf (_("\
270 Input file syntax:\n"));
271 printf (_("\
272 -P, --properties-input input file is in Java .properties syntax\n"));
273 printf (_("\
274 --stringtable-input input file is in NeXTstep/GNUstep .strings syntax\n"));
275 printf ("\n");
276 printf (_("\
277 Informative output:\n"));
278 printf (_("\
279 -h, --help display this help and exit\n"));
280 printf (_("\
281 -V, --version output version information and exit\n"));
282 printf ("\n");
283 fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
284 stdout);
287 exit (status);
291 #ifdef EINTR
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. */
297 static inline int
298 nonintr_close (int fd)
300 int retval;
303 retval = close (fd);
304 while (retval < 0 && errno == EINTR);
306 return retval;
308 #define close nonintr_close
310 #endif
313 /* Pipe a string STR of size LEN bytes to the subprogram.
314 The byte after STR is known to be a '\0' byte. */
315 static void
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"));
324 else
326 /* General command. */
327 char *location;
328 pid_t child;
329 int fd[1];
330 int exitstatus;
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);
337 free (location);
339 /* Open a pipe to a subprocess. */
340 child = create_pipe_out (sub_name, sub_path, sub_argv, NULL, false, true,
341 true, fd);
343 if (full_write (fd[0], str, len) < len)
344 error (EXIT_FAILURE, errno,
345 _("write to %s subprocess failed"), sub_name);
347 close (fd[0]);
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;
359 static void
360 process_message (const message_ty *mp)
362 const char *msgstr = mp->msgstr;
363 size_t msgstr_len = mp->msgstr_len;
364 const char *p;
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);
373 p += length + 1;
378 static void
379 process_message_list (const message_list_ty *mlp)
381 size_t j;
383 for (j = 0; j < mlp->nitems; j++)
384 process_message (mlp->item[j]);
388 static void
389 process_msgdomain_list (const msgdomain_list_ty *mdlp)
391 size_t k;
393 for (k = 0; k < mdlp->nitems; k++)
394 process_message_list (mdlp->item[k]->messages);