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