1 /* 2 * PPD file routines for CUPS. 3 * 4 * Copyright 2007-2017 by Apple Inc. 5 * Copyright 1997-2007 by Easy Software Products, all rights reserved. 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 * missing or damaged, see the license at "http://www.cups.org/". 12 * 13 * PostScript is a trademark of Adobe Systems, Inc. 14 * 15 * This code and any derivative of it may be used and distributed 16 * freely under the terms of the GNU General Public License when 17 * used with GNU Ghostscript or its derivatives. Use of the code 18 * (or any derivative of it) with software other than GNU 19 * GhostScript (or its derivatives) is governed by the CUPS license 20 * agreement. 21 * 22 * This file is subject to the Apple OS-Developed Software exception. 23 */ 24 25 /* 26 * Include necessary headers. 27 */ 28 29 #include "cups-private.h" 30 #include "ppd-private.h" 31 32 33 /* 34 * Definitions... 35 */ 36 37 #define ppd_free(p) if (p) free(p) /* Safe free macro */ 38 39 #define PPD_KEYWORD 1 /* Line contained a keyword */ 40 #define PPD_OPTION 2 /* Line contained an option name */ 41 #define PPD_TEXT 4 /* Line contained human-readable text */ 42 #define PPD_STRING 8 /* Line contained a string or code */ 43 44 #define PPD_HASHSIZE 512 /* Size of hash */ 45 46 47 /* 48 * Line buffer structure... 49 */ 50 51 typedef struct _ppd_line_s 52 { 53 char *buffer; /* Pointer to buffer */ 54 size_t bufsize; /* Size of the buffer */ 55 } _ppd_line_t; 56 57 58 /* 59 * Local globals... 60 */ 61 62 static _cups_threadkey_t ppd_globals_key = _CUPS_THREADKEY_INITIALIZER; 63 /* Thread local storage key */ 64 #ifdef HAVE_PTHREAD_H 65 static pthread_once_t ppd_globals_key_once = PTHREAD_ONCE_INIT; 66 /* One-time initialization object */ 67 #endif /* HAVE_PTHREAD_H */ 68 69 70 /* 71 * Local functions... 72 */ 73 74 static ppd_attr_t *ppd_add_attr(ppd_file_t *ppd, const char *name, 75 const char *spec, const char *text, 76 const char *value); 77 static ppd_choice_t *ppd_add_choice(ppd_option_t *option, const char *name); 78 static ppd_size_t *ppd_add_size(ppd_file_t *ppd, const char *name); 79 static int ppd_compare_attrs(ppd_attr_t *a, ppd_attr_t *b); 80 static int ppd_compare_choices(ppd_choice_t *a, ppd_choice_t *b); 81 static int ppd_compare_coptions(ppd_coption_t *a, 82 ppd_coption_t *b); 83 static int ppd_compare_options(ppd_option_t *a, ppd_option_t *b); 84 static int ppd_decode(char *string); 85 static void ppd_free_filters(ppd_file_t *ppd); 86 static void ppd_free_group(ppd_group_t *group); 87 static void ppd_free_option(ppd_option_t *option); 88 static ppd_coption_t *ppd_get_coption(ppd_file_t *ppd, const char *name); 89 static ppd_cparam_t *ppd_get_cparam(ppd_coption_t *opt, 90 const char *param, 91 const char *text); 92 static ppd_group_t *ppd_get_group(ppd_file_t *ppd, const char *name, 93 const char *text, _ppd_globals_t *pg, 94 cups_encoding_t encoding); 95 static ppd_option_t *ppd_get_option(ppd_group_t *group, const char *name); 96 static _ppd_globals_t *ppd_globals_alloc(void); 97 #if defined(HAVE_PTHREAD_H) || defined(WIN32) 98 static void ppd_globals_free(_ppd_globals_t *g); 99 #endif /* HAVE_PTHREAD_H || WIN32 */ 100 #ifdef HAVE_PTHREAD_H 101 static void ppd_globals_init(void); 102 #endif /* HAVE_PTHREAD_H */ 103 static int ppd_hash_option(ppd_option_t *option); 104 static int ppd_read(cups_file_t *fp, _ppd_line_t *line, 105 char *keyword, char *option, char *text, 106 char **string, int ignoreblank, 107 _ppd_globals_t *pg); 108 static int ppd_update_filters(ppd_file_t *ppd, 109 _ppd_globals_t *pg); 110 111 112 /* 113 * 'ppdClose()' - Free all memory used by the PPD file. 114 */ 115 116 void 117 ppdClose(ppd_file_t *ppd) /* I - PPD file record */ 118 { 119 int i; /* Looping var */ 120 ppd_emul_t *emul; /* Current emulation */ 121 ppd_group_t *group; /* Current group */ 122 char **font; /* Current font */ 123 ppd_attr_t **attr; /* Current attribute */ 124 ppd_coption_t *coption; /* Current custom option */ 125 ppd_cparam_t *cparam; /* Current custom parameter */ 126 127 128 /* 129 * Range check arguments... 130 */ 131 132 if (!ppd) 133 return; 134 135 /* 136 * Free all strings at the top level... 137 */ 138 139 _cupsStrFree(ppd->lang_encoding); 140 _cupsStrFree(ppd->nickname); 141 if (ppd->patches) 142 free(ppd->patches); 143 _cupsStrFree(ppd->jcl_begin); 144 _cupsStrFree(ppd->jcl_end); 145 _cupsStrFree(ppd->jcl_ps); 146 147 /* 148 * Free any emulations... 149 */ 150 151 if (ppd->num_emulations > 0) 152 { 153 for (i = ppd->num_emulations, emul = ppd->emulations; i > 0; i --, emul ++) 154 { 155 _cupsStrFree(emul->start); 156 _cupsStrFree(emul->stop); 157 } 158 159 ppd_free(ppd->emulations); 160 } 161 162 /* 163 * Free any UI groups, subgroups, and options... 164 */ 165 166 if (ppd->num_groups > 0) 167 { 168 for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++) 169 ppd_free_group(group); 170 171 ppd_free(ppd->groups); 172 } 173 174 cupsArrayDelete(ppd->options); 175 cupsArrayDelete(ppd->marked); 176 177 /* 178 * Free any page sizes... 179 */ 180 181 if (ppd->num_sizes > 0) 182 ppd_free(ppd->sizes); 183 184 /* 185 * Free any constraints... 186 */ 187 188 if (ppd->num_consts > 0) 189 ppd_free(ppd->consts); 190 191 /* 192 * Free any filters... 193 */ 194 195 ppd_free_filters(ppd); 196 197 /* 198 * Free any fonts... 199 */ 200 201 if (ppd->num_fonts > 0) 202 { 203 for (i = ppd->num_fonts, font = ppd->fonts; i > 0; i --, font ++) 204 _cupsStrFree(*font); 205 206 ppd_free(ppd->fonts); 207 } 208 209 /* 210 * Free any profiles... 211 */ 212 213 if (ppd->num_profiles > 0) 214 ppd_free(ppd->profiles); 215 216 /* 217 * Free any attributes... 218 */ 219 220 if (ppd->num_attrs > 0) 221 { 222 for (i = ppd->num_attrs, attr = ppd->attrs; i > 0; i --, attr ++) 223 { 224 _cupsStrFree((*attr)->value); 225 ppd_free(*attr); 226 } 227 228 ppd_free(ppd->attrs); 229 } 230 231 cupsArrayDelete(ppd->sorted_attrs); 232 233 /* 234 * Free custom options... 235 */ 236 237 for (coption = (ppd_coption_t *)cupsArrayFirst(ppd->coptions); 238 coption; 239 coption = (ppd_coption_t *)cupsArrayNext(ppd->coptions)) 240 { 241 for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params); 242 cparam; 243 cparam = (ppd_cparam_t *)cupsArrayNext(coption->params)) 244 { 245 switch (cparam->type) 246 { 247 case PPD_CUSTOM_PASSCODE : 248 case PPD_CUSTOM_PASSWORD : 249 case PPD_CUSTOM_STRING : 250 _cupsStrFree(cparam->current.custom_string); 251 break; 252 253 default : 254 break; 255 } 256 257 free(cparam); 258 } 259 260 cupsArrayDelete(coption->params); 261 262 free(coption); 263 } 264 265 cupsArrayDelete(ppd->coptions); 266 267 /* 268 * Free constraints... 269 */ 270 271 if (ppd->cups_uiconstraints) 272 { 273 _ppd_cups_uiconsts_t *consts; /* Current constraints */ 274 275 276 for (consts = (_ppd_cups_uiconsts_t *)cupsArrayFirst(ppd->cups_uiconstraints); 277 consts; 278 consts = (_ppd_cups_uiconsts_t *)cupsArrayNext(ppd->cups_uiconstraints)) 279 { 280 free(consts->constraints); 281 free(consts); 282 } 283 284 cupsArrayDelete(ppd->cups_uiconstraints); 285 } 286 287 /* 288 * Free any PPD cache/mapping data... 289 */ 290 291 if (ppd->cache) 292 _ppdCacheDestroy(ppd->cache); 293 294 /* 295 * Free the whole record... 296 */ 297 298 ppd_free(ppd); 299 } 300 301 302 /* 303 * 'ppdErrorString()' - Returns the text associated with a status. 304 * 305 * @since CUPS 1.1.19/macOS 10.3@ 306 */ 307 308 const char * /* O - Status string */ 309 ppdErrorString(ppd_status_t status) /* I - PPD status */ 310 { 311 static const char * const messages[] =/* Status messages */ 312 { 313 _("OK"), 314 _("Unable to open PPD file"), 315 _("NULL PPD file pointer"), 316 _("Memory allocation error"), 317 _("Missing PPD-Adobe-4.x header"), 318 _("Missing value string"), 319 _("Internal error"), 320 _("Bad OpenGroup"), 321 _("OpenGroup without a CloseGroup first"), 322 _("Bad OpenUI/JCLOpenUI"), 323 _("OpenUI/JCLOpenUI without a CloseUI/JCLCloseUI first"), 324 _("Bad OrderDependency"), 325 _("Bad UIConstraints"), 326 _("Missing asterisk in column 1"), 327 _("Line longer than the maximum allowed (255 characters)"), 328 _("Illegal control character"), 329 _("Illegal main keyword string"), 330 _("Illegal option keyword string"), 331 _("Illegal translation string"), 332 _("Illegal whitespace character"), 333 _("Bad custom parameter"), 334 _("Missing option keyword"), 335 _("Bad value string"), 336 _("Missing CloseGroup") 337 }; 338 339 340 if (status < PPD_OK || status >= PPD_MAX_STATUS) 341 return (_cupsLangString(cupsLangDefault(), _("Unknown"))); 342 else 343 return (_cupsLangString(cupsLangDefault(), messages[status])); 344 } 345 346 347 /* 348 * '_ppdGetEncoding()' - Get the CUPS encoding value for the given 349 * LanguageEncoding. 350 */ 351 352 cups_encoding_t /* O - CUPS encoding value */ 353 _ppdGetEncoding(const char *name) /* I - LanguageEncoding string */ 354 { 355 if (!_cups_strcasecmp(name, "ISOLatin1")) 356 return (CUPS_ISO8859_1); 357 else if (!_cups_strcasecmp(name, "ISOLatin2")) 358 return (CUPS_ISO8859_2); 359 else if (!_cups_strcasecmp(name, "ISOLatin5")) 360 return (CUPS_ISO8859_5); 361 else if (!_cups_strcasecmp(name, "JIS83-RKSJ")) 362 return (CUPS_JIS_X0213); 363 else if (!_cups_strcasecmp(name, "MacStandard")) 364 return (CUPS_MAC_ROMAN); 365 else if (!_cups_strcasecmp(name, "WindowsANSI")) 366 return (CUPS_WINDOWS_1252); 367 else 368 return (CUPS_UTF8); 369 } 370 371 372 /* 373 * '_ppdGlobals()' - Return a pointer to thread local storage 374 */ 375 376 _ppd_globals_t * /* O - Pointer to global data */ 377 _ppdGlobals(void) 378 { 379 _ppd_globals_t *pg; /* Pointer to global data */ 380 381 382 #ifdef HAVE_PTHREAD_H 383 /* 384 * Initialize the global data exactly once... 385 */ 386 387 pthread_once(&ppd_globals_key_once, ppd_globals_init); 388 #endif /* HAVE_PTHREAD_H */ 389 390 /* 391 * See if we have allocated the data yet... 392 */ 393 394 if ((pg = (_ppd_globals_t *)_cupsThreadGetData(ppd_globals_key)) == NULL) 395 { 396 /* 397 * No, allocate memory as set the pointer for the key... 398 */ 399 400 if ((pg = ppd_globals_alloc()) != NULL) 401 _cupsThreadSetData(ppd_globals_key, pg); 402 } 403 404 /* 405 * Return the pointer to the data... 406 */ 407 408 return (pg); 409 } 410 411 412 /* 413 * 'ppdLastError()' - Return the status from the last ppdOpen*(). 414 * 415 * @since CUPS 1.1.19/macOS 10.3@ 416 */ 417 418 ppd_status_t /* O - Status code */ 419 ppdLastError(int *line) /* O - Line number */ 420 { 421 _ppd_globals_t *pg = _ppdGlobals(); 422 /* Global data */ 423 424 425 if (line) 426 *line = pg->ppd_line; 427 428 return (pg->ppd_status); 429 } 430 431 432 /* 433 * '_ppdOpen()' - Read a PPD file into memory. 434 * 435 * @since CUPS 1.2/macOS 10.5@ 436 */ 437 438 ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */ 439 _ppdOpen( 440 cups_file_t *fp, /* I - File to read from */ 441 _ppd_localization_t localization) /* I - Localization to load */ 442 { 443 int i, j, k; /* Looping vars */ 444 int count; /* Temporary count */ 445 _ppd_line_t line; /* Line buffer */ 446 ppd_file_t *ppd; /* PPD file record */ 447 ppd_group_t *group, /* Current group */ 448 *subgroup; /* Current sub-group */ 449 ppd_option_t *option; /* Current option */ 450 ppd_choice_t *choice; /* Current choice */ 451 ppd_const_t *constraint; /* Current constraint */ 452 ppd_size_t *size; /* Current page size */ 453 int mask; /* Line data mask */ 454 char keyword[PPD_MAX_NAME], 455 /* Keyword from file */ 456 name[PPD_MAX_NAME], 457 /* Option from file */ 458 text[PPD_MAX_LINE], 459 /* Human-readable text from file */ 460 *string, /* Code/text from file */ 461 *sptr, /* Pointer into string */ 462 *nameptr, /* Pointer into name */ 463 *temp, /* Temporary string pointer */ 464 **tempfonts; /* Temporary fonts pointer */ 465 float order; /* Order dependency number */ 466 ppd_section_t section; /* Order dependency section */ 467 ppd_profile_t *profile; /* Pointer to color profile */ 468 char **filter; /* Pointer to filter */ 469 struct lconv *loc; /* Locale data */ 470 int ui_keyword; /* Is this line a UI keyword? */ 471 cups_lang_t *lang; /* Language data */ 472 cups_encoding_t encoding; /* Encoding of PPD file */ 473 _ppd_globals_t *pg = _ppdGlobals(); 474 /* Global data */ 475 char custom_name[PPD_MAX_NAME]; 476 /* CustomFoo attribute name */ 477 ppd_attr_t *custom_attr; /* CustomFoo attribute */ 478 char ll[7], /* Base language + '.' */ 479 ll_CC[7]; /* Language w/country + '.' */ 480 size_t ll_len = 0, /* Base language length */ 481 ll_CC_len = 0; /* Language w/country length */ 482 static const char * const ui_keywords[] = 483 { 484 #ifdef CUPS_USE_FULL_UI_KEYWORDS_LIST 485 /* 486 * Adobe defines some 41 keywords as "UI", meaning that they are 487 * user interface elements and that they should be treated as such 488 * even if the PPD creator doesn't use Open/CloseUI around them. 489 * 490 * Since this can cause previously invisible options to appear and 491 * confuse users, the default is to only treat the PageSize and 492 * PageRegion keywords this way. 493 */ 494 /* Boolean keywords */ 495 "BlackSubstitution", 496 "Booklet", 497 "Collate", 498 "ManualFeed", 499 "MirrorPrint", 500 "NegativePrint", 501 "Sorter", 502 "TraySwitch", 503 504 /* PickOne keywords */ 505 "AdvanceMedia", 506 "BindColor", 507 "BindEdge", 508 "BindType", 509 "BindWhen", 510 "BitsPerPixel", 511 "ColorModel", 512 "CutMedia", 513 "Duplex", 514 "FoldType", 515 "FoldWhen", 516 "InputSlot", 517 "JCLFrameBufferSize", 518 "JCLResolution", 519 "Jog", 520 "MediaColor", 521 "MediaType", 522 "MediaWeight", 523 "OutputBin", 524 "OutputMode", 525 "OutputOrder", 526 "PageRegion", 527 "PageSize", 528 "Resolution", 529 "Separations", 530 "Signature", 531 "Slipsheet", 532 "Smoothing", 533 "StapleLocation", 534 "StapleOrientation", 535 "StapleWhen", 536 "StapleX", 537 "StapleY" 538 #else /* !CUPS_USE_FULL_UI_KEYWORDS_LIST */ 539 "PageRegion", 540 "PageSize" 541 #endif /* CUPS_USE_FULL_UI_KEYWORDS_LIST */ 542 }; 543 static const char * const color_keywords[] = /* Keywords associated with color profiles */ 544 { 545 ".cupsICCProfile", 546 ".ColorModel", 547 }; 548 549 550 DEBUG_printf(("_ppdOpen(fp=%p)", fp)); 551 552 /* 553 * Default to "OK" status... 554 */ 555 556 pg->ppd_status = PPD_OK; 557 pg->ppd_line = 0; 558 559 /* 560 * Range check input... 561 */ 562 563 if (fp == NULL) 564 { 565 pg->ppd_status = PPD_NULL_FILE; 566 return (NULL); 567 } 568 569 /* 570 * If only loading a single localization set up the strings to match... 571 */ 572 573 if (localization == _PPD_LOCALIZATION_DEFAULT) 574 { 575 if ((lang = cupsLangDefault()) == NULL) 576 return (NULL); 577 578 snprintf(ll_CC, sizeof(ll_CC), "%s.", lang->language); 579 580 /* 581 * <rdar://problem/22130168> 582 * <rdar://problem/27245567> 583 * 584 * Need to use a different base language for some locales... 585 */ 586 587 if (!strcmp(lang->language, "zh_HK")) 588 { /* Traditional Chinese + variants */ 589 strlcpy(ll_CC, "zh_TW.", sizeof(ll_CC)); 590 strlcpy(ll, "zh_", sizeof(ll)); 591 } 592 else if (!strncmp(lang->language, "zh", 2)) 593 strlcpy(ll, "zh_", sizeof(ll)); /* Any Chinese variant */ 594 else if (!strncmp(lang->language, "jp", 2)) 595 { /* Any Japanese variant */ 596 strlcpy(ll_CC, "ja", sizeof(ll_CC)); 597 strlcpy(ll, "jp", sizeof(ll)); 598 } 599 else if (!strncmp(lang->language, "nb", 2) || !strncmp(lang->language, "no", 2)) 600 { /* Any Norwegian variant */ 601 strlcpy(ll_CC, "nb", sizeof(ll_CC)); 602 strlcpy(ll, "no", sizeof(ll)); 603 } 604 else 605 snprintf(ll, sizeof(ll), "%2.2s.", lang->language); 606 607 ll_CC_len = strlen(ll_CC); 608 ll_len = strlen(ll); 609 610 DEBUG_printf(("2_ppdOpen: Loading localizations matching \"%s\" and \"%s\"", 611 ll_CC, ll)); 612 } 613 614 /* 615 * Grab the first line and make sure it reads '*PPD-Adobe: "major.minor"'... 616 */ 617 618 line.buffer = NULL; 619 line.bufsize = 0; 620 621 mask = ppd_read(fp, &line, keyword, name, text, &string, 0, pg); 622 623 DEBUG_printf(("2_ppdOpen: mask=%x, keyword=\"%s\"...", mask, keyword)); 624 625 if (mask == 0 || 626 strcmp(keyword, "PPD-Adobe") || 627 string == NULL || string[0] != '4') 628 { 629 /* 630 * Either this is not a PPD file, or it is not a 4.x PPD file. 631 */ 632 633 if (pg->ppd_status == PPD_OK) 634 pg->ppd_status = PPD_MISSING_PPDADOBE4; 635 636 _cupsStrFree(string); 637 ppd_free(line.buffer); 638 639 return (NULL); 640 } 641 642 DEBUG_printf(("2_ppdOpen: keyword=%s, string=%p", keyword, string)); 643 644 _cupsStrFree(string); 645 646 /* 647 * Allocate memory for the PPD file record... 648 */ 649 650 if ((ppd = calloc(1, sizeof(ppd_file_t))) == NULL) 651 { 652 pg->ppd_status = PPD_ALLOC_ERROR; 653 654 _cupsStrFree(string); 655 ppd_free(line.buffer); 656 657 return (NULL); 658 } 659 660 ppd->language_level = 2; 661 ppd->color_device = 0; 662 ppd->colorspace = PPD_CS_N; 663 ppd->landscape = -90; 664 ppd->coptions = cupsArrayNew((cups_array_func_t)ppd_compare_coptions, 665 NULL); 666 667 /* 668 * Read lines from the PPD file and add them to the file record... 669 */ 670 671 group = NULL; 672 subgroup = NULL; 673 option = NULL; 674 choice = NULL; 675 ui_keyword = 0; 676 encoding = CUPS_ISO8859_1; 677 loc = localeconv(); 678 679 while ((mask = ppd_read(fp, &line, keyword, name, text, &string, 1, pg)) != 0) 680 { 681 DEBUG_printf(("2_ppdOpen: mask=%x, keyword=\"%s\", name=\"%s\", " 682 "text=\"%s\", string=%d chars...", mask, keyword, name, text, 683 string ? (int)strlen(string) : 0)); 684 685 if (strncmp(keyword, "Default", 7) && !string && 686 pg->ppd_conform != PPD_CONFORM_RELAXED) 687 { 688 /* 689 * Need a string value! 690 */ 691 692 pg->ppd_status = PPD_MISSING_VALUE; 693 694 goto error; 695 } 696 else if (!string) 697 continue; 698 699 /* 700 * Certain main keywords (as defined by the PPD spec) may be used 701 * without the usual OpenUI/CloseUI stuff. Presumably this is just 702 * so that Adobe wouldn't completely break compatibility with PPD 703 * files prior to v4.0 of the spec, but it is hopelessly 704 * inconsistent... Catch these main keywords and automatically 705 * create the corresponding option, as needed... 706 */ 707 708 if (ui_keyword) 709 { 710 /* 711 * Previous line was a UI keyword... 712 */ 713 714 option = NULL; 715 ui_keyword = 0; 716 } 717 718 /* 719 * If we are filtering out keyword localizations, see if this line needs to 720 * be used... 721 */ 722 723 if (localization != _PPD_LOCALIZATION_ALL && 724 (temp = strchr(keyword, '.')) != NULL && 725 ((temp - keyword) == 2 || (temp - keyword) == 5) && 726 _cups_isalpha(keyword[0]) && 727 _cups_isalpha(keyword[1]) && 728 (keyword[2] == '.' || 729 (keyword[2] == '_' && _cups_isalpha(keyword[3]) && 730 _cups_isalpha(keyword[4]) && keyword[5] == '.'))) 731 { 732 if (localization == _PPD_LOCALIZATION_NONE || 733 (localization == _PPD_LOCALIZATION_DEFAULT && 734 strncmp(ll_CC, keyword, ll_CC_len) && 735 strncmp(ll, keyword, ll_len))) 736 { 737 DEBUG_printf(("2_ppdOpen: Ignoring localization: \"%s\"\n", keyword)); 738 continue; 739 } 740 else if (localization == _PPD_LOCALIZATION_ICC_PROFILES) 741 { 742 /* 743 * Only load localizations for the color profile related keywords... 744 */ 745 746 for (i = 0; 747 i < (int)(sizeof(color_keywords) / sizeof(color_keywords[0])); 748 i ++) 749 { 750 if (!_cups_strcasecmp(temp, color_keywords[i])) 751 break; 752 } 753 754 if (i >= (int)(sizeof(color_keywords) / sizeof(color_keywords[0]))) 755 { 756 DEBUG_printf(("2_ppdOpen: Ignoring localization: \"%s\"\n", keyword)); 757 continue; 758 } 759 } 760 } 761 762 if (option == NULL && 763 (mask & (PPD_KEYWORD | PPD_OPTION | PPD_STRING)) == 764 (PPD_KEYWORD | PPD_OPTION | PPD_STRING)) 765 { 766 for (i = 0; i < (int)(sizeof(ui_keywords) / sizeof(ui_keywords[0])); i ++) 767 if (!strcmp(keyword, ui_keywords[i])) 768 break; 769 770 if (i < (int)(sizeof(ui_keywords) / sizeof(ui_keywords[0]))) 771 { 772 /* 773 * Create the option in the appropriate group... 774 */ 775 776 ui_keyword = 1; 777 778 DEBUG_printf(("2_ppdOpen: FOUND ADOBE UI KEYWORD %s WITHOUT OPENUI!", 779 keyword)); 780 781 if (!group) 782 { 783 if ((group = ppd_get_group(ppd, "General", _("General"), pg, 784 encoding)) == NULL) 785 goto error; 786 787 DEBUG_printf(("2_ppdOpen: Adding to group %s...", group->text)); 788 option = ppd_get_option(group, keyword); 789 group = NULL; 790 } 791 else 792 option = ppd_get_option(group, keyword); 793 794 if (option == NULL) 795 { 796 pg->ppd_status = PPD_ALLOC_ERROR; 797 798 goto error; 799 } 800 801 /* 802 * Now fill in the initial information for the option... 803 */ 804 805 if (!strncmp(keyword, "JCL", 3)) 806 option->section = PPD_ORDER_JCL; 807 else 808 option->section = PPD_ORDER_ANY; 809 810 option->order = 10.0f; 811 812 if (i < 8) 813 option->ui = PPD_UI_BOOLEAN; 814 else 815 option->ui = PPD_UI_PICKONE; 816 817 for (j = 0; j < ppd->num_attrs; j ++) 818 if (!strncmp(ppd->attrs[j]->name, "Default", 7) && 819 !strcmp(ppd->attrs[j]->name + 7, keyword) && 820 ppd->attrs[j]->value) 821 { 822 DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...", 823 option->keyword, ppd->attrs[j]->value)); 824 strlcpy(option->defchoice, ppd->attrs[j]->value, 825 sizeof(option->defchoice)); 826 break; 827 } 828 829 if (!strcmp(keyword, "PageSize")) 830 strlcpy(option->text, _("Media Size"), sizeof(option->text)); 831 else if (!strcmp(keyword, "MediaType")) 832 strlcpy(option->text, _("Media Type"), sizeof(option->text)); 833 else if (!strcmp(keyword, "InputSlot")) 834 strlcpy(option->text, _("Media Source"), sizeof(option->text)); 835 else if (!strcmp(keyword, "ColorModel")) 836 strlcpy(option->text, _("Output Mode"), sizeof(option->text)); 837 else if (!strcmp(keyword, "Resolution")) 838 strlcpy(option->text, _("Resolution"), sizeof(option->text)); 839 else 840 strlcpy(option->text, keyword, sizeof(option->text)); 841 } 842 } 843 844 if (!strcmp(keyword, "LanguageLevel")) 845 ppd->language_level = atoi(string); 846 else if (!strcmp(keyword, "LanguageEncoding")) 847 { 848 /* 849 * Say all PPD files are UTF-8, since we convert to UTF-8... 850 */ 851 852 ppd->lang_encoding = _cupsStrAlloc("UTF-8"); 853 encoding = _ppdGetEncoding(string); 854 } 855 else if (!strcmp(keyword, "LanguageVersion")) 856 ppd->lang_version = string; 857 else if (!strcmp(keyword, "Manufacturer")) 858 ppd->manufacturer = string; 859 else if (!strcmp(keyword, "ModelName")) 860 ppd->modelname = string; 861 else if (!strcmp(keyword, "Protocols")) 862 ppd->protocols = string; 863 else if (!strcmp(keyword, "PCFileName")) 864 ppd->pcfilename = string; 865 else if (!strcmp(keyword, "NickName")) 866 { 867 if (encoding != CUPS_UTF8) 868 { 869 cups_utf8_t utf8[256]; /* UTF-8 version of NickName */ 870 871 872 cupsCharsetToUTF8(utf8, string, sizeof(utf8), encoding); 873 ppd->nickname = _cupsStrAlloc((char *)utf8); 874 } 875 else 876 ppd->nickname = _cupsStrAlloc(string); 877 } 878 else if (!strcmp(keyword, "Product")) 879 ppd->product = string; 880 else if (!strcmp(keyword, "ShortNickName")) 881 ppd->shortnickname = string; 882 else if (!strcmp(keyword, "TTRasterizer")) 883 ppd->ttrasterizer = string; 884 else if (!strcmp(keyword, "JCLBegin")) 885 { 886 ppd->jcl_begin = _cupsStrAlloc(string); 887 ppd_decode(ppd->jcl_begin); /* Decode quoted string */ 888 } 889 else if (!strcmp(keyword, "JCLEnd")) 890 { 891 ppd->jcl_end = _cupsStrAlloc(string); 892 ppd_decode(ppd->jcl_end); /* Decode quoted string */ 893 } 894 else if (!strcmp(keyword, "JCLToPSInterpreter")) 895 { 896 ppd->jcl_ps = _cupsStrAlloc(string); 897 ppd_decode(ppd->jcl_ps); /* Decode quoted string */ 898 } 899 else if (!strcmp(keyword, "AccurateScreensSupport")) 900 ppd->accurate_screens = !strcmp(string, "True"); 901 else if (!strcmp(keyword, "ColorDevice")) 902 ppd->color_device = !strcmp(string, "True"); 903 else if (!strcmp(keyword, "ContoneOnly")) 904 ppd->contone_only = !strcmp(string, "True"); 905 else if (!strcmp(keyword, "cupsFlipDuplex")) 906 ppd->flip_duplex = !strcmp(string, "True"); 907 else if (!strcmp(keyword, "cupsManualCopies")) 908 ppd->manual_copies = !strcmp(string, "True"); 909 else if (!strcmp(keyword, "cupsModelNumber")) 910 ppd->model_number = atoi(string); 911 else if (!strcmp(keyword, "cupsColorProfile")) 912 { 913 if (ppd->num_profiles == 0) 914 profile = malloc(sizeof(ppd_profile_t)); 915 else 916 profile = realloc(ppd->profiles, sizeof(ppd_profile_t) * (size_t)(ppd->num_profiles + 1)); 917 918 if (!profile) 919 { 920 pg->ppd_status = PPD_ALLOC_ERROR; 921 922 goto error; 923 } 924 925 ppd->profiles = profile; 926 profile += ppd->num_profiles; 927 ppd->num_profiles ++; 928 929 memset(profile, 0, sizeof(ppd_profile_t)); 930 strlcpy(profile->resolution, name, sizeof(profile->resolution)); 931 strlcpy(profile->media_type, text, sizeof(profile->media_type)); 932 933 profile->density = (float)_cupsStrScand(string, &sptr, loc); 934 profile->gamma = (float)_cupsStrScand(sptr, &sptr, loc); 935 profile->matrix[0][0] = (float)_cupsStrScand(sptr, &sptr, loc); 936 profile->matrix[0][1] = (float)_cupsStrScand(sptr, &sptr, loc); 937 profile->matrix[0][2] = (float)_cupsStrScand(sptr, &sptr, loc); 938 profile->matrix[1][0] = (float)_cupsStrScand(sptr, &sptr, loc); 939 profile->matrix[1][1] = (float)_cupsStrScand(sptr, &sptr, loc); 940 profile->matrix[1][2] = (float)_cupsStrScand(sptr, &sptr, loc); 941 profile->matrix[2][0] = (float)_cupsStrScand(sptr, &sptr, loc); 942 profile->matrix[2][1] = (float)_cupsStrScand(sptr, &sptr, loc); 943 profile->matrix[2][2] = (float)_cupsStrScand(sptr, &sptr, loc); 944 } 945 else if (!strcmp(keyword, "cupsFilter")) 946 { 947 if (ppd->num_filters == 0) 948 filter = malloc(sizeof(char *)); 949 else 950 filter = realloc(ppd->filters, sizeof(char *) * (size_t)(ppd->num_filters + 1)); 951 952 if (filter == NULL) 953 { 954 pg->ppd_status = PPD_ALLOC_ERROR; 955 956 goto error; 957 } 958 959 ppd->filters = filter; 960 filter += ppd->num_filters; 961 ppd->num_filters ++; 962 963 /* 964 * Retain a copy of the filter string... 965 */ 966 967 *filter = _cupsStrRetain(string); 968 } 969 else if (!strcmp(keyword, "Throughput")) 970 ppd->throughput = atoi(string); 971 else if (!strcmp(keyword, "Font")) 972 { 973 /* 974 * Add this font to the list of available fonts... 975 */ 976 977 if (ppd->num_fonts == 0) 978 tempfonts = (char **)malloc(sizeof(char *)); 979 else 980 tempfonts = (char **)realloc(ppd->fonts, sizeof(char *) * (size_t)(ppd->num_fonts + 1)); 981 982 if (tempfonts == NULL) 983 { 984 pg->ppd_status = PPD_ALLOC_ERROR; 985 986 goto error; 987 } 988 989 ppd->fonts = tempfonts; 990 ppd->fonts[ppd->num_fonts] = _cupsStrAlloc(name); 991 ppd->num_fonts ++; 992 } 993 else if (!strncmp(keyword, "ParamCustom", 11)) 994 { 995 ppd_coption_t *coption; /* Custom option */ 996 ppd_cparam_t *cparam; /* Custom parameter */ 997 int corder; /* Order number */ 998 char ctype[33], /* Data type */ 999 cminimum[65], /* Minimum value */ 1000 cmaximum[65]; /* Maximum value */ 1001 1002 1003 /* 1004 * Get the custom option and parameter... 1005 */ 1006 1007 if ((coption = ppd_get_coption(ppd, keyword + 11)) == NULL) 1008 { 1009 pg->ppd_status = PPD_ALLOC_ERROR; 1010 1011 goto error; 1012 } 1013 1014 if ((cparam = ppd_get_cparam(coption, name, text)) == NULL) 1015 { 1016 pg->ppd_status = PPD_ALLOC_ERROR; 1017 1018 goto error; 1019 } 1020 1021 /* 1022 * Get the parameter data... 1023 */ 1024 1025 if (!string || 1026 sscanf(string, "%d%32s%64s%64s", &corder, ctype, cminimum, 1027 cmaximum) != 4) 1028 { 1029 pg->ppd_status = PPD_BAD_CUSTOM_PARAM; 1030 1031 goto error; 1032 } 1033 1034 cparam->order = corder; 1035 1036 if (!strcmp(ctype, "curve")) 1037 { 1038 cparam->type = PPD_CUSTOM_CURVE; 1039 cparam->minimum.custom_curve = (float)_cupsStrScand(cminimum, NULL, loc); 1040 cparam->maximum.custom_curve = (float)_cupsStrScand(cmaximum, NULL, loc); 1041 } 1042 else if (!strcmp(ctype, "int")) 1043 { 1044 cparam->type = PPD_CUSTOM_INT; 1045 cparam->minimum.custom_int = atoi(cminimum); 1046 cparam->maximum.custom_int = atoi(cmaximum); 1047 } 1048 else if (!strcmp(ctype, "invcurve")) 1049 { 1050 cparam->type = PPD_CUSTOM_INVCURVE; 1051 cparam->minimum.custom_invcurve = (float)_cupsStrScand(cminimum, NULL, loc); 1052 cparam->maximum.custom_invcurve = (float)_cupsStrScand(cmaximum, NULL, loc); 1053 } 1054 else if (!strcmp(ctype, "passcode")) 1055 { 1056 cparam->type = PPD_CUSTOM_PASSCODE; 1057 cparam->minimum.custom_passcode = atoi(cminimum); 1058 cparam->maximum.custom_passcode = atoi(cmaximum); 1059 } 1060 else if (!strcmp(ctype, "password")) 1061 { 1062 cparam->type = PPD_CUSTOM_PASSWORD; 1063 cparam->minimum.custom_password = atoi(cminimum); 1064 cparam->maximum.custom_password = atoi(cmaximum); 1065 } 1066 else if (!strcmp(ctype, "points")) 1067 { 1068 cparam->type = PPD_CUSTOM_POINTS; 1069 cparam->minimum.custom_points = (float)_cupsStrScand(cminimum, NULL, loc); 1070 cparam->maximum.custom_points = (float)_cupsStrScand(cmaximum, NULL, loc); 1071 } 1072 else if (!strcmp(ctype, "real")) 1073 { 1074 cparam->type = PPD_CUSTOM_REAL; 1075 cparam->minimum.custom_real = (float)_cupsStrScand(cminimum, NULL, loc); 1076 cparam->maximum.custom_real = (float)_cupsStrScand(cmaximum, NULL, loc); 1077 } 1078 else if (!strcmp(ctype, "string")) 1079 { 1080 cparam->type = PPD_CUSTOM_STRING; 1081 cparam->minimum.custom_string = atoi(cminimum); 1082 cparam->maximum.custom_string = atoi(cmaximum); 1083 } 1084 else 1085 { 1086 pg->ppd_status = PPD_BAD_CUSTOM_PARAM; 1087 1088 goto error; 1089 } 1090 1091 /* 1092 * Now special-case for CustomPageSize... 1093 */ 1094 1095 if (!strcmp(coption->keyword, "PageSize")) 1096 { 1097 if (!strcmp(name, "Width")) 1098 { 1099 ppd->custom_min[0] = cparam->minimum.custom_points; 1100 ppd->custom_max[0] = cparam->maximum.custom_points; 1101 } 1102 else if (!strcmp(name, "Height")) 1103 { 1104 ppd->custom_min[1] = cparam->minimum.custom_points; 1105 ppd->custom_max[1] = cparam->maximum.custom_points; 1106 } 1107 } 1108 } 1109 else if (!strcmp(keyword, "HWMargins")) 1110 { 1111 for (i = 0, sptr = string; i < 4; i ++) 1112 ppd->custom_margins[i] = (float)_cupsStrScand(sptr, &sptr, loc); 1113 } 1114 else if (!strncmp(keyword, "Custom", 6) && !strcmp(name, "True") && !option) 1115 { 1116 ppd_option_t *custom_option; /* Custom option */ 1117 1118 DEBUG_puts("2_ppdOpen: Processing Custom option..."); 1119 1120 /* 1121 * Get the option and custom option... 1122 */ 1123 1124 if (!ppd_get_coption(ppd, keyword + 6)) 1125 { 1126 pg->ppd_status = PPD_ALLOC_ERROR; 1127 1128 goto error; 1129 } 1130 1131 if (option && !_cups_strcasecmp(option->keyword, keyword + 6)) 1132 custom_option = option; 1133 else 1134 custom_option = ppdFindOption(ppd, keyword + 6); 1135 1136 if (custom_option) 1137 { 1138 /* 1139 * Add the "custom" option... 1140 */ 1141 1142 if ((choice = ppdFindChoice(custom_option, "Custom")) == NULL) 1143 if ((choice = ppd_add_choice(custom_option, "Custom")) == NULL) 1144 { 1145 DEBUG_puts("1_ppdOpen: Unable to add Custom choice!"); 1146 1147 pg->ppd_status = PPD_ALLOC_ERROR; 1148 1149 goto error; 1150 } 1151 1152 strlcpy(choice->text, text[0] ? text : _("Custom"), 1153 sizeof(choice->text)); 1154 1155 choice->code = _cupsStrAlloc(string); 1156 1157 if (custom_option->section == PPD_ORDER_JCL) 1158 ppd_decode(choice->code); 1159 } 1160 1161 /* 1162 * Now process custom page sizes specially... 1163 */ 1164 1165 if (!strcmp(keyword, "CustomPageSize")) 1166 { 1167 /* 1168 * Add a "Custom" page size entry... 1169 */ 1170 1171 ppd->variable_sizes = 1; 1172 1173 ppd_add_size(ppd, "Custom"); 1174 1175 if (option && !_cups_strcasecmp(option->keyword, "PageRegion")) 1176 custom_option = option; 1177 else 1178 custom_option = ppdFindOption(ppd, "PageRegion"); 1179 1180 if (custom_option) 1181 { 1182 if ((choice = ppdFindChoice(custom_option, "Custom")) == NULL) 1183 if ((choice = ppd_add_choice(custom_option, "Custom")) == NULL) 1184 { 1185 DEBUG_puts("1_ppdOpen: Unable to add Custom choice!"); 1186 1187 pg->ppd_status = PPD_ALLOC_ERROR; 1188 1189 goto error; 1190 } 1191 1192 strlcpy(choice->text, text[0] ? text : _("Custom"), 1193 sizeof(choice->text)); 1194 } 1195 } 1196 } 1197 else if (!strcmp(keyword, "LandscapeOrientation")) 1198 { 1199 if (!strcmp(string, "Minus90")) 1200 ppd->landscape = -90; 1201 else if (!strcmp(string, "Plus90")) 1202 ppd->landscape = 90; 1203 } 1204 else if (!strcmp(keyword, "Emulators") && string) 1205 { 1206 for (count = 1, sptr = string; sptr != NULL;) 1207 if ((sptr = strchr(sptr, ' ')) != NULL) 1208 { 1209 count ++; 1210 while (*sptr == ' ') 1211 sptr ++; 1212 } 1213 1214 ppd->num_emulations = count; 1215 if ((ppd->emulations = calloc((size_t)count, sizeof(ppd_emul_t))) == NULL) 1216 { 1217 pg->ppd_status = PPD_ALLOC_ERROR; 1218 1219 goto error; 1220 } 1221 1222 for (i = 0, sptr = string; i < count; i ++) 1223 { 1224 for (nameptr = ppd->emulations[i].name; 1225 *sptr != '\0' && *sptr != ' '; 1226 sptr ++) 1227 if (nameptr < (ppd->emulations[i].name + sizeof(ppd->emulations[i].name) - 1)) 1228 *nameptr++ = *sptr; 1229 1230 *nameptr = '\0'; 1231 1232 while (*sptr == ' ') 1233 sptr ++; 1234 } 1235 } 1236 else if (!strncmp(keyword, "StartEmulator_", 14)) 1237 { 1238 ppd_decode(string); 1239 1240 for (i = 0; i < ppd->num_emulations; i ++) 1241 if (!strcmp(keyword + 14, ppd->emulations[i].name)) 1242 { 1243 ppd->emulations[i].start = string; 1244 string = NULL; 1245 } 1246 } 1247 else if (!strncmp(keyword, "StopEmulator_", 13)) 1248 { 1249 ppd_decode(string); 1250 1251 for (i = 0; i < ppd->num_emulations; i ++) 1252 if (!strcmp(keyword + 13, ppd->emulations[i].name)) 1253 { 1254 ppd->emulations[i].stop = string; 1255 string = NULL; 1256 } 1257 } 1258 else if (!strcmp(keyword, "JobPatchFile")) 1259 { 1260 /* 1261 * CUPS STR #3421: Check for "*JobPatchFile: int: string" 1262 */ 1263 1264 if (isdigit(*string & 255)) 1265 { 1266 for (sptr = string + 1; isdigit(*sptr & 255); sptr ++); 1267 1268 if (*sptr == ':') 1269 { 1270 /* 1271 * Found "*JobPatchFile: int: string"... 1272 */ 1273 1274 pg->ppd_status = PPD_BAD_VALUE; 1275 1276 goto error; 1277 } 1278 } 1279 1280 if (!name[0] && pg->ppd_conform == PPD_CONFORM_STRICT) 1281 { 1282 /* 1283 * Found "*JobPatchFile: string"... 1284 */ 1285 1286 pg->ppd_status = PPD_MISSING_OPTION_KEYWORD; 1287 1288 goto error; 1289 } 1290 1291 if (ppd->patches == NULL) 1292 ppd->patches = strdup(string); 1293 else 1294 { 1295 temp = realloc(ppd->patches, strlen(ppd->patches) + 1296 strlen(string) + 1); 1297 if (temp == NULL) 1298 { 1299 pg->ppd_status = PPD_ALLOC_ERROR; 1300 1301 goto error; 1302 } 1303 1304 ppd->patches = temp; 1305 1306 memcpy(ppd->patches + strlen(ppd->patches), string, strlen(string) + 1); 1307 } 1308 } 1309 else if (!strcmp(keyword, "OpenUI")) 1310 { 1311 /* 1312 * Don't allow nesting of options... 1313 */ 1314 1315 if (option && pg->ppd_conform == PPD_CONFORM_STRICT) 1316 { 1317 pg->ppd_status = PPD_NESTED_OPEN_UI; 1318 1319 goto error; 1320 } 1321 1322 /* 1323 * Add an option record to the current sub-group, group, or file... 1324 */ 1325 1326 DEBUG_printf(("2_ppdOpen: name=\"%s\" (%d)", name, (int)strlen(name))); 1327 1328 if (name[0] == '*') 1329 _cups_strcpy(name, name + 1); /* Eliminate leading asterisk */ 1330 1331 for (i = (int)strlen(name) - 1; i > 0 && _cups_isspace(name[i]); i --) 1332 name[i] = '\0'; /* Eliminate trailing spaces */ 1333 1334 DEBUG_printf(("2_ppdOpen: OpenUI of %s in group %s...", name, 1335 group ? group->text : "(null)")); 1336 1337 if (subgroup != NULL) 1338 option = ppd_get_option(subgroup, name); 1339 else if (group == NULL) 1340 { 1341 if ((group = ppd_get_group(ppd, "General", _("General"), pg, 1342 encoding)) == NULL) 1343 goto error; 1344 1345 DEBUG_printf(("2_ppdOpen: Adding to group %s...", group->text)); 1346 option = ppd_get_option(group, name); 1347 group = NULL; 1348 } 1349 else 1350 option = ppd_get_option(group, name); 1351 1352 if (option == NULL) 1353 { 1354 pg->ppd_status = PPD_ALLOC_ERROR; 1355 1356 goto error; 1357 } 1358 1359 /* 1360 * Now fill in the initial information for the option... 1361 */ 1362 1363 if (string && !strcmp(string, "PickMany")) 1364 option->ui = PPD_UI_PICKMANY; 1365 else if (string && !strcmp(string, "Boolean")) 1366 option->ui = PPD_UI_BOOLEAN; 1367 else if (string && !strcmp(string, "PickOne")) 1368 option->ui = PPD_UI_PICKONE; 1369 else if (pg->ppd_conform == PPD_CONFORM_STRICT) 1370 { 1371 pg->ppd_status = PPD_BAD_OPEN_UI; 1372 1373 goto error; 1374 } 1375 else 1376 option->ui = PPD_UI_PICKONE; 1377 1378 for (j = 0; j < ppd->num_attrs; j ++) 1379 if (!strncmp(ppd->attrs[j]->name, "Default", 7) && 1380 !strcmp(ppd->attrs[j]->name + 7, name) && 1381 ppd->attrs[j]->value) 1382 { 1383 DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...", 1384 option->keyword, ppd->attrs[j]->value)); 1385 strlcpy(option->defchoice, ppd->attrs[j]->value, 1386 sizeof(option->defchoice)); 1387 break; 1388 } 1389 1390 if (text[0]) 1391 cupsCharsetToUTF8((cups_utf8_t *)option->text, text, 1392 sizeof(option->text), encoding); 1393 else 1394 { 1395 if (!strcmp(name, "PageSize")) 1396 strlcpy(option->text, _("Media Size"), sizeof(option->text)); 1397 else if (!strcmp(name, "MediaType")) 1398 strlcpy(option->text, _("Media Type"), sizeof(option->text)); 1399 else if (!strcmp(name, "InputSlot")) 1400 strlcpy(option->text, _("Media Source"), sizeof(option->text)); 1401 else if (!strcmp(name, "ColorModel")) 1402 strlcpy(option->text, _("Output Mode"), sizeof(option->text)); 1403 else if (!strcmp(name, "Resolution")) 1404 strlcpy(option->text, _("Resolution"), sizeof(option->text)); 1405 else 1406 strlcpy(option->text, name, sizeof(option->text)); 1407 } 1408 1409 option->section = PPD_ORDER_ANY; 1410 1411 _cupsStrFree(string); 1412 string = NULL; 1413 1414 /* 1415 * Add a custom option choice if we have already seen a CustomFoo 1416 * attribute... 1417 */ 1418 1419 if (!_cups_strcasecmp(name, "PageRegion")) 1420 strlcpy(custom_name, "CustomPageSize", sizeof(custom_name)); 1421 else 1422 snprintf(custom_name, sizeof(custom_name), "Custom%s", name); 1423 1424 if ((custom_attr = ppdFindAttr(ppd, custom_name, "True")) != NULL) 1425 { 1426 if ((choice = ppdFindChoice(option, "Custom")) == NULL) 1427 if ((choice = ppd_add_choice(option, "Custom")) == NULL) 1428 { 1429 DEBUG_puts("1_ppdOpen: Unable to add Custom choice!"); 1430 1431 pg->ppd_status = PPD_ALLOC_ERROR; 1432 1433 goto error; 1434 } 1435 1436 strlcpy(choice->text, 1437 custom_attr->text[0] ? custom_attr->text : _("Custom"), 1438 sizeof(choice->text)); 1439 choice->code = _cupsStrRetain(custom_attr->value); 1440 } 1441 } 1442 else if (!strcmp(keyword, "JCLOpenUI")) 1443 { 1444 /* 1445 * Don't allow nesting of options... 1446 */ 1447 1448 if (option && pg->ppd_conform == PPD_CONFORM_STRICT) 1449 { 1450 pg->ppd_status = PPD_NESTED_OPEN_UI; 1451 1452 goto error; 1453 } 1454 1455 /* 1456 * Find the JCL group, and add if needed... 1457 */ 1458 1459 group = ppd_get_group(ppd, "JCL", _("JCL"), pg, encoding); 1460 1461 if (group == NULL) 1462 goto error; 1463 1464 /* 1465 * Add an option record to the current JCLs... 1466 */ 1467 1468 if (name[0] == '*') 1469 _cups_strcpy(name, name + 1); 1470 1471 option = ppd_get_option(group, name); 1472 1473 if (option == NULL) 1474 { 1475 pg->ppd_status = PPD_ALLOC_ERROR; 1476 1477 goto error; 1478 } 1479 1480 /* 1481 * Now fill in the initial information for the option... 1482 */ 1483 1484 if (string && !strcmp(string, "PickMany")) 1485 option->ui = PPD_UI_PICKMANY; 1486 else if (string && !strcmp(string, "Boolean")) 1487 option->ui = PPD_UI_BOOLEAN; 1488 else if (string && !strcmp(string, "PickOne")) 1489 option->ui = PPD_UI_PICKONE; 1490 else 1491 { 1492 pg->ppd_status = PPD_BAD_OPEN_UI; 1493 1494 goto error; 1495 } 1496 1497 for (j = 0; j < ppd->num_attrs; j ++) 1498 if (!strncmp(ppd->attrs[j]->name, "Default", 7) && 1499 !strcmp(ppd->attrs[j]->name + 7, name) && 1500 ppd->attrs[j]->value) 1501 { 1502 DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...", 1503 option->keyword, ppd->attrs[j]->value)); 1504 strlcpy(option->defchoice, ppd->attrs[j]->value, 1505 sizeof(option->defchoice)); 1506 break; 1507 } 1508 1509 if (text[0]) 1510 cupsCharsetToUTF8((cups_utf8_t *)option->text, text, 1511 sizeof(option->text), encoding); 1512 else 1513 strlcpy(option->text, name, sizeof(option->text)); 1514 1515 option->section = PPD_ORDER_JCL; 1516 group = NULL; 1517 1518 _cupsStrFree(string); 1519 string = NULL; 1520 1521 /* 1522 * Add a custom option choice if we have already seen a CustomFoo 1523 * attribute... 1524 */ 1525 1526 snprintf(custom_name, sizeof(custom_name), "Custom%s", name); 1527 1528 if ((custom_attr = ppdFindAttr(ppd, custom_name, "True")) != NULL) 1529 { 1530 if ((choice = ppd_add_choice(option, "Custom")) == NULL) 1531 { 1532 DEBUG_puts("1_ppdOpen: Unable to add Custom choice!"); 1533 1534 pg->ppd_status = PPD_ALLOC_ERROR; 1535 1536 goto error; 1537 } 1538 1539 strlcpy(choice->text, 1540 custom_attr->text[0] ? custom_attr->text : _("Custom"), 1541 sizeof(choice->text)); 1542 choice->code = _cupsStrRetain(custom_attr->value); 1543 } 1544 } 1545 else if (!strcmp(keyword, "CloseUI") || !strcmp(keyword, "JCLCloseUI")) 1546 { 1547 option = NULL; 1548 1549 _cupsStrFree(string); 1550 string = NULL; 1551 } 1552 else if (!strcmp(keyword, "OpenGroup")) 1553 { 1554 /* 1555 * Open a new group... 1556 */ 1557 1558 if (group != NULL) 1559 { 1560 pg->ppd_status = PPD_NESTED_OPEN_GROUP; 1561 1562 goto error; 1563 } 1564 1565 if (!string) 1566 { 1567 pg->ppd_status = PPD_BAD_OPEN_GROUP; 1568 1569 goto error; 1570 } 1571 1572 /* 1573 * Separate the group name from the text (name/text)... 1574 */ 1575 1576 if ((sptr = strchr(string, '/')) != NULL) 1577 *sptr++ = '\0'; 1578 else 1579 sptr = string; 1580 1581 /* 1582 * Fix up the text... 1583 */ 1584 1585 ppd_decode(sptr); 1586 1587 /* 1588 * Find/add the group... 1589 */ 1590 1591 group = ppd_get_group(ppd, string, sptr, pg, encoding); 1592 1593 if (group == NULL) 1594 goto error; 1595 1596 _cupsStrFree(string); 1597 string = NULL; 1598 } 1599 else if (!strcmp(keyword, "CloseGroup")) 1600 { 1601 group = NULL; 1602 1603 _cupsStrFree(string); 1604 string = NULL; 1605 } 1606 else if (!strcmp(keyword, "OrderDependency")) 1607 { 1608 order = (float)_cupsStrScand(string, &sptr, loc); 1609 1610 if (!sptr || sscanf(sptr, "%40s%40s", name, keyword) != 2) 1611 { 1612 pg->ppd_status = PPD_BAD_ORDER_DEPENDENCY; 1613 1614 goto error; 1615 } 1616 1617 if (keyword[0] == '*') 1618 _cups_strcpy(keyword, keyword + 1); 1619 1620 if (!strcmp(name, "ExitServer")) 1621 section = PPD_ORDER_EXIT; 1622 else if (!strcmp(name, "Prolog")) 1623 section = PPD_ORDER_PROLOG; 1624 else if (!strcmp(name, "DocumentSetup")) 1625 section = PPD_ORDER_DOCUMENT; 1626 else if (!strcmp(name, "PageSetup")) 1627 section = PPD_ORDER_PAGE; 1628 else if (!strcmp(name, "JCLSetup")) 1629 section = PPD_ORDER_JCL; 1630 else 1631 section = PPD_ORDER_ANY; 1632 1633 if (option == NULL) 1634 { 1635 ppd_group_t *gtemp; 1636 1637 1638 /* 1639 * Only valid for Non-UI options... 1640 */ 1641 1642 for (i = ppd->num_groups, gtemp = ppd->groups; i > 0; i --, gtemp ++) 1643 if (gtemp->text[0] == '\0') 1644 break; 1645 1646 if (i > 0) 1647 for (i = 0; i < gtemp->num_options; i ++) 1648 if (!strcmp(keyword, gtemp->options[i].keyword)) 1649 { 1650 gtemp->options[i].section = section; 1651 gtemp->options[i].order = order; 1652 break; 1653 } 1654 } 1655 else 1656 { 1657 option->section = section; 1658 option->order = order; 1659 } 1660 1661 _cupsStrFree(string); 1662 string = NULL; 1663 } 1664 else if (!strncmp(keyword, "Default", 7)) 1665 { 1666 if (string == NULL) 1667 continue; 1668 1669 /* 1670 * Drop UI text, if any, from value... 1671 */ 1672 1673 if (strchr(string, '/') != NULL) 1674 *strchr(string, '/') = '\0'; 1675 1676 /* 1677 * Assign the default value as appropriate... 1678 */ 1679 1680 if (!strcmp(keyword, "DefaultColorSpace")) 1681 { 1682 /* 1683 * Set default colorspace... 1684 */ 1685 1686 if (!strcmp(string, "CMY")) 1687 ppd->colorspace = PPD_CS_CMY; 1688 else if (!strcmp(string, "CMYK")) 1689 ppd->colorspace = PPD_CS_CMYK; 1690 else if (!strcmp(string, "RGB")) 1691 ppd->colorspace = PPD_CS_RGB; 1692 else if (!strcmp(string, "RGBK")) 1693 ppd->colorspace = PPD_CS_RGBK; 1694 else if (!strcmp(string, "N")) 1695 ppd->colorspace = PPD_CS_N; 1696 else 1697 ppd->colorspace = PPD_CS_GRAY; 1698 } 1699 else if (option && !strcmp(keyword + 7, option->keyword)) 1700 { 1701 /* 1702 * Set the default as part of the current option... 1703 */ 1704 1705 DEBUG_printf(("2_ppdOpen: Setting %s to %s...", keyword, string)); 1706 1707 strlcpy(option->defchoice, string, sizeof(option->defchoice)); 1708 1709 DEBUG_printf(("2_ppdOpen: %s is now %s...", keyword, option->defchoice)); 1710 } 1711 else 1712 { 1713 /* 1714 * Lookup option and set if it has been defined... 1715 */ 1716 1717 ppd_option_t *toption; /* Temporary option */ 1718 1719 1720 if ((toption = ppdFindOption(ppd, keyword + 7)) != NULL) 1721 { 1722 DEBUG_printf(("2_ppdOpen: Setting %s to %s...", keyword, string)); 1723 strlcpy(toption->defchoice, string, sizeof(toption->defchoice)); 1724 } 1725 } 1726 } 1727 else if (!strcmp(keyword, "UIConstraints") || 1728 !strcmp(keyword, "NonUIConstraints")) 1729 { 1730 if (!string) 1731 { 1732 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS; 1733 goto error; 1734 } 1735 1736 if (ppd->num_consts == 0) 1737 constraint = calloc(2, sizeof(ppd_const_t)); 1738 else 1739 constraint = realloc(ppd->consts, (size_t)(ppd->num_consts + 2) * sizeof(ppd_const_t)); 1740 1741 if (constraint == NULL) 1742 { 1743 pg->ppd_status = PPD_ALLOC_ERROR; 1744 1745 goto error; 1746 } 1747 1748 ppd->consts = constraint; 1749 constraint += ppd->num_consts; 1750 ppd->num_consts ++; 1751 1752 switch (sscanf(string, "%40s%40s%40s%40s", constraint->option1, 1753 constraint->choice1, constraint->option2, 1754 constraint->choice2)) 1755 { 1756 case 0 : /* Error */ 1757 case 1 : /* Error */ 1758 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS; 1759 goto error; 1760 1761 case 2 : /* Two options... */ 1762 /* 1763 * Check for broken constraints like "* Option"... 1764 */ 1765 1766 if (pg->ppd_conform == PPD_CONFORM_STRICT && 1767 (!strcmp(constraint->option1, "*") || 1768 !strcmp(constraint->choice1, "*"))) 1769 { 1770 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS; 1771 goto error; 1772 } 1773 1774 /* 1775 * The following strcpy's are safe, as optionN and 1776 * choiceN are all the same size (size defined by PPD spec...) 1777 */ 1778 1779 if (constraint->option1[0] == '*') 1780 _cups_strcpy(constraint->option1, constraint->option1 + 1); 1781 else if (pg->ppd_conform == PPD_CONFORM_STRICT) 1782 { 1783 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS; 1784 goto error; 1785 } 1786 1787 if (constraint->choice1[0] == '*') 1788 _cups_strcpy(constraint->option2, constraint->choice1 + 1); 1789 else if (pg->ppd_conform == PPD_CONFORM_STRICT) 1790 { 1791 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS; 1792 goto error; 1793 } 1794 1795 constraint->choice1[0] = '\0'; 1796 constraint->choice2[0] = '\0'; 1797 break; 1798 1799 case 3 : /* Two options, one choice... */ 1800 /* 1801 * Check for broken constraints like "* Option"... 1802 */ 1803 1804 if (pg->ppd_conform == PPD_CONFORM_STRICT && 1805 (!strcmp(constraint->option1, "*") || 1806 !strcmp(constraint->choice1, "*") || 1807 !strcmp(constraint->option2, "*"))) 1808 { 1809 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS; 1810 goto error; 1811 } 1812 1813 /* 1814 * The following _cups_strcpy's are safe, as optionN and 1815 * choiceN are all the same size (size defined by PPD spec...) 1816 */ 1817 1818 if (constraint->option1[0] == '*') 1819 _cups_strcpy(constraint->option1, constraint->option1 + 1); 1820 else if (pg->ppd_conform == PPD_CONFORM_STRICT) 1821 { 1822 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS; 1823 goto error; 1824 } 1825 1826 if (constraint->choice1[0] == '*') 1827 { 1828 if (pg->ppd_conform == PPD_CONFORM_STRICT && 1829 constraint->option2[0] == '*') 1830 { 1831 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS; 1832 goto error; 1833 } 1834 1835 _cups_strcpy(constraint->choice2, constraint->option2); 1836 _cups_strcpy(constraint->option2, constraint->choice1 + 1); 1837 constraint->choice1[0] = '\0'; 1838 } 1839 else 1840 { 1841 if (constraint->option2[0] == '*') 1842 _cups_strcpy(constraint->option2, constraint->option2 + 1); 1843 else if (pg->ppd_conform == PPD_CONFORM_STRICT) 1844 { 1845 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS; 1846 goto error; 1847 } 1848 1849 constraint->choice2[0] = '\0'; 1850 } 1851 break; 1852 1853 case 4 : /* Two options, two choices... */ 1854 /* 1855 * Check for broken constraints like "* Option"... 1856 */ 1857 1858 if (pg->ppd_conform == PPD_CONFORM_STRICT && 1859 (!strcmp(constraint->option1, "*") || 1860 !strcmp(constraint->choice1, "*") || 1861 !strcmp(constraint->option2, "*") || 1862 !strcmp(constraint->choice2, "*"))) 1863 { 1864 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS; 1865 goto error; 1866 } 1867 1868 if (constraint->option1[0] == '*') 1869 _cups_strcpy(constraint->option1, constraint->option1 + 1); 1870 else if (pg->ppd_conform == PPD_CONFORM_STRICT) 1871 { 1872 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS; 1873 goto error; 1874 } 1875 1876 if (pg->ppd_conform == PPD_CONFORM_STRICT && 1877 constraint->choice1[0] == '*') 1878 { 1879 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS; 1880 goto error; 1881 } 1882 1883 if (constraint->option2[0] == '*') 1884 _cups_strcpy(constraint->option2, constraint->option2 + 1); 1885 else if (pg->ppd_conform == PPD_CONFORM_STRICT) 1886 { 1887 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS; 1888 goto error; 1889 } 1890 1891 if (pg->ppd_conform == PPD_CONFORM_STRICT && 1892 constraint->choice2[0] == '*') 1893 { 1894 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS; 1895 goto error; 1896 } 1897 break; 1898 } 1899 1900 /* 1901 * Don't add this one as an attribute... 1902 */ 1903 1904 _cupsStrFree(string); 1905 string = NULL; 1906 } 1907 else if (!strcmp(keyword, "PaperDimension")) 1908 { 1909 if ((size = ppdPageSize(ppd, name)) == NULL) 1910 size = ppd_add_size(ppd, name); 1911 1912 if (size == NULL) 1913 { 1914 /* 1915 * Unable to add or find size! 1916 */ 1917 1918 pg->ppd_status = PPD_ALLOC_ERROR; 1919 1920 goto error; 1921 } 1922 1923 size->width = (float)_cupsStrScand(string, &sptr, loc); 1924 size->length = (float)_cupsStrScand(sptr, NULL, loc); 1925 1926 _cupsStrFree(string); 1927 string = NULL; 1928 } 1929 else if (!strcmp(keyword, "ImageableArea")) 1930 { 1931 if ((size = ppdPageSize(ppd, name)) == NULL) 1932 size = ppd_add_size(ppd, name); 1933 1934 if (size == NULL) 1935 { 1936 /* 1937 * Unable to add or find size! 1938 */ 1939 1940 pg->ppd_status = PPD_ALLOC_ERROR; 1941 1942 goto error; 1943 } 1944 1945 size->left = (float)_cupsStrScand(string, &sptr, loc); 1946 size->bottom = (float)_cupsStrScand(sptr, &sptr, loc); 1947 size->right = (float)_cupsStrScand(sptr, &sptr, loc); 1948 size->top = (float)_cupsStrScand(sptr, NULL, loc); 1949 1950 _cupsStrFree(string); 1951 string = NULL; 1952 } 1953 else if (option != NULL && 1954 (mask & (PPD_KEYWORD | PPD_OPTION | PPD_STRING)) == 1955 (PPD_KEYWORD | PPD_OPTION | PPD_STRING) && 1956 !strcmp(keyword, option->keyword)) 1957 { 1958 DEBUG_printf(("2_ppdOpen: group=%p, subgroup=%p", group, subgroup)); 1959 1960 if (!strcmp(keyword, "PageSize")) 1961 { 1962 /* 1963 * Add a page size... 1964 */ 1965 1966 if (ppdPageSize(ppd, name) == NULL) 1967 ppd_add_size(ppd, name); 1968 } 1969 1970 /* 1971 * Add the option choice... 1972 */ 1973 1974 if ((choice = ppd_add_choice(option, name)) == NULL) 1975 { 1976 pg->ppd_status = PPD_ALLOC_ERROR; 1977 1978 goto error; 1979 } 1980 1981 if (text[0]) 1982 cupsCharsetToUTF8((cups_utf8_t *)choice->text, text, 1983 sizeof(choice->text), encoding); 1984 else if (!strcmp(name, "True")) 1985 strlcpy(choice->text, _("Yes"), sizeof(choice->text)); 1986 else if (!strcmp(name, "False")) 1987 strlcpy(choice->text, _("No"), sizeof(choice->text)); 1988 else 1989 strlcpy(choice->text, name, sizeof(choice->text)); 1990 1991 if (option->section == PPD_ORDER_JCL) 1992 ppd_decode(string); /* Decode quoted string */ 1993 1994 choice->code = string; 1995 string = NULL; /* Don't add as an attribute below */ 1996 } 1997 1998 /* 1999 * Add remaining lines with keywords and string values as attributes... 2000 */ 2001 2002 if (string && 2003 (mask & (PPD_KEYWORD | PPD_STRING)) == (PPD_KEYWORD | PPD_STRING)) 2004 ppd_add_attr(ppd, keyword, name, text, string); 2005 else 2006 _cupsStrFree(string); 2007 } 2008 2009 /* 2010 * Check for a missing CloseGroup... 2011 */ 2012 2013 if (group && pg->ppd_conform == PPD_CONFORM_STRICT) 2014 { 2015 pg->ppd_status = PPD_MISSING_CLOSE_GROUP; 2016 goto error; 2017 } 2018 2019 ppd_free(line.buffer); 2020 2021 /* 2022 * Reset language preferences... 2023 */ 2024 2025 #ifdef DEBUG 2026 if (!cupsFileEOF(fp)) 2027 DEBUG_printf(("1_ppdOpen: Premature EOF at %lu...\n", 2028 (unsigned long)cupsFileTell(fp))); 2029 #endif /* DEBUG */ 2030 2031 if (pg->ppd_status != PPD_OK) 2032 { 2033 /* 2034 * Had an error reading the PPD file, cannot continue! 2035 */ 2036 2037 ppdClose(ppd); 2038 2039 return (NULL); 2040 } 2041 2042 /* 2043 * Update the filters array as needed... 2044 */ 2045 2046 if (!ppd_update_filters(ppd, pg)) 2047 { 2048 ppdClose(ppd); 2049 2050 return (NULL); 2051 } 2052 2053 /* 2054 * Create the sorted options array and set the option back-pointer for 2055 * each choice and custom option... 2056 */ 2057 2058 ppd->options = cupsArrayNew2((cups_array_func_t)ppd_compare_options, NULL, 2059 (cups_ahash_func_t)ppd_hash_option, 2060 PPD_HASHSIZE); 2061 2062 for (i = ppd->num_groups, group = ppd->groups; 2063 i > 0; 2064 i --, group ++) 2065 { 2066 for (j = group->num_options, option = group->options; 2067 j > 0; 2068 j --, option ++) 2069 { 2070 ppd_coption_t *coption; /* Custom option */ 2071 2072 2073 cupsArrayAdd(ppd->options, option); 2074 2075 for (k = 0; k < option->num_choices; k ++) 2076 option->choices[k].option = option; 2077 2078 if ((coption = ppdFindCustomOption(ppd, option->keyword)) != NULL) 2079 coption->option = option; 2080 } 2081 } 2082 2083 /* 2084 * Create an array to track the marked choices... 2085 */ 2086 2087 ppd->marked = cupsArrayNew((cups_array_func_t)ppd_compare_choices, NULL); 2088 2089 /* 2090 * Return the PPD file structure... 2091 */ 2092 2093 return (ppd); 2094 2095 /* 2096 * Common exit point for errors to save code size... 2097 */ 2098 2099 error: 2100 2101 _cupsStrFree(string); 2102 ppd_free(line.buffer); 2103 2104 ppdClose(ppd); 2105 2106 return (NULL); 2107 } 2108 2109 2110 /* 2111 * 'ppdOpen()' - Read a PPD file into memory. 2112 */ 2113 2114 ppd_file_t * /* O - PPD file record */ 2115 ppdOpen(FILE *fp) /* I - File to read from */ 2116 { 2117 ppd_file_t *ppd; /* PPD file record */ 2118 cups_file_t *cf; /* CUPS file */ 2119 2120 2121 /* 2122 * Reopen the stdio file as a CUPS file... 2123 */ 2124 2125 if ((cf = cupsFileOpenFd(fileno(fp), "r")) == NULL) 2126 return (NULL); 2127 2128 /* 2129 * Load the PPD file using the newer API... 2130 */ 2131 2132 ppd = _ppdOpen(cf, _PPD_LOCALIZATION_DEFAULT); 2133 2134 /* 2135 * Close the CUPS file and return the PPD... 2136 */ 2137 2138 cupsFileClose(cf); 2139 2140 return (ppd); 2141 } 2142 2143 2144 /* 2145 * 'ppdOpen2()' - Read a PPD file into memory. 2146 * 2147 * @since CUPS 1.2/macOS 10.5@ 2148 */ 2149 2150 ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */ 2151 ppdOpen2(cups_file_t *fp) /* I - File to read from */ 2152 { 2153 return _ppdOpen(fp, _PPD_LOCALIZATION_DEFAULT); 2154 } 2155 2156 2157 /* 2158 * 'ppdOpenFd()' - Read a PPD file into memory. 2159 */ 2160 2161 ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */ 2162 ppdOpenFd(int fd) /* I - File to read from */ 2163 { 2164 cups_file_t *fp; /* CUPS file pointer */ 2165 ppd_file_t *ppd; /* PPD file record */ 2166 _ppd_globals_t *pg = _ppdGlobals(); 2167 /* Global data */ 2168 2169 2170 /* 2171 * Set the line number to 0... 2172 */ 2173 2174 pg->ppd_line = 0; 2175 2176 /* 2177 * Range check input... 2178 */ 2179 2180 if (fd < 0) 2181 { 2182 pg->ppd_status = PPD_NULL_FILE; 2183 2184 return (NULL); 2185 } 2186 2187 /* 2188 * Try to open the file and parse it... 2189 */ 2190 2191 if ((fp = cupsFileOpenFd(fd, "r")) != NULL) 2192 { 2193 ppd = ppdOpen2(fp); 2194 2195 cupsFileClose(fp); 2196 } 2197 else 2198 { 2199 pg->ppd_status = PPD_FILE_OPEN_ERROR; 2200 ppd = NULL; 2201 } 2202 2203 return (ppd); 2204 } 2205 2206 2207 /* 2208 * '_ppdOpenFile()' - Read a PPD file into memory. 2209 */ 2210 2211 ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */ 2212 _ppdOpenFile(const char *filename, /* I - File to read from */ 2213 _ppd_localization_t localization) /* I - Localization to load */ 2214 { 2215 cups_file_t *fp; /* File pointer */ 2216 ppd_file_t *ppd; /* PPD file record */ 2217 _ppd_globals_t *pg = _ppdGlobals(); 2218 /* Global data */ 2219 2220 2221 /* 2222 * Set the line number to 0... 2223 */ 2224 2225 pg->ppd_line = 0; 2226 2227 /* 2228 * Range check input... 2229 */ 2230 2231 if (filename == NULL) 2232 { 2233 pg->ppd_status = PPD_NULL_FILE; 2234 2235 return (NULL); 2236 } 2237 2238 /* 2239 * Try to open the file and parse it... 2240 */ 2241 2242 if ((fp = cupsFileOpen(filename, "r")) != NULL) 2243 { 2244 ppd = _ppdOpen(fp, localization); 2245 2246 cupsFileClose(fp); 2247 } 2248 else 2249 { 2250 pg->ppd_status = PPD_FILE_OPEN_ERROR; 2251 ppd = NULL; 2252 } 2253 2254 return (ppd); 2255 } 2256 2257 2258 /* 2259 * 'ppdOpenFile()' - Read a PPD file into memory. 2260 */ 2261 2262 ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */ 2263 ppdOpenFile(const char *filename) /* I - File to read from */ 2264 { 2265 return _ppdOpenFile(filename, _PPD_LOCALIZATION_DEFAULT); 2266 } 2267 2268 2269 /* 2270 * 'ppdSetConformance()' - Set the conformance level for PPD files. 2271 * 2272 * @since CUPS 1.1.20/macOS 10.4@ 2273 */ 2274 2275 void 2276 ppdSetConformance(ppd_conform_t c) /* I - Conformance level */ 2277 { 2278 _ppd_globals_t *pg = _ppdGlobals(); 2279 /* Global data */ 2280 2281 2282 pg->ppd_conform = c; 2283 } 2284 2285 2286 /* 2287 * 'ppd_add_attr()' - Add an attribute to the PPD data. 2288 */ 2289 2290 static ppd_attr_t * /* O - New attribute */ 2291 ppd_add_attr(ppd_file_t *ppd, /* I - PPD file data */ 2292 const char *name, /* I - Attribute name */ 2293 const char *spec, /* I - Specifier string, if any */ 2294 const char *text, /* I - Text string, if any */ 2295 const char *value) /* I - Value of attribute */ 2296 { 2297 ppd_attr_t **ptr, /* New array */ 2298 *temp; /* New attribute */ 2299 2300 2301 /* 2302 * Range check input... 2303 */ 2304 2305 if (ppd == NULL || name == NULL || spec == NULL) 2306 return (NULL); 2307 2308 /* 2309 * Create the array as needed... 2310 */ 2311 2312 if (!ppd->sorted_attrs) 2313 ppd->sorted_attrs = cupsArrayNew((cups_array_func_t)ppd_compare_attrs, 2314 NULL); 2315 2316 /* 2317 * Allocate memory for the new attribute... 2318 */ 2319 2320 if (ppd->num_attrs == 0) 2321 ptr = malloc(sizeof(ppd_attr_t *)); 2322 else 2323 ptr = realloc(ppd->attrs, (size_t)(ppd->num_attrs + 1) * sizeof(ppd_attr_t *)); 2324 2325 if (ptr == NULL) 2326 return (NULL); 2327 2328 ppd->attrs = ptr; 2329 ptr += ppd->num_attrs; 2330 2331 if ((temp = calloc(1, sizeof(ppd_attr_t))) == NULL) 2332 return (NULL); 2333 2334 *ptr = temp; 2335 2336 ppd->num_attrs ++; 2337 2338 /* 2339 * Copy data over... 2340 */ 2341 2342 strlcpy(temp->name, name, sizeof(temp->name)); 2343 strlcpy(temp->spec, spec, sizeof(temp->spec)); 2344 strlcpy(temp->text, text, sizeof(temp->text)); 2345 temp->value = (char *)value; 2346 2347 /* 2348 * Add the attribute to the sorted array... 2349 */ 2350 2351 cupsArrayAdd(ppd->sorted_attrs, temp); 2352 2353 /* 2354 * Return the attribute... 2355 */ 2356 2357 return (temp); 2358 } 2359 2360 2361 /* 2362 * 'ppd_add_choice()' - Add a choice to an option. 2363 */ 2364 2365 static ppd_choice_t * /* O - Named choice */ 2366 ppd_add_choice(ppd_option_t *option, /* I - Option */ 2367 const char *name) /* I - Name of choice */ 2368 { 2369 ppd_choice_t *choice; /* Choice */ 2370 2371 2372 if (option->num_choices == 0) 2373 choice = malloc(sizeof(ppd_choice_t)); 2374 else 2375 choice = realloc(option->choices, sizeof(ppd_choice_t) * (size_t)(option->num_choices + 1)); 2376 2377 if (choice == NULL) 2378 return (NULL); 2379 2380 option->choices = choice; 2381 choice += option->num_choices; 2382 option->num_choices ++; 2383 2384 memset(choice, 0, sizeof(ppd_choice_t)); 2385 strlcpy(choice->choice, name, sizeof(choice->choice)); 2386 2387 return (choice); 2388 } 2389 2390 2391 /* 2392 * 'ppd_add_size()' - Add a page size. 2393 */ 2394 2395 static ppd_size_t * /* O - Named size */ 2396 ppd_add_size(ppd_file_t *ppd, /* I - PPD file */ 2397 const char *name) /* I - Name of size */ 2398 { 2399 ppd_size_t *size; /* Size */ 2400 2401 2402 if (ppd->num_sizes == 0) 2403 size = malloc(sizeof(ppd_size_t)); 2404 else 2405 size = realloc(ppd->sizes, sizeof(ppd_size_t) * (size_t)(ppd->num_sizes + 1)); 2406 2407 if (size == NULL) 2408 return (NULL); 2409 2410 ppd->sizes = size; 2411 size += ppd->num_sizes; 2412 ppd->num_sizes ++; 2413 2414 memset(size, 0, sizeof(ppd_size_t)); 2415 strlcpy(size->name, name, sizeof(size->name)); 2416 2417 return (size); 2418 } 2419 2420 2421 /* 2422 * 'ppd_compare_attrs()' - Compare two attributes. 2423 */ 2424 2425 static int /* O - Result of comparison */ 2426 ppd_compare_attrs(ppd_attr_t *a, /* I - First attribute */ 2427 ppd_attr_t *b) /* I - Second attribute */ 2428 { 2429 return (_cups_strcasecmp(a->name, b->name)); 2430 } 2431 2432 2433 /* 2434 * 'ppd_compare_choices()' - Compare two choices... 2435 */ 2436 2437 static int /* O - Result of comparison */ 2438 ppd_compare_choices(ppd_choice_t *a, /* I - First choice */ 2439 ppd_choice_t *b) /* I - Second choice */ 2440 { 2441 return (strcmp(a->option->keyword, b->option->keyword)); 2442 } 2443 2444 2445 /* 2446 * 'ppd_compare_coptions()' - Compare two custom options. 2447 */ 2448 2449 static int /* O - Result of comparison */ 2450 ppd_compare_coptions(ppd_coption_t *a, /* I - First option */ 2451 ppd_coption_t *b) /* I - Second option */ 2452 { 2453 return (_cups_strcasecmp(a->keyword, b->keyword)); 2454 } 2455 2456 2457 /* 2458 * 'ppd_compare_options()' - Compare two options. 2459 */ 2460 2461 static int /* O - Result of comparison */ 2462 ppd_compare_options(ppd_option_t *a, /* I - First option */ 2463 ppd_option_t *b) /* I - Second option */ 2464 { 2465 return (_cups_strcasecmp(a->keyword, b->keyword)); 2466 } 2467 2468 2469 /* 2470 * 'ppd_decode()' - Decode a string value... 2471 */ 2472 2473 static int /* O - Length of decoded string */ 2474 ppd_decode(char *string) /* I - String to decode */ 2475 { 2476 char *inptr, /* Input pointer */ 2477 *outptr; /* Output pointer */ 2478 2479 2480 inptr = string; 2481 outptr = string; 2482 2483 while (*inptr != '\0') 2484 if (*inptr == '<' && isxdigit(inptr[1] & 255)) 2485 { 2486 /* 2487 * Convert hex to 8-bit values... 2488 */ 2489 2490 inptr ++; 2491 while (isxdigit(*inptr & 255)) 2492 { 2493 if (_cups_isalpha(*inptr)) 2494 *outptr = (char)((tolower(*inptr) - 'a' + 10) << 4); 2495 else 2496 *outptr = (char)((*inptr - '0') << 4); 2497 2498 inptr ++; 2499 2500 if (!isxdigit(*inptr & 255)) 2501 break; 2502 2503 if (_cups_isalpha(*inptr)) 2504 *outptr |= (char)(tolower(*inptr) - 'a' + 10); 2505 else 2506 *outptr |= (char)(*inptr - '0'); 2507 2508 inptr ++; 2509 outptr ++; 2510 } 2511 2512 while (*inptr != '>' && *inptr != '\0') 2513 inptr ++; 2514 while (*inptr == '>') 2515 inptr ++; 2516 } 2517 else 2518 *outptr++ = *inptr++; 2519 2520 *outptr = '\0'; 2521 2522 return ((int)(outptr - string)); 2523 } 2524 2525 2526 /* 2527 * 'ppd_free_filters()' - Free the filters array. 2528 */ 2529 2530 static void 2531 ppd_free_filters(ppd_file_t *ppd) /* I - PPD file */ 2532 { 2533 int i; /* Looping var */ 2534 char **filter; /* Current filter */ 2535 2536 2537 if (ppd->num_filters > 0) 2538 { 2539 for (i = ppd->num_filters, filter = ppd->filters; i > 0; i --, filter ++) 2540 _cupsStrFree(*filter); 2541 2542 ppd_free(ppd->filters); 2543 2544 ppd->num_filters = 0; 2545 ppd->filters = NULL; 2546 } 2547 } 2548 2549 2550 /* 2551 * 'ppd_free_group()' - Free a single UI group. 2552 */ 2553 2554 static void 2555 ppd_free_group(ppd_group_t *group) /* I - Group to free */ 2556 { 2557 int i; /* Looping var */ 2558 ppd_option_t *option; /* Current option */ 2559 ppd_group_t *subgroup; /* Current sub-group */ 2560 2561 2562 if (group->num_options > 0) 2563 { 2564 for (i = group->num_options, option = group->options; 2565 i > 0; 2566 i --, option ++) 2567 ppd_free_option(option); 2568 2569 ppd_free(group->options); 2570 } 2571 2572 if (group->num_subgroups > 0) 2573 { 2574 for (i = group->num_subgroups, subgroup = group->subgroups; 2575 i > 0; 2576 i --, subgroup ++) 2577 ppd_free_group(subgroup); 2578 2579 ppd_free(group->subgroups); 2580 } 2581 } 2582 2583 2584 /* 2585 * 'ppd_free_option()' - Free a single option. 2586 */ 2587 2588 static void 2589 ppd_free_option(ppd_option_t *option) /* I - Option to free */ 2590 { 2591 int i; /* Looping var */ 2592 ppd_choice_t *choice; /* Current choice */ 2593 2594 2595 if (option->num_choices > 0) 2596 { 2597 for (i = option->num_choices, choice = option->choices; 2598 i > 0; 2599 i --, choice ++) 2600 { 2601 _cupsStrFree(choice->code); 2602 } 2603 2604 ppd_free(option->choices); 2605 } 2606 } 2607 2608 2609 /* 2610 * 'ppd_get_coption()' - Get a custom option record. 2611 */ 2612 2613 static ppd_coption_t * /* O - Custom option... */ 2614 ppd_get_coption(ppd_file_t *ppd, /* I - PPD file */ 2615 const char *name) /* I - Name of option */ 2616 { 2617 ppd_coption_t *copt; /* New custom option */ 2618 2619 2620 /* 2621 * See if the option already exists... 2622 */ 2623 2624 if ((copt = ppdFindCustomOption(ppd, name)) != NULL) 2625 return (copt); 2626 2627 /* 2628 * Not found, so create the custom option record... 2629 */ 2630 2631 if ((copt = calloc(1, sizeof(ppd_coption_t))) == NULL) 2632 return (NULL); 2633 2634 strlcpy(copt->keyword, name, sizeof(copt->keyword)); 2635 2636 copt->params = cupsArrayNew((cups_array_func_t)NULL, NULL); 2637 2638 cupsArrayAdd(ppd->coptions, copt); 2639 2640 /* 2641 * Return the new record... 2642 */ 2643 2644 return (copt); 2645 } 2646 2647 2648 /* 2649 * 'ppd_get_cparam()' - Get a custom parameter record. 2650 */ 2651 2652 static ppd_cparam_t * /* O - Extended option... */ 2653 ppd_get_cparam(ppd_coption_t *opt, /* I - PPD file */ 2654 const char *param, /* I - Name of parameter */ 2655 const char *text) /* I - Human-readable text */ 2656 { 2657 ppd_cparam_t *cparam; /* New custom parameter */ 2658 2659 2660 /* 2661 * See if the parameter already exists... 2662 */ 2663 2664 if ((cparam = ppdFindCustomParam(opt, param)) != NULL) 2665 return (cparam); 2666 2667 /* 2668 * Not found, so create the custom parameter record... 2669 */ 2670 2671 if ((cparam = calloc(1, sizeof(ppd_cparam_t))) == NULL) 2672 return (NULL); 2673 2674 strlcpy(cparam->name, param, sizeof(cparam->name)); 2675 strlcpy(cparam->text, text[0] ? text : param, sizeof(cparam->text)); 2676 2677 /* 2678 * Add this record to the array... 2679 */ 2680 2681 cupsArrayAdd(opt->params, cparam); 2682 2683 /* 2684 * Return the new record... 2685 */ 2686 2687 return (cparam); 2688 } 2689 2690 2691 /* 2692 * 'ppd_get_group()' - Find or create the named group as needed. 2693 */ 2694 2695 static ppd_group_t * /* O - Named group */ 2696 ppd_get_group(ppd_file_t *ppd, /* I - PPD file */ 2697 const char *name, /* I - Name of group */ 2698 const char *text, /* I - Text for group */ 2699 _ppd_globals_t *pg, /* I - Global data */ 2700 cups_encoding_t encoding) /* I - Encoding of text */ 2701 { 2702 int i; /* Looping var */ 2703 ppd_group_t *group; /* Group */ 2704 2705 2706 DEBUG_printf(("7ppd_get_group(ppd=%p, name=\"%s\", text=\"%s\", cg=%p)", 2707 ppd, name, text, pg)); 2708 2709 for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++) 2710 if (!strcmp(group->name, name)) 2711 break; 2712 2713 if (i == 0) 2714 { 2715 DEBUG_printf(("8ppd_get_group: Adding group %s...", name)); 2716 2717 if (pg->ppd_conform == PPD_CONFORM_STRICT && strlen(text) >= sizeof(group->text)) 2718 { 2719 pg->ppd_status = PPD_ILLEGAL_TRANSLATION; 2720 2721 return (NULL); 2722 } 2723 2724 if (ppd->num_groups == 0) 2725 group = malloc(sizeof(ppd_group_t)); 2726 else 2727 group = realloc(ppd->groups, (size_t)(ppd->num_groups + 1) * sizeof(ppd_group_t)); 2728 2729 if (group == NULL) 2730 { 2731 pg->ppd_status = PPD_ALLOC_ERROR; 2732 2733 return (NULL); 2734 } 2735 2736 ppd->groups = group; 2737 group += ppd->num_groups; 2738 ppd->num_groups ++; 2739 2740 memset(group, 0, sizeof(ppd_group_t)); 2741 strlcpy(group->name, name, sizeof(group->name)); 2742 2743 cupsCharsetToUTF8((cups_utf8_t *)group->text, text, 2744 sizeof(group->text), encoding); 2745 } 2746 2747 return (group); 2748 } 2749 2750 2751 /* 2752 * 'ppd_get_option()' - Find or create the named option as needed. 2753 */ 2754 2755 static ppd_option_t * /* O - Named option */ 2756 ppd_get_option(ppd_group_t *group, /* I - Group */ 2757 const char *name) /* I - Name of option */ 2758 { 2759 int i; /* Looping var */ 2760 ppd_option_t *option; /* Option */ 2761 2762 2763 DEBUG_printf(("7ppd_get_option(group=%p(\"%s\"), name=\"%s\")", 2764 group, group->name, name)); 2765 2766 for (i = group->num_options, option = group->options; i > 0; i --, option ++) 2767 if (!strcmp(option->keyword, name)) 2768 break; 2769 2770 if (i == 0) 2771 { 2772 if (group->num_options == 0) 2773 option = malloc(sizeof(ppd_option_t)); 2774 else 2775 option = realloc(group->options, (size_t)(group->num_options + 1) * sizeof(ppd_option_t)); 2776 2777 if (option == NULL) 2778 return (NULL); 2779 2780 group->options = option; 2781 option += group->num_options; 2782 group->num_options ++; 2783 2784 memset(option, 0, sizeof(ppd_option_t)); 2785 strlcpy(option->keyword, name, sizeof(option->keyword)); 2786 } 2787 2788 return (option); 2789 } 2790 2791 2792 /* 2793 * 'ppd_globals_alloc()' - Allocate and initialize global data. 2794 */ 2795 2796 static _ppd_globals_t * /* O - Pointer to global data */ 2797 ppd_globals_alloc(void) 2798 { 2799 return ((_ppd_globals_t *)calloc(1, sizeof(_ppd_globals_t))); 2800 } 2801 2802 2803 /* 2804 * 'ppd_globals_free()' - Free global data. 2805 */ 2806 2807 #if defined(HAVE_PTHREAD_H) || defined(WIN32) 2808 static void 2809 ppd_globals_free(_ppd_globals_t *pg) /* I - Pointer to global data */ 2810 { 2811 free(pg); 2812 } 2813 #endif /* HAVE_PTHREAD_H || WIN32 */ 2814 2815 2816 #ifdef HAVE_PTHREAD_H 2817 /* 2818 * 'ppd_globals_init()' - Initialize per-thread globals... 2819 */ 2820 2821 static void 2822 ppd_globals_init(void) 2823 { 2824 /* 2825 * Register the global data for this thread... 2826 */ 2827 2828 pthread_key_create(&ppd_globals_key, (void (*)(void *))ppd_globals_free); 2829 } 2830 #endif /* HAVE_PTHREAD_H */ 2831 2832 2833 /* 2834 * 'ppd_hash_option()' - Generate a hash of the option name... 2835 */ 2836 2837 static int /* O - Hash index */ 2838 ppd_hash_option(ppd_option_t *option) /* I - Option */ 2839 { 2840 int hash = 0; /* Hash index */ 2841 const char *k; /* Pointer into keyword */ 2842 2843 2844 for (hash = option->keyword[0], k = option->keyword + 1; *k;) 2845 hash = 33 * hash + *k++; 2846 2847 return (hash & 511); 2848 } 2849 2850 2851 /* 2852 * 'ppd_read()' - Read a line from a PPD file, skipping comment lines as 2853 * necessary. 2854 */ 2855 2856 static int /* O - Bitmask of fields read */ 2857 ppd_read(cups_file_t *fp, /* I - File to read from */ 2858 _ppd_line_t *line, /* I - Line buffer */ 2859 char *keyword, /* O - Keyword from line */ 2860 char *option, /* O - Option from line */ 2861 char *text, /* O - Human-readable text from line */ 2862 char **string, /* O - Code/string data */ 2863 int ignoreblank, /* I - Ignore blank lines? */ 2864 _ppd_globals_t *pg) /* I - Global data */ 2865 { 2866 int ch, /* Character from file */ 2867 col, /* Column in line */ 2868 colon, /* Colon seen? */ 2869 endquote, /* Waiting for an end quote */ 2870 mask, /* Mask to be returned */ 2871 startline, /* Start line */ 2872 textlen; /* Length of text */ 2873 char *keyptr, /* Keyword pointer */ 2874 *optptr, /* Option pointer */ 2875 *textptr, /* Text pointer */ 2876 *strptr, /* Pointer into string */ 2877 *lineptr; /* Current position in line buffer */ 2878 2879 2880 /* 2881 * Now loop until we have a valid line... 2882 */ 2883 2884 *string = NULL; 2885 col = 0; 2886 startline = pg->ppd_line + 1; 2887 2888 if (!line->buffer) 2889 { 2890 line->bufsize = 1024; 2891 line->buffer = malloc(1024); 2892 2893 if (!line->buffer) 2894 return (0); 2895 } 2896 2897 do 2898 { 2899 /* 2900 * Read the line... 2901 */ 2902 2903 lineptr = line->buffer; 2904 endquote = 0; 2905 colon = 0; 2906 2907 while ((ch = cupsFileGetChar(fp)) != EOF) 2908 { 2909 if (lineptr >= (line->buffer + line->bufsize - 1)) 2910 { 2911 /* 2912 * Expand the line buffer... 2913 */ 2914 2915 char *temp; /* Temporary line pointer */ 2916 2917 2918 line->bufsize += 1024; 2919 if (line->bufsize > 262144) 2920 { 2921 /* 2922 * Don't allow lines longer than 256k! 2923 */ 2924 2925 pg->ppd_line = startline; 2926 pg->ppd_status = PPD_LINE_TOO_LONG; 2927 2928 return (0); 2929 } 2930 2931 temp = realloc(line->buffer, line->bufsize); 2932 if (!temp) 2933 { 2934 pg->ppd_line = startline; 2935 pg->ppd_status = PPD_LINE_TOO_LONG; 2936 2937 return (0); 2938 } 2939 2940 lineptr = temp + (lineptr - line->buffer); 2941 line->buffer = temp; 2942 } 2943 2944 if (ch == '\r' || ch == '\n') 2945 { 2946 /* 2947 * Line feed or carriage return... 2948 */ 2949 2950 pg->ppd_line ++; 2951 col = 0; 2952 2953 if (ch == '\r') 2954 { 2955 /* 2956 * Check for a trailing line feed... 2957 */ 2958 2959 if ((ch = cupsFilePeekChar(fp)) == EOF) 2960 { 2961 ch = '\n'; 2962 break; 2963 } 2964 2965 if (ch == 0x0a) 2966 cupsFileGetChar(fp); 2967 } 2968 2969 if (lineptr == line->buffer && ignoreblank) 2970 continue; /* Skip blank lines */ 2971 2972 ch = '\n'; 2973 2974 if (!endquote) /* Continue for multi-line text */ 2975 break; 2976 2977 *lineptr++ = '\n'; 2978 } 2979 else if (ch < ' ' && ch != '\t' && pg->ppd_conform == PPD_CONFORM_STRICT) 2980 { 2981 /* 2982 * Other control characters... 2983 */ 2984 2985 pg->ppd_line = startline; 2986 pg->ppd_status = PPD_ILLEGAL_CHARACTER; 2987 2988 return (0); 2989 } 2990 else if (ch != 0x1a) 2991 { 2992 /* 2993 * Any other character... 2994 */ 2995 2996 *lineptr++ = (char)ch; 2997 col ++; 2998 2999 if (col > (PPD_MAX_LINE - 1)) 3000 { 3001 /* 3002 * Line is too long... 3003 */ 3004 3005 pg->ppd_line = startline; 3006 pg->ppd_status = PPD_LINE_TOO_LONG; 3007 3008 return (0); 3009 } 3010 3011 if (ch == ':' && strncmp(line->buffer, "*%", 2) != 0) 3012 colon = 1; 3013 3014 if (ch == '\"' && colon) 3015 endquote = !endquote; 3016 } 3017 } 3018 3019 if (endquote) 3020 { 3021 /* 3022 * Didn't finish this quoted string... 3023 */ 3024 3025 while ((ch = cupsFileGetChar(fp)) != EOF) 3026 if (ch == '\"') 3027 break; 3028 else if (ch == '\r' || ch == '\n') 3029 { 3030 pg->ppd_line ++; 3031 col = 0; 3032 3033 if (ch == '\r') 3034 { 3035 /* 3036 * Check for a trailing line feed... 3037 */ 3038 3039 if ((ch = cupsFilePeekChar(fp)) == EOF) 3040 break; 3041 if (ch == 0x0a) 3042 cupsFileGetChar(fp); 3043 } 3044 } 3045 else if (ch < ' ' && ch != '\t' && pg->ppd_conform == PPD_CONFORM_STRICT) 3046 { 3047 /* 3048 * Other control characters... 3049 */ 3050 3051 pg->ppd_line = startline; 3052 pg->ppd_status = PPD_ILLEGAL_CHARACTER; 3053 3054 return (0); 3055 } 3056 else if (ch != 0x1a) 3057 { 3058 col ++; 3059 3060 if (col > (PPD_MAX_LINE - 1)) 3061 { 3062 /* 3063 * Line is too long... 3064 */ 3065 3066 pg->ppd_line = startline; 3067 pg->ppd_status = PPD_LINE_TOO_LONG; 3068 3069 return (0); 3070 } 3071 } 3072 } 3073 3074 if (ch != '\n') 3075 { 3076 /* 3077 * Didn't finish this line... 3078 */ 3079 3080 while ((ch = cupsFileGetChar(fp)) != EOF) 3081 if (ch == '\r' || ch == '\n') 3082 { 3083 /* 3084 * Line feed or carriage return... 3085 */ 3086 3087 pg->ppd_line ++; 3088 col = 0; 3089 3090 if (ch == '\r') 3091 { 3092 /* 3093 * Check for a trailing line feed... 3094 */ 3095 3096 if ((ch = cupsFilePeekChar(fp)) == EOF) 3097 break; 3098 if (ch == 0x0a) 3099 cupsFileGetChar(fp); 3100 } 3101 3102 break; 3103 } 3104 else if (ch < ' ' && ch != '\t' && pg->ppd_conform == PPD_CONFORM_STRICT) 3105 { 3106 /* 3107 * Other control characters... 3108 */ 3109 3110 pg->ppd_line = startline; 3111 pg->ppd_status = PPD_ILLEGAL_CHARACTER; 3112 3113 return (0); 3114 } 3115 else if (ch != 0x1a) 3116 { 3117 col ++; 3118 3119 if (col > (PPD_MAX_LINE - 1)) 3120 { 3121 /* 3122 * Line is too long... 3123 */ 3124 3125 pg->ppd_line = startline; 3126 pg->ppd_status = PPD_LINE_TOO_LONG; 3127 3128 return (0); 3129 } 3130 } 3131 } 3132 3133 if (lineptr > line->buffer && lineptr[-1] == '\n') 3134 lineptr --; 3135 3136 *lineptr = '\0'; 3137 3138 DEBUG_printf(("9ppd_read: LINE=\"%s\"", line->buffer)); 3139 3140 /* 3141 * The dynamically created PPDs for older style macOS 3142 * drivers include a large blob of data inserted as comments 3143 * at the end of the file. As an optimization we can stop 3144 * reading the PPD when we get to the start of this data. 3145 */ 3146 3147 if (!strcmp(line->buffer, "*%APLWORKSET START")) 3148 return (0); 3149 3150 if (ch == EOF && lineptr == line->buffer) 3151 return (0); 3152 3153 /* 3154 * Now parse it... 3155 */ 3156 3157 mask = 0; 3158 lineptr = line->buffer + 1; 3159 3160 keyword[0] = '\0'; 3161 option[0] = '\0'; 3162 text[0] = '\0'; 3163 *string = NULL; 3164 3165 if ((!line->buffer[0] || /* Blank line */ 3166 !strncmp(line->buffer, "*%", 2) || /* Comment line */ 3167 !strcmp(line->buffer, "*End")) && /* End of multi-line string */ 3168 ignoreblank) /* Ignore these? */ 3169 { 3170 startline = pg->ppd_line + 1; 3171 continue; 3172 } 3173 3174 if (!strcmp(line->buffer, "*")) /* (Bad) comment line */ 3175 { 3176 if (pg->ppd_conform == PPD_CONFORM_RELAXED) 3177 { 3178 startline = pg->ppd_line + 1; 3179 continue; 3180 } 3181 else 3182 { 3183 pg->ppd_line = startline; 3184 pg->ppd_status = PPD_ILLEGAL_MAIN_KEYWORD; 3185 3186 return (0); 3187 } 3188 } 3189 3190 if (line->buffer[0] != '*') /* All lines start with an asterisk */ 3191 { 3192 /* 3193 * Allow lines consisting of just whitespace... 3194 */ 3195 3196 for (lineptr = line->buffer; *lineptr; lineptr ++) 3197 if (*lineptr && !_cups_isspace(*lineptr)) 3198 break; 3199 3200 if (*lineptr) 3201 { 3202 pg->ppd_status = PPD_MISSING_ASTERISK; 3203 return (0); 3204 } 3205 else if (ignoreblank) 3206 continue; 3207 else 3208 return (0); 3209 } 3210 3211 /* 3212 * Get a keyword... 3213 */ 3214 3215 keyptr = keyword; 3216 3217 while (*lineptr && *lineptr != ':' && !_cups_isspace(*lineptr)) 3218 { 3219 if (*lineptr <= ' ' || *lineptr > 126 || *lineptr == '/' || 3220 (keyptr - keyword) >= (PPD_MAX_NAME - 1)) 3221 { 3222 pg->ppd_status = PPD_ILLEGAL_MAIN_KEYWORD; 3223 return (0); 3224 } 3225 3226 *keyptr++ = *lineptr++; 3227 } 3228 3229 *keyptr = '\0'; 3230 3231 if (!strcmp(keyword, "End")) 3232 continue; 3233 3234 mask |= PPD_KEYWORD; 3235 3236 if (_cups_isspace(*lineptr)) 3237 { 3238 /* 3239 * Get an option name... 3240 */ 3241 3242 while (_cups_isspace(*lineptr)) 3243 lineptr ++; 3244 3245 optptr = option; 3246 3247 while (*lineptr && !_cups_isspace(*lineptr) && *lineptr != ':' && 3248 *lineptr != '/') 3249 { 3250 if (*lineptr <= ' ' || *lineptr > 126 || 3251 (optptr - option) >= (PPD_MAX_NAME - 1)) 3252 { 3253 pg->ppd_status = PPD_ILLEGAL_OPTION_KEYWORD; 3254 return (0); 3255 } 3256 3257 *optptr++ = *lineptr++; 3258 } 3259 3260 *optptr = '\0'; 3261 3262 if (_cups_isspace(*lineptr) && pg->ppd_conform == PPD_CONFORM_STRICT) 3263 { 3264 pg->ppd_status = PPD_ILLEGAL_WHITESPACE; 3265 return (0); 3266 } 3267 3268 while (_cups_isspace(*lineptr)) 3269 lineptr ++; 3270 3271 mask |= PPD_OPTION; 3272 3273 if (*lineptr == '/') 3274 { 3275 /* 3276 * Get human-readable text... 3277 */ 3278 3279 lineptr ++; 3280 3281 textptr = text; 3282 3283 while (*lineptr != '\0' && *lineptr != '\n' && *lineptr != ':') 3284 { 3285 if (((unsigned char)*lineptr < ' ' && *lineptr != '\t') || 3286 (textptr - text) >= (PPD_MAX_LINE - 1)) 3287 { 3288 pg->ppd_status = PPD_ILLEGAL_TRANSLATION; 3289 return (0); 3290 } 3291 3292 *textptr++ = *lineptr++; 3293 } 3294 3295 *textptr = '\0'; 3296 textlen = ppd_decode(text); 3297 3298 if (textlen > PPD_MAX_TEXT && pg->ppd_conform == PPD_CONFORM_STRICT) 3299 { 3300 pg->ppd_status = PPD_ILLEGAL_TRANSLATION; 3301 return (0); 3302 } 3303 3304 mask |= PPD_TEXT; 3305 } 3306 } 3307 3308 if (_cups_isspace(*lineptr) && pg->ppd_conform == PPD_CONFORM_STRICT) 3309 { 3310 pg->ppd_status = PPD_ILLEGAL_WHITESPACE; 3311 return (0); 3312 } 3313 3314 while (_cups_isspace(*lineptr)) 3315 lineptr ++; 3316 3317 if (*lineptr == ':') 3318 { 3319 /* 3320 * Get string after triming leading and trailing whitespace... 3321 */ 3322 3323 lineptr ++; 3324 while (_cups_isspace(*lineptr)) 3325 lineptr ++; 3326 3327 strptr = lineptr + strlen(lineptr) - 1; 3328 while (strptr >= lineptr && _cups_isspace(*strptr)) 3329 *strptr-- = '\0'; 3330 3331 if (*strptr == '\"') 3332 { 3333 /* 3334 * Quoted string by itself, remove quotes... 3335 */ 3336 3337 *strptr = '\0'; 3338 lineptr ++; 3339 } 3340 3341 *string = _cupsStrAlloc(lineptr); 3342 3343 mask |= PPD_STRING; 3344 } 3345 } 3346 while (mask == 0); 3347 3348 return (mask); 3349 } 3350 3351 3352 /* 3353 * 'ppd_update_filters()' - Update the filters array as needed. 3354 * 3355 * This function re-populates the filters array with cupsFilter2 entries that 3356 * have been stripped of the destination MIME media types and any maxsize hints. 3357 * 3358 * (All for backwards-compatibility) 3359 */ 3360 3361 static int /* O - 1 on success, 0 on failure */ 3362 ppd_update_filters(ppd_file_t *ppd, /* I - PPD file */ 3363 _ppd_globals_t *pg) /* I - Global data */ 3364 { 3365 ppd_attr_t *attr; /* Current cupsFilter2 value */ 3366 char srcsuper[16], /* Source MIME media type */ 3367 srctype[256], 3368 dstsuper[16], /* Destination MIME media type */ 3369 dsttype[256], 3370 program[1024], /* Command to run */ 3371 *ptr, /* Pointer into command to run */ 3372 buffer[1024], /* Re-written cupsFilter value */ 3373 **filter; /* Current filter */ 3374 int cost; /* Cost of filter */ 3375 3376 3377 DEBUG_printf(("4ppd_update_filters(ppd=%p, cg=%p)", ppd, pg)); 3378 3379 /* 3380 * See if we have any cupsFilter2 lines... 3381 */ 3382 3383 if ((attr = ppdFindAttr(ppd, "cupsFilter2", NULL)) == NULL) 3384 { 3385 DEBUG_puts("5ppd_update_filters: No cupsFilter2 keywords present."); 3386 return (1); 3387 } 3388 3389 /* 3390 * Yes, free the cupsFilter-defined filters and re-build... 3391 */ 3392 3393 ppd_free_filters(ppd); 3394 3395 do 3396 { 3397 /* 3398 * Parse the cupsFilter2 string: 3399 * 3400 * src/type dst/type cost program 3401 * src/type dst/type cost maxsize(n) program 3402 */ 3403 3404 DEBUG_printf(("5ppd_update_filters: cupsFilter2=\"%s\"", attr->value)); 3405 3406 if (sscanf(attr->value, "%15[^/]/%255s%*[ \t]%15[^/]/%255s%d%*[ \t]%1023[^\n]", 3407 srcsuper, srctype, dstsuper, dsttype, &cost, program) != 6) 3408 { 3409 DEBUG_puts("5ppd_update_filters: Bad cupsFilter2 line."); 3410 pg->ppd_status = PPD_BAD_VALUE; 3411 3412 return (0); 3413 } 3414 3415 DEBUG_printf(("5ppd_update_filters: srcsuper=\"%s\", srctype=\"%s\", " 3416 "dstsuper=\"%s\", dsttype=\"%s\", cost=%d, program=\"%s\"", 3417 srcsuper, srctype, dstsuper, dsttype, cost, program)); 3418 3419 if (!strncmp(program, "maxsize(", 8) && 3420 (ptr = strchr(program + 8, ')')) != NULL) 3421 { 3422 DEBUG_puts("5ppd_update_filters: Found maxsize(nnn)."); 3423 3424 ptr ++; 3425 while (_cups_isspace(*ptr)) 3426 ptr ++; 3427 3428 _cups_strcpy(program, ptr); 3429 DEBUG_printf(("5ppd_update_filters: New program=\"%s\"", program)); 3430 } 3431 3432 /* 3433 * Convert to cupsFilter format: 3434 * 3435 * src/type cost program 3436 */ 3437 3438 snprintf(buffer, sizeof(buffer), "%s/%s %d %s", srcsuper, srctype, cost, 3439 program); 3440 DEBUG_printf(("5ppd_update_filters: Adding \"%s\".", buffer)); 3441 3442 /* 3443 * Add a cupsFilter-compatible string to the filters array. 3444 */ 3445 3446 if (ppd->num_filters == 0) 3447 filter = malloc(sizeof(char *)); 3448 else 3449 filter = realloc(ppd->filters, sizeof(char *) * (size_t)(ppd->num_filters + 1)); 3450 3451 if (filter == NULL) 3452 { 3453 DEBUG_puts("5ppd_update_filters: Out of memory."); 3454 pg->ppd_status = PPD_ALLOC_ERROR; 3455 3456 return (0); 3457 } 3458 3459 ppd->filters = filter; 3460 filter += ppd->num_filters; 3461 ppd->num_filters ++; 3462 3463 *filter = _cupsStrAlloc(buffer); 3464 } 3465 while ((attr = ppdFindNextAttr(ppd, "cupsFilter2", NULL)) != NULL); 3466 3467 DEBUG_puts("5ppd_update_filters: Completed OK."); 3468 return (1); 3469 } 3470