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