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