1 /*
2  * PPD command interpreter for CUPS.
3  *
4  * Copyright © 2007-2018 by Apple Inc.
5  * Copyright © 1993-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 <cups/raster-private.h>
16 #include <cups/ppd-private.h>
17 #include "debug-internal.h"
18 
19 
20 /*
21  * Stack values for the PostScript mini-interpreter...
22  */
23 
24 typedef enum
25 {
26   CUPS_PS_NAME,
27   CUPS_PS_NUMBER,
28   CUPS_PS_STRING,
29   CUPS_PS_BOOLEAN,
30   CUPS_PS_NULL,
31   CUPS_PS_START_ARRAY,
32   CUPS_PS_END_ARRAY,
33   CUPS_PS_START_DICT,
34   CUPS_PS_END_DICT,
35   CUPS_PS_START_PROC,
36   CUPS_PS_END_PROC,
37   CUPS_PS_CLEARTOMARK,
38   CUPS_PS_COPY,
39   CUPS_PS_DUP,
40   CUPS_PS_INDEX,
41   CUPS_PS_POP,
42   CUPS_PS_ROLL,
43   CUPS_PS_SETPAGEDEVICE,
44   CUPS_PS_STOPPED,
45   CUPS_PS_OTHER
46 } _cups_ps_type_t;
47 
48 typedef struct
49 {
50   _cups_ps_type_t	type;		/* Object type */
51   union
52   {
53     int		boolean;		/* Boolean value */
54     char	name[64];		/* Name value */
55     double	number;			/* Number value */
56     char	other[64];		/* Other operator */
57     char	string[64];		/* Sring value */
58   }			value;		/* Value */
59 } _cups_ps_obj_t;
60 
61 typedef struct
62 {
63   int			num_objs,	/* Number of objects on stack */
64 			alloc_objs;	/* Number of allocated objects */
65   _cups_ps_obj_t	*objs;		/* Objects in stack */
66 } _cups_ps_stack_t;
67 
68 
69 /*
70  * Local functions...
71  */
72 
73 static int		cleartomark_stack(_cups_ps_stack_t *st);
74 static int		copy_stack(_cups_ps_stack_t *st, int count);
75 static void		delete_stack(_cups_ps_stack_t *st);
76 static void		error_object(_cups_ps_obj_t *obj);
77 static void		error_stack(_cups_ps_stack_t *st, const char *title);
78 static _cups_ps_obj_t	*index_stack(_cups_ps_stack_t *st, int n);
79 static _cups_ps_stack_t	*new_stack(void);
80 static _cups_ps_obj_t	*pop_stack(_cups_ps_stack_t *st);
81 static _cups_ps_obj_t	*push_stack(_cups_ps_stack_t *st,
82 			            _cups_ps_obj_t *obj);
83 static int		roll_stack(_cups_ps_stack_t *st, int c, int s);
84 static _cups_ps_obj_t	*scan_ps(_cups_ps_stack_t *st, char **ptr);
85 static int		setpagedevice(_cups_ps_stack_t *st,
86 			                cups_page_header2_t *h,
87 			                int *preferred_bits);
88 #ifdef DEBUG
89 static void		DEBUG_object(const char *prefix, _cups_ps_obj_t *obj);
90 static void		DEBUG_stack(const char *prefix, _cups_ps_stack_t *st);
91 #endif /* DEBUG */
92 
93 
94 /*
95  * '_cupsRasterInterpretPPD()' - Interpret PPD commands to create a page header.
96  *
97  * This function is used by raster image processing (RIP) filters like
98  * cgpdftoraster and imagetoraster when writing CUPS raster data for a page.
99  * It is not used by raster printer driver filters which only read CUPS
100  * raster data.
101  *
102  *
103  * @code cupsRasterInterpretPPD@ does not mark the options in the PPD using
104  * the "num_options" and "options" arguments.  Instead, mark the options with
105  * @code cupsMarkOptions@ and @code ppdMarkOption@ prior to calling it -
106  * this allows for per-page options without manipulating the options array.
107  *
108  * The "func" argument specifies an optional callback function that is
109  * called prior to the computation of the final raster data.  The function
110  * can make changes to the @link cups_page_header2_t@ data as needed to use a
111  * supported raster format and then returns 0 on success and -1 if the
112  * requested attributes cannot be supported.
113  *
114  *
115  * @code cupsRasterInterpretPPD@ supports a subset of the PostScript language.
116  * Currently only the @code [@, @code ]@, @code <<@, @code >>@, @code {@,
117  * @code }@, @code cleartomark@, @code copy@, @code dup@, @code index@,
118  * @code pop@, @code roll@, @code setpagedevice@, and @code stopped@ operators
119  * are supported.
120  *
121  * @since CUPS 1.2/macOS 10.5@
122  */
123 
124 int					/* O - 0 on success, -1 on failure */
_cupsRasterInterpretPPD(cups_page_header2_t * h,ppd_file_t * ppd,int num_options,cups_option_t * options,cups_interpret_cb_t func)125 _cupsRasterInterpretPPD(
126     cups_page_header2_t *h,		/* O - Page header to create */
127     ppd_file_t          *ppd,		/* I - PPD file */
128     int                 num_options,	/* I - Number of options */
129     cups_option_t       *options,	/* I - Options */
130     cups_interpret_cb_t func)		/* I - Optional page header callback (@code NULL@ for none) */
131 {
132   int		status;			/* Cummulative status */
133   char		*code;			/* Code to run */
134   const char	*val;			/* Option value */
135   ppd_size_t	*size;			/* Current size */
136   float		left,			/* Left position */
137 		bottom,			/* Bottom position */
138 		right,			/* Right position */
139 		top,			/* Top position */
140 		temp1, temp2;		/* Temporary variables for swapping */
141   int		preferred_bits;		/* Preferred bits per color */
142 
143 
144  /*
145   * Range check input...
146   */
147 
148   _cupsRasterClearError();
149 
150   if (!h)
151   {
152     _cupsRasterAddError("Page header cannot be NULL!\n");
153     return (-1);
154   }
155 
156  /*
157   * Reset the page header to the defaults...
158   */
159 
160   memset(h, 0, sizeof(cups_page_header2_t));
161 
162   h->NumCopies                   = 1;
163   h->PageSize[0]                 = 612;
164   h->PageSize[1]                 = 792;
165   h->HWResolution[0]             = 100;
166   h->HWResolution[1]             = 100;
167   h->cupsBitsPerColor            = 1;
168   h->cupsColorOrder              = CUPS_ORDER_CHUNKED;
169   h->cupsColorSpace              = CUPS_CSPACE_K;
170   h->cupsBorderlessScalingFactor = 1.0f;
171   h->cupsPageSize[0]             = 612.0f;
172   h->cupsPageSize[1]             = 792.0f;
173   h->cupsImagingBBox[0]          = 0.0f;
174   h->cupsImagingBBox[1]          = 0.0f;
175   h->cupsImagingBBox[2]          = 612.0f;
176   h->cupsImagingBBox[3]          = 792.0f;
177 
178   strlcpy(h->cupsPageSizeName, "Letter", sizeof(h->cupsPageSizeName));
179 
180 #ifdef __APPLE__
181  /*
182   * cupsInteger0 is also used for the total page count on macOS; set an
183   * uncommon default value so we can tell if the driver is using cupsInteger0.
184   */
185 
186   h->cupsInteger[0] = 0x80000000;
187 #endif /* __APPLE__ */
188 
189  /*
190   * Apply patches and options to the page header...
191   */
192 
193   status         = 0;
194   preferred_bits = 0;
195 
196   if (ppd)
197   {
198    /*
199     * Apply any patch code (used to override the defaults...)
200     */
201 
202     if (ppd->patches)
203       status |= _cupsRasterExecPS(h, &preferred_bits, ppd->patches);
204 
205    /*
206     * Then apply printer options in the proper order...
207     */
208 
209     if ((code = ppdEmitString(ppd, PPD_ORDER_DOCUMENT, 0.0)) != NULL)
210     {
211       status |= _cupsRasterExecPS(h, &preferred_bits, code);
212       free(code);
213     }
214 
215     if ((code = ppdEmitString(ppd, PPD_ORDER_ANY, 0.0)) != NULL)
216     {
217       status |= _cupsRasterExecPS(h, &preferred_bits, code);
218       free(code);
219     }
220 
221     if ((code = ppdEmitString(ppd, PPD_ORDER_PROLOG, 0.0)) != NULL)
222     {
223       status |= _cupsRasterExecPS(h, &preferred_bits, code);
224       free(code);
225     }
226 
227     if ((code = ppdEmitString(ppd, PPD_ORDER_PAGE, 0.0)) != NULL)
228     {
229       status |= _cupsRasterExecPS(h, &preferred_bits, code);
230       free(code);
231     }
232   }
233 
234  /*
235   * Allow option override for page scaling...
236   */
237 
238   if ((val = cupsGetOption("cupsBorderlessScalingFactor", num_options,
239                            options)) != NULL)
240   {
241     double sc = atof(val);		/* Scale factor */
242 
243     if (sc >= 0.1 && sc <= 2.0)
244       h->cupsBorderlessScalingFactor = (float)sc;
245   }
246 
247  /*
248   * Get the margins for the current size...
249   */
250 
251   if ((size = ppdPageSize(ppd, NULL)) != NULL)
252   {
253    /*
254     * Use the margins from the PPD file...
255     */
256 
257     left   = size->left;
258     bottom = size->bottom;
259     right  = size->right;
260     top    = size->top;
261 
262     strlcpy(h->cupsPageSizeName, size->name, sizeof(h->cupsPageSizeName));
263 
264     h->cupsPageSize[0] = size->width;
265     h->cupsPageSize[1] = size->length;
266   }
267   else
268   {
269    /*
270     * Use the default margins...
271     */
272 
273     left   = 0.0f;
274     bottom = 0.0f;
275     right  = 612.0f;
276     top    = 792.0f;
277   }
278 
279  /*
280   * Handle orientation...
281   */
282 
283   switch (h->Orientation)
284   {
285     case CUPS_ORIENT_0 :
286     default :
287         /* Do nothing */
288         break;
289 
290     case CUPS_ORIENT_90 :
291         temp1              = h->cupsPageSize[0];
292         h->cupsPageSize[0] = h->cupsPageSize[1];
293         h->cupsPageSize[1] = temp1;
294 
295         temp1  = left;
296         temp2  = right;
297         left   = h->cupsPageSize[0] - top;
298         right  = h->cupsPageSize[0] - bottom;
299         bottom = h->cupsPageSize[1] - temp1;
300         top    = h->cupsPageSize[1] - temp2;
301         break;
302 
303     case CUPS_ORIENT_180 :
304         temp1  = left;
305         temp2  = bottom;
306         left   = h->cupsPageSize[0] - right;
307         right  = h->cupsPageSize[0] - temp1;
308         bottom = h->cupsPageSize[1] - top;
309         top    = h->cupsPageSize[1] - temp2;
310         break;
311 
312     case CUPS_ORIENT_270 :
313         temp1              = h->cupsPageSize[0];
314         h->cupsPageSize[0] = h->cupsPageSize[1];
315         h->cupsPageSize[1] = temp1;
316 
317         temp1  = left;
318         temp2  = right;
319         left   = bottom;
320         right  = top;
321         bottom = h->cupsPageSize[1] - temp2;
322         top    = h->cupsPageSize[1] - temp1;
323         break;
324   }
325 
326   if (left > right)
327   {
328     temp1 = left;
329     left  = right;
330     right = temp1;
331   }
332 
333   if (bottom > top)
334   {
335     temp1  = bottom;
336     bottom = top;
337     top    = temp1;
338   }
339 
340   h->PageSize[0]           = (unsigned)(h->cupsPageSize[0] *
341                                         h->cupsBorderlessScalingFactor);
342   h->PageSize[1]           = (unsigned)(h->cupsPageSize[1] *
343                                         h->cupsBorderlessScalingFactor);
344   h->Margins[0]            = (unsigned)(left *
345                                         h->cupsBorderlessScalingFactor);
346   h->Margins[1]            = (unsigned)(bottom *
347                                         h->cupsBorderlessScalingFactor);
348   h->ImagingBoundingBox[0] = (unsigned)(left *
349                                         h->cupsBorderlessScalingFactor);
350   h->ImagingBoundingBox[1] = (unsigned)(bottom *
351                                         h->cupsBorderlessScalingFactor);
352   h->ImagingBoundingBox[2] = (unsigned)(right *
353                                         h->cupsBorderlessScalingFactor);
354   h->ImagingBoundingBox[3] = (unsigned)(top *
355                                         h->cupsBorderlessScalingFactor);
356   h->cupsImagingBBox[0]    = (float)left;
357   h->cupsImagingBBox[1]    = (float)bottom;
358   h->cupsImagingBBox[2]    = (float)right;
359   h->cupsImagingBBox[3]    = (float)top;
360 
361  /*
362   * Use the callback to validate the page header...
363   */
364 
365   if (func && (*func)(h, preferred_bits))
366   {
367     _cupsRasterAddError("Page header callback returned error.\n");
368     return (-1);
369   }
370 
371  /*
372   * Check parameters...
373   */
374 
375   if (!h->HWResolution[0] || !h->HWResolution[1] ||
376       !h->PageSize[0] || !h->PageSize[1] ||
377       (h->cupsBitsPerColor != 1 && h->cupsBitsPerColor != 2 &&
378        h->cupsBitsPerColor != 4 && h->cupsBitsPerColor != 8 &&
379        h->cupsBitsPerColor != 16) ||
380       h->cupsBorderlessScalingFactor < 0.1 ||
381       h->cupsBorderlessScalingFactor > 2.0)
382   {
383     _cupsRasterAddError("Page header uses unsupported values.\n");
384     return (-1);
385   }
386 
387  /*
388   * Compute the bitmap parameters...
389   */
390 
391   h->cupsWidth  = (unsigned)((right - left) * h->cupsBorderlessScalingFactor *
392                         h->HWResolution[0] / 72.0f + 0.5f);
393   h->cupsHeight = (unsigned)((top - bottom) * h->cupsBorderlessScalingFactor *
394                         h->HWResolution[1] / 72.0f + 0.5f);
395 
396   switch (h->cupsColorSpace)
397   {
398     case CUPS_CSPACE_W :
399     case CUPS_CSPACE_K :
400     case CUPS_CSPACE_WHITE :
401     case CUPS_CSPACE_GOLD :
402     case CUPS_CSPACE_SILVER :
403     case CUPS_CSPACE_SW :
404         h->cupsNumColors    = 1;
405         h->cupsBitsPerPixel = h->cupsBitsPerColor;
406 	break;
407 
408     default :
409        /*
410         * Ensure that colorimetric colorspaces use at least 8 bits per
411 	* component...
412 	*/
413 
414         if (h->cupsColorSpace >= CUPS_CSPACE_CIEXYZ &&
415 	    h->cupsBitsPerColor < 8)
416 	  h->cupsBitsPerColor = 8;
417 
418        /*
419         * Figure out the number of bits per pixel...
420 	*/
421 
422 	if (h->cupsColorOrder == CUPS_ORDER_CHUNKED)
423 	{
424 	  if (h->cupsBitsPerColor >= 8)
425             h->cupsBitsPerPixel = h->cupsBitsPerColor * 3;
426 	  else
427             h->cupsBitsPerPixel = h->cupsBitsPerColor * 4;
428 	}
429 	else
430 	  h->cupsBitsPerPixel = h->cupsBitsPerColor;
431 
432         h->cupsNumColors = 3;
433 	break;
434 
435     case CUPS_CSPACE_KCMYcm :
436 	if (h->cupsBitsPerColor == 1)
437 	{
438 	  if (h->cupsColorOrder == CUPS_ORDER_CHUNKED)
439 	    h->cupsBitsPerPixel = 8;
440 	  else
441 	    h->cupsBitsPerPixel = 1;
442 
443           h->cupsNumColors = 6;
444           break;
445 	}
446 
447        /*
448 	* Fall through to CMYK code...
449 	*/
450 
451     case CUPS_CSPACE_RGBA :
452     case CUPS_CSPACE_RGBW :
453     case CUPS_CSPACE_CMYK :
454     case CUPS_CSPACE_YMCK :
455     case CUPS_CSPACE_KCMY :
456     case CUPS_CSPACE_GMCK :
457     case CUPS_CSPACE_GMCS :
458 	if (h->cupsColorOrder == CUPS_ORDER_CHUNKED)
459           h->cupsBitsPerPixel = h->cupsBitsPerColor * 4;
460 	else
461 	  h->cupsBitsPerPixel = h->cupsBitsPerColor;
462 
463         h->cupsNumColors = 4;
464 	break;
465 
466     case CUPS_CSPACE_DEVICE1 :
467     case CUPS_CSPACE_DEVICE2 :
468     case CUPS_CSPACE_DEVICE3 :
469     case CUPS_CSPACE_DEVICE4 :
470     case CUPS_CSPACE_DEVICE5 :
471     case CUPS_CSPACE_DEVICE6 :
472     case CUPS_CSPACE_DEVICE7 :
473     case CUPS_CSPACE_DEVICE8 :
474     case CUPS_CSPACE_DEVICE9 :
475     case CUPS_CSPACE_DEVICEA :
476     case CUPS_CSPACE_DEVICEB :
477     case CUPS_CSPACE_DEVICEC :
478     case CUPS_CSPACE_DEVICED :
479     case CUPS_CSPACE_DEVICEE :
480     case CUPS_CSPACE_DEVICEF :
481         h->cupsNumColors = h->cupsColorSpace - CUPS_CSPACE_DEVICE1 + 1;
482 
483         if (h->cupsColorOrder == CUPS_ORDER_CHUNKED)
484           h->cupsBitsPerPixel = h->cupsBitsPerColor * h->cupsNumColors;
485 	else
486 	  h->cupsBitsPerPixel = h->cupsBitsPerColor;
487 	break;
488   }
489 
490   h->cupsBytesPerLine = (h->cupsBitsPerPixel * h->cupsWidth + 7) / 8;
491 
492   if (h->cupsColorOrder == CUPS_ORDER_BANDED)
493     h->cupsBytesPerLine *= h->cupsNumColors;
494 
495   return (status);
496 }
497 
498 
499 /*
500  * '_cupsRasterExecPS()' - Execute PostScript code to initialize a page header.
501  */
502 
503 int					/* O - 0 on success, -1 on error */
_cupsRasterExecPS(cups_page_header2_t * h,int * preferred_bits,const char * code)504 _cupsRasterExecPS(
505     cups_page_header2_t *h,		/* O - Page header */
506     int                 *preferred_bits,/* O - Preferred bits per color */
507     const char          *code)		/* I - PS code to execute */
508 {
509   int			error = 0;	/* Error condition? */
510   _cups_ps_stack_t	*st;		/* PostScript value stack */
511   _cups_ps_obj_t	*obj;		/* Object from top of stack */
512   char			*codecopy,	/* Copy of code */
513 			*codeptr;	/* Pointer into copy of code */
514 
515 
516   DEBUG_printf(("_cupsRasterExecPS(h=%p, preferred_bits=%p, code=\"%s\")\n",
517                 h, preferred_bits, code));
518 
519  /*
520   * Copy the PostScript code and create a stack...
521   */
522 
523   if ((codecopy = strdup(code)) == NULL)
524   {
525     _cupsRasterAddError("Unable to duplicate code string.\n");
526     return (-1);
527   }
528 
529   if ((st = new_stack()) == NULL)
530   {
531     _cupsRasterAddError("Unable to create stack.\n");
532     free(codecopy);
533     return (-1);
534   }
535 
536  /*
537   * Parse the PS string until we run out of data...
538   */
539 
540   codeptr = codecopy;
541 
542   while ((obj = scan_ps(st, &codeptr)) != NULL)
543   {
544 #ifdef DEBUG
545     DEBUG_printf(("_cupsRasterExecPS: Stack (%d objects)", st->num_objs));
546     DEBUG_object("_cupsRasterExecPS", obj);
547 #endif /* DEBUG */
548 
549     switch (obj->type)
550     {
551       default :
552           /* Do nothing for regular values */
553 	  break;
554 
555       case CUPS_PS_CLEARTOMARK :
556           pop_stack(st);
557 
558 	  if (cleartomark_stack(st))
559 	    _cupsRasterAddError("cleartomark: Stack underflow.\n");
560 
561 #ifdef DEBUG
562           DEBUG_puts("1_cupsRasterExecPS:    dup");
563 	  DEBUG_stack("_cupsRasterExecPS", st);
564 #endif /* DEBUG */
565           break;
566 
567       case CUPS_PS_COPY :
568           pop_stack(st);
569 	  if ((obj = pop_stack(st)) != NULL)
570 	  {
571 	    copy_stack(st, (int)obj->value.number);
572 
573 #ifdef DEBUG
574             DEBUG_puts("_cupsRasterExecPS: copy");
575 	    DEBUG_stack("_cupsRasterExecPS", st);
576 #endif /* DEBUG */
577           }
578           break;
579 
580       case CUPS_PS_DUP :
581           pop_stack(st);
582 	  copy_stack(st, 1);
583 
584 #ifdef DEBUG
585           DEBUG_puts("_cupsRasterExecPS: dup");
586 	  DEBUG_stack("_cupsRasterExecPS", st);
587 #endif /* DEBUG */
588           break;
589 
590       case CUPS_PS_INDEX :
591           pop_stack(st);
592 	  if ((obj = pop_stack(st)) != NULL)
593 	  {
594 	    index_stack(st, (int)obj->value.number);
595 
596 #ifdef DEBUG
597             DEBUG_puts("_cupsRasterExecPS: index");
598 	    DEBUG_stack("_cupsRasterExecPS", st);
599 #endif /* DEBUG */
600           }
601           break;
602 
603       case CUPS_PS_POP :
604           pop_stack(st);
605           pop_stack(st);
606 
607 #ifdef DEBUG
608           DEBUG_puts("_cupsRasterExecPS: pop");
609 	  DEBUG_stack("_cupsRasterExecPS", st);
610 #endif /* DEBUG */
611           break;
612 
613       case CUPS_PS_ROLL :
614           pop_stack(st);
615 	  if ((obj = pop_stack(st)) != NULL)
616 	  {
617             int		c;		/* Count */
618 
619 
620             c = (int)obj->value.number;
621 
622 	    if ((obj = pop_stack(st)) != NULL)
623 	    {
624 	      roll_stack(st, (int)obj->value.number, c);
625 
626 #ifdef DEBUG
627               DEBUG_puts("_cupsRasterExecPS: roll");
628 	      DEBUG_stack("_cupsRasterExecPS", st);
629 #endif /* DEBUG */
630             }
631 	  }
632           break;
633 
634       case CUPS_PS_SETPAGEDEVICE :
635           pop_stack(st);
636 	  setpagedevice(st, h, preferred_bits);
637 
638 #ifdef DEBUG
639           DEBUG_puts("_cupsRasterExecPS: setpagedevice");
640 	  DEBUG_stack("_cupsRasterExecPS", st);
641 #endif /* DEBUG */
642           break;
643 
644       case CUPS_PS_START_PROC :
645       case CUPS_PS_END_PROC :
646       case CUPS_PS_STOPPED :
647           pop_stack(st);
648 	  break;
649 
650       case CUPS_PS_OTHER :
651           _cupsRasterAddError("Unknown operator \"%s\".\n", obj->value.other);
652 	  error = 1;
653           DEBUG_printf(("_cupsRasterExecPS: Unknown operator \"%s\".", obj->value.other));
654           break;
655     }
656 
657     if (error)
658       break;
659   }
660 
661  /*
662   * Cleanup...
663   */
664 
665   free(codecopy);
666 
667   if (st->num_objs > 0)
668   {
669     error_stack(st, "Stack not empty:");
670 
671 #ifdef DEBUG
672     DEBUG_puts("_cupsRasterExecPS: Stack not empty");
673     DEBUG_stack("_cupsRasterExecPS", st);
674 #endif /* DEBUG */
675 
676     delete_stack(st);
677 
678     return (-1);
679   }
680 
681   delete_stack(st);
682 
683  /*
684   * Return success...
685   */
686 
687   return (0);
688 }
689 
690 
691 /*
692  * 'cleartomark_stack()' - Clear to the last mark ([) on the stack.
693  */
694 
695 static int				/* O - 0 on success, -1 on error */
cleartomark_stack(_cups_ps_stack_t * st)696 cleartomark_stack(_cups_ps_stack_t *st)	/* I - Stack */
697 {
698   _cups_ps_obj_t	*obj;		/* Current object on stack */
699 
700 
701   while ((obj = pop_stack(st)) != NULL)
702     if (obj->type == CUPS_PS_START_ARRAY)
703       break;
704 
705   return (obj ? 0 : -1);
706 }
707 
708 
709 /*
710  * 'copy_stack()' - Copy the top N stack objects.
711  */
712 
713 static int				/* O - 0 on success, -1 on error */
copy_stack(_cups_ps_stack_t * st,int c)714 copy_stack(_cups_ps_stack_t *st,	/* I - Stack */
715            int              c)		/* I - Number of objects to copy */
716 {
717   int	n;				/* Index */
718 
719 
720   if (c < 0)
721     return (-1);
722   else if (c == 0)
723     return (0);
724 
725   if ((n = st->num_objs - c) < 0)
726     return (-1);
727 
728   while (c > 0)
729   {
730     if (!push_stack(st, st->objs + n))
731       return (-1);
732 
733     n ++;
734     c --;
735   }
736 
737   return (0);
738 }
739 
740 
741 /*
742  * 'delete_stack()' - Free memory used by a stack.
743  */
744 
745 static void
delete_stack(_cups_ps_stack_t * st)746 delete_stack(_cups_ps_stack_t *st)	/* I - Stack */
747 {
748   free(st->objs);
749   free(st);
750 }
751 
752 
753 /*
754  * 'error_object()' - Add an object's value to the current error message.
755  */
756 
757 static void
error_object(_cups_ps_obj_t * obj)758 error_object(_cups_ps_obj_t *obj)	/* I - Object to add */
759 {
760   switch (obj->type)
761   {
762     case CUPS_PS_NAME :
763 	_cupsRasterAddError(" /%s", obj->value.name);
764 	break;
765 
766     case CUPS_PS_NUMBER :
767 	_cupsRasterAddError(" %g", obj->value.number);
768 	break;
769 
770     case CUPS_PS_STRING :
771 	_cupsRasterAddError(" (%s)", obj->value.string);
772 	break;
773 
774     case CUPS_PS_BOOLEAN :
775 	if (obj->value.boolean)
776 	  _cupsRasterAddError(" true");
777 	else
778 	  _cupsRasterAddError(" false");
779 	break;
780 
781     case CUPS_PS_NULL :
782 	_cupsRasterAddError(" null");
783 	break;
784 
785     case CUPS_PS_START_ARRAY :
786 	_cupsRasterAddError(" [");
787 	break;
788 
789     case CUPS_PS_END_ARRAY :
790 	_cupsRasterAddError(" ]");
791 	break;
792 
793     case CUPS_PS_START_DICT :
794 	_cupsRasterAddError(" <<");
795 	break;
796 
797     case CUPS_PS_END_DICT :
798 	_cupsRasterAddError(" >>");
799 	break;
800 
801     case CUPS_PS_START_PROC :
802 	_cupsRasterAddError(" {");
803 	break;
804 
805     case CUPS_PS_END_PROC :
806 	_cupsRasterAddError(" }");
807 	break;
808 
809     case CUPS_PS_COPY :
810 	_cupsRasterAddError(" --copy--");
811         break;
812 
813     case CUPS_PS_CLEARTOMARK :
814 	_cupsRasterAddError(" --cleartomark--");
815         break;
816 
817     case CUPS_PS_DUP :
818 	_cupsRasterAddError(" --dup--");
819         break;
820 
821     case CUPS_PS_INDEX :
822 	_cupsRasterAddError(" --index--");
823         break;
824 
825     case CUPS_PS_POP :
826 	_cupsRasterAddError(" --pop--");
827         break;
828 
829     case CUPS_PS_ROLL :
830 	_cupsRasterAddError(" --roll--");
831         break;
832 
833     case CUPS_PS_SETPAGEDEVICE :
834 	_cupsRasterAddError(" --setpagedevice--");
835         break;
836 
837     case CUPS_PS_STOPPED :
838 	_cupsRasterAddError(" --stopped--");
839         break;
840 
841     case CUPS_PS_OTHER :
842 	_cupsRasterAddError(" --%s--", obj->value.other);
843 	break;
844   }
845 }
846 
847 
848 /*
849  * 'error_stack()' - Add a stack to the current error message...
850  */
851 
852 static void
error_stack(_cups_ps_stack_t * st,const char * title)853 error_stack(_cups_ps_stack_t *st,	/* I - Stack */
854             const char       *title)	/* I - Title string */
855 {
856   int			c;		/* Looping var */
857   _cups_ps_obj_t	*obj;		/* Current object on stack */
858 
859 
860   _cupsRasterAddError("%s", title);
861 
862   for (obj = st->objs, c = st->num_objs; c > 0; c --, obj ++)
863     error_object(obj);
864 
865   _cupsRasterAddError("\n");
866 }
867 
868 
869 /*
870  * 'index_stack()' - Copy the Nth value on the stack.
871  */
872 
873 static _cups_ps_obj_t	*		/* O - New object */
index_stack(_cups_ps_stack_t * st,int n)874 index_stack(_cups_ps_stack_t *st,	/* I - Stack */
875             int              n)		/* I - Object index */
876 {
877   if (n < 0 || (n = st->num_objs - n - 1) < 0)
878     return (NULL);
879 
880   return (push_stack(st, st->objs + n));
881 }
882 
883 
884 /*
885  * 'new_stack()' - Create a new stack.
886  */
887 
888 static _cups_ps_stack_t	*		/* O - New stack */
new_stack(void)889 new_stack(void)
890 {
891   _cups_ps_stack_t	*st;		/* New stack */
892 
893 
894   if ((st = calloc(1, sizeof(_cups_ps_stack_t))) == NULL)
895     return (NULL);
896 
897   st->alloc_objs = 32;
898 
899   if ((st->objs = calloc(32, sizeof(_cups_ps_obj_t))) == NULL)
900   {
901     free(st);
902     return (NULL);
903   }
904   else
905     return (st);
906 }
907 
908 
909 /*
910  * 'pop_stock()' - Pop the top object off the stack.
911  */
912 
913 static _cups_ps_obj_t	*		/* O - Object */
pop_stack(_cups_ps_stack_t * st)914 pop_stack(_cups_ps_stack_t *st)		/* I - Stack */
915 {
916   if (st->num_objs > 0)
917   {
918     st->num_objs --;
919 
920     return (st->objs + st->num_objs);
921   }
922   else
923     return (NULL);
924 }
925 
926 
927 /*
928  * 'push_stack()' - Push an object on the stack.
929  */
930 
931 static _cups_ps_obj_t	*		/* O - New object */
push_stack(_cups_ps_stack_t * st,_cups_ps_obj_t * obj)932 push_stack(_cups_ps_stack_t *st,	/* I - Stack */
933            _cups_ps_obj_t   *obj)	/* I - Object */
934 {
935   _cups_ps_obj_t	*temp;		/* New object */
936 
937 
938   if (st->num_objs >= st->alloc_objs)
939   {
940 
941 
942     st->alloc_objs += 32;
943 
944     if ((temp = realloc(st->objs, (size_t)st->alloc_objs *
945                                   sizeof(_cups_ps_obj_t))) == NULL)
946       return (NULL);
947 
948     st->objs = temp;
949     memset(temp + st->num_objs, 0, 32 * sizeof(_cups_ps_obj_t));
950   }
951 
952   temp = st->objs + st->num_objs;
953   st->num_objs ++;
954 
955   memcpy(temp, obj, sizeof(_cups_ps_obj_t));
956 
957   return (temp);
958 }
959 
960 
961 /*
962  * 'roll_stack()' - Rotate stack objects.
963  */
964 
965 static int				/* O - 0 on success, -1 on error */
roll_stack(_cups_ps_stack_t * st,int c,int s)966 roll_stack(_cups_ps_stack_t *st,	/* I - Stack */
967 	   int              c,		/* I - Number of objects */
968            int              s)		/* I - Amount to shift */
969 {
970   _cups_ps_obj_t	*temp;		/* Temporary array of objects */
971   int			n;		/* Index into array */
972 
973 
974   DEBUG_printf(("3roll_stack(st=%p, s=%d, c=%d)", st, s, c));
975 
976  /*
977   * Range check input...
978   */
979 
980   if (c < 0)
981     return (-1);
982   else if (c == 0)
983     return (0);
984 
985   if ((n = st->num_objs - c) < 0)
986     return (-1);
987 
988   s %= c;
989 
990   if (s == 0)
991     return (0);
992 
993  /*
994   * Copy N objects and move things around...
995   */
996 
997   if (s < 0)
998   {
999    /*
1000     * Shift down...
1001     */
1002 
1003     s = -s;
1004 
1005     if ((temp = calloc((size_t)s, sizeof(_cups_ps_obj_t))) == NULL)
1006       return (-1);
1007 
1008     memcpy(temp, st->objs + n, (size_t)s * sizeof(_cups_ps_obj_t));
1009     memmove(st->objs + n, st->objs + n + s, (size_t)(c - s) * sizeof(_cups_ps_obj_t));
1010     memcpy(st->objs + n + c - s, temp, (size_t)s * sizeof(_cups_ps_obj_t));
1011   }
1012   else
1013   {
1014    /*
1015     * Shift up...
1016     */
1017 
1018     if ((temp = calloc((size_t)s, sizeof(_cups_ps_obj_t))) == NULL)
1019       return (-1);
1020 
1021     memcpy(temp, st->objs + n + c - s, (size_t)s * sizeof(_cups_ps_obj_t));
1022     memmove(st->objs + n + s, st->objs + n, (size_t)(c - s) * sizeof(_cups_ps_obj_t));
1023     memcpy(st->objs + n, temp, (size_t)s * sizeof(_cups_ps_obj_t));
1024   }
1025 
1026   free(temp);
1027 
1028   return (0);
1029 }
1030 
1031 
1032 /*
1033  * 'scan_ps()' - Scan a string for the next PS object.
1034  */
1035 
1036 static _cups_ps_obj_t	*		/* O  - New object or NULL on EOF */
scan_ps(_cups_ps_stack_t * st,char ** ptr)1037 scan_ps(_cups_ps_stack_t *st,		/* I  - Stack */
1038         char             **ptr)		/* IO - String pointer */
1039 {
1040   _cups_ps_obj_t	obj;		/* Current object */
1041   char			*start,		/* Start of object */
1042 			*cur,		/* Current position */
1043 			*valptr,	/* Pointer into value string */
1044 			*valend;	/* End of value string */
1045   int			parens;		/* Parenthesis nesting level */
1046 
1047 
1048  /*
1049   * Skip leading whitespace...
1050   */
1051 
1052   for (cur = *ptr; *cur; cur ++)
1053   {
1054     if (*cur == '%')
1055     {
1056      /*
1057       * Comment, skip to end of line...
1058       */
1059 
1060       for (cur ++; *cur && *cur != '\n' && *cur != '\r'; cur ++);
1061 
1062       if (!*cur)
1063         cur --;
1064     }
1065     else if (!isspace(*cur & 255))
1066       break;
1067   }
1068 
1069   if (!*cur)
1070   {
1071     *ptr = NULL;
1072 
1073     return (NULL);
1074   }
1075 
1076  /*
1077   * See what we have...
1078   */
1079 
1080   memset(&obj, 0, sizeof(obj));
1081 
1082   switch (*cur)
1083   {
1084     case '(' :				/* (string) */
1085         obj.type = CUPS_PS_STRING;
1086 	start    = cur;
1087 
1088 	for (cur ++, parens = 1, valptr = obj.value.string,
1089 	         valend = obj.value.string + sizeof(obj.value.string) - 1;
1090              *cur;
1091 	     cur ++)
1092 	{
1093 	  if (*cur == ')' && parens == 1)
1094 	    break;
1095 
1096           if (*cur == '(')
1097 	    parens ++;
1098 	  else if (*cur == ')')
1099 	    parens --;
1100 
1101           if (valptr >= valend)
1102 	  {
1103 	    *ptr = start;
1104 
1105 	    return (NULL);
1106 	  }
1107 
1108 	  if (*cur == '\\')
1109 	  {
1110 	   /*
1111 	    * Decode escaped character...
1112 	    */
1113 
1114 	    cur ++;
1115 
1116             if (*cur == 'b')
1117 	      *valptr++ = '\b';
1118 	    else if (*cur == 'f')
1119 	      *valptr++ = '\f';
1120 	    else if (*cur == 'n')
1121 	      *valptr++ = '\n';
1122 	    else if (*cur == 'r')
1123 	      *valptr++ = '\r';
1124 	    else if (*cur == 't')
1125 	      *valptr++ = '\t';
1126 	    else if (*cur >= '0' && *cur <= '7')
1127 	    {
1128 	      int ch = *cur - '0';
1129 
1130               if (cur[1] >= '0' && cur[1] <= '7')
1131 	      {
1132 	        cur ++;
1133 		ch = (ch << 3) + *cur - '0';
1134 	      }
1135 
1136               if (cur[1] >= '0' && cur[1] <= '7')
1137 	      {
1138 	        cur ++;
1139 		ch = (ch << 3) + *cur - '0';
1140 	      }
1141 
1142 	      *valptr++ = (char)ch;
1143 	    }
1144 	    else if (*cur == '\r')
1145 	    {
1146 	      if (cur[1] == '\n')
1147 	        cur ++;
1148 	    }
1149 	    else if (*cur != '\n')
1150 	      *valptr++ = *cur;
1151 	  }
1152 	  else
1153 	    *valptr++ = *cur;
1154 	}
1155 
1156 	if (*cur != ')')
1157 	{
1158 	  *ptr = start;
1159 
1160 	  return (NULL);
1161 	}
1162 
1163 	cur ++;
1164         break;
1165 
1166     case '[' :				/* Start array */
1167         obj.type = CUPS_PS_START_ARRAY;
1168 	cur ++;
1169         break;
1170 
1171     case ']' :				/* End array */
1172         obj.type = CUPS_PS_END_ARRAY;
1173 	cur ++;
1174         break;
1175 
1176     case '<' :				/* Start dictionary or hex string */
1177         if (cur[1] == '<')
1178 	{
1179 	  obj.type = CUPS_PS_START_DICT;
1180 	  cur += 2;
1181 	}
1182 	else
1183 	{
1184           obj.type = CUPS_PS_STRING;
1185 	  start    = cur;
1186 
1187 	  for (cur ++, valptr = obj.value.string,
1188 	           valend = obj.value.string + sizeof(obj.value.string) - 1;
1189                *cur;
1190 	       cur ++)
1191 	  {
1192 	    int	ch;			/* Current character */
1193 
1194 
1195 
1196             if (*cur == '>')
1197 	      break;
1198 	    else if (valptr >= valend || !isxdigit(*cur & 255))
1199 	    {
1200 	      *ptr = start;
1201 	      return (NULL);
1202 	    }
1203 
1204             if (*cur >= '0' && *cur <= '9')
1205 	      ch = (*cur - '0') << 4;
1206 	    else
1207 	      ch = (tolower(*cur) - 'a' + 10) << 4;
1208 
1209 	    if (isxdigit(cur[1] & 255))
1210 	    {
1211 	      cur ++;
1212 
1213               if (*cur >= '0' && *cur <= '9')
1214 		ch |= *cur - '0';
1215 	      else
1216 		ch |= tolower(*cur) - 'a' + 10;
1217             }
1218 
1219 	    *valptr++ = (char)ch;
1220           }
1221 
1222           if (*cur != '>')
1223 	  {
1224 	    *ptr = start;
1225 	    return (NULL);
1226 	  }
1227 
1228 	  cur ++;
1229 	}
1230         break;
1231 
1232     case '>' :				/* End dictionary? */
1233         if (cur[1] == '>')
1234 	{
1235 	  obj.type = CUPS_PS_END_DICT;
1236 	  cur += 2;
1237 	}
1238 	else
1239 	{
1240 	  obj.type           = CUPS_PS_OTHER;
1241 	  obj.value.other[0] = *cur;
1242 
1243 	  cur ++;
1244 	}
1245         break;
1246 
1247     case '{' :				/* Start procedure */
1248         obj.type = CUPS_PS_START_PROC;
1249 	cur ++;
1250         break;
1251 
1252     case '}' :				/* End procedure */
1253         obj.type = CUPS_PS_END_PROC;
1254 	cur ++;
1255         break;
1256 
1257     case '-' :				/* Possible number */
1258     case '+' :
1259         if (!isdigit(cur[1] & 255) && cur[1] != '.')
1260 	{
1261 	  obj.type           = CUPS_PS_OTHER;
1262 	  obj.value.other[0] = *cur;
1263 
1264 	  cur ++;
1265 	  break;
1266 	}
1267 
1268     case '0' :				/* Number */
1269     case '1' :
1270     case '2' :
1271     case '3' :
1272     case '4' :
1273     case '5' :
1274     case '6' :
1275     case '7' :
1276     case '8' :
1277     case '9' :
1278     case '.' :
1279         obj.type = CUPS_PS_NUMBER;
1280 
1281         start = cur;
1282 	for (cur ++; *cur; cur ++)
1283 	  if (!isdigit(*cur & 255))
1284 	    break;
1285 
1286         if (*cur == '#')
1287 	{
1288 	 /*
1289 	  * Integer with radix...
1290 	  */
1291 
1292           obj.value.number = strtol(cur + 1, &cur, atoi(start));
1293 	  break;
1294 	}
1295 	else if (strchr(".Ee()<>[]{}/%", *cur) || isspace(*cur & 255))
1296 	{
1297 	 /*
1298 	  * Integer or real number...
1299 	  */
1300 
1301 	  obj.value.number = _cupsStrScand(start, &cur, localeconv());
1302           break;
1303 	}
1304 	else
1305 	  cur = start;
1306 
1307     default :				/* Operator/variable name */
1308         start = cur;
1309 
1310 	if (*cur == '/')
1311 	{
1312 	  obj.type = CUPS_PS_NAME;
1313           valptr   = obj.value.name;
1314           valend   = obj.value.name + sizeof(obj.value.name) - 1;
1315 	  cur ++;
1316 	}
1317 	else
1318 	{
1319 	  obj.type = CUPS_PS_OTHER;
1320           valptr   = obj.value.other;
1321           valend   = obj.value.other + sizeof(obj.value.other) - 1;
1322 	}
1323 
1324 	while (*cur)
1325 	{
1326 	  if (strchr("()<>[]{}/%", *cur) || isspace(*cur & 255))
1327 	    break;
1328 	  else if (valptr < valend)
1329 	    *valptr++ = *cur++;
1330 	  else
1331 	  {
1332 	    *ptr = start;
1333 	    return (NULL);
1334 	  }
1335 	}
1336 
1337         if (obj.type == CUPS_PS_OTHER)
1338 	{
1339           if (!strcmp(obj.value.other, "true"))
1340 	  {
1341 	    obj.type          = CUPS_PS_BOOLEAN;
1342 	    obj.value.boolean = 1;
1343 	  }
1344 	  else if (!strcmp(obj.value.other, "false"))
1345 	  {
1346 	    obj.type          = CUPS_PS_BOOLEAN;
1347 	    obj.value.boolean = 0;
1348 	  }
1349 	  else if (!strcmp(obj.value.other, "null"))
1350 	    obj.type = CUPS_PS_NULL;
1351 	  else if (!strcmp(obj.value.other, "cleartomark"))
1352 	    obj.type = CUPS_PS_CLEARTOMARK;
1353 	  else if (!strcmp(obj.value.other, "copy"))
1354 	    obj.type = CUPS_PS_COPY;
1355 	  else if (!strcmp(obj.value.other, "dup"))
1356 	    obj.type = CUPS_PS_DUP;
1357 	  else if (!strcmp(obj.value.other, "index"))
1358 	    obj.type = CUPS_PS_INDEX;
1359 	  else if (!strcmp(obj.value.other, "pop"))
1360 	    obj.type = CUPS_PS_POP;
1361 	  else if (!strcmp(obj.value.other, "roll"))
1362 	    obj.type = CUPS_PS_ROLL;
1363 	  else if (!strcmp(obj.value.other, "setpagedevice"))
1364 	    obj.type = CUPS_PS_SETPAGEDEVICE;
1365 	  else if (!strcmp(obj.value.other, "stopped"))
1366 	    obj.type = CUPS_PS_STOPPED;
1367 	}
1368 	break;
1369   }
1370 
1371  /*
1372   * Save the current position in the string and return the new object...
1373   */
1374 
1375   *ptr = cur;
1376 
1377   return (push_stack(st, &obj));
1378 }
1379 
1380 
1381 /*
1382  * 'setpagedevice()' - Simulate the PostScript setpagedevice operator.
1383  */
1384 
1385 static int				/* O - 0 on success, -1 on error */
setpagedevice(_cups_ps_stack_t * st,cups_page_header2_t * h,int * preferred_bits)1386 setpagedevice(
1387     _cups_ps_stack_t    *st,		/* I - Stack */
1388     cups_page_header2_t *h,		/* O - Page header */
1389     int                 *preferred_bits)/* O - Preferred bits per color */
1390 {
1391   int			i;		/* Index into array */
1392   _cups_ps_obj_t	*obj,		/* Current object */
1393 			*end;		/* End of dictionary */
1394   const char		*name;		/* Attribute name */
1395 
1396 
1397  /*
1398   * Make sure we have a dictionary on the stack...
1399   */
1400 
1401   if (st->num_objs == 0)
1402     return (-1);
1403 
1404   obj = end = st->objs + st->num_objs - 1;
1405 
1406   if (obj->type != CUPS_PS_END_DICT)
1407     return (-1);
1408 
1409   obj --;
1410 
1411   while (obj > st->objs)
1412   {
1413     if (obj->type == CUPS_PS_START_DICT)
1414       break;
1415 
1416     obj --;
1417   }
1418 
1419   if (obj < st->objs)
1420     return (-1);
1421 
1422  /*
1423   * Found the start of the dictionary, empty the stack to this point...
1424   */
1425 
1426   st->num_objs = (int)(obj - st->objs);
1427 
1428  /*
1429   * Now pull /name and value pairs from the dictionary...
1430   */
1431 
1432   DEBUG_puts("3setpagedevice: Dictionary:");
1433 
1434   for (obj ++; obj < end; obj ++)
1435   {
1436    /*
1437     * Grab the name...
1438     */
1439 
1440     if (obj->type != CUPS_PS_NAME)
1441       return (-1);
1442 
1443     name = obj->value.name;
1444     obj ++;
1445 
1446 #ifdef DEBUG
1447     DEBUG_printf(("4setpagedevice: /%s ", name));
1448     DEBUG_object("setpagedevice", obj);
1449 #endif /* DEBUG */
1450 
1451    /*
1452     * Then grab the value...
1453     */
1454 
1455     if (!strcmp(name, "MediaClass") && obj->type == CUPS_PS_STRING)
1456       strlcpy(h->MediaClass, obj->value.string, sizeof(h->MediaClass));
1457     else if (!strcmp(name, "MediaColor") && obj->type == CUPS_PS_STRING)
1458       strlcpy(h->MediaColor, obj->value.string, sizeof(h->MediaColor));
1459     else if (!strcmp(name, "MediaType") && obj->type == CUPS_PS_STRING)
1460       strlcpy(h->MediaType, obj->value.string, sizeof(h->MediaType));
1461     else if (!strcmp(name, "OutputType") && obj->type == CUPS_PS_STRING)
1462       strlcpy(h->OutputType, obj->value.string, sizeof(h->OutputType));
1463     else if (!strcmp(name, "AdvanceDistance") && obj->type == CUPS_PS_NUMBER)
1464       h->AdvanceDistance = (unsigned)obj->value.number;
1465     else if (!strcmp(name, "AdvanceMedia") && obj->type == CUPS_PS_NUMBER)
1466       h->AdvanceMedia = (unsigned)obj->value.number;
1467     else if (!strcmp(name, "Collate") && obj->type == CUPS_PS_BOOLEAN)
1468       h->Collate = (unsigned)obj->value.boolean;
1469     else if (!strcmp(name, "CutMedia") && obj->type == CUPS_PS_NUMBER)
1470       h->CutMedia = (cups_cut_t)(unsigned)obj->value.number;
1471     else if (!strcmp(name, "Duplex") && obj->type == CUPS_PS_BOOLEAN)
1472       h->Duplex = (unsigned)obj->value.boolean;
1473     else if (!strcmp(name, "HWResolution") && obj->type == CUPS_PS_START_ARRAY)
1474     {
1475       if (obj[1].type == CUPS_PS_NUMBER && obj[2].type == CUPS_PS_NUMBER &&
1476           obj[3].type == CUPS_PS_END_ARRAY)
1477       {
1478         h->HWResolution[0] = (unsigned)obj[1].value.number;
1479 	h->HWResolution[1] = (unsigned)obj[2].value.number;
1480 	obj += 3;
1481       }
1482       else
1483         return (-1);
1484     }
1485     else if (!strcmp(name, "InsertSheet") && obj->type == CUPS_PS_BOOLEAN)
1486       h->InsertSheet = (unsigned)obj->value.boolean;
1487     else if (!strcmp(name, "Jog") && obj->type == CUPS_PS_NUMBER)
1488       h->Jog = (unsigned)obj->value.number;
1489     else if (!strcmp(name, "LeadingEdge") && obj->type == CUPS_PS_NUMBER)
1490       h->LeadingEdge = (unsigned)obj->value.number;
1491     else if (!strcmp(name, "ManualFeed") && obj->type == CUPS_PS_BOOLEAN)
1492       h->ManualFeed = (unsigned)obj->value.boolean;
1493     else if ((!strcmp(name, "cupsMediaPosition") ||
1494               !strcmp(name, "MediaPosition")) && obj->type == CUPS_PS_NUMBER)
1495     {
1496      /*
1497       * cupsMediaPosition is supported for backwards compatibility only.
1498       * We added it back in the Ghostscript 5.50 days to work around a
1499       * bug in Ghostscript WRT handling of MediaPosition and setpagedevice.
1500       *
1501       * All new development should set MediaPosition...
1502       */
1503 
1504       h->MediaPosition = (unsigned)obj->value.number;
1505     }
1506     else if (!strcmp(name, "MediaWeight") && obj->type == CUPS_PS_NUMBER)
1507       h->MediaWeight = (unsigned)obj->value.number;
1508     else if (!strcmp(name, "MirrorPrint") && obj->type == CUPS_PS_BOOLEAN)
1509       h->MirrorPrint = (unsigned)obj->value.boolean;
1510     else if (!strcmp(name, "NegativePrint") && obj->type == CUPS_PS_BOOLEAN)
1511       h->NegativePrint = (unsigned)obj->value.boolean;
1512     else if (!strcmp(name, "NumCopies") && obj->type == CUPS_PS_NUMBER)
1513       h->NumCopies = (unsigned)obj->value.number;
1514     else if (!strcmp(name, "Orientation") && obj->type == CUPS_PS_NUMBER)
1515       h->Orientation = (unsigned)obj->value.number;
1516     else if (!strcmp(name, "OutputFaceUp") && obj->type == CUPS_PS_BOOLEAN)
1517       h->OutputFaceUp = (unsigned)obj->value.boolean;
1518     else if (!strcmp(name, "PageSize") && obj->type == CUPS_PS_START_ARRAY)
1519     {
1520       if (obj[1].type == CUPS_PS_NUMBER && obj[2].type == CUPS_PS_NUMBER &&
1521           obj[3].type == CUPS_PS_END_ARRAY)
1522       {
1523         h->cupsPageSize[0] = (float)obj[1].value.number;
1524 	h->cupsPageSize[1] = (float)obj[2].value.number;
1525 
1526         h->PageSize[0] = (unsigned)obj[1].value.number;
1527 	h->PageSize[1] = (unsigned)obj[2].value.number;
1528 
1529 	obj += 3;
1530       }
1531       else
1532         return (-1);
1533     }
1534     else if (!strcmp(name, "Separations") && obj->type == CUPS_PS_BOOLEAN)
1535       h->Separations = (unsigned)obj->value.boolean;
1536     else if (!strcmp(name, "TraySwitch") && obj->type == CUPS_PS_BOOLEAN)
1537       h->TraySwitch = (unsigned)obj->value.boolean;
1538     else if (!strcmp(name, "Tumble") && obj->type == CUPS_PS_BOOLEAN)
1539       h->Tumble = (unsigned)obj->value.boolean;
1540     else if (!strcmp(name, "cupsMediaType") && obj->type == CUPS_PS_NUMBER)
1541       h->cupsMediaType = (unsigned)obj->value.number;
1542     else if (!strcmp(name, "cupsBitsPerColor") && obj->type == CUPS_PS_NUMBER)
1543       h->cupsBitsPerColor = (unsigned)obj->value.number;
1544     else if (!strcmp(name, "cupsPreferredBitsPerColor") &&
1545              obj->type == CUPS_PS_NUMBER)
1546       *preferred_bits = (int)obj->value.number;
1547     else if (!strcmp(name, "cupsColorOrder") && obj->type == CUPS_PS_NUMBER)
1548       h->cupsColorOrder = (cups_order_t)(unsigned)obj->value.number;
1549     else if (!strcmp(name, "cupsColorSpace") && obj->type == CUPS_PS_NUMBER)
1550       h->cupsColorSpace = (cups_cspace_t)(unsigned)obj->value.number;
1551     else if (!strcmp(name, "cupsCompression") && obj->type == CUPS_PS_NUMBER)
1552       h->cupsCompression = (unsigned)obj->value.number;
1553     else if (!strcmp(name, "cupsRowCount") && obj->type == CUPS_PS_NUMBER)
1554       h->cupsRowCount = (unsigned)obj->value.number;
1555     else if (!strcmp(name, "cupsRowFeed") && obj->type == CUPS_PS_NUMBER)
1556       h->cupsRowFeed = (unsigned)obj->value.number;
1557     else if (!strcmp(name, "cupsRowStep") && obj->type == CUPS_PS_NUMBER)
1558       h->cupsRowStep = (unsigned)obj->value.number;
1559     else if (!strcmp(name, "cupsBorderlessScalingFactor") &&
1560              obj->type == CUPS_PS_NUMBER)
1561       h->cupsBorderlessScalingFactor = (float)obj->value.number;
1562     else if (!strncmp(name, "cupsInteger", 11) && obj->type == CUPS_PS_NUMBER)
1563     {
1564       if ((i = atoi(name + 11)) < 0 || i > 15)
1565         return (-1);
1566 
1567       h->cupsInteger[i] = (unsigned)obj->value.number;
1568     }
1569     else if (!strncmp(name, "cupsReal", 8) && obj->type == CUPS_PS_NUMBER)
1570     {
1571       if ((i = atoi(name + 8)) < 0 || i > 15)
1572         return (-1);
1573 
1574       h->cupsReal[i] = (float)obj->value.number;
1575     }
1576     else if (!strncmp(name, "cupsString", 10) && obj->type == CUPS_PS_STRING)
1577     {
1578       if ((i = atoi(name + 10)) < 0 || i > 15)
1579         return (-1);
1580 
1581       strlcpy(h->cupsString[i], obj->value.string, sizeof(h->cupsString[i]));
1582     }
1583     else if (!strcmp(name, "cupsMarkerType") && obj->type == CUPS_PS_STRING)
1584       strlcpy(h->cupsMarkerType, obj->value.string, sizeof(h->cupsMarkerType));
1585     else if (!strcmp(name, "cupsPageSizeName") && obj->type == CUPS_PS_STRING)
1586       strlcpy(h->cupsPageSizeName, obj->value.string,
1587               sizeof(h->cupsPageSizeName));
1588     else if (!strcmp(name, "cupsRenderingIntent") &&
1589              obj->type == CUPS_PS_STRING)
1590       strlcpy(h->cupsRenderingIntent, obj->value.string,
1591               sizeof(h->cupsRenderingIntent));
1592     else
1593     {
1594      /*
1595       * Ignore unknown name+value...
1596       */
1597 
1598       DEBUG_printf(("4setpagedevice: Unknown name (\"%s\") or value...\n", name));
1599 
1600       while (obj[1].type != CUPS_PS_NAME && obj < end)
1601         obj ++;
1602     }
1603   }
1604 
1605   return (0);
1606 }
1607 
1608 
1609 #ifdef DEBUG
1610 /*
1611  * 'DEBUG_object()' - Print an object's value...
1612  */
1613 
1614 static void
DEBUG_object(const char * prefix,_cups_ps_obj_t * obj)1615 DEBUG_object(const char *prefix,	/* I - Prefix string */
1616              _cups_ps_obj_t *obj)	/* I - Object to print */
1617 {
1618   switch (obj->type)
1619   {
1620     case CUPS_PS_NAME :
1621 	DEBUG_printf(("4%s: /%s\n", prefix, obj->value.name));
1622 	break;
1623 
1624     case CUPS_PS_NUMBER :
1625 	DEBUG_printf(("4%s: %g\n", prefix, obj->value.number));
1626 	break;
1627 
1628     case CUPS_PS_STRING :
1629 	DEBUG_printf(("4%s: (%s)\n", prefix, obj->value.string));
1630 	break;
1631 
1632     case CUPS_PS_BOOLEAN :
1633 	if (obj->value.boolean)
1634 	  DEBUG_printf(("4%s: true", prefix));
1635 	else
1636 	  DEBUG_printf(("4%s: false", prefix));
1637 	break;
1638 
1639     case CUPS_PS_NULL :
1640 	DEBUG_printf(("4%s: null", prefix));
1641 	break;
1642 
1643     case CUPS_PS_START_ARRAY :
1644 	DEBUG_printf(("4%s: [", prefix));
1645 	break;
1646 
1647     case CUPS_PS_END_ARRAY :
1648 	DEBUG_printf(("4%s: ]", prefix));
1649 	break;
1650 
1651     case CUPS_PS_START_DICT :
1652 	DEBUG_printf(("4%s: <<", prefix));
1653 	break;
1654 
1655     case CUPS_PS_END_DICT :
1656 	DEBUG_printf(("4%s: >>", prefix));
1657 	break;
1658 
1659     case CUPS_PS_START_PROC :
1660 	DEBUG_printf(("4%s: {", prefix));
1661 	break;
1662 
1663     case CUPS_PS_END_PROC :
1664 	DEBUG_printf(("4%s: }", prefix));
1665 	break;
1666 
1667     case CUPS_PS_CLEARTOMARK :
1668 	DEBUG_printf(("4%s: --cleartomark--", prefix));
1669         break;
1670 
1671     case CUPS_PS_COPY :
1672 	DEBUG_printf(("4%s: --copy--", prefix));
1673         break;
1674 
1675     case CUPS_PS_DUP :
1676 	DEBUG_printf(("4%s: --dup--", prefix));
1677         break;
1678 
1679     case CUPS_PS_INDEX :
1680 	DEBUG_printf(("4%s: --index--", prefix));
1681         break;
1682 
1683     case CUPS_PS_POP :
1684 	DEBUG_printf(("4%s: --pop--", prefix));
1685         break;
1686 
1687     case CUPS_PS_ROLL :
1688 	DEBUG_printf(("4%s: --roll--", prefix));
1689         break;
1690 
1691     case CUPS_PS_SETPAGEDEVICE :
1692 	DEBUG_printf(("4%s: --setpagedevice--", prefix));
1693         break;
1694 
1695     case CUPS_PS_STOPPED :
1696 	DEBUG_printf(("4%s: --stopped--", prefix));
1697         break;
1698 
1699     case CUPS_PS_OTHER :
1700 	DEBUG_printf(("4%s: --%s--", prefix, obj->value.other));
1701 	break;
1702   }
1703 }
1704 
1705 
1706 /*
1707  * 'DEBUG_stack()' - Print a stack...
1708  */
1709 
1710 static void
DEBUG_stack(const char * prefix,_cups_ps_stack_t * st)1711 DEBUG_stack(const char       *prefix,	/* I - Prefix string */
1712             _cups_ps_stack_t *st)	/* I - Stack */
1713 {
1714   int			c;		/* Looping var */
1715   _cups_ps_obj_t	*obj;		/* Current object on stack */
1716 
1717 
1718   for (obj = st->objs, c = st->num_objs; c > 0; c --, obj ++)
1719     DEBUG_object(prefix, obj);
1720 }
1721 #endif /* DEBUG */
1722