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