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