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