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