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