1 //
2 // Shared message catalog class for the CUPS PPD Compiler.
3 //
4 // Copyright 2007-2017 by Apple Inc.
5 // Copyright 2002-2006 by Easy Software Products.
6 //
7 // Licensed under Apache License v2.0. See the file "LICENSE" for more information.
8 //
9
10 //
11 // Include necessary headers...
12 //
13
14 #include "ppdc-private.h"
15
16
17 //
18 // Character encodings...
19 //
20
21 typedef 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
34 #if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
35 static void apple_add_message(CFStringRef key, CFStringRef val, ppdcCatalog *c);
36 #endif /* __APPLE__ && CUPS_BUNDLEDIR */
37 static int get_utf8(char *&ptr);
38 static int get_utf16(cups_file_t *fp, ppdc_cs_t &cs);
39 static int put_utf8(int ch, char *&ptr, char *end);
40 static int put_utf16(cups_file_t *fp, int ch);
41
42
43 //
44 // 'ppdcCatalog::ppdcCatalog()' - Create a shared message catalog.
45 //
46
ppdcCatalog(const char * l,const char * f)47 ppdcCatalog::ppdcCatalog(const char *l, // I - Locale
48 const char *f) // I - Message catalog file
49 : ppdcShared()
50 {
51 PPDC_NEW;
52
53 locale = new ppdcString(l);
54 filename = new ppdcString(f);
55 messages = new ppdcArray();
56
57 if (l && strcmp(l, "en"))
58 {
59 // Try loading the base messages for this locale...
60 char pofile[1024]; // Message catalog file
61
62
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";
77 else if (!strncmp(l, "nb", 2))
78 tl = "no";
79 else if (!strncmp(l, "nl", 2))
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
126 snprintf(pofile, sizeof(pofile), "%s/%s/cups_%s.po", cg->localedir, l, l);
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));
135 snprintf(pofile, sizeof(pofile), "%s/%s/cups_%s.po", cg->localedir,
136 baseloc, baseloc);
137
138 load_messages(pofile);
139 }
140 #endif /* __APPLE__ && CUPS_BUNDLEDIR */
141 }
142
143 if (f && *f)
144 load_messages(f);
145 }
146
147
148 //
149 // 'ppdcCatalog::~ppdcCatalog()' - Destroy a shared message catalog.
150 //
151
~ppdcCatalog()152 ppdcCatalog::~ppdcCatalog()
153 {
154 PPDC_DELETE;
155
156 locale->release();
157 filename->release();
158 messages->release();
159 }
160
161
162 //
163 // 'ppdcCatalog::add_message()' - Add a new message.
164 //
165
166 void
add_message(const char * id,const char * string)167 ppdcCatalog::add_message(
168 const char *id, // I - Message ID to add
169 const char *string) // I - Translation string
170 {
171 ppdcMessage *m; // Current message
172 char text[1024]; // Text to translate
173
174
175 // Range check input...
176 if (!id)
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))
184 {
185 if (string)
186 {
187 m->string->release();
188 m->string = new ppdcString(string);
189 }
190 return;
191 }
192
193 // Add the message...
194 if (!string)
195 {
196 snprintf(text, sizeof(text), "TRANSLATE %s", id);
197 string = text;
198 }
199
200 messages->add(new ppdcMessage(id, string));
201 }
202
203
204 //
205 // 'ppdcCatalog::find_message()' - Find a message in a catalog...
206 //
207
208 const char * // O - Message text
find_message(const char * id)209 ppdcCatalog::find_message(
210 const char *id) // I - Message ID
211 {
212 ppdcMessage *m; // Current message
213
214
215 if (!*id)
216 return (id);
217
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
232 int // O - 0 on success, -1 on failure
load_messages(const char * f)233 ppdcCatalog::load_messages(
234 const char *f) // I - Message catalog file
235 {
236 cups_file_t *fp; // Message file
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
248 if ((ptr = (char *)strrchr(f, '.')) == NULL)
249 goto unknown_load_format;
250 else if (!strcmp(ptr, ".strings"))
251 {
252 /*
253 * Read messages in macOS ".strings" format, which are either UTF-8/UTF-16
254 * text files of the format:
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;
279
280 if (ch == 'n')
281 ch = '\n';
282 else if (ch == 't')
283 ch = '\t';
284 }
285 else if (ch == '\"')
286 {
287 *ptr = '\0';
288 ptr = NULL;
289 }
290
291 if (ptr)
292 put_utf8(ch, ptr, end);
293 }
294 else if (ch == '/')
295 {
296 // Start of a comment?
297 if ((ch = get_utf16(fp, cs)) == 0)
298 break;
299
300 if (ch == '*')
301 {
302 // Skip C comment...
303 int lastch = 0;
304
305 while ((ch = get_utf16(fp, cs)) != 0)
306 {
307 if (ch == '/' && lastch == '*')
308 break;
309
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 {
323 // Start quoted string...
324 if (id[0])
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...
338 add_message(id, str);
339 id[0] = '\0';
340 }
341 }
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
362 int which, // In msgid?
363 haveid, // Did we get a msgid string?
364 havestr; // Did we get a msgstr string?
365
366 linenum = 0;
367 id[0] = '\0';
368 str[0] = '\0';
369 haveid = 0;
370 havestr = 0;
371 which = 0;
372
373 while (cupsFileGets(fp, line, sizeof(line)))
374 {
375 linenum ++;
376
377 // Skip blank and comment lines...
378 if (line[0] == '#' || !line[0])
379 continue;
380
381 // Strip the trailing quote...
382 if ((ptr = (char *)strrchr(line, '\"')) == NULL)
383 {
384 _cupsLangPrintf(stderr,
385 _("ppdc: Expected quoted string on line %d of %s."),
386 linenum, f);
387 cupsFileClose(fp);
388 return (-1);
389 }
390
391 *ptr = '\0';
392
393 // Find start of value...
394 if ((ptr = strchr(line, '\"')) == NULL)
395 {
396 _cupsLangPrintf(stderr,
397 _("ppdc: Expected quoted string on line %d of %s."),
398 linenum, f);
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;)
409 {
410 if (*sptr == '\\')
411 {
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 }
422
423 dptr ++;
424 }
425 else
426 {
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
436 sptr ++;
437 }
438 }
439 else
440 *dptr++ = *sptr++;
441 }
442
443 *dptr = '\0';
444
445 // Create or add to a message...
446 if (!strncmp(line, "msgid", 5))
447 {
448 if (haveid && havestr)
449 add_message(id, str);
450
451 strlcpy(id, ptr, sizeof(id));
452 str[0] = '\0';
453 haveid = 1;
454 havestr = 0;
455 which = 1;
456 }
457 else if (!strncmp(line, "msgstr", 6))
458 {
459 if (!haveid)
460 {
461 _cupsLangPrintf(stderr,
462 _("ppdc: Need a msgid line before any "
463 "translation strings on line %d of %s."),
464 linenum, f);
465 cupsFileClose(fp);
466 return (-1);
467 }
468
469 strlcpy(str, ptr, sizeof(str));
470 havestr = 1;
471 which = 2;
472 }
473 else if (line[0] == '\"' && which == 2)
474 strlcat(str, ptr, sizeof(str));
475 else if (line[0] == '\"' && which == 1)
476 strlcat(id, ptr, sizeof(id));
477 else
478 {
479 _cupsLangPrintf(stderr, _("ppdc: Unexpected text on line %d of %s."),
480 linenum, f);
481 cupsFileClose(fp);
482 return (-1);
483 }
484 }
485
486 if (haveid && havestr)
487 add_message(id, str);
488 }
489 else
490 goto unknown_load_format;
491
492 /*
493 * Close the file and return...
494 */
495
496 cupsFileClose(fp);
497
498 return (0);
499
500 /*
501 * Unknown format error...
502 */
503
504 unknown_load_format:
505
506 _cupsLangPrintf(stderr,
507 _("ppdc: Unknown message catalog format for \"%s\"."), f);
508 cupsFileClose(fp);
509 return (-1);
510 }
511
512
513 //
514 // 'ppdcCatalog::save_messages()' - Save the messages to a .po file.
515 //
516
517 int // O - 0 on success, -1 on error
save_messages(const char * f)518 ppdcCatalog::save_messages(
519 const char *f) // I - File to save to
520 {
521 cups_file_t *fp; // Message file
522 ppdcMessage *m; // Current message
523 char *ptr; // Pointer into string
524 int utf16; // Output UTF-16 .strings file?
525 int ch; // Current character
526
527
528 // Open the file...
529 if ((ptr = (char *)strrchr(f, '.')) == NULL)
530 return (-1);
531
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...
547 for (m = (ppdcMessage *)messages->first();
548 m;
549 m = (ppdcMessage *)messages->next())
550 {
551 if (utf16)
552 {
553 put_utf16(fp, '\"');
554
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 }
602
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 }
649 }
650
651 cupsFileClose(fp);
652
653 return (0);
654 }
655
656
657 #if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
658 //
659 // 'apple_add_message()' - Add a message from a localization dictionary.
660 //
661
662 static void
apple_add_message(CFStringRef key,CFStringRef val,ppdcCatalog * c)663 apple_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
678 //
679 // 'get_utf8()' - Get a UTF-8 character.
680 //
681
682 static int // O - Unicode character or 0 on EOF
get_utf8(char * & ptr)683 get_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
739 static int // O - Unicode character or 0 on EOF
get_utf16(cups_file_t * fp,ppdc_cs_t & cs)740 get_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...
796 if ((ch = cupsFileGetChar(fp)) < 0)
797 return (0);
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
874 static int // O - 0 on success, -1 on failure
put_utf8(int ch,char * & ptr,char * end)875 put_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
885 *ptr++ = (char)ch;
886 }
887 else if (ch < 0x800)
888 {
889 // Two-byte UTF-8...
890 if ((ptr + 1) >= end)
891 return (-1);
892
893 *ptr++ = (char)(0xc0 | (ch >> 6));
894 *ptr++ = (char)(0x80 | (ch & 0x3f));
895 }
896 else if (ch < 0x10000)
897 {
898 // Three-byte UTF-8...
899 if ((ptr + 2) >= end)
900 return (-1);
901
902 *ptr++ = (char)(0xe0 | (ch >> 12));
903 *ptr++ = (char)(0x80 | ((ch >> 6) & 0x3f));
904 *ptr++ = (char)(0x80 | (ch & 0x3f));
905 }
906 else
907 {
908 // Four-byte UTF-8...
909 if ((ptr + 3) >= end)
910 return (-1);
911
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));
916 }
917
918 return (0);
919 }
920
921
922 //
923 // 'put_utf16()' - Write a UTF-16 character to a file.
924 //
925
926 static int // O - 0 on success, -1 on failure
put_utf16(cups_file_t * fp,int ch)927 put_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...
936 buffer[0] = (unsigned char)(ch >> 8);
937 buffer[1] = (unsigned char)ch;
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
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;
951
952 if (cupsFileWrite(fp, (char *)buffer, 4) == 4)
953 return (0);
954 }
955
956 return (-1);
957 }
958