]> git.ipfire.org Git - thirdparty/cups.git/blob - ppdc/ppdc-catalog.cxx
Update all references to OS X to macOS.
[thirdparty/cups.git] / ppdc / ppdc-catalog.cxx
1 //
2 // Shared message catalog class for the CUPS PPD Compiler.
3 //
4 // Copyright 2007-2016 by Apple Inc.
5 // Copyright 2002-2006 by Easy Software Products.
6 //
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/".
12 //
13
14 //
15 // Include necessary headers...
16 //
17
18 #include "ppdc-private.h"
19
20
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
38 #if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
39 static void apple_add_message(CFStringRef key, CFStringRef val, ppdcCatalog *c);
40 #endif /* __APPLE__ && CUPS_BUNDLEDIR */
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
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 {
55 PPDC_NEW;
56
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
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))
82 tl = "no";
83 else if (!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
130 snprintf(pofile, sizeof(pofile), "%s/%s/cups_%s.po", cg->localedir, l, l);
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));
139 snprintf(pofile, sizeof(pofile), "%s/%s/cups_%s.po", cg->localedir,
140 baseloc, baseloc);
141
142 load_messages(pofile);
143 }
144 #endif /* __APPLE__ && CUPS_BUNDLEDIR */
145 }
146
147 if (f && *f)
148 load_messages(f);
149 }
150
151
152 //
153 // 'ppdcCatalog::~ppdcCatalog()' - Destroy a shared message catalog.
154 //
155
156 ppdcCatalog::~ppdcCatalog()
157 {
158 PPDC_DELETE;
159
160 locale->release();
161 filename->release();
162 messages->release();
163 }
164
165
166 //
167 // 'ppdcCatalog::add_message()' - Add a new message.
168 //
169
170 void
171 ppdcCatalog::add_message(
172 const char *id, // I - Message ID to add
173 const char *string) // I - Translation string
174 {
175 ppdcMessage *m; // Current message
176 char text[1024]; // Text to translate
177
178
179 // Range check input...
180 if (!id)
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))
188 {
189 if (string)
190 {
191 m->string->release();
192 m->string = new ppdcString(string);
193 }
194 return;
195 }
196
197 // Add the message...
198 if (!string)
199 {
200 snprintf(text, sizeof(text), "TRANSLATE %s", id);
201 string = text;
202 }
203
204 messages->add(new ppdcMessage(id, string));
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
219 if (!*id)
220 return (id);
221
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
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
252 if ((ptr = (char *)strrchr(f, '.')) == NULL)
253 goto unknown_load_format;
254 else if (!strcmp(ptr, ".strings"))
255 {
256 /*
257 * Read messages in macOS ".strings" format, which are either UTF-8/UTF-16
258 * text files of the format:
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;
283
284 if (ch == 'n')
285 ch = '\n';
286 else if (ch == 't')
287 ch = '\t';
288 }
289 else if (ch == '\"')
290 {
291 *ptr = '\0';
292 ptr = NULL;
293 }
294
295 if (ptr)
296 put_utf8(ch, ptr, end);
297 }
298 else if (ch == '/')
299 {
300 // Start of a comment?
301 if ((ch = get_utf16(fp, cs)) == 0)
302 break;
303
304 if (ch == '*')
305 {
306 // Skip C comment...
307 int lastch = 0;
308
309 while ((ch = get_utf16(fp, cs)) != 0)
310 {
311 if (ch == '/' && lastch == '*')
312 break;
313
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 {
327 // Start quoted string...
328 if (id[0])
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...
342 add_message(id, str);
343 id[0] = '\0';
344 }
345 }
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
366 int which, // In msgid?
367 haveid, // Did we get a msgid string?
368 havestr; // Did we get a msgstr string?
369
370 linenum = 0;
371 id[0] = '\0';
372 str[0] = '\0';
373 haveid = 0;
374 havestr = 0;
375 which = 0;
376
377 while (cupsFileGets(fp, line, sizeof(line)))
378 {
379 linenum ++;
380
381 // Skip blank and comment lines...
382 if (line[0] == '#' || !line[0])
383 continue;
384
385 // Strip the trailing quote...
386 if ((ptr = (char *)strrchr(line, '\"')) == NULL)
387 {
388 _cupsLangPrintf(stderr,
389 _("ppdc: Expected quoted string on line %d of %s."),
390 linenum, f);
391 cupsFileClose(fp);
392 return (-1);
393 }
394
395 *ptr = '\0';
396
397 // Find start of value...
398 if ((ptr = strchr(line, '\"')) == NULL)
399 {
400 _cupsLangPrintf(stderr,
401 _("ppdc: Expected quoted string on line %d of %s."),
402 linenum, f);
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;)
413 {
414 if (*sptr == '\\')
415 {
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 }
426
427 dptr ++;
428 }
429 else
430 {
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
440 sptr ++;
441 }
442 }
443 else
444 *dptr++ = *sptr++;
445 }
446
447 *dptr = '\0';
448
449 // Create or add to a message...
450 if (!strncmp(line, "msgid", 5))
451 {
452 if (haveid && havestr)
453 add_message(id, str);
454
455 strlcpy(id, ptr, sizeof(id));
456 str[0] = '\0';
457 haveid = 1;
458 havestr = 0;
459 which = 1;
460 }
461 else if (!strncmp(line, "msgstr", 6))
462 {
463 if (!haveid)
464 {
465 _cupsLangPrintf(stderr,
466 _("ppdc: Need a msgid line before any "
467 "translation strings on line %d of %s."),
468 linenum, f);
469 cupsFileClose(fp);
470 return (-1);
471 }
472
473 strlcpy(str, ptr, sizeof(str));
474 havestr = 1;
475 which = 2;
476 }
477 else if (line[0] == '\"' && which == 2)
478 strlcat(str, ptr, sizeof(str));
479 else if (line[0] == '\"' && which == 1)
480 strlcat(id, ptr, sizeof(id));
481 else
482 {
483 _cupsLangPrintf(stderr, _("ppdc: Unexpected text on line %d of %s."),
484 linenum, f);
485 cupsFileClose(fp);
486 return (-1);
487 }
488 }
489
490 if (haveid && havestr)
491 add_message(id, str);
492 }
493 else
494 goto unknown_load_format;
495
496 /*
497 * Close the file and return...
498 */
499
500 cupsFileClose(fp);
501
502 return (0);
503
504 /*
505 * Unknown format error...
506 */
507
508 unknown_load_format:
509
510 _cupsLangPrintf(stderr,
511 _("ppdc: Unknown message catalog format for \"%s\"."), f);
512 cupsFileClose(fp);
513 return (-1);
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
527 char *ptr; // Pointer into string
528 int utf16; // Output UTF-16 .strings file?
529 int ch; // Current character
530
531
532 // Open the file...
533 if ((ptr = (char *)strrchr(f, '.')) == NULL)
534 return (-1);
535
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...
551 for (m = (ppdcMessage *)messages->first();
552 m;
553 m = (ppdcMessage *)messages->next())
554 {
555 if (utf16)
556 {
557 put_utf16(fp, '\"');
558
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 }
606
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 }
653 }
654
655 cupsFileClose(fp);
656
657 return (0);
658 }
659
660
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
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...
800 if ((ch = cupsFileGetChar(fp)) < 0)
801 return (0);
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
889 *ptr++ = (char)ch;
890 }
891 else if (ch < 0x800)
892 {
893 // Two-byte UTF-8...
894 if ((ptr + 1) >= end)
895 return (-1);
896
897 *ptr++ = (char)(0xc0 | (ch >> 6));
898 *ptr++ = (char)(0x80 | (ch & 0x3f));
899 }
900 else if (ch < 0x10000)
901 {
902 // Three-byte UTF-8...
903 if ((ptr + 2) >= end)
904 return (-1);
905
906 *ptr++ = (char)(0xe0 | (ch >> 12));
907 *ptr++ = (char)(0x80 | ((ch >> 6) & 0x3f));
908 *ptr++ = (char)(0x80 | (ch & 0x3f));
909 }
910 else
911 {
912 // Four-byte UTF-8...
913 if ((ptr + 3) >= end)
914 return (-1);
915
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));
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...
940 buffer[0] = (unsigned char)(ch >> 8);
941 buffer[1] = (unsigned char)ch;
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
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;
955
956 if (cupsFileWrite(fp, (char *)buffer, 4) == 4)
957 return (0);
958 }
959
960 return (-1);
961 }