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