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