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