]>
Commit | Line | Data |
---|---|---|
ac884b6a | 1 | // |
7e86f2f6 | 2 | // Shared message catalog class for the CUPS PPD Compiler. |
ac884b6a | 3 | // |
7e86f2f6 MS |
4 | // Copyright 2007-2014 by Apple Inc. |
5 | // Copyright 2002-2006 by Easy Software Products. | |
ac884b6a | 6 | // |
7e86f2f6 MS |
7 | // These coded instructions, statements, and computer programs are the |
8 | // property of Apple Inc. and are protected by Federal copyright | |
9 | // law. Distribution and use rights are outlined in the file "LICENSE.txt" | |
10 | // which should have been included with this file. If this file is | |
11 | // file is missing or damaged, see the license at "http://www.cups.org/". | |
ac884b6a MS |
12 | // |
13 | ||
14 | // | |
15 | // Include necessary headers... | |
16 | // | |
17 | ||
38e73f87 | 18 | #include "ppdc-private.h" |
ac884b6a MS |
19 | |
20 | ||
839a51c8 MS |
21 | // |
22 | // Character encodings... | |
23 | // | |
24 | ||
25 | typedef enum | |
26 | { | |
27 | PPDC_CS_AUTO, | |
28 | PPDC_CS_UTF8, | |
29 | PPDC_CS_UTF16BE, | |
30 | PPDC_CS_UTF16LE | |
31 | } ppdc_cs_t; | |
32 | ||
33 | ||
34 | // | |
35 | // Local functions... | |
36 | // | |
37 | ||
be76a973 MS |
38 | #if defined(__APPLE__) && defined(CUPS_BUNDLEDIR) |
39 | static void apple_add_message(CFStringRef key, CFStringRef val, ppdcCatalog *c); | |
40 | #endif /* __APPLE__ && CUPS_BUNDLEDIR */ | |
839a51c8 MS |
41 | static int get_utf8(char *&ptr); |
42 | static int get_utf16(cups_file_t *fp, ppdc_cs_t &cs); | |
43 | static int put_utf8(int ch, char *&ptr, char *end); | |
44 | static int put_utf16(cups_file_t *fp, int ch); | |
45 | ||
46 | ||
ac884b6a MS |
47 | // |
48 | // 'ppdcCatalog::ppdcCatalog()' - Create a shared message catalog. | |
49 | // | |
50 | ||
51 | ppdcCatalog::ppdcCatalog(const char *l, // I - Locale | |
52 | const char *f) // I - Message catalog file | |
53 | : ppdcShared() | |
54 | { | |
94da7e34 MS |
55 | PPDC_NEW; |
56 | ||
ac884b6a MS |
57 | locale = new ppdcString(l); |
58 | filename = new ppdcString(f); | |
59 | messages = new ppdcArray(); | |
60 | ||
61 | if (l) | |
62 | { | |
63 | // Try loading the base messages for this locale... | |
64 | char pofile[1024]; // Message catalog file | |
65 | ||
66 | ||
be76a973 MS |
67 | #if defined(__APPLE__) && defined(CUPS_BUNDLEDIR) |
68 | char applelang[256]; // Apple language ID | |
69 | CFURLRef url; // URL to cups.strings file | |
70 | CFReadStreamRef stream = NULL; // File stream | |
71 | CFPropertyListRef plist = NULL; // Localization file | |
72 | ||
73 | snprintf(pofile, sizeof(pofile), CUPS_BUNDLEDIR "/Resources/%s.lproj/cups.strings", _cupsAppleLanguage(l, applelang, sizeof(applelang))); | |
74 | if (access(pofile, 0)) | |
75 | { | |
76 | // Try alternate lproj directory names... | |
77 | const char *tl = l; // Temporary locale string | |
78 | ||
79 | if (!strncmp(l, "en", 2)) | |
80 | tl = "English"; | |
81 | else if (!strncmp(l, "nb", 2) || !strncmp(l, "nl", 2)) | |
82 | tl = "Dutch"; | |
83 | else if (!strncmp(l, "fr", 2)) | |
84 | tl = "French"; | |
85 | else if (!strncmp(l, "de", 2)) | |
86 | tl = "German"; | |
87 | else if (!strncmp(l, "it", 2)) | |
88 | tl = "Italian"; | |
89 | else if (!strncmp(l, "ja", 2)) | |
90 | tl = "Japanese"; | |
91 | else if (!strncmp(l, "es", 2)) | |
92 | tl = "Spanish"; | |
93 | ||
94 | snprintf(pofile, sizeof(pofile), CUPS_BUNDLEDIR "/Resources/%s.lproj/cups.strings", tl); | |
95 | } | |
96 | ||
97 | url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (UInt8 *)pofile, (CFIndex)strlen(pofile), false); | |
98 | if (url) | |
99 | { | |
100 | stream = CFReadStreamCreateWithFile(kCFAllocatorDefault, url); | |
101 | ||
102 | if (stream) | |
103 | { | |
104 | /* | |
105 | * Read the property list containing the localization data. | |
106 | */ | |
107 | ||
108 | CFReadStreamOpen(stream); | |
109 | ||
110 | plist = CFPropertyListCreateWithStream(kCFAllocatorDefault, stream, 0, kCFPropertyListImmutable, NULL, NULL); | |
111 | ||
112 | if (plist && CFGetTypeID(plist) == CFDictionaryGetTypeID()) | |
113 | CFDictionaryApplyFunction((CFDictionaryRef)plist, (CFDictionaryApplierFunction)apple_add_message, this); | |
114 | ||
115 | if (plist) | |
116 | CFRelease(plist); | |
117 | ||
118 | CFRelease(stream); | |
119 | } | |
120 | ||
121 | CFRelease(url); | |
122 | } | |
123 | ||
124 | #else | |
125 | _cups_globals_t *cg = _cupsGlobals(); | |
126 | // Global information | |
127 | ||
61cf44e2 | 128 | snprintf(pofile, sizeof(pofile), "%s/%s/cups_%s.po", cg->localedir, l, l); |
ac884b6a MS |
129 | |
130 | if (load_messages(pofile) && strchr(l, '_')) | |
131 | { | |
132 | // Try the base locale... | |
133 | char baseloc[3]; // Base locale... | |
134 | ||
135 | ||
136 | strlcpy(baseloc, l, sizeof(baseloc)); | |
61cf44e2 | 137 | snprintf(pofile, sizeof(pofile), "%s/%s/cups_%s.po", cg->localedir, |
ac884b6a MS |
138 | baseloc, baseloc); |
139 | ||
140 | load_messages(pofile); | |
141 | } | |
be76a973 | 142 | #endif /* __APPLE__ && CUPS_BUNDLEDIR */ |
ac884b6a MS |
143 | } |
144 | ||
be76a973 | 145 | if (f && *f) |
ac884b6a MS |
146 | load_messages(f); |
147 | } | |
148 | ||
149 | ||
150 | // | |
151 | // 'ppdcCatalog::~ppdcCatalog()' - Destroy a shared message catalog. | |
152 | // | |
153 | ||
154 | ppdcCatalog::~ppdcCatalog() | |
155 | { | |
94da7e34 MS |
156 | PPDC_DELETE; |
157 | ||
e4572d57 MS |
158 | locale->release(); |
159 | filename->release(); | |
160 | messages->release(); | |
ac884b6a MS |
161 | } |
162 | ||
163 | ||
164 | // | |
165 | // 'ppdcCatalog::add_message()' - Add a new message. | |
166 | // | |
167 | ||
168 | void | |
61cf44e2 MS |
169 | ppdcCatalog::add_message( |
170 | const char *id, // I - Message ID to add | |
171 | const char *string) // I - Translation string | |
ac884b6a MS |
172 | { |
173 | ppdcMessage *m; // Current message | |
174 | char text[1024]; // Text to translate | |
175 | ||
176 | ||
177 | // Range check input... | |
61cf44e2 | 178 | if (!id) |
ac884b6a MS |
179 | return; |
180 | ||
181 | // Verify that we don't already have the message ID... | |
182 | for (m = (ppdcMessage *)messages->first(); | |
183 | m; | |
184 | m = (ppdcMessage *)messages->next()) | |
185 | if (!strcmp(m->id->value, id)) | |
61cf44e2 MS |
186 | { |
187 | if (string) | |
188 | { | |
189 | m->string->release(); | |
190 | m->string = new ppdcString(string); | |
191 | } | |
ac884b6a | 192 | return; |
61cf44e2 | 193 | } |
ac884b6a MS |
194 | |
195 | // Add the message... | |
61cf44e2 MS |
196 | if (!string) |
197 | { | |
198 | snprintf(text, sizeof(text), "TRANSLATE %s", id); | |
199 | string = text; | |
200 | } | |
201 | ||
e6013cfa | 202 | messages->add(new ppdcMessage(id, string)); |
ac884b6a MS |
203 | } |
204 | ||
205 | ||
206 | // | |
207 | // 'ppdcCatalog::find_message()' - Find a message in a catalog... | |
208 | // | |
209 | ||
210 | const char * // O - Message text | |
211 | ppdcCatalog::find_message( | |
212 | const char *id) // I - Message ID | |
213 | { | |
214 | ppdcMessage *m; // Current message | |
215 | ||
216 | ||
ef55b745 MS |
217 | if (!*id) |
218 | return (id); | |
219 | ||
ac884b6a MS |
220 | for (m = (ppdcMessage *)messages->first(); |
221 | m; | |
222 | m = (ppdcMessage *)messages->next()) | |
223 | if (!strcmp(m->id->value, id)) | |
224 | return (m->string->value); | |
225 | ||
226 | return (id); | |
227 | } | |
228 | ||
229 | ||
230 | // | |
231 | // 'ppdcCatalog::load_messages()' - Load messages from a .po file. | |
232 | // | |
233 | ||
234 | int // O - 0 on success, -1 on failure | |
235 | ppdcCatalog::load_messages( | |
236 | const char *f) // I - Message catalog file | |
237 | { | |
238 | cups_file_t *fp; // Message file | |
ac884b6a MS |
239 | char line[4096], // Line buffer |
240 | *ptr, // Pointer into buffer | |
241 | id[4096], // Translation ID | |
242 | str[4096]; // Translation string | |
243 | int linenum; // Line number | |
244 | ||
245 | ||
246 | // Open the message catalog file... | |
247 | if ((fp = cupsFileOpen(f, "r")) == NULL) | |
248 | return (-1); | |
249 | ||
ae71f5de | 250 | if ((ptr = (char *)strrchr(f, '.')) == NULL) |
839a51c8 MS |
251 | goto unknown_load_format; |
252 | else if (!strcmp(ptr, ".strings")) | |
253 | { | |
254 | /* | |
be76a973 MS |
255 | * Read messages in OS X ".strings" format, which are either UTF-8/UTF-16 |
256 | * text files of the format: | |
839a51c8 MS |
257 | * |
258 | * "id" = "str"; | |
259 | * | |
260 | * Strings files can also contain C-style comments. | |
261 | */ | |
262 | ||
263 | ppdc_cs_t cs = PPDC_CS_AUTO; // Character set for file | |
264 | int ch; // Current character from file | |
265 | char *end; // End of buffer | |
266 | ||
267 | ||
268 | id[0] = '\0'; | |
269 | str[0] = '\0'; | |
270 | ptr = NULL; | |
271 | end = NULL; | |
272 | ||
273 | while ((ch = get_utf16(fp, cs)) != 0) | |
274 | { | |
275 | if (ptr) | |
276 | { | |
277 | if (ch == '\\') | |
278 | { | |
279 | if ((ch = get_utf16(fp, cs)) == 0) | |
280 | break; | |
ac884b6a | 281 | |
4509bb49 | 282 | if (ch == 'n') |
839a51c8 MS |
283 | ch = '\n'; |
284 | else if (ch == 't') | |
285 | ch = '\t'; | |
286 | } | |
4509bb49 MS |
287 | else if (ch == '\"') |
288 | { | |
289 | *ptr = '\0'; | |
290 | ptr = NULL; | |
291 | } | |
ac884b6a | 292 | |
4509bb49 MS |
293 | if (ptr) |
294 | put_utf8(ch, ptr, end); | |
839a51c8 MS |
295 | } |
296 | else if (ch == '/') | |
297 | { | |
298 | // Start of a comment? | |
299 | if ((ch = get_utf16(fp, cs)) == 0) | |
300 | break; | |
ac884b6a | 301 | |
839a51c8 MS |
302 | if (ch == '*') |
303 | { | |
304 | // Skip C comment... | |
305 | int lastch = 0; | |
ac884b6a | 306 | |
839a51c8 MS |
307 | while ((ch = get_utf16(fp, cs)) != 0) |
308 | { | |
309 | if (ch == '/' && lastch == '*') | |
310 | break; | |
ac884b6a | 311 | |
839a51c8 MS |
312 | lastch = ch; |
313 | } | |
314 | } | |
315 | else if (ch == '/') | |
316 | { | |
317 | // Skip C++ comment... | |
318 | while ((ch = get_utf16(fp, cs)) != 0) | |
319 | if (ch == '\n') | |
320 | break; | |
321 | } | |
322 | } | |
323 | else if (ch == '\"') | |
324 | { | |
4509bb49 MS |
325 | // Start quoted string... |
326 | if (id[0]) | |
839a51c8 MS |
327 | { |
328 | ptr = str; | |
329 | end = str + sizeof(str) - 1; | |
330 | } | |
331 | else | |
332 | { | |
333 | ptr = id; | |
334 | end = id + sizeof(id) - 1; | |
335 | } | |
336 | } | |
337 | else if (ch == ';') | |
338 | { | |
339 | // Add string... | |
61cf44e2 | 340 | add_message(id, str); |
e6013cfa | 341 | id[0] = '\0'; |
839a51c8 | 342 | } |
ac884b6a | 343 | } |
839a51c8 MS |
344 | } |
345 | else if (!strcmp(ptr, ".po") || !strcmp(ptr, ".gz")) | |
346 | { | |
347 | /* | |
348 | * Read messages from the catalog file until EOF... | |
349 | * | |
350 | * The format is the GNU gettext .po format, which is fairly simple: | |
351 | * | |
352 | * msgid "some text" | |
353 | * msgstr "localized text" | |
354 | * | |
355 | * The ID and localized text can span multiple lines using the form: | |
356 | * | |
357 | * msgid "" | |
358 | * "some long text" | |
359 | * msgstr "" | |
360 | * "localized text spanning " | |
361 | * "multiple lines" | |
362 | */ | |
363 | ||
61cf44e2 MS |
364 | int which, // In msgid? |
365 | haveid, // Did we get a msgid string? | |
366 | havestr; // Did we get a msgstr string? | |
f3c17241 | 367 | |
839a51c8 MS |
368 | linenum = 0; |
369 | id[0] = '\0'; | |
370 | str[0] = '\0'; | |
61cf44e2 MS |
371 | haveid = 0; |
372 | havestr = 0; | |
373 | which = 0; | |
839a51c8 MS |
374 | |
375 | while (cupsFileGets(fp, line, sizeof(line))) | |
376 | { | |
377 | linenum ++; | |
ac884b6a | 378 | |
839a51c8 MS |
379 | // Skip blank and comment lines... |
380 | if (line[0] == '#' || !line[0]) | |
381 | continue; | |
ac884b6a | 382 | |
839a51c8 | 383 | // Strip the trailing quote... |
ae71f5de | 384 | if ((ptr = (char *)strrchr(line, '\"')) == NULL) |
839a51c8 | 385 | { |
61cf44e2 | 386 | _cupsLangPrintf(stderr, |
0837b7e8 | 387 | _("ppdc: Expected quoted string on line %d of %s."), |
61cf44e2 | 388 | linenum, f); |
839a51c8 MS |
389 | cupsFileClose(fp); |
390 | return (-1); | |
391 | } | |
ac884b6a | 392 | |
839a51c8 MS |
393 | *ptr = '\0'; |
394 | ||
395 | // Find start of value... | |
396 | if ((ptr = strchr(line, '\"')) == NULL) | |
397 | { | |
61cf44e2 | 398 | _cupsLangPrintf(stderr, |
0837b7e8 | 399 | _("ppdc: Expected quoted string on line %d of %s."), |
61cf44e2 | 400 | linenum, f); |
839a51c8 MS |
401 | cupsFileClose(fp); |
402 | return (-1); | |
403 | } | |
404 | ||
405 | ptr ++; | |
406 | ||
407 | // Unquote the text... | |
408 | char *sptr, *dptr; // Source/destination pointers | |
409 | ||
410 | for (sptr = ptr, dptr = ptr; *sptr;) | |
ac884b6a | 411 | { |
839a51c8 | 412 | if (*sptr == '\\') |
ac884b6a | 413 | { |
839a51c8 MS |
414 | sptr ++; |
415 | if (isdigit(*sptr)) | |
416 | { | |
417 | *dptr = 0; | |
418 | ||
419 | while (isdigit(*sptr)) | |
420 | { | |
421 | *dptr = *dptr * 8 + *sptr - '0'; | |
422 | sptr ++; | |
423 | } | |
ac884b6a | 424 | |
839a51c8 MS |
425 | dptr ++; |
426 | } | |
427 | else | |
ac884b6a | 428 | { |
839a51c8 MS |
429 | if (*sptr == 'n') |
430 | *dptr++ = '\n'; | |
431 | else if (*sptr == 'r') | |
432 | *dptr++ = '\r'; | |
433 | else if (*sptr == 't') | |
434 | *dptr++ = '\t'; | |
435 | else | |
436 | *dptr++ = *sptr; | |
437 | ||
ac884b6a MS |
438 | sptr ++; |
439 | } | |
ac884b6a MS |
440 | } |
441 | else | |
839a51c8 | 442 | *dptr++ = *sptr++; |
ac884b6a | 443 | } |
ac884b6a | 444 | |
839a51c8 | 445 | *dptr = '\0'; |
ac884b6a | 446 | |
839a51c8 MS |
447 | // Create or add to a message... |
448 | if (!strncmp(line, "msgid", 5)) | |
ac884b6a | 449 | { |
61cf44e2 MS |
450 | if (haveid && havestr) |
451 | add_message(id, str); | |
839a51c8 MS |
452 | |
453 | strlcpy(id, ptr, sizeof(id)); | |
454 | str[0] = '\0'; | |
61cf44e2 MS |
455 | haveid = 1; |
456 | havestr = 0; | |
457 | which = 1; | |
ac884b6a | 458 | } |
839a51c8 MS |
459 | else if (!strncmp(line, "msgstr", 6)) |
460 | { | |
61cf44e2 | 461 | if (!haveid) |
839a51c8 | 462 | { |
61cf44e2 | 463 | _cupsLangPrintf(stderr, |
0837b7e8 MS |
464 | _("ppdc: Need a msgid line before any " |
465 | "translation strings on line %d of %s."), | |
61cf44e2 | 466 | linenum, f); |
839a51c8 MS |
467 | cupsFileClose(fp); |
468 | return (-1); | |
469 | } | |
ac884b6a | 470 | |
839a51c8 | 471 | strlcpy(str, ptr, sizeof(str)); |
61cf44e2 MS |
472 | havestr = 1; |
473 | which = 2; | |
839a51c8 | 474 | } |
61cf44e2 | 475 | else if (line[0] == '\"' && which == 2) |
839a51c8 | 476 | strlcat(str, ptr, sizeof(str)); |
61cf44e2 | 477 | else if (line[0] == '\"' && which == 1) |
839a51c8 MS |
478 | strlcat(id, ptr, sizeof(id)); |
479 | else | |
ac884b6a | 480 | { |
0837b7e8 | 481 | _cupsLangPrintf(stderr, _("ppdc: Unexpected text on line %d of %s."), |
61cf44e2 | 482 | linenum, f); |
ac884b6a MS |
483 | cupsFileClose(fp); |
484 | return (-1); | |
485 | } | |
ac884b6a | 486 | } |
839a51c8 | 487 | |
61cf44e2 MS |
488 | if (haveid && havestr) |
489 | add_message(id, str); | |
ac884b6a | 490 | } |
839a51c8 MS |
491 | else |
492 | goto unknown_load_format; | |
ac884b6a | 493 | |
839a51c8 MS |
494 | /* |
495 | * Close the file and return... | |
496 | */ | |
ac884b6a MS |
497 | |
498 | cupsFileClose(fp); | |
499 | ||
500 | return (0); | |
839a51c8 MS |
501 | |
502 | /* | |
503 | * Unknown format error... | |
504 | */ | |
505 | ||
506 | unknown_load_format: | |
507 | ||
61cf44e2 | 508 | _cupsLangPrintf(stderr, |
0837b7e8 | 509 | _("ppdc: Unknown message catalog format for \"%s\"."), f); |
839a51c8 MS |
510 | cupsFileClose(fp); |
511 | return (-1); | |
ac884b6a MS |
512 | } |
513 | ||
514 | ||
515 | // | |
516 | // 'ppdcCatalog::save_messages()' - Save the messages to a .po file. | |
517 | // | |
518 | ||
519 | int // O - 0 on success, -1 on error | |
520 | ppdcCatalog::save_messages( | |
521 | const char *f) // I - File to save to | |
522 | { | |
523 | cups_file_t *fp; // Message file | |
524 | ppdcMessage *m; // Current message | |
839a51c8 MS |
525 | char *ptr; // Pointer into string |
526 | int utf16; // Output UTF-16 .strings file? | |
527 | int ch; // Current character | |
ac884b6a MS |
528 | |
529 | ||
839a51c8 | 530 | // Open the file... |
ae71f5de | 531 | if ((ptr = (char *)strrchr(f, '.')) == NULL) |
ac884b6a MS |
532 | return (-1); |
533 | ||
839a51c8 MS |
534 | if (!strcmp(ptr, ".gz")) |
535 | fp = cupsFileOpen(f, "w9"); | |
536 | else | |
537 | fp = cupsFileOpen(f, "w"); | |
538 | ||
539 | if (!fp) | |
540 | return (-1); | |
541 | ||
542 | // For .strings files, write a BOM for big-endian output... | |
543 | utf16 = !strcmp(ptr, ".strings"); | |
544 | ||
545 | if (utf16) | |
546 | put_utf16(fp, 0xfeff); | |
547 | ||
548 | // Loop through all of the messages... | |
ac884b6a MS |
549 | for (m = (ppdcMessage *)messages->first(); |
550 | m; | |
551 | m = (ppdcMessage *)messages->next()) | |
552 | { | |
839a51c8 MS |
553 | if (utf16) |
554 | { | |
555 | put_utf16(fp, '\"'); | |
ac884b6a | 556 | |
839a51c8 MS |
557 | ptr = m->id->value; |
558 | while ((ch = get_utf8(ptr)) != 0) | |
559 | switch (ch) | |
560 | { | |
561 | case '\n' : | |
562 | put_utf16(fp, '\\'); | |
563 | put_utf16(fp, 'n'); | |
564 | break; | |
565 | case '\\' : | |
566 | put_utf16(fp, '\\'); | |
567 | put_utf16(fp, '\\'); | |
568 | break; | |
569 | case '\"' : | |
570 | put_utf16(fp, '\\'); | |
571 | put_utf16(fp, '\"'); | |
572 | break; | |
573 | default : | |
574 | put_utf16(fp, ch); | |
575 | break; | |
576 | } | |
577 | ||
578 | put_utf16(fp, '\"'); | |
579 | put_utf16(fp, ' '); | |
580 | put_utf16(fp, '='); | |
581 | put_utf16(fp, ' '); | |
582 | put_utf16(fp, '\"'); | |
583 | ||
584 | ptr = m->string->value; | |
585 | while ((ch = get_utf8(ptr)) != 0) | |
586 | switch (ch) | |
587 | { | |
588 | case '\n' : | |
589 | put_utf16(fp, '\\'); | |
590 | put_utf16(fp, 'n'); | |
591 | break; | |
592 | case '\\' : | |
593 | put_utf16(fp, '\\'); | |
594 | put_utf16(fp, '\\'); | |
595 | break; | |
596 | case '\"' : | |
597 | put_utf16(fp, '\\'); | |
598 | put_utf16(fp, '\"'); | |
599 | break; | |
600 | default : | |
601 | put_utf16(fp, ch); | |
602 | break; | |
603 | } | |
ac884b6a | 604 | |
839a51c8 MS |
605 | put_utf16(fp, '\"'); |
606 | put_utf16(fp, ';'); | |
607 | put_utf16(fp, '\n'); | |
608 | } | |
609 | else | |
610 | { | |
611 | cupsFilePuts(fp, "msgid \""); | |
612 | for (ptr = m->id->value; *ptr; ptr ++) | |
613 | switch (*ptr) | |
614 | { | |
615 | case '\n' : | |
616 | cupsFilePuts(fp, "\\n"); | |
617 | break; | |
618 | case '\\' : | |
619 | cupsFilePuts(fp, "\\\\"); | |
620 | break; | |
621 | case '\"' : | |
622 | cupsFilePuts(fp, "\\\""); | |
623 | break; | |
624 | default : | |
625 | cupsFilePutChar(fp, *ptr); | |
626 | break; | |
627 | } | |
628 | cupsFilePuts(fp, "\"\n"); | |
629 | ||
630 | cupsFilePuts(fp, "msgstr \""); | |
631 | for (ptr = m->string->value; *ptr; ptr ++) | |
632 | switch (*ptr) | |
633 | { | |
634 | case '\n' : | |
635 | cupsFilePuts(fp, "\\n"); | |
636 | break; | |
637 | case '\\' : | |
638 | cupsFilePuts(fp, "\\\\"); | |
639 | break; | |
640 | case '\"' : | |
641 | cupsFilePuts(fp, "\\\""); | |
642 | break; | |
643 | default : | |
644 | cupsFilePutChar(fp, *ptr); | |
645 | break; | |
646 | } | |
647 | cupsFilePuts(fp, "\"\n"); | |
648 | ||
649 | cupsFilePutChar(fp, '\n'); | |
650 | } | |
ac884b6a MS |
651 | } |
652 | ||
653 | cupsFileClose(fp); | |
654 | ||
655 | return (0); | |
656 | } | |
657 | ||
658 | ||
be76a973 MS |
659 | #if defined(__APPLE__) && defined(CUPS_BUNDLEDIR) |
660 | // | |
661 | // 'apple_add_message()' - Add a message from a localization dictionary. | |
662 | // | |
663 | ||
664 | static void | |
665 | apple_add_message(CFStringRef key, // I - Localization key | |
666 | CFStringRef val, // I - Localized value | |
667 | ppdcCatalog *c) // I - Message catalog | |
668 | { | |
669 | char id[1024], // Message id | |
670 | str[1024]; // Localized message | |
671 | ||
672 | ||
673 | if (CFStringGetCString(key, id, sizeof(id), kCFStringEncodingUTF8) && | |
674 | CFStringGetCString(val, str, sizeof(str), kCFStringEncodingUTF8)) | |
675 | c->add_message(id, str); | |
676 | } | |
677 | #endif /* __APPLE__ && CUPS_BUNDLEDIR */ | |
678 | ||
679 | ||
839a51c8 MS |
680 | // |
681 | // 'get_utf8()' - Get a UTF-8 character. | |
682 | // | |
683 | ||
684 | static int // O - Unicode character or 0 on EOF | |
685 | get_utf8(char *&ptr) // IO - Pointer to character | |
686 | { | |
687 | int ch; // Current character | |
688 | ||
689 | ||
690 | if ((ch = *ptr++ & 255) < 0xc0) | |
691 | return (ch); | |
692 | ||
693 | if ((ch & 0xe0) == 0xc0) | |
694 | { | |
695 | // Two-byte UTF-8... | |
696 | if ((*ptr & 0xc0) != 0x80) | |
697 | return (0); | |
698 | ||
699 | ch = ((ch & 0x1f) << 6) | (*ptr++ & 0x3f); | |
700 | } | |
701 | else if ((ch & 0xf0) == 0xe0) | |
702 | { | |
703 | // Three-byte UTF-8... | |
704 | if ((*ptr & 0xc0) != 0x80) | |
705 | return (0); | |
706 | ||
707 | ch = ((ch & 0x0f) << 6) | (*ptr++ & 0x3f); | |
708 | ||
709 | if ((*ptr & 0xc0) != 0x80) | |
710 | return (0); | |
711 | ||
712 | ch = (ch << 6) | (*ptr++ & 0x3f); | |
713 | } | |
714 | else if ((ch & 0xf8) == 0xf0) | |
715 | { | |
716 | // Four-byte UTF-8... | |
717 | if ((*ptr & 0xc0) != 0x80) | |
718 | return (0); | |
719 | ||
720 | ch = ((ch & 0x07) << 6) | (*ptr++ & 0x3f); | |
721 | ||
722 | if ((*ptr & 0xc0) != 0x80) | |
723 | return (0); | |
724 | ||
725 | ch = (ch << 6) | (*ptr++ & 0x3f); | |
726 | ||
727 | if ((*ptr & 0xc0) != 0x80) | |
728 | return (0); | |
729 | ||
730 | ch = (ch << 6) | (*ptr++ & 0x3f); | |
731 | } | |
732 | ||
733 | return (ch); | |
734 | } | |
735 | ||
736 | ||
737 | // | |
738 | // 'get_utf16()' - Get a UTF-16 character... | |
739 | // | |
740 | ||
741 | static int // O - Unicode character or 0 on EOF | |
742 | get_utf16(cups_file_t *fp, // I - File to read from | |
743 | ppdc_cs_t &cs) // IO - Character set of file | |
744 | { | |
745 | int ch; // Current character | |
746 | unsigned char buffer[3]; // Bytes | |
747 | ||
748 | ||
749 | if (cs == PPDC_CS_AUTO) | |
750 | { | |
751 | // Get byte-order-mark, if present... | |
752 | if (cupsFileRead(fp, (char *)buffer, 2) != 2) | |
753 | return (0); | |
754 | ||
755 | if (buffer[0] == 0xfe && buffer[1] == 0xff) | |
756 | { | |
757 | // Big-endian UTF-16... | |
758 | cs = PPDC_CS_UTF16BE; | |
759 | ||
760 | if (cupsFileRead(fp, (char *)buffer, 2) != 2) | |
761 | return (0); | |
762 | } | |
763 | else if (buffer[0] == 0xff && buffer[1] == 0xfe) | |
764 | { | |
765 | // Little-endian UTF-16... | |
766 | cs = PPDC_CS_UTF16LE; | |
767 | ||
768 | if (cupsFileRead(fp, (char *)buffer, 2) != 2) | |
769 | return (0); | |
770 | } | |
771 | else if (buffer[0] == 0x00 && buffer[1] != 0x00) | |
772 | { | |
773 | // No BOM, assume big-endian UTF-16... | |
774 | cs = PPDC_CS_UTF16BE; | |
775 | } | |
776 | else if (buffer[0] != 0x00 && buffer[1] == 0x00) | |
777 | { | |
778 | // No BOM, assume little-endian UTF-16... | |
779 | cs = PPDC_CS_UTF16LE; | |
780 | } | |
781 | else | |
782 | { | |
783 | // No BOM, assume UTF-8... | |
784 | cs = PPDC_CS_UTF8; | |
785 | ||
786 | cupsFileRewind(fp); | |
787 | } | |
788 | } | |
789 | else if (cs != PPDC_CS_UTF8) | |
790 | { | |
791 | if (cupsFileRead(fp, (char *)buffer, 2) != 2) | |
792 | return (0); | |
793 | } | |
794 | ||
795 | if (cs == PPDC_CS_UTF8) | |
796 | { | |
797 | // UTF-8 character... | |
4509bb49 MS |
798 | if ((ch = cupsFileGetChar(fp)) < 0) |
799 | return (0); | |
839a51c8 MS |
800 | |
801 | if ((ch & 0xe0) == 0xc0) | |
802 | { | |
803 | // Two-byte UTF-8... | |
804 | if (cupsFileRead(fp, (char *)buffer, 1) != 1) | |
805 | return (0); | |
806 | ||
807 | if ((buffer[0] & 0xc0) != 0x80) | |
808 | return (0); | |
809 | ||
810 | ch = ((ch & 0x1f) << 6) | (buffer[0] & 0x3f); | |
811 | } | |
812 | else if ((ch & 0xf0) == 0xe0) | |
813 | { | |
814 | // Three-byte UTF-8... | |
815 | if (cupsFileRead(fp, (char *)buffer, 2) != 2) | |
816 | return (0); | |
817 | ||
818 | if ((buffer[0] & 0xc0) != 0x80 || | |
819 | (buffer[1] & 0xc0) != 0x80) | |
820 | return (0); | |
821 | ||
822 | ch = ((((ch & 0x0f) << 6) | (buffer[0] & 0x3f)) << 6) | | |
823 | (buffer[1] & 0x3f); | |
824 | } | |
825 | else if ((ch & 0xf8) == 0xf0) | |
826 | { | |
827 | // Four-byte UTF-8... | |
828 | if (cupsFileRead(fp, (char *)buffer, 3) != 3) | |
829 | return (0); | |
830 | ||
831 | if ((buffer[0] & 0xc0) != 0x80 || | |
832 | (buffer[1] & 0xc0) != 0x80 || | |
833 | (buffer[2] & 0xc0) != 0x80) | |
834 | return (0); | |
835 | ||
836 | ch = ((((((ch & 0x07) << 6) | (buffer[0] & 0x3f)) << 6) | | |
837 | (buffer[1] & 0x3f)) << 6) | (buffer[2] & 0x3f); | |
838 | } | |
839 | } | |
840 | else | |
841 | { | |
842 | // UTF-16 character... | |
843 | if (cs == PPDC_CS_UTF16BE) | |
844 | ch = (buffer[0] << 8) | buffer[1]; | |
845 | else | |
846 | ch = (buffer[1] << 8) | buffer[0]; | |
847 | ||
848 | if (ch >= 0xd800 && ch <= 0xdbff) | |
849 | { | |
850 | // Handle multi-word encoding... | |
851 | int lch; | |
852 | ||
853 | if (cupsFileRead(fp, (char *)buffer, 2) != 2) | |
854 | return (0); | |
855 | ||
856 | if (cs == PPDC_CS_UTF16BE) | |
857 | lch = (buffer[0] << 8) | buffer[1]; | |
858 | else | |
859 | lch = (buffer[1] << 8) | buffer[0]; | |
860 | ||
861 | if (lch < 0xdc00 || lch >= 0xdfff) | |
862 | return (0); | |
863 | ||
864 | ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; | |
865 | } | |
866 | } | |
867 | ||
868 | return (ch); | |
869 | } | |
870 | ||
871 | ||
872 | // | |
873 | // 'put_utf8()' - Add a UTF-8 character to a string. | |
874 | // | |
875 | ||
876 | static int // O - 0 on success, -1 on failure | |
877 | put_utf8(int ch, // I - Unicode character | |
878 | char *&ptr, // IO - String pointer | |
879 | char *end) // I - End of buffer | |
880 | { | |
881 | if (ch < 0x80) | |
882 | { | |
883 | // One-byte ASCII... | |
884 | if (ptr >= end) | |
885 | return (-1); | |
886 | ||
7e86f2f6 | 887 | *ptr++ = (char)ch; |
839a51c8 MS |
888 | } |
889 | else if (ch < 0x800) | |
890 | { | |
891 | // Two-byte UTF-8... | |
892 | if ((ptr + 1) >= end) | |
893 | return (-1); | |
894 | ||
7e86f2f6 MS |
895 | *ptr++ = (char)(0xc0 | (ch >> 6)); |
896 | *ptr++ = (char)(0x80 | (ch & 0x3f)); | |
839a51c8 MS |
897 | } |
898 | else if (ch < 0x10000) | |
899 | { | |
900 | // Three-byte UTF-8... | |
901 | if ((ptr + 2) >= end) | |
902 | return (-1); | |
903 | ||
7e86f2f6 MS |
904 | *ptr++ = (char)(0xe0 | (ch >> 12)); |
905 | *ptr++ = (char)(0x80 | ((ch >> 6) & 0x3f)); | |
906 | *ptr++ = (char)(0x80 | (ch & 0x3f)); | |
839a51c8 MS |
907 | } |
908 | else | |
909 | { | |
910 | // Four-byte UTF-8... | |
911 | if ((ptr + 3) >= end) | |
912 | return (-1); | |
913 | ||
7e86f2f6 MS |
914 | *ptr++ = (char)(0xf0 | (ch >> 18)); |
915 | *ptr++ = (char)(0x80 | ((ch >> 12) & 0x3f)); | |
916 | *ptr++ = (char)(0x80 | ((ch >> 6) & 0x3f)); | |
917 | *ptr++ = (char)(0x80 | (ch & 0x3f)); | |
839a51c8 MS |
918 | } |
919 | ||
920 | return (0); | |
921 | } | |
922 | ||
923 | ||
924 | // | |
925 | // 'put_utf16()' - Write a UTF-16 character to a file. | |
926 | // | |
927 | ||
928 | static int // O - 0 on success, -1 on failure | |
929 | put_utf16(cups_file_t *fp, // I - File to write to | |
930 | int ch) // I - Unicode character | |
931 | { | |
932 | unsigned char buffer[4]; // Output buffer | |
933 | ||
934 | ||
935 | if (ch < 0x10000) | |
936 | { | |
937 | // One-word UTF-16 big-endian... | |
7e86f2f6 MS |
938 | buffer[0] = (unsigned char)(ch >> 8); |
939 | buffer[1] = (unsigned char)ch; | |
839a51c8 MS |
940 | |
941 | if (cupsFileWrite(fp, (char *)buffer, 2) == 2) | |
942 | return (0); | |
943 | } | |
944 | else | |
945 | { | |
946 | // Two-word UTF-16 big-endian... | |
947 | ch -= 0x10000; | |
948 | ||
7e86f2f6 MS |
949 | buffer[0] = (unsigned char)(0xd8 | (ch >> 18)); |
950 | buffer[1] = (unsigned char)(ch >> 10); | |
951 | buffer[2] = (unsigned char)(0xdc | ((ch >> 8) & 0x03)); | |
952 | buffer[3] = (unsigned char)ch; | |
839a51c8 MS |
953 | |
954 | if (cupsFileWrite(fp, (char *)buffer, 4) == 4) | |
955 | return (0); | |
956 | } | |
957 | ||
958 | return (-1); | |
959 | } |