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