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