]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/ppd.c
Update Github repository to match macOS 11.2 CUPS sources (cups-494.1).
[thirdparty/cups.git] / cups / ppd.c
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
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 */
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 */
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 */
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 */
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. */
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 option = NULL;
1500
1501 free(string);
1502 string = NULL;
1503 }
1504 else if (!strcmp(keyword, "JCLCloseUI"))
1505 {
1506 if ((!option || option->section != PPD_ORDER_JCL) && pg->ppd_conform == PPD_CONFORM_STRICT)
1507 {
1508 pg->ppd_status = PPD_BAD_CLOSE_UI;
1509
1510 goto error;
1511 }
1512
1513 option = NULL;
1514
1515 free(string);
1516 string = NULL;
1517 }
1518 else if (!strcmp(keyword, "OpenGroup"))
1519 {
1520 /*
1521 * Open a new group...
1522 */
1523
1524 if (group != NULL)
1525 {
1526 pg->ppd_status = PPD_NESTED_OPEN_GROUP;
1527
1528 goto error;
1529 }
1530
1531 if (!string)
1532 {
1533 pg->ppd_status = PPD_BAD_OPEN_GROUP;
1534
1535 goto error;
1536 }
1537
1538 /*
1539 * Separate the group name from the text (name/text)...
1540 */
1541
1542 if ((sptr = strchr(string, '/')) != NULL)
1543 *sptr++ = '\0';
1544 else
1545 sptr = string;
1546
1547 /*
1548 * Fix up the text...
1549 */
1550
1551 ppd_decode(sptr);
1552
1553 /*
1554 * Find/add the group...
1555 */
1556
1557 group = ppd_get_group(ppd, string, sptr, pg, encoding);
1558
1559 if (group == NULL)
1560 goto error;
1561
1562 free(string);
1563 string = NULL;
1564 }
1565 else if (!strcmp(keyword, "CloseGroup"))
1566 {
1567 group = NULL;
1568
1569 free(string);
1570 string = NULL;
1571 }
1572 else if (!strcmp(keyword, "OrderDependency"))
1573 {
1574 order = (float)_cupsStrScand(string, &sptr, loc);
1575
1576 if (!sptr || sscanf(sptr, "%40s%40s", name, keyword) != 2)
1577 {
1578 pg->ppd_status = PPD_BAD_ORDER_DEPENDENCY;
1579
1580 goto error;
1581 }
1582
1583 if (keyword[0] == '*')
1584 _cups_strcpy(keyword, keyword + 1);
1585
1586 if (!strcmp(name, "ExitServer"))
1587 section = PPD_ORDER_EXIT;
1588 else if (!strcmp(name, "Prolog"))
1589 section = PPD_ORDER_PROLOG;
1590 else if (!strcmp(name, "DocumentSetup"))
1591 section = PPD_ORDER_DOCUMENT;
1592 else if (!strcmp(name, "PageSetup"))
1593 section = PPD_ORDER_PAGE;
1594 else if (!strcmp(name, "JCLSetup"))
1595 section = PPD_ORDER_JCL;
1596 else
1597 section = PPD_ORDER_ANY;
1598
1599 if (option == NULL)
1600 {
1601 ppd_group_t *gtemp;
1602
1603
1604 /*
1605 * Only valid for Non-UI options...
1606 */
1607
1608 for (i = ppd->num_groups, gtemp = ppd->groups; i > 0; i --, gtemp ++)
1609 if (gtemp->text[0] == '\0')
1610 break;
1611
1612 if (i > 0)
1613 for (i = 0; i < gtemp->num_options; i ++)
1614 if (!strcmp(keyword, gtemp->options[i].keyword))
1615 {
1616 gtemp->options[i].section = section;
1617 gtemp->options[i].order = order;
1618 break;
1619 }
1620 }
1621 else
1622 {
1623 option->section = section;
1624 option->order = order;
1625 }
1626
1627 free(string);
1628 string = NULL;
1629 }
1630 else if (!strncmp(keyword, "Default", 7))
1631 {
1632 if (string == NULL)
1633 continue;
1634
1635 /*
1636 * Drop UI text, if any, from value...
1637 */
1638
1639 if (strchr(string, '/') != NULL)
1640 *strchr(string, '/') = '\0';
1641
1642 /*
1643 * Assign the default value as appropriate...
1644 */
1645
1646 if (!strcmp(keyword, "DefaultColorSpace"))
1647 {
1648 /*
1649 * Set default colorspace...
1650 */
1651
1652 if (!strcmp(string, "CMY"))
1653 ppd->colorspace = PPD_CS_CMY;
1654 else if (!strcmp(string, "CMYK"))
1655 ppd->colorspace = PPD_CS_CMYK;
1656 else if (!strcmp(string, "RGB"))
1657 ppd->colorspace = PPD_CS_RGB;
1658 else if (!strcmp(string, "RGBK"))
1659 ppd->colorspace = PPD_CS_RGBK;
1660 else if (!strcmp(string, "N"))
1661 ppd->colorspace = PPD_CS_N;
1662 else
1663 ppd->colorspace = PPD_CS_GRAY;
1664 }
1665 else if (option && !strcmp(keyword + 7, option->keyword))
1666 {
1667 /*
1668 * Set the default as part of the current option...
1669 */
1670
1671 DEBUG_printf(("2_ppdOpen: Setting %s to %s...", keyword, string));
1672
1673 strlcpy(option->defchoice, string, sizeof(option->defchoice));
1674
1675 DEBUG_printf(("2_ppdOpen: %s is now %s...", keyword, option->defchoice));
1676 }
1677 else
1678 {
1679 /*
1680 * Lookup option and set if it has been defined...
1681 */
1682
1683 ppd_option_t *toption; /* Temporary option */
1684
1685
1686 if ((toption = ppdFindOption(ppd, keyword + 7)) != NULL)
1687 {
1688 DEBUG_printf(("2_ppdOpen: Setting %s to %s...", keyword, string));
1689 strlcpy(toption->defchoice, string, sizeof(toption->defchoice));
1690 }
1691 }
1692 }
1693 else if (!strcmp(keyword, "UIConstraints") ||
1694 !strcmp(keyword, "NonUIConstraints"))
1695 {
1696 if (!string)
1697 {
1698 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1699 goto error;
1700 }
1701
1702 if (ppd->num_consts == 0)
1703 constraint = calloc(2, sizeof(ppd_const_t));
1704 else
1705 constraint = realloc(ppd->consts, (size_t)(ppd->num_consts + 2) * sizeof(ppd_const_t));
1706
1707 if (constraint == NULL)
1708 {
1709 pg->ppd_status = PPD_ALLOC_ERROR;
1710
1711 goto error;
1712 }
1713
1714 ppd->consts = constraint;
1715 constraint += ppd->num_consts;
1716 ppd->num_consts ++;
1717
1718 switch (sscanf(string, "%40s%40s%40s%40s", constraint->option1,
1719 constraint->choice1, constraint->option2,
1720 constraint->choice2))
1721 {
1722 default : /* Error */
1723 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1724 goto error;
1725
1726 case 2 : /* Two options... */
1727 /*
1728 * Check for broken constraints like "* Option"...
1729 */
1730
1731 if (pg->ppd_conform == PPD_CONFORM_STRICT &&
1732 (!strcmp(constraint->option1, "*") ||
1733 !strcmp(constraint->choice1, "*")))
1734 {
1735 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1736 goto error;
1737 }
1738
1739 /*
1740 * The following strcpy's are safe, as optionN and
1741 * choiceN are all the same size (size defined by PPD spec...)
1742 */
1743
1744 if (constraint->option1[0] == '*')
1745 _cups_strcpy(constraint->option1, constraint->option1 + 1);
1746 else if (pg->ppd_conform == PPD_CONFORM_STRICT)
1747 {
1748 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1749 goto error;
1750 }
1751
1752 if (constraint->choice1[0] == '*')
1753 _cups_strcpy(constraint->option2, constraint->choice1 + 1);
1754 else if (pg->ppd_conform == PPD_CONFORM_STRICT)
1755 {
1756 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1757 goto error;
1758 }
1759
1760 constraint->choice1[0] = '\0';
1761 constraint->choice2[0] = '\0';
1762 break;
1763
1764 case 3 : /* Two options, one choice... */
1765 /*
1766 * Check for broken constraints like "* Option"...
1767 */
1768
1769 if (pg->ppd_conform == PPD_CONFORM_STRICT &&
1770 (!strcmp(constraint->option1, "*") ||
1771 !strcmp(constraint->choice1, "*") ||
1772 !strcmp(constraint->option2, "*")))
1773 {
1774 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1775 goto error;
1776 }
1777
1778 /*
1779 * The following _cups_strcpy's are safe, as optionN and
1780 * choiceN are all the same size (size defined by PPD spec...)
1781 */
1782
1783 if (constraint->option1[0] == '*')
1784 _cups_strcpy(constraint->option1, constraint->option1 + 1);
1785 else if (pg->ppd_conform == PPD_CONFORM_STRICT)
1786 {
1787 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1788 goto error;
1789 }
1790
1791 if (constraint->choice1[0] == '*')
1792 {
1793 if (pg->ppd_conform == PPD_CONFORM_STRICT &&
1794 constraint->option2[0] == '*')
1795 {
1796 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1797 goto error;
1798 }
1799
1800 _cups_strcpy(constraint->choice2, constraint->option2);
1801 _cups_strcpy(constraint->option2, constraint->choice1 + 1);
1802 constraint->choice1[0] = '\0';
1803 }
1804 else
1805 {
1806 if (constraint->option2[0] == '*')
1807 _cups_strcpy(constraint->option2, constraint->option2 + 1);
1808 else if (pg->ppd_conform == PPD_CONFORM_STRICT)
1809 {
1810 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1811 goto error;
1812 }
1813
1814 constraint->choice2[0] = '\0';
1815 }
1816 break;
1817
1818 case 4 : /* Two options, two choices... */
1819 /*
1820 * Check for broken constraints like "* Option"...
1821 */
1822
1823 if (pg->ppd_conform == PPD_CONFORM_STRICT &&
1824 (!strcmp(constraint->option1, "*") ||
1825 !strcmp(constraint->choice1, "*") ||
1826 !strcmp(constraint->option2, "*") ||
1827 !strcmp(constraint->choice2, "*")))
1828 {
1829 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1830 goto error;
1831 }
1832
1833 if (constraint->option1[0] == '*')
1834 _cups_strcpy(constraint->option1, constraint->option1 + 1);
1835 else if (pg->ppd_conform == PPD_CONFORM_STRICT)
1836 {
1837 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1838 goto error;
1839 }
1840
1841 if (pg->ppd_conform == PPD_CONFORM_STRICT &&
1842 constraint->choice1[0] == '*')
1843 {
1844 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1845 goto error;
1846 }
1847
1848 if (constraint->option2[0] == '*')
1849 _cups_strcpy(constraint->option2, constraint->option2 + 1);
1850 else if (pg->ppd_conform == PPD_CONFORM_STRICT)
1851 {
1852 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1853 goto error;
1854 }
1855
1856 if (pg->ppd_conform == PPD_CONFORM_STRICT &&
1857 constraint->choice2[0] == '*')
1858 {
1859 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1860 goto error;
1861 }
1862 break;
1863 }
1864
1865 /*
1866 * Don't add this one as an attribute...
1867 */
1868
1869 free(string);
1870 string = NULL;
1871 }
1872 else if (!strcmp(keyword, "PaperDimension"))
1873 {
1874 if (!_cups_strcasecmp(name, "custom") || !_cups_strncasecmp(name, "custom.", 7))
1875 {
1876 char cname[PPD_MAX_NAME]; /* Rewrite with a leading underscore */
1877 snprintf(cname, sizeof(cname), "_%s", name);
1878 strlcpy(name, cname, sizeof(name));
1879 }
1880
1881 if ((size = ppdPageSize(ppd, name)) == NULL)
1882 size = ppd_add_size(ppd, name);
1883
1884 if (size == NULL)
1885 {
1886 /*
1887 * Unable to add or find size!
1888 */
1889
1890 pg->ppd_status = PPD_ALLOC_ERROR;
1891
1892 goto error;
1893 }
1894
1895 size->width = (float)_cupsStrScand(string, &sptr, loc);
1896 size->length = (float)_cupsStrScand(sptr, NULL, loc);
1897
1898 free(string);
1899 string = NULL;
1900 }
1901 else if (!strcmp(keyword, "ImageableArea"))
1902 {
1903 if (!_cups_strcasecmp(name, "custom") || !_cups_strncasecmp(name, "custom.", 7))
1904 {
1905 char cname[PPD_MAX_NAME]; /* Rewrite with a leading underscore */
1906 snprintf(cname, sizeof(cname), "_%s", name);
1907 strlcpy(name, cname, sizeof(name));
1908 }
1909
1910 if ((size = ppdPageSize(ppd, name)) == NULL)
1911 size = ppd_add_size(ppd, name);
1912
1913 if (size == NULL)
1914 {
1915 /*
1916 * Unable to add or find size!
1917 */
1918
1919 pg->ppd_status = PPD_ALLOC_ERROR;
1920
1921 goto error;
1922 }
1923
1924 size->left = (float)_cupsStrScand(string, &sptr, loc);
1925 size->bottom = (float)_cupsStrScand(sptr, &sptr, loc);
1926 size->right = (float)_cupsStrScand(sptr, &sptr, loc);
1927 size->top = (float)_cupsStrScand(sptr, NULL, loc);
1928
1929 free(string);
1930 string = NULL;
1931 }
1932 else if (option != NULL &&
1933 (mask & (PPD_KEYWORD | PPD_OPTION | PPD_STRING)) ==
1934 (PPD_KEYWORD | PPD_OPTION | PPD_STRING) &&
1935 !strcmp(keyword, option->keyword))
1936 {
1937 DEBUG_printf(("2_ppdOpen: group=%p, subgroup=%p", group, subgroup));
1938
1939 if (!_cups_strcasecmp(name, "custom") || !_cups_strncasecmp(name, "custom.", 7))
1940 {
1941 char cname[PPD_MAX_NAME]; /* Rewrite with a leading underscore */
1942 snprintf(cname, sizeof(cname), "_%s", name);
1943 strlcpy(name, cname, sizeof(name));
1944 }
1945
1946 if (!strcmp(keyword, "PageSize"))
1947 {
1948 /*
1949 * Add a page size...
1950 */
1951
1952 if (ppdPageSize(ppd, name) == NULL)
1953 ppd_add_size(ppd, name);
1954 }
1955
1956 /*
1957 * Add the option choice...
1958 */
1959
1960 if ((choice = ppd_add_choice(option, name)) == NULL)
1961 {
1962 pg->ppd_status = PPD_ALLOC_ERROR;
1963
1964 goto error;
1965 }
1966
1967 if (text[0])
1968 cupsCharsetToUTF8((cups_utf8_t *)choice->text, text,
1969 sizeof(choice->text), encoding);
1970 else if (!strcmp(name, "True"))
1971 strlcpy(choice->text, _("Yes"), sizeof(choice->text));
1972 else if (!strcmp(name, "False"))
1973 strlcpy(choice->text, _("No"), sizeof(choice->text));
1974 else
1975 strlcpy(choice->text, name, sizeof(choice->text));
1976
1977 if (option->section == PPD_ORDER_JCL)
1978 ppd_decode(string); /* Decode quoted string */
1979
1980 choice->code = string;
1981 string = NULL; /* Don't add as an attribute below */
1982 }
1983
1984 /*
1985 * Add remaining lines with keywords and string values as attributes...
1986 */
1987
1988 if (string &&
1989 (mask & (PPD_KEYWORD | PPD_STRING)) == (PPD_KEYWORD | PPD_STRING))
1990 ppd_add_attr(ppd, keyword, name, text, string);
1991 else
1992 free(string);
1993 }
1994
1995 /*
1996 * Check for a missing CloseUI/JCLCloseUI...
1997 */
1998
1999 if (option && pg->ppd_conform == PPD_CONFORM_STRICT)
2000 {
2001 pg->ppd_status = PPD_MISSING_CLOSE_UI;
2002 goto error;
2003 }
2004
2005 /*
2006 * Check for a missing CloseGroup...
2007 */
2008
2009 if (group && pg->ppd_conform == PPD_CONFORM_STRICT)
2010 {
2011 pg->ppd_status = PPD_MISSING_CLOSE_GROUP;
2012 goto error;
2013 }
2014
2015 free(line.buffer);
2016
2017 /*
2018 * Reset language preferences...
2019 */
2020
2021 #ifdef DEBUG
2022 if (!cupsFileEOF(fp))
2023 DEBUG_printf(("1_ppdOpen: Premature EOF at %lu...\n",
2024 (unsigned long)cupsFileTell(fp)));
2025 #endif /* DEBUG */
2026
2027 if (pg->ppd_status != PPD_OK)
2028 {
2029 /*
2030 * Had an error reading the PPD file, cannot continue!
2031 */
2032
2033 ppdClose(ppd);
2034
2035 return (NULL);
2036 }
2037
2038 /*
2039 * Update the filters array as needed...
2040 */
2041
2042 if (!ppd_update_filters(ppd, pg))
2043 {
2044 ppdClose(ppd);
2045
2046 return (NULL);
2047 }
2048
2049 /*
2050 * Create the sorted options array and set the option back-pointer for
2051 * each choice and custom option...
2052 */
2053
2054 ppd->options = cupsArrayNew2((cups_array_func_t)ppd_compare_options, NULL,
2055 (cups_ahash_func_t)ppd_hash_option,
2056 PPD_HASHSIZE);
2057
2058 for (i = ppd->num_groups, group = ppd->groups;
2059 i > 0;
2060 i --, group ++)
2061 {
2062 for (j = group->num_options, option = group->options;
2063 j > 0;
2064 j --, option ++)
2065 {
2066 ppd_coption_t *coption; /* Custom option */
2067
2068
2069 cupsArrayAdd(ppd->options, option);
2070
2071 for (k = 0; k < option->num_choices; k ++)
2072 option->choices[k].option = option;
2073
2074 if ((coption = ppdFindCustomOption(ppd, option->keyword)) != NULL)
2075 coption->option = option;
2076 }
2077 }
2078
2079 /*
2080 * Create an array to track the marked choices...
2081 */
2082
2083 ppd->marked = cupsArrayNew((cups_array_func_t)ppd_compare_choices, NULL);
2084
2085 /*
2086 * Return the PPD file structure...
2087 */
2088
2089 return (ppd);
2090
2091 /*
2092 * Common exit point for errors to save code size...
2093 */
2094
2095 error:
2096
2097 free(string);
2098 free(line.buffer);
2099
2100 ppdClose(ppd);
2101
2102 return (NULL);
2103 }
2104
2105
2106 /*
2107 * 'ppdOpen()' - Read a PPD file into memory.
2108 */
2109
2110 ppd_file_t * /* O - PPD file record */
2111 ppdOpen(FILE *fp) /* I - File to read from */
2112 {
2113 ppd_file_t *ppd; /* PPD file record */
2114 cups_file_t *cf; /* CUPS file */
2115
2116
2117 /*
2118 * Reopen the stdio file as a CUPS file...
2119 */
2120
2121 if ((cf = cupsFileOpenFd(fileno(fp), "r")) == NULL)
2122 return (NULL);
2123
2124 /*
2125 * Load the PPD file using the newer API...
2126 */
2127
2128 ppd = _ppdOpen(cf, _PPD_LOCALIZATION_DEFAULT);
2129
2130 /*
2131 * Close the CUPS file and return the PPD...
2132 */
2133
2134 cupsFileClose(cf);
2135
2136 return (ppd);
2137 }
2138
2139
2140 /*
2141 * 'ppdOpen2()' - Read a PPD file into memory.
2142 *
2143 * @since CUPS 1.2/macOS 10.5@
2144 */
2145
2146 ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
2147 ppdOpen2(cups_file_t *fp) /* I - File to read from */
2148 {
2149 return _ppdOpen(fp, _PPD_LOCALIZATION_DEFAULT);
2150 }
2151
2152
2153 /*
2154 * 'ppdOpenFd()' - Read a PPD file into memory.
2155 */
2156
2157 ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
2158 ppdOpenFd(int fd) /* I - File to read from */
2159 {
2160 cups_file_t *fp; /* CUPS file pointer */
2161 ppd_file_t *ppd; /* PPD file record */
2162 _ppd_globals_t *pg = _ppdGlobals();
2163 /* Global data */
2164
2165
2166 /*
2167 * Set the line number to 0...
2168 */
2169
2170 pg->ppd_line = 0;
2171
2172 /*
2173 * Range check input...
2174 */
2175
2176 if (fd < 0)
2177 {
2178 pg->ppd_status = PPD_NULL_FILE;
2179
2180 return (NULL);
2181 }
2182
2183 /*
2184 * Try to open the file and parse it...
2185 */
2186
2187 if ((fp = cupsFileOpenFd(fd, "r")) != NULL)
2188 {
2189 ppd = ppdOpen2(fp);
2190
2191 cupsFileClose(fp);
2192 }
2193 else
2194 {
2195 pg->ppd_status = PPD_FILE_OPEN_ERROR;
2196 ppd = NULL;
2197 }
2198
2199 return (ppd);
2200 }
2201
2202
2203 /*
2204 * '_ppdOpenFile()' - Read a PPD file into memory.
2205 */
2206
2207 ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
2208 _ppdOpenFile(const char *filename, /* I - File to read from */
2209 _ppd_localization_t localization) /* I - Localization to load */
2210 {
2211 cups_file_t *fp; /* File pointer */
2212 ppd_file_t *ppd; /* PPD file record */
2213 _ppd_globals_t *pg = _ppdGlobals();
2214 /* Global data */
2215
2216
2217 /*
2218 * Set the line number to 0...
2219 */
2220
2221 pg->ppd_line = 0;
2222
2223 /*
2224 * Range check input...
2225 */
2226
2227 if (filename == NULL)
2228 {
2229 pg->ppd_status = PPD_NULL_FILE;
2230
2231 return (NULL);
2232 }
2233
2234 /*
2235 * Try to open the file and parse it...
2236 */
2237
2238 if ((fp = cupsFileOpen(filename, "r")) != NULL)
2239 {
2240 ppd = _ppdOpen(fp, localization);
2241
2242 cupsFileClose(fp);
2243 }
2244 else
2245 {
2246 pg->ppd_status = PPD_FILE_OPEN_ERROR;
2247 ppd = NULL;
2248 }
2249
2250 return (ppd);
2251 }
2252
2253
2254 /*
2255 * 'ppdOpenFile()' - Read a PPD file into memory.
2256 */
2257
2258 ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
2259 ppdOpenFile(const char *filename) /* I - File to read from */
2260 {
2261 return _ppdOpenFile(filename, _PPD_LOCALIZATION_DEFAULT);
2262 }
2263
2264
2265 /*
2266 * 'ppdSetConformance()' - Set the conformance level for PPD files.
2267 *
2268 * @since CUPS 1.1.20/macOS 10.4@
2269 */
2270
2271 void
2272 ppdSetConformance(ppd_conform_t c) /* I - Conformance level */
2273 {
2274 _ppd_globals_t *pg = _ppdGlobals();
2275 /* Global data */
2276
2277
2278 pg->ppd_conform = c;
2279 }
2280
2281
2282 /*
2283 * 'ppd_add_attr()' - Add an attribute to the PPD data.
2284 */
2285
2286 static ppd_attr_t * /* O - New attribute */
2287 ppd_add_attr(ppd_file_t *ppd, /* I - PPD file data */
2288 const char *name, /* I - Attribute name */
2289 const char *spec, /* I - Specifier string, if any */
2290 const char *text, /* I - Text string, if any */
2291 const char *value) /* I - Value of attribute */
2292 {
2293 ppd_attr_t **ptr, /* New array */
2294 *temp; /* New attribute */
2295
2296
2297 /*
2298 * Range check input...
2299 */
2300
2301 if (ppd == NULL || name == NULL || spec == NULL)
2302 return (NULL);
2303
2304 /*
2305 * Create the array as needed...
2306 */
2307
2308 if (!ppd->sorted_attrs)
2309 ppd->sorted_attrs = cupsArrayNew((cups_array_func_t)ppd_compare_attrs,
2310 NULL);
2311
2312 /*
2313 * Allocate memory for the new attribute...
2314 */
2315
2316 if (ppd->num_attrs == 0)
2317 ptr = malloc(sizeof(ppd_attr_t *));
2318 else
2319 ptr = realloc(ppd->attrs, (size_t)(ppd->num_attrs + 1) * sizeof(ppd_attr_t *));
2320
2321 if (ptr == NULL)
2322 return (NULL);
2323
2324 ppd->attrs = ptr;
2325 ptr += ppd->num_attrs;
2326
2327 if ((temp = calloc(1, sizeof(ppd_attr_t))) == NULL)
2328 return (NULL);
2329
2330 *ptr = temp;
2331
2332 ppd->num_attrs ++;
2333
2334 /*
2335 * Copy data over...
2336 */
2337
2338 if (!_cups_strcasecmp(spec, "custom") || !_cups_strncasecmp(spec, "custom.", 7))
2339 {
2340 temp->spec[0] = '_';
2341 strlcpy(temp->spec + 1, spec, sizeof(temp->spec) - 1);
2342 }
2343 else {
2344 strlcpy(temp->spec, spec, sizeof(temp->spec));
2345 }
2346
2347 strlcpy(temp->name, name, sizeof(temp->name));
2348 strlcpy(temp->text, text, sizeof(temp->text));
2349 temp->value = (char *)value;
2350
2351 /*
2352 * Add the attribute to the sorted array...
2353 */
2354
2355 cupsArrayAdd(ppd->sorted_attrs, temp);
2356
2357 /*
2358 * Return the attribute...
2359 */
2360
2361 return (temp);
2362 }
2363
2364
2365 /*
2366 * 'ppd_add_choice()' - Add a choice to an option.
2367 */
2368
2369 static ppd_choice_t * /* O - Named choice */
2370 ppd_add_choice(ppd_option_t *option, /* I - Option */
2371 const char *name) /* I - Name of choice */
2372 {
2373 ppd_choice_t *choice; /* Choice */
2374
2375
2376 if (option->num_choices == 0)
2377 choice = malloc(sizeof(ppd_choice_t));
2378 else
2379 choice = realloc(option->choices, sizeof(ppd_choice_t) * (size_t)(option->num_choices + 1));
2380
2381 if (choice == NULL)
2382 return (NULL);
2383
2384 option->choices = choice;
2385 choice += option->num_choices;
2386 option->num_choices ++;
2387
2388 memset(choice, 0, sizeof(ppd_choice_t));
2389 strlcpy(choice->choice, name, sizeof(choice->choice));
2390
2391 return (choice);
2392 }
2393
2394
2395 /*
2396 * 'ppd_add_size()' - Add a page size.
2397 */
2398
2399 static ppd_size_t * /* O - Named size */
2400 ppd_add_size(ppd_file_t *ppd, /* I - PPD file */
2401 const char *name) /* I - Name of size */
2402 {
2403 ppd_size_t *size; /* Size */
2404
2405
2406 if (ppd->num_sizes == 0)
2407 size = malloc(sizeof(ppd_size_t));
2408 else
2409 size = realloc(ppd->sizes, sizeof(ppd_size_t) * (size_t)(ppd->num_sizes + 1));
2410
2411 if (size == NULL)
2412 return (NULL);
2413
2414 ppd->sizes = size;
2415 size += ppd->num_sizes;
2416 ppd->num_sizes ++;
2417
2418 memset(size, 0, sizeof(ppd_size_t));
2419 strlcpy(size->name, name, sizeof(size->name));
2420
2421 return (size);
2422 }
2423
2424
2425 /*
2426 * 'ppd_compare_attrs()' - Compare two attributes.
2427 */
2428
2429 static int /* O - Result of comparison */
2430 ppd_compare_attrs(ppd_attr_t *a, /* I - First attribute */
2431 ppd_attr_t *b) /* I - Second attribute */
2432 {
2433 return (_cups_strcasecmp(a->name, b->name));
2434 }
2435
2436
2437 /*
2438 * 'ppd_compare_choices()' - Compare two choices...
2439 */
2440
2441 static int /* O - Result of comparison */
2442 ppd_compare_choices(ppd_choice_t *a, /* I - First choice */
2443 ppd_choice_t *b) /* I - Second choice */
2444 {
2445 return (strcmp(a->option->keyword, b->option->keyword));
2446 }
2447
2448
2449 /*
2450 * 'ppd_compare_coptions()' - Compare two custom options.
2451 */
2452
2453 static int /* O - Result of comparison */
2454 ppd_compare_coptions(ppd_coption_t *a, /* I - First option */
2455 ppd_coption_t *b) /* I - Second option */
2456 {
2457 return (_cups_strcasecmp(a->keyword, b->keyword));
2458 }
2459
2460
2461 /*
2462 * 'ppd_compare_options()' - Compare two options.
2463 */
2464
2465 static int /* O - Result of comparison */
2466 ppd_compare_options(ppd_option_t *a, /* I - First option */
2467 ppd_option_t *b) /* I - Second option */
2468 {
2469 return (_cups_strcasecmp(a->keyword, b->keyword));
2470 }
2471
2472
2473 /*
2474 * 'ppd_decode()' - Decode a string value...
2475 */
2476
2477 static int /* O - Length of decoded string */
2478 ppd_decode(char *string) /* I - String to decode */
2479 {
2480 char *inptr, /* Input pointer */
2481 *outptr; /* Output pointer */
2482
2483
2484 inptr = string;
2485 outptr = string;
2486
2487 while (*inptr != '\0')
2488 if (*inptr == '<' && isxdigit(inptr[1] & 255))
2489 {
2490 /*
2491 * Convert hex to 8-bit values...
2492 */
2493
2494 inptr ++;
2495 while (isxdigit(*inptr & 255))
2496 {
2497 if (_cups_isalpha(*inptr))
2498 *outptr = (char)((tolower(*inptr) - 'a' + 10) << 4);
2499 else
2500 *outptr = (char)((*inptr - '0') << 4);
2501
2502 inptr ++;
2503
2504 if (!isxdigit(*inptr & 255))
2505 break;
2506
2507 if (_cups_isalpha(*inptr))
2508 *outptr |= (char)(tolower(*inptr) - 'a' + 10);
2509 else
2510 *outptr |= (char)(*inptr - '0');
2511
2512 inptr ++;
2513 outptr ++;
2514 }
2515
2516 while (*inptr != '>' && *inptr != '\0')
2517 inptr ++;
2518 while (*inptr == '>')
2519 inptr ++;
2520 }
2521 else
2522 *outptr++ = *inptr++;
2523
2524 *outptr = '\0';
2525
2526 return ((int)(outptr - string));
2527 }
2528
2529
2530 /*
2531 * 'ppd_free_filters()' - Free the filters array.
2532 */
2533
2534 static void
2535 ppd_free_filters(ppd_file_t *ppd) /* I - PPD file */
2536 {
2537 int i; /* Looping var */
2538 char **filter; /* Current filter */
2539
2540
2541 if (ppd->num_filters > 0)
2542 {
2543 for (i = ppd->num_filters, filter = ppd->filters; i > 0; i --, filter ++)
2544 free(*filter);
2545
2546 free(ppd->filters);
2547
2548 ppd->num_filters = 0;
2549 ppd->filters = NULL;
2550 }
2551 }
2552
2553
2554 /*
2555 * 'ppd_free_group()' - Free a single UI group.
2556 */
2557
2558 static void
2559 ppd_free_group(ppd_group_t *group) /* I - Group to free */
2560 {
2561 int i; /* Looping var */
2562 ppd_option_t *option; /* Current option */
2563 ppd_group_t *subgroup; /* Current sub-group */
2564
2565
2566 if (group->num_options > 0)
2567 {
2568 for (i = group->num_options, option = group->options;
2569 i > 0;
2570 i --, option ++)
2571 ppd_free_option(option);
2572
2573 free(group->options);
2574 }
2575
2576 if (group->num_subgroups > 0)
2577 {
2578 for (i = group->num_subgroups, subgroup = group->subgroups;
2579 i > 0;
2580 i --, subgroup ++)
2581 ppd_free_group(subgroup);
2582
2583 free(group->subgroups);
2584 }
2585 }
2586
2587
2588 /*
2589 * 'ppd_free_option()' - Free a single option.
2590 */
2591
2592 static void
2593 ppd_free_option(ppd_option_t *option) /* I - Option to free */
2594 {
2595 int i; /* Looping var */
2596 ppd_choice_t *choice; /* Current choice */
2597
2598
2599 if (option->num_choices > 0)
2600 {
2601 for (i = option->num_choices, choice = option->choices;
2602 i > 0;
2603 i --, choice ++)
2604 {
2605 free(choice->code);
2606 }
2607
2608 free(option->choices);
2609 }
2610 }
2611
2612
2613 /*
2614 * 'ppd_get_coption()' - Get a custom option record.
2615 */
2616
2617 static ppd_coption_t * /* O - Custom option... */
2618 ppd_get_coption(ppd_file_t *ppd, /* I - PPD file */
2619 const char *name) /* I - Name of option */
2620 {
2621 ppd_coption_t *copt; /* New custom option */
2622
2623
2624 /*
2625 * See if the option already exists...
2626 */
2627
2628 if ((copt = ppdFindCustomOption(ppd, name)) != NULL)
2629 return (copt);
2630
2631 /*
2632 * Not found, so create the custom option record...
2633 */
2634
2635 if ((copt = calloc(1, sizeof(ppd_coption_t))) == NULL)
2636 return (NULL);
2637
2638 strlcpy(copt->keyword, name, sizeof(copt->keyword));
2639
2640 copt->params = cupsArrayNew((cups_array_func_t)NULL, NULL);
2641
2642 cupsArrayAdd(ppd->coptions, copt);
2643
2644 /*
2645 * Return the new record...
2646 */
2647
2648 return (copt);
2649 }
2650
2651
2652 /*
2653 * 'ppd_get_cparam()' - Get a custom parameter record.
2654 */
2655
2656 static ppd_cparam_t * /* O - Extended option... */
2657 ppd_get_cparam(ppd_coption_t *opt, /* I - PPD file */
2658 const char *param, /* I - Name of parameter */
2659 const char *text) /* I - Human-readable text */
2660 {
2661 ppd_cparam_t *cparam; /* New custom parameter */
2662
2663
2664 /*
2665 * See if the parameter already exists...
2666 */
2667
2668 if ((cparam = ppdFindCustomParam(opt, param)) != NULL)
2669 return (cparam);
2670
2671 /*
2672 * Not found, so create the custom parameter record...
2673 */
2674
2675 if ((cparam = calloc(1, sizeof(ppd_cparam_t))) == NULL)
2676 return (NULL);
2677
2678 cparam->type = PPD_CUSTOM_UNKNOWN;
2679 strlcpy(cparam->name, param, sizeof(cparam->name));
2680 strlcpy(cparam->text, text[0] ? text : param, sizeof(cparam->text));
2681
2682 /*
2683 * Add this record to the array...
2684 */
2685
2686 cupsArrayAdd(opt->params, cparam);
2687
2688 /*
2689 * Return the new record...
2690 */
2691
2692 return (cparam);
2693 }
2694
2695
2696 /*
2697 * 'ppd_get_group()' - Find or create the named group as needed.
2698 */
2699
2700 static ppd_group_t * /* O - Named group */
2701 ppd_get_group(ppd_file_t *ppd, /* I - PPD file */
2702 const char *name, /* I - Name of group */
2703 const char *text, /* I - Text for group */
2704 _ppd_globals_t *pg, /* I - Global data */
2705 cups_encoding_t encoding) /* I - Encoding of text */
2706 {
2707 int i; /* Looping var */
2708 ppd_group_t *group; /* Group */
2709
2710
2711 DEBUG_printf(("7ppd_get_group(ppd=%p, name=\"%s\", text=\"%s\", cg=%p)",
2712 ppd, name, text, pg));
2713
2714 for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
2715 if (!strcmp(group->name, name))
2716 break;
2717
2718 if (i == 0)
2719 {
2720 DEBUG_printf(("8ppd_get_group: Adding group %s...", name));
2721
2722 if (pg->ppd_conform == PPD_CONFORM_STRICT && strlen(text) >= sizeof(group->text))
2723 {
2724 pg->ppd_status = PPD_ILLEGAL_TRANSLATION;
2725
2726 return (NULL);
2727 }
2728
2729 if (ppd->num_groups == 0)
2730 group = malloc(sizeof(ppd_group_t));
2731 else
2732 group = realloc(ppd->groups, (size_t)(ppd->num_groups + 1) * sizeof(ppd_group_t));
2733
2734 if (group == NULL)
2735 {
2736 pg->ppd_status = PPD_ALLOC_ERROR;
2737
2738 return (NULL);
2739 }
2740
2741 ppd->groups = group;
2742 group += ppd->num_groups;
2743 ppd->num_groups ++;
2744
2745 memset(group, 0, sizeof(ppd_group_t));
2746 strlcpy(group->name, name, sizeof(group->name));
2747
2748 cupsCharsetToUTF8((cups_utf8_t *)group->text, text,
2749 sizeof(group->text), encoding);
2750 }
2751
2752 return (group);
2753 }
2754
2755
2756 /*
2757 * 'ppd_get_option()' - Find or create the named option as needed.
2758 */
2759
2760 static ppd_option_t * /* O - Named option */
2761 ppd_get_option(ppd_group_t *group, /* I - Group */
2762 const char *name) /* I - Name of option */
2763 {
2764 int i; /* Looping var */
2765 ppd_option_t *option; /* Option */
2766
2767
2768 DEBUG_printf(("7ppd_get_option(group=%p(\"%s\"), name=\"%s\")",
2769 group, group->name, name));
2770
2771 for (i = group->num_options, option = group->options; i > 0; i --, option ++)
2772 if (!strcmp(option->keyword, name))
2773 break;
2774
2775 if (i == 0)
2776 {
2777 if (group->num_options == 0)
2778 option = malloc(sizeof(ppd_option_t));
2779 else
2780 option = realloc(group->options, (size_t)(group->num_options + 1) * sizeof(ppd_option_t));
2781
2782 if (option == NULL)
2783 return (NULL);
2784
2785 group->options = option;
2786 option += group->num_options;
2787 group->num_options ++;
2788
2789 memset(option, 0, sizeof(ppd_option_t));
2790 strlcpy(option->keyword, name, sizeof(option->keyword));
2791 }
2792
2793 return (option);
2794 }
2795
2796
2797 /*
2798 * 'ppd_globals_alloc()' - Allocate and initialize global data.
2799 */
2800
2801 static _ppd_globals_t * /* O - Pointer to global data */
2802 ppd_globals_alloc(void)
2803 {
2804 return ((_ppd_globals_t *)calloc(1, sizeof(_ppd_globals_t)));
2805 }
2806
2807
2808 /*
2809 * 'ppd_globals_free()' - Free global data.
2810 */
2811
2812 #if defined(HAVE_PTHREAD_H) || defined(_WIN32)
2813 static void
2814 ppd_globals_free(_ppd_globals_t *pg) /* I - Pointer to global data */
2815 {
2816 free(pg);
2817 }
2818 #endif /* HAVE_PTHREAD_H || _WIN32 */
2819
2820
2821 #ifdef HAVE_PTHREAD_H
2822 /*
2823 * 'ppd_globals_init()' - Initialize per-thread globals...
2824 */
2825
2826 static void
2827 ppd_globals_init(void)
2828 {
2829 /*
2830 * Register the global data for this thread...
2831 */
2832
2833 pthread_key_create(&ppd_globals_key, (void (*)(void *))ppd_globals_free);
2834 }
2835 #endif /* HAVE_PTHREAD_H */
2836
2837
2838 /*
2839 * 'ppd_hash_option()' - Generate a hash of the option name...
2840 */
2841
2842 static int /* O - Hash index */
2843 ppd_hash_option(ppd_option_t *option) /* I - Option */
2844 {
2845 int hash = 0; /* Hash index */
2846 const char *k; /* Pointer into keyword */
2847
2848
2849 for (hash = option->keyword[0], k = option->keyword + 1; *k;)
2850 hash = 33 * hash + *k++;
2851
2852 return (hash & 511);
2853 }
2854
2855
2856 /*
2857 * 'ppd_read()' - Read a line from a PPD file, skipping comment lines as
2858 * necessary.
2859 */
2860
2861 static int /* O - Bitmask of fields read */
2862 ppd_read(cups_file_t *fp, /* I - File to read from */
2863 _ppd_line_t *line, /* I - Line buffer */
2864 char *keyword, /* O - Keyword from line */
2865 char *option, /* O - Option from line */
2866 char *text, /* O - Human-readable text from line */
2867 char **string, /* O - Code/string data */
2868 int ignoreblank, /* I - Ignore blank lines? */
2869 _ppd_globals_t *pg) /* I - Global data */
2870 {
2871 int ch, /* Character from file */
2872 col, /* Column in line */
2873 colon, /* Colon seen? */
2874 endquote, /* Waiting for an end quote */
2875 mask, /* Mask to be returned */
2876 startline, /* Start line */
2877 textlen; /* Length of text */
2878 char *keyptr, /* Keyword pointer */
2879 *optptr, /* Option pointer */
2880 *textptr, /* Text pointer */
2881 *strptr, /* Pointer into string */
2882 *lineptr; /* Current position in line buffer */
2883
2884
2885 /*
2886 * Now loop until we have a valid line...
2887 */
2888
2889 *string = NULL;
2890 col = 0;
2891 startline = pg->ppd_line + 1;
2892
2893 if (!line->buffer)
2894 {
2895 line->bufsize = 1024;
2896 line->buffer = malloc(1024);
2897
2898 if (!line->buffer)
2899 return (0);
2900 }
2901
2902 do
2903 {
2904 /*
2905 * Read the line...
2906 */
2907
2908 lineptr = line->buffer;
2909 endquote = 0;
2910 colon = 0;
2911
2912 while ((ch = cupsFileGetChar(fp)) != EOF)
2913 {
2914 if (lineptr >= (line->buffer + line->bufsize - 1))
2915 {
2916 /*
2917 * Expand the line buffer...
2918 */
2919
2920 char *temp; /* Temporary line pointer */
2921
2922
2923 line->bufsize += 1024;
2924 if (line->bufsize > 262144)
2925 {
2926 /*
2927 * Don't allow lines longer than 256k!
2928 */
2929
2930 pg->ppd_line = startline;
2931 pg->ppd_status = PPD_LINE_TOO_LONG;
2932
2933 return (0);
2934 }
2935
2936 temp = realloc(line->buffer, line->bufsize);
2937 if (!temp)
2938 {
2939 pg->ppd_line = startline;
2940 pg->ppd_status = PPD_LINE_TOO_LONG;
2941
2942 return (0);
2943 }
2944
2945 lineptr = temp + (lineptr - line->buffer);
2946 line->buffer = temp;
2947 }
2948
2949 if (ch == '\r' || ch == '\n')
2950 {
2951 /*
2952 * Line feed or carriage return...
2953 */
2954
2955 pg->ppd_line ++;
2956 col = 0;
2957
2958 if (ch == '\r')
2959 {
2960 /*
2961 * Check for a trailing line feed...
2962 */
2963
2964 if ((ch = cupsFilePeekChar(fp)) == EOF)
2965 {
2966 ch = '\n';
2967 break;
2968 }
2969
2970 if (ch == 0x0a)
2971 cupsFileGetChar(fp);
2972 }
2973
2974 if (lineptr == line->buffer && ignoreblank)
2975 continue; /* Skip blank lines */
2976
2977 ch = '\n';
2978
2979 if (!endquote) /* Continue for multi-line text */
2980 break;
2981
2982 *lineptr++ = '\n';
2983 }
2984 else if (ch < ' ' && ch != '\t' && pg->ppd_conform == PPD_CONFORM_STRICT)
2985 {
2986 /*
2987 * Other control characters...
2988 */
2989
2990 pg->ppd_line = startline;
2991 pg->ppd_status = PPD_ILLEGAL_CHARACTER;
2992
2993 return (0);
2994 }
2995 else if (ch != 0x1a)
2996 {
2997 /*
2998 * Any other character...
2999 */
3000
3001 *lineptr++ = (char)ch;
3002 col ++;
3003
3004 if (col > (PPD_MAX_LINE - 1))
3005 {
3006 /*
3007 * Line is too long...
3008 */
3009
3010 pg->ppd_line = startline;
3011 pg->ppd_status = PPD_LINE_TOO_LONG;
3012
3013 return (0);
3014 }
3015
3016 if (ch == ':' && strncmp(line->buffer, "*%", 2) != 0)
3017 colon = 1;
3018
3019 if (ch == '\"' && colon)
3020 endquote = !endquote;
3021 }
3022 }
3023
3024 if (endquote)
3025 {
3026 /*
3027 * Didn't finish this quoted string...
3028 */
3029
3030 while ((ch = cupsFileGetChar(fp)) != EOF)
3031 if (ch == '\"')
3032 break;
3033 else if (ch == '\r' || ch == '\n')
3034 {
3035 pg->ppd_line ++;
3036 col = 0;
3037
3038 if (ch == '\r')
3039 {
3040 /*
3041 * Check for a trailing line feed...
3042 */
3043
3044 if ((ch = cupsFilePeekChar(fp)) == EOF)
3045 break;
3046 if (ch == 0x0a)
3047 cupsFileGetChar(fp);
3048 }
3049 }
3050 else if (ch < ' ' && ch != '\t' && pg->ppd_conform == PPD_CONFORM_STRICT)
3051 {
3052 /*
3053 * Other control characters...
3054 */
3055
3056 pg->ppd_line = startline;
3057 pg->ppd_status = PPD_ILLEGAL_CHARACTER;
3058
3059 return (0);
3060 }
3061 else if (ch != 0x1a)
3062 {
3063 col ++;
3064
3065 if (col > (PPD_MAX_LINE - 1))
3066 {
3067 /*
3068 * Line is too long...
3069 */
3070
3071 pg->ppd_line = startline;
3072 pg->ppd_status = PPD_LINE_TOO_LONG;
3073
3074 return (0);
3075 }
3076 }
3077 }
3078
3079 if (ch != '\n')
3080 {
3081 /*
3082 * Didn't finish this line...
3083 */
3084
3085 while ((ch = cupsFileGetChar(fp)) != EOF)
3086 if (ch == '\r' || ch == '\n')
3087 {
3088 /*
3089 * Line feed or carriage return...
3090 */
3091
3092 pg->ppd_line ++;
3093 col = 0;
3094
3095 if (ch == '\r')
3096 {
3097 /*
3098 * Check for a trailing line feed...
3099 */
3100
3101 if ((ch = cupsFilePeekChar(fp)) == EOF)
3102 break;
3103 if (ch == 0x0a)
3104 cupsFileGetChar(fp);
3105 }
3106
3107 break;
3108 }
3109 else if (ch < ' ' && ch != '\t' && pg->ppd_conform == PPD_CONFORM_STRICT)
3110 {
3111 /*
3112 * Other control characters...
3113 */
3114
3115 pg->ppd_line = startline;
3116 pg->ppd_status = PPD_ILLEGAL_CHARACTER;
3117
3118 return (0);
3119 }
3120 else if (ch != 0x1a)
3121 {
3122 col ++;
3123
3124 if (col > (PPD_MAX_LINE - 1))
3125 {
3126 /*
3127 * Line is too long...
3128 */
3129
3130 pg->ppd_line = startline;
3131 pg->ppd_status = PPD_LINE_TOO_LONG;
3132
3133 return (0);
3134 }
3135 }
3136 }
3137
3138 if (lineptr > line->buffer && lineptr[-1] == '\n')
3139 lineptr --;
3140
3141 *lineptr = '\0';
3142
3143 DEBUG_printf(("9ppd_read: LINE=\"%s\"", line->buffer));
3144
3145 /*
3146 * The dynamically created PPDs for older style macOS
3147 * drivers include a large blob of data inserted as comments
3148 * at the end of the file. As an optimization we can stop
3149 * reading the PPD when we get to the start of this data.
3150 */
3151
3152 if (!strcmp(line->buffer, "*%APLWORKSET START"))
3153 return (0);
3154
3155 if (ch == EOF && lineptr == line->buffer)
3156 return (0);
3157
3158 /*
3159 * Now parse it...
3160 */
3161
3162 mask = 0;
3163 lineptr = line->buffer + 1;
3164
3165 keyword[0] = '\0';
3166 option[0] = '\0';
3167 text[0] = '\0';
3168 *string = NULL;
3169
3170 if ((!line->buffer[0] || /* Blank line */
3171 !strncmp(line->buffer, "*%", 2) || /* Comment line */
3172 !strcmp(line->buffer, "*End")) && /* End of multi-line string */
3173 ignoreblank) /* Ignore these? */
3174 {
3175 startline = pg->ppd_line + 1;
3176 continue;
3177 }
3178
3179 if (!strcmp(line->buffer, "*")) /* (Bad) comment line */
3180 {
3181 if (pg->ppd_conform == PPD_CONFORM_RELAXED)
3182 {
3183 startline = pg->ppd_line + 1;
3184 continue;
3185 }
3186 else
3187 {
3188 pg->ppd_line = startline;
3189 pg->ppd_status = PPD_ILLEGAL_MAIN_KEYWORD;
3190
3191 return (0);
3192 }
3193 }
3194
3195 if (line->buffer[0] != '*') /* All lines start with an asterisk */
3196 {
3197 /*
3198 * Allow lines consisting of just whitespace...
3199 */
3200
3201 for (lineptr = line->buffer; *lineptr; lineptr ++)
3202 if (*lineptr && !_cups_isspace(*lineptr))
3203 break;
3204
3205 if (*lineptr)
3206 {
3207 pg->ppd_status = PPD_MISSING_ASTERISK;
3208 return (0);
3209 }
3210 else if (ignoreblank)
3211 continue;
3212 else
3213 return (0);
3214 }
3215
3216 /*
3217 * Get a keyword...
3218 */
3219
3220 keyptr = keyword;
3221
3222 while (*lineptr && *lineptr != ':' && !_cups_isspace(*lineptr))
3223 {
3224 if (*lineptr <= ' ' || *lineptr > 126 || *lineptr == '/' ||
3225 (keyptr - keyword) >= (PPD_MAX_NAME - 1))
3226 {
3227 pg->ppd_status = PPD_ILLEGAL_MAIN_KEYWORD;
3228 return (0);
3229 }
3230
3231 *keyptr++ = *lineptr++;
3232 }
3233
3234 *keyptr = '\0';
3235
3236 if (!strcmp(keyword, "End"))
3237 continue;
3238
3239 mask |= PPD_KEYWORD;
3240
3241 if (_cups_isspace(*lineptr))
3242 {
3243 /*
3244 * Get an option name...
3245 */
3246
3247 while (_cups_isspace(*lineptr))
3248 lineptr ++;
3249
3250 optptr = option;
3251
3252 while (*lineptr && !_cups_isspace(*lineptr) && *lineptr != ':' &&
3253 *lineptr != '/')
3254 {
3255 if (*lineptr <= ' ' || *lineptr > 126 ||
3256 (optptr - option) >= (PPD_MAX_NAME - 1))
3257 {
3258 pg->ppd_status = PPD_ILLEGAL_OPTION_KEYWORD;
3259 return (0);
3260 }
3261
3262 *optptr++ = *lineptr++;
3263 }
3264
3265 *optptr = '\0';
3266
3267 if (_cups_isspace(*lineptr) && pg->ppd_conform == PPD_CONFORM_STRICT)
3268 {
3269 pg->ppd_status = PPD_ILLEGAL_WHITESPACE;
3270 return (0);
3271 }
3272
3273 while (_cups_isspace(*lineptr))
3274 lineptr ++;
3275
3276 mask |= PPD_OPTION;
3277
3278 if (*lineptr == '/')
3279 {
3280 /*
3281 * Get human-readable text...
3282 */
3283
3284 lineptr ++;
3285
3286 textptr = text;
3287
3288 while (*lineptr != '\0' && *lineptr != '\n' && *lineptr != ':')
3289 {
3290 if (((unsigned char)*lineptr < ' ' && *lineptr != '\t') ||
3291 (textptr - text) >= (PPD_MAX_LINE - 1))
3292 {
3293 pg->ppd_status = PPD_ILLEGAL_TRANSLATION;
3294 return (0);
3295 }
3296
3297 *textptr++ = *lineptr++;
3298 }
3299
3300 *textptr = '\0';
3301 textlen = ppd_decode(text);
3302
3303 if (textlen > PPD_MAX_TEXT && pg->ppd_conform == PPD_CONFORM_STRICT)
3304 {
3305 pg->ppd_status = PPD_ILLEGAL_TRANSLATION;
3306 return (0);
3307 }
3308
3309 mask |= PPD_TEXT;
3310 }
3311 }
3312
3313 if (_cups_isspace(*lineptr) && pg->ppd_conform == PPD_CONFORM_STRICT)
3314 {
3315 pg->ppd_status = PPD_ILLEGAL_WHITESPACE;
3316 return (0);
3317 }
3318
3319 while (_cups_isspace(*lineptr))
3320 lineptr ++;
3321
3322 if (*lineptr == ':')
3323 {
3324 /*
3325 * Get string after triming leading and trailing whitespace...
3326 */
3327
3328 lineptr ++;
3329 while (_cups_isspace(*lineptr))
3330 lineptr ++;
3331
3332 strptr = lineptr + strlen(lineptr) - 1;
3333 while (strptr >= lineptr && _cups_isspace(*strptr))
3334 *strptr-- = '\0';
3335
3336 if (*strptr == '\"')
3337 {
3338 /*
3339 * Quoted string by itself, remove quotes...
3340 */
3341
3342 *strptr = '\0';
3343 lineptr ++;
3344 }
3345
3346 *string = strdup(lineptr);
3347
3348 mask |= PPD_STRING;
3349 }
3350 }
3351 while (mask == 0);
3352
3353 return (mask);
3354 }
3355
3356
3357 /*
3358 * 'ppd_update_filters()' - Update the filters array as needed.
3359 *
3360 * This function re-populates the filters array with cupsFilter2 entries that
3361 * have been stripped of the destination MIME media types and any maxsize hints.
3362 *
3363 * (All for backwards-compatibility)
3364 */
3365
3366 static int /* O - 1 on success, 0 on failure */
3367 ppd_update_filters(ppd_file_t *ppd, /* I - PPD file */
3368 _ppd_globals_t *pg) /* I - Global data */
3369 {
3370 ppd_attr_t *attr; /* Current cupsFilter2 value */
3371 char srcsuper[16], /* Source MIME media type */
3372 srctype[256],
3373 dstsuper[16], /* Destination MIME media type */
3374 dsttype[256],
3375 program[1024], /* Command to run */
3376 *ptr, /* Pointer into command to run */
3377 buffer[1024], /* Re-written cupsFilter value */
3378 **filter; /* Current filter */
3379 int cost; /* Cost of filter */
3380
3381
3382 DEBUG_printf(("4ppd_update_filters(ppd=%p, cg=%p)", ppd, pg));
3383
3384 /*
3385 * See if we have any cupsFilter2 lines...
3386 */
3387
3388 if ((attr = ppdFindAttr(ppd, "cupsFilter2", NULL)) == NULL)
3389 {
3390 DEBUG_puts("5ppd_update_filters: No cupsFilter2 keywords present.");
3391 return (1);
3392 }
3393
3394 /*
3395 * Yes, free the cupsFilter-defined filters and re-build...
3396 */
3397
3398 ppd_free_filters(ppd);
3399
3400 do
3401 {
3402 /*
3403 * Parse the cupsFilter2 string:
3404 *
3405 * src/type dst/type cost program
3406 * src/type dst/type cost maxsize(n) program
3407 */
3408
3409 DEBUG_printf(("5ppd_update_filters: cupsFilter2=\"%s\"", attr->value));
3410
3411 if (sscanf(attr->value, "%15[^/]/%255s%*[ \t]%15[^/]/%255s%d%*[ \t]%1023[^\n]",
3412 srcsuper, srctype, dstsuper, dsttype, &cost, program) != 6)
3413 {
3414 DEBUG_puts("5ppd_update_filters: Bad cupsFilter2 line.");
3415 pg->ppd_status = PPD_BAD_VALUE;
3416
3417 return (0);
3418 }
3419
3420 DEBUG_printf(("5ppd_update_filters: srcsuper=\"%s\", srctype=\"%s\", "
3421 "dstsuper=\"%s\", dsttype=\"%s\", cost=%d, program=\"%s\"",
3422 srcsuper, srctype, dstsuper, dsttype, cost, program));
3423
3424 if (!strncmp(program, "maxsize(", 8) &&
3425 (ptr = strchr(program + 8, ')')) != NULL)
3426 {
3427 DEBUG_puts("5ppd_update_filters: Found maxsize(nnn).");
3428
3429 ptr ++;
3430 while (_cups_isspace(*ptr))
3431 ptr ++;
3432
3433 _cups_strcpy(program, ptr);
3434 DEBUG_printf(("5ppd_update_filters: New program=\"%s\"", program));
3435 }
3436
3437 /*
3438 * Convert to cupsFilter format:
3439 *
3440 * src/type cost program
3441 */
3442
3443 snprintf(buffer, sizeof(buffer), "%s/%s %d %s", srcsuper, srctype, cost,
3444 program);
3445 DEBUG_printf(("5ppd_update_filters: Adding \"%s\".", buffer));
3446
3447 /*
3448 * Add a cupsFilter-compatible string to the filters array.
3449 */
3450
3451 if (ppd->num_filters == 0)
3452 filter = malloc(sizeof(char *));
3453 else
3454 filter = realloc(ppd->filters, sizeof(char *) * (size_t)(ppd->num_filters + 1));
3455
3456 if (filter == NULL)
3457 {
3458 DEBUG_puts("5ppd_update_filters: Out of memory.");
3459 pg->ppd_status = PPD_ALLOC_ERROR;
3460
3461 return (0);
3462 }
3463
3464 ppd->filters = filter;
3465 filter += ppd->num_filters;
3466 ppd->num_filters ++;
3467
3468 *filter = strdup(buffer);
3469 }
3470 while ((attr = ppdFindNextAttr(ppd, "cupsFilter2", NULL)) != NULL);
3471
3472 DEBUG_puts("5ppd_update_filters: Completed OK.");
3473 return (1);
3474 }