• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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