1*946379e7Schristos /* gettext - retrieve text string from message catalog and print it.
2*946379e7Schristos Copyright (C) 1995-1997, 2000-2006 Free Software Foundation, Inc.
3*946379e7Schristos Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, May 1995.
4*946379e7Schristos
5*946379e7Schristos This program is free software; you can redistribute it and/or modify
6*946379e7Schristos it under the terms of the GNU General Public License as published by
7*946379e7Schristos the Free Software Foundation; either version 2, or (at your option)
8*946379e7Schristos any later version.
9*946379e7Schristos
10*946379e7Schristos This program is distributed in the hope that it will be useful,
11*946379e7Schristos but WITHOUT ANY WARRANTY; without even the implied warranty of
12*946379e7Schristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13*946379e7Schristos GNU General Public License for more details.
14*946379e7Schristos
15*946379e7Schristos You should have received a copy of the GNU General Public License
16*946379e7Schristos along with this program; if not, write to the Free Software Foundation,
17*946379e7Schristos Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
18*946379e7Schristos
19*946379e7Schristos #ifdef HAVE_CONFIG_H
20*946379e7Schristos # include <config.h>
21*946379e7Schristos #endif
22*946379e7Schristos
23*946379e7Schristos #include <getopt.h>
24*946379e7Schristos #include <stdbool.h>
25*946379e7Schristos #include <stdio.h>
26*946379e7Schristos #include <stdlib.h>
27*946379e7Schristos #include <string.h>
28*946379e7Schristos #include <locale.h>
29*946379e7Schristos
30*946379e7Schristos #include "closeout.h"
31*946379e7Schristos #include "error.h"
32*946379e7Schristos #include "progname.h"
33*946379e7Schristos #include "relocatable.h"
34*946379e7Schristos #include "basename.h"
35*946379e7Schristos #include "xalloc.h"
36*946379e7Schristos #include "exit.h"
37*946379e7Schristos #include "propername.h"
38*946379e7Schristos #include "gettext.h"
39*946379e7Schristos
40*946379e7Schristos #define _(str) gettext (str)
41*946379e7Schristos
42*946379e7Schristos /* If true, add newline after last string. This makes only sense in
43*946379e7Schristos the `echo' emulation mode. */
44*946379e7Schristos static bool add_newline;
45*946379e7Schristos
46*946379e7Schristos /* If true, expand escape sequences in strings before looking in the
47*946379e7Schristos message catalog. */
48*946379e7Schristos static bool do_expand;
49*946379e7Schristos
50*946379e7Schristos /* Long options. */
51*946379e7Schristos static const struct option long_options[] =
52*946379e7Schristos {
53*946379e7Schristos { "domain", required_argument, NULL, 'd' },
54*946379e7Schristos { "help", no_argument, NULL, 'h' },
55*946379e7Schristos { "shell-script", no_argument, NULL, 's' },
56*946379e7Schristos { "version", no_argument, NULL, 'V' },
57*946379e7Schristos { NULL, 0, NULL, 0 }
58*946379e7Schristos };
59*946379e7Schristos
60*946379e7Schristos /* Forward declaration of local functions. */
61*946379e7Schristos static void usage (int status)
62*946379e7Schristos #if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
63*946379e7Schristos __attribute__ ((noreturn))
64*946379e7Schristos #endif
65*946379e7Schristos ;
66*946379e7Schristos static const char *expand_escape (const char *str);
67*946379e7Schristos
68*946379e7Schristos int
main(int argc,char * argv[])69*946379e7Schristos main (int argc, char *argv[])
70*946379e7Schristos {
71*946379e7Schristos int optchar;
72*946379e7Schristos const char *msgid;
73*946379e7Schristos
74*946379e7Schristos /* Default values for command line options. */
75*946379e7Schristos bool do_help = false;
76*946379e7Schristos bool do_shell = false;
77*946379e7Schristos bool do_version = false;
78*946379e7Schristos const char *domain = getenv ("TEXTDOMAIN");
79*946379e7Schristos const char *domaindir = getenv ("TEXTDOMAINDIR");
80*946379e7Schristos add_newline = true;
81*946379e7Schristos do_expand = false;
82*946379e7Schristos
83*946379e7Schristos /* Set program name for message texts. */
84*946379e7Schristos set_program_name (argv[0]);
85*946379e7Schristos
86*946379e7Schristos #ifdef HAVE_SETLOCALE
87*946379e7Schristos /* Set locale via LC_ALL. */
88*946379e7Schristos setlocale (LC_ALL, "");
89*946379e7Schristos #endif
90*946379e7Schristos
91*946379e7Schristos /* Set the text message domain. */
92*946379e7Schristos bindtextdomain (PACKAGE, relocate (LOCALEDIR));
93*946379e7Schristos textdomain (PACKAGE);
94*946379e7Schristos
95*946379e7Schristos /* Ensure that write errors on stdout are detected. */
96*946379e7Schristos atexit (close_stdout);
97*946379e7Schristos
98*946379e7Schristos /* Parse command line options. */
99*946379e7Schristos while ((optchar = getopt_long (argc, argv, "+d:eEhnsV", long_options, NULL))
100*946379e7Schristos != EOF)
101*946379e7Schristos switch (optchar)
102*946379e7Schristos {
103*946379e7Schristos case '\0': /* Long option. */
104*946379e7Schristos break;
105*946379e7Schristos case 'd':
106*946379e7Schristos domain = optarg;
107*946379e7Schristos break;
108*946379e7Schristos case 'e':
109*946379e7Schristos do_expand = true;
110*946379e7Schristos break;
111*946379e7Schristos case 'E':
112*946379e7Schristos /* Ignore. Just for compatibility. */
113*946379e7Schristos break;
114*946379e7Schristos case 'h':
115*946379e7Schristos do_help = true;
116*946379e7Schristos break;
117*946379e7Schristos case 'n':
118*946379e7Schristos add_newline = false;
119*946379e7Schristos break;
120*946379e7Schristos case 's':
121*946379e7Schristos do_shell = true;
122*946379e7Schristos break;
123*946379e7Schristos case 'V':
124*946379e7Schristos do_version = true;
125*946379e7Schristos break;
126*946379e7Schristos default:
127*946379e7Schristos usage (EXIT_FAILURE);
128*946379e7Schristos }
129*946379e7Schristos
130*946379e7Schristos /* Version information is requested. */
131*946379e7Schristos if (do_version)
132*946379e7Schristos {
133*946379e7Schristos printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
134*946379e7Schristos /* xgettext: no-wrap */
135*946379e7Schristos printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
136*946379e7Schristos This is free software; see the source for copying conditions. There is NO\n\
137*946379e7Schristos warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
138*946379e7Schristos "),
139*946379e7Schristos "1995-1997, 2000-2006");
140*946379e7Schristos printf (_("Written by %s.\n"), proper_name ("Ulrich Drepper"));
141*946379e7Schristos exit (EXIT_SUCCESS);
142*946379e7Schristos }
143*946379e7Schristos
144*946379e7Schristos /* Help is requested. */
145*946379e7Schristos if (do_help)
146*946379e7Schristos usage (EXIT_SUCCESS);
147*946379e7Schristos
148*946379e7Schristos /* We have two major modes: use following Uniforum spec and as
149*946379e7Schristos internationalized `echo' program. */
150*946379e7Schristos if (!do_shell)
151*946379e7Schristos {
152*946379e7Schristos /* We have to write a single strings translation to stdout. */
153*946379e7Schristos
154*946379e7Schristos /* Get arguments. */
155*946379e7Schristos switch (argc - optind)
156*946379e7Schristos {
157*946379e7Schristos default:
158*946379e7Schristos error (EXIT_FAILURE, 0, _("too many arguments"));
159*946379e7Schristos
160*946379e7Schristos case 2:
161*946379e7Schristos domain = argv[optind++];
162*946379e7Schristos /* FALLTHROUGH */
163*946379e7Schristos
164*946379e7Schristos case 1:
165*946379e7Schristos break;
166*946379e7Schristos
167*946379e7Schristos case 0:
168*946379e7Schristos error (EXIT_FAILURE, 0, _("missing arguments"));
169*946379e7Schristos }
170*946379e7Schristos
171*946379e7Schristos msgid = argv[optind++];
172*946379e7Schristos
173*946379e7Schristos /* Expand escape sequences if enabled. */
174*946379e7Schristos if (do_expand)
175*946379e7Schristos msgid = expand_escape (msgid);
176*946379e7Schristos
177*946379e7Schristos /* If no domain name is given we don't translate. */
178*946379e7Schristos if (domain == NULL || domain[0] == '\0')
179*946379e7Schristos {
180*946379e7Schristos fputs (msgid, stdout);
181*946379e7Schristos }
182*946379e7Schristos else
183*946379e7Schristos {
184*946379e7Schristos /* Bind domain to appropriate directory. */
185*946379e7Schristos if (domaindir != NULL && domaindir[0] != '\0')
186*946379e7Schristos bindtextdomain (domain, domaindir);
187*946379e7Schristos
188*946379e7Schristos /* Write out the result. */
189*946379e7Schristos fputs (dgettext (domain, msgid), stdout);
190*946379e7Schristos }
191*946379e7Schristos }
192*946379e7Schristos else
193*946379e7Schristos {
194*946379e7Schristos if (optind < argc)
195*946379e7Schristos {
196*946379e7Schristos /* If no domain name is given we print the original string.
197*946379e7Schristos We mark this assigning NULL to domain. */
198*946379e7Schristos if (domain == NULL || domain[0] == '\0')
199*946379e7Schristos domain = NULL;
200*946379e7Schristos else
201*946379e7Schristos /* Bind domain to appropriate directory. */
202*946379e7Schristos if (domaindir != NULL && domaindir[0] != '\0')
203*946379e7Schristos bindtextdomain (domain, domaindir);
204*946379e7Schristos
205*946379e7Schristos /* We have to simulate `echo'. All arguments are strings. */
206*946379e7Schristos do
207*946379e7Schristos {
208*946379e7Schristos msgid = argv[optind++];
209*946379e7Schristos
210*946379e7Schristos /* Expand escape sequences if enabled. */
211*946379e7Schristos if (do_expand)
212*946379e7Schristos msgid = expand_escape (msgid);
213*946379e7Schristos
214*946379e7Schristos /* Write out the result. */
215*946379e7Schristos fputs (domain == NULL ? msgid : dgettext (domain, msgid),
216*946379e7Schristos stdout);
217*946379e7Schristos
218*946379e7Schristos /* We separate the arguments by a single ' '. */
219*946379e7Schristos if (optind < argc)
220*946379e7Schristos fputc (' ', stdout);
221*946379e7Schristos }
222*946379e7Schristos while (optind < argc);
223*946379e7Schristos }
224*946379e7Schristos
225*946379e7Schristos /* If not otherwise told: add trailing newline. */
226*946379e7Schristos if (add_newline)
227*946379e7Schristos fputc ('\n', stdout);
228*946379e7Schristos }
229*946379e7Schristos
230*946379e7Schristos exit (EXIT_SUCCESS);
231*946379e7Schristos }
232*946379e7Schristos
233*946379e7Schristos
234*946379e7Schristos /* Display usage information and exit. */
235*946379e7Schristos static void
usage(int status)236*946379e7Schristos usage (int status)
237*946379e7Schristos {
238*946379e7Schristos if (status != EXIT_SUCCESS)
239*946379e7Schristos fprintf (stderr, _("Try `%s --help' for more information.\n"),
240*946379e7Schristos program_name);
241*946379e7Schristos else
242*946379e7Schristos {
243*946379e7Schristos /* xgettext: no-wrap */
244*946379e7Schristos printf (_("\
245*946379e7Schristos Usage: %s [OPTION] [[TEXTDOMAIN] MSGID]\n\
246*946379e7Schristos or: %s [OPTION] -s [MSGID]...\n\
247*946379e7Schristos "), program_name, program_name);
248*946379e7Schristos printf ("\n");
249*946379e7Schristos /* xgettext: no-wrap */
250*946379e7Schristos printf (_("\
251*946379e7Schristos Display native language translation of a textual message.\n"));
252*946379e7Schristos printf ("\n");
253*946379e7Schristos /* xgettext: no-wrap */
254*946379e7Schristos printf (_("\
255*946379e7Schristos -d, --domain=TEXTDOMAIN retrieve translated messages from TEXTDOMAIN\n\
256*946379e7Schristos -e enable expansion of some escape sequences\n\
257*946379e7Schristos -E (ignored for compatibility)\n\
258*946379e7Schristos -h, --help display this help and exit\n\
259*946379e7Schristos -n suppress trailing newline\n\
260*946379e7Schristos -V, --version display version information and exit\n\
261*946379e7Schristos [TEXTDOMAIN] MSGID retrieve translated message corresponding\n\
262*946379e7Schristos to MSGID from TEXTDOMAIN\n"));
263*946379e7Schristos printf ("\n");
264*946379e7Schristos /* xgettext: no-wrap */
265*946379e7Schristos printf (_("\
266*946379e7Schristos If the TEXTDOMAIN parameter is not given, the domain is determined from the\n\
267*946379e7Schristos environment variable TEXTDOMAIN. If the message catalog is not found in the\n\
268*946379e7Schristos regular directory, another location can be specified with the environment\n\
269*946379e7Schristos variable TEXTDOMAINDIR.\n\
270*946379e7Schristos When used with the -s option the program behaves like the `echo' command.\n\
271*946379e7Schristos But it does not simply copy its arguments to stdout. Instead those messages\n\
272*946379e7Schristos found in the selected catalog are translated.\n\
273*946379e7Schristos Standard search directory: %s\n"),
274*946379e7Schristos getenv ("IN_HELP2MAN") == NULL ? LOCALEDIR : "@localedir@");
275*946379e7Schristos printf ("\n");
276*946379e7Schristos fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"), stdout);
277*946379e7Schristos }
278*946379e7Schristos
279*946379e7Schristos exit (status);
280*946379e7Schristos }
281*946379e7Schristos
282*946379e7Schristos
283*946379e7Schristos /* Expand some escape sequences found in the argument string. */
284*946379e7Schristos static const char *
expand_escape(const char * str)285*946379e7Schristos expand_escape (const char *str)
286*946379e7Schristos {
287*946379e7Schristos char *retval, *rp;
288*946379e7Schristos const char *cp = str;
289*946379e7Schristos
290*946379e7Schristos for (;;)
291*946379e7Schristos {
292*946379e7Schristos while (cp[0] != '\0' && cp[0] != '\\')
293*946379e7Schristos ++cp;
294*946379e7Schristos if (cp[0] == '\0')
295*946379e7Schristos return str;
296*946379e7Schristos /* Found a backslash. */
297*946379e7Schristos if (cp[1] == '\0')
298*946379e7Schristos return str;
299*946379e7Schristos if (strchr ("abcfnrtv\\01234567", cp[1]) != NULL)
300*946379e7Schristos break;
301*946379e7Schristos ++cp;
302*946379e7Schristos }
303*946379e7Schristos
304*946379e7Schristos retval = (char *) xmalloc (strlen (str));
305*946379e7Schristos
306*946379e7Schristos rp = retval + (cp - str);
307*946379e7Schristos memcpy (retval, str, cp - str);
308*946379e7Schristos
309*946379e7Schristos do
310*946379e7Schristos {
311*946379e7Schristos /* Here cp[0] == '\\'. */
312*946379e7Schristos switch (*++cp)
313*946379e7Schristos {
314*946379e7Schristos case 'a': /* alert */
315*946379e7Schristos *rp++ = '\a';
316*946379e7Schristos ++cp;
317*946379e7Schristos break;
318*946379e7Schristos case 'b': /* backspace */
319*946379e7Schristos *rp++ = '\b';
320*946379e7Schristos ++cp;
321*946379e7Schristos break;
322*946379e7Schristos case 'c': /* suppress trailing newline */
323*946379e7Schristos add_newline = false;
324*946379e7Schristos ++cp;
325*946379e7Schristos break;
326*946379e7Schristos case 'f': /* form feed */
327*946379e7Schristos *rp++ = '\f';
328*946379e7Schristos ++cp;
329*946379e7Schristos break;
330*946379e7Schristos case 'n': /* new line */
331*946379e7Schristos *rp++ = '\n';
332*946379e7Schristos ++cp;
333*946379e7Schristos break;
334*946379e7Schristos case 'r': /* carriage return */
335*946379e7Schristos *rp++ = '\r';
336*946379e7Schristos ++cp;
337*946379e7Schristos break;
338*946379e7Schristos case 't': /* horizontal tab */
339*946379e7Schristos *rp++ = '\t';
340*946379e7Schristos ++cp;
341*946379e7Schristos break;
342*946379e7Schristos case 'v': /* vertical tab */
343*946379e7Schristos *rp++ = '\v';
344*946379e7Schristos ++cp;
345*946379e7Schristos break;
346*946379e7Schristos case '\\':
347*946379e7Schristos *rp = '\\';
348*946379e7Schristos ++cp;
349*946379e7Schristos break;
350*946379e7Schristos case '0': case '1': case '2': case '3':
351*946379e7Schristos case '4': case '5': case '6': case '7':
352*946379e7Schristos {
353*946379e7Schristos int ch = *cp++ - '0';
354*946379e7Schristos
355*946379e7Schristos if (*cp >= '0' && *cp <= '7')
356*946379e7Schristos {
357*946379e7Schristos ch *= 8;
358*946379e7Schristos ch += *cp++ - '0';
359*946379e7Schristos
360*946379e7Schristos if (*cp >= '0' && *cp <= '7')
361*946379e7Schristos {
362*946379e7Schristos ch *= 8;
363*946379e7Schristos ch += *cp++ - '0';
364*946379e7Schristos }
365*946379e7Schristos }
366*946379e7Schristos *rp = ch;
367*946379e7Schristos }
368*946379e7Schristos break;
369*946379e7Schristos default:
370*946379e7Schristos *rp = '\\';
371*946379e7Schristos break;
372*946379e7Schristos }
373*946379e7Schristos
374*946379e7Schristos while (cp[0] != '\0' && cp[0] != '\\')
375*946379e7Schristos *rp++ = *cp++;
376*946379e7Schristos }
377*946379e7Schristos while (cp[0] != '\0');
378*946379e7Schristos
379*946379e7Schristos /* Terminate string. */
380*946379e7Schristos *rp = '\0';
381*946379e7Schristos
382*946379e7Schristos return (const char *) retval;
383*946379e7Schristos }
384