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