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