2 * PPD file routines for CUPS.
4 * Copyright 2007-2017 by Apple Inc.
5 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
9 * PostScript is a trademark of Adobe Systems, Inc.
11 * This code and any derivative of it may be used and distributed
12 * freely under the terms of the GNU General Public License when
13 * used with GNU Ghostscript or its derivatives. Use of the code
14 * (or any derivative of it) with software other than GNU
15 * GhostScript (or its derivatives) is governed by the CUPS license
20 * Include necessary headers.
23 #include "cups-private.h"
24 #include "ppd-private.h"
31 #define ppd_free(p) if (p) free(p) /* Safe free macro */
33 #define PPD_KEYWORD 1 /* Line contained a keyword */
34 #define PPD_OPTION 2 /* Line contained an option name */
35 #define PPD_TEXT 4 /* Line contained human-readable text */
36 #define PPD_STRING 8 /* Line contained a string or code */
38 #define PPD_HASHSIZE 512 /* Size of hash */
42 * Line buffer structure...
45 typedef struct _ppd_line_s
47 char *buffer
; /* Pointer to buffer */
48 size_t bufsize
; /* Size of the buffer */
56 static _cups_threadkey_t ppd_globals_key
= _CUPS_THREADKEY_INITIALIZER
;
57 /* Thread local storage key */
59 static pthread_once_t ppd_globals_key_once
= PTHREAD_ONCE_INIT
;
60 /* One-time initialization object */
61 #endif /* HAVE_PTHREAD_H */
68 static ppd_attr_t
*ppd_add_attr(ppd_file_t
*ppd
, const char *name
,
69 const char *spec
, const char *text
,
71 static ppd_choice_t
*ppd_add_choice(ppd_option_t
*option
, const char *name
);
72 static ppd_size_t
*ppd_add_size(ppd_file_t
*ppd
, const char *name
);
73 static int ppd_compare_attrs(ppd_attr_t
*a
, ppd_attr_t
*b
);
74 static int ppd_compare_choices(ppd_choice_t
*a
, ppd_choice_t
*b
);
75 static int ppd_compare_coptions(ppd_coption_t
*a
,
77 static int ppd_compare_options(ppd_option_t
*a
, ppd_option_t
*b
);
78 static int ppd_decode(char *string
);
79 static void ppd_free_filters(ppd_file_t
*ppd
);
80 static void ppd_free_group(ppd_group_t
*group
);
81 static void ppd_free_option(ppd_option_t
*option
);
82 static ppd_coption_t
*ppd_get_coption(ppd_file_t
*ppd
, const char *name
);
83 static ppd_cparam_t
*ppd_get_cparam(ppd_coption_t
*opt
,
86 static ppd_group_t
*ppd_get_group(ppd_file_t
*ppd
, const char *name
,
87 const char *text
, _ppd_globals_t
*pg
,
88 cups_encoding_t encoding
);
89 static ppd_option_t
*ppd_get_option(ppd_group_t
*group
, const char *name
);
90 static _ppd_globals_t
*ppd_globals_alloc(void);
91 #if defined(HAVE_PTHREAD_H) || defined(WIN32)
92 static void ppd_globals_free(_ppd_globals_t
*g
);
93 #endif /* HAVE_PTHREAD_H || WIN32 */
95 static void ppd_globals_init(void);
96 #endif /* HAVE_PTHREAD_H */
97 static int ppd_hash_option(ppd_option_t
*option
);
98 static int ppd_read(cups_file_t
*fp
, _ppd_line_t
*line
,
99 char *keyword
, char *option
, char *text
,
100 char **string
, int ignoreblank
,
102 static int ppd_update_filters(ppd_file_t
*ppd
,
107 * 'ppdClose()' - Free all memory used by the PPD file.
111 ppdClose(ppd_file_t
*ppd
) /* I - PPD file record */
113 int i
; /* Looping var */
114 ppd_emul_t
*emul
; /* Current emulation */
115 ppd_group_t
*group
; /* Current group */
116 char **font
; /* Current font */
117 ppd_attr_t
**attr
; /* Current attribute */
118 ppd_coption_t
*coption
; /* Current custom option */
119 ppd_cparam_t
*cparam
; /* Current custom parameter */
123 * Range check arguments...
130 * Free all strings at the top level...
133 _cupsStrFree(ppd
->lang_encoding
);
134 _cupsStrFree(ppd
->nickname
);
137 _cupsStrFree(ppd
->jcl_begin
);
138 _cupsStrFree(ppd
->jcl_end
);
139 _cupsStrFree(ppd
->jcl_ps
);
142 * Free any emulations...
145 if (ppd
->num_emulations
> 0)
147 for (i
= ppd
->num_emulations
, emul
= ppd
->emulations
; i
> 0; i
--, emul
++)
149 _cupsStrFree(emul
->start
);
150 _cupsStrFree(emul
->stop
);
153 ppd_free(ppd
->emulations
);
157 * Free any UI groups, subgroups, and options...
160 if (ppd
->num_groups
> 0)
162 for (i
= ppd
->num_groups
, group
= ppd
->groups
; i
> 0; i
--, group
++)
163 ppd_free_group(group
);
165 ppd_free(ppd
->groups
);
168 cupsArrayDelete(ppd
->options
);
169 cupsArrayDelete(ppd
->marked
);
172 * Free any page sizes...
175 if (ppd
->num_sizes
> 0)
176 ppd_free(ppd
->sizes
);
179 * Free any constraints...
182 if (ppd
->num_consts
> 0)
183 ppd_free(ppd
->consts
);
186 * Free any filters...
189 ppd_free_filters(ppd
);
195 if (ppd
->num_fonts
> 0)
197 for (i
= ppd
->num_fonts
, font
= ppd
->fonts
; i
> 0; i
--, font
++)
200 ppd_free(ppd
->fonts
);
204 * Free any profiles...
207 if (ppd
->num_profiles
> 0)
208 ppd_free(ppd
->profiles
);
211 * Free any attributes...
214 if (ppd
->num_attrs
> 0)
216 for (i
= ppd
->num_attrs
, attr
= ppd
->attrs
; i
> 0; i
--, attr
++)
218 _cupsStrFree((*attr
)->value
);
222 ppd_free(ppd
->attrs
);
225 cupsArrayDelete(ppd
->sorted_attrs
);
228 * Free custom options...
231 for (coption
= (ppd_coption_t
*)cupsArrayFirst(ppd
->coptions
);
233 coption
= (ppd_coption_t
*)cupsArrayNext(ppd
->coptions
))
235 for (cparam
= (ppd_cparam_t
*)cupsArrayFirst(coption
->params
);
237 cparam
= (ppd_cparam_t
*)cupsArrayNext(coption
->params
))
239 switch (cparam
->type
)
241 case PPD_CUSTOM_PASSCODE
:
242 case PPD_CUSTOM_PASSWORD
:
243 case PPD_CUSTOM_STRING
:
244 _cupsStrFree(cparam
->current
.custom_string
);
254 cupsArrayDelete(coption
->params
);
259 cupsArrayDelete(ppd
->coptions
);
262 * Free constraints...
265 if (ppd
->cups_uiconstraints
)
267 _ppd_cups_uiconsts_t
*consts
; /* Current constraints */
270 for (consts
= (_ppd_cups_uiconsts_t
*)cupsArrayFirst(ppd
->cups_uiconstraints
);
272 consts
= (_ppd_cups_uiconsts_t
*)cupsArrayNext(ppd
->cups_uiconstraints
))
274 free(consts
->constraints
);
278 cupsArrayDelete(ppd
->cups_uiconstraints
);
282 * Free any PPD cache/mapping data...
286 _ppdCacheDestroy(ppd
->cache
);
289 * Free the whole record...
297 * 'ppdErrorString()' - Returns the text associated with a status.
299 * @since CUPS 1.1.19/macOS 10.3@
302 const char * /* O - Status string */
303 ppdErrorString(ppd_status_t status
) /* I - PPD status */
305 static const char * const messages
[] =/* Status messages */
308 _("Unable to open PPD file"),
309 _("NULL PPD file pointer"),
310 _("Memory allocation error"),
311 _("Missing PPD-Adobe-4.x header"),
312 _("Missing value string"),
315 _("OpenGroup without a CloseGroup first"),
316 _("Bad OpenUI/JCLOpenUI"),
317 _("OpenUI/JCLOpenUI without a CloseUI/JCLCloseUI first"),
318 _("Bad OrderDependency"),
319 _("Bad UIConstraints"),
320 _("Missing asterisk in column 1"),
321 _("Line longer than the maximum allowed (255 characters)"),
322 _("Illegal control character"),
323 _("Illegal main keyword string"),
324 _("Illegal option keyword string"),
325 _("Illegal translation string"),
326 _("Illegal whitespace character"),
327 _("Bad custom parameter"),
328 _("Missing option keyword"),
329 _("Bad value string"),
330 _("Missing CloseGroup")
334 if (status
< PPD_OK
|| status
>= PPD_MAX_STATUS
)
335 return (_cupsLangString(cupsLangDefault(), _("Unknown")));
337 return (_cupsLangString(cupsLangDefault(), messages
[status
]));
342 * '_ppdGetEncoding()' - Get the CUPS encoding value for the given
346 cups_encoding_t
/* O - CUPS encoding value */
347 _ppdGetEncoding(const char *name
) /* I - LanguageEncoding string */
349 if (!_cups_strcasecmp(name
, "ISOLatin1"))
350 return (CUPS_ISO8859_1
);
351 else if (!_cups_strcasecmp(name
, "ISOLatin2"))
352 return (CUPS_ISO8859_2
);
353 else if (!_cups_strcasecmp(name
, "ISOLatin5"))
354 return (CUPS_ISO8859_5
);
355 else if (!_cups_strcasecmp(name
, "JIS83-RKSJ"))
356 return (CUPS_JIS_X0213
);
357 else if (!_cups_strcasecmp(name
, "MacStandard"))
358 return (CUPS_MAC_ROMAN
);
359 else if (!_cups_strcasecmp(name
, "WindowsANSI"))
360 return (CUPS_WINDOWS_1252
);
367 * '_ppdGlobals()' - Return a pointer to thread local storage
370 _ppd_globals_t
* /* O - Pointer to global data */
373 _ppd_globals_t
*pg
; /* Pointer to global data */
376 #ifdef HAVE_PTHREAD_H
378 * Initialize the global data exactly once...
381 pthread_once(&ppd_globals_key_once
, ppd_globals_init
);
382 #endif /* HAVE_PTHREAD_H */
385 * See if we have allocated the data yet...
388 if ((pg
= (_ppd_globals_t
*)_cupsThreadGetData(ppd_globals_key
)) == NULL
)
391 * No, allocate memory as set the pointer for the key...
394 if ((pg
= ppd_globals_alloc()) != NULL
)
395 _cupsThreadSetData(ppd_globals_key
, pg
);
399 * Return the pointer to the data...
407 * 'ppdLastError()' - Return the status from the last ppdOpen*().
409 * @since CUPS 1.1.19/macOS 10.3@
412 ppd_status_t
/* O - Status code */
413 ppdLastError(int *line
) /* O - Line number */
415 _ppd_globals_t
*pg
= _ppdGlobals();
420 *line
= pg
->ppd_line
;
422 return (pg
->ppd_status
);
427 * '_ppdOpen()' - Read a PPD file into memory.
429 * @since CUPS 1.2/macOS 10.5@
432 ppd_file_t
* /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
434 cups_file_t
*fp
, /* I - File to read from */
435 _ppd_localization_t localization
) /* I - Localization to load */
437 int i
, j
, k
; /* Looping vars */
438 int count
; /* Temporary count */
439 _ppd_line_t line
; /* Line buffer */
440 ppd_file_t
*ppd
; /* PPD file record */
441 ppd_group_t
*group
, /* Current group */
442 *subgroup
; /* Current sub-group */
443 ppd_option_t
*option
; /* Current option */
444 ppd_choice_t
*choice
; /* Current choice */
445 ppd_const_t
*constraint
; /* Current constraint */
446 ppd_size_t
*size
; /* Current page size */
447 int mask
; /* Line data mask */
448 char keyword
[PPD_MAX_NAME
],
449 /* Keyword from file */
451 /* Option from file */
453 /* Human-readable text from file */
454 *string
, /* Code/text from file */
455 *sptr
, /* Pointer into string */
456 *nameptr
, /* Pointer into name */
457 *temp
, /* Temporary string pointer */
458 **tempfonts
; /* Temporary fonts pointer */
459 float order
; /* Order dependency number */
460 ppd_section_t section
; /* Order dependency section */
461 ppd_profile_t
*profile
; /* Pointer to color profile */
462 char **filter
; /* Pointer to filter */
463 struct lconv
*loc
; /* Locale data */
464 int ui_keyword
; /* Is this line a UI keyword? */
465 cups_lang_t
*lang
; /* Language data */
466 cups_encoding_t encoding
; /* Encoding of PPD file */
467 _ppd_globals_t
*pg
= _ppdGlobals();
469 char custom_name
[PPD_MAX_NAME
];
470 /* CustomFoo attribute name */
471 ppd_attr_t
*custom_attr
; /* CustomFoo attribute */
472 char ll
[7], /* Base language + '.' */
473 ll_CC
[7]; /* Language w/country + '.' */
474 size_t ll_len
= 0, /* Base language length */
475 ll_CC_len
= 0; /* Language w/country length */
476 static const char * const ui_keywords
[] =
478 #ifdef CUPS_USE_FULL_UI_KEYWORDS_LIST
480 * Adobe defines some 41 keywords as "UI", meaning that they are
481 * user interface elements and that they should be treated as such
482 * even if the PPD creator doesn't use Open/CloseUI around them.
484 * Since this can cause previously invisible options to appear and
485 * confuse users, the default is to only treat the PageSize and
486 * PageRegion keywords this way.
488 /* Boolean keywords */
498 /* PickOne keywords */
511 "JCLFrameBufferSize",
532 #else /* !CUPS_USE_FULL_UI_KEYWORDS_LIST */
535 #endif /* CUPS_USE_FULL_UI_KEYWORDS_LIST */
537 static const char * const color_keywords
[] = /* Keywords associated with color profiles */
544 DEBUG_printf(("_ppdOpen(fp=%p)", fp
));
547 * Default to "OK" status...
550 pg
->ppd_status
= PPD_OK
;
554 * Range check input...
559 pg
->ppd_status
= PPD_NULL_FILE
;
564 * If only loading a single localization set up the strings to match...
567 if (localization
== _PPD_LOCALIZATION_DEFAULT
)
569 if ((lang
= cupsLangDefault()) == NULL
)
572 snprintf(ll_CC
, sizeof(ll_CC
), "%s.", lang
->language
);
575 * <rdar://problem/22130168>
576 * <rdar://problem/27245567>
578 * Need to use a different base language for some locales...
581 if (!strcmp(lang
->language
, "zh_HK"))
582 { /* Traditional Chinese + variants */
583 strlcpy(ll_CC
, "zh_TW.", sizeof(ll_CC
));
584 strlcpy(ll
, "zh_", sizeof(ll
));
586 else if (!strncmp(lang
->language
, "zh", 2))
587 strlcpy(ll
, "zh_", sizeof(ll
)); /* Any Chinese variant */
588 else if (!strncmp(lang
->language
, "jp", 2))
589 { /* Any Japanese variant */
590 strlcpy(ll_CC
, "ja", sizeof(ll_CC
));
591 strlcpy(ll
, "jp", sizeof(ll
));
593 else if (!strncmp(lang
->language
, "nb", 2) || !strncmp(lang
->language
, "no", 2))
594 { /* Any Norwegian variant */
595 strlcpy(ll_CC
, "nb", sizeof(ll_CC
));
596 strlcpy(ll
, "no", sizeof(ll
));
599 snprintf(ll
, sizeof(ll
), "%2.2s.", lang
->language
);
601 ll_CC_len
= strlen(ll_CC
);
604 DEBUG_printf(("2_ppdOpen: Loading localizations matching \"%s\" and \"%s\"",
609 * Grab the first line and make sure it reads '*PPD-Adobe: "major.minor"'...
615 mask
= ppd_read(fp
, &line
, keyword
, name
, text
, &string
, 0, pg
);
617 DEBUG_printf(("2_ppdOpen: mask=%x, keyword=\"%s\"...", mask
, keyword
));
620 strcmp(keyword
, "PPD-Adobe") ||
621 string
== NULL
|| string
[0] != '4')
624 * Either this is not a PPD file, or it is not a 4.x PPD file.
627 if (pg
->ppd_status
== PPD_OK
)
628 pg
->ppd_status
= PPD_MISSING_PPDADOBE4
;
630 _cupsStrFree(string
);
631 ppd_free(line
.buffer
);
636 DEBUG_printf(("2_ppdOpen: keyword=%s, string=%p", keyword
, string
));
638 _cupsStrFree(string
);
641 * Allocate memory for the PPD file record...
644 if ((ppd
= calloc(1, sizeof(ppd_file_t
))) == NULL
)
646 pg
->ppd_status
= PPD_ALLOC_ERROR
;
648 _cupsStrFree(string
);
649 ppd_free(line
.buffer
);
654 ppd
->language_level
= 2;
655 ppd
->color_device
= 0;
656 ppd
->colorspace
= PPD_CS_N
;
657 ppd
->landscape
= -90;
658 ppd
->coptions
= cupsArrayNew((cups_array_func_t
)ppd_compare_coptions
,
662 * Read lines from the PPD file and add them to the file record...
670 encoding
= CUPS_ISO8859_1
;
673 while ((mask
= ppd_read(fp
, &line
, keyword
, name
, text
, &string
, 1, pg
)) != 0)
675 DEBUG_printf(("2_ppdOpen: mask=%x, keyword=\"%s\", name=\"%s\", "
676 "text=\"%s\", string=%d chars...", mask
, keyword
, name
, text
,
677 string
? (int)strlen(string
) : 0));
679 if (strncmp(keyword
, "Default", 7) && !string
&&
680 pg
->ppd_conform
!= PPD_CONFORM_RELAXED
)
683 * Need a string value!
686 pg
->ppd_status
= PPD_MISSING_VALUE
;
694 * Certain main keywords (as defined by the PPD spec) may be used
695 * without the usual OpenUI/CloseUI stuff. Presumably this is just
696 * so that Adobe wouldn't completely break compatibility with PPD
697 * files prior to v4.0 of the spec, but it is hopelessly
698 * inconsistent... Catch these main keywords and automatically
699 * create the corresponding option, as needed...
705 * Previous line was a UI keyword...
713 * If we are filtering out keyword localizations, see if this line needs to
717 if (localization
!= _PPD_LOCALIZATION_ALL
&&
718 (temp
= strchr(keyword
, '.')) != NULL
&&
719 ((temp
- keyword
) == 2 || (temp
- keyword
) == 5) &&
720 _cups_isalpha(keyword
[0]) &&
721 _cups_isalpha(keyword
[1]) &&
722 (keyword
[2] == '.' ||
723 (keyword
[2] == '_' && _cups_isalpha(keyword
[3]) &&
724 _cups_isalpha(keyword
[4]) && keyword
[5] == '.')))
726 if (localization
== _PPD_LOCALIZATION_NONE
||
727 (localization
== _PPD_LOCALIZATION_DEFAULT
&&
728 strncmp(ll_CC
, keyword
, ll_CC_len
) &&
729 strncmp(ll
, keyword
, ll_len
)))
731 DEBUG_printf(("2_ppdOpen: Ignoring localization: \"%s\"\n", keyword
));
734 else if (localization
== _PPD_LOCALIZATION_ICC_PROFILES
)
737 * Only load localizations for the color profile related keywords...
741 i
< (int)(sizeof(color_keywords
) / sizeof(color_keywords
[0]));
744 if (!_cups_strcasecmp(temp
, color_keywords
[i
]))
748 if (i
>= (int)(sizeof(color_keywords
) / sizeof(color_keywords
[0])))
750 DEBUG_printf(("2_ppdOpen: Ignoring localization: \"%s\"\n", keyword
));
756 if (option
== NULL
&&
757 (mask
& (PPD_KEYWORD
| PPD_OPTION
| PPD_STRING
)) ==
758 (PPD_KEYWORD
| PPD_OPTION
| PPD_STRING
))
760 for (i
= 0; i
< (int)(sizeof(ui_keywords
) / sizeof(ui_keywords
[0])); i
++)
761 if (!strcmp(keyword
, ui_keywords
[i
]))
764 if (i
< (int)(sizeof(ui_keywords
) / sizeof(ui_keywords
[0])))
767 * Create the option in the appropriate group...
772 DEBUG_printf(("2_ppdOpen: FOUND ADOBE UI KEYWORD %s WITHOUT OPENUI!",
777 if ((group
= ppd_get_group(ppd
, "General", _("General"), pg
,
781 DEBUG_printf(("2_ppdOpen: Adding to group %s...", group
->text
));
782 option
= ppd_get_option(group
, keyword
);
786 option
= ppd_get_option(group
, keyword
);
790 pg
->ppd_status
= PPD_ALLOC_ERROR
;
796 * Now fill in the initial information for the option...
799 if (!strncmp(keyword
, "JCL", 3))
800 option
->section
= PPD_ORDER_JCL
;
802 option
->section
= PPD_ORDER_ANY
;
804 option
->order
= 10.0f
;
807 option
->ui
= PPD_UI_BOOLEAN
;
809 option
->ui
= PPD_UI_PICKONE
;
811 for (j
= 0; j
< ppd
->num_attrs
; j
++)
812 if (!strncmp(ppd
->attrs
[j
]->name
, "Default", 7) &&
813 !strcmp(ppd
->attrs
[j
]->name
+ 7, keyword
) &&
814 ppd
->attrs
[j
]->value
)
816 DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...",
817 option
->keyword
, ppd
->attrs
[j
]->value
));
818 strlcpy(option
->defchoice
, ppd
->attrs
[j
]->value
,
819 sizeof(option
->defchoice
));
823 if (!strcmp(keyword
, "PageSize"))
824 strlcpy(option
->text
, _("Media Size"), sizeof(option
->text
));
825 else if (!strcmp(keyword
, "MediaType"))
826 strlcpy(option
->text
, _("Media Type"), sizeof(option
->text
));
827 else if (!strcmp(keyword
, "InputSlot"))
828 strlcpy(option
->text
, _("Media Source"), sizeof(option
->text
));
829 else if (!strcmp(keyword
, "ColorModel"))
830 strlcpy(option
->text
, _("Output Mode"), sizeof(option
->text
));
831 else if (!strcmp(keyword
, "Resolution"))
832 strlcpy(option
->text
, _("Resolution"), sizeof(option
->text
));
834 strlcpy(option
->text
, keyword
, sizeof(option
->text
));
838 if (!strcmp(keyword
, "LanguageLevel"))
839 ppd
->language_level
= atoi(string
);
840 else if (!strcmp(keyword
, "LanguageEncoding"))
843 * Say all PPD files are UTF-8, since we convert to UTF-8...
846 ppd
->lang_encoding
= _cupsStrAlloc("UTF-8");
847 encoding
= _ppdGetEncoding(string
);
849 else if (!strcmp(keyword
, "LanguageVersion"))
850 ppd
->lang_version
= string
;
851 else if (!strcmp(keyword
, "Manufacturer"))
852 ppd
->manufacturer
= string
;
853 else if (!strcmp(keyword
, "ModelName"))
854 ppd
->modelname
= string
;
855 else if (!strcmp(keyword
, "Protocols"))
856 ppd
->protocols
= string
;
857 else if (!strcmp(keyword
, "PCFileName"))
858 ppd
->pcfilename
= string
;
859 else if (!strcmp(keyword
, "NickName"))
861 if (encoding
!= CUPS_UTF8
)
863 cups_utf8_t utf8
[256]; /* UTF-8 version of NickName */
866 cupsCharsetToUTF8(utf8
, string
, sizeof(utf8
), encoding
);
867 ppd
->nickname
= _cupsStrAlloc((char *)utf8
);
870 ppd
->nickname
= _cupsStrAlloc(string
);
872 else if (!strcmp(keyword
, "Product"))
873 ppd
->product
= string
;
874 else if (!strcmp(keyword
, "ShortNickName"))
875 ppd
->shortnickname
= string
;
876 else if (!strcmp(keyword
, "TTRasterizer"))
877 ppd
->ttrasterizer
= string
;
878 else if (!strcmp(keyword
, "JCLBegin"))
880 ppd
->jcl_begin
= _cupsStrAlloc(string
);
881 ppd_decode(ppd
->jcl_begin
); /* Decode quoted string */
883 else if (!strcmp(keyword
, "JCLEnd"))
885 ppd
->jcl_end
= _cupsStrAlloc(string
);
886 ppd_decode(ppd
->jcl_end
); /* Decode quoted string */
888 else if (!strcmp(keyword
, "JCLToPSInterpreter"))
890 ppd
->jcl_ps
= _cupsStrAlloc(string
);
891 ppd_decode(ppd
->jcl_ps
); /* Decode quoted string */
893 else if (!strcmp(keyword
, "AccurateScreensSupport"))
894 ppd
->accurate_screens
= !strcmp(string
, "True");
895 else if (!strcmp(keyword
, "ColorDevice"))
896 ppd
->color_device
= !strcmp(string
, "True");
897 else if (!strcmp(keyword
, "ContoneOnly"))
898 ppd
->contone_only
= !strcmp(string
, "True");
899 else if (!strcmp(keyword
, "cupsFlipDuplex"))
900 ppd
->flip_duplex
= !strcmp(string
, "True");
901 else if (!strcmp(keyword
, "cupsManualCopies"))
902 ppd
->manual_copies
= !strcmp(string
, "True");
903 else if (!strcmp(keyword
, "cupsModelNumber"))
904 ppd
->model_number
= atoi(string
);
905 else if (!strcmp(keyword
, "cupsColorProfile"))
907 if (ppd
->num_profiles
== 0)
908 profile
= malloc(sizeof(ppd_profile_t
));
910 profile
= realloc(ppd
->profiles
, sizeof(ppd_profile_t
) * (size_t)(ppd
->num_profiles
+ 1));
914 pg
->ppd_status
= PPD_ALLOC_ERROR
;
919 ppd
->profiles
= profile
;
920 profile
+= ppd
->num_profiles
;
921 ppd
->num_profiles
++;
923 memset(profile
, 0, sizeof(ppd_profile_t
));
924 strlcpy(profile
->resolution
, name
, sizeof(profile
->resolution
));
925 strlcpy(profile
->media_type
, text
, sizeof(profile
->media_type
));
927 profile
->density
= (float)_cupsStrScand(string
, &sptr
, loc
);
928 profile
->gamma
= (float)_cupsStrScand(sptr
, &sptr
, loc
);
929 profile
->matrix
[0][0] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
930 profile
->matrix
[0][1] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
931 profile
->matrix
[0][2] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
932 profile
->matrix
[1][0] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
933 profile
->matrix
[1][1] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
934 profile
->matrix
[1][2] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
935 profile
->matrix
[2][0] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
936 profile
->matrix
[2][1] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
937 profile
->matrix
[2][2] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
939 else if (!strcmp(keyword
, "cupsFilter"))
941 if (ppd
->num_filters
== 0)
942 filter
= malloc(sizeof(char *));
944 filter
= realloc(ppd
->filters
, sizeof(char *) * (size_t)(ppd
->num_filters
+ 1));
948 pg
->ppd_status
= PPD_ALLOC_ERROR
;
953 ppd
->filters
= filter
;
954 filter
+= ppd
->num_filters
;
958 * Retain a copy of the filter string...
961 *filter
= _cupsStrRetain(string
);
963 else if (!strcmp(keyword
, "Throughput"))
964 ppd
->throughput
= atoi(string
);
965 else if (!strcmp(keyword
, "Font"))
968 * Add this font to the list of available fonts...
971 if (ppd
->num_fonts
== 0)
972 tempfonts
= (char **)malloc(sizeof(char *));
974 tempfonts
= (char **)realloc(ppd
->fonts
, sizeof(char *) * (size_t)(ppd
->num_fonts
+ 1));
976 if (tempfonts
== NULL
)
978 pg
->ppd_status
= PPD_ALLOC_ERROR
;
983 ppd
->fonts
= tempfonts
;
984 ppd
->fonts
[ppd
->num_fonts
] = _cupsStrAlloc(name
);
987 else if (!strncmp(keyword
, "ParamCustom", 11))
989 ppd_coption_t
*coption
; /* Custom option */
990 ppd_cparam_t
*cparam
; /* Custom parameter */
991 int corder
; /* Order number */
992 char ctype
[33], /* Data type */
993 cminimum
[65], /* Minimum value */
994 cmaximum
[65]; /* Maximum value */
998 * Get the custom option and parameter...
1001 if ((coption
= ppd_get_coption(ppd
, keyword
+ 11)) == NULL
)
1003 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1008 if ((cparam
= ppd_get_cparam(coption
, name
, text
)) == NULL
)
1010 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1016 * Get the parameter data...
1020 sscanf(string
, "%d%32s%64s%64s", &corder
, ctype
, cminimum
,
1023 pg
->ppd_status
= PPD_BAD_CUSTOM_PARAM
;
1028 cparam
->order
= corder
;
1030 if (!strcmp(ctype
, "curve"))
1032 cparam
->type
= PPD_CUSTOM_CURVE
;
1033 cparam
->minimum
.custom_curve
= (float)_cupsStrScand(cminimum
, NULL
, loc
);
1034 cparam
->maximum
.custom_curve
= (float)_cupsStrScand(cmaximum
, NULL
, loc
);
1036 else if (!strcmp(ctype
, "int"))
1038 cparam
->type
= PPD_CUSTOM_INT
;
1039 cparam
->minimum
.custom_int
= atoi(cminimum
);
1040 cparam
->maximum
.custom_int
= atoi(cmaximum
);
1042 else if (!strcmp(ctype
, "invcurve"))
1044 cparam
->type
= PPD_CUSTOM_INVCURVE
;
1045 cparam
->minimum
.custom_invcurve
= (float)_cupsStrScand(cminimum
, NULL
, loc
);
1046 cparam
->maximum
.custom_invcurve
= (float)_cupsStrScand(cmaximum
, NULL
, loc
);
1048 else if (!strcmp(ctype
, "passcode"))
1050 cparam
->type
= PPD_CUSTOM_PASSCODE
;
1051 cparam
->minimum
.custom_passcode
= atoi(cminimum
);
1052 cparam
->maximum
.custom_passcode
= atoi(cmaximum
);
1054 else if (!strcmp(ctype
, "password"))
1056 cparam
->type
= PPD_CUSTOM_PASSWORD
;
1057 cparam
->minimum
.custom_password
= atoi(cminimum
);
1058 cparam
->maximum
.custom_password
= atoi(cmaximum
);
1060 else if (!strcmp(ctype
, "points"))
1062 cparam
->type
= PPD_CUSTOM_POINTS
;
1063 cparam
->minimum
.custom_points
= (float)_cupsStrScand(cminimum
, NULL
, loc
);
1064 cparam
->maximum
.custom_points
= (float)_cupsStrScand(cmaximum
, NULL
, loc
);
1066 else if (!strcmp(ctype
, "real"))
1068 cparam
->type
= PPD_CUSTOM_REAL
;
1069 cparam
->minimum
.custom_real
= (float)_cupsStrScand(cminimum
, NULL
, loc
);
1070 cparam
->maximum
.custom_real
= (float)_cupsStrScand(cmaximum
, NULL
, loc
);
1072 else if (!strcmp(ctype
, "string"))
1074 cparam
->type
= PPD_CUSTOM_STRING
;
1075 cparam
->minimum
.custom_string
= atoi(cminimum
);
1076 cparam
->maximum
.custom_string
= atoi(cmaximum
);
1080 pg
->ppd_status
= PPD_BAD_CUSTOM_PARAM
;
1086 * Now special-case for CustomPageSize...
1089 if (!strcmp(coption
->keyword
, "PageSize"))
1091 if (!strcmp(name
, "Width"))
1093 ppd
->custom_min
[0] = cparam
->minimum
.custom_points
;
1094 ppd
->custom_max
[0] = cparam
->maximum
.custom_points
;
1096 else if (!strcmp(name
, "Height"))
1098 ppd
->custom_min
[1] = cparam
->minimum
.custom_points
;
1099 ppd
->custom_max
[1] = cparam
->maximum
.custom_points
;
1103 else if (!strcmp(keyword
, "HWMargins"))
1105 for (i
= 0, sptr
= string
; i
< 4; i
++)
1106 ppd
->custom_margins
[i
] = (float)_cupsStrScand(sptr
, &sptr
, loc
);
1108 else if (!strncmp(keyword
, "Custom", 6) && !strcmp(name
, "True") && !option
)
1110 ppd_option_t
*custom_option
; /* Custom option */
1112 DEBUG_puts("2_ppdOpen: Processing Custom option...");
1115 * Get the option and custom option...
1118 if (!ppd_get_coption(ppd
, keyword
+ 6))
1120 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1125 if (option
&& !_cups_strcasecmp(option
->keyword
, keyword
+ 6))
1126 custom_option
= option
;
1128 custom_option
= ppdFindOption(ppd
, keyword
+ 6);
1133 * Add the "custom" option...
1136 if ((choice
= ppdFindChoice(custom_option
, "Custom")) == NULL
)
1137 if ((choice
= ppd_add_choice(custom_option
, "Custom")) == NULL
)
1139 DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
1141 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1146 strlcpy(choice
->text
, text
[0] ? text
: _("Custom"),
1147 sizeof(choice
->text
));
1149 choice
->code
= _cupsStrAlloc(string
);
1151 if (custom_option
->section
== PPD_ORDER_JCL
)
1152 ppd_decode(choice
->code
);
1156 * Now process custom page sizes specially...
1159 if (!strcmp(keyword
, "CustomPageSize"))
1162 * Add a "Custom" page size entry...
1165 ppd
->variable_sizes
= 1;
1167 ppd_add_size(ppd
, "Custom");
1169 if (option
&& !_cups_strcasecmp(option
->keyword
, "PageRegion"))
1170 custom_option
= option
;
1172 custom_option
= ppdFindOption(ppd
, "PageRegion");
1176 if ((choice
= ppdFindChoice(custom_option
, "Custom")) == NULL
)
1177 if ((choice
= ppd_add_choice(custom_option
, "Custom")) == NULL
)
1179 DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
1181 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1186 strlcpy(choice
->text
, text
[0] ? text
: _("Custom"),
1187 sizeof(choice
->text
));
1191 else if (!strcmp(keyword
, "LandscapeOrientation"))
1193 if (!strcmp(string
, "Minus90"))
1194 ppd
->landscape
= -90;
1195 else if (!strcmp(string
, "Plus90"))
1196 ppd
->landscape
= 90;
1198 else if (!strcmp(keyword
, "Emulators") && string
)
1200 for (count
= 1, sptr
= string
; sptr
!= NULL
;)
1201 if ((sptr
= strchr(sptr
, ' ')) != NULL
)
1204 while (*sptr
== ' ')
1208 ppd
->num_emulations
= count
;
1209 if ((ppd
->emulations
= calloc((size_t)count
, sizeof(ppd_emul_t
))) == NULL
)
1211 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1216 for (i
= 0, sptr
= string
; i
< count
; i
++)
1218 for (nameptr
= ppd
->emulations
[i
].name
;
1219 *sptr
!= '\0' && *sptr
!= ' ';
1221 if (nameptr
< (ppd
->emulations
[i
].name
+ sizeof(ppd
->emulations
[i
].name
) - 1))
1226 while (*sptr
== ' ')
1230 else if (!strncmp(keyword
, "StartEmulator_", 14))
1234 for (i
= 0; i
< ppd
->num_emulations
; i
++)
1235 if (!strcmp(keyword
+ 14, ppd
->emulations
[i
].name
))
1237 ppd
->emulations
[i
].start
= string
;
1241 else if (!strncmp(keyword
, "StopEmulator_", 13))
1245 for (i
= 0; i
< ppd
->num_emulations
; i
++)
1246 if (!strcmp(keyword
+ 13, ppd
->emulations
[i
].name
))
1248 ppd
->emulations
[i
].stop
= string
;
1252 else if (!strcmp(keyword
, "JobPatchFile"))
1255 * CUPS STR #3421: Check for "*JobPatchFile: int: string"
1258 if (isdigit(*string
& 255))
1260 for (sptr
= string
+ 1; isdigit(*sptr
& 255); sptr
++);
1265 * Found "*JobPatchFile: int: string"...
1268 pg
->ppd_status
= PPD_BAD_VALUE
;
1274 if (!name
[0] && pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1277 * Found "*JobPatchFile: string"...
1280 pg
->ppd_status
= PPD_MISSING_OPTION_KEYWORD
;
1285 if (ppd
->patches
== NULL
)
1286 ppd
->patches
= strdup(string
);
1289 temp
= realloc(ppd
->patches
, strlen(ppd
->patches
) +
1290 strlen(string
) + 1);
1293 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1298 ppd
->patches
= temp
;
1300 memcpy(ppd
->patches
+ strlen(ppd
->patches
), string
, strlen(string
) + 1);
1303 else if (!strcmp(keyword
, "OpenUI"))
1306 * Don't allow nesting of options...
1309 if (option
&& pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1311 pg
->ppd_status
= PPD_NESTED_OPEN_UI
;
1317 * Add an option record to the current sub-group, group, or file...
1320 DEBUG_printf(("2_ppdOpen: name=\"%s\" (%d)", name
, (int)strlen(name
)));
1323 _cups_strcpy(name
, name
+ 1); /* Eliminate leading asterisk */
1325 for (i
= (int)strlen(name
) - 1; i
> 0 && _cups_isspace(name
[i
]); i
--)
1326 name
[i
] = '\0'; /* Eliminate trailing spaces */
1328 DEBUG_printf(("2_ppdOpen: OpenUI of %s in group %s...", name
,
1329 group
? group
->text
: "(null)"));
1331 if (subgroup
!= NULL
)
1332 option
= ppd_get_option(subgroup
, name
);
1333 else if (group
== NULL
)
1335 if ((group
= ppd_get_group(ppd
, "General", _("General"), pg
,
1339 DEBUG_printf(("2_ppdOpen: Adding to group %s...", group
->text
));
1340 option
= ppd_get_option(group
, name
);
1344 option
= ppd_get_option(group
, name
);
1348 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1354 * Now fill in the initial information for the option...
1357 if (string
&& !strcmp(string
, "PickMany"))
1358 option
->ui
= PPD_UI_PICKMANY
;
1359 else if (string
&& !strcmp(string
, "Boolean"))
1360 option
->ui
= PPD_UI_BOOLEAN
;
1361 else if (string
&& !strcmp(string
, "PickOne"))
1362 option
->ui
= PPD_UI_PICKONE
;
1363 else if (pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1365 pg
->ppd_status
= PPD_BAD_OPEN_UI
;
1370 option
->ui
= PPD_UI_PICKONE
;
1372 for (j
= 0; j
< ppd
->num_attrs
; j
++)
1373 if (!strncmp(ppd
->attrs
[j
]->name
, "Default", 7) &&
1374 !strcmp(ppd
->attrs
[j
]->name
+ 7, name
) &&
1375 ppd
->attrs
[j
]->value
)
1377 DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...",
1378 option
->keyword
, ppd
->attrs
[j
]->value
));
1379 strlcpy(option
->defchoice
, ppd
->attrs
[j
]->value
,
1380 sizeof(option
->defchoice
));
1385 cupsCharsetToUTF8((cups_utf8_t
*)option
->text
, text
,
1386 sizeof(option
->text
), encoding
);
1389 if (!strcmp(name
, "PageSize"))
1390 strlcpy(option
->text
, _("Media Size"), sizeof(option
->text
));
1391 else if (!strcmp(name
, "MediaType"))
1392 strlcpy(option
->text
, _("Media Type"), sizeof(option
->text
));
1393 else if (!strcmp(name
, "InputSlot"))
1394 strlcpy(option
->text
, _("Media Source"), sizeof(option
->text
));
1395 else if (!strcmp(name
, "ColorModel"))
1396 strlcpy(option
->text
, _("Output Mode"), sizeof(option
->text
));
1397 else if (!strcmp(name
, "Resolution"))
1398 strlcpy(option
->text
, _("Resolution"), sizeof(option
->text
));
1400 strlcpy(option
->text
, name
, sizeof(option
->text
));
1403 option
->section
= PPD_ORDER_ANY
;
1405 _cupsStrFree(string
);
1409 * Add a custom option choice if we have already seen a CustomFoo
1413 if (!_cups_strcasecmp(name
, "PageRegion"))
1414 strlcpy(custom_name
, "CustomPageSize", sizeof(custom_name
));
1416 snprintf(custom_name
, sizeof(custom_name
), "Custom%s", name
);
1418 if ((custom_attr
= ppdFindAttr(ppd
, custom_name
, "True")) != NULL
)
1420 if ((choice
= ppdFindChoice(option
, "Custom")) == NULL
)
1421 if ((choice
= ppd_add_choice(option
, "Custom")) == NULL
)
1423 DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
1425 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1430 strlcpy(choice
->text
,
1431 custom_attr
->text
[0] ? custom_attr
->text
: _("Custom"),
1432 sizeof(choice
->text
));
1433 choice
->code
= _cupsStrRetain(custom_attr
->value
);
1436 else if (!strcmp(keyword
, "JCLOpenUI"))
1439 * Don't allow nesting of options...
1442 if (option
&& pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1444 pg
->ppd_status
= PPD_NESTED_OPEN_UI
;
1450 * Find the JCL group, and add if needed...
1453 group
= ppd_get_group(ppd
, "JCL", _("JCL"), pg
, encoding
);
1459 * Add an option record to the current JCLs...
1463 _cups_strcpy(name
, name
+ 1);
1465 option
= ppd_get_option(group
, name
);
1469 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1475 * Now fill in the initial information for the option...
1478 if (string
&& !strcmp(string
, "PickMany"))
1479 option
->ui
= PPD_UI_PICKMANY
;
1480 else if (string
&& !strcmp(string
, "Boolean"))
1481 option
->ui
= PPD_UI_BOOLEAN
;
1482 else if (string
&& !strcmp(string
, "PickOne"))
1483 option
->ui
= PPD_UI_PICKONE
;
1486 pg
->ppd_status
= PPD_BAD_OPEN_UI
;
1491 for (j
= 0; j
< ppd
->num_attrs
; j
++)
1492 if (!strncmp(ppd
->attrs
[j
]->name
, "Default", 7) &&
1493 !strcmp(ppd
->attrs
[j
]->name
+ 7, name
) &&
1494 ppd
->attrs
[j
]->value
)
1496 DEBUG_printf(("2_ppdOpen: Setting Default%s to %s via attribute...",
1497 option
->keyword
, ppd
->attrs
[j
]->value
));
1498 strlcpy(option
->defchoice
, ppd
->attrs
[j
]->value
,
1499 sizeof(option
->defchoice
));
1504 cupsCharsetToUTF8((cups_utf8_t
*)option
->text
, text
,
1505 sizeof(option
->text
), encoding
);
1507 strlcpy(option
->text
, name
, sizeof(option
->text
));
1509 option
->section
= PPD_ORDER_JCL
;
1512 _cupsStrFree(string
);
1516 * Add a custom option choice if we have already seen a CustomFoo
1520 snprintf(custom_name
, sizeof(custom_name
), "Custom%s", name
);
1522 if ((custom_attr
= ppdFindAttr(ppd
, custom_name
, "True")) != NULL
)
1524 if ((choice
= ppd_add_choice(option
, "Custom")) == NULL
)
1526 DEBUG_puts("1_ppdOpen: Unable to add Custom choice!");
1528 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1533 strlcpy(choice
->text
,
1534 custom_attr
->text
[0] ? custom_attr
->text
: _("Custom"),
1535 sizeof(choice
->text
));
1536 choice
->code
= _cupsStrRetain(custom_attr
->value
);
1539 else if (!strcmp(keyword
, "CloseUI") || !strcmp(keyword
, "JCLCloseUI"))
1543 _cupsStrFree(string
);
1546 else if (!strcmp(keyword
, "OpenGroup"))
1549 * Open a new group...
1554 pg
->ppd_status
= PPD_NESTED_OPEN_GROUP
;
1561 pg
->ppd_status
= PPD_BAD_OPEN_GROUP
;
1567 * Separate the group name from the text (name/text)...
1570 if ((sptr
= strchr(string
, '/')) != NULL
)
1576 * Fix up the text...
1582 * Find/add the group...
1585 group
= ppd_get_group(ppd
, string
, sptr
, pg
, encoding
);
1590 _cupsStrFree(string
);
1593 else if (!strcmp(keyword
, "CloseGroup"))
1597 _cupsStrFree(string
);
1600 else if (!strcmp(keyword
, "OrderDependency"))
1602 order
= (float)_cupsStrScand(string
, &sptr
, loc
);
1604 if (!sptr
|| sscanf(sptr
, "%40s%40s", name
, keyword
) != 2)
1606 pg
->ppd_status
= PPD_BAD_ORDER_DEPENDENCY
;
1611 if (keyword
[0] == '*')
1612 _cups_strcpy(keyword
, keyword
+ 1);
1614 if (!strcmp(name
, "ExitServer"))
1615 section
= PPD_ORDER_EXIT
;
1616 else if (!strcmp(name
, "Prolog"))
1617 section
= PPD_ORDER_PROLOG
;
1618 else if (!strcmp(name
, "DocumentSetup"))
1619 section
= PPD_ORDER_DOCUMENT
;
1620 else if (!strcmp(name
, "PageSetup"))
1621 section
= PPD_ORDER_PAGE
;
1622 else if (!strcmp(name
, "JCLSetup"))
1623 section
= PPD_ORDER_JCL
;
1625 section
= PPD_ORDER_ANY
;
1633 * Only valid for Non-UI options...
1636 for (i
= ppd
->num_groups
, gtemp
= ppd
->groups
; i
> 0; i
--, gtemp
++)
1637 if (gtemp
->text
[0] == '\0')
1641 for (i
= 0; i
< gtemp
->num_options
; i
++)
1642 if (!strcmp(keyword
, gtemp
->options
[i
].keyword
))
1644 gtemp
->options
[i
].section
= section
;
1645 gtemp
->options
[i
].order
= order
;
1651 option
->section
= section
;
1652 option
->order
= order
;
1655 _cupsStrFree(string
);
1658 else if (!strncmp(keyword
, "Default", 7))
1664 * Drop UI text, if any, from value...
1667 if (strchr(string
, '/') != NULL
)
1668 *strchr(string
, '/') = '\0';
1671 * Assign the default value as appropriate...
1674 if (!strcmp(keyword
, "DefaultColorSpace"))
1677 * Set default colorspace...
1680 if (!strcmp(string
, "CMY"))
1681 ppd
->colorspace
= PPD_CS_CMY
;
1682 else if (!strcmp(string
, "CMYK"))
1683 ppd
->colorspace
= PPD_CS_CMYK
;
1684 else if (!strcmp(string
, "RGB"))
1685 ppd
->colorspace
= PPD_CS_RGB
;
1686 else if (!strcmp(string
, "RGBK"))
1687 ppd
->colorspace
= PPD_CS_RGBK
;
1688 else if (!strcmp(string
, "N"))
1689 ppd
->colorspace
= PPD_CS_N
;
1691 ppd
->colorspace
= PPD_CS_GRAY
;
1693 else if (option
&& !strcmp(keyword
+ 7, option
->keyword
))
1696 * Set the default as part of the current option...
1699 DEBUG_printf(("2_ppdOpen: Setting %s to %s...", keyword
, string
));
1701 strlcpy(option
->defchoice
, string
, sizeof(option
->defchoice
));
1703 DEBUG_printf(("2_ppdOpen: %s is now %s...", keyword
, option
->defchoice
));
1708 * Lookup option and set if it has been defined...
1711 ppd_option_t
*toption
; /* Temporary option */
1714 if ((toption
= ppdFindOption(ppd
, keyword
+ 7)) != NULL
)
1716 DEBUG_printf(("2_ppdOpen: Setting %s to %s...", keyword
, string
));
1717 strlcpy(toption
->defchoice
, string
, sizeof(toption
->defchoice
));
1721 else if (!strcmp(keyword
, "UIConstraints") ||
1722 !strcmp(keyword
, "NonUIConstraints"))
1726 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1730 if (ppd
->num_consts
== 0)
1731 constraint
= calloc(2, sizeof(ppd_const_t
));
1733 constraint
= realloc(ppd
->consts
, (size_t)(ppd
->num_consts
+ 2) * sizeof(ppd_const_t
));
1735 if (constraint
== NULL
)
1737 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1742 ppd
->consts
= constraint
;
1743 constraint
+= ppd
->num_consts
;
1746 switch (sscanf(string
, "%40s%40s%40s%40s", constraint
->option1
,
1747 constraint
->choice1
, constraint
->option2
,
1748 constraint
->choice2
))
1750 case 0 : /* Error */
1751 case 1 : /* Error */
1752 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1755 case 2 : /* Two options... */
1757 * Check for broken constraints like "* Option"...
1760 if (pg
->ppd_conform
== PPD_CONFORM_STRICT
&&
1761 (!strcmp(constraint
->option1
, "*") ||
1762 !strcmp(constraint
->choice1
, "*")))
1764 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1769 * The following strcpy's are safe, as optionN and
1770 * choiceN are all the same size (size defined by PPD spec...)
1773 if (constraint
->option1
[0] == '*')
1774 _cups_strcpy(constraint
->option1
, constraint
->option1
+ 1);
1775 else if (pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1777 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1781 if (constraint
->choice1
[0] == '*')
1782 _cups_strcpy(constraint
->option2
, constraint
->choice1
+ 1);
1783 else if (pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1785 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1789 constraint
->choice1
[0] = '\0';
1790 constraint
->choice2
[0] = '\0';
1793 case 3 : /* Two options, one choice... */
1795 * Check for broken constraints like "* Option"...
1798 if (pg
->ppd_conform
== PPD_CONFORM_STRICT
&&
1799 (!strcmp(constraint
->option1
, "*") ||
1800 !strcmp(constraint
->choice1
, "*") ||
1801 !strcmp(constraint
->option2
, "*")))
1803 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1808 * The following _cups_strcpy's are safe, as optionN and
1809 * choiceN are all the same size (size defined by PPD spec...)
1812 if (constraint
->option1
[0] == '*')
1813 _cups_strcpy(constraint
->option1
, constraint
->option1
+ 1);
1814 else if (pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1816 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1820 if (constraint
->choice1
[0] == '*')
1822 if (pg
->ppd_conform
== PPD_CONFORM_STRICT
&&
1823 constraint
->option2
[0] == '*')
1825 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1829 _cups_strcpy(constraint
->choice2
, constraint
->option2
);
1830 _cups_strcpy(constraint
->option2
, constraint
->choice1
+ 1);
1831 constraint
->choice1
[0] = '\0';
1835 if (constraint
->option2
[0] == '*')
1836 _cups_strcpy(constraint
->option2
, constraint
->option2
+ 1);
1837 else if (pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1839 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1843 constraint
->choice2
[0] = '\0';
1847 case 4 : /* Two options, two choices... */
1849 * Check for broken constraints like "* Option"...
1852 if (pg
->ppd_conform
== PPD_CONFORM_STRICT
&&
1853 (!strcmp(constraint
->option1
, "*") ||
1854 !strcmp(constraint
->choice1
, "*") ||
1855 !strcmp(constraint
->option2
, "*") ||
1856 !strcmp(constraint
->choice2
, "*")))
1858 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1862 if (constraint
->option1
[0] == '*')
1863 _cups_strcpy(constraint
->option1
, constraint
->option1
+ 1);
1864 else if (pg
->ppd_conform
== PPD_CONFORM_STRICT
)
1866 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1870 if (pg
->ppd_conform
== PPD_CONFORM_STRICT
&&
1871 constraint
->choice1
[0] == '*')
1873 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1877 if (constraint
->option2
[0] == '*')
1878 _cups_strcpy(constraint
->option2
, constraint
->option2
+ 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
->choice2
[0] == '*')
1888 pg
->ppd_status
= PPD_BAD_UI_CONSTRAINTS
;
1895 * Don't add this one as an attribute...
1898 _cupsStrFree(string
);
1901 else if (!strcmp(keyword
, "PaperDimension"))
1903 if ((size
= ppdPageSize(ppd
, name
)) == NULL
)
1904 size
= ppd_add_size(ppd
, name
);
1909 * Unable to add or find size!
1912 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1917 size
->width
= (float)_cupsStrScand(string
, &sptr
, loc
);
1918 size
->length
= (float)_cupsStrScand(sptr
, NULL
, loc
);
1920 _cupsStrFree(string
);
1923 else if (!strcmp(keyword
, "ImageableArea"))
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
->left
= (float)_cupsStrScand(string
, &sptr
, loc
);
1940 size
->bottom
= (float)_cupsStrScand(sptr
, &sptr
, loc
);
1941 size
->right
= (float)_cupsStrScand(sptr
, &sptr
, loc
);
1942 size
->top
= (float)_cupsStrScand(sptr
, NULL
, loc
);
1944 _cupsStrFree(string
);
1947 else if (option
!= NULL
&&
1948 (mask
& (PPD_KEYWORD
| PPD_OPTION
| PPD_STRING
)) ==
1949 (PPD_KEYWORD
| PPD_OPTION
| PPD_STRING
) &&
1950 !strcmp(keyword
, option
->keyword
))
1952 DEBUG_printf(("2_ppdOpen: group=%p, subgroup=%p", group
, subgroup
));
1954 if (!strcmp(keyword
, "PageSize"))
1957 * Add a page size...
1960 if (ppdPageSize(ppd
, name
) == NULL
)
1961 ppd_add_size(ppd
, name
);
1965 * Add the option choice...
1968 if ((choice
= ppd_add_choice(option
, name
)) == NULL
)
1970 pg
->ppd_status
= PPD_ALLOC_ERROR
;
1976 cupsCharsetToUTF8((cups_utf8_t
*)choice
->text
, text
,
1977 sizeof(choice
->text
), encoding
);
1978 else if (!strcmp(name
, "True"))
1979 strlcpy(choice
->text
, _("Yes"), sizeof(choice
->text
));
1980 else if (!strcmp(name
, "False"))
1981 strlcpy(choice
->text
, _("No"), sizeof(choice
->text
));
1983 strlcpy(choice
->text
, name
, sizeof(choice
->text
));
1985 if (option
->section
== PPD_ORDER_JCL
)
1986 ppd_decode(string
); /* Decode quoted string */
1988 choice
->code
= string
;
1989 string
= NULL
; /* Don't add as an attribute below */
1993 * Add remaining lines with keywords and string values as attributes...
1997 (mask
& (PPD_KEYWORD
| PPD_STRING
)) == (PPD_KEYWORD
| PPD_STRING
))
1998 ppd_add_attr(ppd
, keyword
, name
, text
, string
);
2000 _cupsStrFree(string
);
2004 * Check for a missing CloseGroup...
2007 if (group
&& pg
->ppd_conform
== PPD_CONFORM_STRICT
)
2009 pg
->ppd_status
= PPD_MISSING_CLOSE_GROUP
;
2013 ppd_free(line
.buffer
);
2016 * Reset language preferences...
2020 if (!cupsFileEOF(fp
))
2021 DEBUG_printf(("1_ppdOpen: Premature EOF at %lu...\n",
2022 (unsigned long)cupsFileTell(fp
)));
2025 if (pg
->ppd_status
!= PPD_OK
)
2028 * Had an error reading the PPD file, cannot continue!
2037 * Update the filters array as needed...
2040 if (!ppd_update_filters(ppd
, pg
))
2048 * Create the sorted options array and set the option back-pointer for
2049 * each choice and custom option...
2052 ppd
->options
= cupsArrayNew2((cups_array_func_t
)ppd_compare_options
, NULL
,
2053 (cups_ahash_func_t
)ppd_hash_option
,
2056 for (i
= ppd
->num_groups
, group
= ppd
->groups
;
2060 for (j
= group
->num_options
, option
= group
->options
;
2064 ppd_coption_t
*coption
; /* Custom option */
2067 cupsArrayAdd(ppd
->options
, option
);
2069 for (k
= 0; k
< option
->num_choices
; k
++)
2070 option
->choices
[k
].option
= option
;
2072 if ((coption
= ppdFindCustomOption(ppd
, option
->keyword
)) != NULL
)
2073 coption
->option
= option
;
2078 * Create an array to track the marked choices...
2081 ppd
->marked
= cupsArrayNew((cups_array_func_t
)ppd_compare_choices
, NULL
);
2084 * Return the PPD file structure...
2090 * Common exit point for errors to save code size...
2095 _cupsStrFree(string
);
2096 ppd_free(line
.buffer
);
2105 * 'ppdOpen()' - Read a PPD file into memory.
2108 ppd_file_t
* /* O - PPD file record */
2109 ppdOpen(FILE *fp
) /* I - File to read from */
2111 ppd_file_t
*ppd
; /* PPD file record */
2112 cups_file_t
*cf
; /* CUPS file */
2116 * Reopen the stdio file as a CUPS file...
2119 if ((cf
= cupsFileOpenFd(fileno(fp
), "r")) == NULL
)
2123 * Load the PPD file using the newer API...
2126 ppd
= _ppdOpen(cf
, _PPD_LOCALIZATION_DEFAULT
);
2129 * Close the CUPS file and return the PPD...
2139 * 'ppdOpen2()' - Read a PPD file into memory.
2141 * @since CUPS 1.2/macOS 10.5@
2144 ppd_file_t
* /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
2145 ppdOpen2(cups_file_t
*fp
) /* I - File to read from */
2147 return _ppdOpen(fp
, _PPD_LOCALIZATION_DEFAULT
);
2152 * 'ppdOpenFd()' - Read a PPD file into memory.
2155 ppd_file_t
* /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
2156 ppdOpenFd(int fd
) /* I - File to read from */
2158 cups_file_t
*fp
; /* CUPS file pointer */
2159 ppd_file_t
*ppd
; /* PPD file record */
2160 _ppd_globals_t
*pg
= _ppdGlobals();
2165 * Set the line number to 0...
2171 * Range check input...
2176 pg
->ppd_status
= PPD_NULL_FILE
;
2182 * Try to open the file and parse it...
2185 if ((fp
= cupsFileOpenFd(fd
, "r")) != NULL
)
2193 pg
->ppd_status
= PPD_FILE_OPEN_ERROR
;
2202 * '_ppdOpenFile()' - Read a PPD file into memory.
2205 ppd_file_t
* /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
2206 _ppdOpenFile(const char *filename
, /* I - File to read from */
2207 _ppd_localization_t localization
) /* I - Localization to load */
2209 cups_file_t
*fp
; /* File pointer */
2210 ppd_file_t
*ppd
; /* PPD file record */
2211 _ppd_globals_t
*pg
= _ppdGlobals();
2216 * Set the line number to 0...
2222 * Range check input...
2225 if (filename
== NULL
)
2227 pg
->ppd_status
= PPD_NULL_FILE
;
2233 * Try to open the file and parse it...
2236 if ((fp
= cupsFileOpen(filename
, "r")) != NULL
)
2238 ppd
= _ppdOpen(fp
, localization
);
2244 pg
->ppd_status
= PPD_FILE_OPEN_ERROR
;
2253 * 'ppdOpenFile()' - Read a PPD file into memory.
2256 ppd_file_t
* /* O - PPD file record or @code NULL@ if the PPD file could not be opened. */
2257 ppdOpenFile(const char *filename
) /* I - File to read from */
2259 return _ppdOpenFile(filename
, _PPD_LOCALIZATION_DEFAULT
);
2264 * 'ppdSetConformance()' - Set the conformance level for PPD files.
2266 * @since CUPS 1.1.20/macOS 10.4@
2270 ppdSetConformance(ppd_conform_t c
) /* I - Conformance level */
2272 _ppd_globals_t
*pg
= _ppdGlobals();
2276 pg
->ppd_conform
= c
;
2281 * 'ppd_add_attr()' - Add an attribute to the PPD data.
2284 static ppd_attr_t
* /* O - New attribute */
2285 ppd_add_attr(ppd_file_t
*ppd
, /* I - PPD file data */
2286 const char *name
, /* I - Attribute name */
2287 const char *spec
, /* I - Specifier string, if any */
2288 const char *text
, /* I - Text string, if any */
2289 const char *value
) /* I - Value of attribute */
2291 ppd_attr_t
**ptr
, /* New array */
2292 *temp
; /* New attribute */
2296 * Range check input...
2299 if (ppd
== NULL
|| name
== NULL
|| spec
== NULL
)
2303 * Create the array as needed...
2306 if (!ppd
->sorted_attrs
)
2307 ppd
->sorted_attrs
= cupsArrayNew((cups_array_func_t
)ppd_compare_attrs
,
2311 * Allocate memory for the new attribute...
2314 if (ppd
->num_attrs
== 0)
2315 ptr
= malloc(sizeof(ppd_attr_t
*));
2317 ptr
= realloc(ppd
->attrs
, (size_t)(ppd
->num_attrs
+ 1) * sizeof(ppd_attr_t
*));
2323 ptr
+= ppd
->num_attrs
;
2325 if ((temp
= calloc(1, sizeof(ppd_attr_t
))) == NULL
)
2336 strlcpy(temp
->name
, name
, sizeof(temp
->name
));
2337 strlcpy(temp
->spec
, spec
, sizeof(temp
->spec
));
2338 strlcpy(temp
->text
, text
, sizeof(temp
->text
));
2339 temp
->value
= (char *)value
;
2342 * Add the attribute to the sorted array...
2345 cupsArrayAdd(ppd
->sorted_attrs
, temp
);
2348 * Return the attribute...
2356 * 'ppd_add_choice()' - Add a choice to an option.
2359 static ppd_choice_t
* /* O - Named choice */
2360 ppd_add_choice(ppd_option_t
*option
, /* I - Option */
2361 const char *name
) /* I - Name of choice */
2363 ppd_choice_t
*choice
; /* Choice */
2366 if (option
->num_choices
== 0)
2367 choice
= malloc(sizeof(ppd_choice_t
));
2369 choice
= realloc(option
->choices
, sizeof(ppd_choice_t
) * (size_t)(option
->num_choices
+ 1));
2374 option
->choices
= choice
;
2375 choice
+= option
->num_choices
;
2376 option
->num_choices
++;
2378 memset(choice
, 0, sizeof(ppd_choice_t
));
2379 strlcpy(choice
->choice
, name
, sizeof(choice
->choice
));
2386 * 'ppd_add_size()' - Add a page size.
2389 static ppd_size_t
* /* O - Named size */
2390 ppd_add_size(ppd_file_t
*ppd
, /* I - PPD file */
2391 const char *name
) /* I - Name of size */
2393 ppd_size_t
*size
; /* Size */
2396 if (ppd
->num_sizes
== 0)
2397 size
= malloc(sizeof(ppd_size_t
));
2399 size
= realloc(ppd
->sizes
, sizeof(ppd_size_t
) * (size_t)(ppd
->num_sizes
+ 1));
2405 size
+= ppd
->num_sizes
;
2408 memset(size
, 0, sizeof(ppd_size_t
));
2409 strlcpy(size
->name
, name
, sizeof(size
->name
));
2416 * 'ppd_compare_attrs()' - Compare two attributes.
2419 static int /* O - Result of comparison */
2420 ppd_compare_attrs(ppd_attr_t
*a
, /* I - First attribute */
2421 ppd_attr_t
*b
) /* I - Second attribute */
2423 return (_cups_strcasecmp(a
->name
, b
->name
));
2428 * 'ppd_compare_choices()' - Compare two choices...
2431 static int /* O - Result of comparison */
2432 ppd_compare_choices(ppd_choice_t
*a
, /* I - First choice */
2433 ppd_choice_t
*b
) /* I - Second choice */
2435 return (strcmp(a
->option
->keyword
, b
->option
->keyword
));
2440 * 'ppd_compare_coptions()' - Compare two custom options.
2443 static int /* O - Result of comparison */
2444 ppd_compare_coptions(ppd_coption_t
*a
, /* I - First option */
2445 ppd_coption_t
*b
) /* I - Second option */
2447 return (_cups_strcasecmp(a
->keyword
, b
->keyword
));
2452 * 'ppd_compare_options()' - Compare two options.
2455 static int /* O - Result of comparison */
2456 ppd_compare_options(ppd_option_t
*a
, /* I - First option */
2457 ppd_option_t
*b
) /* I - Second option */
2459 return (_cups_strcasecmp(a
->keyword
, b
->keyword
));
2464 * 'ppd_decode()' - Decode a string value...
2467 static int /* O - Length of decoded string */
2468 ppd_decode(char *string
) /* I - String to decode */
2470 char *inptr
, /* Input pointer */
2471 *outptr
; /* Output pointer */
2477 while (*inptr
!= '\0')
2478 if (*inptr
== '<' && isxdigit(inptr
[1] & 255))
2481 * Convert hex to 8-bit values...
2485 while (isxdigit(*inptr
& 255))
2487 if (_cups_isalpha(*inptr
))
2488 *outptr
= (char)((tolower(*inptr
) - 'a' + 10) << 4);
2490 *outptr
= (char)((*inptr
- '0') << 4);
2494 if (!isxdigit(*inptr
& 255))
2497 if (_cups_isalpha(*inptr
))
2498 *outptr
|= (char)(tolower(*inptr
) - 'a' + 10);
2500 *outptr
|= (char)(*inptr
- '0');
2506 while (*inptr
!= '>' && *inptr
!= '\0')
2508 while (*inptr
== '>')
2512 *outptr
++ = *inptr
++;
2516 return ((int)(outptr
- string
));
2521 * 'ppd_free_filters()' - Free the filters array.
2525 ppd_free_filters(ppd_file_t
*ppd
) /* I - PPD file */
2527 int i
; /* Looping var */
2528 char **filter
; /* Current filter */
2531 if (ppd
->num_filters
> 0)
2533 for (i
= ppd
->num_filters
, filter
= ppd
->filters
; i
> 0; i
--, filter
++)
2534 _cupsStrFree(*filter
);
2536 ppd_free(ppd
->filters
);
2538 ppd
->num_filters
= 0;
2539 ppd
->filters
= NULL
;
2545 * 'ppd_free_group()' - Free a single UI group.
2549 ppd_free_group(ppd_group_t
*group
) /* I - Group to free */
2551 int i
; /* Looping var */
2552 ppd_option_t
*option
; /* Current option */
2553 ppd_group_t
*subgroup
; /* Current sub-group */
2556 if (group
->num_options
> 0)
2558 for (i
= group
->num_options
, option
= group
->options
;
2561 ppd_free_option(option
);
2563 ppd_free(group
->options
);
2566 if (group
->num_subgroups
> 0)
2568 for (i
= group
->num_subgroups
, subgroup
= group
->subgroups
;
2571 ppd_free_group(subgroup
);
2573 ppd_free(group
->subgroups
);
2579 * 'ppd_free_option()' - Free a single option.
2583 ppd_free_option(ppd_option_t
*option
) /* I - Option to free */
2585 int i
; /* Looping var */
2586 ppd_choice_t
*choice
; /* Current choice */
2589 if (option
->num_choices
> 0)
2591 for (i
= option
->num_choices
, choice
= option
->choices
;
2595 _cupsStrFree(choice
->code
);
2598 ppd_free(option
->choices
);
2604 * 'ppd_get_coption()' - Get a custom option record.
2607 static ppd_coption_t
* /* O - Custom option... */
2608 ppd_get_coption(ppd_file_t
*ppd
, /* I - PPD file */
2609 const char *name
) /* I - Name of option */
2611 ppd_coption_t
*copt
; /* New custom option */
2615 * See if the option already exists...
2618 if ((copt
= ppdFindCustomOption(ppd
, name
)) != NULL
)
2622 * Not found, so create the custom option record...
2625 if ((copt
= calloc(1, sizeof(ppd_coption_t
))) == NULL
)
2628 strlcpy(copt
->keyword
, name
, sizeof(copt
->keyword
));
2630 copt
->params
= cupsArrayNew((cups_array_func_t
)NULL
, NULL
);
2632 cupsArrayAdd(ppd
->coptions
, copt
);
2635 * Return the new record...
2643 * 'ppd_get_cparam()' - Get a custom parameter record.
2646 static ppd_cparam_t
* /* O - Extended option... */
2647 ppd_get_cparam(ppd_coption_t
*opt
, /* I - PPD file */
2648 const char *param
, /* I - Name of parameter */
2649 const char *text
) /* I - Human-readable text */
2651 ppd_cparam_t
*cparam
; /* New custom parameter */
2655 * See if the parameter already exists...
2658 if ((cparam
= ppdFindCustomParam(opt
, param
)) != NULL
)
2662 * Not found, so create the custom parameter record...
2665 if ((cparam
= calloc(1, sizeof(ppd_cparam_t
))) == NULL
)
2668 strlcpy(cparam
->name
, param
, sizeof(cparam
->name
));
2669 strlcpy(cparam
->text
, text
[0] ? text
: param
, sizeof(cparam
->text
));
2672 * Add this record to the array...
2675 cupsArrayAdd(opt
->params
, cparam
);
2678 * Return the new record...
2686 * 'ppd_get_group()' - Find or create the named group as needed.
2689 static ppd_group_t
* /* O - Named group */
2690 ppd_get_group(ppd_file_t
*ppd
, /* I - PPD file */
2691 const char *name
, /* I - Name of group */
2692 const char *text
, /* I - Text for group */
2693 _ppd_globals_t
*pg
, /* I - Global data */
2694 cups_encoding_t encoding
) /* I - Encoding of text */
2696 int i
; /* Looping var */
2697 ppd_group_t
*group
; /* Group */
2700 DEBUG_printf(("7ppd_get_group(ppd=%p, name=\"%s\", text=\"%s\", cg=%p)",
2701 ppd
, name
, text
, pg
));
2703 for (i
= ppd
->num_groups
, group
= ppd
->groups
; i
> 0; i
--, group
++)
2704 if (!strcmp(group
->name
, name
))
2709 DEBUG_printf(("8ppd_get_group: Adding group %s...", name
));
2711 if (pg
->ppd_conform
== PPD_CONFORM_STRICT
&& strlen(text
) >= sizeof(group
->text
))
2713 pg
->ppd_status
= PPD_ILLEGAL_TRANSLATION
;
2718 if (ppd
->num_groups
== 0)
2719 group
= malloc(sizeof(ppd_group_t
));
2721 group
= realloc(ppd
->groups
, (size_t)(ppd
->num_groups
+ 1) * sizeof(ppd_group_t
));
2725 pg
->ppd_status
= PPD_ALLOC_ERROR
;
2730 ppd
->groups
= group
;
2731 group
+= ppd
->num_groups
;
2734 memset(group
, 0, sizeof(ppd_group_t
));
2735 strlcpy(group
->name
, name
, sizeof(group
->name
));
2737 cupsCharsetToUTF8((cups_utf8_t
*)group
->text
, text
,
2738 sizeof(group
->text
), encoding
);
2746 * 'ppd_get_option()' - Find or create the named option as needed.
2749 static ppd_option_t
* /* O - Named option */
2750 ppd_get_option(ppd_group_t
*group
, /* I - Group */
2751 const char *name
) /* I - Name of option */
2753 int i
; /* Looping var */
2754 ppd_option_t
*option
; /* Option */
2757 DEBUG_printf(("7ppd_get_option(group=%p(\"%s\"), name=\"%s\")",
2758 group
, group
->name
, name
));
2760 for (i
= group
->num_options
, option
= group
->options
; i
> 0; i
--, option
++)
2761 if (!strcmp(option
->keyword
, name
))
2766 if (group
->num_options
== 0)
2767 option
= malloc(sizeof(ppd_option_t
));
2769 option
= realloc(group
->options
, (size_t)(group
->num_options
+ 1) * sizeof(ppd_option_t
));
2774 group
->options
= option
;
2775 option
+= group
->num_options
;
2776 group
->num_options
++;
2778 memset(option
, 0, sizeof(ppd_option_t
));
2779 strlcpy(option
->keyword
, name
, sizeof(option
->keyword
));
2787 * 'ppd_globals_alloc()' - Allocate and initialize global data.
2790 static _ppd_globals_t
* /* O - Pointer to global data */
2791 ppd_globals_alloc(void)
2793 return ((_ppd_globals_t
*)calloc(1, sizeof(_ppd_globals_t
)));
2798 * 'ppd_globals_free()' - Free global data.
2801 #if defined(HAVE_PTHREAD_H) || defined(WIN32)
2803 ppd_globals_free(_ppd_globals_t
*pg
) /* I - Pointer to global data */
2807 #endif /* HAVE_PTHREAD_H || WIN32 */
2810 #ifdef HAVE_PTHREAD_H
2812 * 'ppd_globals_init()' - Initialize per-thread globals...
2816 ppd_globals_init(void)
2819 * Register the global data for this thread...
2822 pthread_key_create(&ppd_globals_key
, (void (*)(void *))ppd_globals_free
);
2824 #endif /* HAVE_PTHREAD_H */
2828 * 'ppd_hash_option()' - Generate a hash of the option name...
2831 static int /* O - Hash index */
2832 ppd_hash_option(ppd_option_t
*option
) /* I - Option */
2834 int hash
= 0; /* Hash index */
2835 const char *k
; /* Pointer into keyword */
2838 for (hash
= option
->keyword
[0], k
= option
->keyword
+ 1; *k
;)
2839 hash
= 33 * hash
+ *k
++;
2841 return (hash
& 511);
2846 * 'ppd_read()' - Read a line from a PPD file, skipping comment lines as
2850 static int /* O - Bitmask of fields read */
2851 ppd_read(cups_file_t
*fp
, /* I - File to read from */
2852 _ppd_line_t
*line
, /* I - Line buffer */
2853 char *keyword
, /* O - Keyword from line */
2854 char *option
, /* O - Option from line */
2855 char *text
, /* O - Human-readable text from line */
2856 char **string
, /* O - Code/string data */
2857 int ignoreblank
, /* I - Ignore blank lines? */
2858 _ppd_globals_t
*pg
) /* I - Global data */
2860 int ch
, /* Character from file */
2861 col
, /* Column in line */
2862 colon
, /* Colon seen? */
2863 endquote
, /* Waiting for an end quote */
2864 mask
, /* Mask to be returned */
2865 startline
, /* Start line */
2866 textlen
; /* Length of text */
2867 char *keyptr
, /* Keyword pointer */
2868 *optptr
, /* Option pointer */
2869 *textptr
, /* Text pointer */
2870 *strptr
, /* Pointer into string */
2871 *lineptr
; /* Current position in line buffer */
2875 * Now loop until we have a valid line...
2880 startline
= pg
->ppd_line
+ 1;
2884 line
->bufsize
= 1024;
2885 line
->buffer
= malloc(1024);
2897 lineptr
= line
->buffer
;
2901 while ((ch
= cupsFileGetChar(fp
)) != EOF
)
2903 if (lineptr
>= (line
->buffer
+ line
->bufsize
- 1))
2906 * Expand the line buffer...
2909 char *temp
; /* Temporary line pointer */
2912 line
->bufsize
+= 1024;
2913 if (line
->bufsize
> 262144)
2916 * Don't allow lines longer than 256k!
2919 pg
->ppd_line
= startline
;
2920 pg
->ppd_status
= PPD_LINE_TOO_LONG
;
2925 temp
= realloc(line
->buffer
, line
->bufsize
);
2928 pg
->ppd_line
= startline
;
2929 pg
->ppd_status
= PPD_LINE_TOO_LONG
;
2934 lineptr
= temp
+ (lineptr
- line
->buffer
);
2935 line
->buffer
= temp
;
2938 if (ch
== '\r' || ch
== '\n')
2941 * Line feed or carriage return...
2950 * Check for a trailing line feed...
2953 if ((ch
= cupsFilePeekChar(fp
)) == EOF
)
2960 cupsFileGetChar(fp
);
2963 if (lineptr
== line
->buffer
&& ignoreblank
)
2964 continue; /* Skip blank lines */
2968 if (!endquote
) /* Continue for multi-line text */
2973 else if (ch
< ' ' && ch
!= '\t' && pg
->ppd_conform
== PPD_CONFORM_STRICT
)
2976 * Other control characters...
2979 pg
->ppd_line
= startline
;
2980 pg
->ppd_status
= PPD_ILLEGAL_CHARACTER
;
2984 else if (ch
!= 0x1a)
2987 * Any other character...
2990 *lineptr
++ = (char)ch
;
2993 if (col
> (PPD_MAX_LINE
- 1))
2996 * Line is too long...
2999 pg
->ppd_line
= startline
;
3000 pg
->ppd_status
= PPD_LINE_TOO_LONG
;
3005 if (ch
== ':' && strncmp(line
->buffer
, "*%", 2) != 0)
3008 if (ch
== '\"' && colon
)
3009 endquote
= !endquote
;
3016 * Didn't finish this quoted string...
3019 while ((ch
= cupsFileGetChar(fp
)) != EOF
)
3022 else if (ch
== '\r' || ch
== '\n')
3030 * Check for a trailing line feed...
3033 if ((ch
= cupsFilePeekChar(fp
)) == EOF
)
3036 cupsFileGetChar(fp
);
3039 else if (ch
< ' ' && ch
!= '\t' && pg
->ppd_conform
== PPD_CONFORM_STRICT
)
3042 * Other control characters...
3045 pg
->ppd_line
= startline
;
3046 pg
->ppd_status
= PPD_ILLEGAL_CHARACTER
;
3050 else if (ch
!= 0x1a)
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
;
3071 * Didn't finish this line...
3074 while ((ch
= cupsFileGetChar(fp
)) != EOF
)
3075 if (ch
== '\r' || ch
== '\n')
3078 * Line feed or carriage return...
3087 * Check for a trailing line feed...
3090 if ((ch
= cupsFilePeekChar(fp
)) == EOF
)
3093 cupsFileGetChar(fp
);
3098 else if (ch
< ' ' && ch
!= '\t' && pg
->ppd_conform
== PPD_CONFORM_STRICT
)
3101 * Other control characters...
3104 pg
->ppd_line
= startline
;
3105 pg
->ppd_status
= PPD_ILLEGAL_CHARACTER
;
3109 else if (ch
!= 0x1a)
3113 if (col
> (PPD_MAX_LINE
- 1))
3116 * Line is too long...
3119 pg
->ppd_line
= startline
;
3120 pg
->ppd_status
= PPD_LINE_TOO_LONG
;
3127 if (lineptr
> line
->buffer
&& lineptr
[-1] == '\n')
3132 DEBUG_printf(("9ppd_read: LINE=\"%s\"", line
->buffer
));
3135 * The dynamically created PPDs for older style macOS
3136 * drivers include a large blob of data inserted as comments
3137 * at the end of the file. As an optimization we can stop
3138 * reading the PPD when we get to the start of this data.
3141 if (!strcmp(line
->buffer
, "*%APLWORKSET START"))
3144 if (ch
== EOF
&& lineptr
== line
->buffer
)
3152 lineptr
= line
->buffer
+ 1;
3159 if ((!line
->buffer
[0] || /* Blank line */
3160 !strncmp(line
->buffer
, "*%", 2) || /* Comment line */
3161 !strcmp(line
->buffer
, "*End")) && /* End of multi-line string */
3162 ignoreblank
) /* Ignore these? */
3164 startline
= pg
->ppd_line
+ 1;
3168 if (!strcmp(line
->buffer
, "*")) /* (Bad) comment line */
3170 if (pg
->ppd_conform
== PPD_CONFORM_RELAXED
)
3172 startline
= pg
->ppd_line
+ 1;
3177 pg
->ppd_line
= startline
;
3178 pg
->ppd_status
= PPD_ILLEGAL_MAIN_KEYWORD
;
3184 if (line
->buffer
[0] != '*') /* All lines start with an asterisk */
3187 * Allow lines consisting of just whitespace...
3190 for (lineptr
= line
->buffer
; *lineptr
; lineptr
++)
3191 if (*lineptr
&& !_cups_isspace(*lineptr
))
3196 pg
->ppd_status
= PPD_MISSING_ASTERISK
;
3199 else if (ignoreblank
)
3211 while (*lineptr
&& *lineptr
!= ':' && !_cups_isspace(*lineptr
))
3213 if (*lineptr
<= ' ' || *lineptr
> 126 || *lineptr
== '/' ||
3214 (keyptr
- keyword
) >= (PPD_MAX_NAME
- 1))
3216 pg
->ppd_status
= PPD_ILLEGAL_MAIN_KEYWORD
;
3220 *keyptr
++ = *lineptr
++;
3225 if (!strcmp(keyword
, "End"))
3228 mask
|= PPD_KEYWORD
;
3230 if (_cups_isspace(*lineptr
))
3233 * Get an option name...
3236 while (_cups_isspace(*lineptr
))
3241 while (*lineptr
&& !_cups_isspace(*lineptr
) && *lineptr
!= ':' &&
3244 if (*lineptr
<= ' ' || *lineptr
> 126 ||
3245 (optptr
- option
) >= (PPD_MAX_NAME
- 1))
3247 pg
->ppd_status
= PPD_ILLEGAL_OPTION_KEYWORD
;
3251 *optptr
++ = *lineptr
++;
3256 if (_cups_isspace(*lineptr
) && pg
->ppd_conform
== PPD_CONFORM_STRICT
)
3258 pg
->ppd_status
= PPD_ILLEGAL_WHITESPACE
;
3262 while (_cups_isspace(*lineptr
))
3267 if (*lineptr
== '/')
3270 * Get human-readable text...
3277 while (*lineptr
!= '\0' && *lineptr
!= '\n' && *lineptr
!= ':')
3279 if (((unsigned char)*lineptr
< ' ' && *lineptr
!= '\t') ||
3280 (textptr
- text
) >= (PPD_MAX_LINE
- 1))
3282 pg
->ppd_status
= PPD_ILLEGAL_TRANSLATION
;
3286 *textptr
++ = *lineptr
++;
3290 textlen
= ppd_decode(text
);
3292 if (textlen
> PPD_MAX_TEXT
&& pg
->ppd_conform
== PPD_CONFORM_STRICT
)
3294 pg
->ppd_status
= PPD_ILLEGAL_TRANSLATION
;
3302 if (_cups_isspace(*lineptr
) && pg
->ppd_conform
== PPD_CONFORM_STRICT
)
3304 pg
->ppd_status
= PPD_ILLEGAL_WHITESPACE
;
3308 while (_cups_isspace(*lineptr
))
3311 if (*lineptr
== ':')
3314 * Get string after triming leading and trailing whitespace...
3318 while (_cups_isspace(*lineptr
))
3321 strptr
= lineptr
+ strlen(lineptr
) - 1;
3322 while (strptr
>= lineptr
&& _cups_isspace(*strptr
))
3325 if (*strptr
== '\"')
3328 * Quoted string by itself, remove quotes...
3335 *string
= _cupsStrAlloc(lineptr
);
3347 * 'ppd_update_filters()' - Update the filters array as needed.
3349 * This function re-populates the filters array with cupsFilter2 entries that
3350 * have been stripped of the destination MIME media types and any maxsize hints.
3352 * (All for backwards-compatibility)
3355 static int /* O - 1 on success, 0 on failure */
3356 ppd_update_filters(ppd_file_t
*ppd
, /* I - PPD file */
3357 _ppd_globals_t
*pg
) /* I - Global data */
3359 ppd_attr_t
*attr
; /* Current cupsFilter2 value */
3360 char srcsuper
[16], /* Source MIME media type */
3362 dstsuper
[16], /* Destination MIME media type */
3364 program
[1024], /* Command to run */
3365 *ptr
, /* Pointer into command to run */
3366 buffer
[1024], /* Re-written cupsFilter value */
3367 **filter
; /* Current filter */
3368 int cost
; /* Cost of filter */
3371 DEBUG_printf(("4ppd_update_filters(ppd=%p, cg=%p)", ppd
, pg
));
3374 * See if we have any cupsFilter2 lines...
3377 if ((attr
= ppdFindAttr(ppd
, "cupsFilter2", NULL
)) == NULL
)
3379 DEBUG_puts("5ppd_update_filters: No cupsFilter2 keywords present.");
3384 * Yes, free the cupsFilter-defined filters and re-build...
3387 ppd_free_filters(ppd
);
3392 * Parse the cupsFilter2 string:
3394 * src/type dst/type cost program
3395 * src/type dst/type cost maxsize(n) program
3398 DEBUG_printf(("5ppd_update_filters: cupsFilter2=\"%s\"", attr
->value
));
3400 if (sscanf(attr
->value
, "%15[^/]/%255s%*[ \t]%15[^/]/%255s%d%*[ \t]%1023[^\n]",
3401 srcsuper
, srctype
, dstsuper
, dsttype
, &cost
, program
) != 6)
3403 DEBUG_puts("5ppd_update_filters: Bad cupsFilter2 line.");
3404 pg
->ppd_status
= PPD_BAD_VALUE
;
3409 DEBUG_printf(("5ppd_update_filters: srcsuper=\"%s\", srctype=\"%s\", "
3410 "dstsuper=\"%s\", dsttype=\"%s\", cost=%d, program=\"%s\"",
3411 srcsuper
, srctype
, dstsuper
, dsttype
, cost
, program
));
3413 if (!strncmp(program
, "maxsize(", 8) &&
3414 (ptr
= strchr(program
+ 8, ')')) != NULL
)
3416 DEBUG_puts("5ppd_update_filters: Found maxsize(nnn).");
3419 while (_cups_isspace(*ptr
))
3422 _cups_strcpy(program
, ptr
);
3423 DEBUG_printf(("5ppd_update_filters: New program=\"%s\"", program
));
3427 * Convert to cupsFilter format:
3429 * src/type cost program
3432 snprintf(buffer
, sizeof(buffer
), "%s/%s %d %s", srcsuper
, srctype
, cost
,
3434 DEBUG_printf(("5ppd_update_filters: Adding \"%s\".", buffer
));
3437 * Add a cupsFilter-compatible string to the filters array.
3440 if (ppd
->num_filters
== 0)
3441 filter
= malloc(sizeof(char *));
3443 filter
= realloc(ppd
->filters
, sizeof(char *) * (size_t)(ppd
->num_filters
+ 1));
3447 DEBUG_puts("5ppd_update_filters: Out of memory.");
3448 pg
->ppd_status
= PPD_ALLOC_ERROR
;
3453 ppd
->filters
= filter
;
3454 filter
+= ppd
->num_filters
;
3455 ppd
->num_filters
++;
3457 *filter
= _cupsStrAlloc(buffer
);
3459 while ((attr
= ppdFindNextAttr(ppd
, "cupsFilter2", NULL
)) != NULL
);
3461 DEBUG_puts("5ppd_update_filters: Completed OK.");