1 /* Test of the gettext functions.
2    Copyright (C) 2000-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 #include <libintl.h>
20 #include <locale.h>
21 #include <mcheck.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <error.h>
26 #include <errno.h>
27 
28 
29 const struct
30 {
31   const char *msgid;
32   const char *msgstr;
33 } msgs[] =
34 {
35 #define INPUT(Str) { Str,
36 #define OUTPUT(Str) Str },
37 #include TESTSTRS_H
38 };
39 
40 const char *catname[] =
41 {
42   [LC_MESSAGES] = "LC_MESSAGES",
43   [LC_TIME] = "LC_TIME",
44   [LC_NUMERIC] = "LC_NUMERIC"
45 };
46 
47 
48 static int positive_gettext_test (void);
49 static int negative_gettext_test (void);
50 static int positive_dgettext_test (const char *domain);
51 static int positive_dcgettext_test (const char *domain, int category);
52 static int negative_dcgettext_test (const char *domain, int category);
53 
54 
55 #define check_setlocale(cat, name) do {					      \
56     if (setlocale (cat, name) == NULL)					      \
57       {									      \
58 	printf ("%s:%u: setlocale (%s, \"%s\"): %m\n",			      \
59 		__FILE__, __LINE__, #cat, name);			      \
60 	result = 1;							      \
61       }									      \
62   } while (0)
63 
64 int
main(int argc,char * argv[])65 main (int argc, char *argv[])
66 {
67   int result = 0;
68 
69   /* For debugging.  */
70   mtrace ();
71 
72   /* This is the place where the .mo files are placed.  */
73   if (argc > 1)
74     {
75       bindtextdomain ("existing-domain", argv[1]);
76       bindtextdomain ("existing-time-domain", argv[1]);
77       bindtextdomain ("non-existing-domain", argv[1]);
78     }
79 
80   /* The locale the catalog is created for is "existing-category".  Now
81      set the various variables in question to this value and run the
82      test.  */
83   setenv ("LANGUAGE", "existing-locale", 1);
84   setenv ("LC_ALL", "non-existing-locale", 1);
85   setenv ("LC_MESSAGES", "non-existing-locale", 1);
86   setenv ("LC_CTYPE", "non-existing-locale", 1);
87   setenv ("LANG", "non-existing-locale", 1);
88   check_setlocale (LC_CTYPE, "de_DE.UTF-8");
89   check_setlocale (LC_MESSAGES, "de_DE.UTF-8");
90   unsetenv ("OUTPUT_CHARSET");
91   /* This is the name of the existing domain with a catalog for the
92      LC_MESSAGES category.  */
93   textdomain ("existing-domain");
94   puts ("test `gettext' with LANGUAGE set");
95   if (positive_gettext_test () != 0)
96     {
97       puts ("FAILED");
98       result = 1;
99     }
100   /* This is the name of a non-existing domain with a catalog for the
101      LC_MESSAGES category.  We leave this value set for the `dgettext'
102      and `dcgettext' tests.  */
103   textdomain ("non-existing-domain");
104   puts ("test `gettext' with LANGUAGE set");
105   if (negative_gettext_test () != 0)
106     {
107       puts ("FAILED");
108       result = 1;
109     }
110   puts ("test `dgettext' with LANGUAGE set");
111   if (positive_dgettext_test ("existing-domain") != 0)
112     {
113       puts ("FAILED");
114       result = 1;
115     }
116 
117   /* Now the same tests with LC_ALL deciding.  */
118   unsetenv ("LANGUAGE");
119   setenv ("LC_ALL", "existing-locale", 1);
120   check_setlocale (LC_ALL, "");
121   puts ("test `gettext' with LC_ALL set");
122   /* This is the name of the existing domain with a catalog for the
123      LC_MESSAGES category.  */
124   textdomain ("existing-domain");
125   if (positive_gettext_test () != 0)
126     {
127       puts ("FAILED");
128       result = 1;
129     }
130   /* This is the name of a non-existing domain with a catalog for the
131      LC_MESSAGES category.  We leave this value set for the `dgettext'
132      and `dcgettext' tests.  */
133   textdomain ("non-existing-domain");
134   puts ("test `gettext' with LC_ALL deciding");
135   if (negative_gettext_test () != 0)
136     {
137       puts ("FAILED");
138       result = 1;
139     }
140   puts ("test `dgettext' with LC_ALL deciding");
141   if (positive_dgettext_test ("existing-domain") != 0)
142     {
143       puts ("FAILED");
144       result = 1;
145     }
146 
147   /* Now the same tests with LC_MESSAGES deciding.  */
148   unsetenv ("LC_ALL");
149   setenv ("LC_MESSAGES", "existing-locale", 1);
150   check_setlocale (LC_MESSAGES, "");
151   setenv ("LC_TIME", "existing-locale", 1);
152   check_setlocale (LC_TIME, "");
153   setenv ("LC_NUMERIC", "non-existing-locale", 1);
154   char *what = setlocale (LC_NUMERIC, "");
155   if (what != NULL)
156     {
157       printf ("setlocale succeeded (%s), expected failure\n", what);
158       result = 1;
159     }
160 
161   puts ("test `gettext' with LC_MESSAGES set");
162   /* This is the name of the existing domain with a catalog for the
163      LC_MESSAGES category.  */
164   textdomain ("existing-domain");
165   if (positive_gettext_test () != 0)
166     {
167       puts ("FAILED");
168       result = 1;
169     }
170   /* This is the name of a non-existing domain with a catalog for the
171      LC_MESSAGES category.  We leave this value set for the `dgettext'
172      and `dcgettext' tests.  */
173   textdomain ("non-existing-domain");
174   puts ("test `gettext' with LC_MESSAGES deciding");
175   if (negative_gettext_test () != 0)
176     {
177       puts ("FAILED");
178       result = 1;
179     }
180   puts ("test `dgettext' with LC_MESSAGES deciding");
181   if (positive_dgettext_test ("existing-domain") != 0)
182     {
183       puts ("FAILED");
184       result = 1;
185     }
186   puts ("test `dcgettext' with category == LC_MESSAGES");
187   if (positive_dcgettext_test ("existing-domain", LC_MESSAGES) != 0)
188     {
189       puts ("FAILED");
190       result = 1;
191     }
192   /* Try a different category.  For this we also switch the domain.  */
193   puts ("test `dcgettext' with LANGUAGE == LC_TIME");
194   if (positive_dcgettext_test ("existing-time-domain", LC_TIME) != 0)
195     {
196       puts ("FAILED");
197       result = 1;
198     }
199   /* This time use a category for which there is no catalog.  */
200   puts ("test `dcgettext' with LANGUAGE == LC_NUMERIC");
201   if (negative_dcgettext_test ("existing-domain", LC_NUMERIC) != 0)
202     {
203       puts ("FAILED");
204       result = 1;
205     }
206 
207   /* Now the same tests with LANG deciding.  */
208   unsetenv ("LC_MESSAGES");
209   unsetenv ("LC_CTYPE");
210   unsetenv ("LC_TIME");
211   unsetenv ("LC_NUMERIC");
212   setenv ("LANG", "existing-locale", 1);
213   check_setlocale (LC_ALL, "");
214   /* This is the name of the existing domain with a catalog for the
215      LC_MESSAGES category.  */
216   textdomain ("existing-domain");
217   puts ("test `gettext' with LANG set");
218   if (positive_gettext_test () != 0)
219     {
220       puts ("FAILED");
221       result = 1;
222     }
223   /* This is the name of a non-existing domain with a catalog for the
224      LC_MESSAGES category.  We leave this value set for the `dgettext'
225      and `dcgettext' tests.  */
226   textdomain ("non-existing-domain");
227   puts ("test `gettext' with LANG set");
228   if (negative_gettext_test () != 0)
229     {
230       puts ("FAILED");
231       result = 1;
232     }
233   puts ("test `dgettext' with LANG set");
234   if (positive_dgettext_test ("existing-domain") != 0)
235     {
236       puts ("FAILED");
237       result = 1;
238     }
239 
240   check_setlocale (LC_ALL, "C");
241 
242   return result;
243 }
244 
245 
246 static int
positive_gettext_test(void)247 positive_gettext_test (void)
248 {
249   size_t cnt;
250   int result = 0;
251 
252   for (cnt = 0; cnt < sizeof (msgs) / sizeof (msgs[0]); ++cnt)
253     {
254       const char *found = gettext (msgs[cnt].msgid);
255 
256       if (found == NULL
257 	  || (msgs[cnt].msgstr[0] != '\0'
258 	      && strcmp (found, msgs[cnt].msgstr) != 0))
259 	{
260 	  /* Oops, shouldn't happen.  */
261 	  printf ("\
262   gettext (\"%s\") failed, returned \"%s\", expected \"%s\"\n",
263 		  msgs[cnt].msgid, found, msgs[cnt].msgstr);
264 	  result = 1;
265 	}
266     }
267 
268   return result;
269 }
270 
271 
272 static int
negative_gettext_test(void)273 negative_gettext_test (void)
274 {
275   size_t cnt;
276   int result = 0;
277 
278   for (cnt = 0; cnt < sizeof (msgs) / sizeof (msgs[0]); ++cnt)
279     {
280       const char *found = gettext (msgs[cnt].msgid);
281 
282       if (found != msgs[cnt].msgid)
283 	{
284 	  /* Oops, shouldn't happen.  */
285 	  printf ("  gettext (\"%s\") failed\n", msgs[cnt].msgid);
286 	  result = 1;
287 	}
288     }
289 
290   return result;
291 }
292 
293 
294 static int
positive_dgettext_test(const char * domain)295 positive_dgettext_test (const char *domain)
296 {
297   size_t cnt;
298   int result = 0;
299 
300   for (cnt = 0; cnt < sizeof (msgs) / sizeof (msgs[0]); ++cnt)
301     {
302       const char *found = dgettext (domain, msgs[cnt].msgid);
303 
304       if (found == NULL
305 	  || (msgs[cnt].msgstr[0] != '\0'
306 	      && strcmp (found, msgs[cnt].msgstr) != 0))
307 	{
308 	  /* Oops, shouldn't happen.  */
309 	  printf ("\
310   dgettext (\"%s\", \"%s\") failed, returned \"%s\", expected \"%s\"\n",
311 		  domain, msgs[cnt].msgid, found, msgs[cnt].msgstr);
312 	  result = 1;
313 	}
314     }
315 
316   return result;
317 }
318 
319 
320 static int
positive_dcgettext_test(const char * domain,int category)321 positive_dcgettext_test (const char *domain, int category)
322 {
323   size_t cnt;
324   int result = 0;
325 
326   for (cnt = 0; cnt < sizeof (msgs) / sizeof (msgs[0]); ++cnt)
327     {
328       const char *found = dcgettext (domain, msgs[cnt].msgid, category);
329 
330       if (found == NULL
331 	  || (msgs[cnt].msgstr[0] != '\0'
332 	      && strcmp (found, msgs[cnt].msgstr) != 0))
333 	{
334 	  /* Oops, shouldn't happen.  */
335 	  printf ("\
336   dcgettext (\"%s\", \"%s\", %s) failed, returned \"%s\", expected \"%s\"\n",
337 		  domain, msgs[cnt].msgid, catname[category], found,
338 		  msgs[cnt].msgstr);
339 	  result = 1;
340 	}
341     }
342 
343   return result;
344 }
345 
346 
347 static int
negative_dcgettext_test(const char * domain,int category)348 negative_dcgettext_test (const char *domain, int category)
349 {
350   size_t cnt;
351   int result = 0;
352 
353   for (cnt = 0; cnt < sizeof (msgs) / sizeof (msgs[0]); ++cnt)
354     {
355       const char *found = dcgettext (domain, msgs[cnt].msgid, category);
356 
357       if (found != msgs[cnt].msgid)
358 	{
359 	  /* Oops, shouldn't happen.  */
360 	  printf ("  dcgettext (\"%s\", \"%s\", %s) failed\n",
361 		  domain, msgs[cnt].msgid, catname[category]);
362 	  result = 1;
363 	}
364     }
365 
366   return result;
367 }
368