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