]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/ppd.c
Fix potential unaligned accesses in the string pool (Issue #5474)
[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 free(string);
615
616 /*
617 * Allocate memory for the PPD file record...
618 */
619
620 if ((ppd = calloc(1, sizeof(ppd_file_t))) == NULL)
621 {
622 pg->ppd_status = PPD_ALLOC_ERROR;
623
624 free(string);
625 free(line.buffer);
626
627 return (NULL);
628 }
629
630 ppd->language_level = 2;
631 ppd->color_device = 0;
632 ppd->colorspace = PPD_CS_N;
633 ppd->landscape = -90;
634 ppd->coptions = cupsArrayNew((cups_array_func_t)ppd_compare_coptions,
635 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, "JobPatchFile"))
1175 {
1176 /*
1177 * CUPS STR #3421: Check for "*JobPatchFile: int: string"
1178 */
1179
1180 if (isdigit(*string & 255))
1181 {
1182 for (sptr = string + 1; isdigit(*sptr & 255); sptr ++);
1183
1184 if (*sptr == ':')
1185 {
1186 /*
1187 * Found "*JobPatchFile: int: string"...
1188 */
1189
1190 pg->ppd_status = PPD_BAD_VALUE;
1191
1192 goto error;
1193 }
1194 }
1195
1196 if (!name[0] && pg->ppd_conform == PPD_CONFORM_STRICT)
1197 {
1198 /*
1199 * Found "*JobPatchFile: string"...
1200 */
1201
1202 pg->ppd_status = PPD_MISSING_OPTION_KEYWORD;
1203
1204 goto error;
1205 }
1206
1207 if (ppd->patches == NULL)
1208 ppd->patches = strdup(string);
1209 else
1210 {
1211 temp = realloc(ppd->patches, strlen(ppd->patches) +
1212 strlen(string) + 1);
1213 if (temp == NULL)
1214 {
1215 pg->ppd_status = PPD_ALLOC_ERROR;
1216
1217 goto error;
1218 }
1219
1220 ppd->patches = temp;
1221
1222 memcpy(ppd->patches + strlen(ppd->patches), string, strlen(string) + 1);
1223 }
1224 }
1225 else if (!strcmp(keyword, "OpenUI"))
1226 {
1227 /*
1228 * Don't allow nesting of options...
1229 */
1230
1231 if (option && pg->ppd_conform == PPD_CONFORM_STRICT)
1232 {
1233 pg->ppd_status = PPD_NESTED_OPEN_UI;
1234
1235 goto error;
1236 }
1237
1238 /*
1239 * Add an option record to the current sub-group, group, or file...
1240 */
1241
1242 DEBUG_printf(("2_ppdOpen: name=\"%s\" (%d)", name, (int)strlen(name)));
1243
1244 if (name[0] == '*')
1245 _cups_strcpy(name, name + 1); /* Eliminate leading asterisk */
1246
1247 for (i = (int)strlen(name) - 1; i > 0 && _cups_isspace(name[i]); i --)
1248 name[i] = '\0'; /* Eliminate trailing spaces */
1249
1250 DEBUG_printf(("2_ppdOpen: OpenUI of %s in group %s...", name,
1251 group ? group->text : "(null)"));
1252
1253 if (subgroup != NULL)
1254 option = ppd_get_option(subgroup, name);
1255 else if (group == NULL)
1256 {
1257 if ((group = ppd_get_group(ppd, "General", _("General"), pg,
1258 encoding)) == NULL)
1259 goto error;
1260
1261 DEBUG_printf(("2_ppdOpen: Adding to group %s...", group->text));
1262 option = ppd_get_option(group, name);
1263 group = NULL;
1264 }
1265 else
1266 option = ppd_get_option(group, name);
1267
1268 if (option == NULL)
1269 {
1270 pg->ppd_status = PPD_ALLOC_ERROR;
1271
1272 goto error;
1273 }
1274
1275 /*
1276 * Now fill in the initial information for the option...
1277 */
1278
1279 if (string && !strcmp(string, "PickMany"))
1280 option->ui = PPD_UI_PICKMANY;
1281 else if (string && !strcmp(string, "Boolean"))
1282 option->ui = PPD_UI_BOOLEAN;
1283 else if (string && !strcmp(string, "PickOne"))
1284 option->ui = PPD_UI_PICKONE;
1285 else if (pg->ppd_conform == PPD_CONFORM_STRICT)
1286 {
1287 pg->ppd_status = PPD_BAD_OPEN_UI;
1288
1289 goto error;
1290 }
1291 else
1292 option->ui = PPD_UI_PICKONE;
1293
1294 for (j = 0; j < ppd->num_attrs; j ++)
1295 if (!strncmp(ppd->attrs[j]->name, "Default", 7) &&
1296 !strcmp(ppd->attrs[j]->name + 7, name) &&
1297 ppd->attrs[j]->value)
1298 {
1299 DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...",
1300 option->keyword, ppd->attrs[j]->value));
1301 strlcpy(option->defchoice, ppd->attrs[j]->value,
1302 sizeof(option->defchoice));
1303 break;
1304 }
1305
1306 if (text[0])
1307 cupsCharsetToUTF8((cups_utf8_t *)option->text, text,
1308 sizeof(option->text), encoding);
1309 else
1310 {
1311 if (!strcmp(name, "PageSize"))
1312 strlcpy(option->text, _("Media Size"), sizeof(option->text));
1313 else if (!strcmp(name, "MediaType"))
1314 strlcpy(option->text, _("Media Type"), sizeof(option->text));
1315 else if (!strcmp(name, "InputSlot"))
1316 strlcpy(option->text, _("Media Source"), sizeof(option->text));
1317 else if (!strcmp(name, "ColorModel"))
1318 strlcpy(option->text, _("Output Mode"), sizeof(option->text));
1319 else if (!strcmp(name, "Resolution"))
1320 strlcpy(option->text, _("Resolution"), sizeof(option->text));
1321 else
1322 strlcpy(option->text, name, sizeof(option->text));
1323 }
1324
1325 option->section = PPD_ORDER_ANY;
1326
1327 free(string);
1328 string = NULL;
1329
1330 /*
1331 * Add a custom option choice if we have already seen a CustomFoo
1332 * attribute...
1333 */
1334
1335 if (!_cups_strcasecmp(name, "PageRegion"))
1336 strlcpy(custom_name, "CustomPageSize", sizeof(custom_name));
1337 else
1338 snprintf(custom_name, sizeof(custom_name), "Custom%s", name);
1339
1340 if ((custom_attr = ppdFindAttr(ppd, custom_name, "True")) != NULL)
1341 {
1342 if ((choice = ppdFindChoice(option, "Custom")) == NULL)
1343 if ((choice = ppd_add_choice(option, "Custom")) == NULL)
1344 {
1345 DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
1346
1347 pg->ppd_status = PPD_ALLOC_ERROR;
1348
1349 goto error;
1350 }
1351
1352 strlcpy(choice->text,
1353 custom_attr->text[0] ? custom_attr->text : _("Custom"),
1354 sizeof(choice->text));
1355 choice->code = strdup(custom_attr->value);
1356 }
1357 }
1358 else if (!strcmp(keyword, "JCLOpenUI"))
1359 {
1360 /*
1361 * Don't allow nesting of options...
1362 */
1363
1364 if (option && pg->ppd_conform == PPD_CONFORM_STRICT)
1365 {
1366 pg->ppd_status = PPD_NESTED_OPEN_UI;
1367
1368 goto error;
1369 }
1370
1371 /*
1372 * Find the JCL group, and add if needed...
1373 */
1374
1375 group = ppd_get_group(ppd, "JCL", _("JCL"), pg, encoding);
1376
1377 if (group == NULL)
1378 goto error;
1379
1380 /*
1381 * Add an option record to the current JCLs...
1382 */
1383
1384 if (name[0] == '*')
1385 _cups_strcpy(name, name + 1);
1386
1387 option = ppd_get_option(group, name);
1388
1389 if (option == NULL)
1390 {
1391 pg->ppd_status = PPD_ALLOC_ERROR;
1392
1393 goto error;
1394 }
1395
1396 /*
1397 * Now fill in the initial information for the option...
1398 */
1399
1400 if (string && !strcmp(string, "PickMany"))
1401 option->ui = PPD_UI_PICKMANY;
1402 else if (string && !strcmp(string, "Boolean"))
1403 option->ui = PPD_UI_BOOLEAN;
1404 else if (string && !strcmp(string, "PickOne"))
1405 option->ui = PPD_UI_PICKONE;
1406 else
1407 {
1408 pg->ppd_status = PPD_BAD_OPEN_UI;
1409
1410 goto error;
1411 }
1412
1413 for (j = 0; j < ppd->num_attrs; j ++)
1414 if (!strncmp(ppd->attrs[j]->name, "Default", 7) &&
1415 !strcmp(ppd->attrs[j]->name + 7, name) &&
1416 ppd->attrs[j]->value)
1417 {
1418 DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...",
1419 option->keyword, ppd->attrs[j]->value));
1420 strlcpy(option->defchoice, ppd->attrs[j]->value,
1421 sizeof(option->defchoice));
1422 break;
1423 }
1424
1425 if (text[0])
1426 cupsCharsetToUTF8((cups_utf8_t *)option->text, text,
1427 sizeof(option->text), encoding);
1428 else
1429 strlcpy(option->text, name, sizeof(option->text));
1430
1431 option->section = PPD_ORDER_JCL;
1432 group = NULL;
1433
1434 free(string);
1435 string = NULL;
1436
1437 /*
1438 * Add a custom option choice if we have already seen a CustomFoo
1439 * attribute...
1440 */
1441
1442 snprintf(custom_name, sizeof(custom_name), "Custom%s", name);
1443
1444 if ((custom_attr = ppdFindAttr(ppd, custom_name, "True")) != NULL)
1445 {
1446 if ((choice = ppd_add_choice(option, "Custom")) == NULL)
1447 {
1448 DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
1449
1450 pg->ppd_status = PPD_ALLOC_ERROR;
1451
1452 goto error;
1453 }
1454
1455 strlcpy(choice->text,
1456 custom_attr->text[0] ? custom_attr->text : _("Custom"),
1457 sizeof(choice->text));
1458 choice->code = strdup(custom_attr->value);
1459 }
1460 }
1461 else if (!strcmp(keyword, "CloseUI"))
1462 {
1463 if ((!option || option->section == PPD_ORDER_JCL) && pg->ppd_conform == PPD_CONFORM_STRICT)
1464 {
1465 pg->ppd_status = PPD_BAD_CLOSE_UI;
1466
1467 goto error;
1468 }
1469
1470 option = NULL;
1471
1472 free(string);
1473 string = NULL;
1474 }
1475 else if (!strcmp(keyword, "JCLCloseUI"))
1476 {
1477 if ((!option || option->section != PPD_ORDER_JCL) && pg->ppd_conform == PPD_CONFORM_STRICT)
1478 {
1479 pg->ppd_status = PPD_BAD_CLOSE_UI;
1480
1481 goto error;
1482 }
1483
1484 option = NULL;
1485
1486 free(string);
1487 string = NULL;
1488 }
1489 else if (!strcmp(keyword, "OpenGroup"))
1490 {
1491 /*
1492 * Open a new group...
1493 */
1494
1495 if (group != NULL)
1496 {
1497 pg->ppd_status = PPD_NESTED_OPEN_GROUP;
1498
1499 goto error;
1500 }
1501
1502 if (!string)
1503 {
1504 pg->ppd_status = PPD_BAD_OPEN_GROUP;
1505
1506 goto error;
1507 }
1508
1509 /*
1510 * Separate the group name from the text (name/text)...
1511 */
1512
1513 if ((sptr = strchr(string, '/')) != NULL)
1514 *sptr++ = '\0';
1515 else
1516 sptr = string;
1517
1518 /*
1519 * Fix up the text...
1520 */
1521
1522 ppd_decode(sptr);
1523
1524 /*
1525 * Find/add the group...
1526 */
1527
1528 group = ppd_get_group(ppd, string, sptr, pg, encoding);
1529
1530 if (group == NULL)
1531 goto error;
1532
1533 free(string);
1534 string = NULL;
1535 }
1536 else if (!strcmp(keyword, "CloseGroup"))
1537 {
1538 group = NULL;
1539
1540 free(string);
1541 string = NULL;
1542 }
1543 else if (!strcmp(keyword, "OrderDependency"))
1544 {
1545 order = (float)_cupsStrScand(string, &sptr, loc);
1546
1547 if (!sptr || sscanf(sptr, "%40s%40s", name, keyword) != 2)
1548 {
1549 pg->ppd_status = PPD_BAD_ORDER_DEPENDENCY;
1550
1551 goto error;
1552 }
1553
1554 if (keyword[0] == '*')
1555 _cups_strcpy(keyword, keyword + 1);
1556
1557 if (!strcmp(name, "ExitServer"))
1558 section = PPD_ORDER_EXIT;
1559 else if (!strcmp(name, "Prolog"))
1560 section = PPD_ORDER_PROLOG;
1561 else if (!strcmp(name, "DocumentSetup"))
1562 section = PPD_ORDER_DOCUMENT;
1563 else if (!strcmp(name, "PageSetup"))
1564 section = PPD_ORDER_PAGE;
1565 else if (!strcmp(name, "JCLSetup"))
1566 section = PPD_ORDER_JCL;
1567 else
1568 section = PPD_ORDER_ANY;
1569
1570 if (option == NULL)
1571 {
1572 ppd_group_t *gtemp;
1573
1574
1575 /*
1576 * Only valid for Non-UI options...
1577 */
1578
1579 for (i = ppd->num_groups, gtemp = ppd->groups; i > 0; i --, gtemp ++)
1580 if (gtemp->text[0] == '\0')
1581 break;
1582
1583 if (i > 0)
1584 for (i = 0; i < gtemp->num_options; i ++)
1585 if (!strcmp(keyword, gtemp->options[i].keyword))
1586 {
1587 gtemp->options[i].section = section;
1588 gtemp->options[i].order = order;
1589 break;
1590 }
1591 }
1592 else
1593 {
1594 option->section = section;
1595 option->order = order;
1596 }
1597
1598 free(string);
1599 string = NULL;
1600 }
1601 else if (!strncmp(keyword, "Default", 7))
1602 {
1603 if (string == NULL)
1604 continue;
1605
1606 /*
1607 * Drop UI text, if any, from value...
1608 */
1609
1610 if (strchr(string, '/') != NULL)
1611 *strchr(string, '/') = '\0';
1612
1613 /*
1614 * Assign the default value as appropriate...
1615 */
1616
1617 if (!strcmp(keyword, "DefaultColorSpace"))
1618 {
1619 /*
1620 * Set default colorspace...
1621 */
1622
1623 if (!strcmp(string, "CMY"))
1624 ppd->colorspace = PPD_CS_CMY;
1625 else if (!strcmp(string, "CMYK"))
1626 ppd->colorspace = PPD_CS_CMYK;
1627 else if (!strcmp(string, "RGB"))
1628 ppd->colorspace = PPD_CS_RGB;
1629 else if (!strcmp(string, "RGBK"))
1630 ppd->colorspace = PPD_CS_RGBK;
1631 else if (!strcmp(string, "N"))
1632 ppd->colorspace = PPD_CS_N;
1633 else
1634 ppd->colorspace = PPD_CS_GRAY;
1635 }
1636 else if (option && !strcmp(keyword + 7, option->keyword))
1637 {
1638 /*
1639 * Set the default as part of the current option...
1640 */
1641
1642 DEBUG_printf(("2_ppdOpen: Setting %s to %s...", keyword, string));
1643
1644 strlcpy(option->defchoice, string, sizeof(option->defchoice));
1645
1646 DEBUG_printf(("2_ppdOpen: %s is now %s...", keyword, option->defchoice));
1647 }
1648 else
1649 {
1650 /*
1651 * Lookup option and set if it has been defined...
1652 */
1653
1654 ppd_option_t *toption; /* Temporary option */
1655
1656
1657 if ((toption = ppdFindOption(ppd, keyword + 7)) != NULL)
1658 {
1659 DEBUG_printf(("2_ppdOpen: Setting %s to %s...", keyword, string));
1660 strlcpy(toption->defchoice, string, sizeof(toption->defchoice));
1661 }
1662 }
1663 }
1664 else if (!strcmp(keyword, "UIConstraints") ||
1665 !strcmp(keyword, "NonUIConstraints"))
1666 {
1667 if (!string)
1668 {
1669 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1670 goto error;
1671 }
1672
1673 if (ppd->num_consts == 0)
1674 constraint = calloc(2, sizeof(ppd_const_t));
1675 else
1676 constraint = realloc(ppd->consts, (size_t)(ppd->num_consts + 2) * sizeof(ppd_const_t));
1677
1678 if (constraint == NULL)
1679 {
1680 pg->ppd_status = PPD_ALLOC_ERROR;
1681
1682 goto error;
1683 }
1684
1685 ppd->consts = constraint;
1686 constraint += ppd->num_consts;
1687 ppd->num_consts ++;
1688
1689 switch (sscanf(string, "%40s%40s%40s%40s", constraint->option1,
1690 constraint->choice1, constraint->option2,
1691 constraint->choice2))
1692 {
1693 case 0 : /* Error */
1694 case 1 : /* Error */
1695 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1696 goto error;
1697
1698 case 2 : /* Two options... */
1699 /*
1700 * Check for broken constraints like "* Option"...
1701 */
1702
1703 if (pg->ppd_conform == PPD_CONFORM_STRICT &&
1704 (!strcmp(constraint->option1, "*") ||
1705 !strcmp(constraint->choice1, "*")))
1706 {
1707 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1708 goto error;
1709 }
1710
1711 /*
1712 * The following strcpy's are safe, as optionN and
1713 * choiceN are all the same size (size defined by PPD spec...)
1714 */
1715
1716 if (constraint->option1[0] == '*')
1717 _cups_strcpy(constraint->option1, constraint->option1 + 1);
1718 else if (pg->ppd_conform == PPD_CONFORM_STRICT)
1719 {
1720 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1721 goto error;
1722 }
1723
1724 if (constraint->choice1[0] == '*')
1725 _cups_strcpy(constraint->option2, constraint->choice1 + 1);
1726 else if (pg->ppd_conform == PPD_CONFORM_STRICT)
1727 {
1728 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1729 goto error;
1730 }
1731
1732 constraint->choice1[0] = '\0';
1733 constraint->choice2[0] = '\0';
1734 break;
1735
1736 case 3 : /* Two options, one choice... */
1737 /*
1738 * Check for broken constraints like "* Option"...
1739 */
1740
1741 if (pg->ppd_conform == PPD_CONFORM_STRICT &&
1742 (!strcmp(constraint->option1, "*") ||
1743 !strcmp(constraint->choice1, "*") ||
1744 !strcmp(constraint->option2, "*")))
1745 {
1746 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1747 goto error;
1748 }
1749
1750 /*
1751 * The following _cups_strcpy's are safe, as optionN and
1752 * choiceN are all the same size (size defined by PPD spec...)
1753 */
1754
1755 if (constraint->option1[0] == '*')
1756 _cups_strcpy(constraint->option1, constraint->option1 + 1);
1757 else if (pg->ppd_conform == PPD_CONFORM_STRICT)
1758 {
1759 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1760 goto error;
1761 }
1762
1763 if (constraint->choice1[0] == '*')
1764 {
1765 if (pg->ppd_conform == PPD_CONFORM_STRICT &&
1766 constraint->option2[0] == '*')
1767 {
1768 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1769 goto error;
1770 }
1771
1772 _cups_strcpy(constraint->choice2, constraint->option2);
1773 _cups_strcpy(constraint->option2, constraint->choice1 + 1);
1774 constraint->choice1[0] = '\0';
1775 }
1776 else
1777 {
1778 if (constraint->option2[0] == '*')
1779 _cups_strcpy(constraint->option2, constraint->option2 + 1);
1780 else if (pg->ppd_conform == PPD_CONFORM_STRICT)
1781 {
1782 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1783 goto error;
1784 }
1785
1786 constraint->choice2[0] = '\0';
1787 }
1788 break;
1789
1790 case 4 : /* Two options, two choices... */
1791 /*
1792 * Check for broken constraints like "* Option"...
1793 */
1794
1795 if (pg->ppd_conform == PPD_CONFORM_STRICT &&
1796 (!strcmp(constraint->option1, "*") ||
1797 !strcmp(constraint->choice1, "*") ||
1798 !strcmp(constraint->option2, "*") ||
1799 !strcmp(constraint->choice2, "*")))
1800 {
1801 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1802 goto error;
1803 }
1804
1805 if (constraint->option1[0] == '*')
1806 _cups_strcpy(constraint->option1, constraint->option1 + 1);
1807 else if (pg->ppd_conform == PPD_CONFORM_STRICT)
1808 {
1809 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1810 goto error;
1811 }
1812
1813 if (pg->ppd_conform == PPD_CONFORM_STRICT &&
1814 constraint->choice1[0] == '*')
1815 {
1816 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1817 goto error;
1818 }
1819
1820 if (constraint->option2[0] == '*')
1821 _cups_strcpy(constraint->option2, constraint->option2 + 1);
1822 else if (pg->ppd_conform == PPD_CONFORM_STRICT)
1823 {
1824 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1825 goto error;
1826 }
1827
1828 if (pg->ppd_conform == PPD_CONFORM_STRICT &&
1829 constraint->choice2[0] == '*')
1830 {
1831 pg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1832 goto error;
1833 }
1834 break;
1835 }
1836
1837 /*
1838 * Don't add this one as an attribute...
1839 */
1840
1841 free(string);
1842 string = NULL;
1843 }
1844 else if (!strcmp(keyword, "PaperDimension"))
1845 {
1846 if ((size = ppdPageSize(ppd, name)) == NULL)
1847 size = ppd_add_size(ppd, name);
1848
1849 if (size == NULL)
1850 {
1851 /*
1852 * Unable to add or find size!
1853 */
1854
1855 pg->ppd_status = PPD_ALLOC_ERROR;
1856
1857 goto error;
1858 }
1859
1860 size->width = (float)_cupsStrScand(string, &sptr, loc);
1861 size->length = (float)_cupsStrScand(sptr, NULL, loc);
1862
1863 free(string);
1864 string = NULL;
1865 }
1866 else if (!strcmp(keyword, "ImageableArea"))
1867 {
1868 if ((size = ppdPageSize(ppd, name)) == NULL)
1869 size = ppd_add_size(ppd, name);
1870
1871 if (size == NULL)
1872 {
1873 /*
1874 * Unable to add or find size!
1875 */
1876
1877 pg->ppd_status = PPD_ALLOC_ERROR;
1878
1879 goto error;
1880 }
1881
1882 size->left = (float)_cupsStrScand(string, &sptr, loc);
1883 size->bottom = (float)_cupsStrScand(sptr, &sptr, loc);
1884 size->right = (float)_cupsStrScand(sptr, &sptr, loc);
1885 size->top = (float)_cupsStrScand(sptr, NULL, loc);
1886
1887 free(string);
1888 string = NULL;
1889 }
1890 else if (option != NULL &&
1891 (mask & (PPD_KEYWORD | PPD_OPTION | PPD_STRING)) ==
1892 (PPD_KEYWORD | PPD_OPTION | PPD_STRING) &&
1893 !strcmp(keyword, option->keyword))
1894 {
1895 DEBUG_printf(("2_ppdOpen: group=%p, subgroup=%p", group, subgroup));
1896
1897 if (!strcmp(keyword, "PageSize"))
1898 {
1899 /*
1900 * Add a page size...
1901 */
1902
1903 if (ppdPageSize(ppd, name) == NULL)
1904 ppd_add_size(ppd, name);
1905 }
1906
1907 /*
1908 * Add the option choice...
1909 */
1910
1911 if ((choice = ppd_add_choice(option, name)) == NULL)
1912 {
1913 pg->ppd_status = PPD_ALLOC_ERROR;
1914
1915 goto error;
1916 }
1917
1918 if (text[0])
1919 cupsCharsetToUTF8((cups_utf8_t *)choice->text, text,
1920 sizeof(choice->text), encoding);
1921 else if (!strcmp(name, "True"))
1922 strlcpy(choice->text, _("Yes"), sizeof(choice->text));
1923 else if (!strcmp(name, "False"))
1924 strlcpy(choice->text, _("No"), sizeof(choice->text));
1925 else
1926 strlcpy(choice->text, name, sizeof(choice->text));
1927
1928 if (option->section == PPD_ORDER_JCL)
1929 ppd_decode(string); /* Decode quoted string */
1930
1931 choice->code = string;
1932 string = NULL; /* Don't add as an attribute below */
1933 }
1934
1935 /*
1936 * Add remaining lines with keywords and string values as attributes...
1937 */
1938
1939 if (string &&
1940 (mask & (PPD_KEYWORD | PPD_STRING)) == (PPD_KEYWORD | PPD_STRING))
1941 ppd_add_attr(ppd, keyword, name, text, string);
1942 else
1943 free(string);
1944 }
1945
1946 /*
1947 * Check for a missing CloseUI/JCLCloseUI...
1948 */
1949
1950 if (option && pg->ppd_conform == PPD_CONFORM_STRICT)
1951 {
1952 pg->ppd_status = PPD_MISSING_CLOSE_UI;
1953 goto error;
1954 }
1955
1956 /*
1957 * Check for a missing CloseGroup...
1958 */
1959
1960 if (group && pg->ppd_conform == PPD_CONFORM_STRICT)
1961 {
1962 pg->ppd_status = PPD_MISSING_CLOSE_GROUP;
1963 goto error;
1964 }
1965
1966 free(line.buffer);
1967
1968 /*
1969 * Reset language preferences...
1970 */
1971
1972 #ifdef DEBUG
1973 if (!cupsFileEOF(fp))
1974 DEBUG_printf(("1_ppdOpen: Premature EOF at %lu...\n",
1975 (unsigned long)cupsFileTell(fp)));
1976 #endif /* DEBUG */
1977
1978 if (pg->ppd_status != PPD_OK)
1979 {
1980 /*
1981 * Had an error reading the PPD file, cannot continue!
1982 */
1983
1984 ppdClose(ppd);
1985
1986 return (NULL);
1987 }
1988
1989 /*
1990 * Update the filters array as needed...
1991 */
1992
1993 if (!ppd_update_filters(ppd, pg))
1994 {
1995 ppdClose(ppd);
1996
1997 return (NULL);
1998 }
1999
2000 /*
2001 * Create the sorted options array and set the option back-pointer for
2002 * each choice and custom option...
2003 */
2004
2005 ppd->options = cupsArrayNew2((cups_array_func_t)ppd_compare_options, NULL,
2006 (cups_ahash_func_t)ppd_hash_option,
2007 PPD_HASHSIZE);
2008
2009 for (i = ppd->num_groups, group = ppd->groups;
2010 i > 0;
2011 i --, group ++)
2012 {
2013 for (j = group->num_options, option = group->options;
2014 j > 0;
2015 j --, option ++)
2016 {
2017 ppd_coption_t *coption; /* Custom option */
2018
2019
2020 cupsArrayAdd(ppd->options, option);
2021
2022 for (k = 0; k < option->num_choices; k ++)
2023 option->choices[k].option = option;
2024
2025 if ((coption = ppdFindCustomOption(ppd, option->keyword)) != NULL)
2026 coption->option = option;
2027 }
2028 }
2029
2030 /*
2031 * Create an array to track the marked choices...
2032 */
2033
2034 ppd->marked = cupsArrayNew((cups_array_func_t)ppd_compare_choices, NULL);
2035
2036 /*
2037 * Return the PPD file structure...
2038 */
2039
2040 return (ppd);
2041
2042 /*
2043 * Common exit point for errors to save code size...
2044 */
2045
2046 error:
2047
2048 free(string);
2049 free(line.buffer);
2050
2051 ppdClose(ppd);
2052
2053 return (NULL);
2054 }
2055
2056
2057 /*
2058 * 'ppdOpen()' - Read a PPD file into memory.
2059 */
2060
2061 ppd_file_t * /* O - PPD file record */
2062 ppdOpen(FILE *fp) /* I - File to read from */
2063 {
2064 ppd_file_t *ppd; /* PPD file record */
2065 cups_file_t *cf; /* CUPS file */
2066
2067
2068 /*
2069 * Reopen the stdio file as a CUPS file...
2070 */
2071
2072 if ((cf = cupsFileOpenFd(fileno(fp), "r")) == NULL)
2073 return (NULL);
2074
2075 /*
2076 * Load the PPD file using the newer API...
2077 */
2078
2079 ppd = _ppdOpen(cf, _PPD_LOCALIZATION_DEFAULT);
2080
2081 /*
2082 * Close the CUPS file and return the PPD...
2083 */
2084
2085 cupsFileClose(cf);
2086
2087 return (ppd);
2088 }
2089
2090
2091 /*
2092 * 'ppdOpen2()' - Read a PPD file into memory.
2093 *
2094 * @since CUPS 1.2/macOS 10.5@
2095 */
2096
2097 ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
2098 ppdOpen2(cups_file_t *fp) /* I - File to read from */
2099 {
2100 return _ppdOpen(fp, _PPD_LOCALIZATION_DEFAULT);
2101 }
2102
2103
2104 /*
2105 * 'ppdOpenFd()' - Read a PPD file into memory.
2106 */
2107
2108 ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
2109 ppdOpenFd(int fd) /* I - File to read from */
2110 {
2111 cups_file_t *fp; /* CUPS file pointer */
2112 ppd_file_t *ppd; /* PPD file record */
2113 _ppd_globals_t *pg = _ppdGlobals();
2114 /* Global data */
2115
2116
2117 /*
2118 * Set the line number to 0...
2119 */
2120
2121 pg->ppd_line = 0;
2122
2123 /*
2124 * Range check input...
2125 */
2126
2127 if (fd < 0)
2128 {
2129 pg->ppd_status = PPD_NULL_FILE;
2130
2131 return (NULL);
2132 }
2133
2134 /*
2135 * Try to open the file and parse it...
2136 */
2137
2138 if ((fp = cupsFileOpenFd(fd, "r")) != NULL)
2139 {
2140 ppd = ppdOpen2(fp);
2141
2142 cupsFileClose(fp);
2143 }
2144 else
2145 {
2146 pg->ppd_status = PPD_FILE_OPEN_ERROR;
2147 ppd = NULL;
2148 }
2149
2150 return (ppd);
2151 }
2152
2153
2154 /*
2155 * '_ppdOpenFile()' - Read a PPD file into memory.
2156 */
2157
2158 ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
2159 _ppdOpenFile(const char *filename, /* I - File to read from */
2160 _ppd_localization_t localization) /* I - Localization to load */
2161 {
2162 cups_file_t *fp; /* File pointer */
2163 ppd_file_t *ppd; /* PPD file record */
2164 _ppd_globals_t *pg = _ppdGlobals();
2165 /* Global data */
2166
2167
2168 /*
2169 * Set the line number to 0...
2170 */
2171
2172 pg->ppd_line = 0;
2173
2174 /*
2175 * Range check input...
2176 */
2177
2178 if (filename == NULL)
2179 {
2180 pg->ppd_status = PPD_NULL_FILE;
2181
2182 return (NULL);
2183 }
2184
2185 /*
2186 * Try to open the file and parse it...
2187 */
2188
2189 if ((fp = cupsFileOpen(filename, "r")) != NULL)
2190 {
2191 ppd = _ppdOpen(fp, localization);
2192
2193 cupsFileClose(fp);
2194 }
2195 else
2196 {
2197 pg->ppd_status = PPD_FILE_OPEN_ERROR;
2198 ppd = NULL;
2199 }
2200
2201 return (ppd);
2202 }
2203
2204
2205 /*
2206 * 'ppdOpenFile()' - Read a PPD file into memory.
2207 */
2208
2209 ppd_file_t * /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
2210 ppdOpenFile(const char *filename) /* I - File to read from */
2211 {
2212 return _ppdOpenFile(filename, _PPD_LOCALIZATION_DEFAULT);
2213 }
2214
2215
2216 /*
2217 * 'ppdSetConformance()' - Set the conformance level for PPD files.
2218 *
2219 * @since CUPS 1.1.20/macOS 10.4@
2220 */
2221
2222 void
2223 ppdSetConformance(ppd_conform_t c) /* I - Conformance level */
2224 {
2225 _ppd_globals_t *pg = _ppdGlobals();
2226 /* Global data */
2227
2228
2229 pg->ppd_conform = c;
2230 }
2231
2232
2233 /*
2234 * 'ppd_add_attr()' - Add an attribute to the PPD data.
2235 */
2236
2237 static ppd_attr_t * /* O - New attribute */
2238 ppd_add_attr(ppd_file_t *ppd, /* I - PPD file data */
2239 const char *name, /* I - Attribute name */
2240 const char *spec, /* I - Specifier string, if any */
2241 const char *text, /* I - Text string, if any */
2242 const char *value) /* I - Value of attribute */
2243 {
2244 ppd_attr_t **ptr, /* New array */
2245 *temp; /* New attribute */
2246
2247
2248 /*
2249 * Range check input...
2250 */
2251
2252 if (ppd == NULL || name == NULL || spec == NULL)
2253 return (NULL);
2254
2255 /*
2256 * Create the array as needed...
2257 */
2258
2259 if (!ppd->sorted_attrs)
2260 ppd->sorted_attrs = cupsArrayNew((cups_array_func_t)ppd_compare_attrs,
2261 NULL);
2262
2263 /*
2264 * Allocate memory for the new attribute...
2265 */
2266
2267 if (ppd->num_attrs == 0)
2268 ptr = malloc(sizeof(ppd_attr_t *));
2269 else
2270 ptr = realloc(ppd->attrs, (size_t)(ppd->num_attrs + 1) * sizeof(ppd_attr_t *));
2271
2272 if (ptr == NULL)
2273 return (NULL);
2274
2275 ppd->attrs = ptr;
2276 ptr += ppd->num_attrs;
2277
2278 if ((temp = calloc(1, sizeof(ppd_attr_t))) == NULL)
2279 return (NULL);
2280
2281 *ptr = temp;
2282
2283 ppd->num_attrs ++;
2284
2285 /*
2286 * Copy data over...
2287 */
2288
2289 strlcpy(temp->name, name, sizeof(temp->name));
2290 strlcpy(temp->spec, spec, sizeof(temp->spec));
2291 strlcpy(temp->text, text, sizeof(temp->text));
2292 temp->value = (char *)value;
2293
2294 /*
2295 * Add the attribute to the sorted array...
2296 */
2297
2298 cupsArrayAdd(ppd->sorted_attrs, temp);
2299
2300 /*
2301 * Return the attribute...
2302 */
2303
2304 return (temp);
2305 }
2306
2307
2308 /*
2309 * 'ppd_add_choice()' - Add a choice to an option.
2310 */
2311
2312 static ppd_choice_t * /* O - Named choice */
2313 ppd_add_choice(ppd_option_t *option, /* I - Option */
2314 const char *name) /* I - Name of choice */
2315 {
2316 ppd_choice_t *choice; /* Choice */
2317
2318
2319 if (option->num_choices == 0)
2320 choice = malloc(sizeof(ppd_choice_t));
2321 else
2322 choice = realloc(option->choices, sizeof(ppd_choice_t) * (size_t)(option->num_choices + 1));
2323
2324 if (choice == NULL)
2325 return (NULL);
2326
2327 option->choices = choice;
2328 choice += option->num_choices;
2329 option->num_choices ++;
2330
2331 memset(choice, 0, sizeof(ppd_choice_t));
2332 strlcpy(choice->choice, name, sizeof(choice->choice));
2333
2334 return (choice);
2335 }
2336
2337
2338 /*
2339 * 'ppd_add_size()' - Add a page size.
2340 */
2341
2342 static ppd_size_t * /* O - Named size */
2343 ppd_add_size(ppd_file_t *ppd, /* I - PPD file */
2344 const char *name) /* I - Name of size */
2345 {
2346 ppd_size_t *size; /* Size */
2347
2348
2349 if (ppd->num_sizes == 0)
2350 size = malloc(sizeof(ppd_size_t));
2351 else
2352 size = realloc(ppd->sizes, sizeof(ppd_size_t) * (size_t)(ppd->num_sizes + 1));
2353
2354 if (size == NULL)
2355 return (NULL);
2356
2357 ppd->sizes = size;
2358 size += ppd->num_sizes;
2359 ppd->num_sizes ++;
2360
2361 memset(size, 0, sizeof(ppd_size_t));
2362 strlcpy(size->name, name, sizeof(size->name));
2363
2364 return (size);
2365 }
2366
2367
2368 /*
2369 * 'ppd_compare_attrs()' - Compare two attributes.
2370 */
2371
2372 static int /* O - Result of comparison */
2373 ppd_compare_attrs(ppd_attr_t *a, /* I - First attribute */
2374 ppd_attr_t *b) /* I - Second attribute */
2375 {
2376 return (_cups_strcasecmp(a->name, b->name));
2377 }
2378
2379
2380 /*
2381 * 'ppd_compare_choices()' - Compare two choices...
2382 */
2383
2384 static int /* O - Result of comparison */
2385 ppd_compare_choices(ppd_choice_t *a, /* I - First choice */
2386 ppd_choice_t *b) /* I - Second choice */
2387 {
2388 return (strcmp(a->option->keyword, b->option->keyword));
2389 }
2390
2391
2392 /*
2393 * 'ppd_compare_coptions()' - Compare two custom options.
2394 */
2395
2396 static int /* O - Result of comparison */
2397 ppd_compare_coptions(ppd_coption_t *a, /* I - First option */
2398 ppd_coption_t *b) /* I - Second option */
2399 {
2400 return (_cups_strcasecmp(a->keyword, b->keyword));
2401 }
2402
2403
2404 /*
2405 * 'ppd_compare_options()' - Compare two options.
2406 */
2407
2408 static int /* O - Result of comparison */
2409 ppd_compare_options(ppd_option_t *a, /* I - First option */
2410 ppd_option_t *b) /* I - Second option */
2411 {
2412 return (_cups_strcasecmp(a->keyword, b->keyword));
2413 }
2414
2415
2416 /*
2417 * 'ppd_decode()' - Decode a string value...
2418 */
2419
2420 static int /* O - Length of decoded string */
2421 ppd_decode(char *string) /* I - String to decode */
2422 {
2423 char *inptr, /* Input pointer */
2424 *outptr; /* Output pointer */
2425
2426
2427 inptr = string;
2428 outptr = string;
2429
2430 while (*inptr != '\0')
2431 if (*inptr == '<' && isxdigit(inptr[1] & 255))
2432 {
2433 /*
2434 * Convert hex to 8-bit values...
2435 */
2436
2437 inptr ++;
2438 while (isxdigit(*inptr & 255))
2439 {
2440 if (_cups_isalpha(*inptr))
2441 *outptr = (char)((tolower(*inptr) - 'a' + 10) << 4);
2442 else
2443 *outptr = (char)((*inptr - '0') << 4);
2444
2445 inptr ++;
2446
2447 if (!isxdigit(*inptr & 255))
2448 break;
2449
2450 if (_cups_isalpha(*inptr))
2451 *outptr |= (char)(tolower(*inptr) - 'a' + 10);
2452 else
2453 *outptr |= (char)(*inptr - '0');
2454
2455 inptr ++;
2456 outptr ++;
2457 }
2458
2459 while (*inptr != '>' && *inptr != '\0')
2460 inptr ++;
2461 while (*inptr == '>')
2462 inptr ++;
2463 }
2464 else
2465 *outptr++ = *inptr++;
2466
2467 *outptr = '\0';
2468
2469 return ((int)(outptr - string));
2470 }
2471
2472
2473 /*
2474 * 'ppd_free_filters()' - Free the filters array.
2475 */
2476
2477 static void
2478 ppd_free_filters(ppd_file_t *ppd) /* I - PPD file */
2479 {
2480 int i; /* Looping var */
2481 char **filter; /* Current filter */
2482
2483
2484 if (ppd->num_filters > 0)
2485 {
2486 for (i = ppd->num_filters, filter = ppd->filters; i > 0; i --, filter ++)
2487 free(*filter);
2488
2489 free(ppd->filters);
2490
2491 ppd->num_filters = 0;
2492 ppd->filters = NULL;
2493 }
2494 }
2495
2496
2497 /*
2498 * 'ppd_free_group()' - Free a single UI group.
2499 */
2500
2501 static void
2502 ppd_free_group(ppd_group_t *group) /* I - Group to free */
2503 {
2504 int i; /* Looping var */
2505 ppd_option_t *option; /* Current option */
2506 ppd_group_t *subgroup; /* Current sub-group */
2507
2508
2509 if (group->num_options > 0)
2510 {
2511 for (i = group->num_options, option = group->options;
2512 i > 0;
2513 i --, option ++)
2514 ppd_free_option(option);
2515
2516 free(group->options);
2517 }
2518
2519 if (group->num_subgroups > 0)
2520 {
2521 for (i = group->num_subgroups, subgroup = group->subgroups;
2522 i > 0;
2523 i --, subgroup ++)
2524 ppd_free_group(subgroup);
2525
2526 free(group->subgroups);
2527 }
2528 }
2529
2530
2531 /*
2532 * 'ppd_free_option()' - Free a single option.
2533 */
2534
2535 static void
2536 ppd_free_option(ppd_option_t *option) /* I - Option to free */
2537 {
2538 int i; /* Looping var */
2539 ppd_choice_t *choice; /* Current choice */
2540
2541
2542 if (option->num_choices > 0)
2543 {
2544 for (i = option->num_choices, choice = option->choices;
2545 i > 0;
2546 i --, choice ++)
2547 {
2548 free(choice->code);
2549 }
2550
2551 free(option->choices);
2552 }
2553 }
2554
2555
2556 /*
2557 * 'ppd_get_coption()' - Get a custom option record.
2558 */
2559
2560 static ppd_coption_t * /* O - Custom option... */
2561 ppd_get_coption(ppd_file_t *ppd, /* I - PPD file */
2562 const char *name) /* I - Name of option */
2563 {
2564 ppd_coption_t *copt; /* New custom option */
2565
2566
2567 /*
2568 * See if the option already exists...
2569 */
2570
2571 if ((copt = ppdFindCustomOption(ppd, name)) != NULL)
2572 return (copt);
2573
2574 /*
2575 * Not found, so create the custom option record...
2576 */
2577
2578 if ((copt = calloc(1, sizeof(ppd_coption_t))) == NULL)
2579 return (NULL);
2580
2581 strlcpy(copt->keyword, name, sizeof(copt->keyword));
2582
2583 copt->params = cupsArrayNew((cups_array_func_t)NULL, NULL);
2584
2585 cupsArrayAdd(ppd->coptions, copt);
2586
2587 /*
2588 * Return the new record...
2589 */
2590
2591 return (copt);
2592 }
2593
2594
2595 /*
2596 * 'ppd_get_cparam()' - Get a custom parameter record.
2597 */
2598
2599 static ppd_cparam_t * /* O - Extended option... */
2600 ppd_get_cparam(ppd_coption_t *opt, /* I - PPD file */
2601 const char *param, /* I - Name of parameter */
2602 const char *text) /* I - Human-readable text */
2603 {
2604 ppd_cparam_t *cparam; /* New custom parameter */
2605
2606
2607 /*
2608 * See if the parameter already exists...
2609 */
2610
2611 if ((cparam = ppdFindCustomParam(opt, param)) != NULL)
2612 return (cparam);
2613
2614 /*
2615 * Not found, so create the custom parameter record...
2616 */
2617
2618 if ((cparam = calloc(1, sizeof(ppd_cparam_t))) == NULL)
2619 return (NULL);
2620
2621 strlcpy(cparam->name, param, sizeof(cparam->name));
2622 strlcpy(cparam->text, text[0] ? text : param, sizeof(cparam->text));
2623
2624 /*
2625 * Add this record to the array...
2626 */
2627
2628 cupsArrayAdd(opt->params, cparam);
2629
2630 /*
2631 * Return the new record...
2632 */
2633
2634 return (cparam);
2635 }
2636
2637
2638 /*
2639 * 'ppd_get_group()' - Find or create the named group as needed.
2640 */
2641
2642 static ppd_group_t * /* O - Named group */
2643 ppd_get_group(ppd_file_t *ppd, /* I - PPD file */
2644 const char *name, /* I - Name of group */
2645 const char *text, /* I - Text for group */
2646 _ppd_globals_t *pg, /* I - Global data */
2647 cups_encoding_t encoding) /* I - Encoding of text */
2648 {
2649 int i; /* Looping var */
2650 ppd_group_t *group; /* Group */
2651
2652
2653 DEBUG_printf(("7ppd_get_group(ppd=%p, name=\"%s\", text=\"%s\", cg=%p)",
2654 ppd, name, text, pg));
2655
2656 for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
2657 if (!strcmp(group->name, name))
2658 break;
2659
2660 if (i == 0)
2661 {
2662 DEBUG_printf(("8ppd_get_group: Adding group %s...", name));
2663
2664 if (pg->ppd_conform == PPD_CONFORM_STRICT && strlen(text) >= sizeof(group->text))
2665 {
2666 pg->ppd_status = PPD_ILLEGAL_TRANSLATION;
2667
2668 return (NULL);
2669 }
2670
2671 if (ppd->num_groups == 0)
2672 group = malloc(sizeof(ppd_group_t));
2673 else
2674 group = realloc(ppd->groups, (size_t)(ppd->num_groups + 1) * sizeof(ppd_group_t));
2675
2676 if (group == NULL)
2677 {
2678 pg->ppd_status = PPD_ALLOC_ERROR;
2679
2680 return (NULL);
2681 }
2682
2683 ppd->groups = group;
2684 group += ppd->num_groups;
2685 ppd->num_groups ++;
2686
2687 memset(group, 0, sizeof(ppd_group_t));
2688 strlcpy(group->name, name, sizeof(group->name));
2689
2690 cupsCharsetToUTF8((cups_utf8_t *)group->text, text,
2691 sizeof(group->text), encoding);
2692 }
2693
2694 return (group);
2695 }
2696
2697
2698 /*
2699 * 'ppd_get_option()' - Find or create the named option as needed.
2700 */
2701
2702 static ppd_option_t * /* O - Named option */
2703 ppd_get_option(ppd_group_t *group, /* I - Group */
2704 const char *name) /* I - Name of option */
2705 {
2706 int i; /* Looping var */
2707 ppd_option_t *option; /* Option */
2708
2709
2710 DEBUG_printf(("7ppd_get_option(group=%p(\"%s\"), name=\"%s\")",
2711 group, group->name, name));
2712
2713 for (i = group->num_options, option = group->options; i > 0; i --, option ++)
2714 if (!strcmp(option->keyword, name))
2715 break;
2716
2717 if (i == 0)
2718 {
2719 if (group->num_options == 0)
2720 option = malloc(sizeof(ppd_option_t));
2721 else
2722 option = realloc(group->options, (size_t)(group->num_options + 1) * sizeof(ppd_option_t));
2723
2724 if (option == NULL)
2725 return (NULL);
2726
2727 group->options = option;
2728 option += group->num_options;
2729 group->num_options ++;
2730
2731 memset(option, 0, sizeof(ppd_option_t));
2732 strlcpy(option->keyword, name, sizeof(option->keyword));
2733 }
2734
2735 return (option);
2736 }
2737
2738
2739 /*
2740 * 'ppd_globals_alloc()' - Allocate and initialize global data.
2741 */
2742
2743 static _ppd_globals_t * /* O - Pointer to global data */
2744 ppd_globals_alloc(void)
2745 {
2746 return ((_ppd_globals_t *)calloc(1, sizeof(_ppd_globals_t)));
2747 }
2748
2749
2750 /*
2751 * 'ppd_globals_free()' - Free global data.
2752 */
2753
2754 #if defined(HAVE_PTHREAD_H) || defined(_WIN32)
2755 static void
2756 ppd_globals_free(_ppd_globals_t *pg) /* I - Pointer to global data */
2757 {
2758 free(pg);
2759 }
2760 #endif /* HAVE_PTHREAD_H || _WIN32 */
2761
2762
2763 #ifdef HAVE_PTHREAD_H
2764 /*
2765 * 'ppd_globals_init()' - Initialize per-thread globals...
2766 */
2767
2768 static void
2769 ppd_globals_init(void)
2770 {
2771 /*
2772 * Register the global data for this thread...
2773 */
2774
2775 pthread_key_create(&ppd_globals_key, (void (*)(void *))ppd_globals_free);
2776 }
2777 #endif /* HAVE_PTHREAD_H */
2778
2779
2780 /*
2781 * 'ppd_hash_option()' - Generate a hash of the option name...
2782 */
2783
2784 static int /* O - Hash index */
2785 ppd_hash_option(ppd_option_t *option) /* I - Option */
2786 {
2787 int hash = 0; /* Hash index */
2788 const char *k; /* Pointer into keyword */
2789
2790
2791 for (hash = option->keyword[0], k = option->keyword + 1; *k;)
2792 hash = 33 * hash + *k++;
2793
2794 return (hash & 511);
2795 }
2796
2797
2798 /*
2799 * 'ppd_read()' - Read a line from a PPD file, skipping comment lines as
2800 * necessary.
2801 */
2802
2803 static int /* O - Bitmask of fields read */
2804 ppd_read(cups_file_t *fp, /* I - File to read from */
2805 _ppd_line_t *line, /* I - Line buffer */
2806 char *keyword, /* O - Keyword from line */
2807 char *option, /* O - Option from line */
2808 char *text, /* O - Human-readable text from line */
2809 char **string, /* O - Code/string data */
2810 int ignoreblank, /* I - Ignore blank lines? */
2811 _ppd_globals_t *pg) /* I - Global data */
2812 {
2813 int ch, /* Character from file */
2814 col, /* Column in line */
2815 colon, /* Colon seen? */
2816 endquote, /* Waiting for an end quote */
2817 mask, /* Mask to be returned */
2818 startline, /* Start line */
2819 textlen; /* Length of text */
2820 char *keyptr, /* Keyword pointer */
2821 *optptr, /* Option pointer */
2822 *textptr, /* Text pointer */
2823 *strptr, /* Pointer into string */
2824 *lineptr; /* Current position in line buffer */
2825
2826
2827 /*
2828 * Now loop until we have a valid line...
2829 */
2830
2831 *string = NULL;
2832 col = 0;
2833 startline = pg->ppd_line + 1;
2834
2835 if (!line->buffer)
2836 {
2837 line->bufsize = 1024;
2838 line->buffer = malloc(1024);
2839
2840 if (!line->buffer)
2841 return (0);
2842 }
2843
2844 do
2845 {
2846 /*
2847 * Read the line...
2848 */
2849
2850 lineptr = line->buffer;
2851 endquote = 0;
2852 colon = 0;
2853
2854 while ((ch = cupsFileGetChar(fp)) != EOF)
2855 {
2856 if (lineptr >= (line->buffer + line->bufsize - 1))
2857 {
2858 /*
2859 * Expand the line buffer...
2860 */
2861
2862 char *temp; /* Temporary line pointer */
2863
2864
2865 line->bufsize += 1024;
2866 if (line->bufsize > 262144)
2867 {
2868 /*
2869 * Don't allow lines longer than 256k!
2870 */
2871
2872 pg->ppd_line = startline;
2873 pg->ppd_status = PPD_LINE_TOO_LONG;
2874
2875 return (0);
2876 }
2877
2878 temp = realloc(line->buffer, line->bufsize);
2879 if (!temp)
2880 {
2881 pg->ppd_line = startline;
2882 pg->ppd_status = PPD_LINE_TOO_LONG;
2883
2884 return (0);
2885 }
2886
2887 lineptr = temp + (lineptr - line->buffer);
2888 line->buffer = temp;
2889 }
2890
2891 if (ch == '\r' || ch == '\n')
2892 {
2893 /*
2894 * Line feed or carriage return...
2895 */
2896
2897 pg->ppd_line ++;
2898 col = 0;
2899
2900 if (ch == '\r')
2901 {
2902 /*
2903 * Check for a trailing line feed...
2904 */
2905
2906 if ((ch = cupsFilePeekChar(fp)) == EOF)
2907 {
2908 ch = '\n';
2909 break;
2910 }
2911
2912 if (ch == 0x0a)
2913 cupsFileGetChar(fp);
2914 }
2915
2916 if (lineptr == line->buffer && ignoreblank)
2917 continue; /* Skip blank lines */
2918
2919 ch = '\n';
2920
2921 if (!endquote) /* Continue for multi-line text */
2922 break;
2923
2924 *lineptr++ = '\n';
2925 }
2926 else if (ch < ' ' && ch != '\t' && pg->ppd_conform == PPD_CONFORM_STRICT)
2927 {
2928 /*
2929 * Other control characters...
2930 */
2931
2932 pg->ppd_line = startline;
2933 pg->ppd_status = PPD_ILLEGAL_CHARACTER;
2934
2935 return (0);
2936 }
2937 else if (ch != 0x1a)
2938 {
2939 /*
2940 * Any other character...
2941 */
2942
2943 *lineptr++ = (char)ch;
2944 col ++;
2945
2946 if (col > (PPD_MAX_LINE - 1))
2947 {
2948 /*
2949 * Line is too long...
2950 */
2951
2952 pg->ppd_line = startline;
2953 pg->ppd_status = PPD_LINE_TOO_LONG;
2954
2955 return (0);
2956 }
2957
2958 if (ch == ':' && strncmp(line->buffer, "*%", 2) != 0)
2959 colon = 1;
2960
2961 if (ch == '\"' && colon)
2962 endquote = !endquote;
2963 }
2964 }
2965
2966 if (endquote)
2967 {
2968 /*
2969 * Didn't finish this quoted string...
2970 */
2971
2972 while ((ch = cupsFileGetChar(fp)) != EOF)
2973 if (ch == '\"')
2974 break;
2975 else if (ch == '\r' || ch == '\n')
2976 {
2977 pg->ppd_line ++;
2978 col = 0;
2979
2980 if (ch == '\r')
2981 {
2982 /*
2983 * Check for a trailing line feed...
2984 */
2985
2986 if ((ch = cupsFilePeekChar(fp)) == EOF)
2987 break;
2988 if (ch == 0x0a)
2989 cupsFileGetChar(fp);
2990 }
2991 }
2992 else if (ch < ' ' && ch != '\t' && pg->ppd_conform == PPD_CONFORM_STRICT)
2993 {
2994 /*
2995 * Other control characters...
2996 */
2997
2998 pg->ppd_line = startline;
2999 pg->ppd_status = PPD_ILLEGAL_CHARACTER;
3000
3001 return (0);
3002 }
3003 else if (ch != 0x1a)
3004 {
3005 col ++;
3006
3007 if (col > (PPD_MAX_LINE - 1))
3008 {
3009 /*
3010 * Line is too long...
3011 */
3012
3013 pg->ppd_line = startline;
3014 pg->ppd_status = PPD_LINE_TOO_LONG;
3015
3016 return (0);
3017 }
3018 }
3019 }
3020
3021 if (ch != '\n')
3022 {
3023 /*
3024 * Didn't finish this line...
3025 */
3026
3027 while ((ch = cupsFileGetChar(fp)) != EOF)
3028 if (ch == '\r' || ch == '\n')
3029 {
3030 /*
3031 * Line feed or carriage return...
3032 */
3033
3034 pg->ppd_line ++;
3035 col = 0;
3036
3037 if (ch == '\r')
3038 {
3039 /*
3040 * Check for a trailing line feed...
3041 */
3042
3043 if ((ch = cupsFilePeekChar(fp)) == EOF)
3044 break;
3045 if (ch == 0x0a)
3046 cupsFileGetChar(fp);
3047 }
3048
3049 break;
3050 }
3051 else if (ch < ' ' && ch != '\t' && pg->ppd_conform == PPD_CONFORM_STRICT)
3052 {
3053 /*
3054 * Other control characters...
3055 */
3056
3057 pg->ppd_line = startline;
3058 pg->ppd_status = PPD_ILLEGAL_CHARACTER;
3059
3060 return (0);
3061 }
3062 else if (ch != 0x1a)
3063 {
3064 col ++;
3065
3066 if (col > (PPD_MAX_LINE - 1))
3067 {
3068 /*
3069 * Line is too long...
3070 */
3071
3072 pg->ppd_line = startline;
3073 pg->ppd_status = PPD_LINE_TOO_LONG;
3074
3075 return (0);
3076 }
3077 }
3078 }
3079
3080 if (lineptr > line->buffer && lineptr[-1] == '\n')
3081 lineptr --;
3082
3083 *lineptr = '\0';
3084
3085 DEBUG_printf(("9ppd_read: LINE=\"%s\"", line->buffer));
3086
3087 /*
3088 * The dynamically created PPDs for older style macOS
3089 * drivers include a large blob of data inserted as comments
3090 * at the end of the file. As an optimization we can stop
3091 * reading the PPD when we get to the start of this data.
3092 */
3093
3094 if (!strcmp(line->buffer, "*%APLWORKSET START"))
3095 return (0);
3096
3097 if (ch == EOF && lineptr == line->buffer)
3098 return (0);
3099
3100 /*
3101 * Now parse it...
3102 */
3103
3104 mask = 0;
3105 lineptr = line->buffer + 1;
3106
3107 keyword[0] = '\0';
3108 option[0] = '\0';
3109 text[0] = '\0';
3110 *string = NULL;
3111
3112 if ((!line->buffer[0] || /* Blank line */
3113 !strncmp(line->buffer, "*%", 2) || /* Comment line */
3114 !strcmp(line->buffer, "*End")) && /* End of multi-line string */
3115 ignoreblank) /* Ignore these? */
3116 {
3117 startline = pg->ppd_line + 1;
3118 continue;
3119 }
3120
3121 if (!strcmp(line->buffer, "*")) /* (Bad) comment line */
3122 {
3123 if (pg->ppd_conform == PPD_CONFORM_RELAXED)
3124 {
3125 startline = pg->ppd_line + 1;
3126 continue;
3127 }
3128 else
3129 {
3130 pg->ppd_line = startline;
3131 pg->ppd_status = PPD_ILLEGAL_MAIN_KEYWORD;
3132
3133 return (0);
3134 }
3135 }
3136
3137 if (line->buffer[0] != '*') /* All lines start with an asterisk */
3138 {
3139 /*
3140 * Allow lines consisting of just whitespace...
3141 */
3142
3143 for (lineptr = line->buffer; *lineptr; lineptr ++)
3144 if (*lineptr && !_cups_isspace(*lineptr))
3145 break;
3146
3147 if (*lineptr)
3148 {
3149 pg->ppd_status = PPD_MISSING_ASTERISK;
3150 return (0);
3151 }
3152 else if (ignoreblank)
3153 continue;
3154 else
3155 return (0);
3156 }
3157
3158 /*
3159 * Get a keyword...
3160 */
3161
3162 keyptr = keyword;
3163
3164 while (*lineptr && *lineptr != ':' && !_cups_isspace(*lineptr))
3165 {
3166 if (*lineptr <= ' ' || *lineptr > 126 || *lineptr == '/' ||
3167 (keyptr - keyword) >= (PPD_MAX_NAME - 1))
3168 {
3169 pg->ppd_status = PPD_ILLEGAL_MAIN_KEYWORD;
3170 return (0);
3171 }
3172
3173 *keyptr++ = *lineptr++;
3174 }
3175
3176 *keyptr = '\0';
3177
3178 if (!strcmp(keyword, "End"))
3179 continue;
3180
3181 mask |= PPD_KEYWORD;
3182
3183 if (_cups_isspace(*lineptr))
3184 {
3185 /*
3186 * Get an option name...
3187 */
3188
3189 while (_cups_isspace(*lineptr))
3190 lineptr ++;
3191
3192 optptr = option;
3193
3194 while (*lineptr && !_cups_isspace(*lineptr) && *lineptr != ':' &&
3195 *lineptr != '/')
3196 {
3197 if (*lineptr <= ' ' || *lineptr > 126 ||
3198 (optptr - option) >= (PPD_MAX_NAME - 1))
3199 {
3200 pg->ppd_status = PPD_ILLEGAL_OPTION_KEYWORD;
3201 return (0);
3202 }
3203
3204 *optptr++ = *lineptr++;
3205 }
3206
3207 *optptr = '\0';
3208
3209 if (_cups_isspace(*lineptr) && pg->ppd_conform == PPD_CONFORM_STRICT)
3210 {
3211 pg->ppd_status = PPD_ILLEGAL_WHITESPACE;
3212 return (0);
3213 }
3214
3215 while (_cups_isspace(*lineptr))
3216 lineptr ++;
3217
3218 mask |= PPD_OPTION;
3219
3220 if (*lineptr == '/')
3221 {
3222 /*
3223 * Get human-readable text...
3224 */
3225
3226 lineptr ++;
3227
3228 textptr = text;
3229
3230 while (*lineptr != '\0' && *lineptr != '\n' && *lineptr != ':')
3231 {
3232 if (((unsigned char)*lineptr < ' ' && *lineptr != '\t') ||
3233 (textptr - text) >= (PPD_MAX_LINE - 1))
3234 {
3235 pg->ppd_status = PPD_ILLEGAL_TRANSLATION;
3236 return (0);
3237 }
3238
3239 *textptr++ = *lineptr++;
3240 }
3241
3242 *textptr = '\0';
3243 textlen = ppd_decode(text);
3244
3245 if (textlen > PPD_MAX_TEXT && pg->ppd_conform == PPD_CONFORM_STRICT)
3246 {
3247 pg->ppd_status = PPD_ILLEGAL_TRANSLATION;
3248 return (0);
3249 }
3250
3251 mask |= PPD_TEXT;
3252 }
3253 }
3254
3255 if (_cups_isspace(*lineptr) && pg->ppd_conform == PPD_CONFORM_STRICT)
3256 {
3257 pg->ppd_status = PPD_ILLEGAL_WHITESPACE;
3258 return (0);
3259 }
3260
3261 while (_cups_isspace(*lineptr))
3262 lineptr ++;
3263
3264 if (*lineptr == ':')
3265 {
3266 /*
3267 * Get string after triming leading and trailing whitespace...
3268 */
3269
3270 lineptr ++;
3271 while (_cups_isspace(*lineptr))
3272 lineptr ++;
3273
3274 strptr = lineptr + strlen(lineptr) - 1;
3275 while (strptr >= lineptr && _cups_isspace(*strptr))
3276 *strptr-- = '\0';
3277
3278 if (*strptr == '\"')
3279 {
3280 /*
3281 * Quoted string by itself, remove quotes...
3282 */
3283
3284 *strptr = '\0';
3285 lineptr ++;
3286 }
3287
3288 *string = strdup(lineptr);
3289
3290 mask |= PPD_STRING;
3291 }
3292 }
3293 while (mask == 0);
3294
3295 return (mask);
3296 }
3297
3298
3299 /*
3300 * 'ppd_update_filters()' - Update the filters array as needed.
3301 *
3302 * This function re-populates the filters array with cupsFilter2 entries that
3303 * have been stripped of the destination MIME media types and any maxsize hints.
3304 *
3305 * (All for backwards-compatibility)
3306 */
3307
3308 static int /* O - 1 on success, 0 on failure */
3309 ppd_update_filters(ppd_file_t *ppd, /* I - PPD file */
3310 _ppd_globals_t *pg) /* I - Global data */
3311 {
3312 ppd_attr_t *attr; /* Current cupsFilter2 value */
3313 char srcsuper[16], /* Source MIME media type */
3314 srctype[256],
3315 dstsuper[16], /* Destination MIME media type */
3316 dsttype[256],
3317 program[1024], /* Command to run */
3318 *ptr, /* Pointer into command to run */
3319 buffer[1024], /* Re-written cupsFilter value */
3320 **filter; /* Current filter */
3321 int cost; /* Cost of filter */
3322
3323
3324 DEBUG_printf(("4ppd_update_filters(ppd=%p, cg=%p)", ppd, pg));
3325
3326 /*
3327 * See if we have any cupsFilter2 lines...
3328 */
3329
3330 if ((attr = ppdFindAttr(ppd, "cupsFilter2", NULL)) == NULL)
3331 {
3332 DEBUG_puts("5ppd_update_filters: No cupsFilter2 keywords present.");
3333 return (1);
3334 }
3335
3336 /*
3337 * Yes, free the cupsFilter-defined filters and re-build...
3338 */
3339
3340 ppd_free_filters(ppd);
3341
3342 do
3343 {
3344 /*
3345 * Parse the cupsFilter2 string:
3346 *
3347 * src/type dst/type cost program
3348 * src/type dst/type cost maxsize(n) program
3349 */
3350
3351 DEBUG_printf(("5ppd_update_filters: cupsFilter2=\"%s\"", attr->value));
3352
3353 if (sscanf(attr->value, "%15[^/]/%255s%*[ \t]%15[^/]/%255s%d%*[ \t]%1023[^\n]",
3354 srcsuper, srctype, dstsuper, dsttype, &cost, program) != 6)
3355 {
3356 DEBUG_puts("5ppd_update_filters: Bad cupsFilter2 line.");
3357 pg->ppd_status = PPD_BAD_VALUE;
3358
3359 return (0);
3360 }
3361
3362 DEBUG_printf(("5ppd_update_filters: srcsuper=\"%s\", srctype=\"%s\", "
3363 "dstsuper=\"%s\", dsttype=\"%s\", cost=%d, program=\"%s\"",
3364 srcsuper, srctype, dstsuper, dsttype, cost, program));
3365
3366 if (!strncmp(program, "maxsize(", 8) &&
3367 (ptr = strchr(program + 8, ')')) != NULL)
3368 {
3369 DEBUG_puts("5ppd_update_filters: Found maxsize(nnn).");
3370
3371 ptr ++;
3372 while (_cups_isspace(*ptr))
3373 ptr ++;
3374
3375 _cups_strcpy(program, ptr);
3376 DEBUG_printf(("5ppd_update_filters: New program=\"%s\"", program));
3377 }
3378
3379 /*
3380 * Convert to cupsFilter format:
3381 *
3382 * src/type cost program
3383 */
3384
3385 snprintf(buffer, sizeof(buffer), "%s/%s %d %s", srcsuper, srctype, cost,
3386 program);
3387 DEBUG_printf(("5ppd_update_filters: Adding \"%s\".", buffer));
3388
3389 /*
3390 * Add a cupsFilter-compatible string to the filters array.
3391 */
3392
3393 if (ppd->num_filters == 0)
3394 filter = malloc(sizeof(char *));
3395 else
3396 filter = realloc(ppd->filters, sizeof(char *) * (size_t)(ppd->num_filters + 1));
3397
3398 if (filter == NULL)
3399 {
3400 DEBUG_puts("5ppd_update_filters: Out of memory.");
3401 pg->ppd_status = PPD_ALLOC_ERROR;
3402
3403 return (0);
3404 }
3405
3406 ppd->filters = filter;
3407 filter += ppd->num_filters;
3408 ppd->num_filters ++;
3409
3410 *filter = strdup(buffer);
3411 }
3412 while ((attr = ppdFindNextAttr(ppd, "cupsFilter2", NULL)) != NULL);
3413
3414 DEBUG_puts("5ppd_update_filters: Completed OK.");
3415 return (1);
3416 }