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