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