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 "attribute.h"
31 #include "noreturn.h"
32 #include "closeout.h"
33 #include "error.h"
34 #include "progname.h"
35 #include "relocatable.h"
36 #include "basename-lgpl.h"
37 #include "xalloc.h"
38 #include "propername.h"
39 #include "xsetenv.h"
40 #include "../../gettext-runtime/src/escapes.h"
41
42 /* Make sure we use the included libintl, not the system's one. */
43 #undef _LIBINTL_H
44 #include "libgnuintl.h"
45
46 #if defined _WIN32 && !defined __CYGWIN__
47 # undef setlocale
48 # define setlocale fake_setlocale
49 extern char *setlocale (int category, const char *locale);
50 #endif
51
52 #define _(str) gettext (str)
53
54 /* If false, add newline after last string. This makes only sense in
55 the 'echo' emulation mode. */
56 static bool inhibit_added_newline;
57
58 /* If true, expand escape sequences in strings before looking in the
59 message catalog. */
60 static bool do_expand;
61
62 /* Long options. */
63 static const struct option long_options[] =
64 {
65 { "domain", required_argument, NULL, 'd' },
66 { "env", required_argument, NULL, '=' },
67 { "help", no_argument, NULL, 'h' },
68 { "shell-script", no_argument, NULL, 's' },
69 { "version", no_argument, NULL, 'V' },
70 { NULL, 0, NULL, 0 }
71 };
72
73 /* Forward declaration of local functions. */
74 _GL_NORETURN_FUNC static void usage (int status);
75
76 int
77 main (int argc, char *argv[])
78 {
79 int optchar;
80 const char *msgid;
81
82 /* Default values for command line options. */
83 bool do_help = false;
84 bool do_shell = false;
85 bool do_version = false;
86 bool environ_changed = false;
87 const char *domain = getenv ("TEXTDOMAIN");
88 const char *domaindir = getenv ("TEXTDOMAINDIR");
89 inhibit_added_newline = false;
90 do_expand = false;
91
92 /* Set program name for message texts. */
93 set_program_name (argv[0]);
94
95 /* Set locale via LC_ALL. */
96 setlocale (LC_ALL, "");
97
98 /* Set the text message domain. */
99 bindtextdomain (PACKAGE, relocate (LOCALEDIR));
100 textdomain (PACKAGE);
101
102 /* Ensure that write errors on stdout are detected. */
103 atexit (close_stdout);
104
105 /* Parse command line options. */
106 while ((optchar = getopt_long (argc, argv, "+d:eEhnsV", long_options, NULL))
107 != EOF)
108 switch (optchar)
109 {
110 case '\0': /* Long option. */
111 break;
112 case 'd':
113 domain = optarg;
114 break;
115 case 'e':
116 do_expand = true;
117 break;
118 case 'E':
119 /* Ignore. Just for compatibility. */
120 break;
121 case 'h':
122 do_help = true;
123 break;
124 case 'n':
125 inhibit_added_newline = true;
126 break;
127 case 's':
128 do_shell = true;
129 break;
130 case 'V':
131 do_version = true;
132 break;
133 case '=':
134 {
135 /* Undocumented option --env sets an environment variable. */
136 char *separator = strchr (optarg, '=');
137 if (separator != NULL)
138 {
139 *separator = '\0';
140 xsetenv (optarg, separator + 1, 1);
141 environ_changed = true;
142 break;
143 }
144 }
145 FALLTHROUGH;
146 default:
147 usage (EXIT_FAILURE);
148 }
149
150 if (environ_changed)
151 /* Set locale again via LC_ALL. */
152 setlocale (LC_ALL, "");
153
154 /* Version information is requested. */
155 if (do_version)
156 {
157 printf ("%s (GNU %s) %s\n", last_component (program_name),
158 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 <%s>\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", "https://gnu.org/licenses/gpl.html");
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_escapes (msgid, &inhibit_added_newline);
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_escapes (msgid, &inhibit_added_newline);
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 (!inhibit_added_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 first placeholder is the web address of the Savannah
303 project of this package. The second placeholder is the bug-reporting
304 email address for this package. Please add _another line_ saying
305 "Report translation bugs to <...>\n" with the address for translation
306 bugs (typically your translation team's web or email address). */
307 printf(_("\
308 Report bugs in the bug tracker at <%s>\n\
309 or by email to <%s>.\n"),
310 "https://savannah.gnu.org/projects/gettext",
311 "bug-gettext@gnu.org");
312 }
313
314 exit (status);
315 }