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