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