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