• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Localization test program for CUPS.
3  *
4  * Usage:
5  *
6  *   ./testlang [-l locale] [-p ppd] ["String to localize"]
7  *
8  * Copyright © 2020-2024 by OpenPrinting.
9  * Copyright © 2007-2017 by Apple Inc.
10  * Copyright © 1997-2006 by Easy Software Products.
11  *
12  * Licensed under Apache License v2.0.  See the file "LICENSE" for more
13  * information.
14  */
15 
16 /*
17  * Include necessary headers...
18  */
19 
20 #include "cups-private.h"
21 #include "ppd-private.h"
22 #ifdef __APPLE__
23 #  include <CoreFoundation/CoreFoundation.h>
24 #endif /* __APPLE__ */
25 #include <cups/dir.h>
26 
27 
28 /*
29  * Local functions...
30  */
31 
32 static int	show_ppd(const char *filename);
33 #ifdef __APPLE__
34 static int	test_apple(void);
35 #endif // __APPLE__
36 static int	test_language(const char *locale);
37 static int	test_string(cups_lang_t *language, const char *msgid);
38 static void	usage(void);
39 
40 
41 /*
42  * 'main()' - Load the specified language and show the strings for yes and no.
43  */
44 
45 int					/* O - Exit status */
main(int argc,char * argv[])46 main(int  argc,				/* I - Number of command-line arguments */
47      char *argv[])			/* I - Command-line arguments */
48 {
49   int		i;			/* Looping var */
50   const char	*opt;			/* Current option */
51   int		errors = 0;		/* Number of errors */
52   int		dotests = 1;		/* Do standard tests? */
53   const char	*lang = NULL;		/* Single language test? */
54   cups_lang_t	*language = NULL;	/* Message catalog */
55 
56 
57  /*
58   * Parse command-line...
59   */
60 
61   _cupsSetLocale(argv);
62 
63   for (i = 1; i < argc; i ++)
64   {
65     if (argv[i][0] == '-')
66     {
67       if (!strcmp(argv[i], "--help"))
68       {
69         usage();
70       }
71       else
72       {
73         for (opt = argv[i] + 1; *opt; opt ++)
74         {
75           switch (*opt)
76           {
77             case 'l' :
78                 i ++;
79                 if (i >= argc)
80                 {
81                   usage();
82                   return (1);
83                 }
84 
85                 lang = argv[i];
86 		break;
87 
88 	    case 'p' :
89                 i ++;
90                 if (i >= argc)
91                 {
92                   usage();
93                   return (1);
94                 }
95 
96 		dotests = 0;
97 		errors += show_ppd(argv[i]);
98                 break;
99 
100             default :
101                 usage();
102                 return (1);
103 	  }
104         }
105       }
106     }
107     else
108     {
109       if (!language)
110         language = cupsLangGet(lang);
111 
112       dotests = 0;
113       errors += test_string(language, argv[i]);
114     }
115   }
116 
117   if (dotests)
118   {
119     if (lang)
120     {
121      /*
122       * Test a single language...
123       */
124 
125       errors += test_language(lang);
126     }
127     else
128     {
129      /*
130       * Test all locales we find in LOCALEDIR...
131       */
132 
133       cups_dir_t	*dir;		/* Locale directory */
134       cups_dentry_t	*dent;		/* Directory entry */
135 
136       if ((dir = cupsDirOpen(getenv("LOCALEDIR"))) != NULL)
137       {
138 	while ((dent = cupsDirRead(dir)) != NULL)
139 	  errors += test_language(dent->filename);
140       }
141       else
142       {
143         // No LOCALEDIR, just use the default language...
144         errors += test_language(NULL);
145       }
146 
147       cupsDirClose(dir);
148     }
149 
150 #ifdef __APPLE__
151     errors += test_apple();
152 #endif // __APPLE__
153 
154     if (!errors)
155       puts("ALL TESTS PASSED");
156   }
157 
158   return (errors > 0);
159 }
160 
161 
162 /*
163  * 'show_ppd()' - Show localized strings in a PPD file.
164  *
165  * TODO: Move this to the testppd program.
166  */
167 
168 static int				/* O - Number of errors */
show_ppd(const char * filename)169 show_ppd(const char *filename)		/* I - Filename */
170 {
171   ppd_file_t	*ppd;			/* PPD file */
172   ppd_option_t	*option;		/* PageSize option */
173   ppd_choice_t	*choice;		/* PageSize/Letter choice */
174   char		buffer[1024];		/* String buffer */
175 
176 
177   if ((ppd = ppdOpenFile(filename)) == NULL)
178   {
179     printf("Unable to open PPD file \"%s\".\n", filename);
180     return (1);
181   }
182 
183   ppdLocalize(ppd);
184 
185   if ((option = ppdFindOption(ppd, "PageSize")) == NULL)
186   {
187     puts("No PageSize option.");
188     return (1);
189   }
190   else
191   {
192     printf("PageSize: %s\n", option->text);
193 
194     if ((choice = ppdFindChoice(option, "Letter")) == NULL)
195     {
196       puts("No Letter PageSize choice.");
197       return (1);
198     }
199     else
200     {
201       printf("Letter: %s\n", choice->text);
202     }
203   }
204 
205   printf("media-empty: %s\n", ppdLocalizeIPPReason(ppd, "media-empty", NULL, buffer, sizeof(buffer)));
206 
207   ppdClose(ppd);
208 
209   return (0);
210 }
211 
212 
213 #ifdef __APPLE__
214 /*
215  * 'test_apple()' - Test macOS locale handing...
216  */
217 
218 static int				/* O - Number of errors */
test_apple(void)219 test_apple(void)
220 {
221   int		errors = 0;		/* Number of errors */
222   CFIndex	i,			/* Looping var */
223 		num_locales;		/* Number of locales */
224   CFArrayRef	locales;		/* Locales */
225   CFStringRef	locale_id,		/* Current locale ID */
226 		language_id;		/* Current language ID */
227   cups_lang_t	*language = NULL;	/* Message catalog */
228   char		locale_str[256],	/* Locale ID C string */
229 		language_str[256],	/* Language ID C string */
230 		buffer[1024],		/* String buffer */
231 		*bufptr;		/* Pointer to ".UTF-8" in POSIX locale */
232   size_t	buflen;			/* Length of POSIX locale */
233 
234 
235  /*
236   * Test all possible language IDs for compatibility with _cupsAppleLocale...
237   */
238 
239   locales     = CFLocaleCopyAvailableLocaleIdentifiers();
240   num_locales = CFArrayGetCount(locales);
241 
242   printf("CFLocaleCopyAvailableLocaleIdentifiers: %d locales\n", (int)num_locales);
243 
244   for (i = 0; i < num_locales; i ++)
245   {
246     locale_id   = CFArrayGetValueAtIndex(locales, i);
247     language_id = CFLocaleCreateCanonicalLanguageIdentifierFromString(kCFAllocatorDefault, locale_id);
248 
249     printf("CFStringGetCString(locale_id %d): ", (int)i);
250     if (!locale_id || !CFStringGetCString(locale_id, locale_str, (CFIndex)sizeof(locale_str), kCFStringEncodingASCII))
251     {
252       puts("FAIL");
253       errors ++;
254       continue;
255     }
256     else
257       printf("PASS (\"%s\")\n", locale_str);
258 
259     printf("CFStringGetCString(language_id %d): ", (int)i);
260     if (!language_id || !CFStringGetCString(language_id, language_str, (CFIndex)sizeof(language_str), kCFStringEncodingASCII))
261     {
262       printf("%d %s: FAIL (unable to get language ID string)\n", (int)i + 1, locale_str);
263       errors ++;
264       continue;
265     }
266     else
267       printf("PASS (\"%s\")\n", language_str);
268 
269     printf("_cupsAppleLocale(\"%s\"): ", language_str);
270     if (!_cupsAppleLocale(language_id, buffer, sizeof(buffer)))
271     {
272       puts("FAIL");
273       errors ++;
274       continue;
275     }
276     else
277       printf("PASS (\"%s\")\n", buffer);
278 
279     if ((bufptr = strstr(buffer, ".UTF-8")) != NULL)
280       buflen = (size_t)(bufptr - buffer);
281     else
282       buflen = strlen(buffer);
283 
284     printf("cupsLangGet(\"%s\"): ", buffer);
285     if ((language = cupsLangGet(buffer)) == NULL)
286     {
287       puts("FAIL");
288       errors ++;
289       continue;
290     }
291     else if (strncasecmp(language->language, buffer, buflen))
292     {
293       printf("FAIL (got \"%s\")\n", language->language);
294       errors ++;
295       continue;
296     }
297     else
298       puts("PASS");
299   }
300 
301   CFRelease(locales);
302 
303   return (errors);
304 }
305 #endif // __APPLE__
306 
307 
308 /*
309  * 'test_language()' - Test a specific language...
310  */
311 
312 static int				/* O - Number of errors */
test_language(const char * lang)313 test_language(const char *lang)		/* I - Locale language code, NULL for default */
314 {
315   int		i;			/* Looping var */
316   int		errors = 0;		/* Number of errors */
317   cups_lang_t	*language = NULL,	/* Message catalog */
318 		*language2 = NULL;	/* Message catalog (second time) */
319   struct lconv	*loc;			/* Locale data */
320   char		buffer[1024];		/* String buffer */
321   double	number;			/* Number */
322   static const char * const tests[] =	/* Test strings */
323   {
324     "1",
325     "-1",
326     "3",
327     "5.125"
328   };
329 
330 
331   // Override the locale environment as needed...
332   if (lang)
333   {
334     // Test the specified locale code...
335     setenv("LANG", lang, 1);
336     setenv("SOFTWARE", "CUPS/" CUPS_SVERSION, 1);
337 
338     printf("cupsLangGet(\"%s\"): ", lang);
339     if ((language = cupsLangGet(lang)) == NULL)
340     {
341       puts("FAIL");
342       errors ++;
343     }
344     else if (strcasecmp(language->language, lang))
345     {
346       printf("FAIL (got \"%s\")\n", language->language);
347       errors ++;
348     }
349     else
350       puts("PASS");
351 
352     printf("cupsLangGet(\"%s\") again: ", lang);
353     if ((language2 = cupsLangGet(lang)) == NULL)
354     {
355       puts("FAIL");
356       errors ++;
357     }
358     else if (strcasecmp(language2->language, lang))
359     {
360       printf("FAIL (got \"%s\")\n", language2->language);
361       errors ++;
362     }
363     else if (language2 != language)
364     {
365       puts("FAIL (cache failure)");
366       errors ++;
367     }
368     else
369       puts("PASS");
370   }
371   else
372   {
373     // Test the default locale...
374     fputs("cupsLangDefault: ", stdout);
375     if ((language = cupsLangDefault()) == NULL)
376     {
377       puts("FAIL");
378       errors ++;
379     }
380     else
381       puts("PASS");
382 
383     fputs("cupsLangDefault again: ", stdout);
384     if ((language2 = cupsLangDefault()) == NULL)
385     {
386       puts("FAIL");
387       errors ++;
388     }
389     else if (language2 != language)
390     {
391       puts("FAIL (cache failure)");
392       errors ++;
393     }
394     else
395       puts("PASS");
396   }
397 
398   printf("language->language: \"%s\"\n", language->language);
399   printf("_cupsEncodingName(language): \"%s\"\n", _cupsEncodingName(language->encoding));
400 
401   errors += test_string(language, "No");
402   errors += test_string(language, "Yes");
403 
404   if (language != language2)
405   {
406     printf("language2->language: \"%s\"\n", language2->language);
407     printf("_cupsEncodingName(language2): \"%s\"\n", _cupsEncodingName(language2->encoding));
408   }
409 
410   loc = localeconv();
411 
412   for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i ++)
413   {
414     number = _cupsStrScand(tests[i], NULL, loc);
415 
416     printf("_cupsStrScand(\"%s\"): %f\n", tests[i], number);
417 
418     _cupsStrFormatd(buffer, buffer + sizeof(buffer), number, loc);
419 
420     printf("_cupsStrFormatd(%f): ", number);
421 
422     if (strcmp(buffer, tests[i]))
423     {
424       errors ++;
425       printf("FAIL (got \"%s\")\n", buffer);
426     }
427     else
428       puts("PASS");
429   }
430 
431   return (errors);
432 }
433 
434 
435 /*
436  * 'test_string()' - Test the localization of a string.
437  */
438 
439 static int				/* O - 1 on failure, 0 on success */
test_string(cups_lang_t * language,const char * msgid)440 test_string(cups_lang_t *language,	/* I - Language */
441             const char  *msgid)		/* I - Message */
442 {
443   const char  *msgstr;			/* Localized string */
444 
445 
446  /*
447   * Get the localized string and then see if we got what we expected.
448   *
449   * For the POSIX locale, the string pointers should be the same.
450   * For any other locale, the string pointers should be different.
451   */
452 
453   printf("_cupsLangString(\"%s\"): ", msgid);
454   msgstr = _cupsLangString(language, msgid);
455   if (strcmp(language->language, "C") && msgid == msgstr)
456   {
457     puts("FAIL (no message catalog loaded)");
458     return (1);
459   }
460   else if (!strcmp(language->language, "C") && msgid != msgstr)
461   {
462     puts("FAIL (POSIX locale is localized)");
463     return (1);
464   }
465 
466   printf("PASS (\"%s\")\n", msgstr);
467 
468   return (0);
469 }
470 
471 
472 /*
473  * 'usage()' - Show program usage.
474  */
475 
476 static void
usage(void)477 usage(void)
478 {
479   puts("Usage: ./testlang [-l locale] [-p ppd] [\"String to localize\"]");
480   puts("");
481   puts("If no arguments are specified, all locales are tested.");
482 }
483