1// 2// "$Id: ppdc-catalog.cxx 11801 2014-04-08 19:55:21Z msweet $" 3// 4// Shared message catalog class for the CUPS PPD Compiler. 5// 6// Copyright 2007-2014 by Apple Inc. 7// Copyright 2002-2006 by Easy Software Products. 8// 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/". 14// 15 16// 17// Include necessary headers... 18// 19 20#include "ppdc-private.h" 21 22 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 40#if defined(__APPLE__) && defined(CUPS_BUNDLEDIR) 41static void apple_add_message(CFStringRef key, CFStringRef val, ppdcCatalog *c); 42#endif /* __APPLE__ && CUPS_BUNDLEDIR */ 43static int get_utf8(char *&ptr); 44static int get_utf16(cups_file_t *fp, ppdc_cs_t &cs); 45static int put_utf8(int ch, char *&ptr, char *end); 46static int put_utf16(cups_file_t *fp, int ch); 47 48 49// 50// 'ppdcCatalog::ppdcCatalog()' - Create a shared message catalog. 51// 52 53ppdcCatalog::ppdcCatalog(const char *l, // I - Locale 54 const char *f) // I - Message catalog file 55 : ppdcShared() 56{ 57 PPDC_NEW; 58 59 locale = new ppdcString(l); 60 filename = new ppdcString(f); 61 messages = new ppdcArray(); 62 63 if (l) 64 { 65 // Try loading the base messages for this locale... 66 char pofile[1024]; // Message catalog file 67 68 69#if defined(__APPLE__) && defined(CUPS_BUNDLEDIR) 70 char applelang[256]; // Apple language ID 71 CFURLRef url; // URL to cups.strings file 72 CFReadStreamRef stream = NULL; // File stream 73 CFPropertyListRef plist = NULL; // Localization file 74 75 snprintf(pofile, sizeof(pofile), CUPS_BUNDLEDIR "/Resources/%s.lproj/cups.strings", _cupsAppleLanguage(l, applelang, sizeof(applelang))); 76 if (access(pofile, 0)) 77 { 78 // Try alternate lproj directory names... 79 const char *tl = l; // Temporary locale string 80 81 if (!strncmp(l, "en", 2)) 82 tl = "English"; 83 else if (!strncmp(l, "nb", 2) || !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 156ppdcCatalog::~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 170void 171ppdcCatalog::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 212const char * // O - Message text 213ppdcCatalog::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 236int // O - 0 on success, -1 on failure 237ppdcCatalog::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 OS X ".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 521int // O - 0 on success, -1 on error 522ppdcCatalog::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 666static void 667apple_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 686static int // O - Unicode character or 0 on EOF 687get_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 743static int // O - Unicode character or 0 on EOF 744get_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 878static int // O - 0 on success, -1 on failure 879put_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 930static int // O - 0 on success, -1 on failure 931put_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} 962 963 964// 965// End of "$Id: ppdc-catalog.cxx 11801 2014-04-08 19:55:21Z msweet $". 966// 967