1 /* ngettext - retrieve plural form string from message catalog and print it.
2 Copyright (C) 1995-1997, 2000-2007, 2012, 2018-2023 Free Software Foundation, Inc.
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
16
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20
21 #include <getopt.h>
22 #include <stdbool.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <locale.h>
27 #include <errno.h>
28
29 #include "attribute.h"
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 "escapes.h"
39 #include "gettext.h"
40
41 #define _(str) gettext (str)
42
43 /* If true, expand escape sequences in strings before looking in the
44 message catalog. */
45 static int do_expand;
46
47 /* Long options. */
48 static const struct option long_options[] =
49 {
50 { "context", required_argument, NULL, 'c' },
51 { "domain", required_argument, NULL, 'd' },
52 { "help", no_argument, NULL, 'h' },
53 { "version", no_argument, NULL, 'V' },
54 { NULL, 0, NULL, 0 }
55 };
56
57 /* Forward declaration of local functions. */
58 _GL_NORETURN_FUNC static void usage (int status);
59
60 int
61 main (int argc, char *argv[])
62 {
63 int optchar;
64 const char *msgid;
65 const char *msgid_plural;
66 const char *count;
67 unsigned long n;
68
69 /* Default values for command line options. */
70 bool do_help = false;
71 bool do_version = false;
72 const char *domain = getenv ("TEXTDOMAIN");
73 const char *domaindir = getenv ("TEXTDOMAINDIR");
74 const char *context = NULL;
75 do_expand = false;
76
77 /* Set program name for message texts. */
78 set_program_name (argv[0]);
79
80 /* Set locale via LC_ALL. */
81 setlocale (LC_ALL, "");
82
83 /* Set the text message domain. */
84 bindtextdomain (PACKAGE, relocate (LOCALEDIR));
85 textdomain (PACKAGE);
86
87 /* Ensure that write errors on stdout are detected. */
88 atexit (close_stdout);
89
90 /* Parse command line options. */
91 while ((optchar = getopt_long (argc, argv, "+c:d:eEhV", long_options, NULL))
92 != EOF)
93 switch (optchar)
94 {
95 case '\0': /* Long option. */
96 break;
97 case 'c':
98 context = optarg;
99 break;
100 case 'd':
101 domain = optarg;
102 break;
103 case 'e':
104 do_expand = true;
105 break;
106 case 'E':
107 /* Ignore. Just for compatibility. */
108 break;
109 case 'h':
110 do_help = true;
111 break;
112 case 'V':
113 do_version = true;
114 break;
115 default:
116 usage (EXIT_FAILURE);
117 }
118
119 /* Version information is requested. */
120 if (do_version)
121 {
122 printf ("%s (GNU %s) %s\n", last_component (program_name),
123 PACKAGE, VERSION);
124 /* xgettext: no-wrap */
125 printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
126 License GPLv3+: GNU GPL version 3 or later <%s>\n\
127 This is free software: you are free to change and redistribute it.\n\
128 There is NO WARRANTY, to the extent permitted by law.\n\
129 "),
130 "1995-1997, 2000-2023", "https://gnu.org/licenses/gpl.html");
131 printf (_("Written by %s.\n"), proper_name ("Ulrich Drepper"));
132 exit (EXIT_SUCCESS);
133 }
134
135 /* Help is requested. */
136 if (do_help)
137 usage (EXIT_SUCCESS);
138
139 /* More optional command line options. */
140 switch (argc - optind)
141 {
142 default:
143 error (EXIT_FAILURE, 0, _("too many arguments"));
144
145 case 4:
146 domain = argv[optind++];
147 FALLTHROUGH;
148
149 case 3:
150 break;
151
152 case 2:
153 case 1:
154 case 0:
155 error (EXIT_FAILURE, 0, _("missing arguments"));
156 }
157
158 /* Now the mandatory command line options. */
159 msgid = argv[optind++];
160 msgid_plural = argv[optind++];
161 count = argv[optind++];
162
163 if (optind != argc)
164 abort ();
165
166 {
167 char *endp;
168 unsigned long tmp_val;
169
170 errno = 0;
171 tmp_val = strtoul (count, &endp, 10);
172 if (errno == 0 && count[0] != '\0' && endp[0] == '\0')
173 n = tmp_val;
174 else
175 /* When COUNT is not valid, use plural. */
176 n = 99;
177 }
178
179 /* Expand escape sequences if enabled. */
180 if (do_expand)
181 {
182 msgid = expand_escapes (msgid, NULL);
183 msgid_plural = expand_escapes (msgid_plural, NULL);
184 }
185
186 /* If no domain name is given we don't translate, and we use English
187 plural form handling. */
188 if (domain == NULL || domain[0] == '\0')
189 fputs (n == 1 ? msgid : msgid_plural, stdout);
190 else
191 {
192 /* Bind domain to appropriate directory. */
193 if (domaindir != NULL && domaindir[0] != '\0')
194 bindtextdomain (domain, domaindir);
195
196 /* Write out the result. */
197 fputs ((context != NULL
198 ? dnpgettext_expr (domain, context, msgid, msgid_plural, n)
199 : dngettext (domain, msgid, msgid_plural, n)),
200 stdout);
201 }
202
203 exit (EXIT_SUCCESS);
204 }
205
206
207 /* Display usage information and exit. */
208 static void
209 usage (int status)
210 {
211 if (status != EXIT_SUCCESS)
212 fprintf (stderr, _("Try '%s --help' for more information.\n"),
213 program_name);
214 else
215 {
216 /* xgettext: no-wrap */
217 printf (_("\
218 Usage: %s [OPTION] [TEXTDOMAIN] MSGID MSGID-PLURAL COUNT\n\
219 "), program_name);
220 printf ("\n");
221 /* xgettext: no-wrap */
222 printf (_("\
223 Display native language translation of a textual message whose grammatical\n\
224 form depends on a number.\n"));
225 printf ("\n");
226 /* xgettext: no-wrap */
227 printf (_("\
228 -d, --domain=TEXTDOMAIN retrieve translated message from TEXTDOMAIN\n"));
229 printf (_("\
230 -c, --context=CONTEXT specify context for MSGID\n"));
231 printf (_("\
232 -e enable expansion of some escape sequences\n"));
233 printf (_("\
234 -E (ignored for compatibility)\n"));
235 printf (_("\
236 [TEXTDOMAIN] retrieve translated message from TEXTDOMAIN\n"));
237 printf (_("\
238 MSGID MSGID-PLURAL translate MSGID (singular) / MSGID-PLURAL (plural)\n"));
239 printf (_("\
240 COUNT choose singular/plural form based on this value\n"));
241 printf ("\n");
242 printf (_("\
243 Informative output:\n"));
244 printf (_("\
245 -h, --help display this help and exit\n"));
246 printf (_("\
247 -V, --version display version information and exit\n"));
248 printf ("\n");
249 /* xgettext: no-wrap */
250 printf (_("\
251 If the TEXTDOMAIN parameter is not given, the domain is determined from the\n\
252 environment variable TEXTDOMAIN. If the message catalog is not found in the\n\
253 regular directory, another location can be specified with the environment\n\
254 variable TEXTDOMAINDIR.\n\
255 Standard search directory: %s\n"),
256 getenv ("IN_HELP2MAN") == NULL ? LOCALEDIR : "@localedir@");
257 printf ("\n");
258 /* TRANSLATORS: The first placeholder is the web address of the Savannah
259 project of this package. The second placeholder is the bug-reporting
260 email address for this package. Please add _another line_ saying
261 "Report translation bugs to <...>\n" with the address for translation
262 bugs (typically your translation team's web or email address). */
263 printf(_("\
264 Report bugs in the bug tracker at <%s>\n\
265 or by email to <%s>.\n"),
266 "https://savannah.gnu.org/projects/gettext",
267 "bug-gettext@gnu.org");
268 }
269
270 exit (status);
271 }