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