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