1 //
2 // Source class for the CUPS PPD Compiler.
3 //
4 // Copyright 2007-2018 by Apple Inc.
5 // Copyright 2002-2007 by Easy Software Products.
6 //
7 // Licensed under Apache License v2.0.  See the file "LICENSE" for more
8 // information.
9 //
10 
11 //
12 // Include necessary headers...
13 //
14 
15 #include "ppdc-private.h"
16 #include <limits.h>
17 #include <math.h>
18 #include <unistd.h>
19 #include <cups/raster.h>
20 #include "data/epson.h"
21 #include "data/hp.h"
22 #include "data/label.h"
23 #ifndef _WIN32
24 #  include <sys/utsname.h>
25 #endif // !_WIN32
26 
27 
28 //
29 // Class globals...
30 //
31 
32 ppdcArray	*ppdcSource::includes = 0;
33 const char	*ppdcSource::driver_types[] =
34 		{
35 		  "custom",
36 		  "ps",
37 		  "escp",
38 		  "pcl",
39 		  "label",
40 		  "epson",
41 		  "hp"
42 		};
43 
44 
45 //
46 // 'ppdcSource::ppdcSource()' - Load a driver source file.
47 //
48 
ppdcSource(const char * f,cups_file_t * ffp)49 ppdcSource::ppdcSource(const char  *f,	// I - File to read
50                        cups_file_t *ffp)// I - File pointer to use
51   : ppdcShared()
52 {
53   PPDC_NEW;
54 
55   filename      = new ppdcString(f);
56   base_fonts    = new ppdcArray();
57   drivers       = new ppdcArray();
58   po_files      = new ppdcArray();
59   sizes         = new ppdcArray();
60   vars          = new ppdcArray();
61   cond_state    = PPDC_COND_NORMAL;
62   cond_current  = cond_stack;
63   cond_stack[0] = PPDC_COND_NORMAL;
64 
65   // Add standard #define variables...
66 #define MAKE_STRING(x) #x
67 
68   vars->add(new ppdcVariable("CUPS_VERSION", MAKE_STRING(CUPS_VERSION)));
69   vars->add(new ppdcVariable("CUPS_VERSION_MAJOR", MAKE_STRING(CUPS_VERSION_MAJOR)));
70   vars->add(new ppdcVariable("CUPS_VERSION_MINOR", MAKE_STRING(CUPS_VERSION_MINOR)));
71   vars->add(new ppdcVariable("CUPS_VERSION_PATCH", MAKE_STRING(CUPS_VERSION_PATCH)));
72 
73 #ifdef _WIN32
74   vars->add(new ppdcVariable("PLATFORM_NAME", "Windows"));
75   vars->add(new ppdcVariable("PLATFORM_ARCH", "X86"));
76 
77 #else
78   struct utsname name;			// uname information
79 
80   if (!uname(&name))
81   {
82     vars->add(new ppdcVariable("PLATFORM_NAME", name.sysname));
83     vars->add(new ppdcVariable("PLATFORM_ARCH", name.machine));
84   }
85   else
86   {
87     vars->add(new ppdcVariable("PLATFORM_NAME", "unknown"));
88     vars->add(new ppdcVariable("PLATFORM_ARCH", "unknown"));
89   }
90 #endif // _WIN32
91 
92   if (f)
93     read_file(f, ffp);
94 }
95 
96 
97 //
98 // 'ppdcSource::~ppdcSource()' - Free a driver source file.
99 //
100 
~ppdcSource()101 ppdcSource::~ppdcSource()
102 {
103   PPDC_DELETE;
104 
105   filename->release();
106   base_fonts->release();
107   drivers->release();
108   po_files->release();
109   sizes->release();
110   vars->release();
111 }
112 
113 
114 //
115 // 'ppdcSource::add_include()' - Add an include directory.
116 //
117 
118 void
add_include(const char * d)119 ppdcSource::add_include(const char *d)	// I - Include directory
120 {
121   if (!d)
122     return;
123 
124   if (!includes)
125     includes = new ppdcArray();
126 
127   includes->add(new ppdcString(d));
128 }
129 
130 
131 //
132 // 'ppdcSource::find_driver()' - Find a driver.
133 //
134 
135 ppdcDriver *				// O - Driver
find_driver(const char * f)136 ppdcSource::find_driver(const char *f)	// I - Driver file name
137 {
138   ppdcDriver	*d;			// Current driver
139 
140 
141   for (d = (ppdcDriver *)drivers->first(); d; d = (ppdcDriver *)drivers->next())
142     if (!_cups_strcasecmp(f, d->pc_file_name->value))
143       return (d);
144 
145   return (NULL);
146 }
147 
148 
149 //
150 // 'ppdcSource::find_include()' - Find an include file.
151 //
152 
153 char *					// O - Found path or NULL
find_include(const char * f,const char * base,char * n,int nlen)154 ppdcSource::find_include(
155     const char *f,			// I - Include filename
156     const char *base,			// I - Current directory
157     char       *n,			// I - Path buffer
158     int        nlen)			// I - Path buffer length
159 {
160   ppdcString	*dir;			// Include directory
161   char		temp[1024],		// Temporary path
162 		*ptr;			// Pointer to end of path
163 
164 
165   // Range check input...
166   if (!f || !*f || !n || nlen < 2)
167     return (0);
168 
169   // Check the first character to see if we have <name> or "name"...
170   if (*f == '<')
171   {
172     // Remove the surrounding <> from the name...
173     strlcpy(temp, f + 1, sizeof(temp));
174     ptr = temp + strlen(temp) - 1;
175 
176     if (*ptr != '>')
177     {
178       _cupsLangPrintf(stderr,
179                       _("ppdc: Invalid #include/#po filename \"%s\"."), n);
180       return (0);
181     }
182 
183     *ptr = '\0';
184     f    = temp;
185   }
186   else
187   {
188     // Check for the local file relative to the current directory...
189     if (base && *base && f[0] != '/')
190       snprintf(n, (size_t)nlen, "%s/%s", base, f);
191     else
192       strlcpy(n, f, (size_t)nlen);
193 
194     if (!access(n, 0))
195       return (n);
196     else if (*f == '/')
197     {
198       // Absolute path that doesn't exist...
199       return (0);
200     }
201   }
202 
203   // Search the include directories, if any...
204   if (includes)
205   {
206     for (dir = (ppdcString *)includes->first(); dir; dir = (ppdcString *)includes->next())
207     {
208       snprintf(n, (size_t)nlen, "%s/%s", dir->value, f);
209       if (!access(n, 0))
210         return (n);
211     }
212   }
213 
214   // Search the standard include directories...
215   _cups_globals_t *cg = _cupsGlobals();	// Global data
216 
217   snprintf(n, (size_t)nlen, "%s/ppdc/%s", cg->cups_datadir, f);
218   if (!access(n, 0))
219     return (n);
220 
221   snprintf(n, (size_t)nlen, "%s/po/%s", cg->cups_datadir, f);
222   if (!access(n, 0))
223     return (n);
224   else
225     return (0);
226 }
227 
228 
229 //
230 // 'ppdcSource::find_po()' - Find a message catalog for the given locale.
231 //
232 
233 ppdcCatalog *				// O - Message catalog or NULL
find_po(const char * l)234 ppdcSource::find_po(const char *l)	// I - Locale name
235 {
236   ppdcCatalog	*cat;			// Current message catalog
237 
238 
239   for (cat = (ppdcCatalog *)po_files->first();
240        cat;
241        cat = (ppdcCatalog *)po_files->next())
242     if (!_cups_strcasecmp(l, cat->locale->value))
243       return (cat);
244 
245   return (NULL);
246 }
247 
248 
249 //
250 // 'ppdcSource::find_size()' - Find a media size.
251 //
252 
253 ppdcMediaSize *				// O - Size
find_size(const char * s)254 ppdcSource::find_size(const char *s)	// I - Size name
255 {
256   ppdcMediaSize	*m;			// Current media size
257 
258 
259   for (m = (ppdcMediaSize *)sizes->first(); m; m = (ppdcMediaSize *)sizes->next())
260     if (!_cups_strcasecmp(s, m->name->value))
261       return (m);
262 
263   return (NULL);
264 }
265 
266 
267 //
268 // 'ppdcSource::find_variable()' - Find a variable.
269 //
270 
271 ppdcVariable *				// O - Variable
find_variable(const char * n)272 ppdcSource::find_variable(const char *n)// I - Variable name
273 {
274   ppdcVariable	*v;			// Current variable
275 
276 
277   for (v = (ppdcVariable *)vars->first(); v; v = (ppdcVariable *)vars->next())
278     if (!_cups_strcasecmp(n, v->name->value))
279       return (v);
280 
281   return (NULL);
282 }
283 
284 
285 //
286 // 'ppdcSource::get_attr()' - Get an attribute.
287 //
288 
289 ppdcAttr *				// O - Attribute
get_attr(ppdcFile * fp,bool loc)290 ppdcSource::get_attr(ppdcFile *fp, 	// I - File to read
291                      bool     loc)	// I - Localize this attribute?
292 {
293   char	name[1024],			// Name string
294 	selector[1024],			// Selector string
295 	*text,				// Text string
296 	value[1024];			// Value string
297 
298 
299   // Get the attribute parameters:
300   //
301   // Attribute name selector value
302   if (!get_token(fp, name, sizeof(name)))
303   {
304     _cupsLangPrintf(stderr,
305                     _("ppdc: Expected name after %s on line %d of %s."),
306 		    loc ? "LocAttribute" : "Attribute", fp->line, fp->filename);
307     return (0);
308   }
309 
310   if (!get_token(fp, selector, sizeof(selector)))
311   {
312     _cupsLangPrintf(stderr,
313                     _("ppdc: Expected selector after %s on line %d of %s."),
314 		    loc ? "LocAttribute" : "Attribute", fp->line, fp->filename);
315     return (0);
316   }
317 
318   if ((text = strchr(selector, '/')) != NULL)
319     *text++ = '\0';
320 
321   if (!get_token(fp, value, sizeof(value)))
322   {
323     _cupsLangPrintf(stderr,
324                     _("ppdc: Expected value after %s on line %d of %s."),
325 		    loc ? "LocAttribute" : "Attribute", fp->line, fp->filename);
326     return (0);
327   }
328 
329   return (new ppdcAttr(name, selector, text, value, loc));
330 }
331 
332 
333 //
334 // 'ppdcSource::get_boolean()' - Get a boolean value.
335 //
336 
337 int					// O - Boolean value
get_boolean(ppdcFile * fp)338 ppdcSource::get_boolean(ppdcFile *fp)	// I - File to read
339 {
340   char	buffer[256];			// String buffer
341 
342 
343   if (!get_token(fp, buffer, sizeof(buffer)))
344   {
345     _cupsLangPrintf(stderr,
346                     _("ppdc: Expected boolean value on line %d of %s."),
347 		    fp->line, fp->filename);
348     return (-1);
349   }
350 
351   if (!_cups_strcasecmp(buffer, "on") ||
352       !_cups_strcasecmp(buffer, "yes") ||
353       !_cups_strcasecmp(buffer, "true"))
354     return (1);
355   else if (!_cups_strcasecmp(buffer, "off") ||
356 	   !_cups_strcasecmp(buffer, "no") ||
357 	   !_cups_strcasecmp(buffer, "false"))
358     return (0);
359   else
360   {
361     _cupsLangPrintf(stderr,
362                     _("ppdc: Bad boolean value (%s) on line %d of %s."),
363 		    buffer, fp->line, fp->filename);
364     return (-1);
365   }
366 }
367 
368 
369 //
370 // 'ppdcSource::get_choice()' - Get a choice.
371 //
372 
373 ppdcChoice *				// O - Choice data
get_choice(ppdcFile * fp)374 ppdcSource::get_choice(ppdcFile *fp)	// I - File to read
375 {
376   char	name[1024],			// Name
377 	*text,				// Text
378 	code[10240];			// Code
379 
380 
381   // Read a choice from the file:
382   //
383   // Choice name/text code
384   if (!get_token(fp, name, sizeof(name)))
385   {
386     _cupsLangPrintf(stderr,
387                     _("ppdc: Expected choice name/text on line %d of %s."),
388 		    fp->line, fp->filename);
389     return (NULL);
390   }
391 
392   if ((text = strchr(name, '/')) != NULL)
393     *text++ = '\0';
394   else
395     text = name;
396 
397   if (!get_token(fp, code, sizeof(code)))
398   {
399     _cupsLangPrintf(stderr, _("ppdc: Expected choice code on line %d of %s."),
400 		    fp->line, fp->filename);
401     return (NULL);
402   }
403 
404   // Return the new choice
405   return (new ppdcChoice(name, text, code));
406 }
407 
408 
409 //
410 // 'ppdcSource::get_color_model()' - Get an old-style color model option.
411 //
412 
413 ppdcChoice *				// O - Choice data
get_color_model(ppdcFile * fp)414 ppdcSource::get_color_model(ppdcFile *fp)
415 					// I - File to read
416 {
417   char		name[1024],		// Option name
418 		*text,			// Text option
419 		temp[256];		// Temporary string
420   int		color_space,		// Colorspace
421 		color_order,		// Color order
422 		compression;		// Compression mode
423 
424 
425   // Get the ColorModel parameters:
426   //
427   // ColorModel name/text colorspace colororder compression
428   if (!get_token(fp, name, sizeof(name)))
429   {
430     _cupsLangPrintf(stderr,
431                     _("ppdc: Expected name/text combination for ColorModel on "
432 		      "line %d of %s."), fp->line, fp->filename);
433     return (NULL);
434   }
435 
436   if ((text = strchr(name, '/')) != NULL)
437     *text++ = '\0';
438   else
439     text = name;
440 
441   if (!get_token(fp, temp, sizeof(temp)))
442   {
443     _cupsLangPrintf(stderr,
444                     _("ppdc: Expected colorspace for ColorModel on line %d of "
445 		      "%s."), fp->line, fp->filename);
446     return (NULL);
447   }
448 
449   if ((color_space = get_color_space(temp)) < 0)
450     color_space = get_integer(temp);
451 
452   if (!get_token(fp, temp, sizeof(temp)))
453   {
454     _cupsLangPrintf(stderr,
455                     _("ppdc: Expected color order for ColorModel on line %d of "
456 		      "%s."), fp->line, fp->filename);
457     return (NULL);
458   }
459 
460   if ((color_order = get_color_order(temp)) < 0)
461     color_order = get_integer(temp);
462 
463   if (!get_token(fp, temp, sizeof(temp)))
464   {
465     _cupsLangPrintf(stderr,
466                     _("ppdc: Expected compression for ColorModel on line %d of "
467 		      "%s."), fp->line, fp->filename);
468     return (NULL);
469   }
470 
471   compression = get_integer(temp);
472 
473   snprintf(temp, sizeof(temp),
474            "<</cupsColorSpace %d/cupsColorOrder %d/cupsCompression %d>>"
475 	   "setpagedevice",
476            color_space, color_order, compression);
477 
478   return (new ppdcChoice(name, text, temp));
479 }
480 
481 
482 //
483 // 'ppdcSource::get_color_order()' - Get an old-style color order value.
484 //
485 
486 int					// O - Color order value
get_color_order(const char * co)487 ppdcSource::get_color_order(
488     const char *co)			// I - Color order string
489 {
490   if (!_cups_strcasecmp(co, "chunked") ||
491       !_cups_strcasecmp(co, "chunky"))
492     return (CUPS_ORDER_CHUNKED);
493   else if (!_cups_strcasecmp(co, "banded"))
494     return (CUPS_ORDER_BANDED);
495   else if (!_cups_strcasecmp(co, "planar"))
496     return (CUPS_ORDER_PLANAR);
497   else
498     return (-1);
499 }
500 
501 
502 //
503 // 'ppdcSource::get_color_profile()' - Get a color profile definition.
504 //
505 
506 ppdcProfile *				// O - Color profile
get_color_profile(ppdcFile * fp)507 ppdcSource::get_color_profile(
508     ppdcFile *fp)			// I - File to read
509 {
510   char		resolution[1024],	// Resolution/media type
511 		*media_type;		// Media type
512   int		i;			// Looping var
513   float		g,			// Gamma value
514 		d,			// Density value
515 		m[9];			// Transform matrix
516 
517 
518   // Get the ColorProfile parameters:
519   //
520   // ColorProfile resolution/mediatype gamma density m00 m01 m02 ... m22
521   if (!get_token(fp, resolution, sizeof(resolution)))
522   {
523     _cupsLangPrintf(stderr,
524                     _("ppdc: Expected resolution/mediatype following "
525 		      "ColorProfile on line %d of %s."),
526 		    fp->line, fp->filename);
527     return (NULL);
528   }
529 
530   if ((media_type = strchr(resolution, '/')) != NULL)
531     *media_type++ = '\0';
532   else
533     media_type = resolution;
534 
535   g = get_float(fp);
536   d = get_float(fp);
537   for (i = 0; i < 9; i ++)
538     m[i] = get_float(fp);
539 
540   return (new ppdcProfile(resolution, media_type, g, d, m));
541 }
542 
543 
544 //
545 // 'ppdcSource::get_color_space()' - Get an old-style colorspace value.
546 //
547 
548 int					// O - Colorspace value
get_color_space(const char * cs)549 ppdcSource::get_color_space(
550     const char *cs)			// I - Colorspace string
551 {
552   if (!_cups_strcasecmp(cs, "w"))
553     return (CUPS_CSPACE_W);
554   else if (!_cups_strcasecmp(cs, "rgb"))
555     return (CUPS_CSPACE_RGB);
556   else if (!_cups_strcasecmp(cs, "rgba"))
557     return (CUPS_CSPACE_RGBA);
558   else if (!_cups_strcasecmp(cs, "k"))
559     return (CUPS_CSPACE_K);
560   else if (!_cups_strcasecmp(cs, "cmy"))
561     return (CUPS_CSPACE_CMY);
562   else if (!_cups_strcasecmp(cs, "ymc"))
563     return (CUPS_CSPACE_YMC);
564   else if (!_cups_strcasecmp(cs, "cmyk"))
565     return (CUPS_CSPACE_CMYK);
566   else if (!_cups_strcasecmp(cs, "ymck"))
567     return (CUPS_CSPACE_YMCK);
568   else if (!_cups_strcasecmp(cs, "kcmy"))
569     return (CUPS_CSPACE_KCMY);
570   else if (!_cups_strcasecmp(cs, "kcmycm"))
571     return (CUPS_CSPACE_KCMYcm);
572   else if (!_cups_strcasecmp(cs, "gmck"))
573     return (CUPS_CSPACE_GMCK);
574   else if (!_cups_strcasecmp(cs, "gmcs"))
575     return (CUPS_CSPACE_GMCS);
576   else if (!_cups_strcasecmp(cs, "white"))
577     return (CUPS_CSPACE_WHITE);
578   else if (!_cups_strcasecmp(cs, "gold"))
579     return (CUPS_CSPACE_GOLD);
580   else if (!_cups_strcasecmp(cs, "silver"))
581     return (CUPS_CSPACE_SILVER);
582   else if (!_cups_strcasecmp(cs, "CIEXYZ"))
583     return (CUPS_CSPACE_CIEXYZ);
584   else if (!_cups_strcasecmp(cs, "CIELab"))
585     return (CUPS_CSPACE_CIELab);
586   else if (!_cups_strcasecmp(cs, "RGBW"))
587     return (CUPS_CSPACE_RGBW);
588   else if (!_cups_strcasecmp(cs, "ICC1"))
589     return (CUPS_CSPACE_ICC1);
590   else if (!_cups_strcasecmp(cs, "ICC2"))
591     return (CUPS_CSPACE_ICC2);
592   else if (!_cups_strcasecmp(cs, "ICC3"))
593     return (CUPS_CSPACE_ICC3);
594   else if (!_cups_strcasecmp(cs, "ICC4"))
595     return (CUPS_CSPACE_ICC4);
596   else if (!_cups_strcasecmp(cs, "ICC5"))
597     return (CUPS_CSPACE_ICC5);
598   else if (!_cups_strcasecmp(cs, "ICC6"))
599     return (CUPS_CSPACE_ICC6);
600   else if (!_cups_strcasecmp(cs, "ICC7"))
601     return (CUPS_CSPACE_ICC7);
602   else if (!_cups_strcasecmp(cs, "ICC8"))
603     return (CUPS_CSPACE_ICC8);
604   else if (!_cups_strcasecmp(cs, "ICC9"))
605     return (CUPS_CSPACE_ICC9);
606   else if (!_cups_strcasecmp(cs, "ICCA"))
607     return (CUPS_CSPACE_ICCA);
608   else if (!_cups_strcasecmp(cs, "ICCB"))
609     return (CUPS_CSPACE_ICCB);
610   else if (!_cups_strcasecmp(cs, "ICCC"))
611     return (CUPS_CSPACE_ICCC);
612   else if (!_cups_strcasecmp(cs, "ICCD"))
613     return (CUPS_CSPACE_ICCD);
614   else if (!_cups_strcasecmp(cs, "ICCE"))
615     return (CUPS_CSPACE_ICCE);
616   else if (!_cups_strcasecmp(cs, "ICCF"))
617     return (CUPS_CSPACE_ICCF);
618   else
619     return (-1);
620 }
621 
622 
623 //
624 // 'ppdcSource::get_constraint()' - Get a constraint.
625 //
626 
627 ppdcConstraint *			// O - Constraint
get_constraint(ppdcFile * fp)628 ppdcSource::get_constraint(ppdcFile *fp)// I - File to read
629 {
630   char		temp[1024],		// One string to rule them all
631 		*ptr,			// Pointer into string
632 		*option1,		// Constraint option 1
633 		*choice1,		// Constraint choice 1
634 		*option2,		// Constraint option 2
635 		*choice2;		// Constraint choice 2
636 
637 
638   // Read the UIConstaints parameter in one of the following forms:
639   //
640   // UIConstraints "*Option1 *Option2"
641   // UIConstraints "*Option1 Choice1 *Option2"
642   // UIConstraints "*Option1 *Option2 Choice2"
643   // UIConstraints "*Option1 Choice1 *Option2 Choice2"
644   if (!get_token(fp, temp, sizeof(temp)))
645   {
646     _cupsLangPrintf(stderr,
647                     _("ppdc: Expected constraints string for UIConstraints on "
648 		      "line %d of %s."), fp->line, fp->filename);
649     return (NULL);
650   }
651 
652   for (ptr = temp; isspace(*ptr); ptr ++);
653 
654   if (*ptr != '*')
655   {
656     _cupsLangPrintf(stderr,
657                     _("ppdc: Option constraint must *name on line %d of %s."),
658 		    fp->line, fp->filename);
659     return (NULL);
660   }
661 
662   option1 = ptr;
663 
664   for (; *ptr && !isspace(*ptr); ptr ++);
665   for (; isspace(*ptr); *ptr++ = '\0');
666 
667   if (*ptr != '*')
668   {
669     choice1 = ptr;
670 
671     for (; *ptr && !isspace(*ptr); ptr ++);
672     for (; isspace(*ptr); *ptr++ = '\0');
673   }
674   else
675     choice1 = NULL;
676 
677   if (*ptr != '*')
678   {
679     _cupsLangPrintf(stderr,
680                     _("ppdc: Expected two option names on line %d of %s."),
681 		    fp->line, fp->filename);
682     return (NULL);
683   }
684 
685   option2 = ptr;
686 
687   for (; *ptr && !isspace(*ptr); ptr ++);
688   for (; isspace(*ptr); *ptr++ = '\0');
689 
690   if (*ptr)
691     choice2 = ptr;
692   else
693     choice2 = NULL;
694 
695   return (new ppdcConstraint(option1, choice1, option2, choice2));
696 }
697 
698 
699 //
700 // 'ppdcSource::get_custom_size()' - Get a custom media size definition from a file.
701 //
702 
703 ppdcMediaSize *				// O - Media size
get_custom_size(ppdcFile * fp)704 ppdcSource::get_custom_size(ppdcFile *fp)
705 					// I - File to read
706 {
707   char		name[1024],		// Name
708 		*text,			// Text
709 		size_code[10240],	// PageSize code
710 		region_code[10240];	// PageRegion
711   float		width,			// Width
712 		length,			// Length
713 		left,			// Left margin
714 		bottom,			// Bottom margin
715 		right,			// Right margin
716 		top;			// Top margin
717 
718 
719   // Get the name, text, width, length, margins, and code:
720   //
721   // CustomMedia name/text width length left bottom right top size-code region-code
722   if (!get_token(fp, name, sizeof(name)))
723     return (NULL);
724 
725   if ((text = strchr(name, '/')) != NULL)
726     *text++ = '\0';
727   else
728     text = name;
729 
730   if ((width = get_measurement(fp)) < 0.0f)
731     return (NULL);
732 
733   if ((length = get_measurement(fp)) < 0.0f)
734     return (NULL);
735 
736   if ((left = get_measurement(fp)) < 0.0f)
737     return (NULL);
738 
739   if ((bottom = get_measurement(fp)) < 0.0f)
740     return (NULL);
741 
742   if ((right = get_measurement(fp)) < 0.0f)
743     return (NULL);
744 
745   if ((top = get_measurement(fp)) < 0.0f)
746     return (NULL);
747 
748   if (!get_token(fp, size_code, sizeof(size_code)))
749     return (NULL);
750 
751   if (!get_token(fp, region_code, sizeof(region_code)))
752     return (NULL);
753 
754   // Return the new media size...
755   return (new ppdcMediaSize(name, text, width, length, left, bottom,
756                             right, top, size_code, region_code));
757 }
758 
759 
760 //
761 // 'ppdcSource::get_duplex()' - Get a duplex option.
762 //
763 
764 void
get_duplex(ppdcFile * fp,ppdcDriver * d)765 ppdcSource::get_duplex(ppdcFile   *fp,	// I - File to read from
766                        ppdcDriver *d)	// I - Current driver
767 {
768   char		temp[256];		// Duplex keyword
769   ppdcAttr	*attr;			// cupsFlipDuplex attribute
770   ppdcGroup	*g;			// Current group
771   ppdcOption	*o;			// Duplex option
772 
773 
774   // Duplex {boolean|none|normal|flip}
775   if (!get_token(fp, temp, sizeof(temp)))
776   {
777     _cupsLangPrintf(stderr,
778                     _("ppdc: Expected duplex type after Duplex on line %d of "
779 		      "%s."), fp->line, fp->filename);
780     return;
781   }
782 
783   if (cond_state)
784     return;
785 
786   if (!_cups_strcasecmp(temp, "none") || !_cups_strcasecmp(temp, "false") ||
787       !_cups_strcasecmp(temp, "no") || !_cups_strcasecmp(temp, "off"))
788   {
789     g = d->find_group("General");
790     if ((o = g->find_option("Duplex")) != NULL)
791       g->options->remove(o);
792 
793     for (attr = (ppdcAttr *)d->attrs->first();
794          attr;
795 	 attr = (ppdcAttr *)d->attrs->next())
796       if (!strcmp(attr->name->value, "cupsFlipDuplex"))
797       {
798         d->attrs->remove(attr);
799 	break;
800       }
801   }
802   else if (!_cups_strcasecmp(temp, "normal") || !_cups_strcasecmp(temp, "true") ||
803 	   !_cups_strcasecmp(temp, "yes") || !_cups_strcasecmp(temp, "on") ||
804 	   !_cups_strcasecmp(temp, "flip") || !_cups_strcasecmp(temp, "rotated") ||
805 	   !_cups_strcasecmp(temp, "manualtumble"))
806   {
807     g = d->find_group("General");
808     o = g->find_option("Duplex");
809 
810     if (!o)
811     {
812       o = new ppdcOption(PPDC_PICKONE, "Duplex", "2-Sided Printing",
813                 	 !_cups_strcasecmp(temp, "flip") ? PPDC_SECTION_PAGE :
814 			                             PPDC_SECTION_ANY, 10.0f);
815       o->add_choice(new ppdcChoice("None", "Off (1-Sided)",
816                         	   "<</Duplex false>>setpagedevice"));
817       o->add_choice(new ppdcChoice("DuplexNoTumble", "Long-Edge (Portrait)",
818                                    "<</Duplex true/Tumble false>>setpagedevice"));
819       o->add_choice(new ppdcChoice("DuplexTumble", "Short-Edge (Landscape)",
820                                    "<</Duplex true/Tumble true>>setpagedevice"));
821 
822       g->add_option(o);
823     }
824 
825     for (attr = (ppdcAttr *)d->attrs->first();
826          attr;
827 	 attr = (ppdcAttr *)d->attrs->next())
828       if (!strcmp(attr->name->value, "cupsFlipDuplex"))
829       {
830         if (_cups_strcasecmp(temp, "flip"))
831           d->attrs->remove(attr);
832 	break;
833       }
834 
835     if (!_cups_strcasecmp(temp, "flip") && !attr)
836       d->add_attr(new ppdcAttr("cupsFlipDuplex", NULL, NULL, "true"));
837 
838     for (attr = (ppdcAttr *)d->attrs->first();
839          attr;
840 	 attr = (ppdcAttr *)d->attrs->next())
841       if (!strcmp(attr->name->value, "cupsBackSide"))
842       {
843         d->attrs->remove(attr);
844 	break;
845       }
846 
847     if (!_cups_strcasecmp(temp, "flip"))
848       d->add_attr(new ppdcAttr("cupsBackSide", NULL, NULL, "Flipped"));
849     else if (!_cups_strcasecmp(temp, "rotated"))
850       d->add_attr(new ppdcAttr("cupsBackSide", NULL, NULL, "Rotated"));
851     else if (!_cups_strcasecmp(temp, "manualtumble"))
852       d->add_attr(new ppdcAttr("cupsBackSide", NULL, NULL, "ManualTumble"));
853     else
854       d->add_attr(new ppdcAttr("cupsBackSide", NULL, NULL, "Normal"));
855   }
856   else
857     _cupsLangPrintf(stderr,
858                     _("ppdc: Unknown duplex type \"%s\" on line %d of %s."),
859 		    temp, fp->line, fp->filename);
860 }
861 
862 
863 //
864 // 'ppdcSource::get_filter()' - Get a filter.
865 //
866 
867 ppdcFilter *				// O - Filter
get_filter(ppdcFile * fp)868 ppdcSource::get_filter(ppdcFile *fp)	// I - File to read
869 {
870   char	type[1024],			// MIME type
871 	program[1024],			// Filter program
872 	*ptr;				// Pointer into MIME type
873   int	cost;				// Relative cost
874 
875 
876   // Read filter parameters in one of the following formats:
877   //
878   // Filter "type cost program"
879   // Filter type cost program
880 
881   if (!get_token(fp, type, sizeof(type)))
882   {
883     _cupsLangPrintf(stderr,
884                     _("ppdc: Expected a filter definition on line %d of %s."),
885 		    fp->line, fp->filename);
886     return (NULL);
887   }
888 
889   if ((ptr = strchr(type, ' ')) != NULL)
890   {
891     // Old-style filter definition in one string...
892     *ptr++ = '\0';
893     cost = strtol(ptr, &ptr, 10);
894 
895     while (isspace(*ptr))
896       ptr ++;
897 
898     strlcpy(program, ptr, sizeof(program));
899   }
900   else
901   {
902     cost = get_integer(fp);
903 
904     if (!get_token(fp, program, sizeof(program)))
905     {
906       _cupsLangPrintf(stderr,
907                       _("ppdc: Expected a program name on line %d of %s."),
908 		      fp->line, fp->filename);
909       return (NULL);
910     }
911   }
912 
913   if (!type[0])
914   {
915     _cupsLangPrintf(stderr,
916                     _("ppdc: Invalid empty MIME type for filter on line %d of "
917 		      "%s."), fp->line, fp->filename);
918     return (NULL);
919   }
920 
921   if (cost < 0 || cost > 200)
922   {
923     _cupsLangPrintf(stderr,
924                     _("ppdc: Invalid cost for filter on line %d of %s."),
925 		    fp->line, fp->filename);
926     return (NULL);
927   }
928 
929   if (!program[0])
930   {
931     _cupsLangPrintf(stderr,
932                     _("ppdc: Invalid empty program name for filter on line %d "
933 		      "of %s."), fp->line, fp->filename);
934     return (NULL);
935   }
936 
937   return (new ppdcFilter(type, program, cost));
938 }
939 
940 
941 //
942 // 'ppdcSource::get_float()' - Get a single floating-point number.
943 //
944 
945 float					// O - Number
get_float(ppdcFile * fp)946 ppdcSource::get_float(ppdcFile *fp)	// I - File to read
947 {
948   char	temp[256],			// String buffer
949 	*ptr;				// Pointer into buffer
950   float	val;				// Floating point value
951 
952 
953   // Get the number from the file and range-check...
954   if (!get_token(fp, temp, sizeof(temp)))
955   {
956     _cupsLangPrintf(stderr, _("ppdc: Expected real number on line %d of %s."),
957 		    fp->line, fp->filename);
958     return (-1.0f);
959   }
960 
961   val = (float)strtod(temp, &ptr);
962 
963   if (*ptr)
964   {
965     _cupsLangPrintf(stderr,
966                     _("ppdc: Unknown trailing characters in real number \"%s\" "
967 		      "on line %d of %s."), temp, fp->line, fp->filename);
968     return (-1.0f);
969   }
970   else
971     return (val);
972 }
973 
974 
975 //
976 // 'ppdcSource::get_font()' - Get a font definition.
977 //
978 
979 ppdcFont *				// O - Font data
get_font(ppdcFile * fp)980 ppdcSource::get_font(ppdcFile *fp)	// I - File to read
981 {
982   char			name[256],	// Font name
983 			encoding[256],	// Font encoding
984 			version[256],	// Font version
985 			charset[256],	// Font charset
986 			temp[256];	// Font status string
987   ppdcFontStatus	status;		// Font status enumeration
988 
989 
990   // Read font parameters as follows:
991   //
992   // Font *
993   // Font name encoding version charset status
994   // %font name encoding version charset status
995   //
996   // "Name" is the PostScript font name.
997   //
998   // "Encoding" is the default encoding of the font: Standard, ISOLatin1,
999   // Special, Expert, ExpertSubset, etc.
1000   //
1001   // "Version" is the version number string.
1002   //
1003   // "Charset" specifies the characters that are included in the font:
1004   // Standard, Special, Expert, Adobe-Identity, etc.
1005   //
1006   // "Status" is the keyword ROM or Disk.
1007   if (!get_token(fp, name, sizeof(name)))
1008   {
1009     _cupsLangPrintf(stderr,
1010                     _("ppdc: Expected name after Font on line %d of %s."),
1011 		    fp->line, fp->filename);
1012     return (0);
1013   }
1014 
1015   if (!strcmp(name, "*"))
1016   {
1017     // Include all base fonts...
1018     encoding[0] = '\0';
1019     version[0]  = '\0';
1020     charset[0]  = '\0';
1021     status      = PPDC_FONT_ROM;
1022   }
1023   else
1024   {
1025     // Load a full font definition...
1026     if (!get_token(fp, encoding, sizeof(encoding)))
1027     {
1028       _cupsLangPrintf(stderr,
1029                       _("ppdc: Expected encoding after Font on line %d of "
1030 		        "%s."), fp->line, fp->filename);
1031       return (0);
1032     }
1033 
1034     if (!get_token(fp, version, sizeof(version)))
1035     {
1036       _cupsLangPrintf(stderr,
1037                       _("ppdc: Expected version after Font on line %d of "
1038 		        "%s."), fp->line, fp->filename);
1039       return (0);
1040     }
1041 
1042     if (!get_token(fp, charset, sizeof(charset)))
1043     {
1044       _cupsLangPrintf(stderr,
1045                       _("ppdc: Expected charset after Font on line %d of "
1046 		        "%s."), fp->line, fp->filename);
1047       return (0);
1048     }
1049 
1050     if (!get_token(fp, temp, sizeof(temp)))
1051     {
1052       _cupsLangPrintf(stderr,
1053                       _("ppdc: Expected status after Font on line %d of %s."),
1054 		      fp->line, fp->filename);
1055       return (0);
1056     }
1057 
1058     if (!_cups_strcasecmp(temp, "ROM"))
1059       status = PPDC_FONT_ROM;
1060     else if (!_cups_strcasecmp(temp, "Disk"))
1061       status = PPDC_FONT_DISK;
1062     else
1063     {
1064       _cupsLangPrintf(stderr,
1065                       _("ppdc: Bad status keyword %s on line %d of %s."),
1066 		      temp, fp->line, fp->filename);
1067       return (0);
1068     }
1069   }
1070 
1071 //  printf("Font %s %s %s %s %s\n", name, encoding, version, charset, temp);
1072 
1073   return (new ppdcFont(name, encoding, version, charset, status));
1074 }
1075 
1076 
1077 //
1078 // 'ppdcSource::get_generic()' - Get a generic old-style option.
1079 //
1080 
1081 ppdcChoice *				// O - Choice data
get_generic(ppdcFile * fp,const char * keyword,const char * tattr,const char * nattr)1082 ppdcSource::get_generic(ppdcFile   *fp,	// I - File to read
1083                         const char *keyword,
1084 					// I - Keyword name
1085                         const char *tattr,
1086 					// I - Text attribute
1087 			const char *nattr)
1088 					// I - Numeric attribute
1089 {
1090   char		name[1024],		// Name
1091 		*text,			// Text
1092 		command[256];		// Command string
1093   int		val;			// Numeric value
1094 
1095 
1096   // Read one of the following parameters:
1097   //
1098   // Foo name/text
1099   // Foo integer name/text
1100   if (nattr)
1101     val = get_integer(fp);
1102   else
1103     val = 0;
1104 
1105   if (!get_token(fp, name, sizeof(name)))
1106   {
1107     _cupsLangPrintf(stderr,
1108                     _("ppdc: Expected name/text after %s on line %d of %s."),
1109 		    keyword, fp->line, fp->filename);
1110     return (NULL);
1111   }
1112 
1113   if ((text = strchr(name, '/')) != NULL)
1114     *text++ = '\0';
1115   else
1116     text = name;
1117 
1118   if (nattr)
1119   {
1120     if (tattr)
1121       snprintf(command, sizeof(command),
1122                "<</%s(%s)/%s %d>>setpagedevice",
1123                tattr, name, nattr, val);
1124     else
1125       snprintf(command, sizeof(command),
1126                "<</%s %d>>setpagedevice",
1127                nattr, val);
1128   }
1129   else
1130     snprintf(command, sizeof(command),
1131              "<</%s(%s)>>setpagedevice",
1132              tattr, name);
1133 
1134   return (new ppdcChoice(name, text, command));
1135 }
1136 
1137 
1138 //
1139 // 'ppdcSource::get_group()' - Get an option group.
1140 //
1141 
1142 ppdcGroup *				// O - Group
get_group(ppdcFile * fp,ppdcDriver * d)1143 ppdcSource::get_group(ppdcFile   *fp,	// I - File to read
1144                       ppdcDriver *d)	// I - Printer driver
1145 {
1146   char		name[1024],		// UI name
1147 		*text;			// UI text
1148   ppdcGroup	*g;			// Group
1149 
1150 
1151   // Read the Group parameters:
1152   //
1153   // Group name/text
1154   if (!get_token(fp, name, sizeof(name)))
1155   {
1156     _cupsLangPrintf(stderr,
1157                     _("ppdc: Expected group name/text on line %d of %s."),
1158 		    fp->line, fp->filename);
1159     return (NULL);
1160   }
1161 
1162   if ((text = strchr(name, '/')) != NULL)
1163     *text++ = '\0';
1164   else
1165     text = name;
1166 
1167   // See if the group already exists...
1168   if ((g = d->find_group(name)) == NULL)
1169   {
1170     // Nope, add a new one...
1171     g = new ppdcGroup(name, text);
1172   }
1173 
1174   return (g);
1175 }
1176 
1177 
1178 //
1179 // 'ppdcSource::get_installable()' - Get an installable option.
1180 //
1181 
1182 ppdcOption *				// O - Option
get_installable(ppdcFile * fp)1183 ppdcSource::get_installable(ppdcFile *fp)
1184 					// I - File to read
1185 {
1186   char		name[1024],		// Name for installable option
1187 		*text;			// Text for installable option
1188   ppdcOption	*o;			// Option
1189 
1190 
1191   // Read the parameter for an installable option:
1192   //
1193   // Installable name/text
1194   if (!get_token(fp, name, sizeof(name)))
1195   {
1196     _cupsLangPrintf(stderr,
1197                     _("ppdc: Expected name/text after Installable on line %d "
1198 		      "of %s."), fp->line, fp->filename);
1199     return (NULL);
1200   }
1201 
1202   if ((text = strchr(name, '/')) != NULL)
1203     *text++ = '\0';
1204   else
1205     text = name;
1206 
1207   // Create the option...
1208   o = new ppdcOption(PPDC_BOOLEAN, name, text, PPDC_SECTION_ANY, 10.0f);
1209 
1210   // Add the false and true choices...
1211   o->add_choice(new ppdcChoice("False", "Not Installed", ""));
1212   o->add_choice(new ppdcChoice("True", "Installed", ""));
1213 
1214   return (o);
1215 }
1216 
1217 
1218 //
1219 // 'ppdcSource::get_integer()' - Get an integer value from a string.
1220 //
1221 
1222 #define PPDC_XX	-1			// Bad
1223 #define PPDC_EQ	0			// ==
1224 #define PPDC_NE	1			// !=
1225 #define PPDC_LT	2			// <
1226 #define PPDC_LE	3			// <=
1227 #define PPDC_GT	4			// >
1228 #define PPDC_GE	5			// >=
1229 
1230 int					// O - Integer value
get_integer(const char * v)1231 ppdcSource::get_integer(const char *v)	// I - Value string
1232 {
1233   long		val;			// Value
1234   long		temp,			// Temporary value
1235 		temp2;			// Second temporary value
1236   char		*newv,			// New value string pointer
1237 		ch;			// Temporary character
1238   ppdcVariable	*var;			// #define variable
1239   int		compop;			// Comparison operator
1240 
1241 
1242   // Parse the value string...
1243   if (!v)
1244     return (-1);
1245 
1246   if (isdigit(*v & 255) || *v == '-' || *v == '+')
1247   {
1248     // Return a simple integer value
1249     val = strtol(v, (char **)&v, 0);
1250     if (*v || val == LONG_MIN)
1251       return (-1);
1252     else
1253       return ((int)val);
1254   }
1255   else if (*v == '(')
1256   {
1257     // Evaluate and expression in any of the following formats:
1258     //
1259     // (number number ... number)   Bitwise OR of all numbers
1260     // (NAME == value)              1 if equal, 0 otherwise
1261     // (NAME != value)              1 if not equal, 0 otherwise
1262     // (NAME < value)               1 if less than, 0 otherwise
1263     // (NAME <= value)              1 if less than or equal, 0 otherwise
1264     // (NAME > value)               1 if greater than, 0 otherwise
1265     // (NAME >= value)              1 if greater than or equal, 0 otherwise
1266 
1267     v ++;
1268     val = 0;
1269 
1270     while (*v && *v != ')')
1271     {
1272       // Skip leading whitespace...
1273       while (*v && isspace(*v & 255))
1274         v ++;
1275 
1276       if (!*v || *v == ')')
1277         break;
1278 
1279       if (isdigit(*v & 255) || *v == '-' || *v == '+')
1280       {
1281         // Bitwise OR a number...
1282 	temp = strtol(v, &newv, 0);
1283 
1284 	if (!*newv || newv == v || !(isspace(*newv) || *newv == ')') ||
1285 	    temp == LONG_MIN)
1286 	  return (-1);
1287       }
1288       else
1289       {
1290         // NAME logicop value
1291 	for (newv = (char *)v + 1;
1292 	     *newv && (isalnum(*newv & 255) || *newv == '_');
1293 	     newv ++)
1294 	  /* do nothing */;
1295 
1296         ch    = *newv;
1297 	*newv = '\0';
1298 
1299         if ((var = find_variable(v)) != NULL)
1300 	{
1301 	  if (!var->value || !var->value->value || !var->value->value[0])
1302 	    temp = 0;
1303 	  else if (isdigit(var->value->value[0] & 255) ||
1304 	           var->value->value[0] == '-' ||
1305 	           var->value->value[0] == '+')
1306             temp = strtol(var->value->value, NULL, 0);
1307 	  else
1308 	    temp = 1;
1309 	}
1310 	else
1311 	  temp = 0;
1312 
1313         *newv = ch;
1314 	while (isspace(*newv & 255))
1315 	  newv ++;
1316 
1317         if (!strncmp(newv, "==", 2))
1318 	{
1319 	  compop = PPDC_EQ;
1320 	  newv += 2;
1321 	}
1322         else if (!strncmp(newv, "!=", 2))
1323         {
1324 	  compop = PPDC_NE;
1325 	  newv += 2;
1326 	}
1327         else if (!strncmp(newv, "<=", 2))
1328         {
1329 	  compop = PPDC_LE;
1330 	  newv += 2;
1331 	}
1332 	else if (*newv == '<')
1333         {
1334 	  compop = PPDC_LT;
1335 	  newv ++;
1336 	}
1337         else if (!strncmp(newv, ">=", 2))
1338         {
1339 	  compop = PPDC_GE;
1340 	  newv += 2;
1341 	}
1342 	else if (*newv == '>')
1343 	{
1344 	  compop = PPDC_GT;
1345 	  newv ++;
1346 	}
1347 	else
1348 	  compop = PPDC_XX;
1349 
1350         if (compop != PPDC_XX)
1351 	{
1352 	  while (isspace(*newv & 255))
1353 	    newv ++;
1354 
1355           if (*newv == ')' || !*newv)
1356 	    return (-1);
1357 
1358 	  if (isdigit(*newv & 255) || *newv == '-' || *newv == '+')
1359 	  {
1360 	    // Get the second number...
1361 	    temp2 = strtol(newv, &newv, 0);
1362 	    if (!*newv || newv == v || !(isspace(*newv) || *newv == ')') ||
1363 		temp == LONG_MIN)
1364 	      return (-1);
1365           }
1366 	  else
1367 	  {
1368 	    // Lookup the second name...
1369 	    for (v = newv, newv ++;
1370 		 *newv && (isalnum(*newv & 255) || *newv == '_');
1371 		 newv ++);
1372 
1373 	    ch    = *newv;
1374 	    *newv = '\0';
1375 
1376 	    if ((var = find_variable(v)) != NULL)
1377 	    {
1378 	      if (!var->value || !var->value->value || !var->value->value[0])
1379 		temp2 = 0;
1380 	      else if (isdigit(var->value->value[0] & 255) ||
1381 		       var->value->value[0] == '-' ||
1382 		       var->value->value[0] == '+')
1383 		temp2 = strtol(var->value->value, NULL, 0);
1384 	      else
1385 		temp2 = 1;
1386 	    }
1387 	    else
1388 	      temp2 = 0;
1389 
1390 	    *newv = ch;
1391           }
1392 
1393 	  // Do the comparison...
1394 	  switch (compop)
1395 	  {
1396 	    case PPDC_EQ :
1397 	        temp = temp == temp2;
1398 		break;
1399 	    case PPDC_NE :
1400 	        temp = temp != temp2;
1401 		break;
1402 	    case PPDC_LT :
1403 	        temp = temp < temp2;
1404 		break;
1405 	    case PPDC_LE :
1406 	        temp = temp <= temp2;
1407 		break;
1408 	    case PPDC_GT :
1409 	        temp = temp > temp2;
1410 		break;
1411 	    case PPDC_GE :
1412 	        temp = temp >= temp2;
1413 		break;
1414 	  }
1415 	}
1416       }
1417 
1418       val |= temp;
1419       v   = newv;
1420     }
1421 
1422     if (*v == ')' && !v[1])
1423       return ((int)val);
1424     else
1425       return (-1);
1426   }
1427   else if ((var = find_variable(v)) != NULL)
1428   {
1429     // NAME by itself returns 1 if the #define variable is not blank and
1430     // not "0"...
1431     return (var->value->value && var->value->value[0] &&
1432             strcmp(var->value->value, "0"));
1433   }
1434   else
1435   {
1436     // Anything else is an error...
1437     return (-1);
1438   }
1439 }
1440 
1441 
1442 //
1443 // 'ppdcSource::get_integer()' - Get an integer value from a file.
1444 //
1445 
1446 int					// O - Integer value
get_integer(ppdcFile * fp)1447 ppdcSource::get_integer(ppdcFile *fp)	// I - File to read
1448 {
1449   char	temp[1024];			// String buffer
1450 
1451 
1452   if (!get_token(fp, temp, sizeof(temp)))
1453   {
1454     _cupsLangPrintf(stderr, _("ppdc: Expected integer on line %d of %s."),
1455 		    fp->line, fp->filename);
1456     return (-1);
1457   }
1458   else
1459     return (get_integer(temp));
1460 }
1461 
1462 
1463 //
1464 // 'ppdcSource::get_measurement()' - Get a measurement value.
1465 //
1466 
1467 float					// O - Measurement value in points
get_measurement(ppdcFile * fp)1468 ppdcSource::get_measurement(ppdcFile *fp)
1469 					// I - File to read
1470 {
1471   char	buffer[256],			// Number buffer
1472 	*ptr;				// Pointer into buffer
1473   float	val;				// Measurement value
1474 
1475 
1476   // Grab a token from the file...
1477   if (!get_token(fp, buffer, sizeof(buffer)))
1478     return (-1.0f);
1479 
1480   // Get the floating point value of "s" and skip all digits and decimal points.
1481   val = (float)strtod(buffer, &ptr);
1482 
1483   // Check for a trailing unit specifier...
1484   if (!_cups_strcasecmp(ptr, "mm"))
1485     val *= 72.0f / 25.4f;
1486   else if (!_cups_strcasecmp(ptr, "cm"))
1487     val *= 72.0f / 2.54f;
1488   else if (!_cups_strcasecmp(ptr, "m"))
1489     val *= 72.0f / 0.0254f;
1490   else if (!_cups_strcasecmp(ptr, "in"))
1491     val *= 72.0f;
1492   else if (!_cups_strcasecmp(ptr, "ft"))
1493     val *= 72.0f * 12.0f;
1494   else if (_cups_strcasecmp(ptr, "pt") && *ptr)
1495     return (-1.0f);
1496 
1497   return (val);
1498 }
1499 
1500 
1501 //
1502 // 'ppdcSource::get_option()' - Get an option definition.
1503 //
1504 
1505 ppdcOption *				// O - Option
get_option(ppdcFile * fp,ppdcDriver * d,ppdcGroup * g)1506 ppdcSource::get_option(ppdcFile   *fp,	// I - File to read
1507                        ppdcDriver *d,	// I - Printer driver
1508 		       ppdcGroup  *g)	// I - Current group
1509 {
1510   char		name[1024],		// UI name
1511 		*text,			// UI text
1512 		type[256];		// UI type string
1513   ppdcOptType	ot;			// Option type value
1514   ppdcOptSection section;		// Option section
1515   float		order;			// Option order
1516   ppdcOption	*o;			// Option
1517   ppdcGroup	*mg;			// Matching group, if any
1518 
1519 
1520   // Read the Option parameters:
1521   //
1522   // Option name/text type section order
1523   if (!get_token(fp, name, sizeof(name)))
1524   {
1525     _cupsLangPrintf(stderr,
1526                     _("ppdc: Expected option name/text on line %d of %s."),
1527 		    fp->line, fp->filename);
1528     return (NULL);
1529   }
1530 
1531   if ((text = strchr(name, '/')) != NULL)
1532     *text++ = '\0';
1533   else
1534     text = name;
1535 
1536   if (!get_token(fp, type, sizeof(type)))
1537   {
1538     _cupsLangPrintf(stderr, _("ppdc: Expected option type on line %d of %s."),
1539 		    fp->line, fp->filename);
1540     return (NULL);
1541   }
1542 
1543   if (!_cups_strcasecmp(type, "boolean"))
1544     ot = PPDC_BOOLEAN;
1545   else if (!_cups_strcasecmp(type, "pickone"))
1546     ot = PPDC_PICKONE;
1547   else if (!_cups_strcasecmp(type, "pickmany"))
1548     ot = PPDC_PICKMANY;
1549   else
1550   {
1551     _cupsLangPrintf(stderr,
1552                     _("ppdc: Invalid option type \"%s\" on line %d of %s."),
1553 		    type, fp->line, fp->filename);
1554     return (NULL);
1555   }
1556 
1557   if (!get_token(fp, type, sizeof(type)))
1558   {
1559     _cupsLangPrintf(stderr,
1560                     _("ppdc: Expected option section on line %d of %s."),
1561 		    fp->line, fp->filename);
1562     return (NULL);
1563   }
1564 
1565   if (!_cups_strcasecmp(type, "AnySetup"))
1566     section = PPDC_SECTION_ANY;
1567   else if (!_cups_strcasecmp(type, "DocumentSetup"))
1568     section = PPDC_SECTION_DOCUMENT;
1569   else if (!_cups_strcasecmp(type, "ExitServer"))
1570     section = PPDC_SECTION_EXIT;
1571   else if (!_cups_strcasecmp(type, "JCLSetup"))
1572     section = PPDC_SECTION_JCL;
1573   else if (!_cups_strcasecmp(type, "PageSetup"))
1574     section = PPDC_SECTION_PAGE;
1575   else if (!_cups_strcasecmp(type, "Prolog"))
1576     section = PPDC_SECTION_PROLOG;
1577   else
1578   {
1579     _cupsLangPrintf(stderr,
1580                     _("ppdc: Invalid option section \"%s\" on line %d of "
1581 		      "%s."), type, fp->line, fp->filename);
1582     return (NULL);
1583   }
1584 
1585   order = get_float(fp);
1586 
1587   // See if the option already exists...
1588   if ((o = d->find_option_group(name, &mg)) == NULL)
1589   {
1590     // Nope, add a new one...
1591     o = new ppdcOption(ot, name, text, section, order);
1592   }
1593   else if (o->type != ot)
1594   {
1595     _cupsLangPrintf(stderr,
1596                     _("ppdc: Option %s redefined with a different type on line "
1597 		      "%d of %s."), name, fp->line, fp->filename);
1598     return (NULL);
1599   }
1600   else if (g != mg)
1601   {
1602     _cupsLangPrintf(stderr,
1603                     _("ppdc: Option %s defined in two different groups on line "
1604 		      "%d of %s."), name, fp->line, fp->filename);
1605     return (NULL);
1606   }
1607 
1608   return (o);
1609 }
1610 
1611 
1612 //
1613 // 'ppdcSource::get_po()' - Get a message catalog.
1614 //
1615 
1616 ppdcCatalog *				// O - Message catalog
get_po(ppdcFile * fp)1617 ppdcSource::get_po(ppdcFile *fp)	// I - File to read
1618 {
1619   char		locale[32],		// Locale name
1620 		poname[1024],		// Message catalog filename
1621 		basedir[1024],		// Base directory
1622 		*baseptr,		// Pointer into directory
1623 		pofilename[1024];	// Full filename of message catalog
1624   ppdcCatalog	*cat;			// Message catalog
1625 
1626 
1627   // Read the #po parameters:
1628   //
1629   // #po locale "filename.po"
1630   if (!get_token(fp, locale, sizeof(locale)))
1631   {
1632     _cupsLangPrintf(stderr,
1633                     _("ppdc: Expected locale after #po on line %d of %s."),
1634 		    fp->line, fp->filename);
1635     return (NULL);
1636   }
1637 
1638   if (!get_token(fp, poname, sizeof(poname)))
1639   {
1640     _cupsLangPrintf(stderr,
1641                     _("ppdc: Expected filename after #po %s on line %d of "
1642 		      "%s."), locale, fp->line, fp->filename);
1643     return (NULL);
1644   }
1645 
1646   // See if the locale is already loaded...
1647   if (find_po(locale))
1648   {
1649     _cupsLangPrintf(stderr,
1650                     _("ppdc: Duplicate #po for locale %s on line %d of %s."),
1651 		    locale, fp->line, fp->filename);
1652     return (NULL);
1653   }
1654 
1655   // Figure out the current directory...
1656   strlcpy(basedir, fp->filename, sizeof(basedir));
1657 
1658   if ((baseptr = strrchr(basedir, '/')) != NULL)
1659     *baseptr = '\0';
1660   else
1661     strlcpy(basedir, ".", sizeof(basedir));
1662 
1663   // Find the po file...
1664   pofilename[0] = '\0';
1665 
1666   if (!poname[0] ||
1667       find_include(poname, basedir, pofilename, sizeof(pofilename)))
1668   {
1669     // Found it, so load it...
1670     cat = new ppdcCatalog(locale, pofilename);
1671 
1672     // Reset the filename to the name supplied by the user...
1673     cat->filename->release();
1674     cat->filename = new ppdcString(poname);
1675 
1676     // Return the catalog...
1677     return (cat);
1678   }
1679   else
1680   {
1681     _cupsLangPrintf(stderr,
1682                     _("ppdc: Unable to find #po file %s on line %d of %s."),
1683 		    poname, fp->line, fp->filename);
1684     return (NULL);
1685   }
1686 }
1687 
1688 
1689 //
1690 // 'ppdcSource::get_resolution()' - Get an old-style resolution option.
1691 //
1692 
1693 ppdcChoice *				// O - Choice data
get_resolution(ppdcFile * fp)1694 ppdcSource::get_resolution(ppdcFile *fp)// I - File to read
1695 {
1696   char		name[1024],		// Name
1697 		*text,			// Text
1698 		temp[256],		// Temporary string
1699 		command[256],		// Command string
1700 		*commptr;		// Pointer into command
1701   int		xdpi, ydpi,		// X + Y resolution
1702 		color_order,		// Color order
1703 		color_space,		// Colorspace
1704 		compression,		// Compression mode
1705 		depth,			// Bits per color
1706 		row_count,		// Row count
1707 		row_feed,		// Row feed
1708 		row_step;		// Row step/interval
1709 
1710 
1711   // Read the resolution parameters:
1712   //
1713   // Resolution colorspace bits row-count row-feed row-step name/text
1714   if (!get_token(fp, temp, sizeof(temp)))
1715   {
1716     _cupsLangPrintf(stderr,
1717                     _("ppdc: Expected override field after Resolution on line "
1718 		      "%d of %s."), fp->line, fp->filename);
1719     return (NULL);
1720   }
1721 
1722   color_order = get_color_order(temp);
1723   color_space = get_color_space(temp);
1724   compression = get_integer(temp);
1725 
1726   depth       = get_integer(fp);
1727   row_count   = get_integer(fp);
1728   row_feed    = get_integer(fp);
1729   row_step    = get_integer(fp);
1730 
1731   if (!get_token(fp, name, sizeof(name)))
1732   {
1733     _cupsLangPrintf(stderr,
1734 		    _("ppdc: Expected name/text after Resolution on line %d of "
1735 		      "%s."), fp->line, fp->filename);
1736     return (NULL);
1737   }
1738 
1739   if ((text = strchr(name, '/')) != NULL)
1740     *text++ = '\0';
1741   else
1742     text = name;
1743 
1744   switch (sscanf(name, "%dx%d", &xdpi, &ydpi))
1745   {
1746     case 1 :
1747         ydpi = xdpi;
1748         break;
1749     case 2 :
1750         break;
1751     default :
1752         _cupsLangPrintf(stderr,
1753                   _("ppdc: Bad resolution name \"%s\" on line %d of "
1754         "%s."), name, fp->line, fp->filename);
1755         return (NULL);
1756 }
1757 
1758   // Create the necessary PS commands...
1759   snprintf(command, sizeof(command),
1760            "<</HWResolution[%d %d]/cupsBitsPerColor %d/cupsRowCount %d"
1761            "/cupsRowFeed %d/cupsRowStep %d",
1762 	   xdpi, ydpi, depth, row_count, row_feed, row_step);
1763   commptr = command + strlen(command);
1764 
1765   if (color_order >= 0)
1766   {
1767     snprintf(commptr, sizeof(command) - (size_t)(commptr - command),
1768              "/cupsColorOrder %d", color_order);
1769     commptr += strlen(commptr);
1770   }
1771 
1772   if (color_space >= 0)
1773   {
1774     snprintf(commptr, sizeof(command) - (size_t)(commptr - command),
1775              "/cupsColorSpace %d", color_space);
1776     commptr += strlen(commptr);
1777   }
1778 
1779   if (compression >= 0)
1780   {
1781     snprintf(commptr, sizeof(command) - (size_t)(commptr - command),
1782              "/cupsCompression %d", compression);
1783     commptr += strlen(commptr);
1784   }
1785 
1786   snprintf(commptr, sizeof(command) - (size_t)(commptr - command), ">>setpagedevice");
1787 
1788   // Return the new choice...
1789   return (new ppdcChoice(name, text, command));
1790 }
1791 
1792 
1793 //
1794 // 'ppdcSource::get_simple_profile()' - Get a simple color profile definition.
1795 //
1796 
1797 ppdcProfile *				// O - Color profile
get_simple_profile(ppdcFile * fp)1798 ppdcSource::get_simple_profile(ppdcFile *fp)
1799 					// I - File to read
1800 {
1801   char		resolution[1024],	// Resolution/media type
1802 		*media_type;		// Media type
1803   float		m[9];			// Transform matrix
1804   float		kd, rd, g;		// Densities and gamma
1805   float		red, green, blue;	// RGB adjustments
1806   float		yellow;			// Yellow density
1807   float		color;			// Color density values
1808 
1809 
1810   // Get the SimpleColorProfile parameters:
1811   //
1812   // SimpleColorProfile resolution/mediatype black-density yellow-density
1813   //     red-density gamma red-adjust green-adjust blue-adjust
1814   if (!get_token(fp, resolution, sizeof(resolution)))
1815   {
1816     _cupsLangPrintf(stderr,
1817                     _("ppdc: Expected resolution/mediatype following "
1818 		      "SimpleColorProfile on line %d of %s."),
1819 		    fp->line, fp->filename);
1820     return (NULL);
1821   }
1822 
1823   if ((media_type = strchr(resolution, '/')) != NULL)
1824     *media_type++ = '\0';
1825   else
1826     media_type = resolution;
1827 
1828   // Collect the profile parameters...
1829   kd     = get_float(fp);
1830   yellow = get_float(fp);
1831   rd     = get_float(fp);
1832   g      = get_float(fp);
1833   red    = get_float(fp);
1834   green  = get_float(fp);
1835   blue   = get_float(fp);
1836 
1837   // Build the color profile...
1838   color = 0.5f * rd / kd - kd;
1839   m[0]  = 1.0f;				// C
1840   m[1]  = color + blue;			// C + M (blue)
1841   m[2]  = color - green;		// C + Y (green)
1842   m[3]  = color - blue;			// M + C (blue)
1843   m[4]  = 1.0f;				// M
1844   m[5]  = color + red;			// M + Y (red)
1845   m[6]  = yellow * (color + green);	// Y + C (green)
1846   m[7]  = yellow * (color - red);	// Y + M (red)
1847   m[8]  = yellow;			// Y
1848 
1849   if (m[1] > 0.0f)
1850   {
1851     m[3] -= m[1];
1852     m[1] = 0.0f;
1853   }
1854   else if (m[3] > 0.0f)
1855   {
1856     m[1] -= m[3];
1857     m[3] = 0.0f;
1858   }
1859 
1860   if (m[2] > 0.0f)
1861   {
1862     m[6] -= m[2];
1863     m[2] = 0.0f;
1864   }
1865   else if (m[6] > 0.0f)
1866   {
1867     m[2] -= m[6];
1868     m[6] = 0.0f;
1869   }
1870 
1871   if (m[5] > 0.0f)
1872   {
1873     m[7] -= m[5];
1874     m[5] = 0.0f;
1875   }
1876   else if (m[7] > 0.0f)
1877   {
1878     m[5] -= m[7];
1879     m[7] = 0.0f;
1880   }
1881 
1882   // Return the new profile...
1883   return (new ppdcProfile(resolution, media_type, g, kd, m));
1884 }
1885 
1886 
1887 //
1888 // 'ppdcSource::get_size()' - Get a media size definition from a file.
1889 //
1890 
1891 ppdcMediaSize *				// O - Media size
get_size(ppdcFile * fp)1892 ppdcSource::get_size(ppdcFile *fp)	// I - File to read
1893 {
1894   char		name[1024],		// Name
1895 		*text;			// Text
1896   float		width,			// Width
1897 		length;			// Length
1898 
1899 
1900   // Get the name, text, width, and length:
1901   //
1902   // #media name/text width length
1903   if (!get_token(fp, name, sizeof(name)))
1904     return (NULL);
1905 
1906   if ((text = strchr(name, '/')) != NULL)
1907     *text++ = '\0';
1908   else
1909     text = name;
1910 
1911   if ((width = get_measurement(fp)) < 0.0f)
1912     return (NULL);
1913 
1914   if ((length = get_measurement(fp)) < 0.0f)
1915     return (NULL);
1916 
1917   // Return the new media size...
1918   return (new ppdcMediaSize(name, text, width, length, 0.0f, 0.0f, 0.0f, 0.0f));
1919 }
1920 
1921 
1922 //
1923 // 'ppdcSource::get_token()' - Get a token from a file.
1924 //
1925 
1926 char *					// O - Token string or NULL
get_token(ppdcFile * fp,char * buffer,int buflen)1927 ppdcSource::get_token(ppdcFile *fp,	// I - File to read
1928                       char     *buffer,	// I - Buffer
1929 		      int      buflen)	// I - Length of buffer
1930 {
1931   char		*bufptr,		// Pointer into string buffer
1932 		*bufend;		// End of string buffer
1933   int		ch,			// Character from file
1934 		nextch,			// Next char in file
1935 		quote,			// Quote character used...
1936 		empty,			// Empty input?
1937 		startline;		// Start line for quote
1938   char		name[256],		// Name string
1939 		*nameptr;		// Name pointer
1940   ppdcVariable	*var;			// Variable pointer
1941 
1942 
1943   // Mark the beginning and end of the buffer...
1944   bufptr = buffer;
1945   bufend = buffer + buflen - 1;
1946 
1947   // Loop intil we've read a token...
1948   quote     = 0;
1949   startline = 0;
1950   empty     = 1;
1951 
1952   while ((ch = fp->get()) != EOF)
1953   {
1954     if (isspace(ch) && !quote)
1955     {
1956       if (empty)
1957         continue;
1958       else
1959         break;
1960     }
1961     else if (ch == '$')
1962     {
1963       // Variable substitution
1964       empty = 0;
1965 
1966       for (nameptr = name; (ch = fp->peek()) != EOF;)
1967       {
1968         if (!isalnum(ch) && ch != '_')
1969 	  break;
1970 	else if (nameptr < (name + sizeof(name) - 1))
1971 	  *nameptr++ = (char)fp->get();
1972       }
1973 
1974       if (nameptr == name)
1975       {
1976         // Just substitute this character...
1977 	if (ch == '$')
1978 	{
1979 	  // $$ = $
1980 	  if (bufptr < bufend)
1981 	    *bufptr++ = (char)fp->get();
1982 	}
1983 	else
1984 	{
1985 	  // $ch = $ch
1986           _cupsLangPrintf(stderr,
1987 	                  _("ppdc: Bad variable substitution ($%c) on line %d "
1988 			    "of %s."), ch, fp->line, fp->filename);
1989 
1990 	  if (bufptr < bufend)
1991 	    *bufptr++ = '$';
1992 	}
1993       }
1994       else
1995       {
1996         // Substitute the variable value...
1997 	*nameptr = '\0';
1998 	var = find_variable(name);
1999 	if (var)
2000 	{
2001 	  strlcpy(bufptr, var->value->value, (size_t)(bufend - bufptr + 1));
2002 	  bufptr += strlen(bufptr);
2003 	}
2004 	else
2005 	{
2006 	  if (!(cond_state & PPDC_COND_SKIP))
2007 	    _cupsLangPrintf(stderr,
2008 			    _("ppdc: Undefined variable (%s) on line %d of "
2009 			      "%s."), name, fp->line, fp->filename);
2010 
2011 	  snprintf(bufptr, (size_t)(bufend - bufptr + 1), "$%s", name);
2012 	  bufptr += strlen(bufptr);
2013 	}
2014       }
2015     }
2016     else if (ch == '/' && !quote)
2017     {
2018       // Possibly a comment...
2019       nextch = fp->peek();
2020 
2021       if (nextch == '*')
2022       {
2023         // C comment...
2024 	fp->get();
2025 	ch = fp->get();
2026 	while ((nextch = fp->get()) != EOF)
2027 	{
2028 	  if (ch == '*' && nextch == '/')
2029 	    break;
2030 
2031 	  ch = nextch;
2032 	}
2033 
2034         if (nextch == EOF)
2035           break;
2036       }
2037       else if (nextch == '/')
2038       {
2039         // C++ comment...
2040         while ((nextch = fp->get()) != EOF)
2041           if (nextch == '\n')
2042 	    break;
2043 
2044         if (nextch == EOF)
2045           break;
2046       }
2047       else
2048       {
2049         // Not a comment...
2050         empty = 0;
2051 
2052 	if (bufptr < bufend)
2053 	  *bufptr++ = (char)ch;
2054       }
2055     }
2056     else if (ch == '\'' || ch == '\"')
2057     {
2058       empty = 0;
2059 
2060       if (quote == ch)
2061       {
2062         // Ending the current quoted string...
2063         quote = 0;
2064       }
2065       else if (quote)
2066       {
2067         // Insert the opposing quote char...
2068 	if (bufptr < bufend)
2069           *bufptr++ = (char)ch;
2070       }
2071       else
2072       {
2073         // Start a new quoted string...
2074         startline = fp->line;
2075         quote     = ch;
2076       }
2077     }
2078     else if ((ch == '(' || ch == '<') && !quote)
2079     {
2080       empty     = 0;
2081       quote     = ch;
2082       startline = fp->line;
2083 
2084       if (bufptr < bufend)
2085 	*bufptr++ = (char)ch;
2086     }
2087     else if ((ch == ')' && quote == '(') || (ch == '>' && quote == '<'))
2088     {
2089       quote = 0;
2090 
2091       if (bufptr < bufend)
2092 	*bufptr++ = (char)ch;
2093     }
2094     else if (ch == '\\')
2095     {
2096       empty = 0;
2097 
2098       if ((ch = fp->get()) == EOF)
2099         break;
2100 
2101       if (bufptr < bufend)
2102         *bufptr++ = (char)ch;
2103     }
2104     else if (bufptr < bufend)
2105     {
2106       empty = 0;
2107 
2108       *bufptr++ = (char)ch;
2109 
2110       if ((ch == '{' || ch == '}') && !quote)
2111         break;
2112     }
2113   }
2114 
2115   if (quote)
2116   {
2117     _cupsLangPrintf(stderr,
2118                     _("ppdc: Unterminated string starting with %c on line %d "
2119 		      "of %s."), quote, startline, fp->filename);
2120     return (NULL);
2121   }
2122 
2123   if (empty)
2124     return (NULL);
2125   else
2126   {
2127     *bufptr = '\0';
2128     return (buffer);
2129   }
2130 }
2131 
2132 
2133 //
2134 // 'ppdcSource::get_variable()' - Get a variable definition.
2135 //
2136 
2137 ppdcVariable *				// O - Variable
get_variable(ppdcFile * fp)2138 ppdcSource::get_variable(ppdcFile *fp)	// I - File to read
2139 {
2140   char		name[1024],		// Name
2141 		value[1024];		// Value
2142 
2143 
2144   // Get the name and value:
2145   //
2146   // #define name value
2147   if (!get_token(fp, name, sizeof(name)))
2148     return (NULL);
2149 
2150   if (!get_token(fp, value, sizeof(value)))
2151     return (NULL);
2152 
2153   // Set the variable...
2154   return (set_variable(name, value));
2155 }
2156 
2157 
2158 //
2159 // 'ppdcSource::quotef()' - Write a formatted, quoted string...
2160 //
2161 
2162 int					// O - Number bytes on success, -1 on failure
quotef(cups_file_t * fp,const char * format,...)2163 ppdcSource::quotef(cups_file_t *fp,	// I - File to write to
2164                    const char  *format,	// I - Printf-style format string
2165 		   ...)			// I - Additional args as needed
2166 {
2167   va_list	ap;			// Pointer to additional arguments
2168   int		bytes;			// Bytes written
2169   char		sign,			// Sign of format width
2170 		size,			// Size character (h, l, L)
2171 		type;			// Format type character
2172   const char	*bufformat;		// Start of format
2173   int		width,			// Width of field
2174 		prec;			// Number of characters of precision
2175   char		tformat[100];		// Temporary format string for fprintf()
2176   char		*s;			// Pointer to string
2177   int		slen;			// Length of string
2178   int		i;			// Looping var
2179 
2180 
2181   // Range check input...
2182   if (!fp || !format)
2183     return (-1);
2184 
2185   // Loop through the format string, formatting as needed...
2186   va_start(ap, format);
2187 
2188   bytes = 0;
2189 
2190   while (*format)
2191   {
2192     if (*format == '%')
2193     {
2194       bufformat = format;
2195       format ++;
2196 
2197       if (*format == '%')
2198       {
2199         cupsFilePutChar(fp, *format++);
2200 	bytes ++;
2201 	continue;
2202       }
2203       else if (strchr(" -+#\'", *format))
2204         sign = *format++;
2205       else
2206         sign = 0;
2207 
2208       width = 0;
2209       while (isdigit(*format))
2210         width = width * 10 + *format++ - '0';
2211 
2212       if (*format == '.')
2213       {
2214         format ++;
2215 	prec = 0;
2216 
2217 	while (isdigit(*format))
2218           prec = prec * 10 + *format++ - '0';
2219       }
2220       else
2221         prec = -1;
2222 
2223       if (*format == 'l' && format[1] == 'l')
2224       {
2225         size = 'L';
2226 	format += 2;
2227       }
2228       else if (*format == 'h' || *format == 'l' || *format == 'L')
2229         size = *format++;
2230       else
2231         size = '\0';
2232 
2233       if (!*format)
2234         break;
2235 
2236       type = *format++;
2237 
2238       switch (type)
2239       {
2240 	case 'E' : // Floating point formats
2241 	case 'G' :
2242 	case 'e' :
2243 	case 'f' :
2244 	case 'g' :
2245 	    if ((format - bufformat + 1) > (int)sizeof(tformat))
2246 	      break;
2247 
2248 	    memcpy(tformat, bufformat, (size_t)(format - bufformat));
2249 	    tformat[format - bufformat] = '\0';
2250 
2251 	    bytes += cupsFilePrintf(fp, tformat, va_arg(ap, double));
2252 	    break;
2253 
2254         case 'B' : // Integer formats
2255 	case 'X' :
2256 	case 'b' :
2257         case 'd' :
2258 	case 'i' :
2259 	case 'o' :
2260 	case 'u' :
2261 	case 'x' :
2262 	    if ((format - bufformat + 1) > (int)sizeof(tformat))
2263 	      break;
2264 
2265 	    memcpy(tformat, bufformat, (size_t)(format - bufformat));
2266 	    tformat[format - bufformat] = '\0';
2267 
2268 #  ifdef HAVE_LONG_LONG
2269             if (size == 'L')
2270 	      bytes += cupsFilePrintf(fp, tformat, va_arg(ap, long long));
2271 	    else
2272 #  endif /* HAVE_LONG_LONG */
2273             if (size == 'l')
2274 	      bytes += cupsFilePrintf(fp, tformat, va_arg(ap, long));
2275 	    else
2276 	      bytes += cupsFilePrintf(fp, tformat, va_arg(ap, int));
2277 	    break;
2278 
2279 	case 'p' : // Pointer value
2280 	    if ((format - bufformat + 1) > (int)sizeof(tformat))
2281 	      break;
2282 
2283 	    memcpy(tformat, bufformat, (size_t)(format - bufformat));
2284 	    tformat[format - bufformat] = '\0';
2285 
2286 	    bytes += cupsFilePrintf(fp, tformat, va_arg(ap, void *));
2287 	    break;
2288 
2289         case 'c' : // Character or character array
2290 	    if (width <= 1)
2291 	    {
2292 	      bytes ++;
2293 	      cupsFilePutChar(fp, va_arg(ap, int));
2294 	    }
2295 	    else
2296 	    {
2297 	      cupsFileWrite(fp, va_arg(ap, char *), (size_t)width);
2298 	      bytes += width;
2299 	    }
2300 	    break;
2301 
2302 	case 's' : // String
2303 	    if ((s = va_arg(ap, char *)) == NULL)
2304 	      s = (char *)"(nil)";
2305 
2306 	    slen = (int)strlen(s);
2307 	    if (slen > width && prec != width)
2308 	      width = slen;
2309 
2310             if (slen > width)
2311 	      slen = width;
2312 
2313             if (sign != '-')
2314 	    {
2315 	      for (i = width - slen; i > 0; i --, bytes ++)
2316 	        cupsFilePutChar(fp, ' ');
2317 	    }
2318 
2319             for (i = slen; i > 0; i --, s ++, bytes ++)
2320 	    {
2321 	      if (*s == '\\' || *s == '\"')
2322 	      {
2323 	        cupsFilePutChar(fp, '\\');
2324 		bytes ++;
2325 	      }
2326 
2327 	      cupsFilePutChar(fp, *s);
2328 	    }
2329 
2330             if (sign == '-')
2331 	    {
2332 	      for (i = width - slen; i > 0; i --, bytes ++)
2333 	        cupsFilePutChar(fp, ' ');
2334 	    }
2335 	    break;
2336       }
2337     }
2338     else
2339     {
2340       cupsFilePutChar(fp, *format++);
2341       bytes ++;
2342     }
2343   }
2344 
2345   va_end(ap);
2346 
2347   // Return the number of characters written.
2348   return (bytes);
2349 }
2350 
2351 
2352 //
2353 // 'ppdcSource::read_file()' - Read a driver source file.
2354 //
2355 
2356 void
read_file(const char * f,cups_file_t * ffp)2357 ppdcSource::read_file(const char  *f,	// I - File to read
2358                       cups_file_t *ffp)	// I - File pointer to use
2359 {
2360   ppdcFile *fp = new ppdcFile(f, ffp);
2361   scan_file(fp);
2362   delete fp;
2363 
2364   if (cond_current != cond_stack)
2365     _cupsLangPrintf(stderr, _("ppdc: Missing #endif at end of \"%s\"."), f);
2366 }
2367 
2368 
2369 //
2370 // 'ppdcSource::scan_file()' - Scan a driver source file.
2371 //
2372 
2373 void
scan_file(ppdcFile * fp,ppdcDriver * td,bool inc)2374 ppdcSource::scan_file(ppdcFile   *fp,	// I - File to read
2375                       ppdcDriver *td,	// I - Driver template
2376 		      bool       inc)	// I - Including?
2377 {
2378   ppdcDriver	*d;			// Current driver
2379   ppdcGroup	*g,			// Current group
2380 		*mg,			// Matching group
2381 		*general,		// General options group
2382 		*install;		// Installable options group
2383   ppdcOption	*o;			// Current option
2384   ppdcChoice	*c;			// Current choice
2385   char		temp[256],		// Token from file...
2386 		*ptr;			// Pointer into token
2387   int		isdefault;		// Default option?
2388 
2389 
2390   // Initialize things as needed...
2391   if (inc && td)
2392   {
2393     d = td;
2394     d->retain();
2395   }
2396   else
2397     d = new ppdcDriver(td);
2398 
2399   if ((general = d->find_group("General")) == NULL)
2400   {
2401     general = new ppdcGroup("General", NULL);
2402     d->add_group(general);
2403   }
2404 
2405   if ((install = d->find_group("InstallableOptions")) == NULL)
2406   {
2407     install = new ppdcGroup("InstallableOptions", "Installable Options");
2408     d->add_group(install);
2409   }
2410 
2411   // Loop until EOF or }
2412   o = 0;
2413   g = general;
2414 
2415   while (get_token(fp, temp, sizeof(temp)))
2416   {
2417     if (temp[0] == '*')
2418     {
2419       // Mark the next choice as the default
2420       isdefault = 1;
2421 
2422       for (ptr = temp; ptr[1]; ptr ++)
2423         *ptr = ptr[1];
2424 
2425       *ptr = '\0';
2426     }
2427     else
2428     {
2429       // Don't mark the next choice as the default
2430       isdefault = 0;
2431     }
2432 
2433     if (!_cups_strcasecmp(temp, "}"))
2434     {
2435       // Close this one out...
2436       break;
2437     }
2438     else if (!_cups_strcasecmp(temp, "{"))
2439     {
2440       // Open a new child...
2441       scan_file(fp, d);
2442     }
2443     else if (!_cups_strcasecmp(temp, "#if"))
2444     {
2445       if ((cond_current - cond_stack) >= 100)
2446       {
2447         _cupsLangPrintf(stderr,
2448 	                _("ppdc: Too many nested #if's on line %d of %s."),
2449 			fp->line, fp->filename);
2450 	break;
2451       }
2452 
2453       cond_current ++;
2454       if (get_integer(fp) > 0)
2455         *cond_current = PPDC_COND_SATISFIED;
2456       else
2457       {
2458         *cond_current = PPDC_COND_SKIP;
2459 	cond_state    |= PPDC_COND_SKIP;
2460       }
2461     }
2462     else if (!_cups_strcasecmp(temp, "#elif"))
2463     {
2464       if (cond_current == cond_stack)
2465       {
2466         _cupsLangPrintf(stderr, _("ppdc: Missing #if on line %d of %s."),
2467 	                fp->line, fp->filename);
2468         break;
2469       }
2470 
2471       if (*cond_current & PPDC_COND_SATISFIED)
2472       {
2473         get_integer(fp);
2474 	*cond_current |= PPDC_COND_SKIP;
2475       }
2476       else if (get_integer(fp) > 0)
2477       {
2478         *cond_current |= PPDC_COND_SATISFIED;
2479 	*cond_current &= ~PPDC_COND_SKIP;
2480       }
2481       else
2482         *cond_current |= PPDC_COND_SKIP;
2483 
2484       // Update the current state
2485       int *cond_temp = cond_current;	// Temporary stack pointer
2486 
2487       cond_state = PPDC_COND_NORMAL;
2488       while (cond_temp > cond_stack)
2489         if (*cond_temp & PPDC_COND_SKIP)
2490 	{
2491 	  cond_state = PPDC_COND_SKIP;
2492 	  break;
2493 	}
2494 	else
2495 	  cond_temp --;
2496     }
2497     else if (!_cups_strcasecmp(temp, "#else"))
2498     {
2499       if (cond_current == cond_stack)
2500       {
2501         _cupsLangPrintf(stderr, _("ppdc: Missing #if on line %d of %s."),
2502 		        fp->line, fp->filename);
2503         break;
2504       }
2505 
2506       if (*cond_current & PPDC_COND_SATISFIED)
2507 	*cond_current |= PPDC_COND_SKIP;
2508       else
2509       {
2510         *cond_current |= PPDC_COND_SATISFIED;
2511 	*cond_current &= ~PPDC_COND_SKIP;
2512       }
2513 
2514       // Update the current state
2515       int *cond_temp = cond_current;	// Temporary stack pointer
2516 
2517       cond_state = PPDC_COND_NORMAL;
2518       while (cond_temp > cond_stack)
2519         if (*cond_temp & PPDC_COND_SKIP)
2520 	{
2521 	  cond_state = PPDC_COND_SKIP;
2522 	  break;
2523 	}
2524 	else
2525 	  cond_temp --;
2526     }
2527     else if (!_cups_strcasecmp(temp, "#endif"))
2528     {
2529       if (cond_current == cond_stack)
2530       {
2531         _cupsLangPrintf(stderr, _("ppdc: Missing #if on line %d of %s."),
2532 	                fp->line, fp->filename);
2533         break;
2534       }
2535 
2536       cond_current --;
2537 
2538       // Update the current state
2539       int *cond_temp = cond_current;	// Temporary stack pointer
2540 
2541       cond_state = PPDC_COND_NORMAL;
2542       while (cond_temp > cond_stack)
2543         if (*cond_temp & PPDC_COND_SKIP)
2544 	{
2545 	  cond_state = PPDC_COND_SKIP;
2546 	  break;
2547 	}
2548 	else
2549 	  cond_temp --;
2550     }
2551     else if (!_cups_strcasecmp(temp, "#define"))
2552     {
2553       // Get the variable...
2554       get_variable(fp);
2555     }
2556     else if (!_cups_strcasecmp(temp, "#include"))
2557     {
2558       // #include filename
2559       char	basedir[1024],		// Base directory
2560 		*baseptr,		// Pointer into directory
2561 		inctemp[1024],		// Initial filename
2562 		incname[1024];		// Include filename
2563       ppdcFile	*incfile;		// Include file
2564       int	*old_current = cond_current;
2565 					// Previous current stack
2566 
2567 
2568       // Get the include name...
2569       if (!get_token(fp, inctemp, sizeof(inctemp)))
2570       {
2571         _cupsLangPrintf(stderr,
2572 	                _("ppdc: Expected include filename on line %d of "
2573 			  "%s."), fp->line, fp->filename);
2574         break;
2575       }
2576 
2577       if (cond_state)
2578         continue;
2579 
2580       // Figure out the current directory...
2581       strlcpy(basedir, fp->filename, sizeof(basedir));
2582 
2583       if ((baseptr = strrchr(basedir, '/')) != NULL)
2584 	*baseptr = '\0';
2585       else
2586 	strlcpy(basedir, ".", sizeof(basedir));
2587 
2588       // Find the include file...
2589       if (find_include(inctemp, basedir, incname, sizeof(incname)))
2590       {
2591 	// Open the include file, scan it, and then close it...
2592 	incfile = new ppdcFile(incname);
2593 	scan_file(incfile, d, true);
2594 	delete incfile;
2595 
2596 	if (cond_current != old_current)
2597 	  _cupsLangPrintf(stderr, _("ppdc: Missing #endif at end of \"%s\"."),
2598 	                  incname);
2599       }
2600       else
2601       {
2602 	// Can't find it!
2603 	_cupsLangPrintf(stderr,
2604 		        _("ppdc: Unable to find include file \"%s\" on line %d "
2605 			  "of %s."), inctemp, fp->line, fp->filename);
2606 	break;
2607       }
2608     }
2609     else if (!_cups_strcasecmp(temp, "#media"))
2610     {
2611       ppdcMediaSize	*m;		// Media size
2612 
2613 
2614       // Get a media size...
2615       m = get_size(fp);
2616       if (m)
2617       {
2618         if (cond_state)
2619 	  m->release();
2620 	else
2621           sizes->add(m);
2622       }
2623     }
2624     else if (!_cups_strcasecmp(temp, "#po"))
2625     {
2626       ppdcCatalog	*cat;		// Message catalog
2627 
2628 
2629       // Get a message catalog...
2630       cat = get_po(fp);
2631       if (cat)
2632       {
2633         if (cond_state)
2634 	  cat->release();
2635 	else
2636 	  po_files->add(cat);
2637       }
2638     }
2639     else if (!_cups_strcasecmp(temp, "Attribute") ||
2640              !_cups_strcasecmp(temp, "LocAttribute"))
2641     {
2642       ppdcAttr	*a;			// Attribute
2643 
2644 
2645       // Get an attribute...
2646       a = get_attr(fp, !_cups_strcasecmp(temp, "LocAttribute"));
2647       if (a)
2648       {
2649         if (cond_state)
2650 	  a->release();
2651 	else
2652           d->add_attr(a);
2653       }
2654     }
2655     else if (!_cups_strcasecmp(temp, "Choice"))
2656     {
2657       // Get a choice...
2658       c = get_choice(fp);
2659       if (!c)
2660         break;
2661 
2662       if (cond_state)
2663       {
2664         c->release();
2665         continue;
2666       }
2667 
2668       // Add it to the current option...
2669       if (!o)
2670       {
2671         c->release();
2672         _cupsLangPrintf(stderr,
2673 	                _("ppdc: Choice found on line %d of %s with no "
2674 			  "Option."), fp->line, fp->filename);
2675         break;
2676       }
2677 
2678       o->add_choice(c);
2679 
2680       if (isdefault)
2681         o->set_defchoice(c);
2682     }
2683     else if (!_cups_strcasecmp(temp, "ColorDevice"))
2684     {
2685       // ColorDevice boolean
2686       if (cond_state)
2687         get_boolean(fp);
2688       else
2689         d->color_device = get_boolean(fp);
2690     }
2691     else if (!_cups_strcasecmp(temp, "ColorModel"))
2692     {
2693       // Get the color model
2694       c = get_color_model(fp);
2695       if (!c)
2696         continue;
2697 
2698       if (cond_state)
2699       {
2700         c->release();
2701         continue;
2702       }
2703 
2704       // Add the choice to the ColorModel option...
2705       if ((o = d->find_option("ColorModel")) == NULL)
2706       {
2707 	// Create the ColorModel option...
2708 	o = new ppdcOption(PPDC_PICKONE, "ColorModel", "Color Mode", PPDC_SECTION_ANY, 10.0f);
2709 	g = general;
2710 	g->add_option(o);
2711       }
2712 
2713       o->add_choice(c);
2714 
2715       if (isdefault)
2716 	o->set_defchoice(c);
2717 
2718       o = NULL;
2719     }
2720     else if (!_cups_strcasecmp(temp, "ColorProfile"))
2721     {
2722       ppdcProfile	*p;		// Color profile
2723 
2724 
2725       // Get the color profile...
2726       p = get_color_profile(fp);
2727 
2728       if (p)
2729       {
2730         if (cond_state)
2731 	  p->release();
2732 	else
2733           d->profiles->add(p);
2734       }
2735     }
2736     else if (!_cups_strcasecmp(temp, "Copyright"))
2737     {
2738       // Copyright string
2739       char	copytemp[8192],		// Copyright string
2740 		*copyptr,		// Pointer into string
2741 		*copyend;		// Pointer to end of string
2742 
2743 
2744       // Get the copyright string...
2745       if (!get_token(fp, copytemp, sizeof(temp)))
2746       {
2747         _cupsLangPrintf(stderr,
2748 	                _("ppdc: Expected string after Copyright on line %d "
2749 			  "of %s."), fp->line, fp->filename);
2750 	break;
2751       }
2752 
2753       if (cond_state)
2754         continue;
2755 
2756       // Break it up into individual lines...
2757       for (copyptr = copytemp; copyptr; copyptr = copyend)
2758       {
2759         if ((copyend = strchr(copyptr, '\n')) != NULL)
2760 	  *copyend++ = '\0';
2761 
2762         d->copyright->add(new ppdcString(copyptr));
2763       }
2764     }
2765     else if (!_cups_strcasecmp(temp, "CustomMedia"))
2766     {
2767       ppdcMediaSize	*m;		// Media size
2768 
2769 
2770       // Get a custom media size...
2771       m = get_custom_size(fp);
2772 
2773       if (cond_state)
2774       {
2775         m->release();
2776         continue;
2777       }
2778 
2779       if (m)
2780         d->sizes->add(m);
2781 
2782       if (isdefault)
2783         d->set_default_size(m);
2784     }
2785     else if (!_cups_strcasecmp(temp, "Cutter"))
2786     {
2787       // Cutter boolean
2788       int	have_cutter;		// Have a paper cutter?
2789 
2790 
2791       have_cutter = get_boolean(fp);
2792       if (have_cutter <= 0 || cond_state)
2793         continue;
2794 
2795       if ((o = d->find_option("CutMedia")) == NULL)
2796       {
2797         o = new ppdcOption(PPDC_BOOLEAN, "CutMedia", "Cut Media", PPDC_SECTION_ANY, 10.0f);
2798 
2799 	g = general;
2800 	g->add_option(o);
2801 
2802 	c = new ppdcChoice("False", NULL, "<</CutMedia 0>>setpagedevice");
2803 	o->add_choice(c);
2804 	o->set_defchoice(c);
2805 
2806 	c = new ppdcChoice("True", NULL, "<</CutMedia 4>>setpagedevice");
2807 	o->add_choice(c);
2808       }
2809 
2810       o = NULL;
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 stings...
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