]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/ppd.c
53e9fbf5ac23709bdc885c1c135c5ef13e5df7bb
[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 (!_cups_strcasecmp(name, "ISOLatin1"))
381 return (CUPS_ISO8859_1);
382 else if (!_cups_strcasecmp(name, "ISOLatin2"))
383 return (CUPS_ISO8859_2);
384 else if (!_cups_strcasecmp(name, "ISOLatin5"))
385 return (CUPS_ISO8859_5);
386 else if (!_cups_strcasecmp(name, "JIS83-RKSJ"))
387 return (CUPS_JIS_X0213);
388 else if (!_cups_strcasecmp(name, "MacStandard"))
389 return (CUPS_MAC_ROMAN);
390 else if (!_cups_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 struct lconv *loc; /* Locale data */
487 int ui_keyword; /* Is this line a UI keyword? */
488 cups_encoding_t encoding; /* Encoding of PPD file */
489 _cups_globals_t *cg = _cupsGlobals();
490 /* Global data */
491 char custom_name[PPD_MAX_NAME];
492 /* CustomFoo attribute name */
493 ppd_attr_t *custom_attr; /* CustomFoo attribute */
494 static const char * const ui_keywords[] =
495 {
496 #ifdef CUPS_USE_FULL_UI_KEYWORDS_LIST
497 /*
498 * Adobe defines some 41 keywords as "UI", meaning that they are
499 * user interface elements and that they should be treated as such
500 * even if the PPD creator doesn't use Open/CloseUI around them.
501 *
502 * Since this can cause previously invisible options to appear and
503 * confuse users, the default is to only treat the PageSize and
504 * PageRegion keywords this way.
505 */
506 /* Boolean keywords */
507 "BlackSubstitution",
508 "Booklet",
509 "Collate",
510 "ManualFeed",
511 "MirrorPrint",
512 "NegativePrint",
513 "Sorter",
514 "TraySwitch",
515
516 /* PickOne keywords */
517 "AdvanceMedia",
518 "BindColor",
519 "BindEdge",
520 "BindType",
521 "BindWhen",
522 "BitsPerPixel",
523 "ColorModel",
524 "CutMedia",
525 "Duplex",
526 "FoldType",
527 "FoldWhen",
528 "InputSlot",
529 "JCLFrameBufferSize",
530 "JCLResolution",
531 "Jog",
532 "MediaColor",
533 "MediaType",
534 "MediaWeight",
535 "OutputBin",
536 "OutputMode",
537 "OutputOrder",
538 "PageRegion",
539 "PageSize",
540 "Resolution",
541 "Separations",
542 "Signature",
543 "Slipsheet",
544 "Smoothing",
545 "StapleLocation",
546 "StapleOrientation",
547 "StapleWhen",
548 "StapleX",
549 "StapleY"
550 #else /* !CUPS_USE_FULL_UI_KEYWORDS_LIST */
551 "PageRegion",
552 "PageSize"
553 #endif /* CUPS_USE_FULL_UI_KEYWORDS_LIST */
554 };
555
556
557 DEBUG_printf(("ppdOpen2(fp=%p)", fp));
558
559 /*
560 * Default to "OK" status...
561 */
562
563 cg->ppd_status = PPD_OK;
564 cg->ppd_line = 0;
565
566 /*
567 * Range check input...
568 */
569
570 if (fp == NULL)
571 {
572 cg->ppd_status = PPD_NULL_FILE;
573 return (NULL);
574 }
575
576 /*
577 * Grab the first line and make sure it reads '*PPD-Adobe: "major.minor"'...
578 */
579
580 line.buffer = NULL;
581 line.bufsize = 0;
582
583 mask = ppd_read(fp, &line, keyword, name, text, &string, 0, cg);
584
585 DEBUG_printf(("2ppdOpen2: mask=%x, keyword=\"%s\"...", mask, keyword));
586
587 if (mask == 0 ||
588 strcmp(keyword, "PPD-Adobe") ||
589 string == NULL || string[0] != '4')
590 {
591 /*
592 * Either this is not a PPD file, or it is not a 4.x PPD file.
593 */
594
595 if (cg->ppd_status == PPD_OK)
596 cg->ppd_status = PPD_MISSING_PPDADOBE4;
597
598 _cupsStrFree(string);
599 ppd_free(line.buffer);
600
601 return (NULL);
602 }
603
604 DEBUG_printf(("2ppdOpen2: keyword=%s, string=%p", keyword, string));
605
606 _cupsStrFree(string);
607
608 /*
609 * Allocate memory for the PPD file record...
610 */
611
612 if ((ppd = calloc(1, sizeof(ppd_file_t))) == NULL)
613 {
614 cg->ppd_status = PPD_ALLOC_ERROR;
615
616 _cupsStrFree(string);
617 ppd_free(line.buffer);
618
619 return (NULL);
620 }
621
622 ppd->language_level = 2;
623 ppd->color_device = 0;
624 ppd->colorspace = PPD_CS_N;
625 ppd->landscape = -90;
626 ppd->coptions = cupsArrayNew((cups_array_func_t)ppd_compare_coptions,
627 NULL);
628
629 /*
630 * Read lines from the PPD file and add them to the file record...
631 */
632
633 group = NULL;
634 subgroup = NULL;
635 option = NULL;
636 choice = NULL;
637 ui_keyword = 0;
638 encoding = CUPS_ISO8859_1;
639 loc = localeconv();
640
641 while ((mask = ppd_read(fp, &line, keyword, name, text, &string, 1, cg)) != 0)
642 {
643 DEBUG_printf(("2ppdOpen2: mask=%x, keyword=\"%s\", name=\"%s\", "
644 "text=\"%s\", string=%d chars...", mask, keyword, name, text,
645 string ? (int)strlen(string) : 0));
646
647 if (strncmp(keyword, "Default", 7) && !string &&
648 cg->ppd_conform != PPD_CONFORM_RELAXED)
649 {
650 /*
651 * Need a string value!
652 */
653
654 cg->ppd_status = PPD_MISSING_VALUE;
655
656 goto error;
657 }
658 else if (!string)
659 continue;
660
661 /*
662 * Certain main keywords (as defined by the PPD spec) may be used
663 * without the usual OpenUI/CloseUI stuff. Presumably this is just
664 * so that Adobe wouldn't completely break compatibility with PPD
665 * files prior to v4.0 of the spec, but it is hopelessly
666 * inconsistent... Catch these main keywords and automatically
667 * create the corresponding option, as needed...
668 */
669
670 if (ui_keyword)
671 {
672 /*
673 * Previous line was a UI keyword...
674 */
675
676 option = NULL;
677 ui_keyword = 0;
678 }
679
680 if (option == NULL &&
681 (mask & (PPD_KEYWORD | PPD_OPTION | PPD_STRING)) ==
682 (PPD_KEYWORD | PPD_OPTION | PPD_STRING))
683 {
684 for (i = 0; i < (int)(sizeof(ui_keywords) / sizeof(ui_keywords[0])); i ++)
685 if (!strcmp(keyword, ui_keywords[i]))
686 break;
687
688 if (i < (int)(sizeof(ui_keywords) / sizeof(ui_keywords[0])))
689 {
690 /*
691 * Create the option in the appropriate group...
692 */
693
694 ui_keyword = 1;
695
696 DEBUG_printf(("2ppdOpen2: FOUND ADOBE UI KEYWORD %s WITHOUT OPENUI!",
697 keyword));
698
699 if (!group)
700 {
701 if ((group = ppd_get_group(ppd, "General", _("General"), cg,
702 encoding)) == NULL)
703 goto error;
704
705 DEBUG_printf(("2ppdOpen2: Adding to group %s...", group->text));
706 option = ppd_get_option(group, keyword);
707 group = NULL;
708 }
709 else
710 option = ppd_get_option(group, keyword);
711
712 if (option == NULL)
713 {
714 cg->ppd_status = PPD_ALLOC_ERROR;
715
716 goto error;
717 }
718
719 /*
720 * Now fill in the initial information for the option...
721 */
722
723 if (!strncmp(keyword, "JCL", 3))
724 option->section = PPD_ORDER_JCL;
725 else
726 option->section = PPD_ORDER_ANY;
727
728 option->order = 10.0f;
729
730 if (i < 8)
731 option->ui = PPD_UI_BOOLEAN;
732 else
733 option->ui = PPD_UI_PICKONE;
734
735 for (j = 0; j < ppd->num_attrs; j ++)
736 if (!strncmp(ppd->attrs[j]->name, "Default", 7) &&
737 !strcmp(ppd->attrs[j]->name + 7, keyword) &&
738 ppd->attrs[j]->value)
739 {
740 DEBUG_printf(("2ppdOpen2: Setting Default%s to %s via attribute...",
741 option->keyword, ppd->attrs[j]->value));
742 strlcpy(option->defchoice, ppd->attrs[j]->value,
743 sizeof(option->defchoice));
744 break;
745 }
746
747 if (!strcmp(keyword, "PageSize"))
748 strlcpy(option->text, _("Media Size"), sizeof(option->text));
749 else if (!strcmp(keyword, "MediaType"))
750 strlcpy(option->text, _("Media Type"), sizeof(option->text));
751 else if (!strcmp(keyword, "InputSlot"))
752 strlcpy(option->text, _("Media Source"), sizeof(option->text));
753 else if (!strcmp(keyword, "ColorModel"))
754 strlcpy(option->text, _("Output Mode"), sizeof(option->text));
755 else if (!strcmp(keyword, "Resolution"))
756 strlcpy(option->text, _("Resolution"), sizeof(option->text));
757 else
758 strlcpy(option->text, keyword, sizeof(option->text));
759 }
760 }
761
762 if (!strcmp(keyword, "LanguageLevel"))
763 ppd->language_level = atoi(string);
764 else if (!strcmp(keyword, "LanguageEncoding"))
765 {
766 /*
767 * Say all PPD files are UTF-8, since we convert to UTF-8...
768 */
769
770 ppd->lang_encoding = _cupsStrAlloc("UTF-8");
771 encoding = _ppdGetEncoding(string);
772 }
773 else if (!strcmp(keyword, "LanguageVersion"))
774 ppd->lang_version = string;
775 else if (!strcmp(keyword, "Manufacturer"))
776 ppd->manufacturer = string;
777 else if (!strcmp(keyword, "ModelName"))
778 ppd->modelname = string;
779 else if (!strcmp(keyword, "Protocols"))
780 ppd->protocols = string;
781 else if (!strcmp(keyword, "PCFileName"))
782 ppd->pcfilename = string;
783 else if (!strcmp(keyword, "NickName"))
784 {
785 if (encoding != CUPS_UTF8)
786 {
787 cups_utf8_t utf8[256]; /* UTF-8 version of NickName */
788
789
790 cupsCharsetToUTF8(utf8, string, sizeof(utf8), encoding);
791 ppd->nickname = _cupsStrAlloc((char *)utf8);
792 }
793 else
794 ppd->nickname = _cupsStrAlloc(string);
795 }
796 else if (!strcmp(keyword, "Product"))
797 ppd->product = string;
798 else if (!strcmp(keyword, "ShortNickName"))
799 ppd->shortnickname = string;
800 else if (!strcmp(keyword, "TTRasterizer"))
801 ppd->ttrasterizer = string;
802 else if (!strcmp(keyword, "JCLBegin"))
803 {
804 ppd->jcl_begin = _cupsStrAlloc(string);
805 ppd_decode(ppd->jcl_begin); /* Decode quoted string */
806 }
807 else if (!strcmp(keyword, "JCLEnd"))
808 {
809 ppd->jcl_end = _cupsStrAlloc(string);
810 ppd_decode(ppd->jcl_end); /* Decode quoted string */
811 }
812 else if (!strcmp(keyword, "JCLToPSInterpreter"))
813 {
814 ppd->jcl_ps = _cupsStrAlloc(string);
815 ppd_decode(ppd->jcl_ps); /* Decode quoted string */
816 }
817 else if (!strcmp(keyword, "AccurateScreensSupport"))
818 ppd->accurate_screens = !strcmp(string, "True");
819 else if (!strcmp(keyword, "ColorDevice"))
820 ppd->color_device = !strcmp(string, "True");
821 else if (!strcmp(keyword, "ContoneOnly"))
822 ppd->contone_only = !strcmp(string, "True");
823 else if (!strcmp(keyword, "cupsFlipDuplex"))
824 ppd->flip_duplex = !strcmp(string, "True");
825 else if (!strcmp(keyword, "cupsManualCopies"))
826 ppd->manual_copies = !strcmp(string, "True");
827 else if (!strcmp(keyword, "cupsModelNumber"))
828 ppd->model_number = atoi(string);
829 else if (!strcmp(keyword, "cupsColorProfile"))
830 {
831 if (ppd->num_profiles == 0)
832 profile = malloc(sizeof(ppd_profile_t));
833 else
834 profile = realloc(ppd->profiles, sizeof(ppd_profile_t) *
835 (ppd->num_profiles + 1));
836
837 if (!profile)
838 {
839 cg->ppd_status = PPD_ALLOC_ERROR;
840
841 goto error;
842 }
843
844 ppd->profiles = profile;
845 profile += ppd->num_profiles;
846 ppd->num_profiles ++;
847
848 memset(profile, 0, sizeof(ppd_profile_t));
849 strlcpy(profile->resolution, name, sizeof(profile->resolution));
850 strlcpy(profile->media_type, text, sizeof(profile->media_type));
851
852 profile->density = (float)_cupsStrScand(string, &sptr, loc);
853 profile->gamma = (float)_cupsStrScand(sptr, &sptr, loc);
854 profile->matrix[0][0] = (float)_cupsStrScand(sptr, &sptr, loc);
855 profile->matrix[0][1] = (float)_cupsStrScand(sptr, &sptr, loc);
856 profile->matrix[0][2] = (float)_cupsStrScand(sptr, &sptr, loc);
857 profile->matrix[1][0] = (float)_cupsStrScand(sptr, &sptr, loc);
858 profile->matrix[1][1] = (float)_cupsStrScand(sptr, &sptr, loc);
859 profile->matrix[1][2] = (float)_cupsStrScand(sptr, &sptr, loc);
860 profile->matrix[2][0] = (float)_cupsStrScand(sptr, &sptr, loc);
861 profile->matrix[2][1] = (float)_cupsStrScand(sptr, &sptr, loc);
862 profile->matrix[2][2] = (float)_cupsStrScand(sptr, &sptr, loc);
863 }
864 else if (!strcmp(keyword, "cupsFilter"))
865 {
866 if (ppd->num_filters == 0)
867 filter = malloc(sizeof(char *));
868 else
869 filter = realloc(ppd->filters, sizeof(char *) * (ppd->num_filters + 1));
870
871 if (filter == NULL)
872 {
873 cg->ppd_status = PPD_ALLOC_ERROR;
874
875 goto error;
876 }
877
878 ppd->filters = filter;
879 filter += ppd->num_filters;
880 ppd->num_filters ++;
881
882 /*
883 * Copy filter string and prevent it from being freed below...
884 */
885
886 *filter = string;
887 string = NULL;
888 }
889 else if (!strcmp(keyword, "Throughput"))
890 ppd->throughput = atoi(string);
891 else if (!strcmp(keyword, "Font"))
892 {
893 /*
894 * Add this font to the list of available fonts...
895 */
896
897 if (ppd->num_fonts == 0)
898 tempfonts = (char **)malloc(sizeof(char *));
899 else
900 tempfonts = (char **)realloc(ppd->fonts,
901 sizeof(char *) * (ppd->num_fonts + 1));
902
903 if (tempfonts == NULL)
904 {
905 cg->ppd_status = PPD_ALLOC_ERROR;
906
907 goto error;
908 }
909
910 ppd->fonts = tempfonts;
911 ppd->fonts[ppd->num_fonts] = _cupsStrAlloc(name);
912 ppd->num_fonts ++;
913 }
914 else if (!strncmp(keyword, "ParamCustom", 11))
915 {
916 ppd_coption_t *coption; /* Custom option */
917 ppd_cparam_t *cparam; /* Custom parameter */
918 int corder; /* Order number */
919 char ctype[33], /* Data type */
920 cminimum[65], /* Minimum value */
921 cmaximum[65]; /* Maximum value */
922
923
924 /*
925 * Get the custom option and parameter...
926 */
927
928 if ((coption = ppd_get_coption(ppd, keyword + 11)) == NULL)
929 {
930 cg->ppd_status = PPD_ALLOC_ERROR;
931
932 goto error;
933 }
934
935 if ((cparam = ppd_get_cparam(coption, name, text)) == NULL)
936 {
937 cg->ppd_status = PPD_ALLOC_ERROR;
938
939 goto error;
940 }
941
942 /*
943 * Get the parameter data...
944 */
945
946 if (!string ||
947 sscanf(string, "%d%32s%64s%64s", &corder, ctype, cminimum,
948 cmaximum) != 4)
949 {
950 cg->ppd_status = PPD_BAD_CUSTOM_PARAM;
951
952 goto error;
953 }
954
955 cparam->order = corder;
956
957 if (!strcmp(ctype, "curve"))
958 {
959 cparam->type = PPD_CUSTOM_CURVE;
960 cparam->minimum.custom_curve = (float)_cupsStrScand(cminimum, NULL, loc);
961 cparam->maximum.custom_curve = (float)_cupsStrScand(cmaximum, NULL, loc);
962 }
963 else if (!strcmp(ctype, "int"))
964 {
965 cparam->type = PPD_CUSTOM_INT;
966 cparam->minimum.custom_int = atoi(cminimum);
967 cparam->maximum.custom_int = atoi(cmaximum);
968 }
969 else if (!strcmp(ctype, "invcurve"))
970 {
971 cparam->type = PPD_CUSTOM_INVCURVE;
972 cparam->minimum.custom_invcurve = (float)_cupsStrScand(cminimum, NULL, loc);
973 cparam->maximum.custom_invcurve = (float)_cupsStrScand(cmaximum, NULL, loc);
974 }
975 else if (!strcmp(ctype, "passcode"))
976 {
977 cparam->type = PPD_CUSTOM_PASSCODE;
978 cparam->minimum.custom_passcode = atoi(cminimum);
979 cparam->maximum.custom_passcode = atoi(cmaximum);
980 }
981 else if (!strcmp(ctype, "password"))
982 {
983 cparam->type = PPD_CUSTOM_PASSWORD;
984 cparam->minimum.custom_password = atoi(cminimum);
985 cparam->maximum.custom_password = atoi(cmaximum);
986 }
987 else if (!strcmp(ctype, "points"))
988 {
989 cparam->type = PPD_CUSTOM_POINTS;
990 cparam->minimum.custom_points = (float)_cupsStrScand(cminimum, NULL, loc);
991 cparam->maximum.custom_points = (float)_cupsStrScand(cmaximum, NULL, loc);
992 }
993 else if (!strcmp(ctype, "real"))
994 {
995 cparam->type = PPD_CUSTOM_REAL;
996 cparam->minimum.custom_real = (float)_cupsStrScand(cminimum, NULL, loc);
997 cparam->maximum.custom_real = (float)_cupsStrScand(cmaximum, NULL, loc);
998 }
999 else if (!strcmp(ctype, "string"))
1000 {
1001 cparam->type = PPD_CUSTOM_STRING;
1002 cparam->minimum.custom_string = atoi(cminimum);
1003 cparam->maximum.custom_string = atoi(cmaximum);
1004 }
1005 else
1006 {
1007 cg->ppd_status = PPD_BAD_CUSTOM_PARAM;
1008
1009 goto error;
1010 }
1011
1012 /*
1013 * Now special-case for CustomPageSize...
1014 */
1015
1016 if (!strcmp(coption->keyword, "PageSize"))
1017 {
1018 if (!strcmp(name, "Width"))
1019 {
1020 ppd->custom_min[0] = cparam->minimum.custom_points;
1021 ppd->custom_max[0] = cparam->maximum.custom_points;
1022 }
1023 else if (!strcmp(name, "Height"))
1024 {
1025 ppd->custom_min[1] = cparam->minimum.custom_points;
1026 ppd->custom_max[1] = cparam->maximum.custom_points;
1027 }
1028 }
1029 }
1030 else if (!strcmp(keyword, "HWMargins"))
1031 {
1032 for (i = 0, sptr = string; i < 4; i ++)
1033 ppd->custom_margins[i] = (float)_cupsStrScand(sptr, &sptr, loc);
1034 }
1035 else if (!strncmp(keyword, "Custom", 6) && !strcmp(name, "True") && !option)
1036 {
1037 ppd_option_t *custom_option; /* Custom option */
1038
1039 DEBUG_puts("2ppdOpen2: Processing Custom option...");
1040
1041 /*
1042 * Get the option and custom option...
1043 */
1044
1045 if (!ppd_get_coption(ppd, keyword + 6))
1046 {
1047 cg->ppd_status = PPD_ALLOC_ERROR;
1048
1049 goto error;
1050 }
1051
1052 if (option && !_cups_strcasecmp(option->keyword, keyword + 6))
1053 custom_option = option;
1054 else
1055 custom_option = ppdFindOption(ppd, keyword + 6);
1056
1057 if (custom_option)
1058 {
1059 /*
1060 * Add the "custom" option...
1061 */
1062
1063 if ((choice = ppdFindChoice(custom_option, "Custom")) == NULL)
1064 if ((choice = ppd_add_choice(custom_option, "Custom")) == NULL)
1065 {
1066 DEBUG_puts("1ppdOpen2: Unable to add Custom choice!");
1067
1068 cg->ppd_status = PPD_ALLOC_ERROR;
1069
1070 goto error;
1071 }
1072
1073 strlcpy(choice->text, text[0] ? text : _("Custom"),
1074 sizeof(choice->text));
1075
1076 choice->code = _cupsStrAlloc(string);
1077
1078 if (custom_option->section == PPD_ORDER_JCL)
1079 ppd_decode(choice->code);
1080 }
1081
1082 /*
1083 * Now process custom page sizes specially...
1084 */
1085
1086 if (!strcmp(keyword, "CustomPageSize"))
1087 {
1088 /*
1089 * Add a "Custom" page size entry...
1090 */
1091
1092 ppd->variable_sizes = 1;
1093
1094 ppd_add_size(ppd, "Custom");
1095
1096 if (option && !_cups_strcasecmp(option->keyword, "PageRegion"))
1097 custom_option = option;
1098 else
1099 custom_option = ppdFindOption(ppd, "PageRegion");
1100
1101 if (custom_option)
1102 {
1103 if ((choice = ppdFindChoice(custom_option, "Custom")) == NULL)
1104 if ((choice = ppd_add_choice(custom_option, "Custom")) == NULL)
1105 {
1106 DEBUG_puts("1ppdOpen2: Unable to add Custom choice!");
1107
1108 cg->ppd_status = PPD_ALLOC_ERROR;
1109
1110 goto error;
1111 }
1112
1113 strlcpy(choice->text, text[0] ? text : _("Custom"),
1114 sizeof(choice->text));
1115 }
1116 }
1117 }
1118 else if (!strcmp(keyword, "LandscapeOrientation"))
1119 {
1120 if (!strcmp(string, "Minus90"))
1121 ppd->landscape = -90;
1122 else if (!strcmp(string, "Plus90"))
1123 ppd->landscape = 90;
1124 }
1125 else if (!strcmp(keyword, "Emulators") && string)
1126 {
1127 for (count = 1, sptr = string; sptr != NULL;)
1128 if ((sptr = strchr(sptr, ' ')) != NULL)
1129 {
1130 count ++;
1131 while (*sptr == ' ')
1132 sptr ++;
1133 }
1134
1135 ppd->num_emulations = count;
1136 if ((ppd->emulations = calloc(count, sizeof(ppd_emul_t))) == NULL)
1137 {
1138 cg->ppd_status = PPD_ALLOC_ERROR;
1139
1140 goto error;
1141 }
1142
1143 for (i = 0, sptr = string; i < count; i ++)
1144 {
1145 for (nameptr = ppd->emulations[i].name;
1146 *sptr != '\0' && *sptr != ' ';
1147 sptr ++)
1148 if (nameptr < (ppd->emulations[i].name + sizeof(ppd->emulations[i].name) - 1))
1149 *nameptr++ = *sptr;
1150
1151 *nameptr = '\0';
1152
1153 while (*sptr == ' ')
1154 sptr ++;
1155 }
1156 }
1157 else if (!strncmp(keyword, "StartEmulator_", 14))
1158 {
1159 ppd_decode(string);
1160
1161 for (i = 0; i < ppd->num_emulations; i ++)
1162 if (!strcmp(keyword + 14, ppd->emulations[i].name))
1163 {
1164 ppd->emulations[i].start = string;
1165 string = NULL;
1166 }
1167 }
1168 else if (!strncmp(keyword, "StopEmulator_", 13))
1169 {
1170 ppd_decode(string);
1171
1172 for (i = 0; i < ppd->num_emulations; i ++)
1173 if (!strcmp(keyword + 13, ppd->emulations[i].name))
1174 {
1175 ppd->emulations[i].stop = string;
1176 string = NULL;
1177 }
1178 }
1179 else if (!strcmp(keyword, "JobPatchFile"))
1180 {
1181 /*
1182 * CUPS STR #3421: Check for "*JobPatchFile: int: string"
1183 */
1184
1185 if (isdigit(*string & 255))
1186 {
1187 for (sptr = string + 1; isdigit(*sptr & 255); sptr ++);
1188
1189 if (*sptr == ':')
1190 {
1191 /*
1192 * Found "*JobPatchFile: int: string"...
1193 */
1194
1195 cg->ppd_status = PPD_BAD_VALUE;
1196
1197 goto error;
1198 }
1199 }
1200
1201 if (!name[0] && cg->ppd_conform == PPD_CONFORM_STRICT)
1202 {
1203 /*
1204 * Found "*JobPatchFile: string"...
1205 */
1206
1207 cg->ppd_status = PPD_MISSING_OPTION_KEYWORD;
1208
1209 goto error;
1210 }
1211
1212 if (ppd->patches == NULL)
1213 ppd->patches = strdup(string);
1214 else
1215 {
1216 temp = realloc(ppd->patches, strlen(ppd->patches) +
1217 strlen(string) + 1);
1218 if (temp == NULL)
1219 {
1220 cg->ppd_status = PPD_ALLOC_ERROR;
1221
1222 goto error;
1223 }
1224
1225 ppd->patches = temp;
1226
1227 strcpy(ppd->patches + strlen(ppd->patches), string);
1228 }
1229 }
1230 else if (!strcmp(keyword, "OpenUI"))
1231 {
1232 /*
1233 * Don't allow nesting of options...
1234 */
1235
1236 if (option && cg->ppd_conform == PPD_CONFORM_STRICT)
1237 {
1238 cg->ppd_status = PPD_NESTED_OPEN_UI;
1239
1240 goto error;
1241 }
1242
1243 /*
1244 * Add an option record to the current sub-group, group, or file...
1245 */
1246
1247 DEBUG_printf(("2ppdOpen2: name=\"%s\" (%d)", name, (int)strlen(name)));
1248
1249 if (name[0] == '*')
1250 _cups_strcpy(name, name + 1); /* Eliminate leading asterisk */
1251
1252 for (i = (int)strlen(name) - 1; i > 0 && _cups_isspace(name[i]); i --)
1253 name[i] = '\0'; /* Eliminate trailing spaces */
1254
1255 DEBUG_printf(("2ppdOpen2: OpenUI of %s in group %s...", name,
1256 group ? group->text : "(null)"));
1257
1258 if (subgroup != NULL)
1259 option = ppd_get_option(subgroup, name);
1260 else if (group == NULL)
1261 {
1262 if ((group = ppd_get_group(ppd, "General", _("General"), cg,
1263 encoding)) == NULL)
1264 goto error;
1265
1266 DEBUG_printf(("2ppdOpen2: Adding to group %s...", group->text));
1267 option = ppd_get_option(group, name);
1268 group = NULL;
1269 }
1270 else
1271 option = ppd_get_option(group, name);
1272
1273 if (option == NULL)
1274 {
1275 cg->ppd_status = PPD_ALLOC_ERROR;
1276
1277 goto error;
1278 }
1279
1280 /*
1281 * Now fill in the initial information for the option...
1282 */
1283
1284 if (string && !strcmp(string, "PickMany"))
1285 option->ui = PPD_UI_PICKMANY;
1286 else if (string && !strcmp(string, "Boolean"))
1287 option->ui = PPD_UI_BOOLEAN;
1288 else if (string && !strcmp(string, "PickOne"))
1289 option->ui = PPD_UI_PICKONE;
1290 else if (cg->ppd_conform == PPD_CONFORM_STRICT)
1291 {
1292 cg->ppd_status = PPD_BAD_OPEN_UI;
1293
1294 goto error;
1295 }
1296 else
1297 option->ui = PPD_UI_PICKONE;
1298
1299 for (j = 0; j < ppd->num_attrs; j ++)
1300 if (!strncmp(ppd->attrs[j]->name, "Default", 7) &&
1301 !strcmp(ppd->attrs[j]->name + 7, name) &&
1302 ppd->attrs[j]->value)
1303 {
1304 DEBUG_printf(("2ppdOpen2: Setting Default%s to %s via attribute...",
1305 option->keyword, ppd->attrs[j]->value));
1306 strlcpy(option->defchoice, ppd->attrs[j]->value,
1307 sizeof(option->defchoice));
1308 break;
1309 }
1310
1311 if (text[0])
1312 cupsCharsetToUTF8((cups_utf8_t *)option->text, text,
1313 sizeof(option->text), encoding);
1314 else
1315 {
1316 if (!strcmp(name, "PageSize"))
1317 strlcpy(option->text, _("Media Size"), sizeof(option->text));
1318 else if (!strcmp(name, "MediaType"))
1319 strlcpy(option->text, _("Media Type"), sizeof(option->text));
1320 else if (!strcmp(name, "InputSlot"))
1321 strlcpy(option->text, _("Media Source"), sizeof(option->text));
1322 else if (!strcmp(name, "ColorModel"))
1323 strlcpy(option->text, _("Output Mode"), sizeof(option->text));
1324 else if (!strcmp(name, "Resolution"))
1325 strlcpy(option->text, _("Resolution"), sizeof(option->text));
1326 else
1327 strlcpy(option->text, name, sizeof(option->text));
1328 }
1329
1330 option->section = PPD_ORDER_ANY;
1331
1332 _cupsStrFree(string);
1333 string = NULL;
1334
1335 /*
1336 * Add a custom option choice if we have already seen a CustomFoo
1337 * attribute...
1338 */
1339
1340 if (!_cups_strcasecmp(name, "PageRegion"))
1341 strcpy(custom_name, "CustomPageSize");
1342 else
1343 snprintf(custom_name, sizeof(custom_name), "Custom%s", name);
1344
1345 if ((custom_attr = ppdFindAttr(ppd, custom_name, "True")) != NULL)
1346 {
1347 if ((choice = ppdFindChoice(option, "Custom")) == NULL)
1348 if ((choice = ppd_add_choice(option, "Custom")) == NULL)
1349 {
1350 DEBUG_puts("1ppdOpen2: Unable to add Custom choice!");
1351
1352 cg->ppd_status = PPD_ALLOC_ERROR;
1353
1354 goto error;
1355 }
1356
1357 strlcpy(choice->text,
1358 custom_attr->text[0] ? custom_attr->text : _("Custom"),
1359 sizeof(choice->text));
1360 choice->code = _cupsStrRetain(custom_attr->value);
1361 }
1362 }
1363 else if (!strcmp(keyword, "JCLOpenUI"))
1364 {
1365 /*
1366 * Don't allow nesting of options...
1367 */
1368
1369 if (option && cg->ppd_conform == PPD_CONFORM_STRICT)
1370 {
1371 cg->ppd_status = PPD_NESTED_OPEN_UI;
1372
1373 goto error;
1374 }
1375
1376 /*
1377 * Find the JCL group, and add if needed...
1378 */
1379
1380 group = ppd_get_group(ppd, "JCL", _("JCL"), cg, encoding);
1381
1382 if (group == NULL)
1383 goto error;
1384
1385 /*
1386 * Add an option record to the current JCLs...
1387 */
1388
1389 if (name[0] == '*')
1390 _cups_strcpy(name, name + 1);
1391
1392 option = ppd_get_option(group, name);
1393
1394 if (option == NULL)
1395 {
1396 cg->ppd_status = PPD_ALLOC_ERROR;
1397
1398 goto error;
1399 }
1400
1401 /*
1402 * Now fill in the initial information for the option...
1403 */
1404
1405 if (string && !strcmp(string, "PickMany"))
1406 option->ui = PPD_UI_PICKMANY;
1407 else if (string && !strcmp(string, "Boolean"))
1408 option->ui = PPD_UI_BOOLEAN;
1409 else if (string && !strcmp(string, "PickOne"))
1410 option->ui = PPD_UI_PICKONE;
1411 else
1412 {
1413 cg->ppd_status = PPD_BAD_OPEN_UI;
1414
1415 goto error;
1416 }
1417
1418 for (j = 0; j < ppd->num_attrs; j ++)
1419 if (!strncmp(ppd->attrs[j]->name, "Default", 7) &&
1420 !strcmp(ppd->attrs[j]->name + 7, name) &&
1421 ppd->attrs[j]->value)
1422 {
1423 DEBUG_printf(("2ppdOpen2: Setting Default%s to %s via attribute...",
1424 option->keyword, ppd->attrs[j]->value));
1425 strlcpy(option->defchoice, ppd->attrs[j]->value,
1426 sizeof(option->defchoice));
1427 break;
1428 }
1429
1430 if (text[0])
1431 cupsCharsetToUTF8((cups_utf8_t *)option->text, text,
1432 sizeof(option->text), encoding);
1433 else
1434 strlcpy(option->text, name, sizeof(option->text));
1435
1436 option->section = PPD_ORDER_JCL;
1437 group = NULL;
1438
1439 _cupsStrFree(string);
1440 string = NULL;
1441
1442 /*
1443 * Add a custom option choice if we have already seen a CustomFoo
1444 * attribute...
1445 */
1446
1447 snprintf(custom_name, sizeof(custom_name), "Custom%s", name);
1448
1449 if ((custom_attr = ppdFindAttr(ppd, custom_name, "True")) != NULL)
1450 {
1451 if ((choice = ppd_add_choice(option, "Custom")) == NULL)
1452 {
1453 DEBUG_puts("1ppdOpen2: Unable to add Custom choice!");
1454
1455 cg->ppd_status = PPD_ALLOC_ERROR;
1456
1457 goto error;
1458 }
1459
1460 strlcpy(choice->text,
1461 custom_attr->text[0] ? custom_attr->text : _("Custom"),
1462 sizeof(choice->text));
1463 choice->code = _cupsStrRetain(custom_attr->value);
1464 }
1465 }
1466 else if (!strcmp(keyword, "CloseUI") || !strcmp(keyword, "JCLCloseUI"))
1467 {
1468 option = NULL;
1469
1470 _cupsStrFree(string);
1471 string = NULL;
1472 }
1473 else if (!strcmp(keyword, "OpenGroup"))
1474 {
1475 /*
1476 * Open a new group...
1477 */
1478
1479 if (group != NULL)
1480 {
1481 cg->ppd_status = PPD_NESTED_OPEN_GROUP;
1482
1483 goto error;
1484 }
1485
1486 if (!string)
1487 {
1488 cg->ppd_status = PPD_BAD_OPEN_GROUP;
1489
1490 goto error;
1491 }
1492
1493 /*
1494 * Separate the group name from the text (name/text)...
1495 */
1496
1497 if ((sptr = strchr(string, '/')) != NULL)
1498 *sptr++ = '\0';
1499 else
1500 sptr = string;
1501
1502 /*
1503 * Fix up the text...
1504 */
1505
1506 ppd_decode(sptr);
1507
1508 /*
1509 * Find/add the group...
1510 */
1511
1512 group = ppd_get_group(ppd, string, sptr, cg, encoding);
1513
1514 if (group == NULL)
1515 goto error;
1516
1517 _cupsStrFree(string);
1518 string = NULL;
1519 }
1520 else if (!strcmp(keyword, "CloseGroup"))
1521 {
1522 group = NULL;
1523
1524 _cupsStrFree(string);
1525 string = NULL;
1526 }
1527 else if (!strcmp(keyword, "OrderDependency"))
1528 {
1529 order = (float)_cupsStrScand(string, &sptr, loc);
1530
1531 if (!sptr || sscanf(sptr, "%40s%40s", name, keyword) != 2)
1532 {
1533 cg->ppd_status = PPD_BAD_ORDER_DEPENDENCY;
1534
1535 goto error;
1536 }
1537
1538 if (keyword[0] == '*')
1539 _cups_strcpy(keyword, keyword + 1);
1540
1541 if (!strcmp(name, "ExitServer"))
1542 section = PPD_ORDER_EXIT;
1543 else if (!strcmp(name, "Prolog"))
1544 section = PPD_ORDER_PROLOG;
1545 else if (!strcmp(name, "DocumentSetup"))
1546 section = PPD_ORDER_DOCUMENT;
1547 else if (!strcmp(name, "PageSetup"))
1548 section = PPD_ORDER_PAGE;
1549 else if (!strcmp(name, "JCLSetup"))
1550 section = PPD_ORDER_JCL;
1551 else
1552 section = PPD_ORDER_ANY;
1553
1554 if (option == NULL)
1555 {
1556 ppd_group_t *gtemp;
1557
1558
1559 /*
1560 * Only valid for Non-UI options...
1561 */
1562
1563 for (i = ppd->num_groups, gtemp = ppd->groups; i > 0; i --, gtemp ++)
1564 if (gtemp->text[0] == '\0')
1565 break;
1566
1567 if (i > 0)
1568 for (i = 0; i < gtemp->num_options; i ++)
1569 if (!strcmp(keyword, gtemp->options[i].keyword))
1570 {
1571 gtemp->options[i].section = section;
1572 gtemp->options[i].order = order;
1573 break;
1574 }
1575 }
1576 else
1577 {
1578 option->section = section;
1579 option->order = order;
1580 }
1581
1582 _cupsStrFree(string);
1583 string = NULL;
1584 }
1585 else if (!strncmp(keyword, "Default", 7))
1586 {
1587 if (string == NULL)
1588 continue;
1589
1590 /*
1591 * Drop UI text, if any, from value...
1592 */
1593
1594 if (strchr(string, '/') != NULL)
1595 *strchr(string, '/') = '\0';
1596
1597 /*
1598 * Assign the default value as appropriate...
1599 */
1600
1601 if (!strcmp(keyword, "DefaultColorSpace"))
1602 {
1603 /*
1604 * Set default colorspace...
1605 */
1606
1607 if (!strcmp(string, "CMY"))
1608 ppd->colorspace = PPD_CS_CMY;
1609 else if (!strcmp(string, "CMYK"))
1610 ppd->colorspace = PPD_CS_CMYK;
1611 else if (!strcmp(string, "RGB"))
1612 ppd->colorspace = PPD_CS_RGB;
1613 else if (!strcmp(string, "RGBK"))
1614 ppd->colorspace = PPD_CS_RGBK;
1615 else if (!strcmp(string, "N"))
1616 ppd->colorspace = PPD_CS_N;
1617 else
1618 ppd->colorspace = PPD_CS_GRAY;
1619 }
1620 else if (option && !strcmp(keyword + 7, option->keyword))
1621 {
1622 /*
1623 * Set the default as part of the current option...
1624 */
1625
1626 DEBUG_printf(("2ppdOpen2: Setting %s to %s...", keyword, string));
1627
1628 strlcpy(option->defchoice, string, sizeof(option->defchoice));
1629
1630 DEBUG_printf(("2ppdOpen2: %s is now %s...", keyword, option->defchoice));
1631 }
1632 else
1633 {
1634 /*
1635 * Lookup option and set if it has been defined...
1636 */
1637
1638 ppd_option_t *toption; /* Temporary option */
1639
1640
1641 if ((toption = ppdFindOption(ppd, keyword + 7)) != NULL)
1642 {
1643 DEBUG_printf(("2ppdOpen2: Setting %s to %s...", keyword, string));
1644 strlcpy(toption->defchoice, string, sizeof(toption->defchoice));
1645 }
1646 }
1647 }
1648 else if (!strcmp(keyword, "UIConstraints") ||
1649 !strcmp(keyword, "NonUIConstraints"))
1650 {
1651 if (!string)
1652 {
1653 cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1654 goto error;
1655 }
1656
1657 if (ppd->num_consts == 0)
1658 constraint = calloc(2, sizeof(ppd_const_t));
1659 else
1660 constraint = realloc(ppd->consts,
1661 (ppd->num_consts + 2) * sizeof(ppd_const_t));
1662
1663 if (constraint == NULL)
1664 {
1665 cg->ppd_status = PPD_ALLOC_ERROR;
1666
1667 goto error;
1668 }
1669
1670 ppd->consts = constraint;
1671 constraint += ppd->num_consts;
1672 ppd->num_consts ++;
1673
1674 switch (sscanf(string, "%40s%40s%40s%40s", constraint->option1,
1675 constraint->choice1, constraint->option2,
1676 constraint->choice2))
1677 {
1678 case 0 : /* Error */
1679 case 1 : /* Error */
1680 cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1681 goto error;
1682
1683 case 2 : /* Two options... */
1684 /*
1685 * Check for broken constraints like "* Option"...
1686 */
1687
1688 if (cg->ppd_conform == PPD_CONFORM_STRICT &&
1689 (!strcmp(constraint->option1, "*") ||
1690 !strcmp(constraint->choice1, "*")))
1691 {
1692 cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1693 goto error;
1694 }
1695
1696 /*
1697 * The following strcpy's are safe, as optionN and
1698 * choiceN are all the same size (size defined by PPD spec...)
1699 */
1700
1701 if (constraint->option1[0] == '*')
1702 _cups_strcpy(constraint->option1, constraint->option1 + 1);
1703 else if (cg->ppd_conform == PPD_CONFORM_STRICT)
1704 {
1705 cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1706 goto error;
1707 }
1708
1709 if (constraint->choice1[0] == '*')
1710 _cups_strcpy(constraint->option2, constraint->choice1 + 1);
1711 else if (cg->ppd_conform == PPD_CONFORM_STRICT)
1712 {
1713 cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1714 goto error;
1715 }
1716
1717 constraint->choice1[0] = '\0';
1718 constraint->choice2[0] = '\0';
1719 break;
1720
1721 case 3 : /* Two options, one choice... */
1722 /*
1723 * Check for broken constraints like "* Option"...
1724 */
1725
1726 if (cg->ppd_conform == PPD_CONFORM_STRICT &&
1727 (!strcmp(constraint->option1, "*") ||
1728 !strcmp(constraint->choice1, "*") ||
1729 !strcmp(constraint->option2, "*")))
1730 {
1731 cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1732 goto error;
1733 }
1734
1735 /*
1736 * The following _cups_strcpy's are safe, as optionN and
1737 * choiceN are all the same size (size defined by PPD spec...)
1738 */
1739
1740 if (constraint->option1[0] == '*')
1741 _cups_strcpy(constraint->option1, constraint->option1 + 1);
1742 else if (cg->ppd_conform == PPD_CONFORM_STRICT)
1743 {
1744 cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1745 goto error;
1746 }
1747
1748 if (constraint->choice1[0] == '*')
1749 {
1750 if (cg->ppd_conform == PPD_CONFORM_STRICT &&
1751 constraint->option2[0] == '*')
1752 {
1753 cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1754 goto error;
1755 }
1756
1757 _cups_strcpy(constraint->choice2, constraint->option2);
1758 _cups_strcpy(constraint->option2, constraint->choice1 + 1);
1759 constraint->choice1[0] = '\0';
1760 }
1761 else
1762 {
1763 if (constraint->option2[0] == '*')
1764 _cups_strcpy(constraint->option2, constraint->option2 + 1);
1765 else if (cg->ppd_conform == PPD_CONFORM_STRICT)
1766 {
1767 cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1768 goto error;
1769 }
1770
1771 constraint->choice2[0] = '\0';
1772 }
1773 break;
1774
1775 case 4 : /* Two options, two choices... */
1776 /*
1777 * Check for broken constraints like "* Option"...
1778 */
1779
1780 if (cg->ppd_conform == PPD_CONFORM_STRICT &&
1781 (!strcmp(constraint->option1, "*") ||
1782 !strcmp(constraint->choice1, "*") ||
1783 !strcmp(constraint->option2, "*") ||
1784 !strcmp(constraint->choice2, "*")))
1785 {
1786 cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1787 goto error;
1788 }
1789
1790 if (constraint->option1[0] == '*')
1791 _cups_strcpy(constraint->option1, constraint->option1 + 1);
1792 else if (cg->ppd_conform == PPD_CONFORM_STRICT)
1793 {
1794 cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1795 goto error;
1796 }
1797
1798 if (cg->ppd_conform == PPD_CONFORM_STRICT &&
1799 constraint->choice1[0] == '*')
1800 {
1801 cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1802 goto error;
1803 }
1804
1805 if (constraint->option2[0] == '*')
1806 _cups_strcpy(constraint->option2, constraint->option2 + 1);
1807 else if (cg->ppd_conform == PPD_CONFORM_STRICT)
1808 {
1809 cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1810 goto error;
1811 }
1812
1813 if (cg->ppd_conform == PPD_CONFORM_STRICT &&
1814 constraint->choice2[0] == '*')
1815 {
1816 cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1817 goto error;
1818 }
1819 break;
1820 }
1821
1822 /*
1823 * Don't add this one as an attribute...
1824 */
1825
1826 _cupsStrFree(string);
1827 string = NULL;
1828 }
1829 else if (!strcmp(keyword, "PaperDimension"))
1830 {
1831 if ((size = ppdPageSize(ppd, name)) == NULL)
1832 size = ppd_add_size(ppd, name);
1833
1834 if (size == NULL)
1835 {
1836 /*
1837 * Unable to add or find size!
1838 */
1839
1840 cg->ppd_status = PPD_ALLOC_ERROR;
1841
1842 goto error;
1843 }
1844
1845 size->width = (float)_cupsStrScand(string, &sptr, loc);
1846 size->length = (float)_cupsStrScand(sptr, NULL, loc);
1847
1848 _cupsStrFree(string);
1849 string = NULL;
1850 }
1851 else if (!strcmp(keyword, "ImageableArea"))
1852 {
1853 if ((size = ppdPageSize(ppd, name)) == NULL)
1854 size = ppd_add_size(ppd, name);
1855
1856 if (size == NULL)
1857 {
1858 /*
1859 * Unable to add or find size!
1860 */
1861
1862 cg->ppd_status = PPD_ALLOC_ERROR;
1863
1864 goto error;
1865 }
1866
1867 size->left = (float)_cupsStrScand(string, &sptr, loc);
1868 size->bottom = (float)_cupsStrScand(sptr, &sptr, loc);
1869 size->right = (float)_cupsStrScand(sptr, &sptr, loc);
1870 size->top = (float)_cupsStrScand(sptr, NULL, loc);
1871
1872 _cupsStrFree(string);
1873 string = NULL;
1874 }
1875 else if (option != NULL &&
1876 (mask & (PPD_KEYWORD | PPD_OPTION | PPD_STRING)) ==
1877 (PPD_KEYWORD | PPD_OPTION | PPD_STRING) &&
1878 !strcmp(keyword, option->keyword))
1879 {
1880 DEBUG_printf(("2ppdOpen2: group=%p, subgroup=%p", group, subgroup));
1881
1882 if (!strcmp(keyword, "PageSize"))
1883 {
1884 /*
1885 * Add a page size...
1886 */
1887
1888 if (ppdPageSize(ppd, name) == NULL)
1889 ppd_add_size(ppd, name);
1890 }
1891
1892 /*
1893 * Add the option choice...
1894 */
1895
1896 if ((choice = ppd_add_choice(option, name)) == NULL)
1897 {
1898 cg->ppd_status = PPD_ALLOC_ERROR;
1899
1900 goto error;
1901 }
1902
1903 if (text[0])
1904 cupsCharsetToUTF8((cups_utf8_t *)choice->text, text,
1905 sizeof(choice->text), encoding);
1906 else if (!strcmp(name, "True"))
1907 strcpy(choice->text, _("Yes"));
1908 else if (!strcmp(name, "False"))
1909 strcpy(choice->text, _("No"));
1910 else
1911 strlcpy(choice->text, name, sizeof(choice->text));
1912
1913 if (option->section == PPD_ORDER_JCL)
1914 ppd_decode(string); /* Decode quoted string */
1915
1916 choice->code = string;
1917 string = NULL; /* Don't add as an attribute below */
1918 }
1919
1920 /*
1921 * Add remaining lines with keywords and string values as attributes...
1922 */
1923
1924 if (string &&
1925 (mask & (PPD_KEYWORD | PPD_STRING)) == (PPD_KEYWORD | PPD_STRING))
1926 ppd_add_attr(ppd, keyword, name, text, string);
1927 else
1928 _cupsStrFree(string);
1929 }
1930
1931 /*
1932 * Check for a missing CloseGroup...
1933 */
1934
1935 if (group && cg->ppd_conform == PPD_CONFORM_STRICT)
1936 {
1937 cg->ppd_status = PPD_MISSING_CLOSE_GROUP;
1938 goto error;
1939 }
1940
1941 ppd_free(line.buffer);
1942
1943 /*
1944 * Reset language preferences...
1945 */
1946
1947 #ifdef DEBUG
1948 if (!cupsFileEOF(fp))
1949 DEBUG_printf(("1ppdOpen2: Premature EOF at %lu...\n",
1950 (unsigned long)cupsFileTell(fp)));
1951 #endif /* DEBUG */
1952
1953 if (cg->ppd_status != PPD_OK)
1954 {
1955 /*
1956 * Had an error reading the PPD file, cannot continue!
1957 */
1958
1959 ppdClose(ppd);
1960
1961 return (NULL);
1962 }
1963
1964 /*
1965 * Create the sorted options array and set the option back-pointer for
1966 * each choice and custom option...
1967 */
1968
1969 ppd->options = cupsArrayNew2((cups_array_func_t)ppd_compare_options, NULL,
1970 (cups_ahash_func_t)ppd_hash_option,
1971 PPD_HASHSIZE);
1972
1973 for (i = ppd->num_groups, group = ppd->groups;
1974 i > 0;
1975 i --, group ++)
1976 {
1977 for (j = group->num_options, option = group->options;
1978 j > 0;
1979 j --, option ++)
1980 {
1981 ppd_coption_t *coption; /* Custom option */
1982
1983
1984 cupsArrayAdd(ppd->options, option);
1985
1986 for (k = 0; k < option->num_choices; k ++)
1987 option->choices[k].option = option;
1988
1989 if ((coption = ppdFindCustomOption(ppd, option->keyword)) != NULL)
1990 coption->option = option;
1991 }
1992 }
1993
1994 /*
1995 * Create an array to track the marked choices...
1996 */
1997
1998 ppd->marked = cupsArrayNew((cups_array_func_t)ppd_compare_choices, NULL);
1999
2000 /*
2001 * Return the PPD file structure...
2002 */
2003
2004 return (ppd);
2005
2006 /*
2007 * Common exit point for errors to save code size...
2008 */
2009
2010 error:
2011
2012 _cupsStrFree(string);
2013 ppd_free(line.buffer);
2014
2015 ppdClose(ppd);
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 (_cups_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 (_cups_strcasecmp(a->keyword, b->keyword));
2307 }
2308
2309
2310 /*
2311 * 'ppd_compare_options()' - Compare two options.
2312 */
2313
2314 static int /* O - Result of comparison */
2315 ppd_compare_options(ppd_option_t *a, /* I - First option */
2316 ppd_option_t *b) /* I - Second option */
2317 {
2318 return (_cups_strcasecmp(a->keyword, b->keyword));
2319 }
2320
2321
2322 /*
2323 * 'ppd_decode()' - Decode a string value...
2324 */
2325
2326 static int /* O - Length of decoded string */
2327 ppd_decode(char *string) /* I - String to decode */
2328 {
2329 char *inptr, /* Input pointer */
2330 *outptr; /* Output pointer */
2331
2332
2333 inptr = string;
2334 outptr = string;
2335
2336 while (*inptr != '\0')
2337 if (*inptr == '<' && isxdigit(inptr[1] & 255))
2338 {
2339 /*
2340 * Convert hex to 8-bit values...
2341 */
2342
2343 inptr ++;
2344 while (isxdigit(*inptr & 255))
2345 {
2346 if (_cups_isalpha(*inptr))
2347 *outptr = (tolower(*inptr) - 'a' + 10) << 4;
2348 else
2349 *outptr = (*inptr - '0') << 4;
2350
2351 inptr ++;
2352
2353 if (!isxdigit(*inptr & 255))
2354 break;
2355
2356 if (_cups_isalpha(*inptr))
2357 *outptr |= tolower(*inptr) - 'a' + 10;
2358 else
2359 *outptr |= *inptr - '0';
2360
2361 inptr ++;
2362 outptr ++;
2363 }
2364
2365 while (*inptr != '>' && *inptr != '\0')
2366 inptr ++;
2367 while (*inptr == '>')
2368 inptr ++;
2369 }
2370 else
2371 *outptr++ = *inptr++;
2372
2373 *outptr = '\0';
2374
2375 return ((int)(outptr - string));
2376 }
2377
2378
2379 /*
2380 * 'ppd_free_group()' - Free a single UI group.
2381 */
2382
2383 static void
2384 ppd_free_group(ppd_group_t *group) /* I - Group to free */
2385 {
2386 int i; /* Looping var */
2387 ppd_option_t *option; /* Current option */
2388 ppd_group_t *subgroup; /* Current sub-group */
2389
2390
2391 if (group->num_options > 0)
2392 {
2393 for (i = group->num_options, option = group->options;
2394 i > 0;
2395 i --, option ++)
2396 ppd_free_option(option);
2397
2398 ppd_free(group->options);
2399 }
2400
2401 if (group->num_subgroups > 0)
2402 {
2403 for (i = group->num_subgroups, subgroup = group->subgroups;
2404 i > 0;
2405 i --, subgroup ++)
2406 ppd_free_group(subgroup);
2407
2408 ppd_free(group->subgroups);
2409 }
2410 }
2411
2412
2413 /*
2414 * 'ppd_free_option()' - Free a single option.
2415 */
2416
2417 static void
2418 ppd_free_option(ppd_option_t *option) /* I - Option to free */
2419 {
2420 int i; /* Looping var */
2421 ppd_choice_t *choice; /* Current choice */
2422
2423
2424 if (option->num_choices > 0)
2425 {
2426 for (i = option->num_choices, choice = option->choices;
2427 i > 0;
2428 i --, choice ++)
2429 {
2430 _cupsStrFree(choice->code);
2431 }
2432
2433 ppd_free(option->choices);
2434 }
2435 }
2436
2437
2438 /*
2439 * 'ppd_get_coption()' - Get a custom option record.
2440 */
2441
2442 static ppd_coption_t * /* O - Custom option... */
2443 ppd_get_coption(ppd_file_t *ppd, /* I - PPD file */
2444 const char *name) /* I - Name of option */
2445 {
2446 ppd_coption_t *copt; /* New custom option */
2447
2448
2449 /*
2450 * See if the option already exists...
2451 */
2452
2453 if ((copt = ppdFindCustomOption(ppd, name)) != NULL)
2454 return (copt);
2455
2456 /*
2457 * Not found, so create the custom option record...
2458 */
2459
2460 if ((copt = calloc(1, sizeof(ppd_coption_t))) == NULL)
2461 return (NULL);
2462
2463 strlcpy(copt->keyword, name, sizeof(copt->keyword));
2464
2465 copt->params = cupsArrayNew((cups_array_func_t)NULL, NULL);
2466
2467 cupsArrayAdd(ppd->coptions, copt);
2468
2469 /*
2470 * Return the new record...
2471 */
2472
2473 return (copt);
2474 }
2475
2476
2477 /*
2478 * 'ppd_get_cparam()' - Get a custom parameter record.
2479 */
2480
2481 static ppd_cparam_t * /* O - Extended option... */
2482 ppd_get_cparam(ppd_coption_t *opt, /* I - PPD file */
2483 const char *param, /* I - Name of parameter */
2484 const char *text) /* I - Human-readable text */
2485 {
2486 ppd_cparam_t *cparam; /* New custom parameter */
2487
2488
2489 /*
2490 * See if the parameter already exists...
2491 */
2492
2493 if ((cparam = ppdFindCustomParam(opt, param)) != NULL)
2494 return (cparam);
2495
2496 /*
2497 * Not found, so create the custom parameter record...
2498 */
2499
2500 if ((cparam = calloc(1, sizeof(ppd_cparam_t))) == NULL)
2501 return (NULL);
2502
2503 strlcpy(cparam->name, param, sizeof(cparam->name));
2504 strlcpy(cparam->text, text[0] ? text : param, sizeof(cparam->text));
2505
2506 /*
2507 * Add this record to the array...
2508 */
2509
2510 cupsArrayAdd(opt->params, cparam);
2511
2512 /*
2513 * Return the new record...
2514 */
2515
2516 return (cparam);
2517 }
2518
2519
2520 /*
2521 * 'ppd_get_group()' - Find or create the named group as needed.
2522 */
2523
2524 static ppd_group_t * /* O - Named group */
2525 ppd_get_group(ppd_file_t *ppd, /* I - PPD file */
2526 const char *name, /* I - Name of group */
2527 const char *text, /* I - Text for group */
2528 _cups_globals_t *cg, /* I - Global data */
2529 cups_encoding_t encoding) /* I - Encoding of text */
2530 {
2531 int i; /* Looping var */
2532 ppd_group_t *group; /* Group */
2533
2534
2535 DEBUG_printf(("7ppd_get_group(ppd=%p, name=\"%s\", text=\"%s\", cg=%p)",
2536 ppd, name, text, cg));
2537
2538 for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
2539 if (!strcmp(group->name, name))
2540 break;
2541
2542 if (i == 0)
2543 {
2544 DEBUG_printf(("8ppd_get_group: Adding group %s...", name));
2545
2546 if (cg->ppd_conform == PPD_CONFORM_STRICT && strlen(text) >= sizeof(group->text))
2547 {
2548 cg->ppd_status = PPD_ILLEGAL_TRANSLATION;
2549
2550 return (NULL);
2551 }
2552
2553 if (ppd->num_groups == 0)
2554 group = malloc(sizeof(ppd_group_t));
2555 else
2556 group = realloc(ppd->groups,
2557 (ppd->num_groups + 1) * sizeof(ppd_group_t));
2558
2559 if (group == NULL)
2560 {
2561 cg->ppd_status = PPD_ALLOC_ERROR;
2562
2563 return (NULL);
2564 }
2565
2566 ppd->groups = group;
2567 group += ppd->num_groups;
2568 ppd->num_groups ++;
2569
2570 memset(group, 0, sizeof(ppd_group_t));
2571 strlcpy(group->name, name, sizeof(group->name));
2572
2573 cupsCharsetToUTF8((cups_utf8_t *)group->text, text,
2574 sizeof(group->text), encoding);
2575 }
2576
2577 return (group);
2578 }
2579
2580
2581 /*
2582 * 'ppd_get_option()' - Find or create the named option as needed.
2583 */
2584
2585 static ppd_option_t * /* O - Named option */
2586 ppd_get_option(ppd_group_t *group, /* I - Group */
2587 const char *name) /* I - Name of option */
2588 {
2589 int i; /* Looping var */
2590 ppd_option_t *option; /* Option */
2591
2592
2593 DEBUG_printf(("7ppd_get_option(group=%p(\"%s\"), name=\"%s\")",
2594 group, group->name, name));
2595
2596 for (i = group->num_options, option = group->options; i > 0; i --, option ++)
2597 if (!strcmp(option->keyword, name))
2598 break;
2599
2600 if (i == 0)
2601 {
2602 if (group->num_options == 0)
2603 option = malloc(sizeof(ppd_option_t));
2604 else
2605 option = realloc(group->options,
2606 (group->num_options + 1) * sizeof(ppd_option_t));
2607
2608 if (option == NULL)
2609 return (NULL);
2610
2611 group->options = option;
2612 option += group->num_options;
2613 group->num_options ++;
2614
2615 memset(option, 0, sizeof(ppd_option_t));
2616 strlcpy(option->keyword, name, sizeof(option->keyword));
2617 }
2618
2619 return (option);
2620 }
2621
2622
2623 /*
2624 * 'ppd_hash_option()' - Generate a hash of the option name...
2625 */
2626
2627 static int /* O - Hash index */
2628 ppd_hash_option(ppd_option_t *option) /* I - Option */
2629 {
2630 int hash = 0; /* Hash index */
2631 const char *k; /* Pointer into keyword */
2632
2633
2634 for (hash = option->keyword[0], k = option->keyword + 1; *k;)
2635 hash = 33 * hash + *k++;
2636
2637 return (hash & 511);
2638 }
2639
2640
2641 /*
2642 * 'ppd_read()' - Read a line from a PPD file, skipping comment lines as
2643 * necessary.
2644 */
2645
2646 static int /* O - Bitmask of fields read */
2647 ppd_read(cups_file_t *fp, /* I - File to read from */
2648 _ppd_line_t *line, /* I - Line buffer */
2649 char *keyword, /* O - Keyword from line */
2650 char *option, /* O - Option from line */
2651 char *text, /* O - Human-readable text from line */
2652 char **string, /* O - Code/string data */
2653 int ignoreblank, /* I - Ignore blank lines? */
2654 _cups_globals_t *cg) /* I - Global data */
2655 {
2656 int ch, /* Character from file */
2657 col, /* Column in line */
2658 colon, /* Colon seen? */
2659 endquote, /* Waiting for an end quote */
2660 mask, /* Mask to be returned */
2661 startline, /* Start line */
2662 textlen; /* Length of text */
2663 char *keyptr, /* Keyword pointer */
2664 *optptr, /* Option pointer */
2665 *textptr, /* Text pointer */
2666 *strptr, /* Pointer into string */
2667 *lineptr; /* Current position in line buffer */
2668
2669
2670 /*
2671 * Now loop until we have a valid line...
2672 */
2673
2674 *string = NULL;
2675 col = 0;
2676 startline = cg->ppd_line + 1;
2677
2678 if (!line->buffer)
2679 {
2680 line->bufsize = 1024;
2681 line->buffer = malloc(1024);
2682
2683 if (!line->buffer)
2684 return (0);
2685 }
2686
2687 do
2688 {
2689 /*
2690 * Read the line...
2691 */
2692
2693 lineptr = line->buffer;
2694 endquote = 0;
2695 colon = 0;
2696
2697 while ((ch = cupsFileGetChar(fp)) != EOF)
2698 {
2699 if (lineptr >= (line->buffer + line->bufsize - 1))
2700 {
2701 /*
2702 * Expand the line buffer...
2703 */
2704
2705 char *temp; /* Temporary line pointer */
2706
2707
2708 line->bufsize += 1024;
2709 if (line->bufsize > 262144)
2710 {
2711 /*
2712 * Don't allow lines longer than 256k!
2713 */
2714
2715 cg->ppd_line = startline;
2716 cg->ppd_status = PPD_LINE_TOO_LONG;
2717
2718 return (0);
2719 }
2720
2721 temp = realloc(line->buffer, line->bufsize);
2722 if (!temp)
2723 {
2724 cg->ppd_line = startline;
2725 cg->ppd_status = PPD_LINE_TOO_LONG;
2726
2727 return (0);
2728 }
2729
2730 lineptr = temp + (lineptr - line->buffer);
2731 line->buffer = temp;
2732 }
2733
2734 if (ch == '\r' || ch == '\n')
2735 {
2736 /*
2737 * Line feed or carriage return...
2738 */
2739
2740 cg->ppd_line ++;
2741 col = 0;
2742
2743 if (ch == '\r')
2744 {
2745 /*
2746 * Check for a trailing line feed...
2747 */
2748
2749 if ((ch = cupsFilePeekChar(fp)) == EOF)
2750 {
2751 ch = '\n';
2752 break;
2753 }
2754
2755 if (ch == 0x0a)
2756 cupsFileGetChar(fp);
2757 }
2758
2759 if (lineptr == line->buffer && ignoreblank)
2760 continue; /* Skip blank lines */
2761
2762 ch = '\n';
2763
2764 if (!endquote) /* Continue for multi-line text */
2765 break;
2766
2767 *lineptr++ = '\n';
2768 }
2769 else if (ch < ' ' && ch != '\t' && cg->ppd_conform == PPD_CONFORM_STRICT)
2770 {
2771 /*
2772 * Other control characters...
2773 */
2774
2775 cg->ppd_line = startline;
2776 cg->ppd_status = PPD_ILLEGAL_CHARACTER;
2777
2778 return (0);
2779 }
2780 else if (ch != 0x1a)
2781 {
2782 /*
2783 * Any other character...
2784 */
2785
2786 *lineptr++ = ch;
2787 col ++;
2788
2789 if (col > (PPD_MAX_LINE - 1))
2790 {
2791 /*
2792 * Line is too long...
2793 */
2794
2795 cg->ppd_line = startline;
2796 cg->ppd_status = PPD_LINE_TOO_LONG;
2797
2798 return (0);
2799 }
2800
2801 if (ch == ':' && strncmp(line->buffer, "*%", 2) != 0)
2802 colon = 1;
2803
2804 if (ch == '\"' && colon)
2805 endquote = !endquote;
2806 }
2807 }
2808
2809 if (endquote)
2810 {
2811 /*
2812 * Didn't finish this quoted string...
2813 */
2814
2815 while ((ch = cupsFileGetChar(fp)) != EOF)
2816 if (ch == '\"')
2817 break;
2818 else if (ch == '\r' || ch == '\n')
2819 {
2820 cg->ppd_line ++;
2821 col = 0;
2822
2823 if (ch == '\r')
2824 {
2825 /*
2826 * Check for a trailing line feed...
2827 */
2828
2829 if ((ch = cupsFilePeekChar(fp)) == EOF)
2830 break;
2831 if (ch == 0x0a)
2832 cupsFileGetChar(fp);
2833 }
2834 }
2835 else if (ch < ' ' && ch != '\t' && cg->ppd_conform == PPD_CONFORM_STRICT)
2836 {
2837 /*
2838 * Other control characters...
2839 */
2840
2841 cg->ppd_line = startline;
2842 cg->ppd_status = PPD_ILLEGAL_CHARACTER;
2843
2844 return (0);
2845 }
2846 else if (ch != 0x1a)
2847 {
2848 col ++;
2849
2850 if (col > (PPD_MAX_LINE - 1))
2851 {
2852 /*
2853 * Line is too long...
2854 */
2855
2856 cg->ppd_line = startline;
2857 cg->ppd_status = PPD_LINE_TOO_LONG;
2858
2859 return (0);
2860 }
2861 }
2862 }
2863
2864 if (ch != '\n')
2865 {
2866 /*
2867 * Didn't finish this line...
2868 */
2869
2870 while ((ch = cupsFileGetChar(fp)) != EOF)
2871 if (ch == '\r' || ch == '\n')
2872 {
2873 /*
2874 * Line feed or carriage return...
2875 */
2876
2877 cg->ppd_line ++;
2878 col = 0;
2879
2880 if (ch == '\r')
2881 {
2882 /*
2883 * Check for a trailing line feed...
2884 */
2885
2886 if ((ch = cupsFilePeekChar(fp)) == EOF)
2887 break;
2888 if (ch == 0x0a)
2889 cupsFileGetChar(fp);
2890 }
2891
2892 break;
2893 }
2894 else if (ch < ' ' && ch != '\t' && cg->ppd_conform == PPD_CONFORM_STRICT)
2895 {
2896 /*
2897 * Other control characters...
2898 */
2899
2900 cg->ppd_line = startline;
2901 cg->ppd_status = PPD_ILLEGAL_CHARACTER;
2902
2903 return (0);
2904 }
2905 else if (ch != 0x1a)
2906 {
2907 col ++;
2908
2909 if (col > (PPD_MAX_LINE - 1))
2910 {
2911 /*
2912 * Line is too long...
2913 */
2914
2915 cg->ppd_line = startline;
2916 cg->ppd_status = PPD_LINE_TOO_LONG;
2917
2918 return (0);
2919 }
2920 }
2921 }
2922
2923 if (lineptr > line->buffer && lineptr[-1] == '\n')
2924 lineptr --;
2925
2926 *lineptr = '\0';
2927
2928 DEBUG_printf(("9ppd_read: LINE=\"%s\"", line->buffer));
2929
2930 /*
2931 * The dynamically created PPDs for older style Mac OS X
2932 * drivers include a large blob of data inserted as comments
2933 * at the end of the file. As an optimization we can stop
2934 * reading the PPD when we get to the start of this data.
2935 */
2936
2937 if (!strcmp(line->buffer, "*%APLWORKSET START"))
2938 return (0);
2939
2940 if (ch == EOF && lineptr == line->buffer)
2941 return (0);
2942
2943 /*
2944 * Now parse it...
2945 */
2946
2947 mask = 0;
2948 lineptr = line->buffer + 1;
2949
2950 keyword[0] = '\0';
2951 option[0] = '\0';
2952 text[0] = '\0';
2953 *string = NULL;
2954
2955 if ((!line->buffer[0] || /* Blank line */
2956 !strncmp(line->buffer, "*%", 2) || /* Comment line */
2957 !strcmp(line->buffer, "*End")) && /* End of multi-line string */
2958 ignoreblank) /* Ignore these? */
2959 {
2960 startline = cg->ppd_line + 1;
2961 continue;
2962 }
2963
2964 if (!strcmp(line->buffer, "*")) /* (Bad) comment line */
2965 {
2966 if (cg->ppd_conform == PPD_CONFORM_RELAXED)
2967 {
2968 startline = cg->ppd_line + 1;
2969 continue;
2970 }
2971 else
2972 {
2973 cg->ppd_line = startline;
2974 cg->ppd_status = PPD_ILLEGAL_MAIN_KEYWORD;
2975
2976 return (0);
2977 }
2978 }
2979
2980 if (line->buffer[0] != '*') /* All lines start with an asterisk */
2981 {
2982 /*
2983 * Allow lines consisting of just whitespace...
2984 */
2985
2986 for (lineptr = line->buffer; *lineptr; lineptr ++)
2987 if (*lineptr && !_cups_isspace(*lineptr))
2988 break;
2989
2990 if (*lineptr)
2991 {
2992 cg->ppd_status = PPD_MISSING_ASTERISK;
2993 return (0);
2994 }
2995 else if (ignoreblank)
2996 continue;
2997 else
2998 return (0);
2999 }
3000
3001 /*
3002 * Get a keyword...
3003 */
3004
3005 keyptr = keyword;
3006
3007 while (*lineptr && *lineptr != ':' && !_cups_isspace(*lineptr))
3008 {
3009 if (*lineptr <= ' ' || *lineptr > 126 || *lineptr == '/' ||
3010 (keyptr - keyword) >= (PPD_MAX_NAME - 1))
3011 {
3012 cg->ppd_status = PPD_ILLEGAL_MAIN_KEYWORD;
3013 return (0);
3014 }
3015
3016 *keyptr++ = *lineptr++;
3017 }
3018
3019 *keyptr = '\0';
3020
3021 if (!strcmp(keyword, "End"))
3022 continue;
3023
3024 mask |= PPD_KEYWORD;
3025
3026 if (_cups_isspace(*lineptr))
3027 {
3028 /*
3029 * Get an option name...
3030 */
3031
3032 while (_cups_isspace(*lineptr))
3033 lineptr ++;
3034
3035 optptr = option;
3036
3037 while (*lineptr && !_cups_isspace(*lineptr) && *lineptr != ':' &&
3038 *lineptr != '/')
3039 {
3040 if (*lineptr <= ' ' || *lineptr > 126 ||
3041 (optptr - option) >= (PPD_MAX_NAME - 1))
3042 {
3043 cg->ppd_status = PPD_ILLEGAL_OPTION_KEYWORD;
3044 return (0);
3045 }
3046
3047 *optptr++ = *lineptr++;
3048 }
3049
3050 *optptr = '\0';
3051
3052 if (_cups_isspace(*lineptr) && cg->ppd_conform == PPD_CONFORM_STRICT)
3053 {
3054 cg->ppd_status = PPD_ILLEGAL_WHITESPACE;
3055 return (0);
3056 }
3057
3058 while (_cups_isspace(*lineptr))
3059 lineptr ++;
3060
3061 mask |= PPD_OPTION;
3062
3063 if (*lineptr == '/')
3064 {
3065 /*
3066 * Get human-readable text...
3067 */
3068
3069 lineptr ++;
3070
3071 textptr = text;
3072
3073 while (*lineptr != '\0' && *lineptr != '\n' && *lineptr != ':')
3074 {
3075 if (((unsigned char)*lineptr < ' ' && *lineptr != '\t') ||
3076 (textptr - text) >= (PPD_MAX_LINE - 1))
3077 {
3078 cg->ppd_status = PPD_ILLEGAL_TRANSLATION;
3079 return (0);
3080 }
3081
3082 *textptr++ = *lineptr++;
3083 }
3084
3085 *textptr = '\0';
3086 textlen = ppd_decode(text);
3087
3088 if (textlen > PPD_MAX_TEXT && cg->ppd_conform == PPD_CONFORM_STRICT)
3089 {
3090 cg->ppd_status = PPD_ILLEGAL_TRANSLATION;
3091 return (0);
3092 }
3093
3094 mask |= PPD_TEXT;
3095 }
3096 }
3097
3098 if (_cups_isspace(*lineptr) && cg->ppd_conform == PPD_CONFORM_STRICT)
3099 {
3100 cg->ppd_status = PPD_ILLEGAL_WHITESPACE;
3101 return (0);
3102 }
3103
3104 while (_cups_isspace(*lineptr))
3105 lineptr ++;
3106
3107 if (*lineptr == ':')
3108 {
3109 /*
3110 * Get string after triming leading and trailing whitespace...
3111 */
3112
3113 lineptr ++;
3114 while (_cups_isspace(*lineptr))
3115 lineptr ++;
3116
3117 strptr = lineptr + strlen(lineptr) - 1;
3118 while (strptr >= lineptr && _cups_isspace(*strptr))
3119 *strptr-- = '\0';
3120
3121 if (*strptr == '\"')
3122 {
3123 /*
3124 * Quoted string by itself, remove quotes...
3125 */
3126
3127 *strptr = '\0';
3128 lineptr ++;
3129 }
3130
3131 *string = _cupsStrAlloc(lineptr);
3132
3133 mask |= PPD_STRING;
3134 }
3135 }
3136 while (mask == 0);
3137
3138 return (mask);
3139 }
3140
3141
3142 /*
3143 * End of "$Id: ppd.c 7906 2008-09-03 20:19:43Z mike $".
3144 */