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