Revert manifest to default one
[external/cups.git] / ppdc / ppdc-source.cxx
1 //
2 // "$Id: ppdc-source.cxx 10379 2012-03-23 22:16:22Z mike $"
3 //
4 //   Source class for the CUPS PPD Compiler.
5 //
6 //   Copyright 2007-2012 by Apple Inc.
7 //   Copyright 2002-2007 by Easy Software Products.
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 // Contents:
16 //
17 //   ppdcSource::ppdcSource()         - Load a driver source file.
18 //   ppdcSource::~ppdcSource()        - Free a driver source file.
19 //   ppdcSource::add_include()        - Add an include directory.
20 //   ppdcSource::find_driver()        - Find a driver.
21 //   ppdcSource::find_include()       - Find an include file.
22 //   ppdcSource::find_po()            - Find a message catalog for the given
23 //                                      locale.
24 //   ppdcSource::find_size()          - Find a media size.
25 //   ppdcSource::find_variable()      - Find a variable.
26 //   ppdcSource::get_attr()           - Get an attribute.
27 //   ppdcSource::get_boolean()        - Get a boolean value.
28 //   ppdcSource::get_choice()         - Get a choice.
29 //   ppdcSource::get_color_model()    - Get an old-style color model option.
30 //   ppdcSource::get_color_order()    - Get an old-style color order value.
31 //   ppdcSource::get_color_profile()  - Get a color profile definition.
32 //   ppdcSource::get_color_space()    - Get an old-style colorspace value.
33 //   ppdcSource::get_constraint()     - Get a constraint.
34 //   ppdcSource::get_custom_size()    - Get a custom media size definition from
35 //                                      a file.
36 //   ppdcSource::get_duplex()         - Get a duplex option.
37 //   ppdcSource::get_filter()         - Get a filter.
38 //   ppdcSource::get_float()          - Get a single floating-point number.
39 //   ppdcSource::get_font()           - Get a font definition.
40 //   ppdcSource::get_generic()        - Get a generic old-style option.
41 //   ppdcSource::get_group()          - Get an option group.
42 //   ppdcSource::get_installable()    - Get an installable option.
43 //   ppdcSource::get_integer()        - Get an integer value from a file.
44 //   ppdcSource::get_measurement()    - Get a measurement value.
45 //   ppdcSource::get_option()         - Get an option definition.
46 //   ppdcSource::get_po()             - Get a message catalog.
47 //   ppdcSource::get_resolution()     - Get an old-style resolution option.
48 //   ppdcSource::get_simple_profile() - Get a simple color profile definition.
49 //   ppdcSource::get_size()           - Get a media size definition from a file.
50 //   ppdcSource::get_token()          - Get a token from a file.
51 //   ppdcSource::get_variable()       - Get a variable definition.
52 //   ppdcSource::quotef()             - Write a formatted, quoted string...
53 //   ppdcSource::read_file()          - Read a driver source file.
54 //   ppdcSource::scan_file()          - Scan a driver source file.
55 //   ppdcSource::set_variable()       - Set a variable.
56 //   ppdcSource::write_file()         - Write the current source data to a file.
57 //
58
59 //
60 // Include necessary headers...
61 //
62
63 #include "ppdc-private.h"
64 #include <limits.h>
65 #include <math.h>
66 #include <unistd.h>
67 #include <cups/raster.h>
68 #include "data/epson.h"
69 #include "data/escp.h"
70 #include "data/hp.h"
71 #include "data/label.h"
72 #include "data/pcl.h"
73 #ifndef WIN32
74 #  include <sys/utsname.h>
75 #endif // !WIN32
76
77
78 //
79 // Class globals...
80 //
81
82 ppdcArray       *ppdcSource::includes = 0;
83 const char      *ppdcSource::driver_types[] =
84                 {
85                   "custom",
86                   "ps",
87                   "escp",
88                   "pcl",
89                   "label",
90                   "epson",
91                   "hp"
92                 };
93
94
95 //
96 // 'ppdcSource::ppdcSource()' - Load a driver source file.
97 //
98
99 ppdcSource::ppdcSource(const char  *f,  // I - File to read
100                        cups_file_t *ffp)// I - File pointer to use
101   : ppdcShared()
102 {
103   PPDC_NEW;
104
105   filename      = new ppdcString(f);
106   base_fonts    = new ppdcArray();
107   drivers       = new ppdcArray();
108   po_files      = new ppdcArray();
109   sizes         = new ppdcArray();
110   vars          = new ppdcArray();
111   cond_state    = PPDC_COND_NORMAL;
112   cond_current  = cond_stack;
113   cond_stack[0] = PPDC_COND_NORMAL;
114
115   // Add standard #define variables...
116 #define MAKE_STRING(x) #x
117
118   vars->add(new ppdcVariable("CUPS_VERSION", MAKE_STRING(CUPS_VERSION)));
119   vars->add(new ppdcVariable("CUPS_VERSION_MAJOR", MAKE_STRING(CUPS_VERSION_MAJOR)));
120   vars->add(new ppdcVariable("CUPS_VERSION_MINOR", MAKE_STRING(CUPS_VERSION_MINOR)));
121   vars->add(new ppdcVariable("CUPS_VERSION_PATCH", MAKE_STRING(CUPS_VERSION_PATCH)));
122
123 #ifdef WIN32
124   vars->add(new ppdcVariable("PLATFORM_NAME", "Windows"));
125   vars->add(new ppdcVariable("PLATFORM_ARCH", "X86"));
126
127 #else
128   struct utsname name;                  // uname information
129
130   if (!uname(&name))
131   {
132     vars->add(new ppdcVariable("PLATFORM_NAME", name.sysname));
133     vars->add(new ppdcVariable("PLATFORM_ARCH", name.machine));
134   }
135   else
136   {
137     vars->add(new ppdcVariable("PLATFORM_NAME", "unknown"));
138     vars->add(new ppdcVariable("PLATFORM_ARCH", "unknown"));
139   }
140 #endif // WIN32
141
142   if (f)
143     read_file(f, ffp);
144 }
145
146
147 //
148 // 'ppdcSource::~ppdcSource()' - Free a driver source file.
149 //
150
151 ppdcSource::~ppdcSource()
152 {
153   PPDC_DELETE;
154
155   filename->release();
156   base_fonts->release();
157   drivers->release();
158   po_files->release();
159   sizes->release();
160   vars->release();
161 }
162
163
164 //
165 // 'ppdcSource::add_include()' - Add an include directory.
166 //
167
168 void
169 ppdcSource::add_include(const char *d)  // I - Include directory
170 {
171   if (!d)
172     return;
173
174   if (!includes)
175     includes = new ppdcArray();
176
177   includes->add(new ppdcString(d));
178 }
179
180
181 //
182 // 'ppdcSource::find_driver()' - Find a driver.
183 //
184
185 ppdcDriver *                            // O - Driver
186 ppdcSource::find_driver(const char *f)  // I - Driver file name
187 {
188   ppdcDriver    *d;                     // Current driver
189
190
191   for (d = (ppdcDriver *)drivers->first(); d; d = (ppdcDriver *)drivers->next())
192     if (!_cups_strcasecmp(f, d->pc_file_name->value))
193       return (d);
194
195   return (NULL);
196 }
197
198
199 //
200 // 'ppdcSource::find_include()' - Find an include file.
201 //
202
203 char *                                  // O - Found path or NULL
204 ppdcSource::find_include(
205     const char *f,                      // I - Include filename
206     const char *base,                   // I - Current directory
207     char       *n,                      // I - Path buffer
208     int        nlen)                    // I - Path buffer length
209 {
210   ppdcString    *dir;                   // Include directory
211   char          temp[1024],             // Temporary path
212                 *ptr;                   // Pointer to end of path
213
214
215   // Range check input...
216   if (!f || !*f || !n || nlen < 2)
217     return (0);
218
219   // Check the first character to see if we have <name> or "name"...
220   if (*f == '<')
221   {
222     // Remove the surrounding <> from the name...
223     strlcpy(temp, f + 1, sizeof(temp));
224     ptr = temp + strlen(temp) - 1;
225
226     if (*ptr != '>')
227     {
228       _cupsLangPrintf(stderr,
229                       _("ppdc: Invalid #include/#po filename \"%s\"."), n);
230       return (0);
231     }
232
233     *ptr = '\0';
234     f    = temp;
235   }
236   else
237   {
238     // Check for the local file relative to the current directory...
239     if (base && *base && f[0] != '/')
240       snprintf(n, nlen, "%s/%s", base, f);
241     else
242       strlcpy(n, f, nlen);
243
244     if (!access(n, 0))
245       return (n);
246     else if (*f == '/')
247     {
248       // Absolute path that doesn't exist...
249       return (0);
250     }
251   }
252
253   // Search the include directories, if any...
254   if (includes)
255   {
256     for (dir = (ppdcString *)includes->first(); dir; dir = (ppdcString *)includes->next())
257     {
258       snprintf(n, nlen, "%s/%s", dir->value, f);
259       if (!access(n, 0))
260         return (n);
261     }
262   }
263
264   // Search the standard include directories...
265   _cups_globals_t *cg = _cupsGlobals(); // Global data
266
267   snprintf(n, nlen, "%s/ppdc/%s", cg->cups_datadir, f);
268   if (!access(n, 0))
269     return (n);
270
271   snprintf(n, nlen, "%s/po/%s", cg->cups_datadir, f);
272   if (!access(n, 0))
273     return (n);
274   else
275     return (0);
276 }
277
278
279 //
280 // 'ppdcSource::find_po()' - Find a message catalog for the given locale.
281 //
282
283 ppdcCatalog *                           // O - Message catalog or NULL
284 ppdcSource::find_po(const char *l)      // I - Locale name
285 {
286   ppdcCatalog   *cat;                   // Current message catalog
287
288
289   for (cat = (ppdcCatalog *)po_files->first();
290        cat;
291        cat = (ppdcCatalog *)po_files->next())
292     if (!_cups_strcasecmp(l, cat->locale->value))
293       return (cat);
294
295   return (NULL);
296 }
297
298
299 //
300 // 'ppdcSource::find_size()' - Find a media size.
301 //
302
303 ppdcMediaSize *                         // O - Size
304 ppdcSource::find_size(const char *s)    // I - Size name
305 {
306   ppdcMediaSize *m;                     // Current media size
307
308
309   for (m = (ppdcMediaSize *)sizes->first(); m; m = (ppdcMediaSize *)sizes->next())
310     if (!_cups_strcasecmp(s, m->name->value))
311       return (m);
312
313   return (NULL);
314 }
315
316
317 //
318 // 'ppdcSource::find_variable()' - Find a variable.
319 //
320
321 ppdcVariable *                          // O - Variable
322 ppdcSource::find_variable(const char *n)// I - Variable name
323 {
324   ppdcVariable  *v;                     // Current variable
325
326
327   for (v = (ppdcVariable *)vars->first(); v; v = (ppdcVariable *)vars->next())
328     if (!_cups_strcasecmp(n, v->name->value))
329       return (v);
330
331   return (NULL);
332 }
333
334
335 //
336 // 'ppdcSource::get_attr()' - Get an attribute.
337 //
338
339 ppdcAttr *                              // O - Attribute
340 ppdcSource::get_attr(ppdcFile *fp,      // I - File to read
341                      bool     loc)      // I - Localize this attribute?
342 {
343   char  name[1024],                     // Name string
344         selector[1024],                 // Selector string
345         *text,                          // Text string
346         value[1024];                    // Value string
347
348
349   // Get the attribute parameters:
350   //
351   // Attribute name selector value
352   if (!get_token(fp, name, sizeof(name)))
353   {
354     _cupsLangPrintf(stderr,
355                     _("ppdc: Expected name after %s on line %d of %s."),
356                     loc ? "LocAttribute" : "Attribute", fp->line, fp->filename);
357     return (0);
358   }
359
360   if (!get_token(fp, selector, sizeof(selector)))
361   {
362     _cupsLangPrintf(stderr,
363                     _("ppdc: Expected selector after %s on line %d of %s."),
364                     loc ? "LocAttribute" : "Attribute", fp->line, fp->filename);
365     return (0);
366   }
367
368   if ((text = strchr(selector, '/')) != NULL)
369     *text++ = '\0';
370
371   if (!get_token(fp, value, sizeof(value)))
372   {
373     _cupsLangPrintf(stderr,
374                     _("ppdc: Expected value after %s on line %d of %s."),
375                     loc ? "LocAttribute" : "Attribute", fp->line, fp->filename);
376     return (0);
377   }
378
379   return (new ppdcAttr(name, selector, text, value, loc));
380 }
381
382
383 //
384 // 'ppdcSource::get_boolean()' - Get a boolean value.
385 //
386
387 int                                     // O - Boolean value
388 ppdcSource::get_boolean(ppdcFile *fp)   // I - File to read
389 {
390   char  buffer[256];                    // String buffer
391
392
393   if (!get_token(fp, buffer, sizeof(buffer)))
394   {
395     _cupsLangPrintf(stderr,
396                     _("ppdc: Expected boolean value on line %d of %s."),
397                     fp->line, fp->filename);
398     return (-1);
399   }
400
401   if (!_cups_strcasecmp(buffer, "on") ||
402       !_cups_strcasecmp(buffer, "yes") ||
403       !_cups_strcasecmp(buffer, "true"))
404     return (1);
405   else if (!_cups_strcasecmp(buffer, "off") ||
406            !_cups_strcasecmp(buffer, "no") ||
407            !_cups_strcasecmp(buffer, "false"))
408     return (0);
409   else
410   {
411     _cupsLangPrintf(stderr,
412                     _("ppdc: Bad boolean value (%s) on line %d of %s."),
413                     buffer, fp->line, fp->filename);
414     return (-1);
415   }
416 }
417
418
419 //
420 // 'ppdcSource::get_choice()' - Get a choice.
421 //
422
423 ppdcChoice *                            // O - Choice data
424 ppdcSource::get_choice(ppdcFile *fp)    // I - File to read
425 {
426   char  name[1024],                     // Name
427         *text,                          // Text
428         code[10240];                    // Code
429
430
431   // Read a choice from the file:
432   //
433   // Choice name/text code
434   if (!get_token(fp, name, sizeof(name)))
435   {
436     _cupsLangPrintf(stderr,
437                     _("ppdc: Expected choice name/text on line %d of %s."),
438                     fp->line, fp->filename);
439     return (NULL);
440   }
441
442   if ((text = strchr(name, '/')) != NULL)
443     *text++ = '\0';
444   else
445     text = name;
446
447   if (!get_token(fp, code, sizeof(code)))
448   {
449     _cupsLangPrintf(stderr, _("ppdc: Expected choice code on line %d of %s."),
450                     fp->line, fp->filename);
451     return (NULL);
452   }
453
454   // Return the new choice
455   return (new ppdcChoice(name, text, code));
456 }
457
458
459 //
460 // 'ppdcSource::get_color_model()' - Get an old-style color model option.
461 //
462
463 ppdcChoice *                            // O - Choice data
464 ppdcSource::get_color_model(ppdcFile *fp)
465                                         // I - File to read
466 {
467   char          name[1024],             // Option name
468                 *text,                  // Text option
469                 temp[256];              // Temporary string
470   int           color_space,            // Colorspace
471                 color_order,            // Color order
472                 compression;            // Compression mode
473
474
475   // Get the ColorModel parameters:
476   //
477   // ColorModel name/text colorspace colororder compression
478   if (!get_token(fp, name, sizeof(name)))
479   {
480     _cupsLangPrintf(stderr,
481                     _("ppdc: Expected name/text combination for ColorModel on "
482                       "line %d of %s."), fp->line, fp->filename);
483     return (NULL);
484   }
485
486   if ((text = strchr(name, '/')) != NULL)
487     *text++ = '\0';
488   else
489     text = name;
490
491   if (!get_token(fp, temp, sizeof(temp)))
492   {
493     _cupsLangPrintf(stderr,
494                     _("ppdc: Expected colorspace for ColorModel on line %d of "
495                       "%s."), fp->line, fp->filename);
496     return (NULL);
497   }
498
499   if ((color_space = get_color_space(temp)) < 0)
500     color_space = get_integer(temp);
501
502   if (!get_token(fp, temp, sizeof(temp)))
503   {
504     _cupsLangPrintf(stderr,
505                     _("ppdc: Expected color order for ColorModel on line %d of "
506                       "%s."), fp->line, fp->filename);
507     return (NULL);
508   }
509
510   if ((color_order = get_color_order(temp)) < 0)
511     color_order = get_integer(temp);
512
513   if (!get_token(fp, temp, sizeof(temp)))
514   {
515     _cupsLangPrintf(stderr,
516                     _("ppdc: Expected compression for ColorModel on line %d of "
517                       "%s."), fp->line, fp->filename);
518     return (NULL);
519   }
520
521   compression = get_integer(temp);
522
523   snprintf(temp, sizeof(temp),
524            "<</cupsColorSpace %d/cupsColorOrder %d/cupsCompression %d>>"
525            "setpagedevice",
526            color_space, color_order, compression);
527
528   return (new ppdcChoice(name, text, temp));
529 }
530
531
532 //
533 // 'ppdcSource::get_color_order()' - Get an old-style color order value.
534 //
535
536 int                                     // O - Color order value
537 ppdcSource::get_color_order(
538     const char *co)                     // I - Color order string
539 {
540   if (!_cups_strcasecmp(co, "chunked") ||
541       !_cups_strcasecmp(co, "chunky"))
542     return (CUPS_ORDER_CHUNKED);
543   else if (!_cups_strcasecmp(co, "banded"))
544     return (CUPS_ORDER_BANDED);
545   else if (!_cups_strcasecmp(co, "planar"))
546     return (CUPS_ORDER_PLANAR);
547   else
548     return (-1);
549 }
550
551
552 //
553 // 'ppdcSource::get_color_profile()' - Get a color profile definition.
554 //
555
556 ppdcProfile *                           // O - Color profile
557 ppdcSource::get_color_profile(
558     ppdcFile *fp)                       // I - File to read
559 {
560   char          resolution[1024],       // Resolution/media type
561                 *media_type;            // Media type
562   int           i;                      // Looping var
563   float         g,                      // Gamma value
564                 d,                      // Density value
565                 m[9];                   // Transform matrix
566
567
568   // Get the ColorProfile parameters:
569   //
570   // ColorProfile resolution/mediatype gamma density m00 m01 m02 ... m22
571   if (!get_token(fp, resolution, sizeof(resolution)))
572   {
573     _cupsLangPrintf(stderr,
574                     _("ppdc: Expected resolution/mediatype following "
575                       "ColorProfile on line %d of %s."),
576                     fp->line, fp->filename);
577     return (NULL);
578   }
579
580   if ((media_type = strchr(resolution, '/')) != NULL)
581     *media_type++ = '\0';
582   else
583     media_type = resolution;
584
585   g = get_float(fp);
586   d = get_float(fp);
587   for (i = 0; i < 9; i ++)
588     m[i] = get_float(fp);
589
590   return (new ppdcProfile(resolution, media_type, g, d, m));
591 }
592
593
594 //
595 // 'ppdcSource::get_color_space()' - Get an old-style colorspace value.
596 //
597
598 int                                     // O - Colorspace value
599 ppdcSource::get_color_space(
600     const char *cs)                     // I - Colorspace string
601 {
602   if (!_cups_strcasecmp(cs, "w"))
603     return (CUPS_CSPACE_W);
604   else if (!_cups_strcasecmp(cs, "rgb"))
605     return (CUPS_CSPACE_RGB);
606   else if (!_cups_strcasecmp(cs, "rgba"))
607     return (CUPS_CSPACE_RGBA);
608   else if (!_cups_strcasecmp(cs, "k"))
609     return (CUPS_CSPACE_K);
610   else if (!_cups_strcasecmp(cs, "cmy"))
611     return (CUPS_CSPACE_CMY);
612   else if (!_cups_strcasecmp(cs, "ymc"))
613     return (CUPS_CSPACE_YMC);
614   else if (!_cups_strcasecmp(cs, "cmyk"))
615     return (CUPS_CSPACE_CMYK);
616   else if (!_cups_strcasecmp(cs, "ymck"))
617     return (CUPS_CSPACE_YMCK);
618   else if (!_cups_strcasecmp(cs, "kcmy"))
619     return (CUPS_CSPACE_KCMY);
620   else if (!_cups_strcasecmp(cs, "kcmycm"))
621     return (CUPS_CSPACE_KCMYcm);
622   else if (!_cups_strcasecmp(cs, "gmck"))
623     return (CUPS_CSPACE_GMCK);
624   else if (!_cups_strcasecmp(cs, "gmcs"))
625     return (CUPS_CSPACE_GMCS);
626   else if (!_cups_strcasecmp(cs, "white"))
627     return (CUPS_CSPACE_WHITE);
628   else if (!_cups_strcasecmp(cs, "gold"))
629     return (CUPS_CSPACE_GOLD);
630   else if (!_cups_strcasecmp(cs, "silver"))
631     return (CUPS_CSPACE_SILVER);
632   else if (!_cups_strcasecmp(cs, "CIEXYZ"))
633     return (CUPS_CSPACE_CIEXYZ);
634   else if (!_cups_strcasecmp(cs, "CIELab"))
635     return (CUPS_CSPACE_CIELab);
636   else if (!_cups_strcasecmp(cs, "RGBW"))
637     return (CUPS_CSPACE_RGBW);
638   else if (!_cups_strcasecmp(cs, "ICC1"))
639     return (CUPS_CSPACE_ICC1);
640   else if (!_cups_strcasecmp(cs, "ICC2"))
641     return (CUPS_CSPACE_ICC2);
642   else if (!_cups_strcasecmp(cs, "ICC3"))
643     return (CUPS_CSPACE_ICC3);
644   else if (!_cups_strcasecmp(cs, "ICC4"))
645     return (CUPS_CSPACE_ICC4);
646   else if (!_cups_strcasecmp(cs, "ICC5"))
647     return (CUPS_CSPACE_ICC5);
648   else if (!_cups_strcasecmp(cs, "ICC6"))
649     return (CUPS_CSPACE_ICC6);
650   else if (!_cups_strcasecmp(cs, "ICC7"))
651     return (CUPS_CSPACE_ICC7);
652   else if (!_cups_strcasecmp(cs, "ICC8"))
653     return (CUPS_CSPACE_ICC8);
654   else if (!_cups_strcasecmp(cs, "ICC9"))
655     return (CUPS_CSPACE_ICC9);
656   else if (!_cups_strcasecmp(cs, "ICCA"))
657     return (CUPS_CSPACE_ICCA);
658   else if (!_cups_strcasecmp(cs, "ICCB"))
659     return (CUPS_CSPACE_ICCB);
660   else if (!_cups_strcasecmp(cs, "ICCC"))
661     return (CUPS_CSPACE_ICCC);
662   else if (!_cups_strcasecmp(cs, "ICCD"))
663     return (CUPS_CSPACE_ICCD);
664   else if (!_cups_strcasecmp(cs, "ICCE"))
665     return (CUPS_CSPACE_ICCE);
666   else if (!_cups_strcasecmp(cs, "ICCF"))
667     return (CUPS_CSPACE_ICCF);
668   else
669     return (-1);
670 }
671
672
673 //
674 // 'ppdcSource::get_constraint()' - Get a constraint.
675 //
676
677 ppdcConstraint *                        // O - Constraint
678 ppdcSource::get_constraint(ppdcFile *fp)// I - File to read
679 {
680   char          temp[1024],             // One string to rule them all
681                 *ptr,                   // Pointer into string
682                 *option1,               // Constraint option 1
683                 *choice1,               // Constraint choice 1
684                 *option2,               // Constraint option 2
685                 *choice2;               // Constraint choice 2
686
687
688   // Read the UIConstaints parameter in one of the following forms:
689   //
690   // UIConstraints "*Option1 *Option2"
691   // UIConstraints "*Option1 Choice1 *Option2"
692   // UIConstraints "*Option1 *Option2 Choice2"
693   // UIConstraints "*Option1 Choice1 *Option2 Choice2"
694   if (!get_token(fp, temp, sizeof(temp)))
695   {
696     _cupsLangPrintf(stderr,
697                     _("ppdc: Expected constraints string for UIConstraints on "
698                       "line %d of %s."), fp->line, fp->filename);
699     return (NULL);
700   }
701
702   for (ptr = temp; isspace(*ptr); ptr ++);
703
704   if (*ptr != '*')
705   {
706     _cupsLangPrintf(stderr,
707                     _("ppdc: Option constraint must *name on line %d of %s."),
708                     fp->line, fp->filename);
709     return (NULL);
710   }
711
712   option1 = ptr;
713
714   for (; *ptr && !isspace(*ptr); ptr ++);
715   for (; isspace(*ptr); *ptr++ = '\0');
716
717   if (*ptr != '*')
718   {
719     choice1 = ptr;
720
721     for (; *ptr && !isspace(*ptr); ptr ++);
722     for (; isspace(*ptr); *ptr++ = '\0');
723   }
724   else
725     choice1 = NULL;
726
727   if (*ptr != '*')
728   {
729     _cupsLangPrintf(stderr,
730                     _("ppdc: Expected two option names on line %d of %s."),
731                     fp->line, fp->filename);
732     return (NULL);
733   }
734
735   option2 = ptr;
736
737   for (; *ptr && !isspace(*ptr); ptr ++);
738   for (; isspace(*ptr); *ptr++ = '\0');
739
740   if (*ptr)
741     choice2 = ptr;
742   else
743     choice2 = NULL;
744
745   return (new ppdcConstraint(option1, choice1, option2, choice2));
746 }
747
748
749 //
750 // 'ppdcSource::get_custom_size()' - Get a custom media size definition from a file.
751 //
752
753 ppdcMediaSize *                         // O - Media size
754 ppdcSource::get_custom_size(ppdcFile *fp)
755                                         // I - File to read
756 {
757   char          name[1024],             // Name
758                 *text,                  // Text
759                 size_code[10240],       // PageSize code
760                 region_code[10240];     // PageRegion
761   float         width,                  // Width
762                 length,                 // Length
763                 left,                   // Left margin
764                 bottom,                 // Bottom margin
765                 right,                  // Right margin
766                 top;                    // Top margin
767
768
769   // Get the name, text, width, length, margins, and code:
770   //
771   // CustomMedia name/text width length left bottom right top size-code region-code
772   if (!get_token(fp, name, sizeof(name)))
773     return (NULL);
774
775   if ((text = strchr(name, '/')) != NULL)
776     *text++ = '\0';
777   else
778     text = name;
779
780   if ((width = get_measurement(fp)) < 0.0f)
781     return (NULL);
782
783   if ((length = get_measurement(fp)) < 0.0f)
784     return (NULL);
785
786   if ((left = get_measurement(fp)) < 0.0f)
787     return (NULL);
788
789   if ((bottom = get_measurement(fp)) < 0.0f)
790     return (NULL);
791
792   if ((right = get_measurement(fp)) < 0.0f)
793     return (NULL);
794
795   if ((top = get_measurement(fp)) < 0.0f)
796     return (NULL);
797
798   if (!get_token(fp, size_code, sizeof(size_code)))
799     return (NULL);
800
801   if (!get_token(fp, region_code, sizeof(region_code)))
802     return (NULL);
803
804   // Return the new media size...
805   return (new ppdcMediaSize(name, text, width, length, left, bottom,
806                             right, top, size_code, region_code));
807 }
808
809
810 //
811 // 'ppdcSource::get_duplex()' - Get a duplex option.
812 //
813
814 void
815 ppdcSource::get_duplex(ppdcFile   *fp,  // I - File to read from
816                        ppdcDriver *d)   // I - Current driver
817 {
818   char          temp[256];              // Duplex keyword
819   ppdcAttr      *attr;                  // cupsFlipDuplex attribute
820   ppdcGroup     *g;                     // Current group
821   ppdcOption    *o;                     // Duplex option
822
823
824   // Duplex {boolean|none|normal|flip}
825   if (!get_token(fp, temp, sizeof(temp)))
826   {
827     _cupsLangPrintf(stderr,
828                     _("ppdc: Expected duplex type after Duplex on line %d of "
829                       "%s."), fp->line, fp->filename);
830     return;
831   }
832
833   if (cond_state)
834     return;
835
836   if (!_cups_strcasecmp(temp, "none") || !_cups_strcasecmp(temp, "false") ||
837       !_cups_strcasecmp(temp, "no") || !_cups_strcasecmp(temp, "off"))
838   {
839     g = d->find_group("General");
840     if ((o = g->find_option("Duplex")) != NULL)
841       g->options->remove(o);
842
843     for (attr = (ppdcAttr *)d->attrs->first();
844          attr;
845          attr = (ppdcAttr *)d->attrs->next())
846       if (!strcmp(attr->name->value, "cupsFlipDuplex"))
847       {
848         d->attrs->remove(attr);
849         break;
850       }
851   }
852   else if (!_cups_strcasecmp(temp, "normal") || !_cups_strcasecmp(temp, "true") ||
853            !_cups_strcasecmp(temp, "yes") || !_cups_strcasecmp(temp, "on") ||
854            !_cups_strcasecmp(temp, "flip") || !_cups_strcasecmp(temp, "rotated") ||
855            !_cups_strcasecmp(temp, "manualtumble"))
856   {
857     g = d->find_group("General");
858     o = g->find_option("Duplex");
859
860     if (!o)
861     {
862       o = new ppdcOption(PPDC_PICKONE, "Duplex", "2-Sided Printing",
863                          !_cups_strcasecmp(temp, "flip") ? PPDC_SECTION_PAGE :
864                                                      PPDC_SECTION_ANY, 10.0f);
865       o->add_choice(new ppdcChoice("None", "Off (1-Sided)",
866                                    "<</Duplex false>>setpagedevice"));
867       o->add_choice(new ppdcChoice("DuplexNoTumble", "Long-Edge (Portrait)",
868                                    "<</Duplex true/Tumble false>>setpagedevice"));
869       o->add_choice(new ppdcChoice("DuplexTumble", "Short-Edge (Landscape)",
870                                    "<</Duplex true/Tumble true>>setpagedevice"));
871
872       g->add_option(o);
873     }
874
875     for (attr = (ppdcAttr *)d->attrs->first();
876          attr;
877          attr = (ppdcAttr *)d->attrs->next())
878       if (!strcmp(attr->name->value, "cupsFlipDuplex"))
879       {
880         if (_cups_strcasecmp(temp, "flip"))
881           d->attrs->remove(attr);
882         break;
883       }
884
885     if (!_cups_strcasecmp(temp, "flip") && !attr)
886       d->add_attr(new ppdcAttr("cupsFlipDuplex", NULL, NULL, "true"));
887
888     for (attr = (ppdcAttr *)d->attrs->first();
889          attr;
890          attr = (ppdcAttr *)d->attrs->next())
891       if (!strcmp(attr->name->value, "cupsBackSide"))
892       {
893         d->attrs->remove(attr);
894         break;
895       }
896
897     if (!_cups_strcasecmp(temp, "flip"))
898       d->add_attr(new ppdcAttr("cupsBackSide", NULL, NULL, "Flipped"));
899     else if (!_cups_strcasecmp(temp, "rotated"))
900       d->add_attr(new ppdcAttr("cupsBackSide", NULL, NULL, "Rotated"));
901     else if (!_cups_strcasecmp(temp, "manualtumble"))
902       d->add_attr(new ppdcAttr("cupsBackSide", NULL, NULL, "ManualTumble"));
903     else
904       d->add_attr(new ppdcAttr("cupsBackSide", NULL, NULL, "Normal"));
905   }
906   else
907     _cupsLangPrintf(stderr,
908                     _("ppdc: Unknown duplex type \"%s\" on line %d of %s."),
909                     temp, fp->line, fp->filename);
910 }
911
912
913 //
914 // 'ppdcSource::get_filter()' - Get a filter.
915 //
916
917 ppdcFilter *                            // O - Filter
918 ppdcSource::get_filter(ppdcFile *fp)    // I - File to read
919 {
920   char  type[1024],                     // MIME type
921         program[1024],                  // Filter program
922         *ptr;                           // Pointer into MIME type
923   int   cost;                           // Relative cost
924
925
926   // Read filter parameters in one of the following formats:
927   //
928   // Filter "type cost program"
929   // Filter type cost program
930
931   if (!get_token(fp, type, sizeof(type)))
932   {
933     _cupsLangPrintf(stderr,
934                     _("ppdc: Expected a filter definition on line %d of %s."),
935                     fp->line, fp->filename);
936     return (NULL);
937   }
938
939   if ((ptr = strchr(type, ' ')) != NULL)
940   {
941     // Old-style filter definition in one string...
942     *ptr++ = '\0';
943     cost = strtol(ptr, &ptr, 10);
944
945     while (isspace(*ptr))
946       ptr ++;
947
948     strcpy(program, ptr);
949   }
950   else
951   {
952     cost = get_integer(fp);
953
954     if (!get_token(fp, program, sizeof(program)))
955     {
956       _cupsLangPrintf(stderr,
957                       _("ppdc: Expected a program name on line %d of %s."),
958                       fp->line, fp->filename);
959       return (NULL);
960     }
961   }
962
963   if (!type[0])
964   {
965     _cupsLangPrintf(stderr,
966                     _("ppdc: Invalid empty MIME type for filter on line %d of "
967                       "%s."), fp->line, fp->filename);
968     return (NULL);
969   }
970
971   if (cost < 0 || cost > 200)
972   {
973     _cupsLangPrintf(stderr,
974                     _("ppdc: Invalid cost for filter on line %d of %s."),
975                     fp->line, fp->filename);
976     return (NULL);
977   }
978
979   if (!program[0])
980   {
981     _cupsLangPrintf(stderr,
982                     _("ppdc: Invalid empty program name for filter on line %d "
983                       "of %s."), fp->line, fp->filename);
984     return (NULL);
985   }
986
987   return (new ppdcFilter(type, program, cost));
988 }
989
990
991 //
992 // 'ppdcSource::get_float()' - Get a single floating-point number.
993 //
994
995 float                                   // O - Number
996 ppdcSource::get_float(ppdcFile *fp)     // I - File to read
997 {
998   char  temp[256],                      // String buffer
999         *ptr;                           // Pointer into buffer
1000   float val;                            // Floating point value
1001
1002
1003   // Get the number from the file and range-check...
1004   if (!get_token(fp, temp, sizeof(temp)))
1005   {
1006     _cupsLangPrintf(stderr, _("ppdc: Expected real number on line %d of %s."),
1007                     fp->line, fp->filename);
1008     return (-1.0f);
1009   }
1010
1011   val = (float)strtod(temp, &ptr);
1012
1013   if (*ptr)
1014   {
1015     _cupsLangPrintf(stderr,
1016                     _("ppdc: Unknown trailing characters in real number \"%s\" "
1017                       "on line %d of %s."), temp, fp->line, fp->filename);
1018     return (-1.0f);
1019   }
1020   else
1021     return (val);
1022 }
1023
1024
1025 //
1026 // 'ppdcSource::get_font()' - Get a font definition.
1027 //
1028
1029 ppdcFont *                              // O - Font data
1030 ppdcSource::get_font(ppdcFile *fp)      // I - File to read
1031 {
1032   char                  name[256],      // Font name
1033                         encoding[256],  // Font encoding
1034                         version[256],   // Font version
1035                         charset[256],   // Font charset
1036                         temp[256];      // Font status string
1037   ppdcFontStatus        status;         // Font status enumeration
1038
1039
1040   // Read font parameters as follows:
1041   //
1042   // Font *
1043   // Font name encoding version charset status
1044   // %font name encoding version charset status
1045   //
1046   // "Name" is the PostScript font name.
1047   //
1048   // "Encoding" is the default encoding of the font: Standard, ISOLatin1,
1049   // Special, Expert, ExpertSubset, etc.
1050   //
1051   // "Version" is the version number string.
1052   //
1053   // "Charset" specifies the characters that are included in the font:
1054   // Standard, Special, Expert, Adobe-Identity, etc.
1055   //
1056   // "Status" is the keyword ROM or Disk.
1057   if (!get_token(fp, name, sizeof(name)))
1058   {
1059     _cupsLangPrintf(stderr,
1060                     _("ppdc: Expected name after Font on line %d of %s."),
1061                     fp->line, fp->filename);
1062     return (0);
1063   }
1064
1065   if (!strcmp(name, "*"))
1066   {
1067     // Include all base fonts...
1068     encoding[0] = '\0';
1069     version[0]  = '\0';
1070     charset[0]  = '\0';
1071     status      = PPDC_FONT_ROM;
1072   }
1073   else
1074   {
1075     // Load a full font definition...
1076     if (!get_token(fp, encoding, sizeof(encoding)))
1077     {
1078       _cupsLangPrintf(stderr,
1079                       _("ppdc: Expected encoding after Font on line %d of "
1080                         "%s."), fp->line, fp->filename);
1081       return (0);
1082     }
1083
1084     if (!get_token(fp, version, sizeof(version)))
1085     {
1086       _cupsLangPrintf(stderr,
1087                       _("ppdc: Expected version after Font on line %d of "
1088                         "%s."), fp->line, fp->filename);
1089       return (0);
1090     }
1091
1092     if (!get_token(fp, charset, sizeof(charset)))
1093     {
1094       _cupsLangPrintf(stderr,
1095                       _("ppdc: Expected charset after Font on line %d of "
1096                         "%s."), fp->line, fp->filename);
1097       return (0);
1098     }
1099
1100     if (!get_token(fp, temp, sizeof(temp)))
1101     {
1102       _cupsLangPrintf(stderr,
1103                       _("ppdc: Expected status after Font on line %d of %s."),
1104                       fp->line, fp->filename);
1105       return (0);
1106     }
1107
1108     if (!_cups_strcasecmp(temp, "ROM"))
1109       status = PPDC_FONT_ROM;
1110     else if (!_cups_strcasecmp(temp, "Disk"))
1111       status = PPDC_FONT_DISK;
1112     else
1113     {
1114       _cupsLangPrintf(stderr,
1115                       _("ppdc: Bad status keyword %s on line %d of %s."),
1116                       temp, fp->line, fp->filename);
1117       return (0);
1118     }
1119   }
1120
1121 //  printf("Font %s %s %s %s %s\n", name, encoding, version, charset, temp);
1122
1123   return (new ppdcFont(name, encoding, version, charset, status));
1124 }
1125
1126
1127 //
1128 // 'ppdcSource::get_generic()' - Get a generic old-style option.
1129 //
1130
1131 ppdcChoice *                            // O - Choice data
1132 ppdcSource::get_generic(ppdcFile   *fp, // I - File to read
1133                         const char *keyword,
1134                                         // I - Keyword name
1135                         const char *tattr,
1136                                         // I - Text attribute
1137                         const char *nattr)
1138                                         // I - Numeric attribute
1139 {
1140   char          name[1024],             // Name
1141                 *text,                  // Text
1142                 command[256];           // Command string
1143   int           val;                    // Numeric value
1144
1145
1146   // Read one of the following parameters:
1147   //
1148   // Foo name/text
1149   // Foo integer name/text
1150   if (nattr)
1151     val = get_integer(fp);
1152   else
1153     val = 0;
1154
1155   if (!get_token(fp, name, sizeof(name)))
1156   {
1157     _cupsLangPrintf(stderr,
1158                     _("ppdc: Expected name/text after %s on line %d of %s."),
1159                     keyword, fp->line, fp->filename);
1160     return (NULL);
1161   }
1162
1163   if ((text = strchr(name, '/')) != NULL)
1164     *text++ = '\0';
1165   else
1166     text = name;
1167
1168   if (nattr)
1169   {
1170     if (tattr)
1171       snprintf(command, sizeof(command),
1172                "<</%s(%s)/%s %d>>setpagedevice",
1173                tattr, name, nattr, val);
1174     else
1175       snprintf(command, sizeof(command),
1176                "<</%s %d>>setpagedevice",
1177                nattr, val);
1178   }
1179   else
1180     snprintf(command, sizeof(command),
1181              "<</%s(%s)>>setpagedevice",
1182              tattr, name);
1183
1184   return (new ppdcChoice(name, text, command));
1185 }
1186
1187
1188 //
1189 // 'ppdcSource::get_group()' - Get an option group.
1190 //
1191
1192 ppdcGroup *                             // O - Group
1193 ppdcSource::get_group(ppdcFile   *fp,   // I - File to read
1194                       ppdcDriver *d)    // I - Printer driver
1195 {
1196   char          name[1024],             // UI name
1197                 *text;                  // UI text
1198   ppdcGroup     *g;                     // Group
1199
1200
1201   // Read the Group parameters:
1202   //
1203   // Group name/text
1204   if (!get_token(fp, name, sizeof(name)))
1205   {
1206     _cupsLangPrintf(stderr,
1207                     _("ppdc: Expected group name/text on line %d of %s."),
1208                     fp->line, fp->filename);
1209     return (NULL);
1210   }
1211
1212   if ((text = strchr(name, '/')) != NULL)
1213     *text++ = '\0';
1214   else
1215     text = name;
1216
1217   // See if the group already exists...
1218   if ((g = d->find_group(name)) == NULL)
1219   {
1220     // Nope, add a new one...
1221     g = new ppdcGroup(name, text);
1222   }
1223
1224   return (g);
1225 }
1226
1227
1228 //
1229 // 'ppdcSource::get_installable()' - Get an installable option.
1230 //
1231
1232 ppdcOption *                            // O - Option
1233 ppdcSource::get_installable(ppdcFile *fp)
1234                                         // I - File to read
1235 {
1236   char          name[1024],             // Name for installable option
1237                 *text;                  // Text for installable option
1238   ppdcOption    *o;                     // Option
1239
1240
1241   // Read the parameter for an installable option:
1242   //
1243   // Installable name/text
1244   if (!get_token(fp, name, sizeof(name)))
1245   {
1246     _cupsLangPrintf(stderr,
1247                     _("ppdc: Expected name/text after Installable on line %d "
1248                       "of %s."), fp->line, fp->filename);
1249     return (NULL);
1250   }
1251
1252   if ((text = strchr(name, '/')) != NULL)
1253     *text++ = '\0';
1254   else
1255     text = name;
1256
1257   // Create the option...
1258   o = new ppdcOption(PPDC_BOOLEAN, name, text, PPDC_SECTION_ANY, 10.0f);
1259
1260   // Add the false and true choices...
1261   o->add_choice(new ppdcChoice("False", "Not Installed", ""));
1262   o->add_choice(new ppdcChoice("True", "Installed", ""));
1263
1264   return (o);
1265 }
1266
1267
1268 //
1269 // 'ppdcSource::get_integer()' - Get an integer value from a string.
1270 //
1271
1272 #define PPDC_XX -1                      // Bad
1273 #define PPDC_EQ 0                       // ==
1274 #define PPDC_NE 1                       // !=
1275 #define PPDC_LT 2                       // <
1276 #define PPDC_LE 3                       // <=
1277 #define PPDC_GT 4                       // >
1278 #define PPDC_GE 5                       // >=
1279
1280 int                                     // O - Integer value
1281 ppdcSource::get_integer(const char *v)  // I - Value string
1282 {
1283   long          val;                    // Value
1284   long          temp,                   // Temporary value
1285                 temp2;                  // Second temporary value
1286   char          *newv,                  // New value string pointer
1287                 ch;                     // Temporary character
1288   ppdcVariable  *var;                   // #define variable
1289   int           compop;                 // Comparison operator
1290
1291
1292   // Parse the value string...
1293   if (!v)
1294     return (-1);
1295
1296   if (isdigit(*v & 255) || *v == '-' || *v == '+')
1297   {
1298     // Return a simple integer value
1299     val = strtol(v, (char **)&v, 0);
1300     if (*v || val == LONG_MIN)
1301       return (-1);
1302     else
1303       return ((int)val);
1304   }
1305   else if (*v == '(')
1306   {
1307     // Evaluate and expression in any of the following formats:
1308     //
1309     // (number number ... number)   Bitwise OR of all numbers
1310     // (NAME == value)              1 if equal, 0 otherwise
1311     // (NAME != value)              1 if not equal, 0 otherwise
1312     // (NAME < value)               1 if less than, 0 otherwise
1313     // (NAME <= value)              1 if less than or equal, 0 otherwise
1314     // (NAME > value)               1 if greater than, 0 otherwise
1315     // (NAME >= value)              1 if greater than or equal, 0 otherwise
1316
1317     v ++;
1318     val = 0;
1319
1320     while (*v && *v != ')')
1321     {
1322       // Skip leading whitespace...
1323       while (*v && isspace(*v & 255))
1324         v ++;
1325
1326       if (!*v || *v == ')')
1327         break;
1328
1329       if (isdigit(*v & 255) || *v == '-' || *v == '+')
1330       {
1331         // Bitwise OR a number...
1332         temp = strtol(v, &newv, 0);
1333
1334         if (!*newv || newv == v || !(isspace(*newv) || *newv == ')') ||
1335             temp == LONG_MIN)
1336           return (-1);
1337       }
1338       else
1339       {
1340         // NAME logicop value
1341         for (newv = (char *)v + 1;
1342              *newv && (isalnum(*newv & 255) || *newv == '_');
1343              newv ++)
1344           /* do nothing */;
1345
1346         ch    = *newv;
1347         *newv = '\0';
1348
1349         if ((var = find_variable(v)) != NULL)
1350         {
1351           if (!var->value || !var->value->value || !var->value->value[0])
1352             temp = 0;
1353           else if (isdigit(var->value->value[0] & 255) ||
1354                    var->value->value[0] == '-' ||
1355                    var->value->value[0] == '+')
1356             temp = strtol(var->value->value, NULL, 0);
1357           else
1358             temp = 1;
1359         }
1360         else
1361           temp = 0;
1362
1363         *newv = ch;
1364         while (isspace(*newv & 255))
1365           newv ++;
1366
1367         if (!strncmp(newv, "==", 2))
1368         {
1369           compop = PPDC_EQ;
1370           newv += 2;
1371         }
1372         else if (!strncmp(newv, "!=", 2))
1373         {
1374           compop = PPDC_NE;
1375           newv += 2;
1376         }
1377         else if (!strncmp(newv, "<=", 2))
1378         {
1379           compop = PPDC_LE;
1380           newv += 2;
1381         }
1382         else if (*newv == '<')
1383         {
1384           compop = PPDC_LT;
1385           newv ++;
1386         }
1387         else if (!strncmp(newv, ">=", 2))
1388         {
1389           compop = PPDC_GE;
1390           newv += 2;
1391         }
1392         else if (*newv == '>')
1393         {
1394           compop = PPDC_GT;
1395           newv ++;
1396         }
1397         else
1398           compop = PPDC_XX;
1399
1400         if (compop != PPDC_XX)
1401         {
1402           while (isspace(*newv & 255))
1403             newv ++;
1404
1405           if (*newv == ')' || !*newv)
1406             return (-1);
1407
1408           if (isdigit(*newv & 255) || *newv == '-' || *newv == '+')
1409           {
1410             // Get the second number...
1411             temp2 = strtol(newv, &newv, 0);
1412             if (!*newv || newv == v || !(isspace(*newv) || *newv == ')') ||
1413                 temp == LONG_MIN)
1414               return (-1);
1415           }
1416           else
1417           {
1418             // Lookup the second name...
1419             for (v = newv, newv ++;
1420                  *newv && (isalnum(*newv & 255) || *newv == '_');
1421                  newv ++);
1422
1423             ch    = *newv;
1424             *newv = '\0';
1425
1426             if ((var = find_variable(v)) != NULL)
1427             {
1428               if (!var->value || !var->value->value || !var->value->value[0])
1429                 temp2 = 0;
1430               else if (isdigit(var->value->value[0] & 255) ||
1431                        var->value->value[0] == '-' ||
1432                        var->value->value[0] == '+')
1433                 temp2 = strtol(var->value->value, NULL, 0);
1434               else
1435                 temp2 = 1;
1436             }
1437             else
1438               temp2 = 0;
1439
1440             *newv = ch;
1441           }
1442
1443           // Do the comparison...
1444           switch (compop)
1445           {
1446             case PPDC_EQ :
1447                 temp = temp == temp2;
1448                 break;
1449             case PPDC_NE :
1450                 temp = temp != temp2;
1451                 break;
1452             case PPDC_LT :
1453                 temp = temp < temp2;
1454                 break;
1455             case PPDC_LE :
1456                 temp = temp <= temp2;
1457                 break;
1458             case PPDC_GT :
1459                 temp = temp > temp2;
1460                 break;
1461             case PPDC_GE :
1462                 temp = temp >= temp2;
1463                 break;
1464           }
1465         }
1466       }
1467
1468       val |= temp;
1469       v   = newv;
1470     }
1471
1472     if (*v == ')' && !v[1])
1473       return ((int)val);
1474     else
1475       return (-1);
1476   }
1477   else if ((var = find_variable(v)) != NULL)
1478   {
1479     // NAME by itself returns 1 if the #define variable is not blank and
1480     // not "0"...
1481     return (var->value->value && var->value->value[0] &&
1482             strcmp(var->value->value, "0"));
1483   }
1484   else
1485   {
1486     // Anything else is an error...
1487     return (-1);
1488   }
1489 }
1490
1491
1492 //
1493 // 'ppdcSource::get_integer()' - Get an integer value from a file.
1494 //
1495
1496 int                                     // O - Integer value
1497 ppdcSource::get_integer(ppdcFile *fp)   // I - File to read
1498 {
1499   char  temp[1024];                     // String buffer
1500
1501
1502   if (!get_token(fp, temp, sizeof(temp)))
1503   {
1504     _cupsLangPrintf(stderr, _("ppdc: Expected integer on line %d of %s."),
1505                     fp->line, fp->filename);
1506     return (-1);
1507   }
1508   else
1509     return (get_integer(temp));
1510 }
1511
1512
1513 //
1514 // 'ppdcSource::get_measurement()' - Get a measurement value.
1515 //
1516
1517 float                                   // O - Measurement value in points
1518 ppdcSource::get_measurement(ppdcFile *fp)
1519                                         // I - File to read
1520 {
1521   char  buffer[256],                    // Number buffer
1522         *ptr;                           // Pointer into buffer
1523   float val;                            // Measurement value
1524
1525
1526   // Grab a token from the file...
1527   if (!get_token(fp, buffer, sizeof(buffer)))
1528     return (-1.0f);
1529
1530   // Get the floating point value of "s" and skip all digits and decimal points.
1531   val = (float)strtod(buffer, &ptr);
1532
1533   // Check for a trailing unit specifier...
1534   if (!_cups_strcasecmp(ptr, "mm"))
1535     val *= 72.0f / 25.4f;
1536   else if (!_cups_strcasecmp(ptr, "cm"))
1537     val *= 72.0f / 2.54f;
1538   else if (!_cups_strcasecmp(ptr, "m"))
1539     val *= 72.0f / 0.0254f;
1540   else if (!_cups_strcasecmp(ptr, "in"))
1541     val *= 72.0f;
1542   else if (!_cups_strcasecmp(ptr, "ft"))
1543     val *= 72.0f * 12.0f;
1544   else if (_cups_strcasecmp(ptr, "pt") && *ptr)
1545     return (-1.0f);
1546
1547   return (val);
1548 }
1549
1550
1551 //
1552 // 'ppdcSource::get_option()' - Get an option definition.
1553 //
1554
1555 ppdcOption *                            // O - Option
1556 ppdcSource::get_option(ppdcFile   *fp,  // I - File to read
1557                        ppdcDriver *d,   // I - Printer driver
1558                        ppdcGroup  *g)   // I - Current group
1559 {
1560   char          name[1024],             // UI name
1561                 *text,                  // UI text
1562                 type[256];              // UI type string
1563   ppdcOptType   ot;                     // Option type value
1564   ppdcOptSection section;               // Option section
1565   float         order;                  // Option order
1566   ppdcOption    *o;                     // Option
1567   ppdcGroup     *mg;                    // Matching group, if any
1568
1569
1570   // Read the Option parameters:
1571   //
1572   // Option name/text type section order
1573   if (!get_token(fp, name, sizeof(name)))
1574   {
1575     _cupsLangPrintf(stderr,
1576                     _("ppdc: Expected option name/text on line %d of %s."),
1577                     fp->line, fp->filename);
1578     return (NULL);
1579   }
1580
1581   if ((text = strchr(name, '/')) != NULL)
1582     *text++ = '\0';
1583   else
1584     text = name;
1585
1586   if (!get_token(fp, type, sizeof(type)))
1587   {
1588     _cupsLangPrintf(stderr, _("ppdc: Expected option type on line %d of %s."),
1589                     fp->line, fp->filename);
1590     return (NULL);
1591   }
1592
1593   if (!_cups_strcasecmp(type, "boolean"))
1594     ot = PPDC_BOOLEAN;
1595   else if (!_cups_strcasecmp(type, "pickone"))
1596     ot = PPDC_PICKONE;
1597   else if (!_cups_strcasecmp(type, "pickmany"))
1598     ot = PPDC_PICKMANY;
1599   else
1600   {
1601     _cupsLangPrintf(stderr,
1602                     _("ppdc: Invalid option type \"%s\" on line %d of %s."),
1603                     type, fp->line, fp->filename);
1604     return (NULL);
1605   }
1606
1607   if (!get_token(fp, type, sizeof(type)))
1608   {
1609     _cupsLangPrintf(stderr,
1610                     _("ppdc: Expected option section on line %d of %s."),
1611                     fp->line, fp->filename);
1612     return (NULL);
1613   }
1614
1615   if (!_cups_strcasecmp(type, "AnySetup"))
1616     section = PPDC_SECTION_ANY;
1617   else if (!_cups_strcasecmp(type, "DocumentSetup"))
1618     section = PPDC_SECTION_DOCUMENT;
1619   else if (!_cups_strcasecmp(type, "ExitServer"))
1620     section = PPDC_SECTION_EXIT;
1621   else if (!_cups_strcasecmp(type, "JCLSetup"))
1622     section = PPDC_SECTION_JCL;
1623   else if (!_cups_strcasecmp(type, "PageSetup"))
1624     section = PPDC_SECTION_PAGE;
1625   else if (!_cups_strcasecmp(type, "Prolog"))
1626     section = PPDC_SECTION_PROLOG;
1627   else
1628   {
1629     _cupsLangPrintf(stderr,
1630                     _("ppdc: Invalid option section \"%s\" on line %d of "
1631                       "%s."), type, fp->line, fp->filename);
1632     return (NULL);
1633   }
1634
1635   order = get_float(fp);
1636
1637   // See if the option already exists...
1638   if ((o = d->find_option_group(name, &mg)) == NULL)
1639   {
1640     // Nope, add a new one...
1641     o = new ppdcOption(ot, name, text, section, order);
1642   }
1643   else if (o->type != ot)
1644   {
1645     _cupsLangPrintf(stderr,
1646                     _("ppdc: Option %s redefined with a different type on line "
1647                       "%d of %s."), name, fp->line, fp->filename);
1648     return (NULL);
1649   }
1650   else if (g != mg)
1651   {
1652     _cupsLangPrintf(stderr,
1653                     _("ppdc: Option %s defined in two different groups on line "
1654                       "%d of %s."), name, fp->line, fp->filename);
1655     return (NULL);
1656   }
1657
1658   return (o);
1659 }
1660
1661
1662 //
1663 // 'ppdcSource::get_po()' - Get a message catalog.
1664 //
1665
1666 ppdcCatalog *                           // O - Message catalog
1667 ppdcSource::get_po(ppdcFile *fp)        // I - File to read
1668 {
1669   char          locale[32],             // Locale name
1670                 poname[1024],           // Message catalog filename
1671                 basedir[1024],          // Base directory
1672                 *baseptr,               // Pointer into directory
1673                 pofilename[1024];       // Full filename of message catalog
1674   ppdcCatalog   *cat;                   // Message catalog
1675
1676
1677   // Read the #po parameters:
1678   //
1679   // #po locale "filename.po"
1680   if (!get_token(fp, locale, sizeof(locale)))
1681   {
1682     _cupsLangPrintf(stderr,
1683                     _("ppdc: Expected locale after #po on line %d of %s."),
1684                     fp->line, fp->filename);
1685     return (NULL);
1686   }
1687
1688   if (!get_token(fp, poname, sizeof(poname)))
1689   {
1690     _cupsLangPrintf(stderr,
1691                     _("ppdc: Expected filename after #po %s on line %d of "
1692                       "%s."), locale, fp->line, fp->filename);
1693     return (NULL);
1694   }
1695
1696   // See if the locale is already loaded...
1697   if (find_po(locale))
1698   {
1699     _cupsLangPrintf(stderr,
1700                     _("ppdc: Duplicate #po for locale %s on line %d of %s."),
1701                     locale, fp->line, fp->filename);
1702     return (NULL);
1703   }
1704
1705   // Figure out the current directory...
1706   strlcpy(basedir, fp->filename, sizeof(basedir));
1707
1708   if ((baseptr = strrchr(basedir, '/')) != NULL)
1709     *baseptr = '\0';
1710   else
1711     strcpy(basedir, ".");
1712
1713   // Find the po file...
1714   pofilename[0] = '\0';
1715
1716   if (!poname[0] ||
1717       find_include(poname, basedir, pofilename, sizeof(pofilename)))
1718   {
1719     // Found it, so load it...
1720     cat = new ppdcCatalog(locale, pofilename);
1721
1722     // Reset the filename to the name supplied by the user...
1723     cat->filename->release();
1724     cat->filename = new ppdcString(poname);
1725
1726     // Return the catalog...
1727     return (cat);
1728   }
1729   else
1730   {
1731     _cupsLangPrintf(stderr,
1732                     _("ppdc: Unable to find #po file %s on line %d of %s."),
1733                     poname, fp->line, fp->filename);
1734     return (NULL);
1735   }
1736 }
1737
1738
1739 //
1740 // 'ppdcSource::get_resolution()' - Get an old-style resolution option.
1741 //
1742
1743 ppdcChoice *                            // O - Choice data
1744 ppdcSource::get_resolution(ppdcFile *fp)// I - File to read
1745 {
1746   char          name[1024],             // Name
1747                 *text,                  // Text
1748                 temp[256],              // Temporary string
1749                 command[256],           // Command string
1750                 *commptr;               // Pointer into command
1751   int           xdpi, ydpi,             // X + Y resolution
1752                 color_order,            // Color order
1753                 color_space,            // Colorspace
1754                 compression,            // Compression mode
1755                 depth,                  // Bits per color
1756                 row_count,              // Row count
1757                 row_feed,               // Row feed
1758                 row_step;               // Row step/interval
1759
1760
1761   // Read the resolution parameters:
1762   //
1763   // Resolution colorspace bits row-count row-feed row-step name/text
1764   if (!get_token(fp, temp, sizeof(temp)))
1765   {
1766     _cupsLangPrintf(stderr,
1767                     _("ppdc: Expected override field after Resolution on line "
1768                       "%d of %s."), fp->line, fp->filename);
1769     return (NULL);
1770   }
1771
1772   color_order = get_color_order(temp);
1773   color_space = get_color_space(temp);
1774   compression = get_integer(temp);
1775
1776   depth       = get_integer(fp);
1777   row_count   = get_integer(fp);
1778   row_feed    = get_integer(fp);
1779   row_step    = get_integer(fp);
1780
1781   if (!get_token(fp, name, sizeof(name)))
1782   {
1783     _cupsLangPrintf(stderr,
1784                     _("ppdc: Expected name/text after Resolution on line %d of "
1785                       "%s."), fp->line, fp->filename);
1786     return (NULL);
1787   }
1788
1789   if ((text = strchr(name, '/')) != NULL)
1790     *text++ = '\0';
1791   else
1792     text = name;
1793
1794   switch (sscanf(name, "%dx%d", &xdpi, &ydpi))
1795   {
1796     case 0 :
1797         _cupsLangPrintf(stderr,
1798                         _("ppdc: Bad resolution name \"%s\" on line %d of "
1799                           "%s."), name, fp->line, fp->filename);
1800         break;
1801     case 1 :
1802         ydpi = xdpi;
1803         break;
1804   }
1805
1806   // Create the necessary PS commands...
1807   snprintf(command, sizeof(command),
1808            "<</HWResolution[%d %d]/cupsBitsPerColor %d/cupsRowCount %d"
1809            "/cupsRowFeed %d/cupsRowStep %d",
1810            xdpi, ydpi, depth, row_count, row_feed, row_step);
1811   commptr = command + strlen(command);
1812
1813   if (color_order >= 0)
1814   {
1815     snprintf(commptr, sizeof(command) - (commptr - command),
1816              "/cupsColorOrder %d", color_order);
1817     commptr += strlen(commptr);
1818   }
1819
1820   if (color_space >= 0)
1821   {
1822     snprintf(commptr, sizeof(command) - (commptr - command),
1823              "/cupsColorSpace %d", color_space);
1824     commptr += strlen(commptr);
1825   }
1826
1827   if (compression >= 0)
1828   {
1829     snprintf(commptr, sizeof(command) - (commptr - command),
1830              "/cupsCompression %d", compression);
1831     commptr += strlen(commptr);
1832   }
1833
1834   snprintf(commptr, sizeof(command) - (commptr - command), ">>setpagedevice");
1835
1836   // Return the new choice...
1837   return (new ppdcChoice(name, text, command));
1838 }
1839
1840
1841 //
1842 // 'ppdcSource::get_simple_profile()' - Get a simple color profile definition.
1843 //
1844
1845 ppdcProfile *                           // O - Color profile
1846 ppdcSource::get_simple_profile(ppdcFile *fp)
1847                                         // I - File to read
1848 {
1849   char          resolution[1024],       // Resolution/media type
1850                 *media_type;            // Media type
1851   float         m[9];                   // Transform matrix
1852   float         kd, rd, g;              // Densities and gamma
1853   float         red, green, blue;       // RGB adjustments
1854   float         yellow;                 // Yellow density
1855   float         color;                  // Color density values
1856
1857
1858   // Get the SimpleColorProfile parameters:
1859   //
1860   // SimpleColorProfile resolution/mediatype black-density yellow-density
1861   //     red-density gamma red-adjust green-adjust blue-adjust
1862   if (!get_token(fp, resolution, sizeof(resolution)))
1863   {
1864     _cupsLangPrintf(stderr,
1865                     _("ppdc: Expected resolution/mediatype following "
1866                       "SimpleColorProfile on line %d of %s."),
1867                     fp->line, fp->filename);
1868     return (NULL);
1869   }
1870
1871   if ((media_type = strchr(resolution, '/')) != NULL)
1872     *media_type++ = '\0';
1873   else
1874     media_type = resolution;
1875
1876   // Collect the profile parameters...
1877   kd     = get_float(fp);
1878   yellow = get_float(fp);
1879   rd     = get_float(fp);
1880   g      = get_float(fp);
1881   red    = get_float(fp);
1882   green  = get_float(fp);
1883   blue   = get_float(fp);
1884
1885   // Build the color profile...
1886   color = 0.5f * rd / kd - kd;
1887   m[0]  = 1.0f;                         // C
1888   m[1]  = color + blue;                 // C + M (blue)
1889   m[2]  = color - green;                // C + Y (green)
1890   m[3]  = color - blue;                 // M + C (blue)
1891   m[4]  = 1.0f;                         // M
1892   m[5]  = color + red;                  // M + Y (red)
1893   m[6]  = yellow * (color + green);     // Y + C (green)
1894   m[7]  = yellow * (color - red);       // Y + M (red)
1895   m[8]  = yellow;                       // Y
1896
1897   if (m[1] > 0.0f)
1898   {
1899     m[3] -= m[1];
1900     m[1] = 0.0f;
1901   }
1902   else if (m[3] > 0.0f)
1903   {
1904     m[1] -= m[3];
1905     m[3] = 0.0f;
1906   }
1907
1908   if (m[2] > 0.0f)
1909   {
1910     m[6] -= m[2];
1911     m[2] = 0.0f;
1912   }
1913   else if (m[6] > 0.0f)
1914   {
1915     m[2] -= m[6];
1916     m[6] = 0.0f;
1917   }
1918
1919   if (m[5] > 0.0f)
1920   {
1921     m[7] -= m[5];
1922     m[5] = 0.0f;
1923   }
1924   else if (m[7] > 0.0f)
1925   {
1926     m[5] -= m[7];
1927     m[7] = 0.0f;
1928   }
1929
1930   // Return the new profile...
1931   return (new ppdcProfile(resolution, media_type, g, kd, m));
1932 }
1933
1934
1935 //
1936 // 'ppdcSource::get_size()' - Get a media size definition from a file.
1937 //
1938
1939 ppdcMediaSize *                         // O - Media size
1940 ppdcSource::get_size(ppdcFile *fp)      // I - File to read
1941 {
1942   char          name[1024],             // Name
1943                 *text;                  // Text
1944   float         width,                  // Width
1945                 length;                 // Length
1946
1947
1948   // Get the name, text, width, and length:
1949   //
1950   // #media name/text width length
1951   if (!get_token(fp, name, sizeof(name)))
1952     return (NULL);
1953
1954   if ((text = strchr(name, '/')) != NULL)
1955     *text++ = '\0';
1956   else
1957     text = name;
1958
1959   if ((width = get_measurement(fp)) < 0.0f)
1960     return (NULL);
1961
1962   if ((length = get_measurement(fp)) < 0.0f)
1963     return (NULL);
1964
1965   // Return the new media size...
1966   return (new ppdcMediaSize(name, text, width, length, 0.0f, 0.0f, 0.0f, 0.0f));
1967 }
1968
1969
1970 //
1971 // 'ppdcSource::get_token()' - Get a token from a file.
1972 //
1973
1974 char *                                  // O - Token string or NULL
1975 ppdcSource::get_token(ppdcFile *fp,     // I - File to read
1976                       char     *buffer, // I - Buffer
1977                       int      buflen)  // I - Length of buffer
1978 {
1979   char          *bufptr,                // Pointer into string buffer
1980                 *bufend;                // End of string buffer
1981   int           ch,                     // Character from file
1982                 nextch,                 // Next char in file
1983                 quote,                  // Quote character used...
1984                 empty,                  // Empty input?
1985                 startline;              // Start line for quote
1986   char          name[256],              // Name string
1987                 *nameptr;               // Name pointer
1988   ppdcVariable  *var;                   // Variable pointer
1989
1990
1991   // Mark the beginning and end of the buffer...
1992   bufptr = buffer;
1993   bufend = buffer + buflen - 1;
1994
1995   // Loop intil we've read a token...
1996   quote     = 0;
1997   startline = 0;
1998   empty     = 1;
1999
2000   while ((ch = fp->get()) != EOF)
2001   {
2002     if (isspace(ch) && !quote)
2003     {
2004       if (empty)
2005         continue;
2006       else
2007         break;
2008     }
2009     else if (ch == '$')
2010     {
2011       // Variable substitution
2012       empty = 0;
2013
2014       for (nameptr = name; (ch = fp->peek()) != EOF;)
2015       {
2016         if (!isalnum(ch) && ch != '_')
2017           break;
2018         else if (nameptr < (name + sizeof(name) - 1))
2019           *nameptr++ = fp->get();
2020       }
2021
2022       if (nameptr == name)
2023       {
2024         // Just substitute this character...
2025         if (ch == '$')
2026         {
2027           // $$ = $
2028           if (bufptr < bufend)
2029             *bufptr++ = fp->get();
2030         }
2031         else
2032         {
2033           // $ch = $ch
2034           _cupsLangPrintf(stderr,
2035                           _("ppdc: Bad variable substitution ($%c) on line %d "
2036                             "of %s."), ch, fp->line, fp->filename);
2037
2038           if (bufptr < bufend)
2039             *bufptr++ = '$';
2040         }
2041       }
2042       else
2043       {
2044         // Substitute the variable value...
2045         *nameptr = '\0';
2046         var = find_variable(name);
2047         if (var)
2048         {
2049           strlcpy(bufptr, var->value->value, bufend - bufptr + 1);
2050           bufptr += strlen(bufptr);
2051         }
2052         else
2053         {
2054           if (!(cond_state & PPDC_COND_SKIP))
2055             _cupsLangPrintf(stderr,
2056                             _("ppdc: Undefined variable (%s) on line %d of "
2057                               "%s."), name, fp->line, fp->filename);
2058
2059           snprintf(bufptr, bufend - bufptr + 1, "$%s", name);
2060           bufptr += strlen(bufptr);
2061         }
2062       }
2063     }
2064     else if (ch == '/' && !quote)
2065     {
2066       // Possibly a comment...
2067       nextch = fp->peek();
2068
2069       if (nextch == '*')
2070       {
2071         // C comment...
2072         fp->get();
2073         ch = fp->get();
2074         while ((nextch = fp->get()) != EOF)
2075         {
2076           if (ch == '*' && nextch == '/')
2077             break;
2078
2079           ch = nextch;
2080         }
2081
2082         if (nextch == EOF)
2083           break;
2084       }
2085       else if (nextch == '/')
2086       {
2087         // C++ comment...
2088         while ((nextch = fp->get()) != EOF)
2089           if (nextch == '\n')
2090             break;
2091
2092         if (nextch == EOF)
2093           break;
2094       }
2095       else
2096       {
2097         // Not a comment...
2098         empty = 0;
2099
2100         if (bufptr < bufend)
2101           *bufptr++ = ch;
2102       }
2103     }
2104     else if (ch == '\'' || ch == '\"')
2105     {
2106       empty = 0;
2107
2108       if (quote == ch)
2109       {
2110         // Ending the current quoted string...
2111         quote = 0;
2112       }
2113       else if (quote)
2114       {
2115         // Insert the opposing quote char...
2116         if (bufptr < bufend)
2117           *bufptr++ = ch;
2118       }
2119       else
2120       {
2121         // Start a new quoted string...
2122         startline = fp->line;
2123         quote     = ch;
2124       }
2125     }
2126     else if ((ch == '(' || ch == '<') && !quote)
2127     {
2128       empty     = 0;
2129       quote     = ch;
2130       startline = fp->line;
2131
2132       if (bufptr < bufend)
2133         *bufptr++ = ch;
2134     }
2135     else if ((ch == ')' && quote == '(') || (ch == '>' && quote == '<'))
2136     {
2137       quote = 0;
2138
2139       if (bufptr < bufend)
2140         *bufptr++ = ch;
2141     }
2142     else if (ch == '\\')
2143     {
2144       empty = 0;
2145
2146       if ((ch = fp->get()) == EOF)
2147         break;
2148
2149       if (bufptr < bufend)
2150         *bufptr++ = ch;
2151     }
2152     else if (bufptr < bufend)
2153     {
2154       empty = 0;
2155
2156       *bufptr++ = ch;
2157
2158       if ((ch == '{' || ch == '}') && !quote)
2159         break;
2160     }
2161   }
2162
2163   if (quote)
2164   {
2165     _cupsLangPrintf(stderr,
2166                     _("ppdc: Unterminated string starting with %c on line %d "
2167                       "of %s."), quote, startline, fp->filename);
2168     return (NULL);
2169   }
2170
2171   if (empty)
2172     return (NULL);
2173   else
2174   {
2175     *bufptr = '\0';
2176 //    puts(buffer);
2177     return (buffer);
2178   }
2179 }
2180
2181
2182 //
2183 // 'ppdcSource::get_variable()' - Get a variable definition.
2184 //
2185
2186 ppdcVariable *                          // O - Variable
2187 ppdcSource::get_variable(ppdcFile *fp)  // I - File to read
2188 {
2189   char          name[1024],             // Name
2190                 value[1024];            // Value
2191
2192
2193   // Get the name and value:
2194   //
2195   // #define name value
2196   if (!get_token(fp, name, sizeof(name)))
2197     return (NULL);
2198
2199   if (!get_token(fp, value, sizeof(value)))
2200     return (NULL);
2201
2202   // Set the variable...
2203   return (set_variable(name, value));
2204 }
2205
2206
2207 //
2208 // 'ppdcSource::quotef()' - Write a formatted, quoted string...
2209 //
2210
2211 int                                     // O - Number bytes on success, -1 on failure
2212 ppdcSource::quotef(cups_file_t *fp,     // I - File to write to
2213                    const char  *format, // I - Printf-style format string
2214                    ...)                 // I - Additional args as needed
2215 {
2216   va_list       ap;                     // Pointer to additional arguments
2217   int           bytes;                  // Bytes written
2218   char          sign,                   // Sign of format width
2219                 size,                   // Size character (h, l, L)
2220                 type;                   // Format type character
2221   const char    *bufformat;             // Start of format
2222   int           width,                  // Width of field
2223                 prec;                   // Number of characters of precision
2224   char          tformat[100];           // Temporary format string for fprintf()
2225   char          *s;                     // Pointer to string
2226   int           slen;                   // Length of string
2227   int           i;                      // Looping var
2228
2229
2230   // Range check input...
2231   if (!fp || !format)
2232     return (-1);
2233
2234   // Loop through the format string, formatting as needed...
2235   va_start(ap, format);
2236
2237   bytes = 0;
2238
2239   while (*format)
2240   {
2241     if (*format == '%')
2242     {
2243       bufformat = format;
2244       format ++;
2245
2246       if (*format == '%')
2247       {
2248         cupsFilePutChar(fp, *format++);
2249         bytes ++;
2250         continue;
2251       }
2252       else if (strchr(" -+#\'", *format))
2253         sign = *format++;
2254       else
2255         sign = 0;
2256
2257       width = 0;
2258       while (isdigit(*format))
2259         width = width * 10 + *format++ - '0';
2260
2261       if (*format == '.')
2262       {
2263         format ++;
2264         prec = 0;
2265
2266         while (isdigit(*format))
2267           prec = prec * 10 + *format++ - '0';
2268       }
2269       else
2270         prec = -1;
2271
2272       if (*format == 'l' && format[1] == 'l')
2273       {
2274         size = 'L';
2275         format += 2;
2276       }
2277       else if (*format == 'h' || *format == 'l' || *format == 'L')
2278         size = *format++;
2279       else
2280         size = '\0';
2281
2282       if (!*format)
2283         break;
2284
2285       type = *format++;
2286
2287       switch (type)
2288       {
2289         case 'E' : // Floating point formats
2290         case 'G' :
2291         case 'e' :
2292         case 'f' :
2293         case 'g' :
2294             if ((format - bufformat + 1) > (int)sizeof(tformat))
2295               break;
2296
2297             strncpy(tformat, bufformat, format - bufformat);
2298             tformat[format - bufformat] = '\0';
2299
2300             bytes += cupsFilePrintf(fp, tformat, va_arg(ap, double));
2301             break;
2302
2303         case 'B' : // Integer formats
2304         case 'X' :
2305         case 'b' :
2306         case 'd' :
2307         case 'i' :
2308         case 'o' :
2309         case 'u' :
2310         case 'x' :
2311             if ((format - bufformat + 1) > (int)sizeof(tformat))
2312               break;
2313
2314             strncpy(tformat, bufformat, format - bufformat);
2315             tformat[format - bufformat] = '\0';
2316
2317 #  ifdef HAVE_LONG_LONG
2318             if (size == 'L')
2319               bytes += cupsFilePrintf(fp, tformat, va_arg(ap, long long));
2320             else
2321 #  endif /* HAVE_LONG_LONG */
2322             if (size == 'l')
2323               bytes += cupsFilePrintf(fp, tformat, va_arg(ap, long));
2324             else
2325               bytes += cupsFilePrintf(fp, tformat, va_arg(ap, int));
2326             break;
2327
2328         case 'p' : // Pointer value
2329             if ((format - bufformat + 1) > (int)sizeof(tformat))
2330               break;
2331
2332             strncpy(tformat, bufformat, format - bufformat);
2333             tformat[format - bufformat] = '\0';
2334
2335             bytes += cupsFilePrintf(fp, tformat, va_arg(ap, void *));
2336             break;
2337
2338         case 'c' : // Character or character array
2339             if (width <= 1)
2340             {
2341               bytes ++;
2342               cupsFilePutChar(fp, va_arg(ap, int));
2343             }
2344             else
2345             {
2346               cupsFileWrite(fp, va_arg(ap, char *), width);
2347               bytes += width;
2348             }
2349             break;
2350
2351         case 's' : // String
2352             if ((s = va_arg(ap, char *)) == NULL)
2353               s = (char *)"(nil)";
2354
2355             slen = strlen(s);
2356             if (slen > width && prec != width)
2357               width = slen;
2358
2359             if (slen > width)
2360               slen = width;
2361
2362             if (sign != '-')
2363             {
2364               for (i = width - slen; i > 0; i --, bytes ++)
2365                 cupsFilePutChar(fp, ' ');
2366             }
2367
2368             for (i = slen; i > 0; i --, s ++, bytes ++)
2369             {
2370               if (*s == '\\' || *s == '\"')
2371               {
2372                 cupsFilePutChar(fp, '\\');
2373                 bytes ++;
2374               }
2375
2376               cupsFilePutChar(fp, *s);
2377             }
2378
2379             if (sign == '-')
2380             {
2381               for (i = width - slen; i > 0; i --, bytes ++)
2382                 cupsFilePutChar(fp, ' ');
2383             }
2384             break;
2385       }
2386     }
2387     else
2388     {
2389       cupsFilePutChar(fp, *format++);
2390       bytes ++;
2391     }
2392   }
2393
2394   va_end(ap);
2395
2396   // Return the number of characters written.
2397   return (bytes);
2398 }
2399
2400
2401 //
2402 // 'ppdcSource::read_file()' - Read a driver source file.
2403 //
2404
2405 void
2406 ppdcSource::read_file(const char  *f,   // I - File to read
2407                       cups_file_t *ffp) // I - File pointer to use
2408 {
2409   ppdcFile *fp = new ppdcFile(f, ffp);
2410   scan_file(fp);
2411   delete fp;
2412
2413   if (cond_current != cond_stack)
2414     _cupsLangPrintf(stderr, _("ppdc: Missing #endif at end of \"%s\"."), f);
2415 }
2416
2417
2418 //
2419 // 'ppdcSource::scan_file()' - Scan a driver source file.
2420 //
2421
2422 void
2423 ppdcSource::scan_file(ppdcFile   *fp,   // I - File to read
2424                       ppdcDriver *td,   // I - Driver template
2425                       bool       inc)   // I - Including?
2426 {
2427   ppdcDriver    *d;                     // Current driver
2428   ppdcGroup     *g,                     // Current group
2429                 *mg,                    // Matching group
2430                 *general,               // General options group
2431                 *install;               // Installable options group
2432   ppdcOption    *o;                     // Current option
2433   ppdcChoice    *c;                     // Current choice
2434   char          temp[256],              // Token from file...
2435                 *ptr;                   // Pointer into token
2436   int           isdefault;              // Default option?
2437
2438
2439   // Initialize things as needed...
2440   if (inc && td)
2441   {
2442     d = td;
2443     d->retain();
2444   }
2445   else
2446     d = new ppdcDriver(td);
2447
2448   if ((general = d->find_group("General")) == NULL)
2449   {
2450     general = new ppdcGroup("General", NULL);
2451     d->add_group(general);
2452   }
2453
2454   if ((install = d->find_group("InstallableOptions")) == NULL)
2455   {
2456     install = new ppdcGroup("InstallableOptions", "Installable Options");
2457     d->add_group(install);
2458   }
2459
2460   // Loop until EOF or }
2461   o = 0;
2462   g = general;
2463
2464   while (get_token(fp, temp, sizeof(temp)))
2465   {
2466     if (temp[0] == '*')
2467     {
2468       // Mark the next choice as the default
2469       isdefault = 1;
2470
2471       for (ptr = temp; ptr[1]; ptr ++)
2472         *ptr = ptr[1];
2473
2474       *ptr = '\0';
2475     }
2476     else
2477     {
2478       // Don't mark the next choice as the default
2479       isdefault = 0;
2480     }
2481
2482     if (!_cups_strcasecmp(temp, "}"))
2483     {
2484       // Close this one out...
2485       break;
2486     }
2487     else if (!_cups_strcasecmp(temp, "{"))
2488     {
2489       // Open a new child...
2490       scan_file(fp, d);
2491     }
2492     else if (!_cups_strcasecmp(temp, "#if"))
2493     {
2494       if ((cond_current - cond_stack) >= 100)
2495       {
2496         _cupsLangPrintf(stderr,
2497                         _("ppdc: Too many nested #if's on line %d of %s."),
2498                         fp->line, fp->filename);
2499         break;
2500       }
2501
2502       cond_current ++;
2503       if (get_integer(fp) > 0)
2504         *cond_current = PPDC_COND_SATISFIED;
2505       else
2506       {
2507         *cond_current = PPDC_COND_SKIP;
2508         cond_state    |= PPDC_COND_SKIP;
2509       }
2510     }
2511     else if (!_cups_strcasecmp(temp, "#elif"))
2512     {
2513       if (cond_current == cond_stack)
2514       {
2515         _cupsLangPrintf(stderr, _("ppdc: Missing #if on line %d of %s."),
2516                         fp->line, fp->filename);
2517         break;
2518       }
2519
2520       if (*cond_current & PPDC_COND_SATISFIED)
2521       {
2522         get_integer(fp);
2523         *cond_current |= PPDC_COND_SKIP;
2524       }
2525       else if (get_integer(fp) > 0)
2526       {
2527         *cond_current |= PPDC_COND_SATISFIED;
2528         *cond_current &= ~PPDC_COND_SKIP;
2529       }
2530       else
2531         *cond_current |= PPDC_COND_SKIP;
2532
2533       // Update the current state
2534       int *cond_temp = cond_current;    // Temporary stack pointer
2535
2536       cond_state = PPDC_COND_NORMAL;
2537       while (cond_temp > cond_stack)
2538         if (*cond_temp & PPDC_COND_SKIP)
2539         {
2540           cond_state = PPDC_COND_SKIP;
2541           break;
2542         }
2543         else
2544           cond_temp --;
2545     }
2546     else if (!_cups_strcasecmp(temp, "#else"))
2547     {
2548       if (cond_current == cond_stack)
2549       {
2550         _cupsLangPrintf(stderr, _("ppdc: Missing #if on line %d of %s."),
2551                         fp->line, fp->filename);
2552         break;
2553       }
2554
2555       if (*cond_current & PPDC_COND_SATISFIED)
2556         *cond_current |= PPDC_COND_SKIP;
2557       else
2558       {
2559         *cond_current |= PPDC_COND_SATISFIED;
2560         *cond_current &= ~PPDC_COND_SKIP;
2561       }
2562
2563       // Update the current state
2564       int *cond_temp = cond_current;    // Temporary stack pointer
2565
2566       cond_state = PPDC_COND_NORMAL;
2567       while (cond_temp > cond_stack)
2568         if (*cond_temp & PPDC_COND_SKIP)
2569         {
2570           cond_state = PPDC_COND_SKIP;
2571           break;
2572         }
2573         else
2574           cond_temp --;
2575     }
2576     else if (!_cups_strcasecmp(temp, "#endif"))
2577     {
2578       if (cond_current == cond_stack)
2579       {
2580         _cupsLangPrintf(stderr, _("ppdc: Missing #if on line %d of %s."),
2581                         fp->line, fp->filename);
2582         break;
2583       }
2584
2585       cond_current --;
2586
2587       // Update the current state
2588       int *cond_temp = cond_current;    // Temporary stack pointer
2589
2590       cond_state = PPDC_COND_NORMAL;
2591       while (cond_temp > cond_stack)
2592         if (*cond_temp & PPDC_COND_SKIP)
2593         {
2594           cond_state = PPDC_COND_SKIP;
2595           break;
2596         }
2597         else
2598           cond_temp --;
2599     }
2600     else if (!_cups_strcasecmp(temp, "#define"))
2601     {
2602       // Get the variable...
2603       get_variable(fp);
2604     }
2605     else if (!_cups_strcasecmp(temp, "#include"))
2606     {
2607       // #include filename
2608       char      basedir[1024],          // Base directory
2609                 *baseptr,               // Pointer into directory
2610                 inctemp[1024],          // Initial filename
2611                 incname[1024];          // Include filename
2612       ppdcFile  *incfile;               // Include file
2613       int       *old_current = cond_current;
2614                                         // Previous current stack
2615
2616
2617       // Get the include name...
2618       if (!get_token(fp, inctemp, sizeof(inctemp)))
2619       {
2620         _cupsLangPrintf(stderr,
2621                         _("ppdc: Expected include filename on line %d of "
2622                           "%s."), fp->line, fp->filename);
2623         break;
2624       }
2625
2626       if (cond_state)
2627         continue;
2628
2629       // Figure out the current directory...
2630       strlcpy(basedir, fp->filename, sizeof(basedir));
2631
2632       if ((baseptr = strrchr(basedir, '/')) != NULL)
2633         *baseptr = '\0';
2634       else
2635         strcpy(basedir, ".");
2636
2637       // Find the include file...
2638       if (find_include(inctemp, basedir, incname, sizeof(incname)))
2639       {
2640         // Open the include file, scan it, and then close it...
2641         incfile = new ppdcFile(incname);
2642         scan_file(incfile, d, true);
2643         delete incfile;
2644
2645         if (cond_current != old_current)
2646           _cupsLangPrintf(stderr, _("ppdc: Missing #endif at end of \"%s\"."),
2647                           incname);
2648       }
2649       else
2650       {
2651         // Can't find it!
2652         _cupsLangPrintf(stderr,
2653                         _("ppdc: Unable to find include file \"%s\" on line %d "
2654                           "of %s."), inctemp, fp->line, fp->filename);
2655         break;
2656       }
2657     }
2658     else if (!_cups_strcasecmp(temp, "#media"))
2659     {
2660       ppdcMediaSize     *m;             // Media size
2661
2662
2663       // Get a media size...
2664       m = get_size(fp);
2665       if (m)
2666       {
2667         if (cond_state)
2668           m->release();
2669         else
2670           sizes->add(m);
2671       }
2672     }
2673     else if (!_cups_strcasecmp(temp, "#po"))
2674     {
2675       ppdcCatalog       *cat;           // Message catalog
2676
2677
2678       // Get a message catalog...
2679       cat = get_po(fp);
2680       if (cat)
2681       {
2682         if (cond_state)
2683           cat->release();
2684         else
2685           po_files->add(cat);
2686       }
2687     }
2688     else if (!_cups_strcasecmp(temp, "Attribute") ||
2689              !_cups_strcasecmp(temp, "LocAttribute"))
2690     {
2691       ppdcAttr  *a;                     // Attribute
2692
2693
2694       // Get an attribute...
2695       a = get_attr(fp, !_cups_strcasecmp(temp, "LocAttribute"));
2696       if (a)
2697       {
2698         if (cond_state)
2699           a->release();
2700         else
2701           d->add_attr(a);
2702       }
2703     }
2704     else if (!_cups_strcasecmp(temp, "Choice"))
2705     {
2706       // Get a choice...
2707       c = get_choice(fp);
2708       if (!c)
2709         break;
2710
2711       if (cond_state)
2712       {
2713         c->release();
2714         continue;
2715       }
2716
2717       // Add it to the current option...
2718       if (!o)
2719       {
2720         _cupsLangPrintf(stderr,
2721                         _("ppdc: Choice found on line %d of %s with no "
2722                           "Option."), fp->line, fp->filename);
2723         break;
2724       }
2725
2726       o->add_choice(c);
2727
2728       if (isdefault)
2729         o->set_defchoice(c);
2730     }
2731     else if (!_cups_strcasecmp(temp, "ColorDevice"))
2732     {
2733       // ColorDevice boolean
2734       if (cond_state)
2735         get_boolean(fp);
2736       else
2737         d->color_device = get_boolean(fp);
2738     }
2739     else if (!_cups_strcasecmp(temp, "ColorModel"))
2740     {
2741       // Get the color model
2742       c = get_color_model(fp);
2743       if (!c)
2744         continue;
2745
2746       if (cond_state)
2747       {
2748         c->release();
2749         continue;
2750       }
2751
2752       // Add the choice to the ColorModel option...
2753       if ((o = d->find_option("ColorModel")) == NULL)
2754       {
2755         // Create the ColorModel option...
2756         o = new ppdcOption(PPDC_PICKONE, "ColorModel", "Color Mode", PPDC_SECTION_ANY, 10.0f);
2757         g = general;
2758         g->add_option(o);
2759       }
2760
2761       o->add_choice(c);
2762
2763       if (isdefault)
2764         o->set_defchoice(c);
2765
2766       o = NULL;
2767     }
2768     else if (!_cups_strcasecmp(temp, "ColorProfile"))
2769     {
2770       ppdcProfile       *p;             // Color profile
2771
2772
2773       // Get the color profile...
2774       p = get_color_profile(fp);
2775
2776       if (p)
2777       {
2778         if (cond_state)
2779           p->release();
2780         else
2781           d->profiles->add(p);
2782       }
2783     }
2784     else if (!_cups_strcasecmp(temp, "Copyright"))
2785     {
2786       // Copyright string
2787       char      copytemp[8192],         // Copyright string
2788                 *copyptr,               // Pointer into string
2789                 *copyend;               // Pointer to end of string
2790
2791
2792       // Get the copyright string...
2793       if (!get_token(fp, copytemp, sizeof(temp)))
2794       {
2795         _cupsLangPrintf(stderr,
2796                         _("ppdc: Expected string after Copyright on line %d "
2797                           "of %s."), fp->line, fp->filename);
2798         break;
2799       }
2800
2801       if (cond_state)
2802         continue;
2803
2804       // Break it up into individual lines...
2805       for (copyptr = copytemp; copyptr; copyptr = copyend)
2806       {
2807         if ((copyend = strchr(copyptr, '\n')) != NULL)
2808           *copyend++ = '\0';
2809
2810         d->copyright->add(new ppdcString(copyptr));
2811       }
2812     }
2813     else if (!_cups_strcasecmp(temp, "CustomMedia"))
2814     {
2815       ppdcMediaSize     *m;             // Media size
2816
2817
2818       // Get a custom media size...
2819       m = get_custom_size(fp);
2820
2821       if (cond_state)
2822       {
2823         m->release();
2824         continue;
2825       }
2826
2827       if (m)
2828         d->sizes->add(m);
2829
2830       if (isdefault)
2831         d->set_default_size(m);
2832     }
2833     else if (!_cups_strcasecmp(temp, "Cutter"))
2834     {
2835       // Cutter boolean
2836       int       have_cutter;            // Have a paper cutter?
2837
2838
2839       have_cutter = get_boolean(fp);
2840       if (have_cutter <= 0 || cond_state)
2841         continue;
2842
2843       if ((o = d->find_option("CutMedia")) == NULL)
2844       {
2845         o = new ppdcOption(PPDC_BOOLEAN, "CutMedia", "Cut Media", PPDC_SECTION_ANY, 10.0f);
2846
2847         g = general;
2848         g->add_option(o);
2849
2850         c = new ppdcChoice("False", NULL, "<</CutMedia 0>>setpagedevice");
2851         o->add_choice(c);
2852         o->set_defchoice(c);
2853
2854         c = new ppdcChoice("True", NULL, "<</CutMedia 4>>setpagedevice");
2855         o->add_choice(c);
2856       }
2857
2858       o = NULL;
2859     }
2860     else if (!_cups_strcasecmp(temp, "Darkness"))
2861     {
2862       // Get the darkness choice...
2863       c = get_generic(fp, "Darkness", NULL, "cupsCompression");
2864       if (!c)
2865         continue;
2866
2867       if (cond_state)
2868       {
2869         c->release();
2870         continue;
2871       }
2872
2873       // Add the choice to the cupsDarkness option...
2874       if ((o = d->find_option_group("cupsDarkness", &mg)) == NULL)
2875       {
2876         // Create the cupsDarkness option...
2877         o = new ppdcOption(PPDC_PICKONE, "cupsDarkness", "Darkness", PPDC_SECTION_ANY, 10.0f);
2878         g = general;
2879         g->add_option(o);
2880       }
2881       else if (mg != general)
2882       {
2883         _cupsLangPrintf(stderr,
2884                         _("ppdc: Option %s defined in two different groups on "
2885                           "line %d of %s."), "cupsDarkness", fp->line,
2886                         fp->filename);
2887         c->release();
2888         continue;
2889       }
2890
2891       o->add_choice(c);
2892
2893       if (isdefault)
2894         o->set_defchoice(c);
2895
2896       o = NULL;
2897     }
2898     else if (!_cups_strcasecmp(temp, "DriverType"))
2899     {
2900       int       i;                      // Looping var
2901
2902
2903       // DriverType keyword
2904       if (!get_token(fp, temp, sizeof(temp)))
2905       {
2906         _cupsLangPrintf(stderr,
2907                         _("ppdc: Expected driver type keyword following "
2908                           "DriverType on line %d of %s."),
2909                         fp->line, fp->filename);
2910         continue;
2911       }
2912
2913       if (cond_state)
2914         continue;
2915
2916       for (i = 0; i < (int)(sizeof(driver_types) / sizeof(driver_types[0])); i ++)
2917         if (!_cups_strcasecmp(temp, driver_types[i]))
2918           break;
2919
2920       if (i < (int)(sizeof(driver_types) / sizeof(driver_types[0])))
2921         d->type = (ppdcDrvType)i;
2922       else if (!_cups_strcasecmp(temp, "dymo"))
2923         d->type = PPDC_DRIVER_LABEL;
2924       else
2925         _cupsLangPrintf(stderr,
2926                         _("ppdc: Unknown driver type %s on line %d of %s."),
2927                         temp, fp->line, fp->filename);
2928     }
2929     else if (!_cups_strcasecmp(temp, "Duplex"))
2930       get_duplex(fp, d);
2931     else if (!_cups_strcasecmp(temp, "Filter"))
2932     {
2933       ppdcFilter        *f;             // Filter
2934
2935
2936       // Get the filter value...
2937       f = get_filter(fp);
2938       if (f)
2939       {
2940         if (cond_state)
2941           f->release();
2942         else
2943           d->filters->add(f);
2944       }
2945     }
2946     else if (!_cups_strcasecmp(temp, "Finishing"))
2947     {
2948       // Get the finishing choice...
2949       c = get_generic(fp, "Finishing", "OutputType", NULL);
2950       if (!c)
2951         continue;
2952
2953       if (cond_state)
2954       {
2955         c->release();
2956         continue;
2957       }
2958
2959       // Add the choice to the cupsFinishing option...
2960       if ((o = d->find_option_group("cupsFinishing", &mg)) == NULL)
2961       {
2962         // Create the cupsFinishing option...
2963         o = new ppdcOption(PPDC_PICKONE, "cupsFinishing", "Finishing", PPDC_SECTION_ANY, 10.0f);
2964         g = general;
2965         g->add_option(o);
2966       }
2967       else if (mg != general)
2968       {
2969         _cupsLangPrintf(stderr,
2970                         _("ppdc: Option %s defined in two different groups on "
2971                           "line %d of %s."), "cupsFinishing", fp->line,
2972                         fp->filename);
2973         c->release();
2974         continue;
2975       }
2976
2977       o->add_choice(c);
2978
2979       if (isdefault)
2980         o->set_defchoice(c);
2981
2982       o = NULL;
2983     }
2984     else if (!_cups_strcasecmp(temp, "Font") ||
2985              !_cups_strcasecmp(temp, "#font"))
2986     {
2987       ppdcFont  *f;                     // Font
2988
2989
2990       // Get a font...
2991       f = get_font(fp);
2992       if (f)
2993       {
2994         if (cond_state)
2995           f->release();
2996         else
2997         {
2998           if (!_cups_strcasecmp(temp, "#font"))
2999             base_fonts->add(f);
3000           else
3001             d->add_font(f);
3002
3003           if (isdefault)
3004             d->set_default_font(f);
3005         }
3006       }
3007     }
3008     else if (!_cups_strcasecmp(temp, "Group"))
3009     {
3010       // Get a group...
3011       ppdcGroup *tempg = get_group(fp, d);
3012
3013       if (!tempg)
3014         break;
3015
3016       if (cond_state)
3017       {
3018         if (!d->find_group(tempg->name->value))
3019           tempg->release();
3020       }
3021       else
3022       {
3023         if (!d->find_group(tempg->name->value))
3024           d->add_group(tempg);
3025
3026         g = tempg;
3027       }
3028     }
3029     else if (!_cups_strcasecmp(temp, "HWMargins"))
3030     {
3031       // HWMargins left bottom right top
3032       d->left_margin   = get_measurement(fp);
3033       d->bottom_margin = get_measurement(fp);
3034       d->right_margin  = get_measurement(fp);
3035       d->top_margin    = get_measurement(fp);
3036     }
3037     else if (!_cups_strcasecmp(temp, "InputSlot"))
3038     {
3039       // Get the input slot choice...
3040       c = get_generic(fp, "InputSlot", NULL, "MediaPosition");
3041       if (!c)
3042         continue;
3043
3044       if (cond_state)
3045       {
3046         c->release();
3047         continue;
3048       }
3049
3050       // Add the choice to the InputSlot option...
3051
3052       if ((o = d->find_option_group("InputSlot", &mg)) == NULL)
3053       {
3054         // Create the InputSlot option...
3055         o = new ppdcOption(PPDC_PICKONE, "InputSlot", "Media Source",
3056                            PPDC_SECTION_ANY, 10.0f);
3057         g = general;
3058         g->add_option(o);
3059       }
3060       else if (mg != general)
3061       {
3062         _cupsLangPrintf(stderr,
3063                         _("ppdc: Option %s defined in two different groups on "
3064                           "line %d of %s."), "InputSlot", fp->line,
3065                         fp->filename);
3066         c->release();
3067         continue;
3068       }
3069
3070       o->add_choice(c);
3071
3072       if (isdefault)
3073         o->set_defchoice(c);
3074
3075       o = NULL;
3076     }
3077     else if (!_cups_strcasecmp(temp, "Installable"))
3078     {
3079       // Get the installable option...
3080       o = get_installable(fp);
3081
3082       // Add it as needed...
3083       if (o)
3084       {
3085         if (cond_state)
3086           o->release();
3087         else
3088           install->add_option(o);
3089
3090         o = NULL;
3091       }
3092     }
3093     else if (!_cups_strcasecmp(temp, "ManualCopies"))
3094     {
3095       // ManualCopies boolean
3096       if (cond_state)
3097         get_boolean(fp);
3098       else
3099         d->manual_copies = get_boolean(fp);
3100     }
3101     else if (!_cups_strcasecmp(temp, "Manufacturer"))
3102     {
3103       // Manufacturer name
3104       char      name[256];              // Model name string
3105
3106
3107       if (!get_token(fp, name, sizeof(name)))
3108       {
3109         _cupsLangPrintf(stderr,
3110                         _("ppdc: Expected name after Manufacturer on line %d "
3111                           "of %s."), fp->line, fp->filename);
3112         break;
3113       }
3114
3115       if (!cond_state)
3116         d->set_manufacturer(name);
3117     }
3118     else if (!_cups_strcasecmp(temp, "MaxSize"))
3119     {
3120       // MaxSize width length
3121       if (cond_state)
3122       {
3123         get_measurement(fp);
3124         get_measurement(fp);
3125       }
3126       else
3127       {
3128         d->max_width  = get_measurement(fp);
3129         d->max_length = get_measurement(fp);
3130       }
3131     }
3132     else if (!_cups_strcasecmp(temp, "MediaSize"))
3133     {
3134       // MediaSize keyword
3135       char              name[41];       // Media size name
3136       ppdcMediaSize     *m,             // Matching media size...
3137                         *dm;            // Driver media size...
3138
3139
3140       if (get_token(fp, name, sizeof(name)) == NULL)
3141       {
3142         _cupsLangPrintf(stderr,
3143                         _("ppdc: Expected name after MediaSize on line %d of "
3144                           "%s."), fp->line, fp->filename);
3145         break;
3146       }
3147
3148       if (cond_state)
3149         continue;
3150
3151       m = find_size(name);
3152
3153       if (!m)
3154       {
3155         _cupsLangPrintf(stderr,
3156                         _("ppdc: Unknown media size \"%s\" on line %d of "
3157                           "%s."), name, fp->line, fp->filename);
3158         break;
3159       }
3160
3161       // Add this size to the driver...
3162       dm = new ppdcMediaSize(m->name->value, m->text->value,
3163                              m->width, m->length, d->left_margin,
3164                              d->bottom_margin, d->right_margin,
3165                              d->top_margin);
3166       d->sizes->add(dm);
3167
3168       if (isdefault)
3169         d->set_default_size(dm);
3170     }
3171     else if (!_cups_strcasecmp(temp, "MediaType"))
3172     {
3173       // Get the media type choice...
3174       c = get_generic(fp, "MediaType", "MediaType", "cupsMediaType");
3175       if (!c)
3176         continue;
3177
3178       if (cond_state)
3179       {
3180         c->release();
3181         continue;
3182       }
3183
3184       // Add the choice to the MediaType option...
3185       if ((o = d->find_option_group("MediaType", &mg)) == NULL)
3186       {
3187         // Create the MediaType option...
3188         o = new ppdcOption(PPDC_PICKONE, "MediaType", "Media Type",
3189                            PPDC_SECTION_ANY, 10.0f);
3190         g = general;
3191         g->add_option(o);
3192       }
3193       else if (mg != general)
3194       {
3195         _cupsLangPrintf(stderr,
3196                         _("ppdc: Option %s defined in two different groups on "
3197                           "line %d of %s."), "MediaType", fp->line,
3198                         fp->filename);
3199         c->release();
3200         continue;
3201       }
3202
3203       o->add_choice(c);
3204
3205       if (isdefault)
3206         o->set_defchoice(c);
3207
3208       o = NULL;
3209     }
3210     else if (!_cups_strcasecmp(temp, "MinSize"))
3211     {
3212       // MinSize width length
3213       if (cond_state)
3214       {
3215         get_measurement(fp);
3216         get_measurement(fp);
3217       }
3218       else
3219       {
3220         d->min_width  = get_measurement(fp);
3221         d->min_length = get_measurement(fp);
3222       }
3223     }
3224     else if (!_cups_strcasecmp(temp, "ModelName"))
3225     {
3226       // ModelName name
3227       char      name[256];              // Model name string
3228
3229
3230       if (!get_token(fp, name, sizeof(name)))
3231       {
3232         _cupsLangPrintf(stderr,
3233                         _("ppdc: Expected name after ModelName on line %d of "
3234                           "%s."), fp->line, fp->filename);
3235         break;
3236       }
3237
3238       if (!cond_state)
3239         d->set_model_name(name);
3240     }
3241     else if (!_cups_strcasecmp(temp, "ModelNumber"))
3242     {
3243       // ModelNumber number
3244       if (cond_state)
3245         get_integer(fp);
3246       else
3247         d->model_number = get_integer(fp);
3248     }
3249     else if (!_cups_strcasecmp(temp, "Option"))
3250     {
3251       // Get an option...
3252       ppdcOption *tempo = get_option(fp, d, g);
3253
3254       if (!tempo)
3255         break;
3256
3257       if (cond_state)
3258       {
3259         if (!g->find_option(tempo->name->value))
3260           tempo->release();
3261       }
3262       else
3263       {
3264         if (!g->find_option(tempo->name->value))
3265           g->add_option(tempo);
3266
3267         o = tempo;
3268       }
3269     }
3270     else if (!_cups_strcasecmp(temp, "FileName"))
3271     {
3272       // FileName name
3273       char      name[256];              // Filename string
3274
3275
3276       if (!get_token(fp, name, sizeof(name)))
3277       {
3278         _cupsLangPrintf(stderr,
3279                         _("ppdc: Expected name after FileName on line %d of "
3280                           "%s."), fp->line, fp->filename);
3281         break;
3282       }
3283
3284       if (!cond_state)
3285         d->set_file_name(name);
3286     }
3287     else if (!_cups_strcasecmp(temp, "PCFileName"))
3288     {
3289       // PCFileName name
3290       char      name[256];              // PC filename string
3291
3292
3293       if (!get_token(fp, name, sizeof(name)))
3294       {
3295         _cupsLangPrintf(stderr,
3296                         _("ppdc: Expected name after PCFileName on line %d of "
3297                           "%s."), fp->line, fp->filename);
3298         break;
3299       }
3300
3301       if (!cond_state)
3302         d->set_pc_file_name(name);
3303     }
3304     else if (!_cups_strcasecmp(temp, "Resolution"))
3305     {
3306       // Get the resolution choice...
3307       c = get_resolution(fp);
3308       if (!c)
3309         continue;
3310
3311       if (cond_state)
3312       {
3313         c->release();
3314         continue;
3315       }
3316
3317       // Add the choice to the Resolution option...
3318       if ((o = d->find_option_group("Resolution", &mg)) == NULL)
3319       {
3320         // Create the Resolution option...
3321         o = new ppdcOption(PPDC_PICKONE, "Resolution", NULL, PPDC_SECTION_ANY,
3322                            10.0f);
3323         g = general;
3324         g->add_option(o);
3325       }
3326       else if (mg != general)
3327       {
3328         _cupsLangPrintf(stderr,
3329                         _("ppdc: Option %s defined in two different groups on "
3330                           "line %d of %s."), "Resolution", fp->line,
3331                         fp->filename);
3332         c->release();
3333         continue;
3334       }
3335
3336       o->add_choice(c);
3337
3338       if (isdefault)
3339         o->set_defchoice(c);
3340
3341       o = NULL;
3342     }
3343     else if (!_cups_strcasecmp(temp, "SimpleColorProfile"))
3344     {
3345       ppdcProfile       *p;             // Color profile
3346
3347
3348       // Get the color profile...
3349       p = get_simple_profile(fp);
3350
3351       if (p)
3352       {
3353         if (cond_state)
3354           p->release();
3355         else
3356           d->profiles->add(p);
3357       }
3358     }
3359     else if (!_cups_strcasecmp(temp, "Throughput"))
3360     {
3361       // Throughput number
3362       if (cond_state)
3363         get_integer(fp);
3364       else
3365         d->throughput = get_integer(fp);
3366     }
3367     else if (!_cups_strcasecmp(temp, "UIConstraints"))
3368     {
3369       ppdcConstraint    *con;           // Constraint
3370
3371
3372       con = get_constraint(fp);
3373
3374       if (con)
3375       {
3376         if (cond_state)
3377           con->release();
3378         else
3379           d->constraints->add(con);
3380       }
3381     }
3382     else if (!_cups_strcasecmp(temp, "VariablePaperSize"))
3383     {
3384       // VariablePaperSize boolean
3385       if (cond_state)
3386         get_boolean(fp);
3387       else
3388         d->variable_paper_size = get_boolean(fp);
3389     }
3390     else if (!_cups_strcasecmp(temp, "Version"))
3391     {
3392       // Version string
3393       char      name[256];              // Model name string
3394
3395
3396       if (!get_token(fp, name, sizeof(name)))
3397       {
3398         _cupsLangPrintf(stderr,
3399                         _("ppdc: Expected string after Version on line %d of "
3400                           "%s."), fp->line, fp->filename);
3401         break;
3402       }
3403
3404       if (!cond_state)
3405         d->set_version(name);
3406     }
3407     else
3408     {
3409       _cupsLangPrintf(stderr,
3410                       _("ppdc: Unknown token \"%s\" seen on line %d of %s."),
3411                       temp, fp->line, fp->filename);
3412       break;
3413     }
3414   }
3415
3416   // Done processing this block, is there anything to save?
3417   if (!inc)
3418   {
3419     if (!d->pc_file_name || !d->model_name || !d->manufacturer || !d->version ||
3420         !d->sizes->count)
3421     {
3422       // Nothing to save...
3423       d->release();
3424     }
3425     else
3426     {
3427       // Got a driver, save it...
3428       drivers->add(d);
3429     }
3430   }
3431   else if (inc && td)
3432     td->release();
3433 }
3434
3435
3436 //
3437 // 'ppdcSource::set_variable()' - Set a variable.
3438 //
3439
3440 ppdcVariable *                          // O - Variable
3441 ppdcSource::set_variable(
3442     const char *name,                   // I - Name
3443     const char *value)                  // I - Value
3444 {
3445   ppdcVariable  *v;                     // Variable
3446
3447
3448   // See if the variable exists already...
3449   v = find_variable(name);
3450   if (v)
3451   {
3452     // Change the variable value...
3453     v->set_value(value);
3454   }
3455   else
3456   {
3457     // Create a new variable and add it...
3458     v = new ppdcVariable(name, value);
3459     vars->add(v);
3460   }
3461
3462   return (v);
3463 }
3464
3465
3466 //
3467 // 'ppdcSource::write_file()' - Write the current source data to a file.
3468 //
3469
3470 int                                     // O - 0 on success, -1 on error
3471 ppdcSource::write_file(const char *f)   // I - File to write
3472 {
3473   cups_file_t   *fp;                    // Output file
3474   char          bckname[1024];          // Backup file
3475   ppdcDriver    *d;                     // Current driver
3476   ppdcString    *st;                    // Current string
3477   ppdcAttr      *a;                     // Current attribute
3478   ppdcConstraint *co;                   // Current constraint
3479   ppdcFilter    *fi;                    // Current filter
3480   ppdcFont      *fo;                    // Current font
3481   ppdcGroup     *g;                     // Current group
3482   ppdcOption    *o;                     // Current option
3483   ppdcChoice    *ch;                    // Current choice
3484   ppdcProfile   *p;                     // Current color profile
3485   ppdcMediaSize *si;                    // Current media size
3486   float         left,                   // Current left margin
3487                 bottom,                 // Current bottom margin
3488                 right,                  // Current right margin
3489                 top;                    // Current top margin
3490   int           dtused[PPDC_DRIVER_MAX];// Driver type usage...
3491
3492
3493   // Rename the current file, if any, to .bck...
3494   snprintf(bckname, sizeof(bckname), "%s.bck", f);
3495   rename(f, bckname);
3496
3497   // Open the output file...
3498   fp = cupsFileOpen(f, "w");
3499
3500   if (!fp)
3501   {
3502     // Can't create file; restore backup and return...
3503     rename(bckname, f);
3504     return (-1);
3505   }
3506
3507   cupsFilePuts(fp, "// CUPS PPD Compiler " CUPS_SVERSION "\n\n");
3508
3509   // Include standard files...
3510   cupsFilePuts(fp, "// Include necessary files...\n");
3511   cupsFilePuts(fp, "#include <font.defs>\n");
3512   cupsFilePuts(fp, "#include <media.defs>\n");
3513
3514   memset(dtused, 0, sizeof(dtused));
3515
3516   for (d = (ppdcDriver *)drivers->first(); d; d = (ppdcDriver *)drivers->next())
3517     if (d->type > PPDC_DRIVER_PS && !dtused[d->type])
3518     {
3519       cupsFilePrintf(fp, "#include <%s.h>\n", driver_types[d->type]);
3520       dtused[d->type] = 1;
3521     }
3522
3523   // Output each driver...
3524   for (d = (ppdcDriver *)drivers->first(); d; d = (ppdcDriver *)drivers->next())
3525   {
3526     // Start the driver...
3527     cupsFilePrintf(fp, "\n// %s %s\n", d->manufacturer->value,
3528                    d->model_name->value);
3529     cupsFilePuts(fp, "{\n");
3530
3531     // Write the copyright stings...
3532     for (st = (ppdcString *)d->copyright->first();
3533          st;
3534          st = (ppdcString *)d->copyright->next())
3535       quotef(fp, "  Copyright \"%s\"\n", st->value);
3536
3537     // Write other strings and values...
3538     if (d->manufacturer && d->manufacturer->value)
3539       quotef(fp, "  Manufacturer \"%s\"\n", d->manufacturer->value);
3540     if (d->model_name->value)
3541       quotef(fp, "  ModelName \"%s\"\n", d->model_name->value);
3542     if (d->file_name && d->file_name->value)
3543       quotef(fp, "  FileName \"%s\"\n", d->file_name->value);
3544     if (d->pc_file_name && d->pc_file_name->value)
3545       quotef(fp, "  PCFileName \"%s\"\n", d->pc_file_name->value);
3546     if (d->version && d->version->value)
3547       quotef(fp, "  Version \"%s\"\n", d->version->value);
3548
3549     cupsFilePrintf(fp, "  DriverType %s\n", driver_types[d->type]);
3550
3551     if (d->model_number)
3552     {
3553       switch (d->type)
3554       {
3555         case PPDC_DRIVER_ESCP :
3556             cupsFilePuts(fp, "  ModelNumber (");
3557
3558             if (d->model_number & ESCP_DOTMATRIX)
3559               cupsFilePuts(fp, " $ESCP_DOTMATRIX");
3560             if (d->model_number & ESCP_MICROWEAVE)
3561               cupsFilePuts(fp, " $ESCP_MICROWEAVE");
3562             if (d->model_number & ESCP_STAGGER)
3563               cupsFilePuts(fp, " $ESCP_STAGGER");
3564             if (d->model_number & ESCP_ESCK)
3565               cupsFilePuts(fp, " $ESCP_ESCK");
3566             if (d->model_number & ESCP_EXT_UNITS)
3567               cupsFilePuts(fp, " $ESCP_EXT_UNITS");
3568             if (d->model_number & ESCP_EXT_MARGINS)
3569               cupsFilePuts(fp, " $ESCP_EXT_MARGINS");
3570             if (d->model_number & ESCP_USB)
3571               cupsFilePuts(fp, " $ESCP_USB");
3572             if (d->model_number & ESCP_PAGE_SIZE)
3573               cupsFilePuts(fp, " $ESCP_PAGE_SIZE");
3574             if (d->model_number & ESCP_RASTER_ESCI)
3575               cupsFilePuts(fp, " $ESCP_RASTER_ESCI");
3576             if (d->model_number & ESCP_REMOTE)
3577               cupsFilePuts(fp, " $ESCP_REMOTE");
3578
3579             cupsFilePuts(fp, ")\n");
3580             break;
3581
3582         case PPDC_DRIVER_PCL :
3583             cupsFilePuts(fp, "  ModelNumber (");
3584
3585             if (d->model_number & PCL_PAPER_SIZE)
3586               cupsFilePuts(fp, " $PCL_PAPER_SIZE");
3587             if (d->model_number & PCL_INKJET)
3588               cupsFilePuts(fp, " $PCL_INKJET");
3589             if (d->model_number & PCL_RASTER_END_COLOR)
3590               cupsFilePuts(fp, " $PCL_RASTER_END_COLOR");
3591             if (d->model_number & PCL_RASTER_CID)
3592               cupsFilePuts(fp, " $PCL_RASTER_CID");
3593             if (d->model_number & PCL_RASTER_CRD)
3594               cupsFilePuts(fp, " $PCL_RASTER_CRD");
3595             if (d->model_number & PCL_RASTER_SIMPLE)
3596               cupsFilePuts(fp, " $PCL_RASTER_SIMPLE");
3597             if (d->model_number & PCL_RASTER_RGB24)
3598               cupsFilePuts(fp, " $PCL_RASTER_RGB24");
3599             if (d->model_number & PCL_PJL)
3600               cupsFilePuts(fp, " $PCL_PJL");
3601             if (d->model_number & PCL_PJL_PAPERWIDTH)
3602               cupsFilePuts(fp, " $PCL_PJL_PAPERWIDTH");
3603             if (d->model_number & PCL_PJL_HPGL2)
3604               cupsFilePuts(fp, " $PCL_PJL_HPGL2");
3605             if (d->model_number & PCL_PJL_PCL3GUI)
3606               cupsFilePuts(fp, " $PCL_PJL_PCL3GUI");
3607             if (d->model_number & PCL_PJL_RESOLUTION)
3608               cupsFilePuts(fp, " $PCL_PJL_RESOLUTION");
3609
3610             cupsFilePuts(fp, ")\n");
3611             break;
3612
3613         case PPDC_DRIVER_LABEL :
3614             cupsFilePuts(fp, "  ModelNumber ");
3615
3616             switch (d->model_number)
3617             {
3618               case DYMO_3x0 :
3619                   cupsFilePuts(fp, "$DYMO_3x0\n");
3620                   break;
3621
3622               case ZEBRA_EPL_LINE :
3623                   cupsFilePuts(fp, "$ZEBRA_EPL_LINE\n");
3624                   break;
3625
3626               case ZEBRA_EPL_PAGE :
3627                   cupsFilePuts(fp, "$ZEBRA_EPL_PAGE\n");
3628                   break;
3629
3630               case ZEBRA_ZPL :
3631                   cupsFilePuts(fp, "$ZEBRA_ZPL\n");
3632                   break;
3633
3634               case ZEBRA_CPCL :
3635                   cupsFilePuts(fp, "$ZEBRA_CPCL\n");
3636                   break;
3637
3638               case INTELLITECH_PCL :
3639                   cupsFilePuts(fp, "$INTELLITECH_PCL\n");
3640                   break;
3641
3642               default :
3643                   cupsFilePrintf(fp, "%d\n", d->model_number);
3644                   break;
3645             }
3646             break;
3647
3648         case PPDC_DRIVER_EPSON :
3649             cupsFilePuts(fp, "  ModelNumber ");
3650
3651             switch (d->model_number)
3652             {
3653               case EPSON_9PIN :
3654                   cupsFilePuts(fp, "$EPSON_9PIN\n");
3655                   break;
3656
3657               case EPSON_24PIN :
3658                   cupsFilePuts(fp, "$EPSON_24PIN\n");
3659                   break;
3660
3661               case EPSON_COLOR :
3662                   cupsFilePuts(fp, "$EPSON_COLOR\n");
3663                   break;
3664
3665               case EPSON_PHOTO :
3666                   cupsFilePuts(fp, "$EPSON_PHOTO\n");
3667                   break;
3668
3669               case EPSON_ICOLOR :
3670                   cupsFilePuts(fp, "$EPSON_ICOLOR\n");
3671                   break;
3672
3673               case EPSON_IPHOTO :
3674                   cupsFilePuts(fp, "$EPSON_IPHOTO\n");
3675                   break;
3676
3677               default :
3678                   cupsFilePrintf(fp, "%d\n", d->model_number);
3679                   break;
3680             }
3681             break;
3682
3683         case PPDC_DRIVER_HP :
3684             cupsFilePuts(fp, "  ModelNumber ");
3685             switch (d->model_number)
3686             {
3687               case HP_LASERJET :
3688                   cupsFilePuts(fp, "$HP_LASERJET\n");
3689                   break;
3690
3691               case HP_DESKJET :
3692                   cupsFilePuts(fp, "$HP_DESKJET\n");
3693                   break;
3694
3695               case HP_DESKJET2 :
3696                   cupsFilePuts(fp, "$HP_DESKJET2\n");
3697                   break;
3698
3699               default :
3700                   cupsFilePrintf(fp, "%d\n", d->model_number);
3701                   break;
3702             }
3703
3704             cupsFilePuts(fp, ")\n");
3705             break;
3706
3707         default :
3708             cupsFilePrintf(fp, "  ModelNumber %d\n", d->model_number);
3709             break;
3710       }
3711     }
3712
3713     if (d->manual_copies)
3714       cupsFilePuts(fp, "  ManualCopies Yes\n");
3715
3716     if (d->color_device)
3717       cupsFilePuts(fp, "  ColorDevice Yes\n");
3718
3719     if (d->throughput)
3720       cupsFilePrintf(fp, "  Throughput %d\n", d->throughput);
3721
3722     // Output all of the attributes...
3723     for (a = (ppdcAttr *)d->attrs->first();
3724          a;
3725          a = (ppdcAttr *)d->attrs->next())
3726       if (a->text->value && a->text->value[0])
3727         quotef(fp, "  Attribute \"%s\" \"%s/%s\" \"%s\"\n",
3728                a->name->value, a->selector->value ? a->selector->value : "",
3729                a->text->value, a->value->value ? a->value->value : "");
3730       else
3731         quotef(fp, "  Attribute \"%s\" \"%s\" \"%s\"\n",
3732                a->name->value, a->selector->value ? a->selector->value : "",
3733                a->value->value ? a->value->value : "");
3734
3735     // Output all of the constraints...
3736     for (co = (ppdcConstraint *)d->constraints->first();
3737          co;
3738          co = (ppdcConstraint *)d->constraints->next())
3739     {
3740       if (co->option1->value[0] == '*')
3741         cupsFilePrintf(fp, "  UIConstraints \"%s %s", co->option1->value,
3742                        co->choice1->value ? co->choice1->value : "");
3743       else
3744         cupsFilePrintf(fp, "  UIConstraints \"*%s %s", co->option1->value,
3745                        co->choice1->value ? co->choice1->value : "");
3746
3747       if (co->option2->value[0] == '*')
3748         cupsFilePrintf(fp, " %s %s\"\n", co->option2->value,
3749                        co->choice2->value ? co->choice2->value : "");
3750       else
3751         cupsFilePrintf(fp, " *%s %s\"\n", co->option2->value,
3752                        co->choice2->value ? co->choice2->value : "");
3753     }
3754
3755     // Output all of the filters...
3756     for (fi = (ppdcFilter *)d->filters->first();
3757          fi;
3758          fi = (ppdcFilter *)d->filters->next())
3759       cupsFilePrintf(fp, "  Filter \"%s %d %s\"\n",
3760                      fi->mime_type->value, fi->cost, fi->program->value);
3761
3762     // Output all of the fonts...
3763     for (fo = (ppdcFont *)d->fonts->first();
3764          fo;
3765          fo = (ppdcFont *)d->fonts->next())
3766       if (!strcmp(fo->name->value, "*"))
3767         cupsFilePuts(fp, "  Font *\n");
3768       else
3769         cupsFilePrintf(fp, "  Font \"%s\" \"%s\" \"%s\" \"%s\" %s\n",
3770                        fo->name->value, fo->encoding->value,
3771                        fo->version->value, fo->charset->value,
3772                        fo->status == PPDC_FONT_ROM ? "ROM" : "Disk");
3773
3774     // Output all options...
3775     for (g = (ppdcGroup *)d->groups->first();
3776          g;
3777          g = (ppdcGroup *)d->groups->next())
3778     {
3779       if (g->options->count == 0)
3780         continue;
3781
3782       if (g->text->value && g->text->value[0])
3783         quotef(fp, "  Group \"%s/%s\"\n", g->name->value, g->text->value);
3784       else
3785         cupsFilePrintf(fp, "  Group \"%s\"\n", g->name->value);
3786
3787       for (o = (ppdcOption *)g->options->first();
3788            o;
3789            o = (ppdcOption *)g->options->next())
3790       {
3791         if (o->choices->count == 0)
3792           continue;
3793
3794         if (o->text->value && o->text->value[0])
3795           quotef(fp, "    Option \"%s/%s\"", o->name->value, o->text->value);
3796         else
3797           cupsFilePrintf(fp, "    Option \"%s\"", o->name->value);
3798
3799         cupsFilePrintf(fp, " %s %s %.1f\n",
3800                        o->type == PPDC_BOOLEAN ? "Boolean" :
3801                            o->type == PPDC_PICKONE ? "PickOne" : "PickMany",
3802                        o->section == PPDC_SECTION_ANY ? "AnySetup" :
3803                            o->section == PPDC_SECTION_DOCUMENT ? "DocumentSetup" :
3804                            o->section == PPDC_SECTION_EXIT ? "ExitServer" :
3805                            o->section == PPDC_SECTION_JCL ? "JCLSetup" :
3806                            o->section == PPDC_SECTION_PAGE ? "PageSetup" :
3807                            "Prolog",
3808                        o->order);
3809
3810         for (ch = (ppdcChoice *)o->choices->first();
3811              ch;
3812              ch = (ppdcChoice *)o->choices->next())
3813         {
3814           if (ch->text->value && ch->text->value[0])
3815             quotef(fp, "      %sChoice \"%s/%s\" \"%s\"\n",
3816                    o->defchoice == ch->name ? "*" : "",
3817                    ch->name->value, ch->text->value,
3818                    ch->code->value ? ch->code->value : "");
3819           else
3820             quotef(fp, "      %sChoice \"%s\" \"%s\"\n",
3821                    o->defchoice == ch->name ? "*" : "",
3822                    ch->name->value,
3823                    ch->code->value ? ch->code->value : "");
3824         }
3825       }
3826     }
3827
3828     // Output all of the color profiles...
3829     for (p = (ppdcProfile *)d->profiles->first();
3830          p;
3831          p = (ppdcProfile *)d->profiles->next())
3832       cupsFilePrintf(fp, "  ColorProfile \"%s/%s\" %.3f %.3f "
3833                          "%.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f\n",
3834                      p->resolution->value, p->media_type->value,
3835                      p->density, p->gamma,
3836                      p->profile[0], p->profile[1], p->profile[2],
3837                      p->profile[3], p->profile[4], p->profile[5],
3838                      p->profile[6], p->profile[7], p->profile[8]);
3839
3840     // Output all of the media sizes...
3841     left   = 0.0;
3842     bottom = 0.0;
3843     right  = 0.0;
3844     top    = 0.0;
3845
3846     for (si = (ppdcMediaSize *)d->sizes->first();
3847          si;
3848          si = (ppdcMediaSize *)d->sizes->next())
3849       if (si->size_code->value && si->region_code->value)
3850       {
3851         // Output a custom media size...
3852         quotef(fp, "  %sCustomMedia \"%s/%s\" %.2f %.2f %.2f %.2f %.2f %.2f \"%s\" \"%s\"\n",
3853                si->name == d->default_size ? "*" : "", si->name->value,
3854                si->text->value, si->width, si->length, si->left, si->bottom,
3855                si->right, si->top, si->size_code->value,
3856                si->region_code->value);
3857       }
3858       else
3859       {
3860         // Output a standard media size...
3861         if (fabs(left - si->left) > 0.1 ||
3862             fabs(bottom - si->bottom) > 0.1 ||
3863             fabs(right - si->right) > 0.1 ||
3864             fabs(top - si->top) > 0.1)
3865         {
3866           cupsFilePrintf(fp, "  HWMargins %.2f %.2f %.2f %.2f\n",
3867                          si->left, si->bottom, si->right, si->top);
3868
3869           left   = si->left;
3870           bottom = si->bottom;
3871           right  = si->right;
3872           top    = si->top;
3873         }
3874
3875         cupsFilePrintf(fp, "  %sMediaSize %s\n",
3876                        si->name == d->default_size ? "*" : "",
3877                        si->name->value);
3878       }
3879
3880     if (d->variable_paper_size)
3881     {
3882       cupsFilePuts(fp, "  VariablePaperSize Yes\n");
3883
3884       if (fabs(left - d->left_margin) > 0.1 ||
3885           fabs(bottom - d->bottom_margin) > 0.1 ||
3886           fabs(right - d->right_margin) > 0.1 ||
3887           fabs(top - d->top_margin) > 0.1)
3888       {
3889         cupsFilePrintf(fp, "  HWMargins %.2f %.2f %.2f %.2f\n",
3890                        d->left_margin, d->bottom_margin, d->right_margin,
3891                        d->top_margin);
3892       }
3893
3894       cupsFilePrintf(fp, "  MinSize %.2f %.2f\n", d->min_width, d->min_length);
3895       cupsFilePrintf(fp, "  MaxSize %.2f %.2f\n", d->max_width, d->max_length);
3896     }
3897
3898     // End the driver...
3899     cupsFilePuts(fp, "}\n");
3900   }
3901
3902   // Close the file and return...
3903   cupsFileClose(fp);
3904
3905   return (0);
3906 }
3907
3908
3909 //
3910 // End of "$Id: ppdc-source.cxx 10379 2012-03-23 22:16:22Z mike $".
3911 //