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