2 * PPD file routines for CUPS.
4 * Copyright © 2007-2019 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"
19 #include "debug-internal.h"
26 #define PPD_KEYWORD 1 /* Line contained a keyword */
27 #define PPD_OPTION 2 /* Line contained an option name */
28 #define PPD_TEXT 4 /* Line contained human-readable text */
29 #define PPD_STRING 8 /* Line contained a string or code */
31 #define PPD_HASHSIZE 512 /* Size of hash */
35 * Line buffer structure...
38 typedef struct _ppd_line_s
40 char *buffer
; /* Pointer to buffer */
41 size_t bufsize
; /* Size of the buffer */
49 static _cups_threadkey_t ppd_globals_key
= _CUPS_THREADKEY_INITIALIZER
;
50 /* Thread local storage key */
52 static pthread_once_t ppd_globals_key_once
= PTHREAD_ONCE_INIT
;
53 /* One-time initialization object */
54 #endif /* HAVE_PTHREAD_H */
61 static ppd_attr_t
*ppd_add_attr(ppd_file_t
*ppd
, const char *name
,
62 const char *spec
, const char *text
,
64 static ppd_choice_t
*ppd_add_choice(ppd_option_t
*option
, const char *name
);
65 static ppd_size_t
*ppd_add_size(ppd_file_t
*ppd
, const char *name
);
66 static int ppd_compare_attrs(ppd_attr_t
*a
, ppd_attr_t
*b
);
67 static int ppd_compare_choices(ppd_choice_t
*a
, ppd_choice_t
*b
);
68 static int ppd_compare_coptions(ppd_coption_t
*a
,
70 static int ppd_compare_options(ppd_option_t
*a
, ppd_option_t
*b
);
71 static int ppd_decode(char *string
);
72 static void ppd_free_filters(ppd_file_t
*ppd
);
73 static void ppd_free_group(ppd_group_t
*group
);
74 static void ppd_free_option(ppd_option_t
*option
);
75 static ppd_coption_t
*ppd_get_coption(ppd_file_t
*ppd
, const char *name
);
76 static ppd_cparam_t
*ppd_get_cparam(ppd_coption_t
*opt
,
79 static ppd_group_t
*ppd_get_group(ppd_file_t
*ppd
, const char *name
,
80 const char *text
, _ppd_globals_t
*pg
,
81 cups_encoding_t encoding
);
82 static ppd_option_t
*ppd_get_option(ppd_group_t
*group
, const char *name
);
83 static _ppd_globals_t
*ppd_globals_alloc(void);
84 #if defined(HAVE_PTHREAD_H) || defined(_WIN32)
85 static void ppd_globals_free(_ppd_globals_t
*g
);
86 #endif /* HAVE_PTHREAD_H || _WIN32 */
88 static void ppd_globals_init(void);
89 #endif /* HAVE_PTHREAD_H */
90 static int ppd_hash_option(ppd_option_t
*option
);
91 static int ppd_read(cups_file_t
*fp
, _ppd_line_t
*line
,
92 char *keyword
, char *option
, char *text
,
93 char **string
, int ignoreblank
,
95 static int ppd_update_filters(ppd_file_t
*ppd
,
100 * 'ppdClose()' - Free all memory used by the PPD file.
104 ppdClose(ppd_file_t
*ppd
) /* I - PPD file record */
106 int i
; /* Looping var */
107 ppd_group_t
*group
; /* Current group */
108 char **font
; /* Current font */
109 ppd_attr_t
**attr
; /* Current attribute */
110 ppd_coption_t
*coption
; /* Current custom option */
111 ppd_cparam_t
*cparam
; /* Current custom parameter */
115 * Range check arguments...
122 * Free all strings at the top level...
125 free(ppd
->lang_encoding
);
128 free(ppd
->emulations
);
129 free(ppd
->jcl_begin
);
134 * Free any UI groups, subgroups, and options...
137 if (ppd
->num_groups
> 0)
139 for (i
= ppd
->num_groups
, group
= ppd
->groups
; i
> 0; i
--, group
++)
140 ppd_free_group(group
);
145 cupsArrayDelete(ppd
->options
);
146 cupsArrayDelete(ppd
->marked
);
149 * Free any page sizes...
152 if (ppd
->num_sizes
> 0)
156 * Free any constraints...
159 if (ppd
->num_consts
> 0)
163 * Free any filters...
166 ppd_free_filters(ppd
);
172 if (ppd
->num_fonts
> 0)
174 for (i
= ppd
->num_fonts
, font
= ppd
->fonts
; i
> 0; i
--, font
++)
181 * Free any profiles...
184 if (ppd
->num_profiles
> 0)
188 * Free any attributes...
191 if (ppd
->num_attrs
> 0)
193 for (i
= ppd
->num_attrs
, attr
= ppd
->attrs
; i
> 0; i
--, attr
++)
195 free((*attr
)->value
);
202 cupsArrayDelete(ppd
->sorted_attrs
);
205 * Free custom options...
208 for (coption
= (ppd_coption_t
*)cupsArrayFirst(ppd
->coptions
);
210 coption
= (ppd_coption_t
*)cupsArrayNext(ppd
->coptions
))
212 for (cparam
= (ppd_cparam_t
*)cupsArrayFirst(coption
->params
);
214 cparam
= (ppd_cparam_t
*)cupsArrayNext(coption
->params
))
216 switch (cparam
->type
)
218 case PPD_CUSTOM_PASSCODE
:
219 case PPD_CUSTOM_PASSWORD
:
220 case PPD_CUSTOM_STRING
:
221 free(cparam
->current
.custom_string
);
231 cupsArrayDelete(coption
->params
);
236 cupsArrayDelete(ppd
->coptions
);
239 * Free constraints...
242 if (ppd
->cups_uiconstraints
)
244 _ppd_cups_uiconsts_t
*consts
; /* Current constraints */
247 for (consts
= (_ppd_cups_uiconsts_t
*)cupsArrayFirst(ppd
->cups_uiconstraints
);
249 consts
= (_ppd_cups_uiconsts_t
*)cupsArrayNext(ppd
->cups_uiconstraints
))
251 free(consts
->constraints
);
255 cupsArrayDelete(ppd
->cups_uiconstraints
);
259 * Free any PPD cache/mapping data...
263 _ppdCacheDestroy(ppd
->cache
);
266 * Free the whole record...
274 * 'ppdErrorString()' - Returns the text associated with a status.
276 * @since CUPS 1.1.19/macOS 10.3@
279 const char * /* O - Status string */
280 ppdErrorString(ppd_status_t status
) /* I - PPD status */
282 static const char * const messages
[] =/* Status messages */
285 _("Unable to open PPD file"),
286 _("NULL PPD file pointer"),
287 _("Memory allocation error"),
288 _("Missing PPD-Adobe-4.x header"),
289 _("Missing value string"),
292 _("OpenGroup without a CloseGroup first"),
293 _("Bad OpenUI/JCLOpenUI"),
294 _("OpenUI/JCLOpenUI without a CloseUI/JCLCloseUI first"),
295 _("Bad OrderDependency"),
296 _("Bad UIConstraints"),
297 _("Missing asterisk in column 1"),
298 _("Line longer than the maximum allowed (255 characters)"),
299 _("Illegal control character"),
300 _("Illegal main keyword string"),
301 _("Illegal option keyword string"),
302 _("Illegal translation string"),
303 _("Illegal whitespace character"),
304 _("Bad custom parameter"),
305 _("Missing option keyword"),
306 _("Bad value string"),
307 _("Missing CloseGroup"),
308 _("Bad CloseUI/JCLCloseUI"),
309 _("Missing CloseUI/JCLCloseUI")
313 if (status
< PPD_OK
|| status
>= PPD_MAX_STATUS
)
314 return (_cupsLangString(cupsLangDefault(), _("Unknown")));
316 return (_cupsLangString(cupsLangDefault(), messages
[status
]));
321 * '_ppdGetEncoding()' - Get the CUPS encoding value for the given
325 cups_encoding_t
/* O - CUPS encoding value */
326 _ppdGetEncoding(const char *name
) /* I - LanguageEncoding string */
328 if (!_cups_strcasecmp(name
, "ISOLatin1"))
329 return (CUPS_ISO8859_1
);
330 else if (!_cups_strcasecmp(name
, "ISOLatin2"))
331 return (CUPS_ISO8859_2
);
332 else if (!_cups_strcasecmp(name
, "ISOLatin5"))
333 return (CUPS_ISO8859_5
);
334 else if (!_cups_strcasecmp(name
, "JIS83-RKSJ"))
335 return (CUPS_JIS_X0213
);
336 else if (!_cups_strcasecmp(name
, "MacStandard"))
337 return (CUPS_MAC_ROMAN
);
338 else if (!_cups_strcasecmp(name
, "WindowsANSI"))
339 return (CUPS_WINDOWS_1252
);
346 * '_ppdGlobals()' - Return a pointer to thread local storage
349 _ppd_globals_t
* /* O - Pointer to global data */
352 _ppd_globals_t
*pg
; /* Pointer to global data */
355 #ifdef HAVE_PTHREAD_H
357 * Initialize the global data exactly once...
360 pthread_once(&ppd_globals_key_once
, ppd_globals_init
);
361 #endif /* HAVE_PTHREAD_H */
364 * See if we have allocated the data yet...
367 if ((pg
= (_ppd_globals_t
*)_cupsThreadGetData(ppd_globals_key
)) == NULL
)
370 * No, allocate memory as set the pointer for the key...
373 if ((pg
= ppd_globals_alloc()) != NULL
)
374 _cupsThreadSetData(ppd_globals_key
, pg
);
378 * Return the pointer to the data...
386 * 'ppdLastError()' - Return the status from the last ppdOpen*().
388 * @since CUPS 1.1.19/macOS 10.3@
391 ppd_status_t
/* O - Status code */
392 ppdLastError(int *line
) /* O - Line number */
394 _ppd_globals_t
*pg
= _ppdGlobals();
399 *line
= pg
->ppd_line
;
401 return (pg
->ppd_status
);
406 * '_ppdOpen()' - Read a PPD file into memory.
408 * @since CUPS 1.2/macOS 10.5@
411 ppd_file_t
* /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
413 cups_file_t
*fp
, /* I - File to read from */
414 _ppd_localization_t localization
) /* I - Localization to load */
416 int i
, j
, k
; /* Looping vars */
417 _ppd_line_t line
; /* Line buffer */
418 ppd_file_t
*ppd
; /* PPD file record */
419 ppd_group_t
*group
, /* Current group */
420 *subgroup
; /* Current sub-group */
421 ppd_option_t
*option
; /* Current option */
422 ppd_choice_t
*choice
; /* Current choice */
423 ppd_const_t
*constraint
; /* Current constraint */
424 ppd_size_t
*size
; /* Current page size */
425 int mask
; /* Line data mask */
426 char keyword
[PPD_MAX_NAME
],
427 /* Keyword from file */
429 /* Option from file */
431 /* Human-readable text from file */
432 *string
, /* Code/text from file */
433 *sptr
, /* Pointer into string */
434 *temp
, /* Temporary string pointer */
435 **tempfonts
; /* Temporary fonts pointer */
436 float order
; /* Order dependency number */
437 ppd_section_t section
; /* Order dependency section */
438 ppd_profile_t
*profile
; /* Pointer to color profile */
439 char **filter
; /* Pointer to filter */
440 struct lconv
*loc
; /* Locale data */
441 int ui_keyword
; /* Is this line a UI keyword? */
442 cups_lang_t
*lang
; /* Language data */
443 cups_encoding_t encoding
; /* Encoding of PPD file */
444 _ppd_globals_t
*pg
= _ppdGlobals();
446 char custom_name
[PPD_MAX_NAME
];
447 /* CustomFoo attribute name */
448 ppd_attr_t
*custom_attr
; /* CustomFoo attribute */
449 char ll
[7], /* Base language + '.' */
450 ll_CC
[7]; /* Language w/country + '.' */
451 size_t ll_len
= 0, /* Base language length */
452 ll_CC_len
= 0; /* Language w/country length */
453 static const char * const ui_keywords
[] =
455 #ifdef CUPS_USE_FULL_UI_KEYWORDS_LIST
457 * Adobe defines some 41 keywords as "UI", meaning that they are
458 * user interface elements and that they should be treated as such
459 * even if the PPD creator doesn't use Open/CloseUI around them.
461 * Since this can cause previously invisible options to appear and
462 * confuse users, the default is to only treat the PageSize and
463 * PageRegion keywords this way.
465 /* Boolean keywords */
475 /* PickOne keywords */
488 "JCLFrameBufferSize",
509 #else /* !CUPS_USE_FULL_UI_KEYWORDS_LIST */
512 #endif /* CUPS_USE_FULL_UI_KEYWORDS_LIST */
514 static const char * const color_keywords
[] = /* Keywords associated with color profiles */
521 DEBUG_printf(("_ppdOpen(fp=%p)", fp
));
524 * Default to "OK" status...
527 pg
->ppd_status
= PPD_OK
;
531 * Range check input...
536 pg
->ppd_status
= PPD_NULL_FILE
;
541 * If only loading a single localization set up the strings to match...
544 if (localization
== _PPD_LOCALIZATION_DEFAULT
)
546 if ((lang
= cupsLangDefault()) == NULL
)
549 snprintf(ll_CC
, sizeof(ll_CC
), "%s.", lang
->language
);
552 * <rdar://problem/22130168>
553 * <rdar://problem/27245567>
555 * Need to use a different base language for some locales...
558 if (!strcmp(lang
->language
, "zh_HK"))
559 { /* Traditional Chinese + variants */
560 strlcpy(ll_CC
, "zh_TW.", sizeof(ll_CC
));
561 strlcpy(ll
, "zh_", sizeof(ll
));
563 else if (!strncmp(lang
->language
, "zh", 2))
564 strlcpy(ll
, "zh_", sizeof(ll
)); /* Any Chinese variant */
565 else if (!strncmp(lang
->language
, "jp", 2))
566 { /* Any Japanese variant */
567 strlcpy(ll_CC
, "ja", sizeof(ll_CC
));
568 strlcpy(ll
, "jp", sizeof(ll
));
570 else if (!strncmp(lang
->language
, "nb", 2) || !strncmp(lang
->language
, "no", 2))
571 { /* Any Norwegian variant */
572 strlcpy(ll_CC
, "nb", sizeof(ll_CC
));
573 strlcpy(ll
, "no", sizeof(ll
));
576 snprintf(ll
, sizeof(ll
), "%2.2s.", lang
->language
);
578 ll_CC_len
= strlen(ll_CC
);
581 DEBUG_printf(("2_ppdOpen: Loading localizations matching \"%s\" and \"%s\"",
586 * Grab the first line and make sure it reads '*PPD-Adobe: "major.minor"'...
592 mask
= ppd_read(fp
, &line
, keyword
, name
, text
, &string
, 0, pg
);
594 DEBUG_printf(("2_ppdOpen: mask=%x, keyword=\"%s\"...", mask
, keyword
));
597 strcmp(keyword
, "PPD-Adobe") ||
598 string
== NULL
|| string
[0] != '4')
601 * Either this is not a PPD file, or it is not a 4.x PPD file.
604 if (pg
->ppd_status
== PPD_OK
)
605 pg
->ppd_status
= PPD_MISSING_PPDADOBE4
;
613 DEBUG_printf(("2_ppdOpen: keyword=%s, string=%p", keyword
, string
));
616 * Allocate memory for the PPD file record...
619 if ((ppd
= calloc(1, sizeof(ppd_file_t
))) == NULL
)
621 pg
->ppd_status
= PPD_ALLOC_ERROR
;
632 ppd
->language_level
= 2;
633 ppd
->color_device
= 0;
634 ppd
->colorspace
= PPD_CS_N
;
635 ppd
->landscape
= -90;
636 ppd
->coptions
= cupsArrayNew((cups_array_func_t
)ppd_compare_coptions
, NULL
);
639 * Read lines from the PPD file and add them to the file record...
647 encoding
= CUPS_ISO8859_1
;
650 while ((mask
= ppd_read(fp
, &line
, keyword
, name
, text
, &string
, 1, pg
)) != 0)
652 DEBUG_printf(("2_ppdOpen: mask=%x, keyword=\"%s\", name=\"%s\", "
653 "text=\"%s\", string=%d chars...", mask
, keyword
, name
, text
,
654 string
? (int)strlen(string
) : 0));
656 if (strncmp(keyword
, "Default", 7) && !string
&&
657 pg
->ppd_conform
!= PPD_CONFORM_RELAXED
)
660 * Need a string value!
663 pg
->ppd_status
= PPD_MISSING_VALUE
;
671 * Certain main keywords (as defined by the PPD spec) may be used
672 * without the usual OpenUI/CloseUI stuff. Presumably this is just
673 * so that Adobe wouldn't completely break compatibility with PPD
674 * files prior to v4.0 of the spec, but it is hopelessly
675 * inconsistent... Catch these main keywords and automatically
676 * create the corresponding option, as needed...
682 * Previous line was a UI keyword...
690 * If we are filtering out keyword localizations, see if this line needs to
694 if (localization
!= _PPD_LOCALIZATION_ALL
&&
695 (temp
= strchr(keyword
, '.')) != NULL
&&
696 ((temp
- keyword
) == 2 || (temp
- keyword
) == 5) &&
697 _cups_isalpha(keyword
[0]) &&
698 _cups_isalpha(keyword
[1]) &&
699 (keyword
[2] == '.' ||
700 (keyword
[2] == '_' && _cups_isalpha(keyword
[3]) &&
701 _cups_isalpha(keyword
[4]) && keyword
[5] == '.')))
703 if (localization
== _PPD_LOCALIZATION_NONE
||
704 (localization
== _PPD_LOCALIZATION_DEFAULT
&&
705 strncmp(ll_CC
, keyword
, ll_CC_len
) &&
706 strncmp(ll
, keyword
, ll_len
)))
708 DEBUG_printf(("2_ppdOpen: Ignoring localization: \"%s\"\n", keyword
));
713 else if (localization
== _PPD_LOCALIZATION_ICC_PROFILES
)
716 * Only load localizations for the color profile related keywords...
720 i
< (int)(sizeof(color_keywords
) / sizeof(color_keywords
[0]));
723 if (!_cups_strcasecmp(temp
, color_keywords
[i
]))
727 if (i
>= (int)(sizeof(color_keywords
) / sizeof(color_keywords
[0])))
729 DEBUG_printf(("2_ppdOpen: Ignoring localization: \"%s\"\n", keyword
));
737 if (option
== NULL
&&
738 (mask
& (PPD_KEYWORD
| PPD_OPTION
| PPD_STRING
)) ==
739 (PPD_KEYWORD
| PPD_OPTION
| PPD_STRING
))
741 for (i
= 0; i
< (int)(sizeof(ui_keywords
) / sizeof(ui_keywords
[0])); i
++)
742 if (!strcmp(keyword
, ui_keywords
[i
]))
745 if (i
< (int)(sizeof(ui_keywords
) / sizeof(ui_keywords
[0])))
748 * Create the option in the appropriate group...
753 DEBUG_printf(("2_ppdOpen: FOUND ADOBE UI KEYWORD %s WITHOUT OPENUI!",
758 if ((group
= ppd_get_group(ppd
, "General", _("General"), pg
,
762 DEBUG_printf(("2_ppdOpen: Adding to group %s...", group
->text
));
763 option
= ppd_get_option(group
, keyword
);
767 option
= ppd_get_option(group
, keyword
);
771 pg
->ppd_status
= PPD_ALLOC_ERROR
;
777 * Now fill in the initial information for the option...
780 if (!strncmp(keyword
, "JCL", 3))
781 option
->section
= PPD_ORDER_JCL
;
783 option
->section
= PPD_ORDER_ANY
;
785 option
->order
= 10.0f
;
788 option
->ui
= PPD_UI_BOOLEAN
;
790 option
->ui
= PPD_UI_PICKONE
;
792 for (j
= 0; j
< ppd
->num_attrs
; j
++)
793 if (!strncmp(ppd
->attrs
[j
]->name
, "Default", 7) &&
794 !strcmp(ppd
->attrs
[j
]->name
+ 7, keyword
) &&
795 ppd
->attrs
[j
]->value
)
797 DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...",
798 option
->keyword
, ppd
->attrs
[j
]->value
));
799 strlcpy(option
->defchoice
, ppd
->attrs
[j
]->value
,
800 sizeof(option
->defchoice
));
804 if (!strcmp(keyword
, "PageSize"))
805 strlcpy(option
->text
, _("Media Size"), sizeof(option
->text
));
806 else if (!strcmp(keyword
, "MediaType"))
807 strlcpy(option
->text
, _("Media Type"), sizeof(option
->text
));
808 else if (!strcmp(keyword
, "InputSlot"))
809 strlcpy(option
->text
, _("Media Source"), sizeof(option
->text
));
810 else if (!strcmp(keyword
, "ColorModel"))
811 strlcpy(option
->text
, _("Output Mode"), sizeof(option
->text
));
812 else if (!strcmp(keyword
, "Resolution"))
813 strlcpy(option
->text
, _("Resolution"), sizeof(option
->text
));
815 strlcpy(option
->text
, keyword
, sizeof(option
->text
));
819 if (!strcmp(keyword
, "LanguageLevel"))
820 ppd
->language_level
= atoi(string
);
821 else if (!strcmp(keyword
, "LanguageEncoding"))
824 * Say all PPD files are UTF-8, since we convert to UTF-8...
827 ppd
->lang_encoding
= strdup("UTF-8");
828 encoding
= _ppdGetEncoding(string
);
830 else if (!strcmp(keyword
, "LanguageVersion"))
831 ppd
->lang_version
= string
;
832 else if (!strcmp(keyword
, "Manufacturer"))
833 ppd
->manufacturer
= string
;
834 else if (!strcmp(keyword
, "ModelName"))
835 ppd
->modelname
= string
;
836 else if (!strcmp(keyword
, "Protocols"))
837 ppd
->protocols
= string
;
838 else if (!strcmp(keyword
, "PCFileName"))
839 ppd
->pcfilename
= string
;
840 else if (!strcmp(keyword
, "NickName"))
842 if (encoding
!= CUPS_UTF8
)
844 cups_utf8_t utf8
[256]; /* UTF-8 version of NickName */
847 cupsCharsetToUTF8(utf8
, string
, sizeof(utf8
), encoding
);
848 ppd
->nickname
= strdup((char *)utf8
);
851 ppd
->nickname
= strdup(string
);
853 else if (!strcmp(keyword
, "Product"))
854 ppd
->product
= string
;
855 else if (!strcmp(keyword
, "ShortNickName"))
856 ppd
->shortnickname
= string
;
857 else if (!strcmp(keyword
, "TTRasterizer"))
858 ppd
->ttrasterizer
= string
;
859 else if (!strcmp(keyword
, "JCLBegin"))
861 ppd
->jcl_begin
= strdup(string
);
862 ppd_decode(ppd
->jcl_begin
); /* Decode quoted string */
864 else if (!strcmp(keyword
, "JCLEnd"))
866 ppd
->jcl_end
= strdup(string
);
867 ppd_decode(ppd
->jcl_end
); /* Decode quoted string */
869 else if (!strcmp(keyword
, "JCLToPSInterpreter"))
871 ppd
->jcl_ps
= strdup(string
);
872 ppd_decode(ppd
->jcl_ps
); /* Decode quoted string */
874 else if (!strcmp(keyword
, "AccurateScreensSupport"))
875 ppd
->accurate_screens
= !strcasecmp(string
, "True");
876 else if (!strcmp(keyword
, "ColorDevice"))
877 ppd
->color_device
= !strcasecmp(string
, "True");
878 else if (!strcmp(keyword
, "ContoneOnly"))
879 ppd
->contone_only
= !strcasecmp(string
, "True");
880 else if (!strcmp(keyword
, "cupsFlipDuplex"))
881 ppd
->flip_duplex
= !strcasecmp(string
, "True");
882 else if (!strcmp(keyword
, "cupsManualCopies"))
883 ppd
->manual_copies
= !strcasecmp(string
, "True");
884 else if (!strcmp(keyword
, "cupsModelNumber"))
885 ppd
->model_number
= atoi(string
);
886 else if (!strcmp(keyword
, "cupsColorProfile"))
888 if (ppd
->num_profiles
== 0)
889 profile
= malloc(sizeof(ppd_profile_t
));
891 profile
= realloc(ppd
->profiles
, sizeof(ppd_profile_t
) * (size_t)(ppd
->num_profiles
+ 1));
895 pg
->ppd_status
= PPD_ALLOC_ERROR
;
900 ppd
->profiles
= profile
;
901 profile
+= ppd
->num_profiles
;
902 ppd
->num_profiles
++;
904 memset(profile
, 0, sizeof(ppd_profile_t
));
905 strlcpy(profile
->resolution
, name
, sizeof(profile
->resolution
));
906 strlcpy(profile
->media_type
, text
, sizeof(profile
->media_type
));
908 profile
->density
= (float)_cupsStrScand(string
, &sptr
, loc
);
909 profile
->gamma
= (float)_cupsStrScand(sptr
, &sptr
, loc
);
910 profile
->matrix
[0][0] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
911 profile
->matrix
[0][1] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
912 profile
->matrix
[0][2] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
913 profile
->matrix
[1][0] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
914 profile
->matrix
[1][1] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
915 profile
->matrix
[1][2] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
916 profile
->matrix
[2][0] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
917 profile
->matrix
[2][1] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
918 profile
->matrix
[2][2] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
920 else if (!strcmp(keyword
, "cupsFilter"))
922 if (ppd
->num_filters
== 0)
923 filter
= malloc(sizeof(char *));
925 filter
= realloc(ppd
->filters
, sizeof(char *) * (size_t)(ppd
->num_filters
+ 1));
929 pg
->ppd_status
= PPD_ALLOC_ERROR
;
934 ppd
->filters
= filter
;
935 filter
+= ppd
->num_filters
;
939 * Make a copy of the filter string...
942 *filter
= strdup(string
);
944 else if (!strcmp(keyword
, "Throughput"))
945 ppd
->throughput
= atoi(string
);
946 else if (!strcmp(keyword
, "Font"))
949 * Add this font to the list of available fonts...
952 if (ppd
->num_fonts
== 0)
953 tempfonts
= (char **)malloc(sizeof(char *));
955 tempfonts
= (char **)realloc(ppd
->fonts
, sizeof(char *) * (size_t)(ppd
->num_fonts
+ 1));
957 if (tempfonts
== NULL
)
959 pg
->ppd_status
= PPD_ALLOC_ERROR
;
964 ppd
->fonts
= tempfonts
;
965 ppd
->fonts
[ppd
->num_fonts
] = strdup(name
);
968 else if (!strncmp(keyword
, "ParamCustom", 11))
970 ppd_coption_t
*coption
; /* Custom option */
971 ppd_cparam_t
*cparam
; /* Custom parameter */
972 int corder
; /* Order number */
973 char ctype
[33], /* Data type */
974 cminimum
[65], /* Minimum value */
975 cmaximum
[65]; /* Maximum value */
979 * Get the custom option and parameter...
982 if ((coption
= ppd_get_coption(ppd
, keyword
+ 11)) == NULL
)
984 pg
->ppd_status
= PPD_ALLOC_ERROR
;
989 if ((cparam
= ppd_get_cparam(coption
, name
, text
)) == NULL
)
991 pg
->ppd_status
= PPD_ALLOC_ERROR
;
996 if (cparam
->type
!= PPD_CUSTOM_UNKNOWN
)
998 pg
->ppd_status
= PPD_BAD_CUSTOM_PARAM
;
1004 * Get the parameter data...
1008 sscanf(string
, "%d%32s%64s%64s", &corder
, ctype
, cminimum
,
1011 pg
->ppd_status
= PPD_BAD_CUSTOM_PARAM
;
1016 cparam
->order
= corder
;
1018 if (!strcmp(ctype
, "curve"))
1020 cparam
->type
= PPD_CUSTOM_CURVE
;
1021 cparam
->minimum
.custom_curve
= (float)_cupsStrScand(cminimum
, NULL
, loc
);
1022 cparam
->maximum
.custom_curve
= (float)_cupsStrScand(cmaximum
, NULL
, loc
);
1024 else if (!strcmp(ctype
, "int"))
1026 cparam
->type
= PPD_CUSTOM_INT
;
1027 cparam
->minimum
.custom_int
= atoi(cminimum
);
1028 cparam
->maximum
.custom_int
= atoi(cmaximum
);
1030 else if (!strcmp(ctype
, "invcurve"))
1032 cparam
->type
= PPD_CUSTOM_INVCURVE
;
1033 cparam
->minimum
.custom_invcurve
= (float)_cupsStrScand(cminimum
, NULL
, loc
);
1034 cparam
->maximum
.custom_invcurve
= (float)_cupsStrScand(cmaximum
, NULL
, loc
);
1036 else if (!strcmp(ctype
, "passcode"))
1038 cparam
->type
= PPD_CUSTOM_PASSCODE
;
1039 cparam
->minimum
.custom_passcode
= atoi(cminimum
);
1040 cparam
->maximum
.custom_passcode
= atoi(cmaximum
);
1042 else if (!strcmp(ctype
, "password"))
1044 cparam
->type
= PPD_CUSTOM_PASSWORD
;
1045 cparam
->minimum
.custom_password
= atoi(cminimum
);
1046 cparam
->maximum
.custom_password
= atoi(cmaximum
);
1048 else if (!strcmp(ctype
, "points"))
1050 cparam
->type
= PPD_CUSTOM_POINTS
;
1051 cparam
->minimum
.custom_points
= (float)_cupsStrScand(cminimum
, NULL
, loc
);
1052 cparam
->maximum
.custom_points
= (float)_cupsStrScand(cmaximum
, NULL
, loc
);
1054 else if (!strcmp(ctype
, "real"))
1056 cparam
->type
= PPD_CUSTOM_REAL
;
1057 cparam
->minimum
.custom_real
= (float)_cupsStrScand(cminimum
, NULL
, loc
);
1058 cparam
->maximum
.custom_real
= (float)_cupsStrScand(cmaximum
, NULL
, loc
);
1060 else if (!strcmp(ctype
, "string"))
1062 cparam
->type
= PPD_CUSTOM_STRING
;
1063 cparam
->minimum
.custom_string
= atoi(cminimum
);
1064 cparam
->maximum
.custom_string
= atoi(cmaximum
);
1068 pg
->ppd_status
= PPD_BAD_CUSTOM_PARAM
;
1074 * Now special-case for CustomPageSize...
1077 if (!strcmp(coption
->keyword
, "PageSize"))
1079 if (!strcmp(name
, "Width"))
1081 ppd
->custom_min
[0] = cparam
->minimum
.custom_points
;
1082 ppd
->custom_max
[0] = cparam
->maximum
.custom_points
;
1084 else if (!strcmp(name
, "Height"))
1086 ppd
->custom_min
[1] = cparam
->minimum
.custom_points
;
1087 ppd
->custom_max
[1] = cparam
->maximum
.custom_points
;
1091 else if (!strcmp(keyword
, "HWMargins"))
1093 for (i
= 0, sptr
= string
; i
< 4; i
++)
1094 ppd
->custom_margins
[i
] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
1096 else if (!strncmp(keyword
, "Custom", 6) && !strcmp(name
, "True") && !option
)
1098 ppd_option_t
*custom_option
; /* Custom option */
1100 DEBUG_puts("2_ppdOpen: Processing Custom option...");
1103 * Get the option and custom option...
1106 if (!ppd_get_coption(ppd
, keyword
+ 6))
1108 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1113 if (option
&& !_cups_strcasecmp(option
->keyword
, keyword
+ 6))
1114 custom_option
= option
;
1116 custom_option
= ppdFindOption(ppd
, keyword
+ 6);
1121 * Add the "custom" option...
1124 if ((choice
= ppdFindChoice(custom_option
, "Custom")) == NULL
)
1125 if ((choice
= ppd_add_choice(custom_option
, "Custom")) == NULL
)
1127 DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
1129 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1134 strlcpy(choice
->text
, text
[0] ? text
: _("Custom"),
1135 sizeof(choice
->text
));
1137 choice
->code
= strdup(string
);
1139 if (custom_option
->section
== PPD_ORDER_JCL
)
1140 ppd_decode(choice
->code
);
1144 * Now process custom page sizes specially...
1147 if (!strcmp(keyword
, "CustomPageSize"))
1150 * Add a "Custom" page size entry...
1153 ppd
->variable_sizes
= 1;
1155 ppd_add_size(ppd
, "Custom");
1157 if (option
&& !_cups_strcasecmp(option
->keyword
, "PageRegion"))
1158 custom_option
= option
;
1160 custom_option
= ppdFindOption(ppd
, "PageRegion");
1164 if ((choice
= ppdFindChoice(custom_option
, "Custom")) == NULL
)
1165 if ((choice
= ppd_add_choice(custom_option
, "Custom")) == NULL
)
1167 DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
1169 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1174 strlcpy(choice
->text
, text
[0] ? text
: _("Custom"),
1175 sizeof(choice
->text
));
1179 else if (!strcmp(keyword
, "LandscapeOrientation"))
1181 if (!strcmp(string
, "Minus90"))
1182 ppd
->landscape
= -90;
1183 else if (!strcmp(string
, "Plus90"))
1184 ppd
->landscape
= 90;
1186 else if (!strcmp(keyword
, "Emulators") && string
&& ppd
->num_emulations
== 0)
1189 * Issue #5562: Samsung printer drivers incorrectly use Emulators keyword
1190 * to configure themselves
1192 * The Emulators keyword was loaded but never used by anything in CUPS,
1193 * and has no valid purpose in CUPS. The old code was removed due to a
1194 * memory leak (Issue #5475), so the following (new) code supports a single
1195 * name for the Emulators keyword, allowing these drivers to work until we
1196 * remove PPD and driver support entirely in a future version of CUPS.
1199 ppd
->num_emulations
= 1;
1200 ppd
->emulations
= calloc(1, sizeof(ppd_emul_t
));
1202 strlcpy(ppd
->emulations
[0].name
, string
, sizeof(ppd
->emulations
[0].name
));
1204 else if (!strcmp(keyword
, "JobPatchFile"))
1207 * CUPS STR #3421: Check for "*JobPatchFile: int: string"
1210 if (isdigit(*string
& 255))
1212 for (sptr
= string
+ 1; isdigit(*sptr
& 255); sptr
++);
1217 * Found "*JobPatchFile: int: string"...
1220 pg
->ppd_status
= PPD_BAD_VALUE
;
1226 if (!name
[0] && pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1229 * Found "*JobPatchFile: string"...
1232 pg
->ppd_status
= PPD_MISSING_OPTION_KEYWORD
;
1237 if (ppd
->patches
== NULL
)
1238 ppd
->patches
= strdup(string
);
1241 temp
= realloc(ppd
->patches
, strlen(ppd
->patches
) +
1242 strlen(string
) + 1);
1245 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1250 ppd
->patches
= temp
;
1252 memcpy(ppd
->patches
+ strlen(ppd
->patches
), string
, strlen(string
) + 1);
1255 else if (!strcmp(keyword
, "OpenUI"))
1258 * Don't allow nesting of options...
1261 if (option
&& pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1263 pg
->ppd_status
= PPD_NESTED_OPEN_UI
;
1269 * Add an option record to the current sub-group, group, or file...
1272 DEBUG_printf(("2_ppdOpen: name=\"%s\" (%d)", name
, (int)strlen(name
)));
1275 _cups_strcpy(name
, name
+ 1); /* Eliminate leading asterisk */
1277 for (i
= (int)strlen(name
) - 1; i
> 0 && _cups_isspace(name
[i
]); i
--)
1278 name
[i
] = '\0'; /* Eliminate trailing spaces */
1280 DEBUG_printf(("2_ppdOpen: OpenUI of %s in group %s...", name
,
1281 group
? group
->text
: "(null)"));
1283 if (subgroup
!= NULL
)
1284 option
= ppd_get_option(subgroup
, name
);
1285 else if (group
== NULL
)
1287 if ((group
= ppd_get_group(ppd
, "General", _("General"), pg
,
1291 DEBUG_printf(("2_ppdOpen: Adding to group %s...", group
->text
));
1292 option
= ppd_get_option(group
, name
);
1296 option
= ppd_get_option(group
, name
);
1300 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1306 * Now fill in the initial information for the option...
1309 if (string
&& !strcmp(string
, "PickMany"))
1310 option
->ui
= PPD_UI_PICKMANY
;
1311 else if (string
&& !strcmp(string
, "Boolean"))
1312 option
->ui
= PPD_UI_BOOLEAN
;
1313 else if (string
&& !strcmp(string
, "PickOne"))
1314 option
->ui
= PPD_UI_PICKONE
;
1315 else if (pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1317 pg
->ppd_status
= PPD_BAD_OPEN_UI
;
1322 option
->ui
= PPD_UI_PICKONE
;
1324 for (j
= 0; j
< ppd
->num_attrs
; j
++)
1325 if (!strncmp(ppd
->attrs
[j
]->name
, "Default", 7) &&
1326 !strcmp(ppd
->attrs
[j
]->name
+ 7, name
) &&
1327 ppd
->attrs
[j
]->value
)
1329 DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...",
1330 option
->keyword
, ppd
->attrs
[j
]->value
));
1331 strlcpy(option
->defchoice
, ppd
->attrs
[j
]->value
,
1332 sizeof(option
->defchoice
));
1337 cupsCharsetToUTF8((cups_utf8_t
*)option
->text
, text
,
1338 sizeof(option
->text
), encoding
);
1341 if (!strcmp(name
, "PageSize"))
1342 strlcpy(option
->text
, _("Media Size"), sizeof(option
->text
));
1343 else if (!strcmp(name
, "MediaType"))
1344 strlcpy(option
->text
, _("Media Type"), sizeof(option
->text
));
1345 else if (!strcmp(name
, "InputSlot"))
1346 strlcpy(option
->text
, _("Media Source"), sizeof(option
->text
));
1347 else if (!strcmp(name
, "ColorModel"))
1348 strlcpy(option
->text
, _("Output Mode"), sizeof(option
->text
));
1349 else if (!strcmp(name
, "Resolution"))
1350 strlcpy(option
->text
, _("Resolution"), sizeof(option
->text
));
1352 strlcpy(option
->text
, name
, sizeof(option
->text
));
1355 option
->section
= PPD_ORDER_ANY
;
1361 * Add a custom option choice if we have already seen a CustomFoo
1365 if (!_cups_strcasecmp(name
, "PageRegion"))
1366 strlcpy(custom_name
, "CustomPageSize", sizeof(custom_name
));
1368 snprintf(custom_name
, sizeof(custom_name
), "Custom%s", name
);
1370 if ((custom_attr
= ppdFindAttr(ppd
, custom_name
, "True")) != NULL
)
1372 if ((choice
= ppdFindChoice(option
, "Custom")) == NULL
)
1373 if ((choice
= ppd_add_choice(option
, "Custom")) == NULL
)
1375 DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
1377 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1382 strlcpy(choice
->text
,
1383 custom_attr
->text
[0] ? custom_attr
->text
: _("Custom"),
1384 sizeof(choice
->text
));
1385 choice
->code
= strdup(custom_attr
->value
);
1388 else if (!strcmp(keyword
, "JCLOpenUI"))
1391 * Don't allow nesting of options...
1394 if (option
&& pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1396 pg
->ppd_status
= PPD_NESTED_OPEN_UI
;
1402 * Find the JCL group, and add if needed...
1405 group
= ppd_get_group(ppd
, "JCL", _("JCL"), pg
, encoding
);
1411 * Add an option record to the current JCLs...
1415 _cups_strcpy(name
, name
+ 1);
1417 option
= ppd_get_option(group
, name
);
1421 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1427 * Now fill in the initial information for the option...
1430 if (string
&& !strcmp(string
, "PickMany"))
1431 option
->ui
= PPD_UI_PICKMANY
;
1432 else if (string
&& !strcmp(string
, "Boolean"))
1433 option
->ui
= PPD_UI_BOOLEAN
;
1434 else if (string
&& !strcmp(string
, "PickOne"))
1435 option
->ui
= PPD_UI_PICKONE
;
1438 pg
->ppd_status
= PPD_BAD_OPEN_UI
;
1443 for (j
= 0; j
< ppd
->num_attrs
; j
++)
1444 if (!strncmp(ppd
->attrs
[j
]->name
, "Default", 7) &&
1445 !strcmp(ppd
->attrs
[j
]->name
+ 7, name
) &&
1446 ppd
->attrs
[j
]->value
)
1448 DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...",
1449 option
->keyword
, ppd
->attrs
[j
]->value
));
1450 strlcpy(option
->defchoice
, ppd
->attrs
[j
]->value
,
1451 sizeof(option
->defchoice
));
1456 cupsCharsetToUTF8((cups_utf8_t
*)option
->text
, text
,
1457 sizeof(option
->text
), encoding
);
1459 strlcpy(option
->text
, name
, sizeof(option
->text
));
1461 option
->section
= PPD_ORDER_JCL
;
1468 * Add a custom option choice if we have already seen a CustomFoo
1472 snprintf(custom_name
, sizeof(custom_name
), "Custom%s", name
);
1474 if ((custom_attr
= ppdFindAttr(ppd
, custom_name
, "True")) != NULL
)
1476 if ((choice
= ppd_add_choice(option
, "Custom")) == NULL
)
1478 DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
1480 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1485 strlcpy(choice
->text
,
1486 custom_attr
->text
[0] ? custom_attr
->text
: _("Custom"),
1487 sizeof(choice
->text
));
1488 choice
->code
= strdup(custom_attr
->value
);
1491 else if (!strcmp(keyword
, "CloseUI"))
1493 if ((!option
|| option
->section
== PPD_ORDER_JCL
) && pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1495 pg
->ppd_status
= PPD_BAD_CLOSE_UI
;
1500 if (option
&& (!_cups_strcasecmp(option
->defchoice
, "custom") || !_cups_strncasecmp(option
->defchoice
, "custom.", 7)))
1503 * "*DefaultOption: Custom..." may set the default to a custom value
1504 * or (for a very small number of incompatible PPD files) select a
1505 * standard choice for the option, which CUPS renames to "_Custom..."
1506 * to avoid compatibility issues. See which this is...
1509 char tchoice
[PPD_MAX_NAME
]; /* Temporary choice name */
1511 snprintf(tchoice
, sizeof(tchoice
), "_%s", option
->defchoice
);
1513 if (ppdFindChoice(option
, tchoice
))
1515 strlcpy(option
->defchoice
, tchoice
, sizeof(option
->defchoice
));
1517 DEBUG_printf(("2_ppdOpen: Reset Default%s to %s...", option
->keyword
, tchoice
));
1526 else if (!strcmp(keyword
, "JCLCloseUI"))
1528 if ((!option
|| option
->section
!= PPD_ORDER_JCL
) && pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1530 pg
->ppd_status
= PPD_BAD_CLOSE_UI
;
1535 if (option
&& (!_cups_strcasecmp(option
->defchoice
, "custom") || !_cups_strncasecmp(option
->defchoice
, "custom.", 7)))
1538 * "*DefaultOption: Custom..." may set the default to a custom value
1539 * or (for a very small number of incompatible PPD files) select a
1540 * standard choice for the option, which CUPS renames to "_Custom..."
1541 * to avoid compatibility issues. See which this is...
1544 char tchoice
[PPD_MAX_NAME
]; /* Temporary choice name */
1546 snprintf(tchoice
, sizeof(tchoice
), "_%s", option
->defchoice
);
1548 if (ppdFindChoice(option
, tchoice
))
1550 strlcpy(option
->defchoice
, tchoice
, sizeof(option
->defchoice
));
1552 DEBUG_printf(("2_ppdOpen: Reset Default%s to %s...", option
->keyword
, tchoice
));
1561 else if (!strcmp(keyword
, "OpenGroup"))
1564 * Open a new group...
1569 pg
->ppd_status
= PPD_NESTED_OPEN_GROUP
;
1576 pg
->ppd_status
= PPD_BAD_OPEN_GROUP
;
1582 * Separate the group name from the text (name/text)...
1585 if ((sptr
= strchr(string
, '/')) != NULL
)
1591 * Fix up the text...
1597 * Find/add the group...
1600 group
= ppd_get_group(ppd
, string
, sptr
, pg
, encoding
);
1608 else if (!strcmp(keyword
, "CloseGroup"))
1615 else if (!strcmp(keyword
, "OrderDependency"))
1617 order
= (float)_cupsStrScand(string
, &sptr
, loc
);
1619 if (!sptr
|| sscanf(sptr
, "%40s%40s", name
, keyword
) != 2)
1621 pg
->ppd_status
= PPD_BAD_ORDER_DEPENDENCY
;
1626 if (keyword
[0] == '*')
1627 _cups_strcpy(keyword
, keyword
+ 1);
1629 if (!strcmp(name
, "ExitServer"))
1630 section
= PPD_ORDER_EXIT
;
1631 else if (!strcmp(name
, "Prolog"))
1632 section
= PPD_ORDER_PROLOG
;
1633 else if (!strcmp(name
, "DocumentSetup"))
1634 section
= PPD_ORDER_DOCUMENT
;
1635 else if (!strcmp(name
, "PageSetup"))
1636 section
= PPD_ORDER_PAGE
;
1637 else if (!strcmp(name
, "JCLSetup"))
1638 section
= PPD_ORDER_JCL
;
1640 section
= PPD_ORDER_ANY
;
1648 * Only valid for Non-UI options...
1651 for (i
= ppd
->num_groups
, gtemp
= ppd
->groups
; i
> 0; i
--, gtemp
++)
1652 if (gtemp
->text
[0] == '\0')
1656 for (i
= 0; i
< gtemp
->num_options
; i
++)
1657 if (!strcmp(keyword
, gtemp
->options
[i
].keyword
))
1659 gtemp
->options
[i
].section
= section
;
1660 gtemp
->options
[i
].order
= order
;
1666 option
->section
= section
;
1667 option
->order
= order
;
1673 else if (!strncmp(keyword
, "Default", 7))
1679 * Drop UI text, if any, from value...
1682 if (strchr(string
, '/') != NULL
)
1683 *strchr(string
, '/') = '\0';
1686 * Assign the default value as appropriate...
1689 if (!strcmp(keyword
, "DefaultColorSpace"))
1692 * Set default colorspace...
1695 if (!strcmp(string
, "CMY"))
1696 ppd
->colorspace
= PPD_CS_CMY
;
1697 else if (!strcmp(string
, "CMYK"))
1698 ppd
->colorspace
= PPD_CS_CMYK
;
1699 else if (!strcmp(string
, "RGB"))
1700 ppd
->colorspace
= PPD_CS_RGB
;
1701 else if (!strcmp(string
, "RGBK"))
1702 ppd
->colorspace
= PPD_CS_RGBK
;
1703 else if (!strcmp(string
, "N"))
1704 ppd
->colorspace
= PPD_CS_N
;
1706 ppd
->colorspace
= PPD_CS_GRAY
;
1708 else if (option
&& !strcmp(keyword
+ 7, option
->keyword
))
1711 * Set the default as part of the current option...
1714 strlcpy(option
->defchoice
, string
, sizeof(option
->defchoice
));
1716 DEBUG_printf(("2_ppdOpen: Set %s to %s...", keyword
, option
->defchoice
));
1721 * Lookup option and set if it has been defined...
1724 ppd_option_t
*toption
; /* Temporary option */
1726 if ((toption
= ppdFindOption(ppd
, keyword
+ 7)) != NULL
)
1728 if (!_cups_strcasecmp(string
, "custom") || !_cups_strncasecmp(string
, "custom.", 7))
1731 * "*DefaultOption: Custom..." may set the default to a custom value
1732 * or (for a very small number of incompatible PPD files) select a
1733 * standard choice for the option, which CUPS renames to "_Custom..."
1734 * to avoid compatibility issues. See which this is...
1737 snprintf(toption
->defchoice
, sizeof(toption
->defchoice
), "_%s", string
);
1738 if (!ppdFindChoice(toption
, toption
->defchoice
))
1739 strlcpy(toption
->defchoice
, string
, sizeof(toption
->defchoice
));
1743 strlcpy(toption
->defchoice
, string
, sizeof(toption
->defchoice
));
1746 DEBUG_printf(("2_ppdOpen: Set %s to %s...", keyword
, toption
->defchoice
));
1750 else if (!strcmp(keyword
, "UIConstraints") ||
1751 !strcmp(keyword
, "NonUIConstraints"))
1755 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1759 if (ppd
->num_consts
== 0)
1760 constraint
= calloc(2, sizeof(ppd_const_t
));
1762 constraint
= realloc(ppd
->consts
, (size_t)(ppd
->num_consts
+ 2) * sizeof(ppd_const_t
));
1764 if (constraint
== NULL
)
1766 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1771 ppd
->consts
= constraint
;
1772 constraint
+= ppd
->num_consts
;
1775 switch (sscanf(string
, "%40s%40s%40s%40s", constraint
->option1
,
1776 constraint
->choice1
, constraint
->option2
,
1777 constraint
->choice2
))
1779 default : /* Error */
1780 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1783 case 2 : /* Two options... */
1785 * Check for broken constraints like "* Option"...
1788 if (pg
->ppd_conform
== PPD_CONFORM_STRICT
&&
1789 (!strcmp(constraint
->option1
, "*") ||
1790 !strcmp(constraint
->choice1
, "*")))
1792 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1797 * The following strcpy's are safe, as optionN and
1798 * choiceN are all the same size (size defined by PPD spec...)
1801 if (constraint
->option1
[0] == '*')
1802 _cups_strcpy(constraint
->option1
, constraint
->option1
+ 1);
1803 else if (pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1805 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1809 if (constraint
->choice1
[0] == '*')
1810 _cups_strcpy(constraint
->option2
, constraint
->choice1
+ 1);
1811 else if (pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1813 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1817 constraint
->choice1
[0] = '\0';
1818 constraint
->choice2
[0] = '\0';
1821 case 3 : /* Two options, one choice... */
1823 * Check for broken constraints like "* Option"...
1826 if (pg
->ppd_conform
== PPD_CONFORM_STRICT
&&
1827 (!strcmp(constraint
->option1
, "*") ||
1828 !strcmp(constraint
->choice1
, "*") ||
1829 !strcmp(constraint
->option2
, "*")))
1831 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1836 * The following _cups_strcpy's are safe, as optionN and
1837 * choiceN are all the same size (size defined by PPD spec...)
1840 if (constraint
->option1
[0] == '*')
1841 _cups_strcpy(constraint
->option1
, constraint
->option1
+ 1);
1842 else if (pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1844 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1848 if (constraint
->choice1
[0] == '*')
1850 if (pg
->ppd_conform
== PPD_CONFORM_STRICT
&&
1851 constraint
->option2
[0] == '*')
1853 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1857 _cups_strcpy(constraint
->choice2
, constraint
->option2
);
1858 _cups_strcpy(constraint
->option2
, constraint
->choice1
+ 1);
1859 constraint
->choice1
[0] = '\0';
1863 if (constraint
->option2
[0] == '*')
1864 _cups_strcpy(constraint
->option2
, constraint
->option2
+ 1);
1865 else if (pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1867 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1871 constraint
->choice2
[0] = '\0';
1875 case 4 : /* Two options, two choices... */
1877 * Check for broken constraints like "* Option"...
1880 if (pg
->ppd_conform
== PPD_CONFORM_STRICT
&&
1881 (!strcmp(constraint
->option1
, "*") ||
1882 !strcmp(constraint
->choice1
, "*") ||
1883 !strcmp(constraint
->option2
, "*") ||
1884 !strcmp(constraint
->choice2
, "*")))
1886 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1890 if (constraint
->option1
[0] == '*')
1891 _cups_strcpy(constraint
->option1
, constraint
->option1
+ 1);
1892 else if (pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1894 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1898 if (pg
->ppd_conform
== PPD_CONFORM_STRICT
&&
1899 constraint
->choice1
[0] == '*')
1901 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1905 if (constraint
->option2
[0] == '*')
1906 _cups_strcpy(constraint
->option2
, constraint
->option2
+ 1);
1907 else if (pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1909 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1913 if (pg
->ppd_conform
== PPD_CONFORM_STRICT
&&
1914 constraint
->choice2
[0] == '*')
1916 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1923 * Don't add this one as an attribute...
1929 else if (!strcmp(keyword
, "PaperDimension"))
1931 if (!_cups_strcasecmp(name
, "custom") || !_cups_strncasecmp(name
, "custom.", 7))
1933 char cname
[PPD_MAX_NAME
]; /* Rewrite with a leading underscore */
1934 snprintf(cname
, sizeof(cname
), "_%s", name
);
1935 strlcpy(name
, cname
, sizeof(name
));
1938 if ((size
= ppdPageSize(ppd
, name
)) == NULL
)
1939 size
= ppd_add_size(ppd
, name
);
1944 * Unable to add or find size!
1947 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1952 size
->width
= (float)_cupsStrScand(string
, &sptr
, loc
);
1953 size
->length
= (float)_cupsStrScand(sptr
, NULL
, loc
);
1958 else if (!strcmp(keyword
, "ImageableArea"))
1960 if (!_cups_strcasecmp(name
, "custom") || !_cups_strncasecmp(name
, "custom.", 7))
1962 char cname
[PPD_MAX_NAME
]; /* Rewrite with a leading underscore */
1963 snprintf(cname
, sizeof(cname
), "_%s", name
);
1964 strlcpy(name
, cname
, sizeof(name
));
1967 if ((size
= ppdPageSize(ppd
, name
)) == NULL
)
1968 size
= ppd_add_size(ppd
, name
);
1973 * Unable to add or find size!
1976 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1981 size
->left
= (float)_cupsStrScand(string
, &sptr
, loc
);
1982 size
->bottom
= (float)_cupsStrScand(sptr
, &sptr
, loc
);
1983 size
->right
= (float)_cupsStrScand(sptr
, &sptr
, loc
);
1984 size
->top
= (float)_cupsStrScand(sptr
, NULL
, loc
);
1989 else if (option
!= NULL
&&
1990 (mask
& (PPD_KEYWORD
| PPD_OPTION
| PPD_STRING
)) ==
1991 (PPD_KEYWORD
| PPD_OPTION
| PPD_STRING
) &&
1992 !strcmp(keyword
, option
->keyword
))
1994 DEBUG_printf(("2_ppdOpen: group=%p, subgroup=%p", group
, subgroup
));
1996 if (!_cups_strcasecmp(name
, "custom") || !_cups_strncasecmp(name
, "custom.", 7))
1998 char cname
[PPD_MAX_NAME
]; /* Rewrite with a leading underscore */
1999 snprintf(cname
, sizeof(cname
), "_%s", name
);
2000 strlcpy(name
, cname
, sizeof(name
));
2003 if (!strcmp(keyword
, "PageSize"))
2006 * Add a page size...
2009 if (ppdPageSize(ppd
, name
) == NULL
)
2010 ppd_add_size(ppd
, name
);
2014 * Add the option choice...
2017 if ((choice
= ppd_add_choice(option
, name
)) == NULL
)
2019 pg
->ppd_status
= PPD_ALLOC_ERROR
;
2025 cupsCharsetToUTF8((cups_utf8_t
*)choice
->text
, text
,
2026 sizeof(choice
->text
), encoding
);
2027 else if (!strcmp(name
, "True"))
2028 strlcpy(choice
->text
, _("Yes"), sizeof(choice
->text
));
2029 else if (!strcmp(name
, "False"))
2030 strlcpy(choice
->text
, _("No"), sizeof(choice
->text
));
2032 strlcpy(choice
->text
, name
, sizeof(choice
->text
));
2034 if (option
->section
== PPD_ORDER_JCL
)
2035 ppd_decode(string
); /* Decode quoted string */
2037 choice
->code
= string
;
2038 string
= NULL
; /* Don't add as an attribute below */
2042 * Add remaining lines with keywords and string values as attributes...
2046 (mask
& (PPD_KEYWORD
| PPD_STRING
)) == (PPD_KEYWORD
| PPD_STRING
))
2047 ppd_add_attr(ppd
, keyword
, name
, text
, string
);
2053 * Check for a missing CloseUI/JCLCloseUI...
2056 if (option
&& pg
->ppd_conform
== PPD_CONFORM_STRICT
)
2058 pg
->ppd_status
= PPD_MISSING_CLOSE_UI
;
2063 * Check for a missing CloseGroup...
2066 if (group
&& pg
->ppd_conform
== PPD_CONFORM_STRICT
)
2068 pg
->ppd_status
= PPD_MISSING_CLOSE_GROUP
;
2075 * Reset language preferences...
2079 if (!cupsFileEOF(fp
))
2080 DEBUG_printf(("1_ppdOpen: Premature EOF at %lu...\n",
2081 (unsigned long)cupsFileTell(fp
)));
2084 if (pg
->ppd_status
!= PPD_OK
)
2087 * Had an error reading the PPD file, cannot continue!
2096 * Update the filters array as needed...
2099 if (!ppd_update_filters(ppd
, pg
))
2107 * Create the sorted options array and set the option back-pointer for
2108 * each choice and custom option...
2111 ppd
->options
= cupsArrayNew2((cups_array_func_t
)ppd_compare_options
, NULL
,
2112 (cups_ahash_func_t
)ppd_hash_option
,
2115 for (i
= ppd
->num_groups
, group
= ppd
->groups
;
2119 for (j
= group
->num_options
, option
= group
->options
;
2123 ppd_coption_t
*coption
; /* Custom option */
2126 cupsArrayAdd(ppd
->options
, option
);
2128 for (k
= 0; k
< option
->num_choices
; k
++)
2129 option
->choices
[k
].option
= option
;
2131 if ((coption
= ppdFindCustomOption(ppd
, option
->keyword
)) != NULL
)
2132 coption
->option
= option
;
2137 * Create an array to track the marked choices...
2140 ppd
->marked
= cupsArrayNew((cups_array_func_t
)ppd_compare_choices
, NULL
);
2143 * Return the PPD file structure...
2149 * Common exit point for errors to save code size...
2164 * 'ppdOpen()' - Read a PPD file into memory.
2167 ppd_file_t
* /* O - PPD file record */
2168 ppdOpen(FILE *fp
) /* I - File to read from */
2170 ppd_file_t
*ppd
; /* PPD file record */
2171 cups_file_t
*cf
; /* CUPS file */
2175 * Reopen the stdio file as a CUPS file...
2178 if ((cf
= cupsFileOpenFd(fileno(fp
), "r")) == NULL
)
2182 * Load the PPD file using the newer API...
2185 ppd
= _ppdOpen(cf
, _PPD_LOCALIZATION_DEFAULT
);
2188 * Close the CUPS file and return the PPD...
2198 * 'ppdOpen2()' - Read a PPD file into memory.
2200 * @since CUPS 1.2/macOS 10.5@
2203 ppd_file_t
* /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
2204 ppdOpen2(cups_file_t
*fp
) /* I - File to read from */
2206 return _ppdOpen(fp
, _PPD_LOCALIZATION_DEFAULT
);
2211 * 'ppdOpenFd()' - Read a PPD file into memory.
2214 ppd_file_t
* /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
2215 ppdOpenFd(int fd
) /* I - File to read from */
2217 cups_file_t
*fp
; /* CUPS file pointer */
2218 ppd_file_t
*ppd
; /* PPD file record */
2219 _ppd_globals_t
*pg
= _ppdGlobals();
2224 * Set the line number to 0...
2230 * Range check input...
2235 pg
->ppd_status
= PPD_NULL_FILE
;
2241 * Try to open the file and parse it...
2244 if ((fp
= cupsFileOpenFd(fd
, "r")) != NULL
)
2252 pg
->ppd_status
= PPD_FILE_OPEN_ERROR
;
2261 * '_ppdOpenFile()' - Read a PPD file into memory.
2264 ppd_file_t
* /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
2265 _ppdOpenFile(const char *filename
, /* I - File to read from */
2266 _ppd_localization_t localization
) /* I - Localization to load */
2268 cups_file_t
*fp
; /* File pointer */
2269 ppd_file_t
*ppd
; /* PPD file record */
2270 _ppd_globals_t
*pg
= _ppdGlobals();
2275 * Set the line number to 0...
2281 * Range check input...
2284 if (filename
== NULL
)
2286 pg
->ppd_status
= PPD_NULL_FILE
;
2292 * Try to open the file and parse it...
2295 if ((fp
= cupsFileOpen(filename
, "r")) != NULL
)
2297 ppd
= _ppdOpen(fp
, localization
);
2303 pg
->ppd_status
= PPD_FILE_OPEN_ERROR
;
2312 * 'ppdOpenFile()' - Read a PPD file into memory.
2315 ppd_file_t
* /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
2316 ppdOpenFile(const char *filename
) /* I - File to read from */
2318 return _ppdOpenFile(filename
, _PPD_LOCALIZATION_DEFAULT
);
2323 * 'ppdSetConformance()' - Set the conformance level for PPD files.
2325 * @since CUPS 1.1.20/macOS 10.4@
2329 ppdSetConformance(ppd_conform_t c
) /* I - Conformance level */
2331 _ppd_globals_t
*pg
= _ppdGlobals();
2335 pg
->ppd_conform
= c
;
2340 * 'ppd_add_attr()' - Add an attribute to the PPD data.
2343 static ppd_attr_t
* /* O - New attribute */
2344 ppd_add_attr(ppd_file_t
*ppd
, /* I - PPD file data */
2345 const char *name
, /* I - Attribute name */
2346 const char *spec
, /* I - Specifier string, if any */
2347 const char *text
, /* I - Text string, if any */
2348 const char *value
) /* I - Value of attribute */
2350 ppd_attr_t
**ptr
, /* New array */
2351 *temp
; /* New attribute */
2355 * Range check input...
2358 if (ppd
== NULL
|| name
== NULL
|| spec
== NULL
)
2362 * Create the array as needed...
2365 if (!ppd
->sorted_attrs
)
2366 ppd
->sorted_attrs
= cupsArrayNew((cups_array_func_t
)ppd_compare_attrs
,
2370 * Allocate memory for the new attribute...
2373 if (ppd
->num_attrs
== 0)
2374 ptr
= malloc(sizeof(ppd_attr_t
*));
2376 ptr
= realloc(ppd
->attrs
, (size_t)(ppd
->num_attrs
+ 1) * sizeof(ppd_attr_t
*));
2382 ptr
+= ppd
->num_attrs
;
2384 if ((temp
= calloc(1, sizeof(ppd_attr_t
))) == NULL
)
2395 if (!_cups_strcasecmp(spec
, "custom") || !_cups_strncasecmp(spec
, "custom.", 7))
2397 temp
->spec
[0] = '_';
2398 strlcpy(temp
->spec
+ 1, spec
, sizeof(temp
->spec
) - 1);
2401 strlcpy(temp
->spec
, spec
, sizeof(temp
->spec
));
2404 strlcpy(temp
->name
, name
, sizeof(temp
->name
));
2405 strlcpy(temp
->text
, text
, sizeof(temp
->text
));
2406 temp
->value
= (char *)value
;
2409 * Add the attribute to the sorted array...
2412 cupsArrayAdd(ppd
->sorted_attrs
, temp
);
2415 * Return the attribute...
2423 * 'ppd_add_choice()' - Add a choice to an option.
2426 static ppd_choice_t
* /* O - Named choice */
2427 ppd_add_choice(ppd_option_t
*option
, /* I - Option */
2428 const char *name
) /* I - Name of choice */
2430 ppd_choice_t
*choice
; /* Choice */
2433 if (option
->num_choices
== 0)
2434 choice
= malloc(sizeof(ppd_choice_t
));
2436 choice
= realloc(option
->choices
, sizeof(ppd_choice_t
) * (size_t)(option
->num_choices
+ 1));
2441 option
->choices
= choice
;
2442 choice
+= option
->num_choices
;
2443 option
->num_choices
++;
2445 memset(choice
, 0, sizeof(ppd_choice_t
));
2446 strlcpy(choice
->choice
, name
, sizeof(choice
->choice
));
2453 * 'ppd_add_size()' - Add a page size.
2456 static ppd_size_t
* /* O - Named size */
2457 ppd_add_size(ppd_file_t
*ppd
, /* I - PPD file */
2458 const char *name
) /* I - Name of size */
2460 ppd_size_t
*size
; /* Size */
2463 if (ppd
->num_sizes
== 0)
2464 size
= malloc(sizeof(ppd_size_t
));
2466 size
= realloc(ppd
->sizes
, sizeof(ppd_size_t
) * (size_t)(ppd
->num_sizes
+ 1));
2472 size
+= ppd
->num_sizes
;
2475 memset(size
, 0, sizeof(ppd_size_t
));
2476 strlcpy(size
->name
, name
, sizeof(size
->name
));
2483 * 'ppd_compare_attrs()' - Compare two attributes.
2486 static int /* O - Result of comparison */
2487 ppd_compare_attrs(ppd_attr_t
*a
, /* I - First attribute */
2488 ppd_attr_t
*b
) /* I - Second attribute */
2490 return (_cups_strcasecmp(a
->name
, b
->name
));
2495 * 'ppd_compare_choices()' - Compare two choices...
2498 static int /* O - Result of comparison */
2499 ppd_compare_choices(ppd_choice_t
*a
, /* I - First choice */
2500 ppd_choice_t
*b
) /* I - Second choice */
2502 return (strcmp(a
->option
->keyword
, b
->option
->keyword
));
2507 * 'ppd_compare_coptions()' - Compare two custom options.
2510 static int /* O - Result of comparison */
2511 ppd_compare_coptions(ppd_coption_t
*a
, /* I - First option */
2512 ppd_coption_t
*b
) /* I - Second option */
2514 return (_cups_strcasecmp(a
->keyword
, b
->keyword
));
2519 * 'ppd_compare_options()' - Compare two options.
2522 static int /* O - Result of comparison */
2523 ppd_compare_options(ppd_option_t
*a
, /* I - First option */
2524 ppd_option_t
*b
) /* I - Second option */
2526 return (_cups_strcasecmp(a
->keyword
, b
->keyword
));
2531 * 'ppd_decode()' - Decode a string value...
2534 static int /* O - Length of decoded string */
2535 ppd_decode(char *string
) /* I - String to decode */
2537 char *inptr
, /* Input pointer */
2538 *outptr
; /* Output pointer */
2544 while (*inptr
!= '\0')
2545 if (*inptr
== '<' && isxdigit(inptr
[1] & 255))
2548 * Convert hex to 8-bit values...
2552 while (isxdigit(*inptr
& 255))
2554 if (_cups_isalpha(*inptr
))
2555 *outptr
= (char)((tolower(*inptr
) - 'a' + 10) << 4);
2557 *outptr
= (char)((*inptr
- '0') << 4);
2561 if (!isxdigit(*inptr
& 255))
2564 if (_cups_isalpha(*inptr
))
2565 *outptr
|= (char)(tolower(*inptr
) - 'a' + 10);
2567 *outptr
|= (char)(*inptr
- '0');
2573 while (*inptr
!= '>' && *inptr
!= '\0')
2575 while (*inptr
== '>')
2579 *outptr
++ = *inptr
++;
2583 return ((int)(outptr
- string
));
2588 * 'ppd_free_filters()' - Free the filters array.
2592 ppd_free_filters(ppd_file_t
*ppd
) /* I - PPD file */
2594 int i
; /* Looping var */
2595 char **filter
; /* Current filter */
2598 if (ppd
->num_filters
> 0)
2600 for (i
= ppd
->num_filters
, filter
= ppd
->filters
; i
> 0; i
--, filter
++)
2605 ppd
->num_filters
= 0;
2606 ppd
->filters
= NULL
;
2612 * 'ppd_free_group()' - Free a single UI group.
2616 ppd_free_group(ppd_group_t
*group
) /* I - Group to free */
2618 int i
; /* Looping var */
2619 ppd_option_t
*option
; /* Current option */
2620 ppd_group_t
*subgroup
; /* Current sub-group */
2623 if (group
->num_options
> 0)
2625 for (i
= group
->num_options
, option
= group
->options
;
2628 ppd_free_option(option
);
2630 free(group
->options
);
2633 if (group
->num_subgroups
> 0)
2635 for (i
= group
->num_subgroups
, subgroup
= group
->subgroups
;
2638 ppd_free_group(subgroup
);
2640 free(group
->subgroups
);
2646 * 'ppd_free_option()' - Free a single option.
2650 ppd_free_option(ppd_option_t
*option
) /* I - Option to free */
2652 int i
; /* Looping var */
2653 ppd_choice_t
*choice
; /* Current choice */
2656 if (option
->num_choices
> 0)
2658 for (i
= option
->num_choices
, choice
= option
->choices
;
2665 free(option
->choices
);
2671 * 'ppd_get_coption()' - Get a custom option record.
2674 static ppd_coption_t
* /* O - Custom option... */
2675 ppd_get_coption(ppd_file_t
*ppd
, /* I - PPD file */
2676 const char *name
) /* I - Name of option */
2678 ppd_coption_t
*copt
; /* New custom option */
2682 * See if the option already exists...
2685 if ((copt
= ppdFindCustomOption(ppd
, name
)) != NULL
)
2689 * Not found, so create the custom option record...
2692 if ((copt
= calloc(1, sizeof(ppd_coption_t
))) == NULL
)
2695 strlcpy(copt
->keyword
, name
, sizeof(copt
->keyword
));
2697 copt
->params
= cupsArrayNew((cups_array_func_t
)NULL
, NULL
);
2699 cupsArrayAdd(ppd
->coptions
, copt
);
2702 * Return the new record...
2710 * 'ppd_get_cparam()' - Get a custom parameter record.
2713 static ppd_cparam_t
* /* O - Extended option... */
2714 ppd_get_cparam(ppd_coption_t
*opt
, /* I - PPD file */
2715 const char *param
, /* I - Name of parameter */
2716 const char *text
) /* I - Human-readable text */
2718 ppd_cparam_t
*cparam
; /* New custom parameter */
2722 * See if the parameter already exists...
2725 if ((cparam
= ppdFindCustomParam(opt
, param
)) != NULL
)
2729 * Not found, so create the custom parameter record...
2732 if ((cparam
= calloc(1, sizeof(ppd_cparam_t
))) == NULL
)
2735 cparam
->type
= PPD_CUSTOM_UNKNOWN
;
2736 strlcpy(cparam
->name
, param
, sizeof(cparam
->name
));
2737 strlcpy(cparam
->text
, text
[0] ? text
: param
, sizeof(cparam
->text
));
2740 * Add this record to the array...
2743 cupsArrayAdd(opt
->params
, cparam
);
2746 * Return the new record...
2754 * 'ppd_get_group()' - Find or create the named group as needed.
2757 static ppd_group_t
* /* O - Named group */
2758 ppd_get_group(ppd_file_t
*ppd
, /* I - PPD file */
2759 const char *name
, /* I - Name of group */
2760 const char *text
, /* I - Text for group */
2761 _ppd_globals_t
*pg
, /* I - Global data */
2762 cups_encoding_t encoding
) /* I - Encoding of text */
2764 int i
; /* Looping var */
2765 ppd_group_t
*group
; /* Group */
2768 DEBUG_printf(("7ppd_get_group(ppd=%p, name=\"%s\", text=\"%s\", cg=%p)",
2769 ppd
, name
, text
, pg
));
2771 for (i
= ppd
->num_groups
, group
= ppd
->groups
; i
> 0; i
--, group
++)
2772 if (!strcmp(group
->name
, name
))
2777 DEBUG_printf(("8ppd_get_group: Adding group %s...", name
));
2779 if (pg
->ppd_conform
== PPD_CONFORM_STRICT
&& strlen(text
) >= sizeof(group
->text
))
2781 pg
->ppd_status
= PPD_ILLEGAL_TRANSLATION
;
2786 if (ppd
->num_groups
== 0)
2787 group
= malloc(sizeof(ppd_group_t
));
2789 group
= realloc(ppd
->groups
, (size_t)(ppd
->num_groups
+ 1) * sizeof(ppd_group_t
));
2793 pg
->ppd_status
= PPD_ALLOC_ERROR
;
2798 ppd
->groups
= group
;
2799 group
+= ppd
->num_groups
;
2802 memset(group
, 0, sizeof(ppd_group_t
));
2803 strlcpy(group
->name
, name
, sizeof(group
->name
));
2805 cupsCharsetToUTF8((cups_utf8_t
*)group
->text
, text
,
2806 sizeof(group
->text
), encoding
);
2814 * 'ppd_get_option()' - Find or create the named option as needed.
2817 static ppd_option_t
* /* O - Named option */
2818 ppd_get_option(ppd_group_t
*group
, /* I - Group */
2819 const char *name
) /* I - Name of option */
2821 int i
; /* Looping var */
2822 ppd_option_t
*option
; /* Option */
2825 DEBUG_printf(("7ppd_get_option(group=%p(\"%s\"), name=\"%s\")",
2826 group
, group
->name
, name
));
2828 for (i
= group
->num_options
, option
= group
->options
; i
> 0; i
--, option
++)
2829 if (!strcmp(option
->keyword
, name
))
2834 if (group
->num_options
== 0)
2835 option
= malloc(sizeof(ppd_option_t
));
2837 option
= realloc(group
->options
, (size_t)(group
->num_options
+ 1) * sizeof(ppd_option_t
));
2842 group
->options
= option
;
2843 option
+= group
->num_options
;
2844 group
->num_options
++;
2846 memset(option
, 0, sizeof(ppd_option_t
));
2847 strlcpy(option
->keyword
, name
, sizeof(option
->keyword
));
2855 * 'ppd_globals_alloc()' - Allocate and initialize global data.
2858 static _ppd_globals_t
* /* O - Pointer to global data */
2859 ppd_globals_alloc(void)
2861 return ((_ppd_globals_t
*)calloc(1, sizeof(_ppd_globals_t
)));
2866 * 'ppd_globals_free()' - Free global data.
2869 #if defined(HAVE_PTHREAD_H) || defined(_WIN32)
2871 ppd_globals_free(_ppd_globals_t
*pg
) /* I - Pointer to global data */
2875 #endif /* HAVE_PTHREAD_H || _WIN32 */
2878 #ifdef HAVE_PTHREAD_H
2880 * 'ppd_globals_init()' - Initialize per-thread globals...
2884 ppd_globals_init(void)
2887 * Register the global data for this thread...
2890 pthread_key_create(&ppd_globals_key
, (void (*)(void *))ppd_globals_free
);
2892 #endif /* HAVE_PTHREAD_H */
2896 * 'ppd_hash_option()' - Generate a hash of the option name...
2899 static int /* O - Hash index */
2900 ppd_hash_option(ppd_option_t
*option
) /* I - Option */
2902 int hash
= 0; /* Hash index */
2903 const char *k
; /* Pointer into keyword */
2906 for (hash
= option
->keyword
[0], k
= option
->keyword
+ 1; *k
;)
2907 hash
= (int)(33U * (unsigned)hash
) + *k
++;
2909 return (hash
& 511);
2914 * 'ppd_read()' - Read a line from a PPD file, skipping comment lines as
2918 static int /* O - Bitmask of fields read */
2919 ppd_read(cups_file_t
*fp
, /* I - File to read from */
2920 _ppd_line_t
*line
, /* I - Line buffer */
2921 char *keyword
, /* O - Keyword from line */
2922 char *option
, /* O - Option from line */
2923 char *text
, /* O - Human-readable text from line */
2924 char **string
, /* O - Code/string data */
2925 int ignoreblank
, /* I - Ignore blank lines? */
2926 _ppd_globals_t
*pg
) /* I - Global data */
2928 int ch
, /* Character from file */
2929 col
, /* Column in line */
2930 colon
, /* Colon seen? */
2931 endquote
, /* Waiting for an end quote */
2932 mask
, /* Mask to be returned */
2933 startline
, /* Start line */
2934 textlen
; /* Length of text */
2935 char *keyptr
, /* Keyword pointer */
2936 *optptr
, /* Option pointer */
2937 *textptr
, /* Text pointer */
2938 *strptr
, /* Pointer into string */
2939 *lineptr
; /* Current position in line buffer */
2943 * Now loop until we have a valid line...
2948 startline
= pg
->ppd_line
+ 1;
2952 line
->bufsize
= 1024;
2953 line
->buffer
= malloc(1024);
2965 lineptr
= line
->buffer
;
2969 while ((ch
= cupsFileGetChar(fp
)) != EOF
)
2971 if (lineptr
>= (line
->buffer
+ line
->bufsize
- 1))
2974 * Expand the line buffer...
2977 char *temp
; /* Temporary line pointer */
2980 line
->bufsize
+= 1024;
2981 if (line
->bufsize
> 262144)
2984 * Don't allow lines longer than 256k!
2987 pg
->ppd_line
= startline
;
2988 pg
->ppd_status
= PPD_LINE_TOO_LONG
;
2993 temp
= realloc(line
->buffer
, line
->bufsize
);
2996 pg
->ppd_line
= startline
;
2997 pg
->ppd_status
= PPD_LINE_TOO_LONG
;
3002 lineptr
= temp
+ (lineptr
- line
->buffer
);
3003 line
->buffer
= temp
;
3006 if (ch
== '\r' || ch
== '\n')
3009 * Line feed or carriage return...
3018 * Check for a trailing line feed...
3021 if ((ch
= cupsFilePeekChar(fp
)) == EOF
)
3028 cupsFileGetChar(fp
);
3031 if (lineptr
== line
->buffer
&& ignoreblank
)
3032 continue; /* Skip blank lines */
3036 if (!endquote
) /* Continue for multi-line text */
3041 else if (ch
< ' ' && ch
!= '\t' && pg
->ppd_conform
== PPD_CONFORM_STRICT
)
3044 * Other control characters...
3047 pg
->ppd_line
= startline
;
3048 pg
->ppd_status
= PPD_ILLEGAL_CHARACTER
;
3052 else if (ch
!= 0x1a)
3055 * Any other character...
3058 *lineptr
++ = (char)ch
;
3061 if (col
> (PPD_MAX_LINE
- 1))
3064 * Line is too long...
3067 pg
->ppd_line
= startline
;
3068 pg
->ppd_status
= PPD_LINE_TOO_LONG
;
3073 if (ch
== ':' && strncmp(line
->buffer
, "*%", 2) != 0)
3076 if (ch
== '\"' && colon
)
3077 endquote
= !endquote
;
3084 * Didn't finish this quoted string...
3087 while ((ch
= cupsFileGetChar(fp
)) != EOF
)
3090 else if (ch
== '\r' || ch
== '\n')
3098 * Check for a trailing line feed...
3101 if ((ch
= cupsFilePeekChar(fp
)) == EOF
)
3104 cupsFileGetChar(fp
);
3107 else if (ch
< ' ' && ch
!= '\t' && pg
->ppd_conform
== PPD_CONFORM_STRICT
)
3110 * Other control characters...
3113 pg
->ppd_line
= startline
;
3114 pg
->ppd_status
= PPD_ILLEGAL_CHARACTER
;
3118 else if (ch
!= 0x1a)
3122 if (col
> (PPD_MAX_LINE
- 1))
3125 * Line is too long...
3128 pg
->ppd_line
= startline
;
3129 pg
->ppd_status
= PPD_LINE_TOO_LONG
;
3139 * Didn't finish this line...
3142 while ((ch
= cupsFileGetChar(fp
)) != EOF
)
3143 if (ch
== '\r' || ch
== '\n')
3146 * Line feed or carriage return...
3155 * Check for a trailing line feed...
3158 if ((ch
= cupsFilePeekChar(fp
)) == EOF
)
3161 cupsFileGetChar(fp
);
3166 else if (ch
< ' ' && ch
!= '\t' && pg
->ppd_conform
== PPD_CONFORM_STRICT
)
3169 * Other control characters...
3172 pg
->ppd_line
= startline
;
3173 pg
->ppd_status
= PPD_ILLEGAL_CHARACTER
;
3177 else if (ch
!= 0x1a)
3181 if (col
> (PPD_MAX_LINE
- 1))
3184 * Line is too long...
3187 pg
->ppd_line
= startline
;
3188 pg
->ppd_status
= PPD_LINE_TOO_LONG
;
3195 if (lineptr
> line
->buffer
&& lineptr
[-1] == '\n')
3200 DEBUG_printf(("9ppd_read: LINE=\"%s\"", line
->buffer
));
3203 * The dynamically created PPDs for older style macOS
3204 * drivers include a large blob of data inserted as comments
3205 * at the end of the file. As an optimization we can stop
3206 * reading the PPD when we get to the start of this data.
3209 if (!strcmp(line
->buffer
, "*%APLWORKSET START"))
3212 if (ch
== EOF
&& lineptr
== line
->buffer
)
3220 lineptr
= line
->buffer
+ 1;
3227 if ((!line
->buffer
[0] || /* Blank line */
3228 !strncmp(line
->buffer
, "*%", 2) || /* Comment line */
3229 !strcmp(line
->buffer
, "*End")) && /* End of multi-line string */
3230 ignoreblank
) /* Ignore these? */
3232 startline
= pg
->ppd_line
+ 1;
3236 if (!strcmp(line
->buffer
, "*")) /* (Bad) comment line */
3238 if (pg
->ppd_conform
== PPD_CONFORM_RELAXED
)
3240 startline
= pg
->ppd_line
+ 1;
3245 pg
->ppd_line
= startline
;
3246 pg
->ppd_status
= PPD_ILLEGAL_MAIN_KEYWORD
;
3252 if (line
->buffer
[0] != '*') /* All lines start with an asterisk */
3255 * Allow lines consisting of just whitespace...
3258 for (lineptr
= line
->buffer
; *lineptr
; lineptr
++)
3259 if (*lineptr
&& !_cups_isspace(*lineptr
))
3264 pg
->ppd_status
= PPD_MISSING_ASTERISK
;
3267 else if (ignoreblank
)
3279 while (*lineptr
&& *lineptr
!= ':' && !_cups_isspace(*lineptr
))
3281 if (*lineptr
<= ' ' || *lineptr
> 126 || *lineptr
== '/' ||
3282 (keyptr
- keyword
) >= (PPD_MAX_NAME
- 1))
3284 pg
->ppd_status
= PPD_ILLEGAL_MAIN_KEYWORD
;
3288 *keyptr
++ = *lineptr
++;
3293 if (!strcmp(keyword
, "End"))
3296 mask
|= PPD_KEYWORD
;
3298 if (_cups_isspace(*lineptr
))
3301 * Get an option name...
3304 while (_cups_isspace(*lineptr
))
3309 while (*lineptr
&& !_cups_isspace(*lineptr
) && *lineptr
!= ':' &&
3312 if (*lineptr
<= ' ' || *lineptr
> 126 ||
3313 (optptr
- option
) >= (PPD_MAX_NAME
- 1))
3315 pg
->ppd_status
= PPD_ILLEGAL_OPTION_KEYWORD
;
3319 *optptr
++ = *lineptr
++;
3324 if (_cups_isspace(*lineptr
) && pg
->ppd_conform
== PPD_CONFORM_STRICT
)
3326 pg
->ppd_status
= PPD_ILLEGAL_WHITESPACE
;
3330 while (_cups_isspace(*lineptr
))
3335 if (*lineptr
== '/')
3338 * Get human-readable text...
3345 while (*lineptr
!= '\0' && *lineptr
!= '\n' && *lineptr
!= ':')
3347 if (((unsigned char)*lineptr
< ' ' && *lineptr
!= '\t') ||
3348 (textptr
- text
) >= (PPD_MAX_LINE
- 1))
3350 pg
->ppd_status
= PPD_ILLEGAL_TRANSLATION
;
3354 *textptr
++ = *lineptr
++;
3358 textlen
= ppd_decode(text
);
3360 if (textlen
> PPD_MAX_TEXT
&& pg
->ppd_conform
== PPD_CONFORM_STRICT
)
3362 pg
->ppd_status
= PPD_ILLEGAL_TRANSLATION
;
3370 if (_cups_isspace(*lineptr
) && pg
->ppd_conform
== PPD_CONFORM_STRICT
)
3372 pg
->ppd_status
= PPD_ILLEGAL_WHITESPACE
;
3376 while (_cups_isspace(*lineptr
))
3379 if (*lineptr
== ':')
3382 * Get string after triming leading and trailing whitespace...
3386 while (_cups_isspace(*lineptr
))
3389 strptr
= lineptr
+ strlen(lineptr
) - 1;
3390 while (strptr
>= lineptr
&& _cups_isspace(*strptr
))
3393 if (*strptr
== '\"')
3396 * Quoted string by itself, remove quotes...
3403 *string
= strdup(lineptr
);
3415 * 'ppd_update_filters()' - Update the filters array as needed.
3417 * This function re-populates the filters array with cupsFilter2 entries that
3418 * have been stripped of the destination MIME media types and any maxsize hints.
3420 * (All for backwards-compatibility)
3423 static int /* O - 1 on success, 0 on failure */
3424 ppd_update_filters(ppd_file_t
*ppd
, /* I - PPD file */
3425 _ppd_globals_t
*pg
) /* I - Global data */
3427 ppd_attr_t
*attr
; /* Current cupsFilter2 value */
3428 char srcsuper
[16], /* Source MIME media type */
3430 dstsuper
[16], /* Destination MIME media type */
3432 program
[1024], /* Command to run */
3433 *ptr
, /* Pointer into command to run */
3434 buffer
[1024], /* Re-written cupsFilter value */
3435 **filter
; /* Current filter */
3436 int cost
; /* Cost of filter */
3439 DEBUG_printf(("4ppd_update_filters(ppd=%p, cg=%p)", ppd
, pg
));
3442 * See if we have any cupsFilter2 lines...
3445 if ((attr
= ppdFindAttr(ppd
, "cupsFilter2", NULL
)) == NULL
)
3447 DEBUG_puts("5ppd_update_filters: No cupsFilter2 keywords present.");
3452 * Yes, free the cupsFilter-defined filters and re-build...
3455 ppd_free_filters(ppd
);
3460 * Parse the cupsFilter2 string:
3462 * src/type dst/type cost program
3463 * src/type dst/type cost maxsize(n) program
3466 DEBUG_printf(("5ppd_update_filters: cupsFilter2=\"%s\"", attr
->value
));
3468 if (sscanf(attr
->value
, "%15[^/]/%255s%*[ \t]%15[^/]/%255s%d%*[ \t]%1023[^\n]",
3469 srcsuper
, srctype
, dstsuper
, dsttype
, &cost
, program
) != 6)
3471 DEBUG_puts("5ppd_update_filters: Bad cupsFilter2 line.");
3472 pg
->ppd_status
= PPD_BAD_VALUE
;
3477 DEBUG_printf(("5ppd_update_filters: srcsuper=\"%s\", srctype=\"%s\", "
3478 "dstsuper=\"%s\", dsttype=\"%s\", cost=%d, program=\"%s\"",
3479 srcsuper
, srctype
, dstsuper
, dsttype
, cost
, program
));
3481 if (!strncmp(program
, "maxsize(", 8) &&
3482 (ptr
= strchr(program
+ 8, ')')) != NULL
)
3484 DEBUG_puts("5ppd_update_filters: Found maxsize(nnn).");
3487 while (_cups_isspace(*ptr
))
3490 _cups_strcpy(program
, ptr
);
3491 DEBUG_printf(("5ppd_update_filters: New program=\"%s\"", program
));
3495 * Convert to cupsFilter format:
3497 * src/type cost program
3500 snprintf(buffer
, sizeof(buffer
), "%s/%s %d %s", srcsuper
, srctype
, cost
,
3502 DEBUG_printf(("5ppd_update_filters: Adding \"%s\".", buffer
));
3505 * Add a cupsFilter-compatible string to the filters array.
3508 if (ppd
->num_filters
== 0)
3509 filter
= malloc(sizeof(char *));
3511 filter
= realloc(ppd
->filters
, sizeof(char *) * (size_t)(ppd
->num_filters
+ 1));
3515 DEBUG_puts("5ppd_update_filters: Out of memory.");
3516 pg
->ppd_status
= PPD_ALLOC_ERROR
;
3521 ppd
->filters
= filter
;
3522 filter
+= ppd
->num_filters
;
3523 ppd
->num_filters
++;
3525 *filter
= strdup(buffer
);
3527 while ((attr
= ppdFindNextAttr(ppd
, "cupsFilter2", NULL
)) != NULL
);
3529 DEBUG_puts("5ppd_update_filters: Completed OK.");