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