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