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