2 * PPD file routines for CUPS.
4 * Copyright 2007-2017 by Apple Inc.
5 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more
10 * PostScript is a trademark of Adobe Systems, Inc.
14 * Include necessary headers.
17 #include "cups-private.h"
18 #include "ppd-private.h"
25 #define ppd_free(p) if (p) free(p) /* Safe free macro */
27 #define PPD_KEYWORD 1 /* Line contained a keyword */
28 #define PPD_OPTION 2 /* Line contained an option name */
29 #define PPD_TEXT 4 /* Line contained human-readable text */
30 #define PPD_STRING 8 /* Line contained a string or code */
32 #define PPD_HASHSIZE 512 /* Size of hash */
36 * Line buffer structure...
39 typedef struct _ppd_line_s
41 char *buffer
; /* Pointer to buffer */
42 size_t bufsize
; /* Size of the buffer */
50 static _cups_threadkey_t ppd_globals_key
= _CUPS_THREADKEY_INITIALIZER
;
51 /* Thread local storage key */
53 static pthread_once_t ppd_globals_key_once
= PTHREAD_ONCE_INIT
;
54 /* One-time initialization object */
55 #endif /* HAVE_PTHREAD_H */
62 static ppd_attr_t
*ppd_add_attr(ppd_file_t
*ppd
, const char *name
,
63 const char *spec
, const char *text
,
65 static ppd_choice_t
*ppd_add_choice(ppd_option_t
*option
, const char *name
);
66 static ppd_size_t
*ppd_add_size(ppd_file_t
*ppd
, const char *name
);
67 static int ppd_compare_attrs(ppd_attr_t
*a
, ppd_attr_t
*b
);
68 static int ppd_compare_choices(ppd_choice_t
*a
, ppd_choice_t
*b
);
69 static int ppd_compare_coptions(ppd_coption_t
*a
,
71 static int ppd_compare_options(ppd_option_t
*a
, ppd_option_t
*b
);
72 static int ppd_decode(char *string
);
73 static void ppd_free_filters(ppd_file_t
*ppd
);
74 static void ppd_free_group(ppd_group_t
*group
);
75 static void ppd_free_option(ppd_option_t
*option
);
76 static ppd_coption_t
*ppd_get_coption(ppd_file_t
*ppd
, const char *name
);
77 static ppd_cparam_t
*ppd_get_cparam(ppd_coption_t
*opt
,
80 static ppd_group_t
*ppd_get_group(ppd_file_t
*ppd
, const char *name
,
81 const char *text
, _ppd_globals_t
*pg
,
82 cups_encoding_t encoding
);
83 static ppd_option_t
*ppd_get_option(ppd_group_t
*group
, const char *name
);
84 static _ppd_globals_t
*ppd_globals_alloc(void);
85 #if defined(HAVE_PTHREAD_H) || defined(WIN32)
86 static void ppd_globals_free(_ppd_globals_t
*g
);
87 #endif /* HAVE_PTHREAD_H || WIN32 */
89 static void ppd_globals_init(void);
90 #endif /* HAVE_PTHREAD_H */
91 static int ppd_hash_option(ppd_option_t
*option
);
92 static int ppd_read(cups_file_t
*fp
, _ppd_line_t
*line
,
93 char *keyword
, char *option
, char *text
,
94 char **string
, int ignoreblank
,
96 static int ppd_update_filters(ppd_file_t
*ppd
,
101 * 'ppdClose()' - Free all memory used by the PPD file.
105 ppdClose(ppd_file_t
*ppd
) /* I - PPD file record */
107 int i
; /* Looping var */
108 ppd_emul_t
*emul
; /* Current emulation */
109 ppd_group_t
*group
; /* Current group */
110 char **font
; /* Current font */
111 ppd_attr_t
**attr
; /* Current attribute */
112 ppd_coption_t
*coption
; /* Current custom option */
113 ppd_cparam_t
*cparam
; /* Current custom parameter */
117 * Range check arguments...
124 * Free all strings at the top level...
127 _cupsStrFree(ppd
->lang_encoding
);
128 _cupsStrFree(ppd
->nickname
);
131 _cupsStrFree(ppd
->jcl_begin
);
132 _cupsStrFree(ppd
->jcl_end
);
133 _cupsStrFree(ppd
->jcl_ps
);
136 * Free any emulations...
139 if (ppd
->num_emulations
> 0)
141 for (i
= ppd
->num_emulations
, emul
= ppd
->emulations
; i
> 0; i
--, emul
++)
143 _cupsStrFree(emul
->start
);
144 _cupsStrFree(emul
->stop
);
147 ppd_free(ppd
->emulations
);
151 * Free any UI groups, subgroups, and options...
154 if (ppd
->num_groups
> 0)
156 for (i
= ppd
->num_groups
, group
= ppd
->groups
; i
> 0; i
--, group
++)
157 ppd_free_group(group
);
159 ppd_free(ppd
->groups
);
162 cupsArrayDelete(ppd
->options
);
163 cupsArrayDelete(ppd
->marked
);
166 * Free any page sizes...
169 if (ppd
->num_sizes
> 0)
170 ppd_free(ppd
->sizes
);
173 * Free any constraints...
176 if (ppd
->num_consts
> 0)
177 ppd_free(ppd
->consts
);
180 * Free any filters...
183 ppd_free_filters(ppd
);
189 if (ppd
->num_fonts
> 0)
191 for (i
= ppd
->num_fonts
, font
= ppd
->fonts
; i
> 0; i
--, font
++)
194 ppd_free(ppd
->fonts
);
198 * Free any profiles...
201 if (ppd
->num_profiles
> 0)
202 ppd_free(ppd
->profiles
);
205 * Free any attributes...
208 if (ppd
->num_attrs
> 0)
210 for (i
= ppd
->num_attrs
, attr
= ppd
->attrs
; i
> 0; i
--, attr
++)
212 _cupsStrFree((*attr
)->value
);
216 ppd_free(ppd
->attrs
);
219 cupsArrayDelete(ppd
->sorted_attrs
);
222 * Free custom options...
225 for (coption
= (ppd_coption_t
*)cupsArrayFirst(ppd
->coptions
);
227 coption
= (ppd_coption_t
*)cupsArrayNext(ppd
->coptions
))
229 for (cparam
= (ppd_cparam_t
*)cupsArrayFirst(coption
->params
);
231 cparam
= (ppd_cparam_t
*)cupsArrayNext(coption
->params
))
233 switch (cparam
->type
)
235 case PPD_CUSTOM_PASSCODE
:
236 case PPD_CUSTOM_PASSWORD
:
237 case PPD_CUSTOM_STRING
:
238 _cupsStrFree(cparam
->current
.custom_string
);
248 cupsArrayDelete(coption
->params
);
253 cupsArrayDelete(ppd
->coptions
);
256 * Free constraints...
259 if (ppd
->cups_uiconstraints
)
261 _ppd_cups_uiconsts_t
*consts
; /* Current constraints */
264 for (consts
= (_ppd_cups_uiconsts_t
*)cupsArrayFirst(ppd
->cups_uiconstraints
);
266 consts
= (_ppd_cups_uiconsts_t
*)cupsArrayNext(ppd
->cups_uiconstraints
))
268 free(consts
->constraints
);
272 cupsArrayDelete(ppd
->cups_uiconstraints
);
276 * Free any PPD cache/mapping data...
280 _ppdCacheDestroy(ppd
->cache
);
283 * Free the whole record...
291 * 'ppdErrorString()' - Returns the text associated with a status.
293 * @since CUPS 1.1.19/macOS 10.3@
296 const char * /* O - Status string */
297 ppdErrorString(ppd_status_t status
) /* I - PPD status */
299 static const char * const messages
[] =/* Status messages */
302 _("Unable to open PPD file"),
303 _("NULL PPD file pointer"),
304 _("Memory allocation error"),
305 _("Missing PPD-Adobe-4.x header"),
306 _("Missing value string"),
309 _("OpenGroup without a CloseGroup first"),
310 _("Bad OpenUI/JCLOpenUI"),
311 _("OpenUI/JCLOpenUI without a CloseUI/JCLCloseUI first"),
312 _("Bad OrderDependency"),
313 _("Bad UIConstraints"),
314 _("Missing asterisk in column 1"),
315 _("Line longer than the maximum allowed (255 characters)"),
316 _("Illegal control character"),
317 _("Illegal main keyword string"),
318 _("Illegal option keyword string"),
319 _("Illegal translation string"),
320 _("Illegal whitespace character"),
321 _("Bad custom parameter"),
322 _("Missing option keyword"),
323 _("Bad value string"),
324 _("Missing CloseGroup")
328 if (status
< PPD_OK
|| status
>= PPD_MAX_STATUS
)
329 return (_cupsLangString(cupsLangDefault(), _("Unknown")));
331 return (_cupsLangString(cupsLangDefault(), messages
[status
]));
336 * '_ppdGetEncoding()' - Get the CUPS encoding value for the given
340 cups_encoding_t
/* O - CUPS encoding value */
341 _ppdGetEncoding(const char *name
) /* I - LanguageEncoding string */
343 if (!_cups_strcasecmp(name
, "ISOLatin1"))
344 return (CUPS_ISO8859_1
);
345 else if (!_cups_strcasecmp(name
, "ISOLatin2"))
346 return (CUPS_ISO8859_2
);
347 else if (!_cups_strcasecmp(name
, "ISOLatin5"))
348 return (CUPS_ISO8859_5
);
349 else if (!_cups_strcasecmp(name
, "JIS83-RKSJ"))
350 return (CUPS_JIS_X0213
);
351 else if (!_cups_strcasecmp(name
, "MacStandard"))
352 return (CUPS_MAC_ROMAN
);
353 else if (!_cups_strcasecmp(name
, "WindowsANSI"))
354 return (CUPS_WINDOWS_1252
);
361 * '_ppdGlobals()' - Return a pointer to thread local storage
364 _ppd_globals_t
* /* O - Pointer to global data */
367 _ppd_globals_t
*pg
; /* Pointer to global data */
370 #ifdef HAVE_PTHREAD_H
372 * Initialize the global data exactly once...
375 pthread_once(&ppd_globals_key_once
, ppd_globals_init
);
376 #endif /* HAVE_PTHREAD_H */
379 * See if we have allocated the data yet...
382 if ((pg
= (_ppd_globals_t
*)_cupsThreadGetData(ppd_globals_key
)) == NULL
)
385 * No, allocate memory as set the pointer for the key...
388 if ((pg
= ppd_globals_alloc()) != NULL
)
389 _cupsThreadSetData(ppd_globals_key
, pg
);
393 * Return the pointer to the data...
401 * 'ppdLastError()' - Return the status from the last ppdOpen*().
403 * @since CUPS 1.1.19/macOS 10.3@
406 ppd_status_t
/* O - Status code */
407 ppdLastError(int *line
) /* O - Line number */
409 _ppd_globals_t
*pg
= _ppdGlobals();
414 *line
= pg
->ppd_line
;
416 return (pg
->ppd_status
);
421 * '_ppdOpen()' - Read a PPD file into memory.
423 * @since CUPS 1.2/macOS 10.5@
426 ppd_file_t
* /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
428 cups_file_t
*fp
, /* I - File to read from */
429 _ppd_localization_t localization
) /* I - Localization to load */
431 int i
, j
, k
; /* Looping vars */
432 int count
; /* Temporary count */
433 _ppd_line_t line
; /* Line buffer */
434 ppd_file_t
*ppd
; /* PPD file record */
435 ppd_group_t
*group
, /* Current group */
436 *subgroup
; /* Current sub-group */
437 ppd_option_t
*option
; /* Current option */
438 ppd_choice_t
*choice
; /* Current choice */
439 ppd_const_t
*constraint
; /* Current constraint */
440 ppd_size_t
*size
; /* Current page size */
441 int mask
; /* Line data mask */
442 char keyword
[PPD_MAX_NAME
],
443 /* Keyword from file */
445 /* Option from file */
447 /* Human-readable text from file */
448 *string
, /* Code/text from file */
449 *sptr
, /* Pointer into string */
450 *nameptr
, /* Pointer into name */
451 *temp
, /* Temporary string pointer */
452 **tempfonts
; /* Temporary fonts pointer */
453 float order
; /* Order dependency number */
454 ppd_section_t section
; /* Order dependency section */
455 ppd_profile_t
*profile
; /* Pointer to color profile */
456 char **filter
; /* Pointer to filter */
457 struct lconv
*loc
; /* Locale data */
458 int ui_keyword
; /* Is this line a UI keyword? */
459 cups_lang_t
*lang
; /* Language data */
460 cups_encoding_t encoding
; /* Encoding of PPD file */
461 _ppd_globals_t
*pg
= _ppdGlobals();
463 char custom_name
[PPD_MAX_NAME
];
464 /* CustomFoo attribute name */
465 ppd_attr_t
*custom_attr
; /* CustomFoo attribute */
466 char ll
[7], /* Base language + '.' */
467 ll_CC
[7]; /* Language w/country + '.' */
468 size_t ll_len
= 0, /* Base language length */
469 ll_CC_len
= 0; /* Language w/country length */
470 static const char * const ui_keywords
[] =
472 #ifdef CUPS_USE_FULL_UI_KEYWORDS_LIST
474 * Adobe defines some 41 keywords as "UI", meaning that they are
475 * user interface elements and that they should be treated as such
476 * even if the PPD creator doesn't use Open/CloseUI around them.
478 * Since this can cause previously invisible options to appear and
479 * confuse users, the default is to only treat the PageSize and
480 * PageRegion keywords this way.
482 /* Boolean keywords */
492 /* PickOne keywords */
505 "JCLFrameBufferSize",
526 #else /* !CUPS_USE_FULL_UI_KEYWORDS_LIST */
529 #endif /* CUPS_USE_FULL_UI_KEYWORDS_LIST */
531 static const char * const color_keywords
[] = /* Keywords associated with color profiles */
538 DEBUG_printf(("_ppdOpen(fp=%p)", fp
));
541 * Default to "OK" status...
544 pg
->ppd_status
= PPD_OK
;
548 * Range check input...
553 pg
->ppd_status
= PPD_NULL_FILE
;
558 * If only loading a single localization set up the strings to match...
561 if (localization
== _PPD_LOCALIZATION_DEFAULT
)
563 if ((lang
= cupsLangDefault()) == NULL
)
566 snprintf(ll_CC
, sizeof(ll_CC
), "%s.", lang
->language
);
569 * <rdar://problem/22130168>
570 * <rdar://problem/27245567>
572 * Need to use a different base language for some locales...
575 if (!strcmp(lang
->language
, "zh_HK"))
576 { /* Traditional Chinese + variants */
577 strlcpy(ll_CC
, "zh_TW.", sizeof(ll_CC
));
578 strlcpy(ll
, "zh_", sizeof(ll
));
580 else if (!strncmp(lang
->language
, "zh", 2))
581 strlcpy(ll
, "zh_", sizeof(ll
)); /* Any Chinese variant */
582 else if (!strncmp(lang
->language
, "jp", 2))
583 { /* Any Japanese variant */
584 strlcpy(ll_CC
, "ja", sizeof(ll_CC
));
585 strlcpy(ll
, "jp", sizeof(ll
));
587 else if (!strncmp(lang
->language
, "nb", 2) || !strncmp(lang
->language
, "no", 2))
588 { /* Any Norwegian variant */
589 strlcpy(ll_CC
, "nb", sizeof(ll_CC
));
590 strlcpy(ll
, "no", sizeof(ll
));
593 snprintf(ll
, sizeof(ll
), "%2.2s.", lang
->language
);
595 ll_CC_len
= strlen(ll_CC
);
598 DEBUG_printf(("2_ppdOpen: Loading localizations matching \"%s\" and \"%s\"",
603 * Grab the first line and make sure it reads '*PPD-Adobe: "major.minor"'...
609 mask
= ppd_read(fp
, &line
, keyword
, name
, text
, &string
, 0, pg
);
611 DEBUG_printf(("2_ppdOpen: mask=%x, keyword=\"%s\"...", mask
, keyword
));
614 strcmp(keyword
, "PPD-Adobe") ||
615 string
== NULL
|| string
[0] != '4')
618 * Either this is not a PPD file, or it is not a 4.x PPD file.
621 if (pg
->ppd_status
== PPD_OK
)
622 pg
->ppd_status
= PPD_MISSING_PPDADOBE4
;
624 _cupsStrFree(string
);
625 ppd_free(line
.buffer
);
630 DEBUG_printf(("2_ppdOpen: keyword=%s, string=%p", keyword
, string
));
632 _cupsStrFree(string
);
635 * Allocate memory for the PPD file record...
638 if ((ppd
= calloc(1, sizeof(ppd_file_t
))) == NULL
)
640 pg
->ppd_status
= PPD_ALLOC_ERROR
;
642 _cupsStrFree(string
);
643 ppd_free(line
.buffer
);
648 ppd
->language_level
= 2;
649 ppd
->color_device
= 0;
650 ppd
->colorspace
= PPD_CS_N
;
651 ppd
->landscape
= -90;
652 ppd
->coptions
= cupsArrayNew((cups_array_func_t
)ppd_compare_coptions
,
656 * Read lines from the PPD file and add them to the file record...
664 encoding
= CUPS_ISO8859_1
;
667 while ((mask
= ppd_read(fp
, &line
, keyword
, name
, text
, &string
, 1, pg
)) != 0)
669 DEBUG_printf(("2_ppdOpen: mask=%x, keyword=\"%s\", name=\"%s\", "
670 "text=\"%s\", string=%d chars...", mask
, keyword
, name
, text
,
671 string
? (int)strlen(string
) : 0));
673 if (strncmp(keyword
, "Default", 7) && !string
&&
674 pg
->ppd_conform
!= PPD_CONFORM_RELAXED
)
677 * Need a string value!
680 pg
->ppd_status
= PPD_MISSING_VALUE
;
688 * Certain main keywords (as defined by the PPD spec) may be used
689 * without the usual OpenUI/CloseUI stuff. Presumably this is just
690 * so that Adobe wouldn't completely break compatibility with PPD
691 * files prior to v4.0 of the spec, but it is hopelessly
692 * inconsistent... Catch these main keywords and automatically
693 * create the corresponding option, as needed...
699 * Previous line was a UI keyword...
707 * If we are filtering out keyword localizations, see if this line needs to
711 if (localization
!= _PPD_LOCALIZATION_ALL
&&
712 (temp
= strchr(keyword
, '.')) != NULL
&&
713 ((temp
- keyword
) == 2 || (temp
- keyword
) == 5) &&
714 _cups_isalpha(keyword
[0]) &&
715 _cups_isalpha(keyword
[1]) &&
716 (keyword
[2] == '.' ||
717 (keyword
[2] == '_' && _cups_isalpha(keyword
[3]) &&
718 _cups_isalpha(keyword
[4]) && keyword
[5] == '.')))
720 if (localization
== _PPD_LOCALIZATION_NONE
||
721 (localization
== _PPD_LOCALIZATION_DEFAULT
&&
722 strncmp(ll_CC
, keyword
, ll_CC_len
) &&
723 strncmp(ll
, keyword
, ll_len
)))
725 DEBUG_printf(("2_ppdOpen: Ignoring localization: \"%s\"\n", keyword
));
728 else if (localization
== _PPD_LOCALIZATION_ICC_PROFILES
)
731 * Only load localizations for the color profile related keywords...
735 i
< (int)(sizeof(color_keywords
) / sizeof(color_keywords
[0]));
738 if (!_cups_strcasecmp(temp
, color_keywords
[i
]))
742 if (i
>= (int)(sizeof(color_keywords
) / sizeof(color_keywords
[0])))
744 DEBUG_printf(("2_ppdOpen: Ignoring localization: \"%s\"\n", keyword
));
750 if (option
== NULL
&&
751 (mask
& (PPD_KEYWORD
| PPD_OPTION
| PPD_STRING
)) ==
752 (PPD_KEYWORD
| PPD_OPTION
| PPD_STRING
))
754 for (i
= 0; i
< (int)(sizeof(ui_keywords
) / sizeof(ui_keywords
[0])); i
++)
755 if (!strcmp(keyword
, ui_keywords
[i
]))
758 if (i
< (int)(sizeof(ui_keywords
) / sizeof(ui_keywords
[0])))
761 * Create the option in the appropriate group...
766 DEBUG_printf(("2_ppdOpen: FOUND ADOBE UI KEYWORD %s WITHOUT OPENUI!",
771 if ((group
= ppd_get_group(ppd
, "General", _("General"), pg
,
775 DEBUG_printf(("2_ppdOpen: Adding to group %s...", group
->text
));
776 option
= ppd_get_option(group
, keyword
);
780 option
= ppd_get_option(group
, keyword
);
784 pg
->ppd_status
= PPD_ALLOC_ERROR
;
790 * Now fill in the initial information for the option...
793 if (!strncmp(keyword
, "JCL", 3))
794 option
->section
= PPD_ORDER_JCL
;
796 option
->section
= PPD_ORDER_ANY
;
798 option
->order
= 10.0f
;
801 option
->ui
= PPD_UI_BOOLEAN
;
803 option
->ui
= PPD_UI_PICKONE
;
805 for (j
= 0; j
< ppd
->num_attrs
; j
++)
806 if (!strncmp(ppd
->attrs
[j
]->name
, "Default", 7) &&
807 !strcmp(ppd
->attrs
[j
]->name
+ 7, keyword
) &&
808 ppd
->attrs
[j
]->value
)
810 DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...",
811 option
->keyword
, ppd
->attrs
[j
]->value
));
812 strlcpy(option
->defchoice
, ppd
->attrs
[j
]->value
,
813 sizeof(option
->defchoice
));
817 if (!strcmp(keyword
, "PageSize"))
818 strlcpy(option
->text
, _("Media Size"), sizeof(option
->text
));
819 else if (!strcmp(keyword
, "MediaType"))
820 strlcpy(option
->text
, _("Media Type"), sizeof(option
->text
));
821 else if (!strcmp(keyword
, "InputSlot"))
822 strlcpy(option
->text
, _("Media Source"), sizeof(option
->text
));
823 else if (!strcmp(keyword
, "ColorModel"))
824 strlcpy(option
->text
, _("Output Mode"), sizeof(option
->text
));
825 else if (!strcmp(keyword
, "Resolution"))
826 strlcpy(option
->text
, _("Resolution"), sizeof(option
->text
));
828 strlcpy(option
->text
, keyword
, sizeof(option
->text
));
832 if (!strcmp(keyword
, "LanguageLevel"))
833 ppd
->language_level
= atoi(string
);
834 else if (!strcmp(keyword
, "LanguageEncoding"))
837 * Say all PPD files are UTF-8, since we convert to UTF-8...
840 ppd
->lang_encoding
= _cupsStrAlloc("UTF-8");
841 encoding
= _ppdGetEncoding(string
);
843 else if (!strcmp(keyword
, "LanguageVersion"))
844 ppd
->lang_version
= string
;
845 else if (!strcmp(keyword
, "Manufacturer"))
846 ppd
->manufacturer
= string
;
847 else if (!strcmp(keyword
, "ModelName"))
848 ppd
->modelname
= string
;
849 else if (!strcmp(keyword
, "Protocols"))
850 ppd
->protocols
= string
;
851 else if (!strcmp(keyword
, "PCFileName"))
852 ppd
->pcfilename
= string
;
853 else if (!strcmp(keyword
, "NickName"))
855 if (encoding
!= CUPS_UTF8
)
857 cups_utf8_t utf8
[256]; /* UTF-8 version of NickName */
860 cupsCharsetToUTF8(utf8
, string
, sizeof(utf8
), encoding
);
861 ppd
->nickname
= _cupsStrAlloc((char *)utf8
);
864 ppd
->nickname
= _cupsStrAlloc(string
);
866 else if (!strcmp(keyword
, "Product"))
867 ppd
->product
= string
;
868 else if (!strcmp(keyword
, "ShortNickName"))
869 ppd
->shortnickname
= string
;
870 else if (!strcmp(keyword
, "TTRasterizer"))
871 ppd
->ttrasterizer
= string
;
872 else if (!strcmp(keyword
, "JCLBegin"))
874 ppd
->jcl_begin
= _cupsStrAlloc(string
);
875 ppd_decode(ppd
->jcl_begin
); /* Decode quoted string */
877 else if (!strcmp(keyword
, "JCLEnd"))
879 ppd
->jcl_end
= _cupsStrAlloc(string
);
880 ppd_decode(ppd
->jcl_end
); /* Decode quoted string */
882 else if (!strcmp(keyword
, "JCLToPSInterpreter"))
884 ppd
->jcl_ps
= _cupsStrAlloc(string
);
885 ppd_decode(ppd
->jcl_ps
); /* Decode quoted string */
887 else if (!strcmp(keyword
, "AccurateScreensSupport"))
888 ppd
->accurate_screens
= !strcmp(string
, "True");
889 else if (!strcmp(keyword
, "ColorDevice"))
890 ppd
->color_device
= !strcmp(string
, "True");
891 else if (!strcmp(keyword
, "ContoneOnly"))
892 ppd
->contone_only
= !strcmp(string
, "True");
893 else if (!strcmp(keyword
, "cupsFlipDuplex"))
894 ppd
->flip_duplex
= !strcmp(string
, "True");
895 else if (!strcmp(keyword
, "cupsManualCopies"))
896 ppd
->manual_copies
= !strcmp(string
, "True");
897 else if (!strcmp(keyword
, "cupsModelNumber"))
898 ppd
->model_number
= atoi(string
);
899 else if (!strcmp(keyword
, "cupsColorProfile"))
901 if (ppd
->num_profiles
== 0)
902 profile
= malloc(sizeof(ppd_profile_t
));
904 profile
= realloc(ppd
->profiles
, sizeof(ppd_profile_t
) * (size_t)(ppd
->num_profiles
+ 1));
908 pg
->ppd_status
= PPD_ALLOC_ERROR
;
913 ppd
->profiles
= profile
;
914 profile
+= ppd
->num_profiles
;
915 ppd
->num_profiles
++;
917 memset(profile
, 0, sizeof(ppd_profile_t
));
918 strlcpy(profile
->resolution
, name
, sizeof(profile
->resolution
));
919 strlcpy(profile
->media_type
, text
, sizeof(profile
->media_type
));
921 profile
->density
= (float)_cupsStrScand(string
, &sptr
, loc
);
922 profile
->gamma
= (float)_cupsStrScand(sptr
, &sptr
, loc
);
923 profile
->matrix
[0][0] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
924 profile
->matrix
[0][1] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
925 profile
->matrix
[0][2] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
926 profile
->matrix
[1][0] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
927 profile
->matrix
[1][1] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
928 profile
->matrix
[1][2] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
929 profile
->matrix
[2][0] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
930 profile
->matrix
[2][1] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
931 profile
->matrix
[2][2] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
933 else if (!strcmp(keyword
, "cupsFilter"))
935 if (ppd
->num_filters
== 0)
936 filter
= malloc(sizeof(char *));
938 filter
= realloc(ppd
->filters
, sizeof(char *) * (size_t)(ppd
->num_filters
+ 1));
942 pg
->ppd_status
= PPD_ALLOC_ERROR
;
947 ppd
->filters
= filter
;
948 filter
+= ppd
->num_filters
;
952 * Retain a copy of the filter string...
955 *filter
= _cupsStrRetain(string
);
957 else if (!strcmp(keyword
, "Throughput"))
958 ppd
->throughput
= atoi(string
);
959 else if (!strcmp(keyword
, "Font"))
962 * Add this font to the list of available fonts...
965 if (ppd
->num_fonts
== 0)
966 tempfonts
= (char **)malloc(sizeof(char *));
968 tempfonts
= (char **)realloc(ppd
->fonts
, sizeof(char *) * (size_t)(ppd
->num_fonts
+ 1));
970 if (tempfonts
== NULL
)
972 pg
->ppd_status
= PPD_ALLOC_ERROR
;
977 ppd
->fonts
= tempfonts
;
978 ppd
->fonts
[ppd
->num_fonts
] = _cupsStrAlloc(name
);
981 else if (!strncmp(keyword
, "ParamCustom", 11))
983 ppd_coption_t
*coption
; /* Custom option */
984 ppd_cparam_t
*cparam
; /* Custom parameter */
985 int corder
; /* Order number */
986 char ctype
[33], /* Data type */
987 cminimum
[65], /* Minimum value */
988 cmaximum
[65]; /* Maximum value */
992 * Get the custom option and parameter...
995 if ((coption
= ppd_get_coption(ppd
, keyword
+ 11)) == NULL
)
997 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1002 if ((cparam
= ppd_get_cparam(coption
, name
, text
)) == NULL
)
1004 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1010 * Get the parameter data...
1014 sscanf(string
, "%d%32s%64s%64s", &corder
, ctype
, cminimum
,
1017 pg
->ppd_status
= PPD_BAD_CUSTOM_PARAM
;
1022 cparam
->order
= corder
;
1024 if (!strcmp(ctype
, "curve"))
1026 cparam
->type
= PPD_CUSTOM_CURVE
;
1027 cparam
->minimum
.custom_curve
= (float)_cupsStrScand(cminimum
, NULL
, loc
);
1028 cparam
->maximum
.custom_curve
= (float)_cupsStrScand(cmaximum
, NULL
, loc
);
1030 else if (!strcmp(ctype
, "int"))
1032 cparam
->type
= PPD_CUSTOM_INT
;
1033 cparam
->minimum
.custom_int
= atoi(cminimum
);
1034 cparam
->maximum
.custom_int
= atoi(cmaximum
);
1036 else if (!strcmp(ctype
, "invcurve"))
1038 cparam
->type
= PPD_CUSTOM_INVCURVE
;
1039 cparam
->minimum
.custom_invcurve
= (float)_cupsStrScand(cminimum
, NULL
, loc
);
1040 cparam
->maximum
.custom_invcurve
= (float)_cupsStrScand(cmaximum
, NULL
, loc
);
1042 else if (!strcmp(ctype
, "passcode"))
1044 cparam
->type
= PPD_CUSTOM_PASSCODE
;
1045 cparam
->minimum
.custom_passcode
= atoi(cminimum
);
1046 cparam
->maximum
.custom_passcode
= atoi(cmaximum
);
1048 else if (!strcmp(ctype
, "password"))
1050 cparam
->type
= PPD_CUSTOM_PASSWORD
;
1051 cparam
->minimum
.custom_password
= atoi(cminimum
);
1052 cparam
->maximum
.custom_password
= atoi(cmaximum
);
1054 else if (!strcmp(ctype
, "points"))
1056 cparam
->type
= PPD_CUSTOM_POINTS
;
1057 cparam
->minimum
.custom_points
= (float)_cupsStrScand(cminimum
, NULL
, loc
);
1058 cparam
->maximum
.custom_points
= (float)_cupsStrScand(cmaximum
, NULL
, loc
);
1060 else if (!strcmp(ctype
, "real"))
1062 cparam
->type
= PPD_CUSTOM_REAL
;
1063 cparam
->minimum
.custom_real
= (float)_cupsStrScand(cminimum
, NULL
, loc
);
1064 cparam
->maximum
.custom_real
= (float)_cupsStrScand(cmaximum
, NULL
, loc
);
1066 else if (!strcmp(ctype
, "string"))
1068 cparam
->type
= PPD_CUSTOM_STRING
;
1069 cparam
->minimum
.custom_string
= atoi(cminimum
);
1070 cparam
->maximum
.custom_string
= atoi(cmaximum
);
1074 pg
->ppd_status
= PPD_BAD_CUSTOM_PARAM
;
1080 * Now special-case for CustomPageSize...
1083 if (!strcmp(coption
->keyword
, "PageSize"))
1085 if (!strcmp(name
, "Width"))
1087 ppd
->custom_min
[0] = cparam
->minimum
.custom_points
;
1088 ppd
->custom_max
[0] = cparam
->maximum
.custom_points
;
1090 else if (!strcmp(name
, "Height"))
1092 ppd
->custom_min
[1] = cparam
->minimum
.custom_points
;
1093 ppd
->custom_max
[1] = cparam
->maximum
.custom_points
;
1097 else if (!strcmp(keyword
, "HWMargins"))
1099 for (i
= 0, sptr
= string
; i
< 4; i
++)
1100 ppd
->custom_margins
[i
] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
1102 else if (!strncmp(keyword
, "Custom", 6) && !strcmp(name
, "True") && !option
)
1104 ppd_option_t
*custom_option
; /* Custom option */
1106 DEBUG_puts("2_ppdOpen: Processing Custom option...");
1109 * Get the option and custom option...
1112 if (!ppd_get_coption(ppd
, keyword
+ 6))
1114 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1119 if (option
&& !_cups_strcasecmp(option
->keyword
, keyword
+ 6))
1120 custom_option
= option
;
1122 custom_option
= ppdFindOption(ppd
, keyword
+ 6);
1127 * Add the "custom" option...
1130 if ((choice
= ppdFindChoice(custom_option
, "Custom")) == NULL
)
1131 if ((choice
= ppd_add_choice(custom_option
, "Custom")) == NULL
)
1133 DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
1135 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1140 strlcpy(choice
->text
, text
[0] ? text
: _("Custom"),
1141 sizeof(choice
->text
));
1143 choice
->code
= _cupsStrAlloc(string
);
1145 if (custom_option
->section
== PPD_ORDER_JCL
)
1146 ppd_decode(choice
->code
);
1150 * Now process custom page sizes specially...
1153 if (!strcmp(keyword
, "CustomPageSize"))
1156 * Add a "Custom" page size entry...
1159 ppd
->variable_sizes
= 1;
1161 ppd_add_size(ppd
, "Custom");
1163 if (option
&& !_cups_strcasecmp(option
->keyword
, "PageRegion"))
1164 custom_option
= option
;
1166 custom_option
= ppdFindOption(ppd
, "PageRegion");
1170 if ((choice
= ppdFindChoice(custom_option
, "Custom")) == NULL
)
1171 if ((choice
= ppd_add_choice(custom_option
, "Custom")) == NULL
)
1173 DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
1175 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1180 strlcpy(choice
->text
, text
[0] ? text
: _("Custom"),
1181 sizeof(choice
->text
));
1185 else if (!strcmp(keyword
, "LandscapeOrientation"))
1187 if (!strcmp(string
, "Minus90"))
1188 ppd
->landscape
= -90;
1189 else if (!strcmp(string
, "Plus90"))
1190 ppd
->landscape
= 90;
1192 else if (!strcmp(keyword
, "Emulators") && string
)
1194 for (count
= 1, sptr
= string
; sptr
!= NULL
;)
1195 if ((sptr
= strchr(sptr
, ' ')) != NULL
)
1198 while (*sptr
== ' ')
1202 ppd
->num_emulations
= count
;
1203 if ((ppd
->emulations
= calloc((size_t)count
, sizeof(ppd_emul_t
))) == NULL
)
1205 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1210 for (i
= 0, sptr
= string
; i
< count
; i
++)
1212 for (nameptr
= ppd
->emulations
[i
].name
;
1213 *sptr
!= '\0' && *sptr
!= ' ';
1215 if (nameptr
< (ppd
->emulations
[i
].name
+ sizeof(ppd
->emulations
[i
].name
) - 1))
1220 while (*sptr
== ' ')
1224 else if (!strncmp(keyword
, "StartEmulator_", 14))
1228 for (i
= 0; i
< ppd
->num_emulations
; i
++)
1229 if (!strcmp(keyword
+ 14, ppd
->emulations
[i
].name
))
1231 ppd
->emulations
[i
].start
= string
;
1235 else if (!strncmp(keyword
, "StopEmulator_", 13))
1239 for (i
= 0; i
< ppd
->num_emulations
; i
++)
1240 if (!strcmp(keyword
+ 13, ppd
->emulations
[i
].name
))
1242 ppd
->emulations
[i
].stop
= string
;
1246 else if (!strcmp(keyword
, "JobPatchFile"))
1249 * CUPS STR #3421: Check for "*JobPatchFile: int: string"
1252 if (isdigit(*string
& 255))
1254 for (sptr
= string
+ 1; isdigit(*sptr
& 255); sptr
++);
1259 * Found "*JobPatchFile: int: string"...
1262 pg
->ppd_status
= PPD_BAD_VALUE
;
1268 if (!name
[0] && pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1271 * Found "*JobPatchFile: string"...
1274 pg
->ppd_status
= PPD_MISSING_OPTION_KEYWORD
;
1279 if (ppd
->patches
== NULL
)
1280 ppd
->patches
= strdup(string
);
1283 temp
= realloc(ppd
->patches
, strlen(ppd
->patches
) +
1284 strlen(string
) + 1);
1287 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1292 ppd
->patches
= temp
;
1294 memcpy(ppd
->patches
+ strlen(ppd
->patches
), string
, strlen(string
) + 1);
1297 else if (!strcmp(keyword
, "OpenUI"))
1300 * Don't allow nesting of options...
1303 if (option
&& pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1305 pg
->ppd_status
= PPD_NESTED_OPEN_UI
;
1311 * Add an option record to the current sub-group, group, or file...
1314 DEBUG_printf(("2_ppdOpen: name=\"%s\" (%d)", name
, (int)strlen(name
)));
1317 _cups_strcpy(name
, name
+ 1); /* Eliminate leading asterisk */
1319 for (i
= (int)strlen(name
) - 1; i
> 0 && _cups_isspace(name
[i
]); i
--)
1320 name
[i
] = '\0'; /* Eliminate trailing spaces */
1322 DEBUG_printf(("2_ppdOpen: OpenUI of %s in group %s...", name
,
1323 group
? group
->text
: "(null)"));
1325 if (subgroup
!= NULL
)
1326 option
= ppd_get_option(subgroup
, name
);
1327 else if (group
== NULL
)
1329 if ((group
= ppd_get_group(ppd
, "General", _("General"), pg
,
1333 DEBUG_printf(("2_ppdOpen: Adding to group %s...", group
->text
));
1334 option
= ppd_get_option(group
, name
);
1338 option
= ppd_get_option(group
, name
);
1342 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1348 * Now fill in the initial information for the option...
1351 if (string
&& !strcmp(string
, "PickMany"))
1352 option
->ui
= PPD_UI_PICKMANY
;
1353 else if (string
&& !strcmp(string
, "Boolean"))
1354 option
->ui
= PPD_UI_BOOLEAN
;
1355 else if (string
&& !strcmp(string
, "PickOne"))
1356 option
->ui
= PPD_UI_PICKONE
;
1357 else if (pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1359 pg
->ppd_status
= PPD_BAD_OPEN_UI
;
1364 option
->ui
= PPD_UI_PICKONE
;
1366 for (j
= 0; j
< ppd
->num_attrs
; j
++)
1367 if (!strncmp(ppd
->attrs
[j
]->name
, "Default", 7) &&
1368 !strcmp(ppd
->attrs
[j
]->name
+ 7, name
) &&
1369 ppd
->attrs
[j
]->value
)
1371 DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...",
1372 option
->keyword
, ppd
->attrs
[j
]->value
));
1373 strlcpy(option
->defchoice
, ppd
->attrs
[j
]->value
,
1374 sizeof(option
->defchoice
));
1379 cupsCharsetToUTF8((cups_utf8_t
*)option
->text
, text
,
1380 sizeof(option
->text
), encoding
);
1383 if (!strcmp(name
, "PageSize"))
1384 strlcpy(option
->text
, _("Media Size"), sizeof(option
->text
));
1385 else if (!strcmp(name
, "MediaType"))
1386 strlcpy(option
->text
, _("Media Type"), sizeof(option
->text
));
1387 else if (!strcmp(name
, "InputSlot"))
1388 strlcpy(option
->text
, _("Media Source"), sizeof(option
->text
));
1389 else if (!strcmp(name
, "ColorModel"))
1390 strlcpy(option
->text
, _("Output Mode"), sizeof(option
->text
));
1391 else if (!strcmp(name
, "Resolution"))
1392 strlcpy(option
->text
, _("Resolution"), sizeof(option
->text
));
1394 strlcpy(option
->text
, name
, sizeof(option
->text
));
1397 option
->section
= PPD_ORDER_ANY
;
1399 _cupsStrFree(string
);
1403 * Add a custom option choice if we have already seen a CustomFoo
1407 if (!_cups_strcasecmp(name
, "PageRegion"))
1408 strlcpy(custom_name
, "CustomPageSize", sizeof(custom_name
));
1410 snprintf(custom_name
, sizeof(custom_name
), "Custom%s", name
);
1412 if ((custom_attr
= ppdFindAttr(ppd
, custom_name
, "True")) != NULL
)
1414 if ((choice
= ppdFindChoice(option
, "Custom")) == NULL
)
1415 if ((choice
= ppd_add_choice(option
, "Custom")) == NULL
)
1417 DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
1419 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1424 strlcpy(choice
->text
,
1425 custom_attr
->text
[0] ? custom_attr
->text
: _("Custom"),
1426 sizeof(choice
->text
));
1427 choice
->code
= _cupsStrRetain(custom_attr
->value
);
1430 else if (!strcmp(keyword
, "JCLOpenUI"))
1433 * Don't allow nesting of options...
1436 if (option
&& pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1438 pg
->ppd_status
= PPD_NESTED_OPEN_UI
;
1444 * Find the JCL group, and add if needed...
1447 group
= ppd_get_group(ppd
, "JCL", _("JCL"), pg
, encoding
);
1453 * Add an option record to the current JCLs...
1457 _cups_strcpy(name
, name
+ 1);
1459 option
= ppd_get_option(group
, name
);
1463 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1469 * Now fill in the initial information for the option...
1472 if (string
&& !strcmp(string
, "PickMany"))
1473 option
->ui
= PPD_UI_PICKMANY
;
1474 else if (string
&& !strcmp(string
, "Boolean"))
1475 option
->ui
= PPD_UI_BOOLEAN
;
1476 else if (string
&& !strcmp(string
, "PickOne"))
1477 option
->ui
= PPD_UI_PICKONE
;
1480 pg
->ppd_status
= PPD_BAD_OPEN_UI
;
1485 for (j
= 0; j
< ppd
->num_attrs
; j
++)
1486 if (!strncmp(ppd
->attrs
[j
]->name
, "Default", 7) &&
1487 !strcmp(ppd
->attrs
[j
]->name
+ 7, name
) &&
1488 ppd
->attrs
[j
]->value
)
1490 DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...",
1491 option
->keyword
, ppd
->attrs
[j
]->value
));
1492 strlcpy(option
->defchoice
, ppd
->attrs
[j
]->value
,
1493 sizeof(option
->defchoice
));
1498 cupsCharsetToUTF8((cups_utf8_t
*)option
->text
, text
,
1499 sizeof(option
->text
), encoding
);
1501 strlcpy(option
->text
, name
, sizeof(option
->text
));
1503 option
->section
= PPD_ORDER_JCL
;
1506 _cupsStrFree(string
);
1510 * Add a custom option choice if we have already seen a CustomFoo
1514 snprintf(custom_name
, sizeof(custom_name
), "Custom%s", name
);
1516 if ((custom_attr
= ppdFindAttr(ppd
, custom_name
, "True")) != NULL
)
1518 if ((choice
= ppd_add_choice(option
, "Custom")) == NULL
)
1520 DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
1522 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1527 strlcpy(choice
->text
,
1528 custom_attr
->text
[0] ? custom_attr
->text
: _("Custom"),
1529 sizeof(choice
->text
));
1530 choice
->code
= _cupsStrRetain(custom_attr
->value
);
1533 else if (!strcmp(keyword
, "CloseUI") || !strcmp(keyword
, "JCLCloseUI"))
1537 _cupsStrFree(string
);
1540 else if (!strcmp(keyword
, "OpenGroup"))
1543 * Open a new group...
1548 pg
->ppd_status
= PPD_NESTED_OPEN_GROUP
;
1555 pg
->ppd_status
= PPD_BAD_OPEN_GROUP
;
1561 * Separate the group name from the text (name/text)...
1564 if ((sptr
= strchr(string
, '/')) != NULL
)
1570 * Fix up the text...
1576 * Find/add the group...
1579 group
= ppd_get_group(ppd
, string
, sptr
, pg
, encoding
);
1584 _cupsStrFree(string
);
1587 else if (!strcmp(keyword
, "CloseGroup"))
1591 _cupsStrFree(string
);
1594 else if (!strcmp(keyword
, "OrderDependency"))
1596 order
= (float)_cupsStrScand(string
, &sptr
, loc
);
1598 if (!sptr
|| sscanf(sptr
, "%40s%40s", name
, keyword
) != 2)
1600 pg
->ppd_status
= PPD_BAD_ORDER_DEPENDENCY
;
1605 if (keyword
[0] == '*')
1606 _cups_strcpy(keyword
, keyword
+ 1);
1608 if (!strcmp(name
, "ExitServer"))
1609 section
= PPD_ORDER_EXIT
;
1610 else if (!strcmp(name
, "Prolog"))
1611 section
= PPD_ORDER_PROLOG
;
1612 else if (!strcmp(name
, "DocumentSetup"))
1613 section
= PPD_ORDER_DOCUMENT
;
1614 else if (!strcmp(name
, "PageSetup"))
1615 section
= PPD_ORDER_PAGE
;
1616 else if (!strcmp(name
, "JCLSetup"))
1617 section
= PPD_ORDER_JCL
;
1619 section
= PPD_ORDER_ANY
;
1627 * Only valid for Non-UI options...
1630 for (i
= ppd
->num_groups
, gtemp
= ppd
->groups
; i
> 0; i
--, gtemp
++)
1631 if (gtemp
->text
[0] == '\0')
1635 for (i
= 0; i
< gtemp
->num_options
; i
++)
1636 if (!strcmp(keyword
, gtemp
->options
[i
].keyword
))
1638 gtemp
->options
[i
].section
= section
;
1639 gtemp
->options
[i
].order
= order
;
1645 option
->section
= section
;
1646 option
->order
= order
;
1649 _cupsStrFree(string
);
1652 else if (!strncmp(keyword
, "Default", 7))
1658 * Drop UI text, if any, from value...
1661 if (strchr(string
, '/') != NULL
)
1662 *strchr(string
, '/') = '\0';
1665 * Assign the default value as appropriate...
1668 if (!strcmp(keyword
, "DefaultColorSpace"))
1671 * Set default colorspace...
1674 if (!strcmp(string
, "CMY"))
1675 ppd
->colorspace
= PPD_CS_CMY
;
1676 else if (!strcmp(string
, "CMYK"))
1677 ppd
->colorspace
= PPD_CS_CMYK
;
1678 else if (!strcmp(string
, "RGB"))
1679 ppd
->colorspace
= PPD_CS_RGB
;
1680 else if (!strcmp(string
, "RGBK"))
1681 ppd
->colorspace
= PPD_CS_RGBK
;
1682 else if (!strcmp(string
, "N"))
1683 ppd
->colorspace
= PPD_CS_N
;
1685 ppd
->colorspace
= PPD_CS_GRAY
;
1687 else if (option
&& !strcmp(keyword
+ 7, option
->keyword
))
1690 * Set the default as part of the current option...
1693 DEBUG_printf(("2_ppdOpen: Setting %s to %s...", keyword
, string
));
1695 strlcpy(option
->defchoice
, string
, sizeof(option
->defchoice
));
1697 DEBUG_printf(("2_ppdOpen: %s is now %s...", keyword
, option
->defchoice
));
1702 * Lookup option and set if it has been defined...
1705 ppd_option_t
*toption
; /* Temporary option */
1708 if ((toption
= ppdFindOption(ppd
, keyword
+ 7)) != NULL
)
1710 DEBUG_printf(("2_ppdOpen: Setting %s to %s...", keyword
, string
));
1711 strlcpy(toption
->defchoice
, string
, sizeof(toption
->defchoice
));
1715 else if (!strcmp(keyword
, "UIConstraints") ||
1716 !strcmp(keyword
, "NonUIConstraints"))
1720 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1724 if (ppd
->num_consts
== 0)
1725 constraint
= calloc(2, sizeof(ppd_const_t
));
1727 constraint
= realloc(ppd
->consts
, (size_t)(ppd
->num_consts
+ 2) * sizeof(ppd_const_t
));
1729 if (constraint
== NULL
)
1731 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1736 ppd
->consts
= constraint
;
1737 constraint
+= ppd
->num_consts
;
1740 switch (sscanf(string
, "%40s%40s%40s%40s", constraint
->option1
,
1741 constraint
->choice1
, constraint
->option2
,
1742 constraint
->choice2
))
1744 case 0 : /* Error */
1745 case 1 : /* Error */
1746 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1749 case 2 : /* Two options... */
1751 * Check for broken constraints like "* Option"...
1754 if (pg
->ppd_conform
== PPD_CONFORM_STRICT
&&
1755 (!strcmp(constraint
->option1
, "*") ||
1756 !strcmp(constraint
->choice1
, "*")))
1758 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1763 * The following strcpy's are safe, as optionN and
1764 * choiceN are all the same size (size defined by PPD spec...)
1767 if (constraint
->option1
[0] == '*')
1768 _cups_strcpy(constraint
->option1
, constraint
->option1
+ 1);
1769 else if (pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1771 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1775 if (constraint
->choice1
[0] == '*')
1776 _cups_strcpy(constraint
->option2
, constraint
->choice1
+ 1);
1777 else if (pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1779 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1783 constraint
->choice1
[0] = '\0';
1784 constraint
->choice2
[0] = '\0';
1787 case 3 : /* Two options, one choice... */
1789 * Check for broken constraints like "* Option"...
1792 if (pg
->ppd_conform
== PPD_CONFORM_STRICT
&&
1793 (!strcmp(constraint
->option1
, "*") ||
1794 !strcmp(constraint
->choice1
, "*") ||
1795 !strcmp(constraint
->option2
, "*")))
1797 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1802 * The following _cups_strcpy's are safe, as optionN and
1803 * choiceN are all the same size (size defined by PPD spec...)
1806 if (constraint
->option1
[0] == '*')
1807 _cups_strcpy(constraint
->option1
, constraint
->option1
+ 1);
1808 else if (pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1810 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1814 if (constraint
->choice1
[0] == '*')
1816 if (pg
->ppd_conform
== PPD_CONFORM_STRICT
&&
1817 constraint
->option2
[0] == '*')
1819 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1823 _cups_strcpy(constraint
->choice2
, constraint
->option2
);
1824 _cups_strcpy(constraint
->option2
, constraint
->choice1
+ 1);
1825 constraint
->choice1
[0] = '\0';
1829 if (constraint
->option2
[0] == '*')
1830 _cups_strcpy(constraint
->option2
, constraint
->option2
+ 1);
1831 else if (pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1833 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1837 constraint
->choice2
[0] = '\0';
1841 case 4 : /* Two options, two choices... */
1843 * Check for broken constraints like "* Option"...
1846 if (pg
->ppd_conform
== PPD_CONFORM_STRICT
&&
1847 (!strcmp(constraint
->option1
, "*") ||
1848 !strcmp(constraint
->choice1
, "*") ||
1849 !strcmp(constraint
->option2
, "*") ||
1850 !strcmp(constraint
->choice2
, "*")))
1852 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1856 if (constraint
->option1
[0] == '*')
1857 _cups_strcpy(constraint
->option1
, constraint
->option1
+ 1);
1858 else if (pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1860 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1864 if (pg
->ppd_conform
== PPD_CONFORM_STRICT
&&
1865 constraint
->choice1
[0] == '*')
1867 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1871 if (constraint
->option2
[0] == '*')
1872 _cups_strcpy(constraint
->option2
, constraint
->option2
+ 1);
1873 else if (pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1875 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1879 if (pg
->ppd_conform
== PPD_CONFORM_STRICT
&&
1880 constraint
->choice2
[0] == '*')
1882 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1889 * Don't add this one as an attribute...
1892 _cupsStrFree(string
);
1895 else if (!strcmp(keyword
, "PaperDimension"))
1897 if ((size
= ppdPageSize(ppd
, name
)) == NULL
)
1898 size
= ppd_add_size(ppd
, name
);
1903 * Unable to add or find size!
1906 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1911 size
->width
= (float)_cupsStrScand(string
, &sptr
, loc
);
1912 size
->length
= (float)_cupsStrScand(sptr
, NULL
, loc
);
1914 _cupsStrFree(string
);
1917 else if (!strcmp(keyword
, "ImageableArea"))
1919 if ((size
= ppdPageSize(ppd
, name
)) == NULL
)
1920 size
= ppd_add_size(ppd
, name
);
1925 * Unable to add or find size!
1928 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1933 size
->left
= (float)_cupsStrScand(string
, &sptr
, loc
);
1934 size
->bottom
= (float)_cupsStrScand(sptr
, &sptr
, loc
);
1935 size
->right
= (float)_cupsStrScand(sptr
, &sptr
, loc
);
1936 size
->top
= (float)_cupsStrScand(sptr
, NULL
, loc
);
1938 _cupsStrFree(string
);
1941 else if (option
!= NULL
&&
1942 (mask
& (PPD_KEYWORD
| PPD_OPTION
| PPD_STRING
)) ==
1943 (PPD_KEYWORD
| PPD_OPTION
| PPD_STRING
) &&
1944 !strcmp(keyword
, option
->keyword
))
1946 DEBUG_printf(("2_ppdOpen: group=%p, subgroup=%p", group
, subgroup
));
1948 if (!strcmp(keyword
, "PageSize"))
1951 * Add a page size...
1954 if (ppdPageSize(ppd
, name
) == NULL
)
1955 ppd_add_size(ppd
, name
);
1959 * Add the option choice...
1962 if ((choice
= ppd_add_choice(option
, name
)) == NULL
)
1964 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1970 cupsCharsetToUTF8((cups_utf8_t
*)choice
->text
, text
,
1971 sizeof(choice
->text
), encoding
);
1972 else if (!strcmp(name
, "True"))
1973 strlcpy(choice
->text
, _("Yes"), sizeof(choice
->text
));
1974 else if (!strcmp(name
, "False"))
1975 strlcpy(choice
->text
, _("No"), sizeof(choice
->text
));
1977 strlcpy(choice
->text
, name
, sizeof(choice
->text
));
1979 if (option
->section
== PPD_ORDER_JCL
)
1980 ppd_decode(string
); /* Decode quoted string */
1982 choice
->code
= string
;
1983 string
= NULL
; /* Don't add as an attribute below */
1987 * Add remaining lines with keywords and string values as attributes...
1991 (mask
& (PPD_KEYWORD
| PPD_STRING
)) == (PPD_KEYWORD
| PPD_STRING
))
1992 ppd_add_attr(ppd
, keyword
, name
, text
, string
);
1994 _cupsStrFree(string
);
1998 * Check for a missing CloseGroup...
2001 if (group
&& pg
->ppd_conform
== PPD_CONFORM_STRICT
)
2003 pg
->ppd_status
= PPD_MISSING_CLOSE_GROUP
;
2007 ppd_free(line
.buffer
);
2010 * Reset language preferences...
2014 if (!cupsFileEOF(fp
))
2015 DEBUG_printf(("1_ppdOpen: Premature EOF at %lu...\n",
2016 (unsigned long)cupsFileTell(fp
)));
2019 if (pg
->ppd_status
!= PPD_OK
)
2022 * Had an error reading the PPD file, cannot continue!
2031 * Update the filters array as needed...
2034 if (!ppd_update_filters(ppd
, pg
))
2042 * Create the sorted options array and set the option back-pointer for
2043 * each choice and custom option...
2046 ppd
->options
= cupsArrayNew2((cups_array_func_t
)ppd_compare_options
, NULL
,
2047 (cups_ahash_func_t
)ppd_hash_option
,
2050 for (i
= ppd
->num_groups
, group
= ppd
->groups
;
2054 for (j
= group
->num_options
, option
= group
->options
;
2058 ppd_coption_t
*coption
; /* Custom option */
2061 cupsArrayAdd(ppd
->options
, option
);
2063 for (k
= 0; k
< option
->num_choices
; k
++)
2064 option
->choices
[k
].option
= option
;
2066 if ((coption
= ppdFindCustomOption(ppd
, option
->keyword
)) != NULL
)
2067 coption
->option
= option
;
2072 * Create an array to track the marked choices...
2075 ppd
->marked
= cupsArrayNew((cups_array_func_t
)ppd_compare_choices
, NULL
);
2078 * Return the PPD file structure...
2084 * Common exit point for errors to save code size...
2089 _cupsStrFree(string
);
2090 ppd_free(line
.buffer
);
2099 * 'ppdOpen()' - Read a PPD file into memory.
2102 ppd_file_t
* /* O - PPD file record */
2103 ppdOpen(FILE *fp
) /* I - File to read from */
2105 ppd_file_t
*ppd
; /* PPD file record */
2106 cups_file_t
*cf
; /* CUPS file */
2110 * Reopen the stdio file as a CUPS file...
2113 if ((cf
= cupsFileOpenFd(fileno(fp
), "r")) == NULL
)
2117 * Load the PPD file using the newer API...
2120 ppd
= _ppdOpen(cf
, _PPD_LOCALIZATION_DEFAULT
);
2123 * Close the CUPS file and return the PPD...
2133 * 'ppdOpen2()' - Read a PPD file into memory.
2135 * @since CUPS 1.2/macOS 10.5@
2138 ppd_file_t
* /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
2139 ppdOpen2(cups_file_t
*fp
) /* I - File to read from */
2141 return _ppdOpen(fp
, _PPD_LOCALIZATION_DEFAULT
);
2146 * 'ppdOpenFd()' - Read a PPD file into memory.
2149 ppd_file_t
* /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
2150 ppdOpenFd(int fd
) /* I - File to read from */
2152 cups_file_t
*fp
; /* CUPS file pointer */
2153 ppd_file_t
*ppd
; /* PPD file record */
2154 _ppd_globals_t
*pg
= _ppdGlobals();
2159 * Set the line number to 0...
2165 * Range check input...
2170 pg
->ppd_status
= PPD_NULL_FILE
;
2176 * Try to open the file and parse it...
2179 if ((fp
= cupsFileOpenFd(fd
, "r")) != NULL
)
2187 pg
->ppd_status
= PPD_FILE_OPEN_ERROR
;
2196 * '_ppdOpenFile()' - Read a PPD file into memory.
2199 ppd_file_t
* /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
2200 _ppdOpenFile(const char *filename
, /* I - File to read from */
2201 _ppd_localization_t localization
) /* I - Localization to load */
2203 cups_file_t
*fp
; /* File pointer */
2204 ppd_file_t
*ppd
; /* PPD file record */
2205 _ppd_globals_t
*pg
= _ppdGlobals();
2210 * Set the line number to 0...
2216 * Range check input...
2219 if (filename
== NULL
)
2221 pg
->ppd_status
= PPD_NULL_FILE
;
2227 * Try to open the file and parse it...
2230 if ((fp
= cupsFileOpen(filename
, "r")) != NULL
)
2232 ppd
= _ppdOpen(fp
, localization
);
2238 pg
->ppd_status
= PPD_FILE_OPEN_ERROR
;
2247 * 'ppdOpenFile()' - Read a PPD file into memory.
2250 ppd_file_t
* /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
2251 ppdOpenFile(const char *filename
) /* I - File to read from */
2253 return _ppdOpenFile(filename
, _PPD_LOCALIZATION_DEFAULT
);
2258 * 'ppdSetConformance()' - Set the conformance level for PPD files.
2260 * @since CUPS 1.1.20/macOS 10.4@
2264 ppdSetConformance(ppd_conform_t c
) /* I - Conformance level */
2266 _ppd_globals_t
*pg
= _ppdGlobals();
2270 pg
->ppd_conform
= c
;
2275 * 'ppd_add_attr()' - Add an attribute to the PPD data.
2278 static ppd_attr_t
* /* O - New attribute */
2279 ppd_add_attr(ppd_file_t
*ppd
, /* I - PPD file data */
2280 const char *name
, /* I - Attribute name */
2281 const char *spec
, /* I - Specifier string, if any */
2282 const char *text
, /* I - Text string, if any */
2283 const char *value
) /* I - Value of attribute */
2285 ppd_attr_t
**ptr
, /* New array */
2286 *temp
; /* New attribute */
2290 * Range check input...
2293 if (ppd
== NULL
|| name
== NULL
|| spec
== NULL
)
2297 * Create the array as needed...
2300 if (!ppd
->sorted_attrs
)
2301 ppd
->sorted_attrs
= cupsArrayNew((cups_array_func_t
)ppd_compare_attrs
,
2305 * Allocate memory for the new attribute...
2308 if (ppd
->num_attrs
== 0)
2309 ptr
= malloc(sizeof(ppd_attr_t
*));
2311 ptr
= realloc(ppd
->attrs
, (size_t)(ppd
->num_attrs
+ 1) * sizeof(ppd_attr_t
*));
2317 ptr
+= ppd
->num_attrs
;
2319 if ((temp
= calloc(1, sizeof(ppd_attr_t
))) == NULL
)
2330 strlcpy(temp
->name
, name
, sizeof(temp
->name
));
2331 strlcpy(temp
->spec
, spec
, sizeof(temp
->spec
));
2332 strlcpy(temp
->text
, text
, sizeof(temp
->text
));
2333 temp
->value
= (char *)value
;
2336 * Add the attribute to the sorted array...
2339 cupsArrayAdd(ppd
->sorted_attrs
, temp
);
2342 * Return the attribute...
2350 * 'ppd_add_choice()' - Add a choice to an option.
2353 static ppd_choice_t
* /* O - Named choice */
2354 ppd_add_choice(ppd_option_t
*option
, /* I - Option */
2355 const char *name
) /* I - Name of choice */
2357 ppd_choice_t
*choice
; /* Choice */
2360 if (option
->num_choices
== 0)
2361 choice
= malloc(sizeof(ppd_choice_t
));
2363 choice
= realloc(option
->choices
, sizeof(ppd_choice_t
) * (size_t)(option
->num_choices
+ 1));
2368 option
->choices
= choice
;
2369 choice
+= option
->num_choices
;
2370 option
->num_choices
++;
2372 memset(choice
, 0, sizeof(ppd_choice_t
));
2373 strlcpy(choice
->choice
, name
, sizeof(choice
->choice
));
2380 * 'ppd_add_size()' - Add a page size.
2383 static ppd_size_t
* /* O - Named size */
2384 ppd_add_size(ppd_file_t
*ppd
, /* I - PPD file */
2385 const char *name
) /* I - Name of size */
2387 ppd_size_t
*size
; /* Size */
2390 if (ppd
->num_sizes
== 0)
2391 size
= malloc(sizeof(ppd_size_t
));
2393 size
= realloc(ppd
->sizes
, sizeof(ppd_size_t
) * (size_t)(ppd
->num_sizes
+ 1));
2399 size
+= ppd
->num_sizes
;
2402 memset(size
, 0, sizeof(ppd_size_t
));
2403 strlcpy(size
->name
, name
, sizeof(size
->name
));
2410 * 'ppd_compare_attrs()' - Compare two attributes.
2413 static int /* O - Result of comparison */
2414 ppd_compare_attrs(ppd_attr_t
*a
, /* I - First attribute */
2415 ppd_attr_t
*b
) /* I - Second attribute */
2417 return (_cups_strcasecmp(a
->name
, b
->name
));
2422 * 'ppd_compare_choices()' - Compare two choices...
2425 static int /* O - Result of comparison */
2426 ppd_compare_choices(ppd_choice_t
*a
, /* I - First choice */
2427 ppd_choice_t
*b
) /* I - Second choice */
2429 return (strcmp(a
->option
->keyword
, b
->option
->keyword
));
2434 * 'ppd_compare_coptions()' - Compare two custom options.
2437 static int /* O - Result of comparison */
2438 ppd_compare_coptions(ppd_coption_t
*a
, /* I - First option */
2439 ppd_coption_t
*b
) /* I - Second option */
2441 return (_cups_strcasecmp(a
->keyword
, b
->keyword
));
2446 * 'ppd_compare_options()' - Compare two options.
2449 static int /* O - Result of comparison */
2450 ppd_compare_options(ppd_option_t
*a
, /* I - First option */
2451 ppd_option_t
*b
) /* I - Second option */
2453 return (_cups_strcasecmp(a
->keyword
, b
->keyword
));
2458 * 'ppd_decode()' - Decode a string value...
2461 static int /* O - Length of decoded string */
2462 ppd_decode(char *string
) /* I - String to decode */
2464 char *inptr
, /* Input pointer */
2465 *outptr
; /* Output pointer */
2471 while (*inptr
!= '\0')
2472 if (*inptr
== '<' && isxdigit(inptr
[1] & 255))
2475 * Convert hex to 8-bit values...
2479 while (isxdigit(*inptr
& 255))
2481 if (_cups_isalpha(*inptr
))
2482 *outptr
= (char)((tolower(*inptr
) - 'a' + 10) << 4);
2484 *outptr
= (char)((*inptr
- '0') << 4);
2488 if (!isxdigit(*inptr
& 255))
2491 if (_cups_isalpha(*inptr
))
2492 *outptr
|= (char)(tolower(*inptr
) - 'a' + 10);
2494 *outptr
|= (char)(*inptr
- '0');
2500 while (*inptr
!= '>' && *inptr
!= '\0')
2502 while (*inptr
== '>')
2506 *outptr
++ = *inptr
++;
2510 return ((int)(outptr
- string
));
2515 * 'ppd_free_filters()' - Free the filters array.
2519 ppd_free_filters(ppd_file_t
*ppd
) /* I - PPD file */
2521 int i
; /* Looping var */
2522 char **filter
; /* Current filter */
2525 if (ppd
->num_filters
> 0)
2527 for (i
= ppd
->num_filters
, filter
= ppd
->filters
; i
> 0; i
--, filter
++)
2528 _cupsStrFree(*filter
);
2530 ppd_free(ppd
->filters
);
2532 ppd
->num_filters
= 0;
2533 ppd
->filters
= NULL
;
2539 * 'ppd_free_group()' - Free a single UI group.
2543 ppd_free_group(ppd_group_t
*group
) /* I - Group to free */
2545 int i
; /* Looping var */
2546 ppd_option_t
*option
; /* Current option */
2547 ppd_group_t
*subgroup
; /* Current sub-group */
2550 if (group
->num_options
> 0)
2552 for (i
= group
->num_options
, option
= group
->options
;
2555 ppd_free_option(option
);
2557 ppd_free(group
->options
);
2560 if (group
->num_subgroups
> 0)
2562 for (i
= group
->num_subgroups
, subgroup
= group
->subgroups
;
2565 ppd_free_group(subgroup
);
2567 ppd_free(group
->subgroups
);
2573 * 'ppd_free_option()' - Free a single option.
2577 ppd_free_option(ppd_option_t
*option
) /* I - Option to free */
2579 int i
; /* Looping var */
2580 ppd_choice_t
*choice
; /* Current choice */
2583 if (option
->num_choices
> 0)
2585 for (i
= option
->num_choices
, choice
= option
->choices
;
2589 _cupsStrFree(choice
->code
);
2592 ppd_free(option
->choices
);
2598 * 'ppd_get_coption()' - Get a custom option record.
2601 static ppd_coption_t
* /* O - Custom option... */
2602 ppd_get_coption(ppd_file_t
*ppd
, /* I - PPD file */
2603 const char *name
) /* I - Name of option */
2605 ppd_coption_t
*copt
; /* New custom option */
2609 * See if the option already exists...
2612 if ((copt
= ppdFindCustomOption(ppd
, name
)) != NULL
)
2616 * Not found, so create the custom option record...
2619 if ((copt
= calloc(1, sizeof(ppd_coption_t
))) == NULL
)
2622 strlcpy(copt
->keyword
, name
, sizeof(copt
->keyword
));
2624 copt
->params
= cupsArrayNew((cups_array_func_t
)NULL
, NULL
);
2626 cupsArrayAdd(ppd
->coptions
, copt
);
2629 * Return the new record...
2637 * 'ppd_get_cparam()' - Get a custom parameter record.
2640 static ppd_cparam_t
* /* O - Extended option... */
2641 ppd_get_cparam(ppd_coption_t
*opt
, /* I - PPD file */
2642 const char *param
, /* I - Name of parameter */
2643 const char *text
) /* I - Human-readable text */
2645 ppd_cparam_t
*cparam
; /* New custom parameter */
2649 * See if the parameter already exists...
2652 if ((cparam
= ppdFindCustomParam(opt
, param
)) != NULL
)
2656 * Not found, so create the custom parameter record...
2659 if ((cparam
= calloc(1, sizeof(ppd_cparam_t
))) == NULL
)
2662 strlcpy(cparam
->name
, param
, sizeof(cparam
->name
));
2663 strlcpy(cparam
->text
, text
[0] ? text
: param
, sizeof(cparam
->text
));
2666 * Add this record to the array...
2669 cupsArrayAdd(opt
->params
, cparam
);
2672 * Return the new record...
2680 * 'ppd_get_group()' - Find or create the named group as needed.
2683 static ppd_group_t
* /* O - Named group */
2684 ppd_get_group(ppd_file_t
*ppd
, /* I - PPD file */
2685 const char *name
, /* I - Name of group */
2686 const char *text
, /* I - Text for group */
2687 _ppd_globals_t
*pg
, /* I - Global data */
2688 cups_encoding_t encoding
) /* I - Encoding of text */
2690 int i
; /* Looping var */
2691 ppd_group_t
*group
; /* Group */
2694 DEBUG_printf(("7ppd_get_group(ppd=%p, name=\"%s\", text=\"%s\", cg=%p)",
2695 ppd
, name
, text
, pg
));
2697 for (i
= ppd
->num_groups
, group
= ppd
->groups
; i
> 0; i
--, group
++)
2698 if (!strcmp(group
->name
, name
))
2703 DEBUG_printf(("8ppd_get_group: Adding group %s...", name
));
2705 if (pg
->ppd_conform
== PPD_CONFORM_STRICT
&& strlen(text
) >= sizeof(group
->text
))
2707 pg
->ppd_status
= PPD_ILLEGAL_TRANSLATION
;
2712 if (ppd
->num_groups
== 0)
2713 group
= malloc(sizeof(ppd_group_t
));
2715 group
= realloc(ppd
->groups
, (size_t)(ppd
->num_groups
+ 1) * sizeof(ppd_group_t
));
2719 pg
->ppd_status
= PPD_ALLOC_ERROR
;
2724 ppd
->groups
= group
;
2725 group
+= ppd
->num_groups
;
2728 memset(group
, 0, sizeof(ppd_group_t
));
2729 strlcpy(group
->name
, name
, sizeof(group
->name
));
2731 cupsCharsetToUTF8((cups_utf8_t
*)group
->text
, text
,
2732 sizeof(group
->text
), encoding
);
2740 * 'ppd_get_option()' - Find or create the named option as needed.
2743 static ppd_option_t
* /* O - Named option */
2744 ppd_get_option(ppd_group_t
*group
, /* I - Group */
2745 const char *name
) /* I - Name of option */
2747 int i
; /* Looping var */
2748 ppd_option_t
*option
; /* Option */
2751 DEBUG_printf(("7ppd_get_option(group=%p(\"%s\"), name=\"%s\")",
2752 group
, group
->name
, name
));
2754 for (i
= group
->num_options
, option
= group
->options
; i
> 0; i
--, option
++)
2755 if (!strcmp(option
->keyword
, name
))
2760 if (group
->num_options
== 0)
2761 option
= malloc(sizeof(ppd_option_t
));
2763 option
= realloc(group
->options
, (size_t)(group
->num_options
+ 1) * sizeof(ppd_option_t
));
2768 group
->options
= option
;
2769 option
+= group
->num_options
;
2770 group
->num_options
++;
2772 memset(option
, 0, sizeof(ppd_option_t
));
2773 strlcpy(option
->keyword
, name
, sizeof(option
->keyword
));
2781 * 'ppd_globals_alloc()' - Allocate and initialize global data.
2784 static _ppd_globals_t
* /* O - Pointer to global data */
2785 ppd_globals_alloc(void)
2787 return ((_ppd_globals_t
*)calloc(1, sizeof(_ppd_globals_t
)));
2792 * 'ppd_globals_free()' - Free global data.
2795 #if defined(HAVE_PTHREAD_H) || defined(WIN32)
2797 ppd_globals_free(_ppd_globals_t
*pg
) /* I - Pointer to global data */
2801 #endif /* HAVE_PTHREAD_H || WIN32 */
2804 #ifdef HAVE_PTHREAD_H
2806 * 'ppd_globals_init()' - Initialize per-thread globals...
2810 ppd_globals_init(void)
2813 * Register the global data for this thread...
2816 pthread_key_create(&ppd_globals_key
, (void (*)(void *))ppd_globals_free
);
2818 #endif /* HAVE_PTHREAD_H */
2822 * 'ppd_hash_option()' - Generate a hash of the option name...
2825 static int /* O - Hash index */
2826 ppd_hash_option(ppd_option_t
*option
) /* I - Option */
2828 int hash
= 0; /* Hash index */
2829 const char *k
; /* Pointer into keyword */
2832 for (hash
= option
->keyword
[0], k
= option
->keyword
+ 1; *k
;)
2833 hash
= 33 * hash
+ *k
++;
2835 return (hash
& 511);
2840 * 'ppd_read()' - Read a line from a PPD file, skipping comment lines as
2844 static int /* O - Bitmask of fields read */
2845 ppd_read(cups_file_t
*fp
, /* I - File to read from */
2846 _ppd_line_t
*line
, /* I - Line buffer */
2847 char *keyword
, /* O - Keyword from line */
2848 char *option
, /* O - Option from line */
2849 char *text
, /* O - Human-readable text from line */
2850 char **string
, /* O - Code/string data */
2851 int ignoreblank
, /* I - Ignore blank lines? */
2852 _ppd_globals_t
*pg
) /* I - Global data */
2854 int ch
, /* Character from file */
2855 col
, /* Column in line */
2856 colon
, /* Colon seen? */
2857 endquote
, /* Waiting for an end quote */
2858 mask
, /* Mask to be returned */
2859 startline
, /* Start line */
2860 textlen
; /* Length of text */
2861 char *keyptr
, /* Keyword pointer */
2862 *optptr
, /* Option pointer */
2863 *textptr
, /* Text pointer */
2864 *strptr
, /* Pointer into string */
2865 *lineptr
; /* Current position in line buffer */
2869 * Now loop until we have a valid line...
2874 startline
= pg
->ppd_line
+ 1;
2878 line
->bufsize
= 1024;
2879 line
->buffer
= malloc(1024);
2891 lineptr
= line
->buffer
;
2895 while ((ch
= cupsFileGetChar(fp
)) != EOF
)
2897 if (lineptr
>= (line
->buffer
+ line
->bufsize
- 1))
2900 * Expand the line buffer...
2903 char *temp
; /* Temporary line pointer */
2906 line
->bufsize
+= 1024;
2907 if (line
->bufsize
> 262144)
2910 * Don't allow lines longer than 256k!
2913 pg
->ppd_line
= startline
;
2914 pg
->ppd_status
= PPD_LINE_TOO_LONG
;
2919 temp
= realloc(line
->buffer
, line
->bufsize
);
2922 pg
->ppd_line
= startline
;
2923 pg
->ppd_status
= PPD_LINE_TOO_LONG
;
2928 lineptr
= temp
+ (lineptr
- line
->buffer
);
2929 line
->buffer
= temp
;
2932 if (ch
== '\r' || ch
== '\n')
2935 * Line feed or carriage return...
2944 * Check for a trailing line feed...
2947 if ((ch
= cupsFilePeekChar(fp
)) == EOF
)
2954 cupsFileGetChar(fp
);
2957 if (lineptr
== line
->buffer
&& ignoreblank
)
2958 continue; /* Skip blank lines */
2962 if (!endquote
) /* Continue for multi-line text */
2967 else if (ch
< ' ' && ch
!= '\t' && pg
->ppd_conform
== PPD_CONFORM_STRICT
)
2970 * Other control characters...
2973 pg
->ppd_line
= startline
;
2974 pg
->ppd_status
= PPD_ILLEGAL_CHARACTER
;
2978 else if (ch
!= 0x1a)
2981 * Any other character...
2984 *lineptr
++ = (char)ch
;
2987 if (col
> (PPD_MAX_LINE
- 1))
2990 * Line is too long...
2993 pg
->ppd_line
= startline
;
2994 pg
->ppd_status
= PPD_LINE_TOO_LONG
;
2999 if (ch
== ':' && strncmp(line
->buffer
, "*%", 2) != 0)
3002 if (ch
== '\"' && colon
)
3003 endquote
= !endquote
;
3010 * Didn't finish this quoted string...
3013 while ((ch
= cupsFileGetChar(fp
)) != EOF
)
3016 else if (ch
== '\r' || ch
== '\n')
3024 * Check for a trailing line feed...
3027 if ((ch
= cupsFilePeekChar(fp
)) == EOF
)
3030 cupsFileGetChar(fp
);
3033 else if (ch
< ' ' && ch
!= '\t' && pg
->ppd_conform
== PPD_CONFORM_STRICT
)
3036 * Other control characters...
3039 pg
->ppd_line
= startline
;
3040 pg
->ppd_status
= PPD_ILLEGAL_CHARACTER
;
3044 else if (ch
!= 0x1a)
3048 if (col
> (PPD_MAX_LINE
- 1))
3051 * Line is too long...
3054 pg
->ppd_line
= startline
;
3055 pg
->ppd_status
= PPD_LINE_TOO_LONG
;
3065 * Didn't finish this line...
3068 while ((ch
= cupsFileGetChar(fp
)) != EOF
)
3069 if (ch
== '\r' || ch
== '\n')
3072 * Line feed or carriage return...
3081 * Check for a trailing line feed...
3084 if ((ch
= cupsFilePeekChar(fp
)) == EOF
)
3087 cupsFileGetChar(fp
);
3092 else if (ch
< ' ' && ch
!= '\t' && pg
->ppd_conform
== PPD_CONFORM_STRICT
)
3095 * Other control characters...
3098 pg
->ppd_line
= startline
;
3099 pg
->ppd_status
= PPD_ILLEGAL_CHARACTER
;
3103 else if (ch
!= 0x1a)
3107 if (col
> (PPD_MAX_LINE
- 1))
3110 * Line is too long...
3113 pg
->ppd_line
= startline
;
3114 pg
->ppd_status
= PPD_LINE_TOO_LONG
;
3121 if (lineptr
> line
->buffer
&& lineptr
[-1] == '\n')
3126 DEBUG_printf(("9ppd_read: LINE=\"%s\"", line
->buffer
));
3129 * The dynamically created PPDs for older style macOS
3130 * drivers include a large blob of data inserted as comments
3131 * at the end of the file. As an optimization we can stop
3132 * reading the PPD when we get to the start of this data.
3135 if (!strcmp(line
->buffer
, "*%APLWORKSET START"))
3138 if (ch
== EOF
&& lineptr
== line
->buffer
)
3146 lineptr
= line
->buffer
+ 1;
3153 if ((!line
->buffer
[0] || /* Blank line */
3154 !strncmp(line
->buffer
, "*%", 2) || /* Comment line */
3155 !strcmp(line
->buffer
, "*End")) && /* End of multi-line string */
3156 ignoreblank
) /* Ignore these? */
3158 startline
= pg
->ppd_line
+ 1;
3162 if (!strcmp(line
->buffer
, "*")) /* (Bad) comment line */
3164 if (pg
->ppd_conform
== PPD_CONFORM_RELAXED
)
3166 startline
= pg
->ppd_line
+ 1;
3171 pg
->ppd_line
= startline
;
3172 pg
->ppd_status
= PPD_ILLEGAL_MAIN_KEYWORD
;
3178 if (line
->buffer
[0] != '*') /* All lines start with an asterisk */
3181 * Allow lines consisting of just whitespace...
3184 for (lineptr
= line
->buffer
; *lineptr
; lineptr
++)
3185 if (*lineptr
&& !_cups_isspace(*lineptr
))
3190 pg
->ppd_status
= PPD_MISSING_ASTERISK
;
3193 else if (ignoreblank
)
3205 while (*lineptr
&& *lineptr
!= ':' && !_cups_isspace(*lineptr
))
3207 if (*lineptr
<= ' ' || *lineptr
> 126 || *lineptr
== '/' ||
3208 (keyptr
- keyword
) >= (PPD_MAX_NAME
- 1))
3210 pg
->ppd_status
= PPD_ILLEGAL_MAIN_KEYWORD
;
3214 *keyptr
++ = *lineptr
++;
3219 if (!strcmp(keyword
, "End"))
3222 mask
|= PPD_KEYWORD
;
3224 if (_cups_isspace(*lineptr
))
3227 * Get an option name...
3230 while (_cups_isspace(*lineptr
))
3235 while (*lineptr
&& !_cups_isspace(*lineptr
) && *lineptr
!= ':' &&
3238 if (*lineptr
<= ' ' || *lineptr
> 126 ||
3239 (optptr
- option
) >= (PPD_MAX_NAME
- 1))
3241 pg
->ppd_status
= PPD_ILLEGAL_OPTION_KEYWORD
;
3245 *optptr
++ = *lineptr
++;
3250 if (_cups_isspace(*lineptr
) && pg
->ppd_conform
== PPD_CONFORM_STRICT
)
3252 pg
->ppd_status
= PPD_ILLEGAL_WHITESPACE
;
3256 while (_cups_isspace(*lineptr
))
3261 if (*lineptr
== '/')
3264 * Get human-readable text...
3271 while (*lineptr
!= '\0' && *lineptr
!= '\n' && *lineptr
!= ':')
3273 if (((unsigned char)*lineptr
< ' ' && *lineptr
!= '\t') ||
3274 (textptr
- text
) >= (PPD_MAX_LINE
- 1))
3276 pg
->ppd_status
= PPD_ILLEGAL_TRANSLATION
;
3280 *textptr
++ = *lineptr
++;
3284 textlen
= ppd_decode(text
);
3286 if (textlen
> PPD_MAX_TEXT
&& pg
->ppd_conform
== PPD_CONFORM_STRICT
)
3288 pg
->ppd_status
= PPD_ILLEGAL_TRANSLATION
;
3296 if (_cups_isspace(*lineptr
) && pg
->ppd_conform
== PPD_CONFORM_STRICT
)
3298 pg
->ppd_status
= PPD_ILLEGAL_WHITESPACE
;
3302 while (_cups_isspace(*lineptr
))
3305 if (*lineptr
== ':')
3308 * Get string after triming leading and trailing whitespace...
3312 while (_cups_isspace(*lineptr
))
3315 strptr
= lineptr
+ strlen(lineptr
) - 1;
3316 while (strptr
>= lineptr
&& _cups_isspace(*strptr
))
3319 if (*strptr
== '\"')
3322 * Quoted string by itself, remove quotes...
3329 *string
= _cupsStrAlloc(lineptr
);
3341 * 'ppd_update_filters()' - Update the filters array as needed.
3343 * This function re-populates the filters array with cupsFilter2 entries that
3344 * have been stripped of the destination MIME media types and any maxsize hints.
3346 * (All for backwards-compatibility)
3349 static int /* O - 1 on success, 0 on failure */
3350 ppd_update_filters(ppd_file_t
*ppd
, /* I - PPD file */
3351 _ppd_globals_t
*pg
) /* I - Global data */
3353 ppd_attr_t
*attr
; /* Current cupsFilter2 value */
3354 char srcsuper
[16], /* Source MIME media type */
3356 dstsuper
[16], /* Destination MIME media type */
3358 program
[1024], /* Command to run */
3359 *ptr
, /* Pointer into command to run */
3360 buffer
[1024], /* Re-written cupsFilter value */
3361 **filter
; /* Current filter */
3362 int cost
; /* Cost of filter */
3365 DEBUG_printf(("4ppd_update_filters(ppd=%p, cg=%p)", ppd
, pg
));
3368 * See if we have any cupsFilter2 lines...
3371 if ((attr
= ppdFindAttr(ppd
, "cupsFilter2", NULL
)) == NULL
)
3373 DEBUG_puts("5ppd_update_filters: No cupsFilter2 keywords present.");
3378 * Yes, free the cupsFilter-defined filters and re-build...
3381 ppd_free_filters(ppd
);
3386 * Parse the cupsFilter2 string:
3388 * src/type dst/type cost program
3389 * src/type dst/type cost maxsize(n) program
3392 DEBUG_printf(("5ppd_update_filters: cupsFilter2=\"%s\"", attr
->value
));
3394 if (sscanf(attr
->value
, "%15[^/]/%255s%*[ \t]%15[^/]/%255s%d%*[ \t]%1023[^\n]",
3395 srcsuper
, srctype
, dstsuper
, dsttype
, &cost
, program
) != 6)
3397 DEBUG_puts("5ppd_update_filters: Bad cupsFilter2 line.");
3398 pg
->ppd_status
= PPD_BAD_VALUE
;
3403 DEBUG_printf(("5ppd_update_filters: srcsuper=\"%s\", srctype=\"%s\", "
3404 "dstsuper=\"%s\", dsttype=\"%s\", cost=%d, program=\"%s\"",
3405 srcsuper
, srctype
, dstsuper
, dsttype
, cost
, program
));
3407 if (!strncmp(program
, "maxsize(", 8) &&
3408 (ptr
= strchr(program
+ 8, ')')) != NULL
)
3410 DEBUG_puts("5ppd_update_filters: Found maxsize(nnn).");
3413 while (_cups_isspace(*ptr
))
3416 _cups_strcpy(program
, ptr
);
3417 DEBUG_printf(("5ppd_update_filters: New program=\"%s\"", program
));
3421 * Convert to cupsFilter format:
3423 * src/type cost program
3426 snprintf(buffer
, sizeof(buffer
), "%s/%s %d %s", srcsuper
, srctype
, cost
,
3428 DEBUG_printf(("5ppd_update_filters: Adding \"%s\".", buffer
));
3431 * Add a cupsFilter-compatible string to the filters array.
3434 if (ppd
->num_filters
== 0)
3435 filter
= malloc(sizeof(char *));
3437 filter
= realloc(ppd
->filters
, sizeof(char *) * (size_t)(ppd
->num_filters
+ 1));
3441 DEBUG_puts("5ppd_update_filters: Out of memory.");
3442 pg
->ppd_status
= PPD_ALLOC_ERROR
;
3447 ppd
->filters
= filter
;
3448 filter
+= ppd
->num_filters
;
3449 ppd
->num_filters
++;
3451 *filter
= _cupsStrAlloc(buffer
);
3453 while ((attr
= ppdFindNextAttr(ppd
, "cupsFilter2", NULL
)) != NULL
);
3455 DEBUG_puts("5ppd_update_filters: Completed OK.");