/src/libcups/cups/language.c
Line | Count | Source (jump to first uncovered line) |
1 | | // |
2 | | // I18N/language support for CUPS. |
3 | | // |
4 | | // Copyright © 2022 by OpenPrinting. |
5 | | // Copyright © 2007-2017 by Apple Inc. |
6 | | // Copyright © 1997-2007 by Easy Software Products. |
7 | | // |
8 | | // Licensed under Apache License v2.0. See the file "LICENSE" for more |
9 | | // information. |
10 | | // |
11 | | |
12 | | #include "cups-private.h" |
13 | | #include <sys/stat.h> |
14 | | #if _WIN32 |
15 | | # include <io.h> |
16 | | #else |
17 | | # include <unistd.h> |
18 | | #endif // _WIN32 |
19 | | |
20 | | #include "strings/ca_strings.h" |
21 | | #include "strings/cs_strings.h" |
22 | | #include "strings/da_strings.h" |
23 | | #include "strings/de_strings.h" |
24 | | #include "strings/en_strings.h" |
25 | | #include "strings/es_strings.h" |
26 | | #include "strings/fr_strings.h" |
27 | | #include "strings/it_strings.h" |
28 | | #include "strings/ja_strings.h" |
29 | | #include "strings/pt_BR_strings.h" |
30 | | #include "strings/ru_strings.h" |
31 | | #include "strings/zh_CN_strings.h" |
32 | | |
33 | | |
34 | | // |
35 | | // Types... |
36 | | // |
37 | | |
38 | | typedef struct _cups_message_s // Message catalog entry |
39 | | { |
40 | | char *key, // Key string |
41 | | *text; // Localized text string |
42 | | } _cups_message_t; |
43 | | |
44 | | struct _cups_lang_s // Language Cache |
45 | | { |
46 | | cups_lang_t *next; // Next language in cache |
47 | | cups_rwlock_t rwlock; // Reader/writer lock |
48 | | char language[16]; // Language/locale name |
49 | | size_t num_messages, // Number of messages |
50 | | alloc_messages; // Allocated messages |
51 | | _cups_message_t *messages; // Messages |
52 | | }; |
53 | | |
54 | | |
55 | | // |
56 | | // Local globals... |
57 | | // |
58 | | |
59 | | static cups_mutex_t lang_mutex = CUPS_MUTEX_INITIALIZER; |
60 | | // Mutex to control access to cache |
61 | | static cups_lang_t *lang_cache = NULL; |
62 | | // Language string cache |
63 | | static char *lang_directory = NULL; |
64 | | // Directory for strings files... |
65 | | |
66 | | |
67 | | // |
68 | | // Local functions... |
69 | | // |
70 | | |
71 | | static cups_lang_t *cups_lang_new(const char *language); |
72 | | static int cups_message_compare(_cups_message_t *m1, _cups_message_t *m2); |
73 | | |
74 | | |
75 | | // |
76 | | // 'cupsLangAddStrings()' - Add strings for the specified language. |
77 | | // |
78 | | |
79 | | bool // O - `true` on success, `false` on failure |
80 | | cupsLangAddStrings( |
81 | | const char *language, // I - Language name |
82 | | const char *strings) // I - Contents of ".strings" file |
83 | 0 | { |
84 | 0 | cups_lang_t *lang; // Language data |
85 | | |
86 | |
|
87 | 0 | if ((lang = cupsLangFind(language)) != NULL) |
88 | 0 | return (cupsLangLoadStrings(lang, NULL, strings)); |
89 | 0 | else |
90 | 0 | return (false); |
91 | 0 | } |
92 | | |
93 | | |
94 | | // |
95 | | // 'cupsLangFind()' - Find a language localization. |
96 | | // |
97 | | |
98 | | cups_lang_t * // O - Language data |
99 | | cupsLangFind(const char *language) // I - Language or locale name |
100 | 1 | { |
101 | 1 | char langname[16]; // Requested language name |
102 | 1 | cups_lang_t *lang; // Current language... |
103 | | |
104 | | |
105 | 1 | DEBUG_printf("2cupsLangFind(language=\"%s\")", language); |
106 | | |
107 | 1 | if (!language) |
108 | 0 | return (cupsLangDefault()); |
109 | | |
110 | 1 | cupsMutexLock(&lang_mutex); |
111 | | |
112 | 1 | cupsCopyString(langname, language, sizeof(langname)); |
113 | 1 | if (langname[2] == '-') |
114 | 0 | langname[2] = '_'; |
115 | | |
116 | 1 | for (lang = lang_cache; lang; lang = lang->next) |
117 | 0 | { |
118 | 0 | if (!_cups_strcasecmp(lang->language, langname)) |
119 | 0 | break; |
120 | 0 | } |
121 | | |
122 | 1 | if (!lang) |
123 | 1 | { |
124 | | // Create the language if it doesn't exist... |
125 | 1 | lang = cups_lang_new(langname); |
126 | 1 | } |
127 | | |
128 | 1 | cupsMutexUnlock(&lang_mutex); |
129 | | |
130 | 1 | return (lang); |
131 | 1 | } |
132 | | |
133 | | |
134 | | // |
135 | | // 'cupsLangFormatString()' - Create a localized formatted string. |
136 | | // |
137 | | |
138 | | const char * // O - Formatted string |
139 | | cupsLangFormatString( |
140 | | cups_lang_t *lang, // I - Language data |
141 | | char *buffer, // I - Output buffer |
142 | | size_t bufsize, // I - Size of output buffer |
143 | | const char *format, // I - Printf-style format string |
144 | | ...) // I - Additional arguments |
145 | 0 | { |
146 | 0 | va_list ap; // Pointer to additional arguments |
147 | | |
148 | |
|
149 | 0 | va_start(ap, format); |
150 | 0 | vsnprintf(buffer, bufsize, cupsLangGetString(lang, format), ap); |
151 | 0 | va_end(ap); |
152 | |
|
153 | 0 | return (buffer); |
154 | 0 | } |
155 | | |
156 | | |
157 | | // |
158 | | // 'cupsLangGetName()' - Get the language name. |
159 | | // |
160 | | |
161 | | const char * // O - Language name |
162 | | cupsLangGetName(cups_lang_t *lang) // I - Language data |
163 | 5.36k | { |
164 | 5.36k | return (lang ? lang->language : NULL); |
165 | 5.36k | } |
166 | | |
167 | | |
168 | | // |
169 | | // 'cupsLangGetString()' - Get a localized message string. |
170 | | // |
171 | | // This function gets a localized UTF-8 message string for the specified |
172 | | // language. If the message is not localized, the original message pointer is |
173 | | // returned. |
174 | | // |
175 | | |
176 | | const char * // O - Localized message |
177 | | cupsLangGetString(cups_lang_t *lang, // I - Language |
178 | | const char *message) // I - Message |
179 | 528 | { |
180 | 528 | _cups_message_t key, // Search key |
181 | 528 | *match; // Matching message |
182 | 528 | const char *text; // Localized message text |
183 | | |
184 | | |
185 | 528 | DEBUG_printf("cupsLangGetString(lang=%p(%s), message=\"%s\")", (void *)lang, lang ? lang->language : "null", message); |
186 | | |
187 | | // Range check input... |
188 | 528 | if (!lang || !lang->num_messages || !message || !*message) |
189 | 0 | return (message); |
190 | | |
191 | 528 | cupsRWLockRead(&lang->rwlock); |
192 | | |
193 | 528 | key.key = (char *)message; |
194 | 528 | match = bsearch(&key, lang->messages, lang->num_messages, sizeof(_cups_message_t), (int (*)(const void *, const void *))cups_message_compare); |
195 | 528 | text = match ? match->text : message; |
196 | | |
197 | 528 | cupsRWUnlock(&lang->rwlock); |
198 | | |
199 | 528 | return (text); |
200 | 528 | } |
201 | | |
202 | | |
203 | | // |
204 | | // 'cupsLangLoadStrings()' - Load a message catalog for a language. |
205 | | // |
206 | | |
207 | | bool // O - `true` on success, `false` on failure |
208 | | cupsLangLoadStrings( |
209 | | cups_lang_t *lang, // I - Language data |
210 | | const char *filename, // I - Filename of `NULL` for none |
211 | | const char *strings) // I - Strings or `NULL` for none |
212 | 1 | { |
213 | 1 | bool ret = true; // Return value |
214 | 1 | int linenum; // Current line number in data |
215 | 1 | const char *data, // Pointer to strings data |
216 | 1 | *dataptr; // Pointer into string data |
217 | 1 | char key[1024], // Key string |
218 | 1 | text[1024], // Localized text string |
219 | 1 | *ptr; // Pointer into strings |
220 | 1 | _cups_message_t *m, // Pointer to message |
221 | 1 | mkey; // Search key |
222 | 1 | size_t num_messages; // New number of messages |
223 | | |
224 | | |
225 | 1 | if (filename) |
226 | 0 | { |
227 | | // Load the strings file... |
228 | 0 | int fd; // File descriptor |
229 | 0 | struct stat fileinfo; // File information |
230 | 0 | ssize_t bytes; // Bytes read |
231 | |
|
232 | 0 | if ((fd = open(filename, O_RDONLY)) < 0) |
233 | 0 | { |
234 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); |
235 | 0 | return (false); |
236 | 0 | } |
237 | | |
238 | 0 | if (fstat(fd, &fileinfo)) |
239 | 0 | { |
240 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); |
241 | 0 | close(fd); |
242 | 0 | return (false); |
243 | 0 | } |
244 | | |
245 | 0 | if ((ptr = malloc((size_t)(fileinfo.st_size + 1))) == NULL) |
246 | 0 | { |
247 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); |
248 | 0 | close(fd); |
249 | 0 | return (false); |
250 | 0 | } |
251 | | |
252 | 0 | if ((bytes = read(fd, ptr, (size_t)fileinfo.st_size)) < 0) |
253 | 0 | { |
254 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); |
255 | 0 | close(fd); |
256 | 0 | free(ptr); |
257 | 0 | return (false); |
258 | 0 | } |
259 | | |
260 | 0 | close(fd); |
261 | |
|
262 | 0 | ptr[bytes] = '\0'; |
263 | 0 | data = ptr; |
264 | 0 | } |
265 | 1 | else |
266 | 1 | { |
267 | | // Use in-memory strings data... |
268 | 1 | data = (const char *)strings; |
269 | 1 | } |
270 | | |
271 | 1 | if (!data) |
272 | 0 | { |
273 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); |
274 | 0 | return (false); |
275 | 0 | } |
276 | | |
277 | | // Scan the in-memory strings data and add key/text pairs... |
278 | | // |
279 | | // Format of strings files is: |
280 | | // |
281 | | // "key" = "text"; |
282 | 1 | cupsRWLockWrite(&lang->rwlock); |
283 | | |
284 | 1 | num_messages = lang->num_messages; |
285 | 1 | mkey.key = key; |
286 | | |
287 | 2.46k | for (dataptr = data, linenum = 1; *dataptr; dataptr ++) |
288 | 2.46k | { |
289 | | // Skip leading whitespace... |
290 | 2.46k | while (*dataptr && isspace(*dataptr & 255)) |
291 | 0 | { |
292 | 0 | if (*dataptr == '\n') |
293 | 0 | linenum ++; |
294 | |
|
295 | 0 | dataptr ++; |
296 | 0 | } |
297 | | |
298 | 2.46k | if (!*dataptr) |
299 | 0 | { |
300 | | // End of string... |
301 | 0 | break; |
302 | 0 | } |
303 | 2.46k | else if (*dataptr == '/' && dataptr[1] == '*') |
304 | 0 | { |
305 | | // Start of C-style comment... |
306 | 0 | for (dataptr += 2; *dataptr; dataptr ++) |
307 | 0 | { |
308 | 0 | if (*dataptr == '*' && dataptr[1] == '/') |
309 | 0 | { |
310 | 0 | dataptr += 2; |
311 | 0 | break; |
312 | 0 | } |
313 | 0 | else if (*dataptr == '\n') |
314 | 0 | linenum ++; |
315 | 0 | } |
316 | |
|
317 | 0 | if (!*dataptr) |
318 | 0 | break; |
319 | 0 | } |
320 | 2.46k | else if (*dataptr != '\"') |
321 | 0 | { |
322 | | // Something else we don't recognize... |
323 | 0 | snprintf(text, sizeof(text), "Syntax error on line %d of '%s'.", linenum, filename ? filename : "in-memory"); |
324 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, text, 0); |
325 | 0 | ret = false; |
326 | 0 | break; |
327 | 0 | } |
328 | | |
329 | | // Parse key string... |
330 | 2.46k | dataptr ++; |
331 | 88.7k | for (ptr = key; *dataptr && *dataptr != '\"'; dataptr ++) |
332 | 86.3k | { |
333 | 86.3k | if (*dataptr == '\\' && dataptr[1]) |
334 | 143 | { |
335 | | // Escaped character... |
336 | 143 | int ch; // Character |
337 | | |
338 | 143 | dataptr ++; |
339 | 143 | if (*dataptr == '\\' || *dataptr == '\'' || *dataptr == '\"') |
340 | 133 | { |
341 | 133 | ch = *dataptr; |
342 | 133 | } |
343 | 10 | else if (*dataptr == 'n') |
344 | 10 | { |
345 | 10 | ch = '\n'; |
346 | 10 | } |
347 | 0 | else if (*dataptr == 'r') |
348 | 0 | { |
349 | 0 | ch = '\r'; |
350 | 0 | } |
351 | 0 | else if (*dataptr == 't') |
352 | 0 | { |
353 | 0 | ch = '\t'; |
354 | 0 | } |
355 | 0 | else if (*dataptr >= '0' && *dataptr <= '3' && dataptr[1] >= '0' && dataptr[1] <= '7' && dataptr[2] >= '0' && dataptr[2] <= '7') |
356 | 0 | { |
357 | | // Octal escape |
358 | 0 | ch = ((*dataptr - '0') << 6) | ((dataptr[1] - '0') << 3) | (dataptr[2] - '0'); |
359 | 0 | dataptr += 2; |
360 | 0 | } |
361 | 0 | else |
362 | 0 | { |
363 | 0 | snprintf(text, sizeof(text), "Invalid escape in key string on line %d of '%s'.", linenum, filename ? filename : "in-memory"); |
364 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, text, 0); |
365 | 0 | ret = false; |
366 | 0 | break; |
367 | 0 | } |
368 | | |
369 | 143 | if (ptr < (key + sizeof(key) - 1)) |
370 | 143 | *ptr++ = (char)ch; |
371 | 143 | } |
372 | 86.1k | else if (ptr < (key + sizeof(key) - 1)) |
373 | 86.1k | { |
374 | 86.1k | *ptr++ = *dataptr; |
375 | 86.1k | } |
376 | 86.3k | } |
377 | | |
378 | 2.46k | if (!*dataptr) |
379 | 0 | { |
380 | 0 | snprintf(text, sizeof(text), "Unterminated key string on line %d of '%s'.", linenum, filename ? filename : "in-memory"); |
381 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, text, 0); |
382 | 0 | ret = false; |
383 | 0 | break; |
384 | 0 | } |
385 | | |
386 | 2.46k | dataptr ++; |
387 | 2.46k | *ptr = '\0'; |
388 | | |
389 | | // Parse separator... |
390 | 4.93k | while (*dataptr && isspace(*dataptr & 255)) |
391 | 2.46k | { |
392 | 2.46k | if (*dataptr == '\n') |
393 | 0 | linenum ++; |
394 | | |
395 | 2.46k | dataptr ++; |
396 | 2.46k | } |
397 | | |
398 | 2.46k | if (*dataptr != '=') |
399 | 0 | { |
400 | 0 | snprintf(text, sizeof(text), "Missing separator on line %d of '%s'.", linenum, filename ? filename : "in-memory"); |
401 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, text, 0); |
402 | 0 | ret = false; |
403 | 0 | break; |
404 | 0 | } |
405 | | |
406 | 2.46k | dataptr ++; |
407 | 4.93k | while (*dataptr && isspace(*dataptr & 255)) |
408 | 2.46k | { |
409 | 2.46k | if (*dataptr == '\n') |
410 | 0 | linenum ++; |
411 | | |
412 | 2.46k | dataptr ++; |
413 | 2.46k | } |
414 | | |
415 | 2.46k | if (*dataptr != '\"') |
416 | 0 | { |
417 | 0 | snprintf(text, sizeof(text), "Missing text string on line %d of '%s'.", linenum, filename ? filename : "in-memory"); |
418 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, text, 0); |
419 | 0 | ret = false; |
420 | 0 | break; |
421 | 0 | } |
422 | | |
423 | | // Parse text string... |
424 | 2.46k | dataptr ++; |
425 | 56.5k | for (ptr = text; *dataptr && *dataptr != '\"'; dataptr ++) |
426 | 54.1k | { |
427 | 54.1k | if (*dataptr == '\\') |
428 | 145 | { |
429 | | // Escaped character... |
430 | 145 | int ch; // Character |
431 | | |
432 | 145 | dataptr ++; |
433 | 145 | if (*dataptr == '\\' || *dataptr == '\'' || *dataptr == '\"') |
434 | 135 | { |
435 | 135 | ch = *dataptr; |
436 | 135 | } |
437 | 10 | else if (*dataptr == 'n') |
438 | 10 | { |
439 | 10 | ch = '\n'; |
440 | 10 | } |
441 | 0 | else if (*dataptr == 'r') |
442 | 0 | { |
443 | 0 | ch = '\r'; |
444 | 0 | } |
445 | 0 | else if (*dataptr == 't') |
446 | 0 | { |
447 | 0 | ch = '\t'; |
448 | 0 | } |
449 | 0 | else if (*dataptr >= '0' && *dataptr <= '3' && dataptr[1] >= '0' && dataptr[1] <= '7' && dataptr[2] >= '0' && dataptr[2] <= '7') |
450 | 0 | { |
451 | | // Octal escape |
452 | 0 | ch = ((*dataptr - '0') << 6) | ((dataptr[1] - '0') << 3) | (dataptr[2] - '0'); |
453 | 0 | dataptr += 2; |
454 | 0 | } |
455 | 0 | else |
456 | 0 | { |
457 | 0 | snprintf(text, sizeof(text), "Invalid escape in text string on line %d of '%s'.", linenum, filename ? filename : "in-memory"); |
458 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, text, 0); |
459 | 0 | ret = false; |
460 | 0 | break; |
461 | 0 | } |
462 | | |
463 | 145 | if (ptr < (text + sizeof(text) - 1)) |
464 | 145 | *ptr++ = (char)ch; |
465 | 145 | } |
466 | 53.9k | else if (ptr < (text + sizeof(text) - 1)) |
467 | 53.9k | { |
468 | 53.9k | *ptr++ = *dataptr; |
469 | 53.9k | } |
470 | 54.1k | } |
471 | | |
472 | 2.46k | if (!*dataptr) |
473 | 0 | { |
474 | 0 | snprintf(text, sizeof(text), "Unterminated text string on line %d of '%s'.", linenum, filename ? filename : "in-memory"); |
475 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, text, 0); |
476 | 0 | ret = false; |
477 | 0 | break; |
478 | 0 | } |
479 | | |
480 | 2.46k | dataptr ++; |
481 | 2.46k | *ptr = '\0'; |
482 | | |
483 | | // Look for terminator, then add the pair... |
484 | 2.46k | if (*dataptr != ';') |
485 | 0 | { |
486 | 0 | snprintf(text, sizeof(text), "Missing terminator on line %d of '%s'.", linenum, filename ? filename : "in-memory"); |
487 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, text, 0); |
488 | 0 | ret = false; |
489 | 0 | break; |
490 | 0 | } |
491 | | |
492 | 2.46k | dataptr ++; |
493 | | |
494 | | // Add the message if it doesn't already exist... |
495 | 2.46k | if (lang->num_messages > 0 && bsearch(&mkey, lang->messages, lang->num_messages, sizeof(_cups_message_t), (int (*)(const void *, const void *))cups_message_compare)) |
496 | 0 | continue; |
497 | | |
498 | 2.46k | if (num_messages >= lang->alloc_messages) |
499 | 3 | { |
500 | 3 | if ((m = realloc(lang->messages, (lang->alloc_messages + 1024) * sizeof(_cups_message_t))) == NULL) |
501 | 0 | { |
502 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); |
503 | 0 | ret = false; |
504 | 0 | break; |
505 | 0 | } |
506 | | |
507 | 3 | lang->messages = m; |
508 | 3 | lang->alloc_messages += 1024; |
509 | 3 | } |
510 | | |
511 | 2.46k | m = lang->messages + num_messages; |
512 | | |
513 | 2.46k | if ((m->key = _cupsStrAlloc(key)) == NULL || (m->text = _cupsStrAlloc(text)) == NULL) |
514 | 0 | { |
515 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); |
516 | 0 | _cupsStrFree(m->key); |
517 | 0 | _cupsStrFree(m->text); |
518 | 0 | ret = false; |
519 | 0 | break; |
520 | 0 | } |
521 | | |
522 | 2.46k | num_messages ++; |
523 | 2.46k | } |
524 | | |
525 | | // Re-sort messages as needed... |
526 | 1 | if (num_messages > lang->num_messages) |
527 | 1 | { |
528 | 1 | lang->num_messages = num_messages; |
529 | 1 | qsort(lang->messages, lang->num_messages, sizeof(_cups_message_t), (int (*)(const void *, const void *))cups_message_compare); |
530 | 1 | } |
531 | | |
532 | 1 | cupsRWUnlock(&lang->rwlock); |
533 | | |
534 | | // Free temporary storage and return... |
535 | 1 | if (data != strings) |
536 | 0 | free((void *)data); |
537 | | |
538 | 1 | return (ret); |
539 | 1 | } |
540 | | |
541 | | |
542 | | // |
543 | | // 'cupsLangSetDirectory()' - Set a directory containing localizations. |
544 | | // |
545 | | |
546 | | void |
547 | | cupsLangSetDirectory(const char *d) // I - Directory name |
548 | 0 | { |
549 | 0 | if (d) |
550 | 0 | { |
551 | 0 | cupsMutexLock(&lang_mutex); |
552 | |
|
553 | 0 | free(lang_directory); |
554 | 0 | lang_directory = strdup(d); |
555 | |
|
556 | 0 | cupsMutexUnlock(&lang_mutex); |
557 | 0 | } |
558 | 0 | } |
559 | | |
560 | | |
561 | | // |
562 | | // 'cups_lang_new()' - Create a new language. |
563 | | // |
564 | | |
565 | | static cups_lang_t * // O - Language data |
566 | | cups_lang_new(const char *language) // I - Language name |
567 | 1 | { |
568 | 1 | cups_lang_t *lang; // Language data |
569 | 1 | char filename[1024]; // Strings file... |
570 | 1 | bool status; // Load status |
571 | | |
572 | | |
573 | | // Create an empty language data structure... |
574 | 1 | if ((lang = calloc(1, sizeof(cups_lang_t))) == NULL) |
575 | 0 | return (NULL); |
576 | | |
577 | 1 | cupsRWInit(&lang->rwlock); |
578 | 1 | cupsCopyString(lang->language, language, sizeof(lang->language)); |
579 | | |
580 | | // Add strings... |
581 | 1 | if (!_cups_strncasecmp(language, "ca", 2)) |
582 | 0 | status = cupsLangLoadStrings(lang, NULL, ca_strings); |
583 | 1 | else if (!_cups_strncasecmp(language, "cs", 2)) |
584 | 0 | status = cupsLangLoadStrings(lang, NULL, cs_strings); |
585 | 1 | else if (!_cups_strncasecmp(language, "da", 2)) |
586 | 0 | status = cupsLangLoadStrings(lang, NULL, da_strings); |
587 | 1 | else if (!_cups_strncasecmp(language, "de", 2)) |
588 | 0 | status = cupsLangLoadStrings(lang, NULL, de_strings); |
589 | 1 | else if (!_cups_strncasecmp(language, "es", 2)) |
590 | 0 | status = cupsLangLoadStrings(lang, NULL, es_strings); |
591 | 1 | else if (!_cups_strncasecmp(language, "fr", 2)) |
592 | 0 | status = cupsLangLoadStrings(lang, NULL, fr_strings); |
593 | 1 | else if (!_cups_strncasecmp(language, "it", 2)) |
594 | 0 | status = cupsLangLoadStrings(lang, NULL, it_strings); |
595 | 1 | else if (!_cups_strncasecmp(language, "ja", 2)) |
596 | 0 | status = cupsLangLoadStrings(lang, NULL, ja_strings); |
597 | 1 | else if (!_cups_strncasecmp(language, "pt", 2)) |
598 | 0 | status = cupsLangLoadStrings(lang, NULL, pt_BR_strings); |
599 | 1 | else if (!_cups_strncasecmp(language, "ru", 2)) |
600 | 0 | status = cupsLangLoadStrings(lang, NULL, ru_strings); |
601 | 1 | else if (!_cups_strncasecmp(language, "zh", 2)) |
602 | 0 | status = cupsLangLoadStrings(lang, NULL, zh_CN_strings); |
603 | 1 | else |
604 | 1 | status = cupsLangLoadStrings(lang, NULL, en_strings); |
605 | | |
606 | 1 | if (status && lang_directory) |
607 | 0 | { |
608 | 0 | snprintf(filename, sizeof(filename), "%s/%s.strings", lang_directory, language); |
609 | 0 | if (access(filename, 0) && language[2]) |
610 | 0 | { |
611 | 0 | char baselang[3]; // Base language name |
612 | |
|
613 | 0 | cupsCopyString(baselang, language, sizeof(baselang)); |
614 | 0 | snprintf(filename, sizeof(filename), "%s/%s.strings", lang_directory, baselang); |
615 | 0 | } |
616 | |
|
617 | 0 | if (!access(filename, 0)) |
618 | 0 | status = cupsLangLoadStrings(lang, filename, NULL); |
619 | 0 | } |
620 | | |
621 | 1 | if (!status) |
622 | 0 | { |
623 | | // Free memory if the load failed... |
624 | 0 | size_t i; // Looping var |
625 | |
|
626 | 0 | for (i = 0; i < lang->num_messages; i ++) |
627 | 0 | { |
628 | 0 | _cupsStrFree(lang->messages[i].key); |
629 | 0 | _cupsStrFree(lang->messages[i].text); |
630 | 0 | } |
631 | |
|
632 | 0 | free(lang->messages); |
633 | 0 | free(lang); |
634 | |
|
635 | 0 | return (NULL); |
636 | 0 | } |
637 | | |
638 | | // Add this language to the front of the list... |
639 | 1 | lang->next = lang_cache; |
640 | 1 | lang_cache = lang; |
641 | | |
642 | 1 | return (lang); |
643 | 1 | } |
644 | | |
645 | | |
646 | | // |
647 | | // 'cups_message_compare()' - Compare two messages. |
648 | | // |
649 | | |
650 | | static int // O - Result of comparison |
651 | | cups_message_compare( |
652 | | _cups_message_t *m1, // I - First message |
653 | | _cups_message_t *m2) // I - Second message |
654 | 18.8k | { |
655 | 18.8k | return (strcmp(m1->key, m2->key)); |
656 | 18.8k | } |