2 * PPD file routines for CUPS.
4 * Copyright © 2020-2025 by OpenPrinting.
5 * Copyright © 2007-2019 by Apple Inc.
6 * Copyright © 1997-2007 by Easy Software Products, all rights reserved.
8 * Licensed under Apache License v2.0. See the file "LICENSE" for more
11 * PostScript is a trademark of Adobe Systems, Inc.
14 #include "cups-private.h"
15 #include "ppd-private.h"
16 #include "debug-internal.h"
23 #define PPD_KEYWORD 1 /* Line contained a keyword */
24 #define PPD_OPTION 2 /* Line contained an option name */
25 #define PPD_TEXT 4 /* Line contained human-readable text */
26 #define PPD_STRING 8 /* Line contained a string or code */
28 #define PPD_HASHSIZE 512 /* Size of hash */
32 * Line buffer structure...
35 typedef struct _ppd_line_s
37 char *buffer
; /* Pointer to buffer */
38 size_t bufsize
; /* Size of the buffer */
46 static cups_thread_key_t ppd_globals_key
= CUPS_THREADKEY_INITIALIZER
;
47 /* Thread local storage key */
49 static pthread_once_t ppd_globals_key_once
= PTHREAD_ONCE_INIT
;
50 /* One-time initialization object */
51 #endif /* HAVE_PTHREAD_H */
58 static ppd_attr_t
*ppd_add_attr(ppd_file_t
*ppd
, const char *name
,
59 const char *spec
, const char *text
,
61 static ppd_choice_t
*ppd_add_choice(ppd_option_t
*option
, const char *name
);
62 static ppd_size_t
*ppd_add_size(ppd_file_t
*ppd
, const char *name
);
63 static int ppd_compare_attrs(ppd_attr_t
*a
, ppd_attr_t
*b
, void *data
);
64 static int ppd_compare_choices(ppd_choice_t
*a
, ppd_choice_t
*b
, void *data
);
65 static int ppd_compare_coptions(ppd_coption_t
*a
, ppd_coption_t
*b
, void *data
);
66 static int ppd_compare_options(ppd_option_t
*a
, ppd_option_t
*b
, void *data
);
67 static int ppd_decode(char *string
);
68 static void ppd_free_filters(ppd_file_t
*ppd
);
69 static void ppd_free_group(ppd_group_t
*group
);
70 static void ppd_free_option(ppd_option_t
*option
);
71 static ppd_coption_t
*ppd_get_coption(ppd_file_t
*ppd
, const char *name
);
72 static ppd_cparam_t
*ppd_get_cparam(ppd_coption_t
*opt
, const char *param
,
74 static ppd_group_t
*ppd_get_group(ppd_file_t
*ppd
, const char *name
,
75 const char *text
, _ppd_globals_t
*pg
,
76 cups_encoding_t encoding
);
77 static ppd_option_t
*ppd_get_option(ppd_group_t
*group
, const char *name
);
78 static _ppd_globals_t
*ppd_globals_alloc(void);
79 #if defined(HAVE_PTHREAD_H) || defined(_WIN32)
80 static void ppd_globals_free(_ppd_globals_t
*g
);
81 #endif /* HAVE_PTHREAD_H || _WIN32 */
83 static void ppd_globals_init(void);
84 #endif /* HAVE_PTHREAD_H */
85 static int ppd_hash_option(ppd_option_t
*option
, void *data
);
86 static int ppd_read(cups_file_t
*fp
, _ppd_line_t
*line
,
87 char *keyword
, char *option
, char *text
,
88 char **string
, int ignoreblank
,
90 static int ppd_update_filters(ppd_file_t
*ppd
,
95 * 'ppdClose()' - Free all memory used by the PPD file.
99 ppdClose(ppd_file_t
*ppd
) /* I - PPD file record */
101 int i
; /* Looping var */
102 ppd_group_t
*group
; /* Current group */
103 char **font
; /* Current font */
104 ppd_attr_t
**attr
; /* Current attribute */
105 ppd_coption_t
*coption
; /* Current custom option */
106 ppd_cparam_t
*cparam
; /* Current custom parameter */
110 * Range check arguments...
117 * Free all strings at the top level...
120 free(ppd
->lang_encoding
);
123 free(ppd
->emulations
);
124 free(ppd
->jcl_begin
);
129 * Free any UI groups, subgroups, and options...
132 if (ppd
->num_groups
> 0)
134 for (i
= ppd
->num_groups
, group
= ppd
->groups
; i
> 0; i
--, group
++)
135 ppd_free_group(group
);
140 cupsArrayDelete(ppd
->options
);
141 cupsArrayDelete(ppd
->marked
);
144 * Free any page sizes...
147 if (ppd
->num_sizes
> 0)
151 * Free any constraints...
154 if (ppd
->num_consts
> 0)
158 * Free any filters...
161 ppd_free_filters(ppd
);
167 if (ppd
->num_fonts
> 0)
169 for (i
= ppd
->num_fonts
, font
= ppd
->fonts
; i
> 0; i
--, font
++)
176 * Free any profiles...
179 if (ppd
->num_profiles
> 0)
183 * Free any attributes...
186 if (ppd
->num_attrs
> 0)
188 for (i
= ppd
->num_attrs
, attr
= ppd
->attrs
; i
> 0; i
--, attr
++)
190 free((*attr
)->value
);
197 cupsArrayDelete(ppd
->sorted_attrs
);
200 * Free custom options...
203 for (coption
= (ppd_coption_t
*)cupsArrayFirst(ppd
->coptions
);
205 coption
= (ppd_coption_t
*)cupsArrayNext(ppd
->coptions
))
207 for (cparam
= (ppd_cparam_t
*)cupsArrayFirst(coption
->params
);
209 cparam
= (ppd_cparam_t
*)cupsArrayNext(coption
->params
))
211 switch (cparam
->type
)
213 case PPD_CUSTOM_PASSCODE
:
214 case PPD_CUSTOM_PASSWORD
:
215 case PPD_CUSTOM_STRING
:
216 free(cparam
->current
.custom_string
);
226 cupsArrayDelete(coption
->params
);
231 cupsArrayDelete(ppd
->coptions
);
234 * Free constraints...
237 if (ppd
->cups_uiconstraints
)
239 _ppd_cups_uiconsts_t
*consts
; /* Current constraints */
242 for (consts
= (_ppd_cups_uiconsts_t
*)cupsArrayFirst(ppd
->cups_uiconstraints
);
244 consts
= (_ppd_cups_uiconsts_t
*)cupsArrayNext(ppd
->cups_uiconstraints
))
246 free(consts
->constraints
);
250 cupsArrayDelete(ppd
->cups_uiconstraints
);
254 * Free any PPD cache/mapping data...
258 _ppdCacheDestroy(ppd
->cache
);
261 * Free the whole record...
269 * 'ppdErrorString()' - Returns the text associated with a status.
271 * @since CUPS 1.1.19@
274 const char * /* O - Status string */
275 ppdErrorString(ppd_status_t status
) /* I - PPD status */
277 static const char * const messages
[] =/* Status messages */
280 _("Unable to open PPD file"),
281 _("NULL PPD file pointer"),
282 _("Memory allocation error"),
283 _("Missing PPD-Adobe-4.x header"),
284 _("Missing value string"),
287 _("OpenGroup without a CloseGroup first"),
288 _("Bad OpenUI/JCLOpenUI"),
289 _("OpenUI/JCLOpenUI without a CloseUI/JCLCloseUI first"),
290 _("Bad OrderDependency"),
291 _("Bad UIConstraints"),
292 _("Missing asterisk in column 1"),
293 _("Line longer than the maximum allowed (255 characters)"),
294 _("Illegal control character"),
295 _("Illegal main keyword string"),
296 _("Illegal option keyword string"),
297 _("Illegal translation string"),
298 _("Illegal whitespace character"),
299 _("Bad custom parameter"),
300 _("Missing option keyword"),
301 _("Bad value string"),
302 _("Missing CloseGroup"),
303 _("Bad CloseUI/JCLCloseUI"),
304 _("Missing CloseUI/JCLCloseUI")
308 if (status
< PPD_OK
|| status
>= PPD_MAX_STATUS
)
309 return (_cupsLangString(cupsLangDefault(), _("Unknown")));
311 return (_cupsLangString(cupsLangDefault(), messages
[status
]));
316 * '_ppdGetEncoding()' - Get the CUPS encoding value for the given
320 cups_encoding_t
/* O - CUPS encoding value */
321 _ppdGetEncoding(const char *name
) /* I - LanguageEncoding string */
323 if (!_cups_strcasecmp(name
, "ISOLatin1"))
324 return (CUPS_ISO8859_1
);
325 else if (!_cups_strcasecmp(name
, "ISOLatin2"))
326 return (CUPS_ISO8859_2
);
327 else if (!_cups_strcasecmp(name
, "ISOLatin5"))
328 return (CUPS_ISO8859_5
);
329 else if (!_cups_strcasecmp(name
, "JIS83-RKSJ"))
330 return (CUPS_JIS_X0213
);
331 else if (!_cups_strcasecmp(name
, "MacStandard"))
332 return (CUPS_MAC_ROMAN
);
333 else if (!_cups_strcasecmp(name
, "WindowsANSI"))
334 return (CUPS_WINDOWS_1252
);
341 * '_ppdGlobals()' - Return a pointer to thread local storage
344 _ppd_globals_t
* /* O - Pointer to global data */
347 _ppd_globals_t
*pg
; /* Pointer to global data */
350 #ifdef HAVE_PTHREAD_H
352 * Initialize the global data exactly once...
355 pthread_once(&ppd_globals_key_once
, ppd_globals_init
);
356 #endif /* HAVE_PTHREAD_H */
359 * See if we have allocated the data yet...
362 if ((pg
= (_ppd_globals_t
*)cupsThreadGetData(ppd_globals_key
)) == NULL
)
365 * No, allocate memory as set the pointer for the key...
368 if ((pg
= ppd_globals_alloc()) != NULL
)
369 cupsThreadSetData(ppd_globals_key
, pg
);
373 * Return the pointer to the data...
381 * 'ppdLastError()' - Return the status from the last ppdOpen*().
383 * @since CUPS 1.1.19@
386 ppd_status_t
/* O - Status code */
387 ppdLastError(int *line
) /* O - Line number */
389 _ppd_globals_t
*pg
= _ppdGlobals();
394 *line
= pg
->ppd_line
;
396 return (pg
->ppd_status
);
401 * '_ppdOpen()' - Read a PPD file into memory.
406 ppd_file_t
* /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
408 cups_file_t
*fp
, /* I - File to read from */
409 _ppd_localization_t localization
) /* I - Localization to load */
411 int i
, j
, k
; /* Looping vars */
412 _ppd_line_t line
; /* Line buffer */
413 ppd_file_t
*ppd
; /* PPD file record */
414 ppd_group_t
*group
, /* Current group */
415 *subgroup
; /* Current sub-group */
416 ppd_option_t
*option
; /* Current option */
417 ppd_choice_t
*choice
; /* Current choice */
418 ppd_const_t
*constraint
; /* Current constraint */
419 ppd_size_t
*size
; /* Current page size */
420 int mask
; /* Line data mask */
421 char keyword
[PPD_MAX_NAME
],
422 /* Keyword from file */
424 /* Option from file */
426 /* Human-readable text from file */
427 *string
, /* Code/text from file */
428 *sptr
, /* Pointer into string */
429 *temp
, /* Temporary string pointer */
430 **tempfonts
; /* Temporary fonts pointer */
431 float order
; /* Order dependency number */
432 ppd_section_t section
; /* Order dependency section */
433 ppd_profile_t
*profile
; /* Pointer to color profile */
434 char **filter
; /* Pointer to filter */
435 struct lconv
*loc
; /* Locale data */
436 int ui_keyword
; /* Is this line a UI keyword? */
437 cups_lang_t
*lang
; /* Language data */
438 cups_encoding_t encoding
; /* Encoding of PPD file */
439 _ppd_globals_t
*pg
= _ppdGlobals();
441 char custom_name
[PPD_MAX_NAME
];
442 /* CustomFoo attribute name */
443 ppd_attr_t
*custom_attr
; /* CustomFoo attribute */
444 char ll
[7], /* Base language + '.' */
445 ll_CC
[7]; /* Language w/country + '.' */
446 size_t ll_len
= 0, /* Base language length */
447 ll_CC_len
= 0; /* Language w/country length */
448 static const char * const ui_keywords
[] =
450 #ifdef CUPS_USE_FULL_UI_KEYWORDS_LIST
452 * Adobe defines some 41 keywords as "UI", meaning that they are
453 * user interface elements and that they should be treated as such
454 * even if the PPD creator doesn't use Open/CloseUI around them.
456 * Since this can cause previously invisible options to appear and
457 * confuse users, the default is to only treat the PageSize and
458 * PageRegion keywords this way.
460 /* Boolean keywords */
470 /* PickOne keywords */
483 "JCLFrameBufferSize",
504 #else /* !CUPS_USE_FULL_UI_KEYWORDS_LIST */
507 #endif /* CUPS_USE_FULL_UI_KEYWORDS_LIST */
509 static const char * const color_keywords
[] = /* Keywords associated with color profiles */
516 DEBUG_printf("_ppdOpen(fp=%p)", (void *)fp
);
519 * Default to "OK" status...
522 pg
->ppd_status
= PPD_OK
;
526 * Range check input...
531 pg
->ppd_status
= PPD_NULL_FILE
;
536 * If only loading a single localization set up the strings to match...
539 if (localization
== _PPD_LOCALIZATION_DEFAULT
)
541 if ((lang
= cupsLangDefault()) == NULL
)
544 snprintf(ll_CC
, sizeof(ll_CC
), "%s.", lang
->language
);
547 * <rdar://problem/22130168>
548 * <rdar://problem/27245567>
550 * Need to use a different base language for some locales...
553 if (!strcmp(lang
->language
, "zh_HK"))
554 { /* Traditional Chinese + variants */
555 cupsCopyString(ll_CC
, "zh_TW.", sizeof(ll_CC
));
556 cupsCopyString(ll
, "zh_", sizeof(ll
));
558 else if (!strncmp(lang
->language
, "zh", 2))
559 cupsCopyString(ll
, "zh_", sizeof(ll
)); /* Any Chinese variant */
560 else if (!strncmp(lang
->language
, "jp", 2))
561 { /* Any Japanese variant */
562 cupsCopyString(ll_CC
, "ja", sizeof(ll_CC
));
563 cupsCopyString(ll
, "jp", sizeof(ll
));
565 else if (!strncmp(lang
->language
, "nb", 2) || !strncmp(lang
->language
, "no", 2))
566 { /* Any Norwegian variant */
567 cupsCopyString(ll_CC
, "nb", sizeof(ll_CC
));
568 cupsCopyString(ll
, "no", sizeof(ll
));
571 snprintf(ll
, sizeof(ll
), "%2.2s.", lang
->language
);
573 ll_CC_len
= strlen(ll_CC
);
576 DEBUG_printf("2_ppdOpen: Loading localizations matching \"%s\" and \"%s\"", ll_CC
, ll
);
580 * Grab the first line and make sure it reads '*PPD-Adobe: "major.minor"'...
586 mask
= ppd_read(fp
, &line
, keyword
, name
, text
, &string
, 0, pg
);
588 DEBUG_printf("2_ppdOpen: mask=%x, keyword=\"%s\"...", mask
, keyword
);
591 strcmp(keyword
, "PPD-Adobe") ||
592 string
== NULL
|| string
[0] != '4')
595 * Either this is not a PPD file, or it is not a 4.x PPD file.
598 if (pg
->ppd_status
== PPD_OK
)
599 pg
->ppd_status
= PPD_MISSING_PPDADOBE4
;
607 DEBUG_printf("2_ppdOpen: keyword=%s, string=%p", keyword
, (void *)string
);
610 * Allocate memory for the PPD file record...
613 if ((ppd
= calloc(1, sizeof(ppd_file_t
))) == NULL
)
615 pg
->ppd_status
= PPD_ALLOC_ERROR
;
626 ppd
->language_level
= 2;
627 ppd
->color_device
= 0;
628 ppd
->colorspace
= PPD_CS_N
;
629 ppd
->landscape
= -90;
630 ppd
->coptions
= cupsArrayNew((cups_array_func_t
)ppd_compare_coptions
, NULL
);
633 * Read lines from the PPD file and add them to the file record...
641 encoding
= CUPS_ISO8859_1
;
644 while ((mask
= ppd_read(fp
, &line
, keyword
, name
, text
, &string
, 1, pg
)) != 0)
646 DEBUG_printf("2_ppdOpen: mask=%x, keyword=\"%s\", name=\"%s\", text=\"%s\", string=%d chars...", mask
, keyword
, name
, text
, string
? (int)strlen(string
) : 0);
648 if (strncmp(keyword
, "Default", 7) && !string
&&
649 pg
->ppd_conform
!= PPD_CONFORM_RELAXED
)
652 * Need a string value!
655 pg
->ppd_status
= PPD_MISSING_VALUE
;
663 * Certain main keywords (as defined by the PPD spec) may be used
664 * without the usual OpenUI/CloseUI stuff. Presumably this is just
665 * so that Adobe wouldn't completely break compatibility with PPD
666 * files prior to v4.0 of the spec, but it is hopelessly
667 * inconsistent... Catch these main keywords and automatically
668 * create the corresponding option, as needed...
674 * Previous line was a UI keyword...
682 * If we are filtering out keyword localizations, see if this line needs to
686 if (localization
!= _PPD_LOCALIZATION_ALL
&&
687 (temp
= strchr(keyword
, '.')) != NULL
&&
688 ((temp
- keyword
) == 2 || (temp
- keyword
) == 5) &&
689 _cups_isalpha(keyword
[0]) &&
690 _cups_isalpha(keyword
[1]) &&
691 (keyword
[2] == '.' ||
692 (keyword
[2] == '_' && _cups_isalpha(keyword
[3]) &&
693 _cups_isalpha(keyword
[4]) && keyword
[5] == '.')))
695 if (localization
== _PPD_LOCALIZATION_NONE
||
696 (localization
== _PPD_LOCALIZATION_DEFAULT
&&
697 strncmp(ll_CC
, keyword
, ll_CC_len
) &&
698 strncmp(ll
, keyword
, ll_len
)))
700 DEBUG_printf("2_ppdOpen: Ignoring localization: \"%s\"\n", keyword
);
705 else if (localization
== _PPD_LOCALIZATION_ICC_PROFILES
)
708 * Only load localizations for the color profile related keywords...
712 i
< (int)(sizeof(color_keywords
) / sizeof(color_keywords
[0]));
715 if (!_cups_strcasecmp(temp
, color_keywords
[i
]))
719 if (i
>= (int)(sizeof(color_keywords
) / sizeof(color_keywords
[0])))
721 DEBUG_printf("2_ppdOpen: Ignoring localization: \"%s\"\n", keyword
);
729 if (option
== NULL
&&
730 (mask
& (PPD_KEYWORD
| PPD_OPTION
| PPD_STRING
)) ==
731 (PPD_KEYWORD
| PPD_OPTION
| PPD_STRING
))
733 for (i
= 0; i
< (int)(sizeof(ui_keywords
) / sizeof(ui_keywords
[0])); i
++)
734 if (!strcmp(keyword
, ui_keywords
[i
]))
737 if (i
< (int)(sizeof(ui_keywords
) / sizeof(ui_keywords
[0])))
740 * Create the option in the appropriate group...
745 DEBUG_printf("2_ppdOpen: FOUND ADOBE UI KEYWORD %s WITHOUT OPENUI!", keyword
);
749 if ((group
= ppd_get_group(ppd
, "General", _("General"), pg
,
753 DEBUG_printf("2_ppdOpen: Adding to group %s...", group
->text
);
754 option
= ppd_get_option(group
, keyword
);
758 option
= ppd_get_option(group
, keyword
);
762 pg
->ppd_status
= PPD_ALLOC_ERROR
;
768 * Now fill in the initial information for the option...
771 if (!strncmp(keyword
, "JCL", 3))
772 option
->section
= PPD_ORDER_JCL
;
774 option
->section
= PPD_ORDER_ANY
;
776 option
->order
= 10.0f
;
779 option
->ui
= PPD_UI_BOOLEAN
;
781 option
->ui
= PPD_UI_PICKONE
;
783 for (j
= 0; j
< ppd
->num_attrs
; j
++)
784 if (!strncmp(ppd
->attrs
[j
]->name
, "Default", 7) &&
785 !strcmp(ppd
->attrs
[j
]->name
+ 7, keyword
) &&
786 ppd
->attrs
[j
]->value
)
788 DEBUG_printf("2_ppdOpen: Setting Default%s to %s via attribute...", option
->keyword
, ppd
->attrs
[j
]->value
);
789 cupsCopyString(option
->defchoice
, ppd
->attrs
[j
]->value
,
790 sizeof(option
->defchoice
));
794 if (!strcmp(keyword
, "PageSize"))
795 cupsCopyString(option
->text
, _("Media Size"), sizeof(option
->text
));
796 else if (!strcmp(keyword
, "MediaType"))
797 cupsCopyString(option
->text
, _("Media Type"), sizeof(option
->text
));
798 else if (!strcmp(keyword
, "InputSlot"))
799 cupsCopyString(option
->text
, _("Media Source"), sizeof(option
->text
));
800 else if (!strcmp(keyword
, "ColorModel"))
801 cupsCopyString(option
->text
, _("Output Mode"), sizeof(option
->text
));
802 else if (!strcmp(keyword
, "Resolution"))
803 cupsCopyString(option
->text
, _("Resolution"), sizeof(option
->text
));
805 cupsCopyString(option
->text
, keyword
, sizeof(option
->text
));
809 if (!strcmp(keyword
, "LanguageLevel"))
810 ppd
->language_level
= atoi(string
);
811 else if (!strcmp(keyword
, "LanguageEncoding"))
814 * Say all PPD files are UTF-8, since we convert to UTF-8...
817 ppd
->lang_encoding
= strdup("UTF-8");
818 encoding
= _ppdGetEncoding(string
);
820 else if (!strcmp(keyword
, "LanguageVersion"))
821 ppd
->lang_version
= string
;
822 else if (!strcmp(keyword
, "Manufacturer"))
823 ppd
->manufacturer
= string
;
824 else if (!strcmp(keyword
, "ModelName"))
825 ppd
->modelname
= string
;
826 else if (!strcmp(keyword
, "Protocols"))
827 ppd
->protocols
= string
;
828 else if (!strcmp(keyword
, "PCFileName"))
829 ppd
->pcfilename
= string
;
830 else if (!strcmp(keyword
, "NickName"))
832 if (encoding
!= CUPS_UTF8
)
834 cups_utf8_t utf8
[256]; /* UTF-8 version of NickName */
837 cupsCharsetToUTF8(utf8
, string
, sizeof(utf8
), encoding
);
838 ppd
->nickname
= strdup((char *)utf8
);
841 ppd
->nickname
= strdup(string
);
843 else if (!strcmp(keyword
, "Product"))
844 ppd
->product
= string
;
845 else if (!strcmp(keyword
, "ShortNickName"))
846 ppd
->shortnickname
= string
;
847 else if (!strcmp(keyword
, "TTRasterizer"))
848 ppd
->ttrasterizer
= string
;
849 else if (!strcmp(keyword
, "JCLBegin"))
851 ppd
->jcl_begin
= strdup(string
);
852 ppd_decode(ppd
->jcl_begin
); /* Decode quoted string */
854 else if (!strcmp(keyword
, "JCLEnd"))
856 ppd
->jcl_end
= strdup(string
);
857 ppd_decode(ppd
->jcl_end
); /* Decode quoted string */
859 else if (!strcmp(keyword
, "JCLToPSInterpreter"))
861 ppd
->jcl_ps
= strdup(string
);
862 ppd_decode(ppd
->jcl_ps
); /* Decode quoted string */
864 else if (!strcmp(keyword
, "AccurateScreensSupport"))
865 ppd
->accurate_screens
= !strcasecmp(string
, "True");
866 else if (!strcmp(keyword
, "ColorDevice"))
867 ppd
->color_device
= !strcasecmp(string
, "True");
868 else if (!strcmp(keyword
, "ContoneOnly"))
869 ppd
->contone_only
= !strcasecmp(string
, "True");
870 else if (!strcmp(keyword
, "cupsFlipDuplex"))
871 ppd
->flip_duplex
= !strcasecmp(string
, "True");
872 else if (!strcmp(keyword
, "cupsManualCopies"))
873 ppd
->manual_copies
= !strcasecmp(string
, "True");
874 else if (!strcmp(keyword
, "cupsModelNumber"))
875 ppd
->model_number
= atoi(string
);
876 else if (!strcmp(keyword
, "cupsColorProfile"))
878 if (ppd
->num_profiles
== 0)
879 profile
= malloc(sizeof(ppd_profile_t
));
881 profile
= realloc(ppd
->profiles
, sizeof(ppd_profile_t
) * (size_t)(ppd
->num_profiles
+ 1));
885 pg
->ppd_status
= PPD_ALLOC_ERROR
;
890 ppd
->profiles
= profile
;
891 profile
+= ppd
->num_profiles
;
892 ppd
->num_profiles
++;
894 memset(profile
, 0, sizeof(ppd_profile_t
));
895 cupsCopyString(profile
->resolution
, name
, sizeof(profile
->resolution
));
896 cupsCopyString(profile
->media_type
, text
, sizeof(profile
->media_type
));
898 profile
->density
= (float)_cupsStrScand(string
, &sptr
, loc
);
899 profile
->gamma
= (float)_cupsStrScand(sptr
, &sptr
, loc
);
900 profile
->matrix
[0][0] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
901 profile
->matrix
[0][1] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
902 profile
->matrix
[0][2] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
903 profile
->matrix
[1][0] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
904 profile
->matrix
[1][1] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
905 profile
->matrix
[1][2] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
906 profile
->matrix
[2][0] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
907 profile
->matrix
[2][1] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
908 profile
->matrix
[2][2] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
910 else if (!strcmp(keyword
, "cupsFilter"))
912 if (ppd
->num_filters
== 0)
913 filter
= malloc(sizeof(char *));
915 filter
= realloc(ppd
->filters
, sizeof(char *) * (size_t)(ppd
->num_filters
+ 1));
919 pg
->ppd_status
= PPD_ALLOC_ERROR
;
924 ppd
->filters
= filter
;
925 filter
+= ppd
->num_filters
;
929 * Make a copy of the filter string...
932 *filter
= strdup(string
);
934 else if (!strcmp(keyword
, "Throughput"))
935 ppd
->throughput
= atoi(string
);
936 else if (!strcmp(keyword
, "Font"))
939 * Add this font to the list of available fonts...
942 if (ppd
->num_fonts
== 0)
943 tempfonts
= (char **)malloc(sizeof(char *));
945 tempfonts
= (char **)realloc(ppd
->fonts
, sizeof(char *) * (size_t)(ppd
->num_fonts
+ 1));
947 if (tempfonts
== NULL
)
949 pg
->ppd_status
= PPD_ALLOC_ERROR
;
954 ppd
->fonts
= tempfonts
;
955 ppd
->fonts
[ppd
->num_fonts
] = strdup(name
);
958 else if (!strncmp(keyword
, "ParamCustom", 11))
960 ppd_coption_t
*coption
; /* Custom option */
961 ppd_cparam_t
*cparam
; /* Custom parameter */
962 int corder
; /* Order number */
963 char ctype
[33], /* Data type */
964 cminimum
[65], /* Minimum value */
965 cmaximum
[65]; /* Maximum value */
969 * Get the custom option and parameter...
972 if ((coption
= ppd_get_coption(ppd
, keyword
+ 11)) == NULL
)
974 pg
->ppd_status
= PPD_ALLOC_ERROR
;
979 if ((cparam
= ppd_get_cparam(coption
, name
, text
)) == NULL
)
981 pg
->ppd_status
= PPD_ALLOC_ERROR
;
986 if (cparam
->type
!= PPD_CUSTOM_UNKNOWN
)
988 pg
->ppd_status
= PPD_BAD_CUSTOM_PARAM
;
994 * Get the parameter data...
998 sscanf(string
, "%d%32s%64s%64s", &corder
, ctype
, cminimum
,
1001 pg
->ppd_status
= PPD_BAD_CUSTOM_PARAM
;
1006 cparam
->order
= corder
;
1008 if (!strcmp(ctype
, "curve"))
1010 cparam
->type
= PPD_CUSTOM_CURVE
;
1011 cparam
->minimum
.custom_curve
= (float)_cupsStrScand(cminimum
, NULL
, loc
);
1012 cparam
->maximum
.custom_curve
= (float)_cupsStrScand(cmaximum
, NULL
, loc
);
1014 else if (!strcmp(ctype
, "int"))
1016 cparam
->type
= PPD_CUSTOM_INT
;
1017 cparam
->minimum
.custom_int
= atoi(cminimum
);
1018 cparam
->maximum
.custom_int
= atoi(cmaximum
);
1020 else if (!strcmp(ctype
, "invcurve"))
1022 cparam
->type
= PPD_CUSTOM_INVCURVE
;
1023 cparam
->minimum
.custom_invcurve
= (float)_cupsStrScand(cminimum
, NULL
, loc
);
1024 cparam
->maximum
.custom_invcurve
= (float)_cupsStrScand(cmaximum
, NULL
, loc
);
1026 else if (!strcmp(ctype
, "passcode"))
1028 cparam
->type
= PPD_CUSTOM_PASSCODE
;
1029 cparam
->minimum
.custom_passcode
= atoi(cminimum
);
1030 cparam
->maximum
.custom_passcode
= atoi(cmaximum
);
1032 else if (!strcmp(ctype
, "password"))
1034 cparam
->type
= PPD_CUSTOM_PASSWORD
;
1035 cparam
->minimum
.custom_password
= atoi(cminimum
);
1036 cparam
->maximum
.custom_password
= atoi(cmaximum
);
1038 else if (!strcmp(ctype
, "points"))
1040 cparam
->type
= PPD_CUSTOM_POINTS
;
1041 cparam
->minimum
.custom_points
= (float)_cupsStrScand(cminimum
, NULL
, loc
);
1042 cparam
->maximum
.custom_points
= (float)_cupsStrScand(cmaximum
, NULL
, loc
);
1044 else if (!strcmp(ctype
, "real"))
1046 cparam
->type
= PPD_CUSTOM_REAL
;
1047 cparam
->minimum
.custom_real
= (float)_cupsStrScand(cminimum
, NULL
, loc
);
1048 cparam
->maximum
.custom_real
= (float)_cupsStrScand(cmaximum
, NULL
, loc
);
1050 else if (!strcmp(ctype
, "string"))
1052 cparam
->type
= PPD_CUSTOM_STRING
;
1053 cparam
->minimum
.custom_string
= atoi(cminimum
);
1054 cparam
->maximum
.custom_string
= atoi(cmaximum
);
1058 pg
->ppd_status
= PPD_BAD_CUSTOM_PARAM
;
1064 * Now special-case for CustomPageSize...
1067 if (!strcmp(coption
->keyword
, "PageSize"))
1069 if (!strcmp(name
, "Width"))
1071 ppd
->custom_min
[0] = cparam
->minimum
.custom_points
;
1072 ppd
->custom_max
[0] = cparam
->maximum
.custom_points
;
1074 else if (!strcmp(name
, "Height"))
1076 ppd
->custom_min
[1] = cparam
->minimum
.custom_points
;
1077 ppd
->custom_max
[1] = cparam
->maximum
.custom_points
;
1081 else if (!strcmp(keyword
, "HWMargins"))
1083 for (i
= 0, sptr
= string
; i
< 4; i
++)
1084 ppd
->custom_margins
[i
] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
1086 else if (!strncmp(keyword
, "Custom", 6) && !strcmp(name
, "True") && !option
)
1088 ppd_option_t
*custom_option
; /* Custom option */
1090 DEBUG_puts("2_ppdOpen: Processing Custom option...");
1093 * Get the option and custom option...
1096 if (!ppd_get_coption(ppd
, keyword
+ 6))
1098 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1103 if (option
&& !_cups_strcasecmp(option
->keyword
, keyword
+ 6))
1104 custom_option
= option
;
1106 custom_option
= ppdFindOption(ppd
, keyword
+ 6);
1111 * Add the "custom" option...
1114 if ((choice
= ppdFindChoice(custom_option
, "Custom")) == NULL
)
1115 if ((choice
= ppd_add_choice(custom_option
, "Custom")) == NULL
)
1117 DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
1119 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1124 cupsCopyString(choice
->text
, text
[0] ? text
: _("Custom"),
1125 sizeof(choice
->text
));
1127 choice
->code
= strdup(string
);
1129 if (custom_option
->section
== PPD_ORDER_JCL
)
1130 ppd_decode(choice
->code
);
1134 * Now process custom page sizes specially...
1137 if (!strcmp(keyword
, "CustomPageSize"))
1140 * Add a "Custom" page size entry...
1143 ppd
->variable_sizes
= 1;
1145 ppd_add_size(ppd
, "Custom");
1147 if (option
&& !_cups_strcasecmp(option
->keyword
, "PageRegion"))
1148 custom_option
= option
;
1150 custom_option
= ppdFindOption(ppd
, "PageRegion");
1154 if ((choice
= ppdFindChoice(custom_option
, "Custom")) == NULL
)
1155 if ((choice
= ppd_add_choice(custom_option
, "Custom")) == NULL
)
1157 DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
1159 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1164 cupsCopyString(choice
->text
, text
[0] ? text
: _("Custom"),
1165 sizeof(choice
->text
));
1169 else if (!strcmp(keyword
, "LandscapeOrientation"))
1171 if (!strcmp(string
, "Minus90"))
1172 ppd
->landscape
= -90;
1173 else if (!strcmp(string
, "Plus90"))
1174 ppd
->landscape
= 90;
1176 else if (!strcmp(keyword
, "Emulators") && string
&& ppd
->num_emulations
== 0)
1179 * Issue #5562: Samsung printer drivers incorrectly use Emulators keyword
1180 * to configure themselves
1182 * The Emulators keyword was loaded but never used by anything in CUPS,
1183 * and has no valid purpose in CUPS. The old code was removed due to a
1184 * memory leak (Issue #5475), so the following (new) code supports a single
1185 * name for the Emulators keyword, allowing these drivers to work until we
1186 * remove PPD and driver support entirely in a future version of CUPS.
1189 ppd
->num_emulations
= 1;
1190 ppd
->emulations
= calloc(1, sizeof(ppd_emul_t
));
1192 cupsCopyString(ppd
->emulations
[0].name
, string
, sizeof(ppd
->emulations
[0].name
));
1194 else if (!strcmp(keyword
, "JobPatchFile"))
1197 * CUPS STR #3421: Check for "*JobPatchFile: int: string"
1200 if (isdigit(*string
& 255))
1202 for (sptr
= string
+ 1; isdigit(*sptr
& 255); sptr
++);
1207 * Found "*JobPatchFile: int: string"...
1210 pg
->ppd_status
= PPD_BAD_VALUE
;
1216 if (!name
[0] && pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1219 * Found "*JobPatchFile: string"...
1222 pg
->ppd_status
= PPD_MISSING_OPTION_KEYWORD
;
1227 if (ppd
->patches
== NULL
)
1228 ppd
->patches
= strdup(string
);
1231 temp
= realloc(ppd
->patches
, strlen(ppd
->patches
) +
1232 strlen(string
) + 1);
1235 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1240 ppd
->patches
= temp
;
1242 memcpy(ppd
->patches
+ strlen(ppd
->patches
), string
, strlen(string
) + 1);
1245 else if (!strcmp(keyword
, "OpenUI"))
1248 * Don't allow nesting of options...
1251 if (option
&& pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1253 pg
->ppd_status
= PPD_NESTED_OPEN_UI
;
1259 * Add an option record to the current sub-group, group, or file...
1262 DEBUG_printf("2_ppdOpen: name=\"%s\" (%d)", name
, (int)strlen(name
));
1265 _cups_strcpy(name
, name
+ 1); /* Eliminate leading asterisk */
1267 for (i
= (int)strlen(name
) - 1; i
> 0 && _cups_isspace(name
[i
]); i
--)
1268 name
[i
] = '\0'; /* Eliminate trailing spaces */
1270 DEBUG_printf("2_ppdOpen: OpenUI of %s in group %s...", name
, group
? group
->text
: "(null)");
1272 if (subgroup
!= NULL
)
1273 option
= ppd_get_option(subgroup
, name
);
1274 else if (group
== NULL
)
1276 if ((group
= ppd_get_group(ppd
, "General", _("General"), pg
,
1280 DEBUG_printf("2_ppdOpen: Adding to group %s...", group
->text
);
1281 option
= ppd_get_option(group
, name
);
1285 option
= ppd_get_option(group
, name
);
1289 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1295 * Now fill in the initial information for the option...
1298 if (string
&& !strcmp(string
, "PickMany"))
1299 option
->ui
= PPD_UI_PICKMANY
;
1300 else if (string
&& !strcmp(string
, "Boolean"))
1301 option
->ui
= PPD_UI_BOOLEAN
;
1302 else if (string
&& !strcmp(string
, "PickOne"))
1303 option
->ui
= PPD_UI_PICKONE
;
1304 else if (pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1306 pg
->ppd_status
= PPD_BAD_OPEN_UI
;
1311 option
->ui
= PPD_UI_PICKONE
;
1313 for (j
= 0; j
< ppd
->num_attrs
; j
++)
1314 if (!strncmp(ppd
->attrs
[j
]->name
, "Default", 7) &&
1315 !strcmp(ppd
->attrs
[j
]->name
+ 7, name
) &&
1316 ppd
->attrs
[j
]->value
)
1318 DEBUG_printf("2_ppdOpen: Setting Default%s to %s via attribute...", option
->keyword
, ppd
->attrs
[j
]->value
);
1319 cupsCopyString(option
->defchoice
, ppd
->attrs
[j
]->value
,
1320 sizeof(option
->defchoice
));
1325 cupsCharsetToUTF8((cups_utf8_t
*)option
->text
, text
,
1326 sizeof(option
->text
), encoding
);
1329 if (!strcmp(name
, "PageSize"))
1330 cupsCopyString(option
->text
, _("Media Size"), sizeof(option
->text
));
1331 else if (!strcmp(name
, "MediaType"))
1332 cupsCopyString(option
->text
, _("Media Type"), sizeof(option
->text
));
1333 else if (!strcmp(name
, "InputSlot"))
1334 cupsCopyString(option
->text
, _("Media Source"), sizeof(option
->text
));
1335 else if (!strcmp(name
, "ColorModel"))
1336 cupsCopyString(option
->text
, _("Output Mode"), sizeof(option
->text
));
1337 else if (!strcmp(name
, "Resolution"))
1338 cupsCopyString(option
->text
, _("Resolution"), sizeof(option
->text
));
1340 cupsCopyString(option
->text
, name
, sizeof(option
->text
));
1343 option
->section
= PPD_ORDER_ANY
;
1349 * Add a custom option choice if we have already seen a CustomFoo
1353 if (!_cups_strcasecmp(name
, "PageRegion"))
1354 cupsCopyString(custom_name
, "CustomPageSize", sizeof(custom_name
));
1356 snprintf(custom_name
, sizeof(custom_name
), "Custom%s", name
);
1358 if ((custom_attr
= ppdFindAttr(ppd
, custom_name
, "True")) != NULL
)
1360 if ((choice
= ppdFindChoice(option
, "Custom")) == NULL
)
1361 if ((choice
= ppd_add_choice(option
, "Custom")) == NULL
)
1363 DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
1365 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1370 cupsCopyString(choice
->text
,
1371 custom_attr
->text
[0] ? custom_attr
->text
: _("Custom"),
1372 sizeof(choice
->text
));
1373 choice
->code
= strdup(custom_attr
->value
);
1376 else if (!strcmp(keyword
, "JCLOpenUI"))
1379 * Don't allow nesting of options...
1382 if (option
&& pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1384 pg
->ppd_status
= PPD_NESTED_OPEN_UI
;
1390 * Find the JCL group, and add if needed...
1393 group
= ppd_get_group(ppd
, "JCL", _("JCL"), pg
, encoding
);
1399 * Add an option record to the current JCLs...
1403 _cups_strcpy(name
, name
+ 1);
1405 option
= ppd_get_option(group
, name
);
1409 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1415 * Now fill in the initial information for the option...
1418 if (string
&& !strcmp(string
, "PickMany"))
1419 option
->ui
= PPD_UI_PICKMANY
;
1420 else if (string
&& !strcmp(string
, "Boolean"))
1421 option
->ui
= PPD_UI_BOOLEAN
;
1422 else if (string
&& !strcmp(string
, "PickOne"))
1423 option
->ui
= PPD_UI_PICKONE
;
1426 pg
->ppd_status
= PPD_BAD_OPEN_UI
;
1431 for (j
= 0; j
< ppd
->num_attrs
; j
++)
1432 if (!strncmp(ppd
->attrs
[j
]->name
, "Default", 7) &&
1433 !strcmp(ppd
->attrs
[j
]->name
+ 7, name
) &&
1434 ppd
->attrs
[j
]->value
)
1436 DEBUG_printf("2_ppdOpen: Setting Default%s to %s via attribute...", option
->keyword
, ppd
->attrs
[j
]->value
);
1437 cupsCopyString(option
->defchoice
, ppd
->attrs
[j
]->value
,
1438 sizeof(option
->defchoice
));
1443 cupsCharsetToUTF8((cups_utf8_t
*)option
->text
, text
,
1444 sizeof(option
->text
), encoding
);
1446 cupsCopyString(option
->text
, name
, sizeof(option
->text
));
1448 option
->section
= PPD_ORDER_JCL
;
1455 * Add a custom option choice if we have already seen a CustomFoo
1459 snprintf(custom_name
, sizeof(custom_name
), "Custom%s", name
);
1461 if ((custom_attr
= ppdFindAttr(ppd
, custom_name
, "True")) != NULL
)
1463 if ((choice
= ppd_add_choice(option
, "Custom")) == NULL
)
1465 DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
1467 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1472 cupsCopyString(choice
->text
,
1473 custom_attr
->text
[0] ? custom_attr
->text
: _("Custom"),
1474 sizeof(choice
->text
));
1475 choice
->code
= strdup(custom_attr
->value
);
1478 else if (!strcmp(keyword
, "CloseUI"))
1480 if ((!option
|| option
->section
== PPD_ORDER_JCL
) && pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1482 pg
->ppd_status
= PPD_BAD_CLOSE_UI
;
1487 if (option
&& (!_cups_strcasecmp(option
->defchoice
, "custom") || !_cups_strncasecmp(option
->defchoice
, "custom.", 7)))
1490 * "*DefaultOption: Custom..." may set the default to a custom value
1491 * or (for a very small number of incompatible PPD files) select a
1492 * standard choice for the option, which CUPS renames to "_Custom..."
1493 * to avoid compatibility issues. See which this is...
1496 char tchoice
[PPD_MAX_NAME
]; /* Temporary choice name */
1498 snprintf(tchoice
, sizeof(tchoice
), "_%s", option
->defchoice
);
1500 if (ppdFindChoice(option
, tchoice
))
1502 cupsCopyString(option
->defchoice
, tchoice
, sizeof(option
->defchoice
));
1504 DEBUG_printf("2_ppdOpen: Reset Default%s to %s...", option
->keyword
, tchoice
);
1513 else if (!strcmp(keyword
, "JCLCloseUI"))
1515 if ((!option
|| option
->section
!= PPD_ORDER_JCL
) && pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1517 pg
->ppd_status
= PPD_BAD_CLOSE_UI
;
1522 if (option
&& (!_cups_strcasecmp(option
->defchoice
, "custom") || !_cups_strncasecmp(option
->defchoice
, "custom.", 7)))
1525 * "*DefaultOption: Custom..." may set the default to a custom value
1526 * or (for a very small number of incompatible PPD files) select a
1527 * standard choice for the option, which CUPS renames to "_Custom..."
1528 * to avoid compatibility issues. See which this is...
1531 char tchoice
[PPD_MAX_NAME
]; /* Temporary choice name */
1533 snprintf(tchoice
, sizeof(tchoice
), "_%s", option
->defchoice
);
1535 if (ppdFindChoice(option
, tchoice
))
1537 cupsCopyString(option
->defchoice
, tchoice
, sizeof(option
->defchoice
));
1539 DEBUG_printf("2_ppdOpen: Reset Default%s to %s...", option
->keyword
, tchoice
);
1548 else if (!strcmp(keyword
, "OpenGroup"))
1551 * Open a new group...
1556 pg
->ppd_status
= PPD_NESTED_OPEN_GROUP
;
1563 pg
->ppd_status
= PPD_BAD_OPEN_GROUP
;
1569 * Separate the group name from the text (name/text)...
1572 if ((sptr
= strchr(string
, '/')) != NULL
)
1578 * Fix up the text...
1584 * Find/add the group...
1587 group
= ppd_get_group(ppd
, string
, sptr
, pg
, encoding
);
1595 else if (!strcmp(keyword
, "CloseGroup"))
1602 else if (!strcmp(keyword
, "OrderDependency"))
1604 order
= (float)_cupsStrScand(string
, &sptr
, loc
);
1606 if (!sptr
|| sscanf(sptr
, "%40s%40s", name
, keyword
) != 2)
1608 pg
->ppd_status
= PPD_BAD_ORDER_DEPENDENCY
;
1613 if (keyword
[0] == '*')
1614 _cups_strcpy(keyword
, keyword
+ 1);
1616 if (!strcmp(name
, "ExitServer"))
1617 section
= PPD_ORDER_EXIT
;
1618 else if (!strcmp(name
, "Prolog"))
1619 section
= PPD_ORDER_PROLOG
;
1620 else if (!strcmp(name
, "DocumentSetup"))
1621 section
= PPD_ORDER_DOCUMENT
;
1622 else if (!strcmp(name
, "PageSetup"))
1623 section
= PPD_ORDER_PAGE
;
1624 else if (!strcmp(name
, "JCLSetup"))
1625 section
= PPD_ORDER_JCL
;
1627 section
= PPD_ORDER_ANY
;
1635 * Only valid for Non-UI options...
1638 for (i
= ppd
->num_groups
, gtemp
= ppd
->groups
; i
> 0; i
--, gtemp
++)
1639 if (gtemp
->text
[0] == '\0')
1643 for (i
= 0; i
< gtemp
->num_options
; i
++)
1644 if (!strcmp(keyword
, gtemp
->options
[i
].keyword
))
1646 gtemp
->options
[i
].section
= section
;
1647 gtemp
->options
[i
].order
= order
;
1653 option
->section
= section
;
1654 option
->order
= order
;
1660 else if (!strncmp(keyword
, "Default", 7))
1666 * Drop UI text, if any, from value...
1669 if (strchr(string
, '/') != NULL
)
1670 *strchr(string
, '/') = '\0';
1673 * Assign the default value as appropriate...
1676 if (!strcmp(keyword
, "DefaultColorSpace"))
1679 * Set default colorspace...
1682 if (!strcmp(string
, "CMY"))
1683 ppd
->colorspace
= PPD_CS_CMY
;
1684 else if (!strcmp(string
, "CMYK"))
1685 ppd
->colorspace
= PPD_CS_CMYK
;
1686 else if (!strcmp(string
, "RGB"))
1687 ppd
->colorspace
= PPD_CS_RGB
;
1688 else if (!strcmp(string
, "RGBK"))
1689 ppd
->colorspace
= PPD_CS_RGBK
;
1690 else if (!strcmp(string
, "N"))
1691 ppd
->colorspace
= PPD_CS_N
;
1693 ppd
->colorspace
= PPD_CS_GRAY
;
1695 else if (option
&& !strcmp(keyword
+ 7, option
->keyword
))
1698 * Set the default as part of the current option...
1701 cupsCopyString(option
->defchoice
, string
, sizeof(option
->defchoice
));
1703 DEBUG_printf("2_ppdOpen: Set %s to %s...", keyword
, option
->defchoice
);
1708 * Lookup option and set if it has been defined...
1711 ppd_option_t
*toption
; /* Temporary option */
1713 if ((toption
= ppdFindOption(ppd
, keyword
+ 7)) != NULL
)
1715 if (!_cups_strcasecmp(string
, "custom") || !_cups_strncasecmp(string
, "custom.", 7))
1718 * "*DefaultOption: Custom..." may set the default to a custom value
1719 * or (for a very small number of incompatible PPD files) select a
1720 * standard choice for the option, which CUPS renames to "_Custom..."
1721 * to avoid compatibility issues. See which this is...
1724 snprintf(toption
->defchoice
, sizeof(toption
->defchoice
), "_%s", string
);
1725 if (!ppdFindChoice(toption
, toption
->defchoice
))
1726 cupsCopyString(toption
->defchoice
, string
, sizeof(toption
->defchoice
));
1730 cupsCopyString(toption
->defchoice
, string
, sizeof(toption
->defchoice
));
1733 DEBUG_printf("2_ppdOpen: Set %s to %s...", keyword
, toption
->defchoice
);
1737 else if (!strcmp(keyword
, "UIConstraints") ||
1738 !strcmp(keyword
, "NonUIConstraints"))
1742 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1746 if (ppd
->num_consts
== 0)
1747 constraint
= calloc(2, sizeof(ppd_const_t
));
1749 constraint
= realloc(ppd
->consts
, (size_t)(ppd
->num_consts
+ 2) * sizeof(ppd_const_t
));
1751 if (constraint
== NULL
)
1753 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1758 ppd
->consts
= constraint
;
1759 constraint
+= ppd
->num_consts
;
1762 switch (sscanf(string
, "%40s%40s%40s%40s", constraint
->option1
,
1763 constraint
->choice1
, constraint
->option2
,
1764 constraint
->choice2
))
1766 default : /* Error */
1767 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1770 case 2 : /* Two options... */
1772 * Check for broken constraints like "* Option"...
1775 if (pg
->ppd_conform
== PPD_CONFORM_STRICT
&&
1776 (!strcmp(constraint
->option1
, "*") ||
1777 !strcmp(constraint
->choice1
, "*")))
1779 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1784 * The following strcpy's are safe, as optionN and
1785 * choiceN are all the same size (size defined by PPD spec...)
1788 if (constraint
->option1
[0] == '*')
1789 _cups_strcpy(constraint
->option1
, constraint
->option1
+ 1);
1790 else if (pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1792 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1796 if (constraint
->choice1
[0] == '*')
1797 _cups_strcpy(constraint
->option2
, constraint
->choice1
+ 1);
1798 else if (pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1800 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1804 constraint
->choice1
[0] = '\0';
1805 constraint
->choice2
[0] = '\0';
1808 case 3 : /* Two options, one choice... */
1810 * Check for broken constraints like "* Option"...
1813 if (pg
->ppd_conform
== PPD_CONFORM_STRICT
&&
1814 (!strcmp(constraint
->option1
, "*") ||
1815 !strcmp(constraint
->choice1
, "*") ||
1816 !strcmp(constraint
->option2
, "*")))
1818 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1823 * The following _cups_strcpy's are safe, as optionN and
1824 * choiceN are all the same size (size defined by PPD spec...)
1827 if (constraint
->option1
[0] == '*')
1828 _cups_strcpy(constraint
->option1
, constraint
->option1
+ 1);
1829 else if (pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1831 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1835 if (constraint
->choice1
[0] == '*')
1837 if (pg
->ppd_conform
== PPD_CONFORM_STRICT
&&
1838 constraint
->option2
[0] == '*')
1840 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1844 _cups_strcpy(constraint
->choice2
, constraint
->option2
);
1845 _cups_strcpy(constraint
->option2
, constraint
->choice1
+ 1);
1846 constraint
->choice1
[0] = '\0';
1850 if (constraint
->option2
[0] == '*')
1851 _cups_strcpy(constraint
->option2
, constraint
->option2
+ 1);
1852 else if (pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1854 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1858 constraint
->choice2
[0] = '\0';
1862 case 4 : /* Two options, two choices... */
1864 * Check for broken constraints like "* Option"...
1867 if (pg
->ppd_conform
== PPD_CONFORM_STRICT
&&
1868 (!strcmp(constraint
->option1
, "*") ||
1869 !strcmp(constraint
->choice1
, "*") ||
1870 !strcmp(constraint
->option2
, "*") ||
1871 !strcmp(constraint
->choice2
, "*")))
1873 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1877 if (constraint
->option1
[0] == '*')
1878 _cups_strcpy(constraint
->option1
, constraint
->option1
+ 1);
1879 else if (pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1881 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1885 if (pg
->ppd_conform
== PPD_CONFORM_STRICT
&&
1886 constraint
->choice1
[0] == '*')
1888 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1892 if (constraint
->option2
[0] == '*')
1893 _cups_strcpy(constraint
->option2
, constraint
->option2
+ 1);
1894 else if (pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1896 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1900 if (pg
->ppd_conform
== PPD_CONFORM_STRICT
&&
1901 constraint
->choice2
[0] == '*')
1903 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1910 * Don't add this one as an attribute...
1916 else if (!strcmp(keyword
, "PaperDimension"))
1918 if (!_cups_strcasecmp(name
, "custom") || !_cups_strncasecmp(name
, "custom.", 7))
1920 char cname
[PPD_MAX_NAME
]; /* Rewrite with a leading underscore */
1921 snprintf(cname
, sizeof(cname
), "_%s", name
);
1922 cupsCopyString(name
, cname
, sizeof(name
));
1925 if ((size
= ppdPageSize(ppd
, name
)) == NULL
)
1926 size
= ppd_add_size(ppd
, name
);
1931 * Unable to add or find size!
1934 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1939 size
->width
= (float)_cupsStrScand(string
, &sptr
, loc
);
1940 size
->length
= (float)_cupsStrScand(sptr
, NULL
, loc
);
1945 else if (!strcmp(keyword
, "ImageableArea"))
1947 if (!_cups_strcasecmp(name
, "custom") || !_cups_strncasecmp(name
, "custom.", 7))
1949 char cname
[PPD_MAX_NAME
]; /* Rewrite with a leading underscore */
1950 snprintf(cname
, sizeof(cname
), "_%s", name
);
1951 cupsCopyString(name
, cname
, sizeof(name
));
1954 if ((size
= ppdPageSize(ppd
, name
)) == NULL
)
1955 size
= ppd_add_size(ppd
, name
);
1960 * Unable to add or find size!
1963 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1968 size
->left
= (float)_cupsStrScand(string
, &sptr
, loc
);
1969 size
->bottom
= (float)_cupsStrScand(sptr
, &sptr
, loc
);
1970 size
->right
= (float)_cupsStrScand(sptr
, &sptr
, loc
);
1971 size
->top
= (float)_cupsStrScand(sptr
, NULL
, loc
);
1976 else if (option
!= NULL
&&
1977 (mask
& (PPD_KEYWORD
| PPD_OPTION
| PPD_STRING
)) ==
1978 (PPD_KEYWORD
| PPD_OPTION
| PPD_STRING
) &&
1979 !strcmp(keyword
, option
->keyword
))
1981 DEBUG_printf("2_ppdOpen: group=%p, subgroup=%p", (void *)group
, (void *)subgroup
);
1983 if (!_cups_strcasecmp(name
, "custom") || !_cups_strncasecmp(name
, "custom.", 7))
1985 char cname
[PPD_MAX_NAME
]; /* Rewrite with a leading underscore */
1986 snprintf(cname
, sizeof(cname
), "_%s", name
);
1987 cupsCopyString(name
, cname
, sizeof(name
));
1990 if (!strcmp(keyword
, "PageSize"))
1993 * Add a page size...
1996 if (ppdPageSize(ppd
, name
) == NULL
)
1997 ppd_add_size(ppd
, name
);
2001 * Add the option choice...
2004 if ((choice
= ppd_add_choice(option
, name
)) == NULL
)
2006 pg
->ppd_status
= PPD_ALLOC_ERROR
;
2012 cupsCharsetToUTF8((cups_utf8_t
*)choice
->text
, text
,
2013 sizeof(choice
->text
), encoding
);
2014 else if (!strcmp(name
, "True"))
2015 cupsCopyString(choice
->text
, _("Yes"), sizeof(choice
->text
));
2016 else if (!strcmp(name
, "False"))
2017 cupsCopyString(choice
->text
, _("No"), sizeof(choice
->text
));
2019 cupsCopyString(choice
->text
, name
, sizeof(choice
->text
));
2021 if (option
->section
== PPD_ORDER_JCL
)
2022 ppd_decode(string
); /* Decode quoted string */
2024 choice
->code
= string
;
2025 string
= NULL
; /* Don't add as an attribute below */
2029 * Add remaining lines with keywords and string values as attributes...
2033 (mask
& (PPD_KEYWORD
| PPD_STRING
)) == (PPD_KEYWORD
| PPD_STRING
))
2034 ppd_add_attr(ppd
, keyword
, name
, text
, string
);
2040 * Check for a missing CloseUI/JCLCloseUI...
2043 if (option
&& pg
->ppd_conform
== PPD_CONFORM_STRICT
)
2045 pg
->ppd_status
= PPD_MISSING_CLOSE_UI
;
2050 * Check for a missing CloseGroup...
2053 if (group
&& pg
->ppd_conform
== PPD_CONFORM_STRICT
)
2055 pg
->ppd_status
= PPD_MISSING_CLOSE_GROUP
;
2062 * Reset language preferences...
2066 if (!cupsFileEOF(fp
))
2067 DEBUG_printf("1_ppdOpen: Premature EOF at %lu...\n", (unsigned long)cupsFileTell(fp
));
2070 if (pg
->ppd_status
!= PPD_OK
)
2073 * Had an error reading the PPD file, cannot continue!
2082 * Update the filters array as needed...
2085 if (!ppd_update_filters(ppd
, pg
))
2093 * Create the sorted options array and set the option back-pointer for
2094 * each choice and custom option...
2097 ppd
->options
= cupsArrayNew2((cups_array_func_t
)ppd_compare_options
, NULL
,
2098 (cups_ahash_func_t
)ppd_hash_option
,
2101 for (i
= ppd
->num_groups
, group
= ppd
->groups
;
2105 for (j
= group
->num_options
, option
= group
->options
;
2109 ppd_coption_t
*coption
; /* Custom option */
2112 cupsArrayAdd(ppd
->options
, option
);
2114 for (k
= 0; k
< option
->num_choices
; k
++)
2115 option
->choices
[k
].option
= option
;
2117 if ((coption
= ppdFindCustomOption(ppd
, option
->keyword
)) != NULL
)
2118 coption
->option
= option
;
2123 * Create an array to track the marked choices...
2126 ppd
->marked
= cupsArrayNew((cups_array_func_t
)ppd_compare_choices
, NULL
);
2129 * Return the PPD file structure...
2135 * Common exit point for errors to save code size...
2150 * 'ppdOpen()' - Read a PPD file into memory.
2153 ppd_file_t
* /* O - PPD file record */
2154 ppdOpen(FILE *fp
) /* I - File to read from */
2156 ppd_file_t
*ppd
; /* PPD file record */
2157 cups_file_t
*cf
; /* CUPS file */
2161 * Reopen the stdio file as a CUPS file...
2164 if ((cf
= cupsFileOpenFd(fileno(fp
), "r")) == NULL
)
2168 * Load the PPD file using the newer API...
2171 ppd
= _ppdOpen(cf
, _PPD_LOCALIZATION_DEFAULT
);
2174 * Close the CUPS file and return the PPD...
2184 * 'ppdOpen2()' - Read a PPD file into memory.
2189 ppd_file_t
* /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
2190 ppdOpen2(cups_file_t
*fp
) /* I - File to read from */
2192 return _ppdOpen(fp
, _PPD_LOCALIZATION_DEFAULT
);
2197 * 'ppdOpenFd()' - Read a PPD file into memory.
2200 ppd_file_t
* /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
2201 ppdOpenFd(int fd
) /* I - File to read from */
2203 cups_file_t
*fp
; /* CUPS 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...
2221 pg
->ppd_status
= PPD_NULL_FILE
;
2227 * Try to open the file and parse it...
2230 if ((fp
= cupsFileOpenFd(fd
, "r")) != NULL
)
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 */
2252 _ppd_localization_t localization
) /* I - Localization to load */
2254 cups_file_t
*fp
; /* File pointer */
2255 ppd_file_t
*ppd
; /* PPD file record */
2256 _ppd_globals_t
*pg
= _ppdGlobals();
2261 * Set the line number to 0...
2267 * Range check input...
2270 if (filename
== NULL
)
2272 pg
->ppd_status
= PPD_NULL_FILE
;
2278 * Try to open the file and parse it...
2281 if ((fp
= cupsFileOpen(filename
, "r")) != NULL
)
2283 ppd
= _ppdOpen(fp
, localization
);
2289 pg
->ppd_status
= PPD_FILE_OPEN_ERROR
;
2298 * 'ppdOpenFile()' - Read a PPD file into memory.
2301 ppd_file_t
* /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
2302 ppdOpenFile(const char *filename
) /* I - File to read from */
2304 return _ppdOpenFile(filename
, _PPD_LOCALIZATION_DEFAULT
);
2309 * 'ppdSetConformance()' - Set the conformance level for PPD files.
2311 * @since CUPS 1.1.20@
2315 ppdSetConformance(ppd_conform_t c
) /* I - Conformance level */
2317 _ppd_globals_t
*pg
= _ppdGlobals();
2321 pg
->ppd_conform
= c
;
2326 * 'ppd_add_attr()' - Add an attribute to the PPD data.
2329 static ppd_attr_t
* /* O - New attribute */
2330 ppd_add_attr(ppd_file_t
*ppd
, /* I - PPD file data */
2331 const char *name
, /* I - Attribute name */
2332 const char *spec
, /* I - Specifier string, if any */
2333 const char *text
, /* I - Text string, if any */
2334 const char *value
) /* I - Value of attribute */
2336 ppd_attr_t
**ptr
, /* New array */
2337 *temp
; /* New attribute */
2341 * Range check input...
2344 if (ppd
== NULL
|| name
== NULL
|| spec
== NULL
)
2348 * Create the array as needed...
2351 if (!ppd
->sorted_attrs
)
2352 ppd
->sorted_attrs
= cupsArrayNew((cups_array_func_t
)ppd_compare_attrs
,
2356 * Allocate memory for the new attribute...
2359 if (ppd
->num_attrs
== 0)
2360 ptr
= malloc(sizeof(ppd_attr_t
*));
2362 ptr
= realloc(ppd
->attrs
, (size_t)(ppd
->num_attrs
+ 1) * sizeof(ppd_attr_t
*));
2368 ptr
+= ppd
->num_attrs
;
2370 if ((temp
= calloc(1, sizeof(ppd_attr_t
))) == NULL
)
2381 if (!_cups_strcasecmp(spec
, "custom") || !_cups_strncasecmp(spec
, "custom.", 7))
2383 temp
->spec
[0] = '_';
2384 cupsCopyString(temp
->spec
+ 1, spec
, sizeof(temp
->spec
) - 1);
2387 cupsCopyString(temp
->spec
, spec
, sizeof(temp
->spec
));
2390 cupsCopyString(temp
->name
, name
, sizeof(temp
->name
));
2391 cupsCopyString(temp
->text
, text
, sizeof(temp
->text
));
2392 temp
->value
= (char *)value
;
2395 * Add the attribute to the sorted array...
2398 cupsArrayAdd(ppd
->sorted_attrs
, temp
);
2401 * Return the attribute...
2409 * 'ppd_add_choice()' - Add a choice to an option.
2412 static ppd_choice_t
* /* O - Named choice */
2413 ppd_add_choice(ppd_option_t
*option
, /* I - Option */
2414 const char *name
) /* I - Name of choice */
2416 ppd_choice_t
*choice
; /* Choice */
2419 if (option
->num_choices
== 0)
2420 choice
= malloc(sizeof(ppd_choice_t
));
2422 choice
= realloc(option
->choices
, sizeof(ppd_choice_t
) * (size_t)(option
->num_choices
+ 1));
2427 option
->choices
= choice
;
2428 choice
+= option
->num_choices
;
2429 option
->num_choices
++;
2431 memset(choice
, 0, sizeof(ppd_choice_t
));
2432 cupsCopyString(choice
->choice
, name
, sizeof(choice
->choice
));
2439 * 'ppd_add_size()' - Add a page size.
2442 static ppd_size_t
* /* O - Named size */
2443 ppd_add_size(ppd_file_t
*ppd
, /* I - PPD file */
2444 const char *name
) /* I - Name of size */
2446 ppd_size_t
*size
; /* Size */
2449 if (ppd
->num_sizes
== 0)
2450 size
= malloc(sizeof(ppd_size_t
));
2452 size
= realloc(ppd
->sizes
, sizeof(ppd_size_t
) * (size_t)(ppd
->num_sizes
+ 1));
2458 size
+= ppd
->num_sizes
;
2461 memset(size
, 0, sizeof(ppd_size_t
));
2462 cupsCopyString(size
->name
, name
, sizeof(size
->name
));
2469 * 'ppd_compare_attrs()' - Compare two attributes.
2472 static int /* O - Result of comparison */
2473 ppd_compare_attrs(ppd_attr_t
*a
, /* I - First attribute */
2474 ppd_attr_t
*b
, /* I - Second attribute */
2475 void *data
) /* Unused */
2478 return (_cups_strcasecmp(a
->name
, b
->name
));
2483 * 'ppd_compare_choices()' - Compare two choices...
2486 static int /* O - Result of comparison */
2487 ppd_compare_choices(ppd_choice_t
*a
, /* I - First choice */
2488 ppd_choice_t
*b
, /* I - Second choice */
2489 void *data
) /* Unused */
2492 return (strcmp(a
->option
->keyword
, b
->option
->keyword
));
2497 * 'ppd_compare_coptions()' - Compare two custom options.
2500 static int /* O - Result of comparison */
2501 ppd_compare_coptions(ppd_coption_t
*a
, /* I - First option */
2502 ppd_coption_t
*b
, /* I - Second option */
2503 void *data
) /* Unused */
2506 return (_cups_strcasecmp(a
->keyword
, b
->keyword
));
2511 * 'ppd_compare_options()' - Compare two options.
2514 static int /* O - Result of comparison */
2515 ppd_compare_options(ppd_option_t
*a
, /* I - First option */
2516 ppd_option_t
*b
, /* I - Second option */
2517 void *data
) /* Unused */
2520 return (_cups_strcasecmp(a
->keyword
, b
->keyword
));
2525 * 'ppd_decode()' - Decode a string value...
2528 static int /* O - Length of decoded string */
2529 ppd_decode(char *string
) /* I - String to decode */
2531 char *inptr
, /* Input pointer */
2532 *outptr
; /* Output pointer */
2538 while (*inptr
!= '\0')
2539 if (*inptr
== '<' && isxdigit(inptr
[1] & 255))
2542 * Convert hex to 8-bit values...
2546 while (isxdigit(*inptr
& 255))
2548 if (_cups_isalpha(*inptr
))
2549 *outptr
= (char)((tolower(*inptr
) - 'a' + 10) << 4);
2551 *outptr
= (char)((*inptr
- '0') << 4);
2555 if (!isxdigit(*inptr
& 255))
2558 if (_cups_isalpha(*inptr
))
2559 *outptr
|= (char)(tolower(*inptr
) - 'a' + 10);
2561 *outptr
|= (char)(*inptr
- '0');
2567 while (*inptr
!= '>' && *inptr
!= '\0')
2569 while (*inptr
== '>')
2573 *outptr
++ = *inptr
++;
2577 return ((int)(outptr
- string
));
2582 * 'ppd_free_filters()' - Free the filters array.
2586 ppd_free_filters(ppd_file_t
*ppd
) /* I - PPD file */
2588 int i
; /* Looping var */
2589 char **filter
; /* Current filter */
2592 if (ppd
->num_filters
> 0)
2594 for (i
= ppd
->num_filters
, filter
= ppd
->filters
; i
> 0; i
--, filter
++)
2599 ppd
->num_filters
= 0;
2600 ppd
->filters
= NULL
;
2606 * 'ppd_free_group()' - Free a single UI group.
2610 ppd_free_group(ppd_group_t
*group
) /* I - Group to free */
2612 int i
; /* Looping var */
2613 ppd_option_t
*option
; /* Current option */
2614 ppd_group_t
*subgroup
; /* Current sub-group */
2617 if (group
->num_options
> 0)
2619 for (i
= group
->num_options
, option
= group
->options
;
2622 ppd_free_option(option
);
2624 free(group
->options
);
2627 if (group
->num_subgroups
> 0)
2629 for (i
= group
->num_subgroups
, subgroup
= group
->subgroups
;
2632 ppd_free_group(subgroup
);
2634 free(group
->subgroups
);
2640 * 'ppd_free_option()' - Free a single option.
2644 ppd_free_option(ppd_option_t
*option
) /* I - Option to free */
2646 int i
; /* Looping var */
2647 ppd_choice_t
*choice
; /* Current choice */
2650 if (option
->num_choices
> 0)
2652 for (i
= option
->num_choices
, choice
= option
->choices
;
2659 free(option
->choices
);
2665 * 'ppd_get_coption()' - Get a custom option record.
2668 static ppd_coption_t
* /* O - Custom option... */
2669 ppd_get_coption(ppd_file_t
*ppd
, /* I - PPD file */
2670 const char *name
) /* I - Name of option */
2672 ppd_coption_t
*copt
; /* New custom option */
2676 * See if the option already exists...
2679 if ((copt
= ppdFindCustomOption(ppd
, name
)) != NULL
)
2683 * Not found, so create the custom option record...
2686 if ((copt
= calloc(1, sizeof(ppd_coption_t
))) == NULL
)
2689 cupsCopyString(copt
->keyword
, name
, sizeof(copt
->keyword
));
2691 copt
->params
= cupsArrayNew((cups_array_func_t
)NULL
, NULL
);
2693 cupsArrayAdd(ppd
->coptions
, copt
);
2696 * Return the new record...
2704 * 'ppd_get_cparam()' - Get a custom parameter record.
2707 static ppd_cparam_t
* /* O - Extended option... */
2708 ppd_get_cparam(ppd_coption_t
*opt
, /* I - PPD file */
2709 const char *param
, /* I - Name of parameter */
2710 const char *text
) /* I - Human-readable text */
2712 ppd_cparam_t
*cparam
; /* New custom parameter */
2716 * See if the parameter already exists...
2719 if ((cparam
= ppdFindCustomParam(opt
, param
)) != NULL
)
2723 * Not found, so create the custom parameter record...
2726 if ((cparam
= calloc(1, sizeof(ppd_cparam_t
))) == NULL
)
2729 cparam
->type
= PPD_CUSTOM_UNKNOWN
;
2730 cupsCopyString(cparam
->name
, param
, sizeof(cparam
->name
));
2731 cupsCopyString(cparam
->text
, text
[0] ? text
: param
, sizeof(cparam
->text
));
2734 * Add this record to the array...
2737 cupsArrayAdd(opt
->params
, cparam
);
2740 * Return the new record...
2748 * 'ppd_get_group()' - Find or create the named group as needed.
2751 static ppd_group_t
* /* O - Named group */
2752 ppd_get_group(ppd_file_t
*ppd
, /* I - PPD file */
2753 const char *name
, /* I - Name of group */
2754 const char *text
, /* I - Text for group */
2755 _ppd_globals_t
*pg
, /* I - Global data */
2756 cups_encoding_t encoding
) /* I - Encoding of text */
2758 int i
; /* Looping var */
2759 ppd_group_t
*group
; /* Group */
2762 DEBUG_printf("7ppd_get_group(ppd=%p, name=\"%s\", text=\"%s\", cg=%p)", (void *)ppd
, name
, text
, (void *)pg
);
2764 for (i
= ppd
->num_groups
, group
= ppd
->groups
; i
> 0; i
--, group
++)
2765 if (!strcmp(group
->name
, name
))
2770 DEBUG_printf("8ppd_get_group: Adding group %s...", name
);
2772 if (pg
->ppd_conform
== PPD_CONFORM_STRICT
&& strlen(text
) >= sizeof(group
->text
))
2774 pg
->ppd_status
= PPD_ILLEGAL_TRANSLATION
;
2779 if (ppd
->num_groups
== 0)
2780 group
= malloc(sizeof(ppd_group_t
));
2782 group
= realloc(ppd
->groups
, (size_t)(ppd
->num_groups
+ 1) * sizeof(ppd_group_t
));
2786 pg
->ppd_status
= PPD_ALLOC_ERROR
;
2791 ppd
->groups
= group
;
2792 group
+= ppd
->num_groups
;
2795 memset(group
, 0, sizeof(ppd_group_t
));
2796 cupsCopyString(group
->name
, name
, sizeof(group
->name
));
2798 cupsCharsetToUTF8((cups_utf8_t
*)group
->text
, text
,
2799 sizeof(group
->text
), encoding
);
2807 * 'ppd_get_option()' - Find or create the named option as needed.
2810 static ppd_option_t
* /* O - Named option */
2811 ppd_get_option(ppd_group_t
*group
, /* I - Group */
2812 const char *name
) /* I - Name of option */
2814 int i
; /* Looping var */
2815 ppd_option_t
*option
; /* Option */
2818 DEBUG_printf("7ppd_get_option(group=%p(\"%s\"), name=\"%s\")", (void *)group
, group
->name
, name
);
2820 for (i
= group
->num_options
, option
= group
->options
; i
> 0; i
--, option
++)
2821 if (!strcmp(option
->keyword
, name
))
2826 if (group
->num_options
== 0)
2827 option
= malloc(sizeof(ppd_option_t
));
2829 option
= realloc(group
->options
, (size_t)(group
->num_options
+ 1) * sizeof(ppd_option_t
));
2834 group
->options
= option
;
2835 option
+= group
->num_options
;
2836 group
->num_options
++;
2838 memset(option
, 0, sizeof(ppd_option_t
));
2839 cupsCopyString(option
->keyword
, name
, sizeof(option
->keyword
));
2847 * 'ppd_globals_alloc()' - Allocate and initialize global data.
2850 static _ppd_globals_t
* /* O - Pointer to global data */
2851 ppd_globals_alloc(void)
2853 return ((_ppd_globals_t
*)calloc(1, sizeof(_ppd_globals_t
)));
2858 * 'ppd_globals_free()' - Free global data.
2861 #if defined(HAVE_PTHREAD_H) || defined(_WIN32)
2863 ppd_globals_free(_ppd_globals_t
*pg
) /* I - Pointer to global data */
2867 #endif /* HAVE_PTHREAD_H || _WIN32 */
2870 #ifdef HAVE_PTHREAD_H
2872 * 'ppd_globals_init()' - Initialize per-thread globals...
2876 ppd_globals_init(void)
2879 * Register the global data for this thread...
2882 pthread_key_create(&ppd_globals_key
, (void (*)(void *))ppd_globals_free
);
2884 #endif /* HAVE_PTHREAD_H */
2888 * 'ppd_hash_option()' - Generate a hash of the option name...
2891 static int /* O - Hash index */
2892 ppd_hash_option(ppd_option_t
*option
, /* I - Option */
2894 int hash
= 0; /* Hash index */
2895 const char *k
; /* Pointer into keyword */
2899 for (hash
= option
->keyword
[0], k
= option
->keyword
+ 1; *k
;)
2900 hash
= (int)(33U * (unsigned)hash
) + *k
++;
2902 return (hash
& 511);
2907 * 'ppd_read()' - Read a line from a PPD file, skipping comment lines as
2911 static int /* O - Bitmask of fields read */
2912 ppd_read(cups_file_t
*fp
, /* I - File to read from */
2913 _ppd_line_t
*line
, /* I - Line buffer */
2914 char *keyword
, /* O - Keyword from line */
2915 char *option
, /* O - Option from line */
2916 char *text
, /* O - Human-readable text from line */
2917 char **string
, /* O - Code/string data */
2918 int ignoreblank
, /* I - Ignore blank lines? */
2919 _ppd_globals_t
*pg
) /* I - Global data */
2921 int ch
, /* Character from file */
2922 col
, /* Column in line */
2923 colon
, /* Colon seen? */
2924 endquote
, /* Waiting for an end quote */
2925 mask
, /* Mask to be returned */
2926 startline
, /* Start line */
2927 textlen
; /* Length of text */
2928 char *keyptr
, /* Keyword pointer */
2929 *optptr
, /* Option pointer */
2930 *textptr
, /* Text pointer */
2931 *strptr
, /* Pointer into string */
2932 *lineptr
; /* Current position in line buffer */
2936 * Now loop until we have a valid line...
2941 startline
= pg
->ppd_line
+ 1;
2945 line
->bufsize
= 1024;
2946 line
->buffer
= malloc(1024);
2958 lineptr
= line
->buffer
;
2962 while ((ch
= cupsFileGetChar(fp
)) != EOF
)
2964 if (lineptr
>= (line
->buffer
+ line
->bufsize
- 1))
2967 * Expand the line buffer...
2970 char *temp
; /* Temporary line pointer */
2973 line
->bufsize
+= 1024;
2974 if (line
->bufsize
> 262144)
2977 * Don't allow lines longer than 256k!
2980 pg
->ppd_line
= startline
;
2981 pg
->ppd_status
= PPD_LINE_TOO_LONG
;
2986 temp
= realloc(line
->buffer
, line
->bufsize
);
2989 pg
->ppd_line
= startline
;
2990 pg
->ppd_status
= PPD_LINE_TOO_LONG
;
2995 lineptr
= temp
+ (lineptr
- line
->buffer
);
2996 line
->buffer
= temp
;
2999 if (ch
== '\r' || ch
== '\n')
3002 * Line feed or carriage return...
3011 * Check for a trailing line feed...
3014 if ((ch
= cupsFilePeekChar(fp
)) == EOF
)
3021 cupsFileGetChar(fp
);
3024 if (lineptr
== line
->buffer
&& ignoreblank
)
3025 continue; /* Skip blank lines */
3029 if (!endquote
) /* Continue for multi-line text */
3034 else if (ch
< ' ' && ch
!= '\t' && pg
->ppd_conform
== PPD_CONFORM_STRICT
)
3037 * Other control characters...
3040 pg
->ppd_line
= startline
;
3041 pg
->ppd_status
= PPD_ILLEGAL_CHARACTER
;
3045 else if (ch
!= 0x1a)
3048 * Any other character...
3051 *lineptr
++ = (char)ch
;
3054 if (col
> (PPD_MAX_LINE
- 1))
3057 * Line is too long...
3060 pg
->ppd_line
= startline
;
3061 pg
->ppd_status
= PPD_LINE_TOO_LONG
;
3066 if (ch
== ':' && strncmp(line
->buffer
, "*%", 2) != 0)
3069 if (ch
== '\"' && colon
)
3070 endquote
= !endquote
;
3077 * Didn't finish this quoted string...
3080 while ((ch
= cupsFileGetChar(fp
)) != EOF
)
3083 else if (ch
== '\r' || ch
== '\n')
3091 * Check for a trailing line feed...
3094 if ((ch
= cupsFilePeekChar(fp
)) == EOF
)
3097 cupsFileGetChar(fp
);
3100 else if (ch
< ' ' && ch
!= '\t' && pg
->ppd_conform
== PPD_CONFORM_STRICT
)
3103 * Other control characters...
3106 pg
->ppd_line
= startline
;
3107 pg
->ppd_status
= PPD_ILLEGAL_CHARACTER
;
3111 else if (ch
!= 0x1a)
3115 if (col
> (PPD_MAX_LINE
- 1))
3118 * Line is too long...
3121 pg
->ppd_line
= startline
;
3122 pg
->ppd_status
= PPD_LINE_TOO_LONG
;
3132 * Didn't finish this line...
3135 while ((ch
= cupsFileGetChar(fp
)) != EOF
)
3136 if (ch
== '\r' || ch
== '\n')
3139 * Line feed or carriage return...
3148 * Check for a trailing line feed...
3151 if ((ch
= cupsFilePeekChar(fp
)) == EOF
)
3154 cupsFileGetChar(fp
);
3159 else if (ch
< ' ' && ch
!= '\t' && pg
->ppd_conform
== PPD_CONFORM_STRICT
)
3162 * Other control characters...
3165 pg
->ppd_line
= startline
;
3166 pg
->ppd_status
= PPD_ILLEGAL_CHARACTER
;
3170 else if (ch
!= 0x1a)
3174 if (col
> (PPD_MAX_LINE
- 1))
3177 * Line is too long...
3180 pg
->ppd_line
= startline
;
3181 pg
->ppd_status
= PPD_LINE_TOO_LONG
;
3188 if (lineptr
> line
->buffer
&& lineptr
[-1] == '\n')
3193 DEBUG_printf("9ppd_read: LINE=\"%s\"", line
->buffer
);
3196 * The dynamically created PPDs for older style macOS
3197 * drivers include a large blob of data inserted as comments
3198 * at the end of the file. As an optimization we can stop
3199 * reading the PPD when we get to the start of this data.
3202 if (!strcmp(line
->buffer
, "*%APLWORKSET START"))
3205 if (ch
== EOF
&& lineptr
== line
->buffer
)
3213 lineptr
= line
->buffer
+ 1;
3220 if ((!line
->buffer
[0] || /* Blank line */
3221 !strncmp(line
->buffer
, "*%", 2) || /* Comment line */
3222 !strcmp(line
->buffer
, "*End")) && /* End of multi-line string */
3223 ignoreblank
) /* Ignore these? */
3225 startline
= pg
->ppd_line
+ 1;
3229 if (!strcmp(line
->buffer
, "*")) /* (Bad) comment line */
3231 if (pg
->ppd_conform
== PPD_CONFORM_RELAXED
)
3233 startline
= pg
->ppd_line
+ 1;
3238 pg
->ppd_line
= startline
;
3239 pg
->ppd_status
= PPD_ILLEGAL_MAIN_KEYWORD
;
3245 if (line
->buffer
[0] != '*') /* All lines start with an asterisk */
3248 * Allow lines consisting of just whitespace...
3251 for (lineptr
= line
->buffer
; *lineptr
; lineptr
++)
3252 if (*lineptr
&& !_cups_isspace(*lineptr
))
3257 pg
->ppd_status
= PPD_MISSING_ASTERISK
;
3260 else if (ignoreblank
)
3272 while (*lineptr
&& *lineptr
!= ':' && !_cups_isspace(*lineptr
))
3274 if (*lineptr
<= ' ' || *lineptr
> 126 || *lineptr
== '/' ||
3275 (keyptr
- keyword
) >= (PPD_MAX_NAME
- 1))
3277 pg
->ppd_status
= PPD_ILLEGAL_MAIN_KEYWORD
;
3281 *keyptr
++ = *lineptr
++;
3286 if (!strcmp(keyword
, "End"))
3289 mask
|= PPD_KEYWORD
;
3291 if (_cups_isspace(*lineptr
))
3294 * Get an option name...
3297 while (_cups_isspace(*lineptr
))
3302 while (*lineptr
&& !_cups_isspace(*lineptr
) && *lineptr
!= ':' &&
3305 if (*lineptr
<= ' ' || *lineptr
> 126 ||
3306 (optptr
- option
) >= (PPD_MAX_NAME
- 1))
3308 pg
->ppd_status
= PPD_ILLEGAL_OPTION_KEYWORD
;
3312 *optptr
++ = *lineptr
++;
3317 if (_cups_isspace(*lineptr
) && pg
->ppd_conform
== PPD_CONFORM_STRICT
)
3319 pg
->ppd_status
= PPD_ILLEGAL_WHITESPACE
;
3323 while (_cups_isspace(*lineptr
))
3328 if (*lineptr
== '/')
3331 * Get human-readable text...
3338 while (*lineptr
!= '\0' && *lineptr
!= '\n' && *lineptr
!= ':')
3340 if (((unsigned char)*lineptr
< ' ' && *lineptr
!= '\t') ||
3341 (textptr
- text
) >= (PPD_MAX_LINE
- 1))
3343 pg
->ppd_status
= PPD_ILLEGAL_TRANSLATION
;
3347 *textptr
++ = *lineptr
++;
3351 textlen
= ppd_decode(text
);
3353 if (textlen
> PPD_MAX_TEXT
&& pg
->ppd_conform
== PPD_CONFORM_STRICT
)
3355 pg
->ppd_status
= PPD_ILLEGAL_TRANSLATION
;
3363 if (_cups_isspace(*lineptr
) && pg
->ppd_conform
== PPD_CONFORM_STRICT
)
3365 pg
->ppd_status
= PPD_ILLEGAL_WHITESPACE
;
3369 while (_cups_isspace(*lineptr
))
3372 if (*lineptr
== ':')
3375 * Get string after trimming leading and trailing whitespace...
3379 while (_cups_isspace(*lineptr
))
3382 strptr
= lineptr
+ strlen(lineptr
) - 1;
3383 while (strptr
>= lineptr
&& _cups_isspace(*strptr
))
3386 if (*strptr
== '\"')
3389 * Quoted string by itself, remove quotes...
3396 *string
= strdup(lineptr
);
3408 * 'ppd_update_filters()' - Update the filters array as needed.
3410 * This function re-populates the filters array with cupsFilter2 entries that
3411 * have been stripped of the destination MIME media types and any maxsize hints.
3413 * (All for backwards-compatibility)
3416 static int /* O - 1 on success, 0 on failure */
3417 ppd_update_filters(ppd_file_t
*ppd
, /* I - PPD file */
3418 _ppd_globals_t
*pg
) /* I - Global data */
3420 ppd_attr_t
*attr
; /* Current cupsFilter2 value */
3421 char srcsuper
[16], /* Source MIME media type */
3423 dstsuper
[16], /* Destination MIME media type */
3425 *ptr
, /* Pointer into command to run */
3426 buffer
[1024], /* Re-written cupsFilter value */
3427 **filter
; /* Current filter */
3428 int cost
; /* Cost of filter */
3430 char program
[1024] = { 0 }; /* Command to run */
3432 DEBUG_printf("4ppd_update_filters(ppd=%p, cg=%p)", (void *)ppd
, (void *)pg
);
3435 * See if we have any cupsFilter2 lines...
3438 if ((attr
= ppdFindAttr(ppd
, "cupsFilter2", NULL
)) == NULL
)
3440 DEBUG_puts("5ppd_update_filters: No cupsFilter2 keywords present.");
3445 * Yes, free the cupsFilter-defined filters and re-build...
3448 ppd_free_filters(ppd
);
3453 * Parse the cupsFilter2 string:
3455 * src/type dst/type cost program
3456 * src/type dst/type cost maxsize(n) program
3459 DEBUG_printf("5ppd_update_filters: cupsFilter2=\"%s\"", attr
->value
);
3461 if (sscanf(attr
->value
, "%15[^/]/%255s%*[ \t]%15[^/]/%255s%d%*[ \t]%1023[^\n]",
3462 srcsuper
, srctype
, dstsuper
, dsttype
, &cost
, program
) != 6)
3464 DEBUG_puts("5ppd_update_filters: Bad cupsFilter2 line.");
3465 pg
->ppd_status
= PPD_BAD_VALUE
;
3470 DEBUG_printf("5ppd_update_filters: srcsuper=\"%s\", srctype=\"%s\", dstsuper=\"%s\", dsttype=\"%s\", cost=%d, program=\"%s\"", srcsuper
, srctype
, dstsuper
, dsttype
, cost
, program
);
3472 if (!strncmp(program
, "maxsize(", 8) &&
3473 (ptr
= strchr(program
+ 8, ')')) != NULL
)
3475 DEBUG_puts("5ppd_update_filters: Found maxsize(nnn).");
3478 while (_cups_isspace(*ptr
))
3481 _cups_strcpy(program
, ptr
);
3482 DEBUG_printf("5ppd_update_filters: New program=\"%s\"", program
);
3486 * Convert to cupsFilter format:
3488 * src/type cost program
3491 snprintf(buffer
, sizeof(buffer
), "%s/%s %d %s", srcsuper
, srctype
, cost
,
3493 DEBUG_printf("5ppd_update_filters: Adding \"%s\".", buffer
);
3496 * Add a cupsFilter-compatible string to the filters array.
3499 if (ppd
->num_filters
== 0)
3500 filter
= malloc(sizeof(char *));
3502 filter
= realloc(ppd
->filters
, sizeof(char *) * (size_t)(ppd
->num_filters
+ 1));
3506 DEBUG_puts("5ppd_update_filters: Out of memory.");
3507 pg
->ppd_status
= PPD_ALLOC_ERROR
;
3512 ppd
->filters
= filter
;
3513 filter
+= ppd
->num_filters
;
3514 ppd
->num_filters
++;
3516 *filter
= strdup(buffer
);
3518 while ((attr
= ppdFindNextAttr(ppd
, "cupsFilter2", NULL
)) != NULL
);
3520 DEBUG_puts("5ppd_update_filters: Completed OK.");