]> 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 6586 2007-06-21 17:44:22Z 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 int groupidx = -1; /* Index for current group */
1026 ppd_group_t *gtemp; /* Temporary group */
1027
1028
1029 DEBUG_printf(("%s option not found for %s...\n", keyword + 6, keyword));
1030
1031 if (group)
1032 groupidx = group - ppd->groups; /* Save index for current group */
1033
1034 if ((gtemp = ppd_get_group(ppd, "General", _("General"), cg,
1035 encoding)) == NULL)
1036 {
1037 DEBUG_puts("Unable to get general group!");
1038
1039 goto error;
1040 }
1041
1042 if (group)
1043 group = ppd->groups + groupidx; /* Restore group pointer */
1044
1045 if ((option = ppd_get_option(gtemp, keyword + 6)) == NULL)
1046 {
1047 DEBUG_printf(("Unable to get %s option!\n", keyword + 6));
1048
1049 cg->ppd_status = PPD_ALLOC_ERROR;
1050
1051 goto error;
1052 }
1053 }
1054
1055 if (!ppd_get_coption(ppd, keyword + 6))
1056 {
1057 cg->ppd_status = PPD_ALLOC_ERROR;
1058
1059 goto error;
1060 }
1061
1062 /*
1063 * Add the "custom" option...
1064 */
1065
1066 if ((choice = ppd_add_choice(option, "Custom")) == NULL)
1067 {
1068 DEBUG_puts("Unable to add Custom choice!");
1069
1070 cg->ppd_status = PPD_ALLOC_ERROR;
1071
1072 goto error;
1073 }
1074
1075 strlcpy(choice->text, text[0] ? text : _("Custom"),
1076 sizeof(choice->text));
1077
1078 choice->code = string;
1079 string = NULL; /* Don't add as an attribute below */
1080 option = NULL;
1081
1082 /*
1083 * Now process custom page sizes specially...
1084 */
1085
1086 if (!strcmp(keyword, "CustomPageSize"))
1087 {
1088 ppd->variable_sizes = 1;
1089
1090 /*
1091 * Add a "Custom" page size entry...
1092 */
1093
1094 ppd_add_size(ppd, "Custom");
1095
1096 if ((option = ppdFindOption(ppd, "PageRegion")) == NULL)
1097 {
1098 int groupidx = -1; /* Index to current group */
1099 ppd_group_t *gtemp; /* Temporary group */
1100
1101 if (group)
1102 groupidx = group - ppd->groups; /* Save index for current group */
1103
1104 if ((gtemp = ppd_get_group(ppd, "General", _("General"), cg,
1105 encoding)) == NULL)
1106 {
1107 DEBUG_puts("Unable to get general group!");
1108
1109 goto error;
1110 }
1111
1112 if (group)
1113 group = ppd->groups + groupidx; /* Restore group pointer */
1114
1115 option = ppd_get_option(gtemp, "PageRegion");
1116 }
1117
1118 if ((choice = ppd_add_choice(option, "Custom")) == NULL)
1119 {
1120 DEBUG_puts("Unable to add Custom choice!");
1121
1122 cg->ppd_status = PPD_ALLOC_ERROR;
1123
1124 goto error;
1125 }
1126
1127 strlcpy(choice->text, text[0] ? text : _("Custom"),
1128 sizeof(choice->text));
1129 option = NULL;
1130 }
1131 }
1132 else if (!strcmp(keyword, "LandscapeOrientation"))
1133 {
1134 if (!strcmp(string, "Minus90"))
1135 ppd->landscape = -90;
1136 else if (!strcmp(string, "Plus90"))
1137 ppd->landscape = 90;
1138 }
1139 else if (!strcmp(keyword, "Emulators"))
1140 {
1141 for (count = 1, sptr = string; sptr != NULL;)
1142 if ((sptr = strchr(sptr, ' ')) != NULL)
1143 {
1144 count ++;
1145 while (*sptr == ' ')
1146 sptr ++;
1147 }
1148
1149 ppd->num_emulations = count;
1150 ppd->emulations = calloc(count, sizeof(ppd_emul_t));
1151
1152 for (i = 0, sptr = string; i < count; i ++)
1153 {
1154 for (nameptr = ppd->emulations[i].name;
1155 *sptr != '\0' && *sptr != ' ';
1156 sptr ++)
1157 if (nameptr < (ppd->emulations[i].name + sizeof(ppd->emulations[i].name) - 1))
1158 *nameptr++ = *sptr;
1159
1160 *nameptr = '\0';
1161
1162 while (*sptr == ' ')
1163 sptr ++;
1164 }
1165 }
1166 else if (!strncmp(keyword, "StartEmulator_", 14))
1167 {
1168 ppd_decode(string);
1169
1170 for (i = 0; i < ppd->num_emulations; i ++)
1171 if (!strcmp(keyword + 14, ppd->emulations[i].name))
1172 {
1173 ppd->emulations[i].start = string;
1174 string = NULL;
1175 }
1176 }
1177 else if (!strncmp(keyword, "StopEmulator_", 13))
1178 {
1179 ppd_decode(string);
1180
1181 for (i = 0; i < ppd->num_emulations; i ++)
1182 if (!strcmp(keyword + 13, ppd->emulations[i].name))
1183 {
1184 ppd->emulations[i].stop = string;
1185 string = NULL;
1186 }
1187 }
1188 else if (!strcmp(keyword, "JobPatchFile"))
1189 {
1190 if (ppd->patches == NULL)
1191 ppd->patches = strdup(string);
1192 else
1193 {
1194 temp = realloc(ppd->patches, strlen(ppd->patches) +
1195 strlen(string) + 1);
1196 if (temp == NULL)
1197 {
1198 cg->ppd_status = PPD_ALLOC_ERROR;
1199
1200 goto error;
1201 }
1202
1203 ppd->patches = temp;
1204
1205 strcpy(ppd->patches + strlen(ppd->patches), string);
1206 }
1207 }
1208 else if (!strcmp(keyword, "OpenUI"))
1209 {
1210 /*
1211 * Don't allow nesting of options...
1212 */
1213
1214 if (option && cg->ppd_conform == PPD_CONFORM_STRICT)
1215 {
1216 cg->ppd_status = PPD_NESTED_OPEN_UI;
1217
1218 goto error;
1219 }
1220
1221 /*
1222 * Add an option record to the current sub-group, group, or file...
1223 */
1224
1225 if (name[0] == '*')
1226 _cups_strcpy(name, name + 1); /* Eliminate leading asterisk */
1227
1228 for (i = (int)strlen(name) - 1; i > 0 && isspace(name[i] & 255); i --)
1229 name[i] = '\0'; /* Eliminate trailing spaces */
1230
1231 DEBUG_printf(("OpenUI of %s in group %s...\n", name,
1232 group ? group->text : "(null)"));
1233
1234 if (subgroup != NULL)
1235 option = ppd_get_option(subgroup, name);
1236 else if (group == NULL)
1237 {
1238 if ((group = ppd_get_group(ppd, "General", _("General"), cg,
1239 encoding)) == NULL)
1240 goto error;
1241
1242 DEBUG_printf(("Adding to group %s...\n", group->text));
1243 option = ppd_get_option(group, name);
1244 group = NULL;
1245 }
1246 else
1247 option = ppd_get_option(group, name);
1248
1249 if (option == NULL)
1250 {
1251 cg->ppd_status = PPD_ALLOC_ERROR;
1252
1253 goto error;
1254 }
1255
1256 /*
1257 * Now fill in the initial information for the option...
1258 */
1259
1260 if (string && !strcmp(string, "PickMany"))
1261 option->ui = PPD_UI_PICKMANY;
1262 else if (string && !strcmp(string, "Boolean"))
1263 option->ui = PPD_UI_BOOLEAN;
1264 else if (string && !strcmp(string, "PickOne"))
1265 option->ui = PPD_UI_PICKONE;
1266 else if (cg->ppd_conform == PPD_CONFORM_STRICT)
1267 {
1268 cg->ppd_status = PPD_BAD_OPEN_UI;
1269
1270 goto error;
1271 }
1272 else
1273 option->ui = PPD_UI_PICKONE;
1274
1275 for (j = 0; j < ppd->num_attrs; j ++)
1276 if (!strncmp(ppd->attrs[j]->name, "Default", 7) &&
1277 !strcmp(ppd->attrs[j]->name + 7, name) &&
1278 ppd->attrs[j]->value)
1279 {
1280 DEBUG_printf(("Setting Default%s to %s via attribute...\n",
1281 option->keyword, ppd->attrs[j]->value));
1282 strlcpy(option->defchoice, ppd->attrs[j]->value,
1283 sizeof(option->defchoice));
1284 break;
1285 }
1286
1287 if (text[0])
1288 cupsCharsetToUTF8((cups_utf8_t *)option->text, text,
1289 sizeof(option->text), encoding);
1290 else
1291 {
1292 if (!strcmp(name, "PageSize"))
1293 strlcpy(option->text, _("Media Size"), sizeof(option->text));
1294 else if (!strcmp(name, "MediaType"))
1295 strlcpy(option->text, _("Media Type"), sizeof(option->text));
1296 else if (!strcmp(name, "InputSlot"))
1297 strlcpy(option->text, _("Media Source"), sizeof(option->text));
1298 else if (!strcmp(name, "ColorModel"))
1299 strlcpy(option->text, _("Output Mode"), sizeof(option->text));
1300 else if (!strcmp(name, "Resolution"))
1301 strlcpy(option->text, _("Resolution"), sizeof(option->text));
1302 else
1303 strlcpy(option->text, name, sizeof(option->text));
1304 }
1305
1306 option->section = PPD_ORDER_ANY;
1307
1308 ppd_free(string);
1309 string = NULL;
1310 }
1311 else if (!strcmp(keyword, "JCLOpenUI"))
1312 {
1313 /*
1314 * Don't allow nesting of options...
1315 */
1316
1317 if (option && cg->ppd_conform == PPD_CONFORM_STRICT)
1318 {
1319 cg->ppd_status = PPD_NESTED_OPEN_UI;
1320
1321 goto error;
1322 }
1323
1324 /*
1325 * Find the JCL group, and add if needed...
1326 */
1327
1328 group = ppd_get_group(ppd, "JCL", _("JCL"), cg, encoding);
1329
1330 if (group == NULL)
1331 goto error;
1332
1333 /*
1334 * Add an option record to the current JCLs...
1335 */
1336
1337 if (name[0] == '*')
1338 _cups_strcpy(name, name + 1);
1339
1340 option = ppd_get_option(group, name);
1341
1342 if (option == NULL)
1343 {
1344 cg->ppd_status = PPD_ALLOC_ERROR;
1345
1346 goto error;
1347 }
1348
1349 /*
1350 * Now fill in the initial information for the option...
1351 */
1352
1353 if (string && !strcmp(string, "PickMany"))
1354 option->ui = PPD_UI_PICKMANY;
1355 else if (string && !strcmp(string, "Boolean"))
1356 option->ui = PPD_UI_BOOLEAN;
1357 else if (string && !strcmp(string, "PickOne"))
1358 option->ui = PPD_UI_PICKONE;
1359 else
1360 {
1361 cg->ppd_status = PPD_BAD_OPEN_UI;
1362
1363 goto error;
1364 }
1365
1366 for (j = 0; j < ppd->num_attrs; j ++)
1367 if (!strncmp(ppd->attrs[j]->name, "Default", 7) &&
1368 !strcmp(ppd->attrs[j]->name + 7, name) &&
1369 ppd->attrs[j]->value)
1370 {
1371 DEBUG_printf(("Setting Default%s to %s via attribute...\n",
1372 option->keyword, ppd->attrs[j]->value));
1373 strlcpy(option->defchoice, ppd->attrs[j]->value,
1374 sizeof(option->defchoice));
1375 break;
1376 }
1377
1378 if (text[0])
1379 cupsCharsetToUTF8((cups_utf8_t *)option->text, text,
1380 sizeof(option->text), encoding);
1381 else
1382 strlcpy(option->text, name, sizeof(option->text));
1383
1384 option->section = PPD_ORDER_JCL;
1385 group = NULL;
1386
1387 ppd_free(string);
1388 string = NULL;
1389 }
1390 else if (!strcmp(keyword, "CloseUI") || !strcmp(keyword, "JCLCloseUI"))
1391 {
1392 option = NULL;
1393
1394 ppd_free(string);
1395 string = NULL;
1396 }
1397 else if (!strcmp(keyword, "OpenGroup"))
1398 {
1399 /*
1400 * Open a new group...
1401 */
1402
1403 if (group != NULL)
1404 {
1405 cg->ppd_status = PPD_NESTED_OPEN_GROUP;
1406
1407 goto error;
1408 }
1409
1410 if (!string)
1411 {
1412 cg->ppd_status = PPD_BAD_OPEN_GROUP;
1413
1414 goto error;
1415 }
1416
1417 /*
1418 * Separate the group name from the text (name/text)...
1419 */
1420
1421 if ((sptr = strchr(string, '/')) != NULL)
1422 *sptr++ = '\0';
1423 else
1424 sptr = string;
1425
1426 /*
1427 * Fix up the text...
1428 */
1429
1430 ppd_decode(sptr);
1431
1432 /*
1433 * Find/add the group...
1434 */
1435
1436 group = ppd_get_group(ppd, string, sptr, cg, encoding);
1437
1438 if (group == NULL)
1439 goto error;
1440
1441 ppd_free(string);
1442 string = NULL;
1443 }
1444 else if (!strcmp(keyword, "CloseGroup"))
1445 {
1446 group = NULL;
1447
1448 ppd_free(string);
1449 string = NULL;
1450 }
1451 else if (!strcmp(keyword, "OrderDependency") ||
1452 !strcmp(keyword, "NonUIOrderDependency"))
1453 {
1454 order = (float)_cupsStrScand(string, &sptr, loc);
1455
1456 if (!sptr || sscanf(sptr, "%40s%40s", name, keyword) != 2)
1457 {
1458 cg->ppd_status = PPD_BAD_ORDER_DEPENDENCY;
1459
1460 goto error;
1461 }
1462
1463 if (keyword[0] == '*')
1464 _cups_strcpy(keyword, keyword + 1);
1465
1466 if (!strcmp(name, "ExitServer"))
1467 section = PPD_ORDER_EXIT;
1468 else if (!strcmp(name, "Prolog"))
1469 section = PPD_ORDER_PROLOG;
1470 else if (!strcmp(name, "DocumentSetup"))
1471 section = PPD_ORDER_DOCUMENT;
1472 else if (!strcmp(name, "PageSetup"))
1473 section = PPD_ORDER_PAGE;
1474 else if (!strcmp(name, "JCLSetup"))
1475 section = PPD_ORDER_JCL;
1476 else
1477 section = PPD_ORDER_ANY;
1478
1479 if (option == NULL)
1480 {
1481 ppd_group_t *gtemp;
1482
1483
1484 /*
1485 * Only valid for Non-UI options...
1486 */
1487
1488 for (i = ppd->num_groups, gtemp = ppd->groups; i > 0; i --, gtemp ++)
1489 if (gtemp->text[0] == '\0')
1490 break;
1491
1492 if (i > 0)
1493 for (i = 0; i < gtemp->num_options; i ++)
1494 if (!strcmp(keyword, gtemp->options[i].keyword))
1495 {
1496 gtemp->options[i].section = section;
1497 gtemp->options[i].order = order;
1498 break;
1499 }
1500 }
1501 else
1502 {
1503 option->section = section;
1504 option->order = order;
1505 }
1506
1507 ppd_free(string);
1508 string = NULL;
1509 }
1510 else if (!strncmp(keyword, "Default", 7))
1511 {
1512 if (string == NULL)
1513 continue;
1514
1515 /*
1516 * Drop UI text, if any, from value...
1517 */
1518
1519 if (strchr(string, '/') != NULL)
1520 *strchr(string, '/') = '\0';
1521
1522 /*
1523 * Assign the default value as appropriate...
1524 */
1525
1526 if (!strcmp(keyword, "DefaultColorSpace"))
1527 {
1528 /*
1529 * Set default colorspace...
1530 */
1531
1532 if (!strcmp(string, "CMY"))
1533 ppd->colorspace = PPD_CS_CMY;
1534 else if (!strcmp(string, "CMYK"))
1535 ppd->colorspace = PPD_CS_CMYK;
1536 else if (!strcmp(string, "RGB"))
1537 ppd->colorspace = PPD_CS_RGB;
1538 else if (!strcmp(string, "RGBK"))
1539 ppd->colorspace = PPD_CS_RGBK;
1540 else if (!strcmp(string, "N"))
1541 ppd->colorspace = PPD_CS_N;
1542 else
1543 ppd->colorspace = PPD_CS_GRAY;
1544 }
1545 else if (option && !strcmp(keyword + 7, option->keyword))
1546 {
1547 /*
1548 * Set the default as part of the current option...
1549 */
1550
1551 DEBUG_printf(("Setting %s to %s...\n", keyword, string));
1552
1553 strlcpy(option->defchoice, string, sizeof(option->defchoice));
1554
1555 DEBUG_printf(("%s is now %s...\n", keyword, option->defchoice));
1556 }
1557 else
1558 {
1559 /*
1560 * Lookup option and set if it has been defined...
1561 */
1562
1563 ppd_option_t *toption; /* Temporary option */
1564
1565
1566 if ((toption = ppdFindOption(ppd, keyword + 7)) != NULL)
1567 {
1568 DEBUG_printf(("Setting %s to %s...\n", keyword, string));
1569 strlcpy(toption->defchoice, string, sizeof(toption->defchoice));
1570 }
1571 }
1572 }
1573 else if (!strcmp(keyword, "UIConstraints") ||
1574 !strcmp(keyword, "NonUIConstraints"))
1575 {
1576 if (ppd->num_consts == 0)
1577 constraint = calloc(2, sizeof(ppd_const_t));
1578 else
1579 constraint = realloc(ppd->consts,
1580 (ppd->num_consts + 2) * sizeof(ppd_const_t));
1581
1582 if (constraint == NULL)
1583 {
1584 cg->ppd_status = PPD_ALLOC_ERROR;
1585
1586 goto error;
1587 }
1588
1589 ppd->consts = constraint;
1590 constraint += ppd->num_consts;
1591 ppd->num_consts ++;
1592
1593 switch (sscanf(string, "%40s%40s%40s%40s", constraint->option1,
1594 constraint->choice1, constraint->option2,
1595 constraint->choice2))
1596 {
1597 case 0 : /* Error */
1598 case 1 : /* Error */
1599 cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1600 goto error;
1601
1602 case 2 : /* Two options... */
1603 /*
1604 * Check for broken constraints like "* Option"...
1605 */
1606
1607 if (cg->ppd_conform == PPD_CONFORM_STRICT &&
1608 (!strcmp(constraint->option1, "*") ||
1609 !strcmp(constraint->choice1, "*")))
1610 {
1611 cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1612 goto error;
1613 }
1614
1615 /*
1616 * The following strcpy's are safe, as optionN and
1617 * choiceN are all the same size (size defined by PPD spec...)
1618 */
1619
1620 if (constraint->option1[0] == '*')
1621 _cups_strcpy(constraint->option1, constraint->option1 + 1);
1622 else if (cg->ppd_conform == PPD_CONFORM_STRICT)
1623 {
1624 cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1625 goto error;
1626 }
1627
1628 if (constraint->choice1[0] == '*')
1629 _cups_strcpy(constraint->option2, constraint->choice1 + 1);
1630 else if (cg->ppd_conform == PPD_CONFORM_STRICT)
1631 {
1632 cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1633 goto error;
1634 }
1635
1636 constraint->choice1[0] = '\0';
1637 constraint->choice2[0] = '\0';
1638 break;
1639
1640 case 3 : /* Two options, one choice... */
1641 /*
1642 * Check for broken constraints like "* Option"...
1643 */
1644
1645 if (cg->ppd_conform == PPD_CONFORM_STRICT &&
1646 (!strcmp(constraint->option1, "*") ||
1647 !strcmp(constraint->choice1, "*") ||
1648 !strcmp(constraint->option2, "*")))
1649 {
1650 cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1651 goto error;
1652 }
1653
1654 /*
1655 * The following _cups_strcpy's are safe, as optionN and
1656 * choiceN are all the same size (size defined by PPD spec...)
1657 */
1658
1659 if (constraint->option1[0] == '*')
1660 _cups_strcpy(constraint->option1, constraint->option1 + 1);
1661 else if (cg->ppd_conform == PPD_CONFORM_STRICT)
1662 {
1663 cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1664 goto error;
1665 }
1666
1667 if (constraint->choice1[0] == '*')
1668 {
1669 if (cg->ppd_conform == PPD_CONFORM_STRICT &&
1670 constraint->option2[0] == '*')
1671 {
1672 cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1673 goto error;
1674 }
1675
1676 _cups_strcpy(constraint->choice2, constraint->option2);
1677 _cups_strcpy(constraint->option2, constraint->choice1 + 1);
1678 constraint->choice1[0] = '\0';
1679 }
1680 else
1681 {
1682 if (constraint->option2[0] == '*')
1683 _cups_strcpy(constraint->option2, constraint->option2 + 1);
1684 else if (cg->ppd_conform == PPD_CONFORM_STRICT)
1685 {
1686 cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1687 goto error;
1688 }
1689
1690 constraint->choice2[0] = '\0';
1691 }
1692 break;
1693
1694 case 4 : /* Two options, two choices... */
1695 /*
1696 * Check for broken constraints like "* Option"...
1697 */
1698
1699 if (cg->ppd_conform == PPD_CONFORM_STRICT &&
1700 (!strcmp(constraint->option1, "*") ||
1701 !strcmp(constraint->choice1, "*") ||
1702 !strcmp(constraint->option2, "*") ||
1703 !strcmp(constraint->choice2, "*")))
1704 {
1705 cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1706 goto error;
1707 }
1708
1709 if (constraint->option1[0] == '*')
1710 _cups_strcpy(constraint->option1, constraint->option1 + 1);
1711 else if (cg->ppd_conform == PPD_CONFORM_STRICT)
1712 {
1713 cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1714 goto error;
1715 }
1716
1717 if (cg->ppd_conform == PPD_CONFORM_STRICT &&
1718 constraint->choice1[0] == '*')
1719 {
1720 cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1721 goto error;
1722 }
1723
1724 if (constraint->option2[0] == '*')
1725 _cups_strcpy(constraint->option2, constraint->option2 + 1);
1726 else if (cg->ppd_conform == PPD_CONFORM_STRICT)
1727 {
1728 cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1729 goto error;
1730 }
1731
1732 if (cg->ppd_conform == PPD_CONFORM_STRICT &&
1733 constraint->choice2[0] == '*')
1734 {
1735 cg->ppd_status = PPD_BAD_UI_CONSTRAINTS;
1736 goto error;
1737 }
1738 break;
1739 }
1740
1741 /*
1742 * For CustomPageSize and InputSlot/ManualFeed, create a duplicate
1743 * constraint for PageRegion...
1744 */
1745
1746 if (!strcasecmp(constraint->option1, "CustomPageSize") &&
1747 (!strcasecmp(constraint->option2, "InputSlot") ||
1748 !strcasecmp(constraint->option2, "ManualFeed")))
1749 {
1750 ppd->num_consts ++;
1751
1752 strcpy(constraint[1].option1, "PageRegion");
1753 strcpy(constraint[1].choice1, "Custom");
1754 strcpy(constraint[1].option2, constraint->option2);
1755 strcpy(constraint[1].choice2, constraint->choice2);
1756 }
1757 else if (!strcasecmp(constraint->option2, "CustomPageSize") &&
1758 (!strcasecmp(constraint->option1, "InputSlot") ||
1759 !strcasecmp(constraint->option1, "ManualFeed")))
1760 {
1761 ppd->num_consts ++;
1762
1763 strcpy(constraint[1].option1, constraint->option1);
1764 strcpy(constraint[1].choice1, constraint->choice1);
1765 strcpy(constraint[1].option2, "PageRegion");
1766 strcpy(constraint[1].choice2, "Custom");
1767 }
1768
1769 /*
1770 * Handle CustomFoo option constraints...
1771 */
1772
1773 if (!strncasecmp(constraint->option1, "Custom", 6) &&
1774 !strcasecmp(constraint->choice1, "True"))
1775 {
1776 _cups_strcpy(constraint->option1, constraint->option1 + 6);
1777 strcpy(constraint->choice1, "Custom");
1778 }
1779
1780 if (!strncasecmp(constraint->option2, "Custom", 6) &&
1781 !strcasecmp(constraint->choice2, "True"))
1782 {
1783 _cups_strcpy(constraint->option2, constraint->option2 + 6);
1784 strcpy(constraint->choice2, "Custom");
1785 }
1786
1787 /*
1788 * Don't add this one as an attribute...
1789 */
1790
1791 ppd_free(string);
1792 string = NULL;
1793 }
1794 else if (!strcmp(keyword, "PaperDimension"))
1795 {
1796 if ((size = ppdPageSize(ppd, name)) == NULL)
1797 size = ppd_add_size(ppd, name);
1798
1799 if (size == NULL)
1800 {
1801 /*
1802 * Unable to add or find size!
1803 */
1804
1805 cg->ppd_status = PPD_ALLOC_ERROR;
1806
1807 goto error;
1808 }
1809
1810 size->width = (float)_cupsStrScand(string, &sptr, loc);
1811 size->length = (float)_cupsStrScand(sptr, NULL, loc);
1812
1813 ppd_free(string);
1814 string = NULL;
1815 }
1816 else if (!strcmp(keyword, "ImageableArea"))
1817 {
1818 if ((size = ppdPageSize(ppd, name)) == NULL)
1819 size = ppd_add_size(ppd, name);
1820
1821 if (size == NULL)
1822 {
1823 /*
1824 * Unable to add or find size!
1825 */
1826
1827 cg->ppd_status = PPD_ALLOC_ERROR;
1828
1829 goto error;
1830 }
1831
1832 size->left = (float)_cupsStrScand(string, &sptr, loc);
1833 size->bottom = (float)_cupsStrScand(sptr, &sptr, loc);
1834 size->right = (float)_cupsStrScand(sptr, &sptr, loc);
1835 size->top = (float)_cupsStrScand(sptr, NULL, loc);
1836
1837 ppd_free(string);
1838 string = NULL;
1839 }
1840 else if (option != NULL &&
1841 (mask & (PPD_KEYWORD | PPD_OPTION | PPD_STRING)) ==
1842 (PPD_KEYWORD | PPD_OPTION | PPD_STRING) &&
1843 !strcmp(keyword, option->keyword))
1844 {
1845 DEBUG_printf(("group = %p, subgroup = %p\n", group, subgroup));
1846
1847 if (!strcmp(keyword, "PageSize"))
1848 {
1849 /*
1850 * Add a page size...
1851 */
1852
1853 if (ppdPageSize(ppd, name) == NULL)
1854 ppd_add_size(ppd, name);
1855 }
1856
1857 /*
1858 * Add the option choice...
1859 */
1860
1861 choice = ppd_add_choice(option, name);
1862
1863 if (text[0])
1864 cupsCharsetToUTF8((cups_utf8_t *)choice->text, text,
1865 sizeof(choice->text), encoding);
1866 else if (!strcmp(name, "True"))
1867 strcpy(choice->text, _("Yes"));
1868 else if (!strcmp(name, "False"))
1869 strcpy(choice->text, _("No"));
1870 else
1871 strlcpy(choice->text, name, sizeof(choice->text));
1872
1873 if (option->section == PPD_ORDER_JCL)
1874 ppd_decode(string); /* Decode quoted string */
1875
1876 choice->code = string;
1877 string = NULL; /* Don't add as an attribute below */
1878 }
1879
1880 /*
1881 * Add remaining lines with keywords and string values as attributes...
1882 */
1883
1884 if (string &&
1885 (mask & (PPD_KEYWORD | PPD_STRING)) == (PPD_KEYWORD | PPD_STRING))
1886 ppd_add_attr(ppd, keyword, name, text, string);
1887 else
1888 ppd_free(string);
1889 }
1890
1891 /*
1892 * Reset language preferences...
1893 */
1894
1895 cupsLangFree(language);
1896
1897 #ifdef DEBUG
1898 if (!feof(fp))
1899 printf("Premature EOF at %lu...\n", (unsigned long)ftell(fp));
1900 #endif /* DEBUG */
1901
1902 if (cg->ppd_status != PPD_OK)
1903 {
1904 /*
1905 * Had an error reading the PPD file, cannot continue!
1906 */
1907
1908 ppdClose(ppd);
1909
1910 return (NULL);
1911 }
1912
1913 /*
1914 * Create the sorted options array and set the option back-pointer for
1915 * each choice and custom option...
1916 */
1917
1918 ppd->options = cupsArrayNew2((cups_array_func_t)ppd_compare_options, NULL,
1919 (cups_ahash_func_t)ppd_hash_option,
1920 PPD_HASHSIZE);
1921
1922 for (i = ppd->num_groups, group = ppd->groups;
1923 i > 0;
1924 i --, group ++)
1925 {
1926 for (j = group->num_options, option = group->options;
1927 j > 0;
1928 j --, option ++)
1929 {
1930 ppd_coption_t *coption; /* Custom option */
1931
1932
1933 cupsArrayAdd(ppd->options, option);
1934
1935 for (k = 0; k < option->num_choices; k ++)
1936 option->choices[k].option = option;
1937
1938 if ((coption = ppdFindCustomOption(ppd, option->keyword)) != NULL)
1939 coption->option = option;
1940 }
1941 }
1942
1943 /*
1944 * Sort the constraints...
1945 */
1946
1947 if (ppd->num_consts > 1)
1948 qsort(ppd->consts, ppd->num_consts, sizeof(ppd_const_t),
1949 (int (*)(const void *, const void *))ppd_compare_consts);
1950
1951 /*
1952 * Create an array to track the marked choices...
1953 */
1954
1955 ppd->marked = cupsArrayNew((cups_array_func_t)ppd_compare_choices, NULL);
1956
1957 /*
1958 * Return the PPD file structure...
1959 */
1960
1961 return (ppd);
1962
1963 /*
1964 * Common exit point for errors to save code size...
1965 */
1966
1967 error:
1968
1969 ppd_free(string);
1970
1971 ppdClose(ppd);
1972
1973 cupsLangFree(language);
1974
1975 return (NULL);
1976 }
1977
1978
1979 /*
1980 * 'ppdOpenFd()' - Read a PPD file into memory.
1981 */
1982
1983 ppd_file_t * /* O - PPD file record */
1984 ppdOpenFd(int fd) /* I - File to read from */
1985 {
1986 cups_file_t *fp; /* CUPS file pointer */
1987 ppd_file_t *ppd; /* PPD file record */
1988 _cups_globals_t *cg = _cupsGlobals();
1989 /* Global data */
1990
1991
1992 /*
1993 * Set the line number to 0...
1994 */
1995
1996 cg->ppd_line = 0;
1997
1998 /*
1999 * Range check input...
2000 */
2001
2002 if (fd < 0)
2003 {
2004 cg->ppd_status = PPD_NULL_FILE;
2005
2006 return (NULL);
2007 }
2008
2009 /*
2010 * Try to open the file and parse it...
2011 */
2012
2013 if ((fp = cupsFileOpenFd(fd, "r")) != NULL)
2014 {
2015 ppd = ppdOpen2(fp);
2016
2017 cupsFileClose(fp);
2018 }
2019 else
2020 {
2021 cg->ppd_status = PPD_FILE_OPEN_ERROR;
2022 ppd = NULL;
2023 }
2024
2025 return (ppd);
2026 }
2027
2028
2029 /*
2030 * 'ppdOpenFile()' - Read a PPD file into memory.
2031 */
2032
2033 ppd_file_t * /* O - PPD file record */
2034 ppdOpenFile(const char *filename) /* I - File to read from */
2035 {
2036 cups_file_t *fp; /* File pointer */
2037 ppd_file_t *ppd; /* PPD file record */
2038 _cups_globals_t *cg = _cupsGlobals();
2039 /* Global data */
2040
2041
2042 /*
2043 * Set the line number to 0...
2044 */
2045
2046 cg->ppd_line = 0;
2047
2048 /*
2049 * Range check input...
2050 */
2051
2052 if (filename == NULL)
2053 {
2054 cg->ppd_status = PPD_NULL_FILE;
2055
2056 return (NULL);
2057 }
2058
2059 /*
2060 * Try to open the file and parse it...
2061 */
2062
2063 if ((fp = cupsFileOpen(filename, "r")) != NULL)
2064 {
2065 ppd = ppdOpen2(fp);
2066
2067 cupsFileClose(fp);
2068 }
2069 else
2070 {
2071 cg->ppd_status = PPD_FILE_OPEN_ERROR;
2072 ppd = NULL;
2073 }
2074
2075 return (ppd);
2076 }
2077
2078
2079 /*
2080 * 'ppdSetConformance()' - Set the conformance level for PPD files.
2081 *
2082 * @since CUPS 1.1.20@
2083 */
2084
2085 void
2086 ppdSetConformance(ppd_conform_t c) /* I - Conformance level */
2087 {
2088 _cups_globals_t *cg = _cupsGlobals();
2089 /* Global data */
2090
2091
2092 cg->ppd_conform = c;
2093 }
2094
2095
2096 /*
2097 * 'ppd_add_attr()' - Add an attribute to the PPD data.
2098 */
2099
2100 static ppd_attr_t * /* O - New attribute */
2101 ppd_add_attr(ppd_file_t *ppd, /* I - PPD file data */
2102 const char *name, /* I - Attribute name */
2103 const char *spec, /* I - Specifier string, if any */
2104 const char *text, /* I - Text string, if any */
2105 const char *value) /* I - Value of attribute */
2106 {
2107 ppd_attr_t **ptr, /* New array */
2108 *temp; /* New attribute */
2109
2110
2111 /*
2112 * Range check input...
2113 */
2114
2115 if (ppd == NULL || name == NULL || spec == NULL)
2116 return (NULL);
2117
2118 /*
2119 * Create the array as needed...
2120 */
2121
2122 if (!ppd->sorted_attrs)
2123 ppd->sorted_attrs = cupsArrayNew((cups_array_func_t)ppd_compare_attrs,
2124 NULL);
2125
2126 /*
2127 * Allocate memory for the new attribute...
2128 */
2129
2130 if (ppd->num_attrs == 0)
2131 ptr = malloc(sizeof(ppd_attr_t *));
2132 else
2133 ptr = realloc(ppd->attrs, (ppd->num_attrs + 1) * sizeof(ppd_attr_t *));
2134
2135 if (ptr == NULL)
2136 return (NULL);
2137
2138 ppd->attrs = ptr;
2139 ptr += ppd->num_attrs;
2140
2141 if ((temp = calloc(1, sizeof(ppd_attr_t))) == NULL)
2142 return (NULL);
2143
2144 *ptr = temp;
2145
2146 ppd->num_attrs ++;
2147
2148 /*
2149 * Copy data over...
2150 */
2151
2152 strlcpy(temp->name, name, sizeof(temp->name));
2153 strlcpy(temp->spec, spec, sizeof(temp->spec));
2154 strlcpy(temp->text, text, sizeof(temp->text));
2155 temp->value = (char *)value;
2156
2157 /*
2158 * Add the attribute to the sorted array...
2159 */
2160
2161 cupsArrayAdd(ppd->sorted_attrs, temp);
2162
2163 /*
2164 * Return the attribute...
2165 */
2166
2167 return (temp);
2168 }
2169
2170
2171 /*
2172 * 'ppd_add_choice()' - Add a choice to an option.
2173 */
2174
2175 static ppd_choice_t * /* O - Named choice */
2176 ppd_add_choice(ppd_option_t *option, /* I - Option */
2177 const char *name) /* I - Name of choice */
2178 {
2179 ppd_choice_t *choice; /* Choice */
2180
2181
2182 if (option->num_choices == 0)
2183 choice = malloc(sizeof(ppd_choice_t));
2184 else
2185 choice = realloc(option->choices,
2186 sizeof(ppd_choice_t) * (option->num_choices + 1));
2187
2188 if (choice == NULL)
2189 return (NULL);
2190
2191 option->choices = choice;
2192 choice += option->num_choices;
2193 option->num_choices ++;
2194
2195 memset(choice, 0, sizeof(ppd_choice_t));
2196 strlcpy(choice->choice, name, sizeof(choice->choice));
2197
2198 return (choice);
2199 }
2200
2201
2202 /*
2203 * 'ppd_add_size()' - Add a page size.
2204 */
2205
2206 static ppd_size_t * /* O - Named size */
2207 ppd_add_size(ppd_file_t *ppd, /* I - PPD file */
2208 const char *name) /* I - Name of size */
2209 {
2210 ppd_size_t *size; /* Size */
2211
2212
2213 if (ppd->num_sizes == 0)
2214 size = malloc(sizeof(ppd_size_t));
2215 else
2216 size = realloc(ppd->sizes, sizeof(ppd_size_t) * (ppd->num_sizes + 1));
2217
2218 if (size == NULL)
2219 return (NULL);
2220
2221 ppd->sizes = size;
2222 size += ppd->num_sizes;
2223 ppd->num_sizes ++;
2224
2225 memset(size, 0, sizeof(ppd_size_t));
2226 strlcpy(size->name, name, sizeof(size->name));
2227
2228 return (size);
2229 }
2230
2231
2232 /*
2233 * 'ppd_compare_attrs()' - Compare two attributes.
2234 */
2235
2236 static int /* O - Result of comparison */
2237 ppd_compare_attrs(ppd_attr_t *a, /* I - First attribute */
2238 ppd_attr_t *b) /* I - Second attribute */
2239 {
2240 int ret; /* Result of comparison */
2241
2242
2243 if ((ret = strcasecmp(a->name, b->name)) != 0)
2244 return (ret);
2245 else
2246 return (strcasecmp(a->spec, b->spec));
2247 }
2248
2249
2250 /*
2251 * 'ppd_compare_choices()' - Compare two choices...
2252 */
2253
2254 static int /* O - Result of comparison */
2255 ppd_compare_choices(ppd_choice_t *a, /* I - First choice */
2256 ppd_choice_t *b) /* I - Second choice */
2257 {
2258 return (a->option - b->option);
2259 }
2260
2261
2262 /*
2263 * 'ppd_compare_consts()' - Compare two constraints.
2264 */
2265
2266 static int /* O - Result of comparison */
2267 ppd_compare_consts(ppd_const_t *a, /* I - First constraint */
2268 ppd_const_t *b) /* I - Second constraint */
2269 {
2270 int ret; /* Result of comparison */
2271
2272
2273 if ((ret = strcmp(a->option1, b->option1)) != 0)
2274 return (ret);
2275 else if ((ret = strcmp(a->choice1, b->choice1)) != 0)
2276 return (ret);
2277 else if ((ret = strcmp(a->option2, b->option2)) != 0)
2278 return (ret);
2279 else
2280 return (strcmp(a->choice2, b->choice2));
2281 }
2282
2283
2284 /*
2285 * 'ppd_compare_coptions()' - Compare two custom options.
2286 */
2287
2288 static int /* O - Result of comparison */
2289 ppd_compare_coptions(ppd_coption_t *a, /* I - First option */
2290 ppd_coption_t *b) /* I - Second option */
2291 {
2292 return (strcasecmp(a->keyword, b->keyword));
2293 }
2294
2295
2296 /*
2297 * 'ppd_compare_cparams()' - Compare two custom parameters.
2298 */
2299
2300 static int /* O - Result of comparison */
2301 ppd_compare_cparams(ppd_cparam_t *a, /* I - First parameter */
2302 ppd_cparam_t *b) /* I - Second parameter */
2303 {
2304 return (strcasecmp(a->name, b->name));
2305 }
2306
2307
2308 /*
2309 * 'ppd_compare_options()' - Compare two options.
2310 */
2311
2312 static int /* O - Result of comparison */
2313 ppd_compare_options(ppd_option_t *a, /* I - First option */
2314 ppd_option_t *b) /* I - Second option */
2315 {
2316 return (strcasecmp(a->keyword, b->keyword));
2317 }
2318
2319
2320 /*
2321 * 'ppd_decode()' - Decode a string value...
2322 */
2323
2324 static int /* O - Length of decoded string */
2325 ppd_decode(char *string) /* I - String to decode */
2326 {
2327 char *inptr, /* Input pointer */
2328 *outptr; /* Output pointer */
2329
2330
2331 inptr = string;
2332 outptr = string;
2333
2334 while (*inptr != '\0')
2335 if (*inptr == '<' && isxdigit(inptr[1] & 255))
2336 {
2337 /*
2338 * Convert hex to 8-bit values...
2339 */
2340
2341 inptr ++;
2342 while (isxdigit(*inptr & 255))
2343 {
2344 if (isalpha(*inptr))
2345 *outptr = (tolower(*inptr) - 'a' + 10) << 4;
2346 else
2347 *outptr = (*inptr - '0') << 4;
2348
2349 inptr ++;
2350
2351 if (!isxdigit(*inptr & 255))
2352 break;
2353
2354 if (isalpha(*inptr))
2355 *outptr |= tolower(*inptr) - 'a' + 10;
2356 else
2357 *outptr |= *inptr - '0';
2358
2359 inptr ++;
2360 outptr ++;
2361 }
2362
2363 while (*inptr != '>' && *inptr != '\0')
2364 inptr ++;
2365 while (*inptr == '>')
2366 inptr ++;
2367 }
2368 else
2369 *outptr++ = *inptr++;
2370
2371 *outptr = '\0';
2372
2373 return ((int)(outptr - string));
2374 }
2375
2376
2377 /*
2378 * 'ppd_free_group()' - Free a single UI group.
2379 */
2380
2381 static void
2382 ppd_free_group(ppd_group_t *group) /* I - Group to free */
2383 {
2384 int i; /* Looping var */
2385 ppd_option_t *option; /* Current option */
2386 ppd_group_t *subgroup; /* Current sub-group */
2387
2388
2389 if (group->num_options > 0)
2390 {
2391 for (i = group->num_options, option = group->options;
2392 i > 0;
2393 i --, option ++)
2394 ppd_free_option(option);
2395
2396 ppd_free(group->options);
2397 }
2398
2399 if (group->num_subgroups > 0)
2400 {
2401 for (i = group->num_subgroups, subgroup = group->subgroups;
2402 i > 0;
2403 i --, subgroup ++)
2404 ppd_free_group(subgroup);
2405
2406 ppd_free(group->subgroups);
2407 }
2408 }
2409
2410
2411 /*
2412 * 'ppd_free_option()' - Free a single option.
2413 */
2414
2415 static void
2416 ppd_free_option(ppd_option_t *option) /* I - Option to free */
2417 {
2418 int i; /* Looping var */
2419 ppd_choice_t *choice; /* Current choice */
2420
2421
2422 if (option->num_choices > 0)
2423 {
2424 for (i = option->num_choices, choice = option->choices;
2425 i > 0;
2426 i --, choice ++)
2427 {
2428 ppd_free(choice->code);
2429 }
2430
2431 ppd_free(option->choices);
2432 }
2433 }
2434
2435
2436 /*
2437 * 'ppd_get_coption()' - Get a custom option record.
2438 */
2439
2440 static ppd_coption_t * /* O - Custom option... */
2441 ppd_get_coption(ppd_file_t *ppd, /* I - PPD file */
2442 const char *name) /* I - Name of option */
2443 {
2444 ppd_coption_t *copt; /* New custom option */
2445
2446
2447 /*
2448 * See if the option already exists...
2449 */
2450
2451 if ((copt = ppdFindCustomOption(ppd, name)) != NULL)
2452 return (copt);
2453
2454 /*
2455 * Not found, so create the custom option record...
2456 */
2457
2458 if ((copt = calloc(1, sizeof(ppd_coption_t))) == NULL)
2459 return (NULL);
2460
2461 strlcpy(copt->keyword, name, sizeof(copt->keyword));
2462
2463 copt->params = cupsArrayNew((cups_array_func_t)ppd_compare_cparams, NULL);
2464
2465 cupsArrayAdd(ppd->coptions, copt);
2466
2467 /*
2468 * Return the new record...
2469 */
2470
2471 return (copt);
2472 }
2473
2474
2475 /*
2476 * 'ppd_get_cparam()' - Get a custom parameter record.
2477 */
2478
2479 static ppd_cparam_t * /* O - Extended option... */
2480 ppd_get_cparam(ppd_coption_t *opt, /* I - PPD file */
2481 const char *param, /* I - Name of parameter */
2482 const char *text) /* I - Human-readable text */
2483 {
2484 ppd_cparam_t *cparam; /* New custom parameter */
2485
2486
2487 /*
2488 * See if the parameter already exists...
2489 */
2490
2491 if ((cparam = ppdFindCustomParam(opt, param)) != NULL)
2492 return (cparam);
2493
2494 /*
2495 * Not found, so create the custom parameter record...
2496 */
2497
2498 if ((cparam = calloc(1, sizeof(ppd_cparam_t))) == NULL)
2499 return (NULL);
2500
2501 strlcpy(cparam->name, param, sizeof(cparam->name));
2502 strlcpy(cparam->text, text[0] ? text : param, sizeof(cparam->text));
2503
2504 /*
2505 * Add this record to the array...
2506 */
2507
2508 cupsArrayAdd(opt->params, cparam);
2509
2510 /*
2511 * Return the new record...
2512 */
2513
2514 return (cparam);
2515 }
2516
2517
2518 /*
2519 * 'ppd_get_group()' - Find or create the named group as needed.
2520 */
2521
2522 static ppd_group_t * /* O - Named group */
2523 ppd_get_group(ppd_file_t *ppd, /* I - PPD file */
2524 const char *name, /* I - Name of group */
2525 const char *text, /* I - Text for group */
2526 _cups_globals_t *cg, /* I - Global data */
2527 cups_encoding_t encoding) /* I - Encoding of text */
2528 {
2529 int i; /* Looping var */
2530 ppd_group_t *group; /* Group */
2531
2532
2533 DEBUG_printf(("ppd_get_group(ppd=%p, name=\"%s\", text=\"%s\", cg=%p)\n",
2534 ppd, name, text, cg));
2535
2536 for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
2537 if (!strcmp(group->name, name))
2538 break;
2539
2540 if (i == 0)
2541 {
2542 DEBUG_printf(("Adding group %s...\n", name));
2543
2544 if (cg->ppd_conform == PPD_CONFORM_STRICT && strlen(text) >= sizeof(group->text))
2545 {
2546 cg->ppd_status = PPD_ILLEGAL_TRANSLATION;
2547
2548 return (NULL);
2549 }
2550
2551 if (ppd->num_groups == 0)
2552 group = malloc(sizeof(ppd_group_t));
2553 else
2554 group = realloc(ppd->groups,
2555 (ppd->num_groups + 1) * sizeof(ppd_group_t));
2556
2557 if (group == NULL)
2558 {
2559 cg->ppd_status = PPD_ALLOC_ERROR;
2560
2561 return (NULL);
2562 }
2563
2564 ppd->groups = group;
2565 group += ppd->num_groups;
2566 ppd->num_groups ++;
2567
2568 memset(group, 0, sizeof(ppd_group_t));
2569 strlcpy(group->name, name, sizeof(group->name));
2570
2571 cupsCharsetToUTF8((cups_utf8_t *)group->text, text,
2572 sizeof(group->text), encoding);
2573 }
2574
2575 return (group);
2576 }
2577
2578
2579 /*
2580 * 'ppd_get_option()' - Find or create the named option as needed.
2581 */
2582
2583 static ppd_option_t * /* O - Named option */
2584 ppd_get_option(ppd_group_t *group, /* I - Group */
2585 const char *name) /* I - Name of option */
2586 {
2587 int i; /* Looping var */
2588 ppd_option_t *option; /* Option */
2589
2590
2591 DEBUG_printf(("ppd_get_option(group=%p(\"%s\"), name=\"%s\")\n",
2592 group, group->name, name));
2593
2594 for (i = group->num_options, option = group->options; i > 0; i --, option ++)
2595 if (!strcmp(option->keyword, name))
2596 break;
2597
2598 if (i == 0)
2599 {
2600 if (group->num_options == 0)
2601 option = malloc(sizeof(ppd_option_t));
2602 else
2603 option = realloc(group->options,
2604 (group->num_options + 1) * sizeof(ppd_option_t));
2605
2606 if (option == NULL)
2607 return (NULL);
2608
2609 group->options = option;
2610 option += group->num_options;
2611 group->num_options ++;
2612
2613 memset(option, 0, sizeof(ppd_option_t));
2614 strlcpy(option->keyword, name, sizeof(option->keyword));
2615 }
2616
2617 return (option);
2618 }
2619
2620
2621 /*
2622 * 'ppd_hash_option()' - Generate a hash of the option name...
2623 */
2624
2625 static int /* O - Hash index */
2626 ppd_hash_option(ppd_option_t *option) /* I - Option */
2627 {
2628 int hash = 0; /* Hash index */
2629 const char *k; /* Pointer into keyword */
2630
2631
2632 for (hash = option->keyword[0], k = option->keyword + 1; *k;)
2633 hash = 33 * hash + *k++;
2634
2635 return (hash & 511);
2636 }
2637
2638
2639 /*
2640 * 'ppd_read()' - Read a line from a PPD file, skipping comment lines as
2641 * necessary.
2642 */
2643
2644 static int /* O - Bitmask of fields read */
2645 ppd_read(cups_file_t *fp, /* I - File to read from */
2646 char *keyword, /* O - Keyword from line */
2647 char *option, /* O - Option from line */
2648 char *text, /* O - Human-readable text from line */
2649 char **string, /* O - Code/string data */
2650 int ignoreblank, /* I - Ignore blank lines? */
2651 _cups_globals_t *cg) /* I - Global data */
2652 {
2653 int ch, /* Character from file */
2654 col, /* Column in line */
2655 colon, /* Colon seen? */
2656 endquote, /* Waiting for an end quote */
2657 mask, /* Mask to be returned */
2658 startline, /* Start line */
2659 textlen; /* Length of text */
2660 char *keyptr, /* Keyword pointer */
2661 *optptr, /* Option pointer */
2662 *textptr, /* Text pointer */
2663 *strptr, /* Pointer into string */
2664 *lineptr, /* Current position in line buffer */
2665 *line; /* Line buffer */
2666 int linesize; /* Current size of line buffer */
2667
2668 /*
2669 * Range check everything...
2670 */
2671
2672 if (!fp || !keyword || !option || !text || !string)
2673 return (0);
2674
2675 /*
2676 * Now loop until we have a valid line...
2677 */
2678
2679 *string = NULL;
2680 col = 0;
2681 startline = cg->ppd_line + 1;
2682 linesize = 1024;
2683 line = malloc(linesize);
2684
2685 if (!line)
2686 return (0);
2687
2688 do
2689 {
2690 /*
2691 * Read the line...
2692 */
2693
2694 lineptr = line;
2695 endquote = 0;
2696 colon = 0;
2697
2698 while ((ch = cupsFileGetChar(fp)) != EOF)
2699 {
2700 if (lineptr >= (line + linesize - 1))
2701 {
2702 /*
2703 * Expand the line buffer...
2704 */
2705
2706 char *temp; /* Temporary line pointer */
2707
2708
2709 linesize += 1024;
2710 if (linesize > 262144)
2711 {
2712 /*
2713 * Don't allow lines longer than 256k!
2714 */
2715
2716 cg->ppd_line = startline;
2717 cg->ppd_status = PPD_LINE_TOO_LONG;
2718
2719 free(line);
2720
2721 return (0);
2722 }
2723
2724 temp = realloc(line, linesize);
2725 if (!temp)
2726 {
2727 cg->ppd_line = startline;
2728 cg->ppd_status = PPD_LINE_TOO_LONG;
2729
2730 free(line);
2731
2732 return (0);
2733 }
2734
2735 lineptr = temp + (lineptr - line);
2736 line = temp;
2737 }
2738
2739 if (ch == '\r' || ch == '\n')
2740 {
2741 /*
2742 * Line feed or carriage return...
2743 */
2744
2745 cg->ppd_line ++;
2746 col = 0;
2747
2748 if (ch == '\r')
2749 {
2750 /*
2751 * Check for a trailing line feed...
2752 */
2753
2754 if ((ch = cupsFilePeekChar(fp)) == EOF)
2755 {
2756 ch = '\n';
2757 break;
2758 }
2759
2760 if (ch == 0x0a)
2761 cupsFileGetChar(fp);
2762 }
2763
2764 if (lineptr == line && ignoreblank)
2765 continue; /* Skip blank lines */
2766
2767 ch = '\n';
2768
2769 if (!endquote) /* Continue for multi-line text */
2770 break;
2771
2772 *lineptr++ = '\n';
2773 }
2774 else if (ch < ' ' && ch != '\t' && cg->ppd_conform == PPD_CONFORM_STRICT)
2775 {
2776 /*
2777 * Other control characters...
2778 */
2779
2780 cg->ppd_line = startline;
2781 cg->ppd_status = PPD_ILLEGAL_CHARACTER;
2782
2783 free(line);
2784
2785 return (0);
2786 }
2787 else if (ch != 0x1a)
2788 {
2789 /*
2790 * Any other character...
2791 */
2792
2793 *lineptr++ = ch;
2794 col ++;
2795
2796 if (col > (PPD_MAX_LINE - 1))
2797 {
2798 /*
2799 * Line is too long...
2800 */
2801
2802 cg->ppd_line = startline;
2803 cg->ppd_status = PPD_LINE_TOO_LONG;
2804
2805 free(line);
2806
2807 return (0);
2808 }
2809
2810 if (ch == ':' && strncmp(line, "*%", 2) != 0)
2811 colon = 1;
2812
2813 if (ch == '\"' && colon)
2814 endquote = !endquote;
2815 }
2816 }
2817
2818 if (endquote)
2819 {
2820 /*
2821 * Didn't finish this quoted string...
2822 */
2823
2824 while ((ch = cupsFileGetChar(fp)) != EOF)
2825 if (ch == '\"')
2826 break;
2827 else if (ch == '\r' || ch == '\n')
2828 {
2829 cg->ppd_line ++;
2830 col = 0;
2831
2832 if (ch == '\r')
2833 {
2834 /*
2835 * Check for a trailing line feed...
2836 */
2837
2838 if ((ch = cupsFilePeekChar(fp)) == EOF)
2839 break;
2840 if (ch == 0x0a)
2841 cupsFileGetChar(fp);
2842 }
2843
2844 ch = '\n';
2845 }
2846 else if (ch < ' ' && ch != '\t' && cg->ppd_conform == PPD_CONFORM_STRICT)
2847 {
2848 /*
2849 * Other control characters...
2850 */
2851
2852 cg->ppd_line = startline;
2853 cg->ppd_status = PPD_ILLEGAL_CHARACTER;
2854
2855 free(line);
2856
2857 return (0);
2858 }
2859 else if (ch != 0x1a)
2860 {
2861 col ++;
2862
2863 if (col > (PPD_MAX_LINE - 1))
2864 {
2865 /*
2866 * Line is too long...
2867 */
2868
2869 cg->ppd_line = startline;
2870 cg->ppd_status = PPD_LINE_TOO_LONG;
2871
2872 free(line);
2873
2874 return (0);
2875 }
2876 }
2877 }
2878
2879 if (ch != '\n')
2880 {
2881 /*
2882 * Didn't finish this line...
2883 */
2884
2885 while ((ch = cupsFileGetChar(fp)) != EOF)
2886 if (ch == '\r' || ch == '\n')
2887 {
2888 /*
2889 * Line feed or carriage return...
2890 */
2891
2892 cg->ppd_line ++;
2893 col = 0;
2894
2895 if (ch == '\r')
2896 {
2897 /*
2898 * Check for a trailing line feed...
2899 */
2900
2901 if ((ch = cupsFilePeekChar(fp)) == EOF)
2902 break;
2903 if (ch == 0x0a)
2904 cupsFileGetChar(fp);
2905 }
2906
2907 break;
2908 }
2909 else if (ch < ' ' && ch != '\t' && cg->ppd_conform == PPD_CONFORM_STRICT)
2910 {
2911 /*
2912 * Other control characters...
2913 */
2914
2915 cg->ppd_line = startline;
2916 cg->ppd_status = PPD_ILLEGAL_CHARACTER;
2917
2918 free(line);
2919
2920 return (0);
2921 }
2922 else if (ch != 0x1a)
2923 {
2924 col ++;
2925
2926 if (col > (PPD_MAX_LINE - 1))
2927 {
2928 /*
2929 * Line is too long...
2930 */
2931
2932 cg->ppd_line = startline;
2933 cg->ppd_status = PPD_LINE_TOO_LONG;
2934
2935 free(line);
2936
2937 return (0);
2938 }
2939 }
2940 }
2941
2942 if (lineptr > line && lineptr[-1] == '\n')
2943 lineptr --;
2944
2945 *lineptr = '\0';
2946
2947 DEBUG_printf(("LINE = \"%s\"\n", line));
2948
2949 /*
2950 * The dynamically created PPDs for older style Mac OS X
2951 * drivers include a large blob of data inserted as comments
2952 * at the end of the file. As an optimization we can stop
2953 * reading the PPD when we get to the start of this data.
2954 */
2955
2956 if (!strcmp(line, "*%APLWORKSET START"))
2957 {
2958 free(line);
2959 return (0);
2960 }
2961
2962 if (ch == EOF && lineptr == line)
2963 {
2964 free(line);
2965 return (0);
2966 }
2967
2968 /*
2969 * Now parse it...
2970 */
2971
2972 mask = 0;
2973 lineptr = line + 1;
2974
2975 keyword[0] = '\0';
2976 option[0] = '\0';
2977 text[0] = '\0';
2978 *string = NULL;
2979
2980 if ((!line[0] || /* Blank line */
2981 !strncmp(line, "*%", 2) || /* Comment line */
2982 !strcmp(line, "*End")) && /* End of multi-line string */
2983 ignoreblank) /* Ignore these? */
2984 {
2985 startline = cg->ppd_line + 1;
2986 continue;
2987 }
2988
2989 if (!strcmp(line, "*")) /* (Bad) comment line */
2990 {
2991 if (cg->ppd_conform == PPD_CONFORM_RELAXED)
2992 {
2993 startline = cg->ppd_line + 1;
2994 continue;
2995 }
2996 else
2997 {
2998 cg->ppd_line = startline;
2999 cg->ppd_status = PPD_ILLEGAL_MAIN_KEYWORD;
3000
3001 free(line);
3002 return (0);
3003 }
3004 }
3005
3006 if (line[0] != '*') /* All lines start with an asterisk */
3007 {
3008 /*
3009 * Allow lines consisting of just whitespace...
3010 */
3011
3012 for (lineptr = line; *lineptr; lineptr ++)
3013 if (!isspace(*lineptr & 255))
3014 break;
3015
3016 if (*lineptr)
3017 {
3018 cg->ppd_status = PPD_MISSING_ASTERISK;
3019 free(line);
3020 return (0);
3021 }
3022 else if (ignoreblank)
3023 continue;
3024 else
3025 {
3026 free(line);
3027 return (0);
3028 }
3029 }
3030
3031 /*
3032 * Get a keyword...
3033 */
3034
3035 keyptr = keyword;
3036
3037 while (*lineptr != '\0' && *lineptr != ':' && !isspace(*lineptr & 255))
3038 {
3039 if (*lineptr <= ' ' || *lineptr > 126 || *lineptr == '/' ||
3040 (keyptr - keyword) >= (PPD_MAX_NAME - 1))
3041 {
3042 cg->ppd_status = PPD_ILLEGAL_MAIN_KEYWORD;
3043 free(line);
3044 return (0);
3045 }
3046
3047 *keyptr++ = *lineptr++;
3048 }
3049
3050 *keyptr = '\0';
3051
3052 if (!strcmp(keyword, "End"))
3053 continue;
3054
3055 mask |= PPD_KEYWORD;
3056
3057 /* DEBUG_printf(("keyword = \"%s\", lineptr = \"%s\"\n", keyword, lineptr));*/
3058
3059 if (isspace(*lineptr & 255))
3060 {
3061 /*
3062 * Get an option name...
3063 */
3064
3065 while (isspace(*lineptr & 255))
3066 lineptr ++;
3067
3068 optptr = option;
3069
3070 while (*lineptr != '\0' && !isspace(*lineptr & 255) && *lineptr != ':' &&
3071 *lineptr != '/')
3072 {
3073 if (*lineptr <= ' ' || *lineptr > 126 ||
3074 (optptr - option) >= (PPD_MAX_NAME - 1))
3075 {
3076 cg->ppd_status = PPD_ILLEGAL_OPTION_KEYWORD;
3077 free(line);
3078 return (0);
3079 }
3080
3081 *optptr++ = *lineptr++;
3082 }
3083
3084 *optptr = '\0';
3085
3086 if (isspace(*lineptr & 255) && cg->ppd_conform == PPD_CONFORM_STRICT)
3087 {
3088 cg->ppd_status = PPD_ILLEGAL_WHITESPACE;
3089 free(line);
3090 return (0);
3091 }
3092
3093 while (isspace(*lineptr & 255))
3094 lineptr ++;
3095
3096 mask |= PPD_OPTION;
3097
3098 /* DEBUG_printf(("option = \"%s\", lineptr = \"%s\"\n", option, lineptr));*/
3099
3100 if (*lineptr == '/')
3101 {
3102 /*
3103 * Get human-readable text...
3104 */
3105
3106 lineptr ++;
3107
3108 textptr = text;
3109
3110 while (*lineptr != '\0' && *lineptr != '\n' && *lineptr != ':')
3111 {
3112 if (((unsigned char)*lineptr < ' ' && *lineptr != '\t') ||
3113 (textptr - text) >= (PPD_MAX_LINE - 1))
3114 {
3115 cg->ppd_status = PPD_ILLEGAL_TRANSLATION;
3116 free(line);
3117 return (0);
3118 }
3119
3120 *textptr++ = *lineptr++;
3121 }
3122
3123 *textptr = '\0';
3124 textlen = ppd_decode(text);
3125
3126 if (textlen > PPD_MAX_TEXT && cg->ppd_conform == PPD_CONFORM_STRICT)
3127 {
3128 cg->ppd_status = PPD_ILLEGAL_TRANSLATION;
3129 free(line);
3130 return (0);
3131 }
3132
3133 mask |= PPD_TEXT;
3134 }
3135
3136 /* DEBUG_printf(("text = \"%s\", lineptr = \"%s\"\n", text, lineptr));*/
3137 }
3138
3139 if (isspace(*lineptr & 255) && cg->ppd_conform == PPD_CONFORM_STRICT)
3140 {
3141 cg->ppd_status = PPD_ILLEGAL_WHITESPACE;
3142 free(line);
3143 return (0);
3144 }
3145
3146 while (isspace(*lineptr & 255))
3147 lineptr ++;
3148
3149 if (*lineptr == ':')
3150 {
3151 /*
3152 * Get string after triming leading and trailing whitespace...
3153 */
3154
3155 lineptr ++;
3156 while (isspace(*lineptr & 255))
3157 lineptr ++;
3158
3159 strptr = lineptr + strlen(lineptr) - 1;
3160 while (strptr >= lineptr && isspace(*strptr & 255))
3161 *strptr-- = '\0';
3162
3163 if (*strptr == '\"')
3164 {
3165 /*
3166 * Quoted string by itself...
3167 */
3168
3169 *string = malloc(strlen(lineptr) + 1);
3170
3171 strptr = *string;
3172
3173 for (; *lineptr != '\0'; lineptr ++)
3174 if (*lineptr != '\"')
3175 *strptr++ = *lineptr;
3176
3177 *strptr = '\0';
3178 }
3179 else
3180 *string = strdup(lineptr);
3181
3182 /* DEBUG_printf(("string = \"%s\", lineptr = \"%s\"\n", *string, lineptr));*/
3183
3184 mask |= PPD_STRING;
3185 }
3186 }
3187 while (mask == 0);
3188
3189 free(line);
3190
3191 return (mask);
3192 }
3193
3194
3195 /*
3196 * End of "$Id: ppd.c 6586 2007-06-21 17:44:22Z mike $".
3197 */