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