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