• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* gettext - retrieve text string from message catalog and print it.
2    Copyright (C) 1995-1997, 2000-2007, 2012, 2018-2020 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 <https://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 "noreturn.h"
31 #include "closeout.h"
32 #include "error.h"
33 #include "progname.h"
34 #include "relocatable.h"
35 #include "basename-lgpl.h"
36 #include "xalloc.h"
37 #include "propername.h"
38 #include "xsetenv.h"
39 #include "../../gettext-runtime/src/escapes.h"
40 
41 /* Make sure we use the included libintl, not the system's one. */
42 #undef _LIBINTL_H
43 #include "libgnuintl.h"
44 
45 #if defined _WIN32 && !defined __CYGWIN__
46 # undef setlocale
47 # define setlocale fake_setlocale
48 extern char *setlocale (int category, SETLOCALE_CONST char *locale);
49 #endif
50 
51 #define _(str) gettext (str)
52 
53 /* If false, add newline after last string.  This makes only sense in
54    the 'echo' emulation mode.  */
55 static bool inhibit_added_newline;
56 
57 /* If true, expand escape sequences in strings before looking in the
58    message catalog.  */
59 static bool do_expand;
60 
61 /* Long options.  */
62 static const struct option long_options[] =
63 {
64   { "domain", required_argument, NULL, 'd' },
65   { "env", required_argument, NULL, '=' },
66   { "help", no_argument, NULL, 'h' },
67   { "shell-script", no_argument, NULL, 's' },
68   { "version", no_argument, NULL, 'V' },
69   { NULL, 0, NULL, 0 }
70 };
71 
72 /* Forward declaration of local functions.  */
73 _GL_NORETURN_FUNC static void usage (int status);
74 
75 int
main(int argc,char * argv[])76 main (int argc, char *argv[])
77 {
78   int optchar;
79   const char *msgid;
80 
81   /* Default values for command line options.  */
82   bool do_help = false;
83   bool do_shell = false;
84   bool do_version = false;
85   bool environ_changed = false;
86   const char *domain = getenv ("TEXTDOMAIN");
87   const char *domaindir = getenv ("TEXTDOMAINDIR");
88   inhibit_added_newline = false;
89   do_expand = false;
90 
91   /* Set program name for message texts.  */
92   set_program_name (argv[0]);
93 
94   /* Set locale via LC_ALL.  */
95   setlocale (LC_ALL, "");
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       inhibit_added_newline = true;
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   if (environ_changed)
150     /* Set locale again via LC_ALL.  */
151     setlocale (LC_ALL, "");
152 
153   /* Version information is requested.  */
154   if (do_version)
155     {
156       printf ("%s (GNU %s) %s\n", last_component (program_name),
157               PACKAGE, VERSION);
158       /* xgettext: no-wrap */
159       printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
160 License GPLv3+: GNU GPL version 3 or later <%s>\n\
161 This is free software: you are free to change and redistribute it.\n\
162 There is NO WARRANTY, to the extent permitted by law.\n\
163 "),
164               "1995-1997, 2000-2006", "https://gnu.org/licenses/gpl.html");
165       printf (_("Written by %s.\n"), proper_name ("Ulrich Drepper"));
166       exit (EXIT_SUCCESS);
167     }
168 
169   /* Help is requested.  */
170   if (do_help)
171     usage (EXIT_SUCCESS);
172 
173   /* We have two major modes: use following Uniforum spec and as
174      internationalized 'echo' program.  */
175   if (!do_shell)
176     {
177       /* We have to write a single strings translation to stdout.  */
178 
179       /* Get arguments.  */
180       switch (argc - optind)
181         {
182           default:
183             error (EXIT_FAILURE, 0, _("too many arguments"));
184 
185           case 2:
186             domain = argv[optind++];
187             /* FALLTHROUGH */
188 
189           case 1:
190             break;
191 
192           case 0:
193             error (EXIT_FAILURE, 0, _("missing arguments"));
194         }
195 
196       msgid = argv[optind++];
197 
198       /* Expand escape sequences if enabled.  */
199       if (do_expand)
200         msgid = expand_escapes (msgid, &inhibit_added_newline);
201 
202       /* If no domain name is given we don't translate.  */
203       if (domain == NULL || domain[0] == '\0')
204         {
205           fputs (msgid, stdout);
206         }
207       else
208         {
209           /* Bind domain to appropriate directory.  */
210           if (domaindir != NULL && domaindir[0] != '\0')
211             bindtextdomain (domain, domaindir);
212 
213           /* Write out the result.  */
214           fputs (dgettext (domain, msgid), stdout);
215         }
216     }
217   else
218     {
219       if (optind < argc)
220         {
221           /* If no domain name is given we print the original string.
222              We mark this assigning NULL to domain.  */
223           if (domain == NULL || domain[0] == '\0')
224             domain = NULL;
225           else
226             /* Bind domain to appropriate directory.  */
227             if (domaindir != NULL && domaindir[0] != '\0')
228               bindtextdomain (domain, domaindir);
229 
230           /* We have to simulate 'echo'.  All arguments are strings.  */
231           do
232             {
233               msgid = argv[optind++];
234 
235               /* Expand escape sequences if enabled.  */
236               if (do_expand)
237                 msgid = expand_escapes (msgid, &inhibit_added_newline);
238 
239               /* Write out the result.  */
240               fputs (domain == NULL ? msgid : dgettext (domain, msgid),
241                      stdout);
242 
243               /* We separate the arguments by a single ' '.  */
244               if (optind < argc)
245                 fputc (' ', stdout);
246             }
247           while (optind < argc);
248         }
249 
250       /* If not otherwise told: add trailing newline.  */
251       if (!inhibit_added_newline)
252         fputc ('\n', stdout);
253     }
254 
255   exit (EXIT_SUCCESS);
256 }
257 
258 
259 /* Display usage information and exit.  */
260 static void
usage(int status)261 usage (int status)
262 {
263   if (status != EXIT_SUCCESS)
264     fprintf (stderr, _("Try '%s --help' for more information.\n"),
265              program_name);
266   else
267     {
268       /* xgettext: no-wrap */
269       printf (_("\
270 Usage: %s [OPTION] [[TEXTDOMAIN] MSGID]\n\
271 or:    %s [OPTION] -s [MSGID]...\n\
272 "), program_name, program_name);
273       printf ("\n");
274       /* xgettext: no-wrap */
275       printf (_("\
276 Display native language translation of a textual message.\n"));
277       printf ("\n");
278       /* xgettext: no-wrap */
279       printf (_("\
280   -d, --domain=TEXTDOMAIN   retrieve translated messages from TEXTDOMAIN\n\
281   -e                        enable expansion of some escape sequences\n\
282   -E                        (ignored for compatibility)\n\
283   -h, --help                display this help and exit\n\
284   -n                        suppress trailing newline\n\
285   -V, --version             display version information and exit\n\
286   [TEXTDOMAIN] MSGID        retrieve translated message corresponding\n\
287                             to MSGID from TEXTDOMAIN\n"));
288       printf ("\n");
289       /* xgettext: no-wrap */
290       printf (_("\
291 If the TEXTDOMAIN parameter is not given, the domain is determined from the\n\
292 environment variable TEXTDOMAIN.  If the message catalog is not found in the\n\
293 regular directory, another location can be specified with the environment\n\
294 variable TEXTDOMAINDIR.\n\
295 When used with the -s option the program behaves like the 'echo' command.\n\
296 But it does not simply copy its arguments to stdout.  Instead those messages\n\
297 found in the selected catalog are translated.\n\
298 Standard search directory: %s\n"),
299               getenv ("IN_HELP2MAN") == NULL ? LOCALEDIR : "@localedir@");
300       printf ("\n");
301       /* TRANSLATORS: The first placeholder is the web address of the Savannah
302          project of this package.  The second placeholder is the bug-reporting
303          email address 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       printf(_("\
307 Report bugs in the bug tracker at <%s>\n\
308 or by email to <%s>.\n"),
309              "https://savannah.gnu.org/projects/gettext",
310              "bug-gettext@gnu.org");
311     }
312 
313   exit (status);
314 }
315