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