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