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