]> git.ipfire.org Git - thirdparty/cups.git/blame - ppdc/ppdc-catalog.cxx
Import CUPS v2.0b1
[thirdparty/cups.git] / ppdc / ppdc-catalog.cxx
CommitLineData
ac884b6a 1//
1a18c85c 2// "$Id: ppdc-catalog.cxx 11800 2014-04-08 19:53:57Z msweet $"
ac884b6a 3//
1a18c85c 4// Shared message catalog class for the CUPS PPD Compiler.
ac884b6a 5//
1a18c85c
MS
6// Copyright 2007-2014 by Apple Inc.
7// Copyright 2002-2006 by Easy Software Products.
ac884b6a 8//
1a18c85c
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
27typedef 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
1a18c85c
MS
40#if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
41static void apple_add_message(CFStringRef key, CFStringRef val, ppdcCatalog *c);
42#endif /* __APPLE__ && CUPS_BUNDLEDIR */
839a51c8
MS
43static int get_utf8(char *&ptr);
44static int get_utf16(cups_file_t *fp, ppdc_cs_t &cs);
45static int put_utf8(int ch, char *&ptr, char *end);
46static int put_utf16(cups_file_t *fp, int ch);
47
48
ac884b6a
MS
49//
50// 'ppdcCatalog::ppdcCatalog()' - Create a shared message catalog.
51//
52
53ppdcCatalog::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
1a18c85c
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 }
1a18c85c 144#endif /* __APPLE__ && CUPS_BUNDLEDIR */
ac884b6a
MS
145 }
146
1a18c85c 147 if (f && *f)
ac884b6a
MS
148 load_messages(f);
149}
150
151
152//
153// 'ppdcCatalog::~ppdcCatalog()' - Destroy a shared message catalog.
154//
155
156ppdcCatalog::~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
170void
61cf44e2
MS
171ppdcCatalog::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
212const char * // O - Message text
213ppdcCatalog::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
236int // O - 0 on success, -1 on failure
237ppdcCatalog::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 /*
1a18c85c
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
521int // O - 0 on success, -1 on error
522ppdcCatalog::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
1a18c85c
MS
661#if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
662//
663// 'apple_add_message()' - Add a message from a localization dictionary.
664//
665
666static void
667apple_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
686static int // O - Unicode character or 0 on EOF
687get_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
743static int // O - Unicode character or 0 on EOF
744get_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
878static int // O - 0 on success, -1 on failure
879put_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
1a18c85c 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
1a18c85c
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
1a18c85c
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
1a18c85c
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
930static int // O - 0 on success, -1 on failure
931put_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...
1a18c85c
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
1a18c85c
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 964//
1a18c85c 965// End of "$Id: ppdc-catalog.cxx 11800 2014-04-08 19:53:57Z msweet $".
ac884b6a 966//