• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * PPD code emission routines for CUPS.
3  *
4  * Copyright © 2020-2024 by OpenPrinting.
5  * Copyright 2007-2019 by Apple Inc.
6  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
7  *
8  * Licensed under Apache License v2.0.  See the file "LICENSE" for more
9  * information.
10  *
11  * PostScript is a trademark of Adobe Systems, Inc.
12  */
13 
14 /*
15  * Include necessary headers...
16  */
17 
18 #include "cups-private.h"
19 #include "debug-internal.h"
20 #include "ppd.h"
21 #if defined(_WIN32) || defined(__EMX__)
22 #  include <io.h>
23 #else
24 #  include <unistd.h>
25 #endif /* _WIN32 || __EMX__ */
26 
27 
28 /*
29  * Local functions...
30  */
31 
32 static int	ppd_compare_cparams(ppd_cparam_t *a, ppd_cparam_t *b);
33 static void	ppd_handle_media(ppd_file_t *ppd);
34 
35 
36 /*
37  * Local globals...
38  */
39 
40 static const char ppd_custom_code[] =
41 		"pop pop pop\n"
42 		"<</PageSize[5 -2 roll]/ImagingBBox null>>setpagedevice\n";
43 
44 
45 /*
46  * 'ppdCollect()' - Collect all marked options that reside in the specified
47  *                  section.
48  *
49  * The choices array should be freed using @code free@ when you are
50  * finished with it.
51  */
52 
53 int					/* O - Number of options marked */
ppdCollect(ppd_file_t * ppd,ppd_section_t section,ppd_choice_t *** choices)54 ppdCollect(ppd_file_t    *ppd,		/* I - PPD file data */
55            ppd_section_t section,	/* I - Section to collect */
56            ppd_choice_t  ***choices)	/* O - Pointers to choices */
57 {
58   return (ppdCollect2(ppd, section, 0.0, choices));
59 }
60 
61 
62 /*
63  * 'ppdCollect2()' - Collect all marked options that reside in the
64  *                   specified section and minimum order.
65  *
66  * The choices array should be freed using @code free@ when you are
67  * finished with it.
68  *
69  * @since CUPS 1.2/macOS 10.5@
70  */
71 
72 int					/* O - Number of options marked */
ppdCollect2(ppd_file_t * ppd,ppd_section_t section,float min_order,ppd_choice_t *** choices)73 ppdCollect2(ppd_file_t    *ppd,		/* I - PPD file data */
74             ppd_section_t section,	/* I - Section to collect */
75 	    float         min_order,	/* I - Minimum OrderDependency value */
76             ppd_choice_t  ***choices)	/* O - Pointers to choices */
77 {
78   ppd_choice_t	*c;			/* Current choice */
79   ppd_section_t	csection;		/* Current section */
80   float		corder;			/* Current OrderDependency value */
81   int		count;			/* Number of choices collected */
82   ppd_choice_t	**collect;		/* Collected choices */
83   float		*orders;		/* Collected order values */
84 
85 
86   DEBUG_printf(("ppdCollect2(ppd=%p, section=%d, min_order=%f, choices=%p)",
87                 ppd, section, min_order, choices));
88 
89   if (!ppd || !choices)
90   {
91     if (choices)
92       *choices = NULL;
93 
94     return (0);
95   }
96 
97  /*
98   * Allocate memory for up to N selected choices...
99   */
100 
101   count = 0;
102   if ((collect = calloc((size_t)cupsArrayCount(ppd->marked),
103                         sizeof(ppd_choice_t *))) == NULL)
104   {
105     *choices = NULL;
106     return (0);
107   }
108 
109   if ((orders = calloc((size_t)cupsArrayCount(ppd->marked), sizeof(float))) == NULL)
110   {
111     *choices = NULL;
112     free(collect);
113     return (0);
114   }
115 
116  /*
117   * Loop through all options and add choices as needed...
118   */
119 
120   for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked);
121        c;
122        c = (ppd_choice_t *)cupsArrayNext(ppd->marked))
123   {
124     csection = c->option->section;
125     corder   = c->option->order;
126 
127     if (!strcmp(c->choice, "Custom"))
128     {
129       ppd_attr_t	*attr;		/* NonUIOrderDependency value */
130       float		aorder;		/* Order value */
131       char		asection[17],	/* Section name */
132 			amain[PPD_MAX_NAME + 1],
133 			aoption[PPD_MAX_NAME];
134 					/* *CustomFoo and True */
135 
136 
137       for (attr = ppdFindAttr(ppd, "NonUIOrderDependency", NULL);
138            attr;
139 	   attr = ppdFindNextAttr(ppd, "NonUIOrderDependency", NULL))
140         if (attr->value &&
141 	    sscanf(attr->value, "%f%16s%41s%40s", &aorder, asection, amain,
142 	           aoption) == 4 &&
143 	    !strncmp(amain, "*Custom", 7) &&
144 	    !strcmp(amain + 7, c->option->keyword) && !strcmp(aoption, "True"))
145 	{
146 	 /*
147 	  * Use this NonUIOrderDependency...
148 	  */
149 
150           corder = aorder;
151 
152 	  if (!strcmp(asection, "DocumentSetup"))
153 	    csection = PPD_ORDER_DOCUMENT;
154 	  else if (!strcmp(asection, "ExitServer"))
155 	    csection = PPD_ORDER_EXIT;
156 	  else if (!strcmp(asection, "JCLSetup"))
157 	    csection = PPD_ORDER_JCL;
158 	  else if (!strcmp(asection, "PageSetup"))
159 	    csection = PPD_ORDER_PAGE;
160 	  else if (!strcmp(asection, "Prolog"))
161 	    csection = PPD_ORDER_PROLOG;
162 	  else
163 	    csection = PPD_ORDER_ANY;
164 
165 	  break;
166 	}
167     }
168 
169     if (csection == section && corder >= min_order)
170     {
171       collect[count] = c;
172       orders[count]  = corder;
173       count ++;
174     }
175   }
176 
177  /*
178   * If we have more than 1 marked choice, sort them...
179   */
180 
181   if (count > 1)
182   {
183     int i, j;				/* Looping vars */
184 
185     for (i = 0; i < (count - 1); i ++)
186       for (j = i + 1; j < count; j ++)
187         if (orders[i] > orders[j])
188 	{
189 	  c          = collect[i];
190 	  corder     = orders[i];
191 	  collect[i] = collect[j];
192 	  orders[i]  = orders[j];
193 	  collect[j] = c;
194 	  orders[j]  = corder;
195 	}
196   }
197 
198   free(orders);
199 
200   DEBUG_printf(("2ppdCollect2: %d marked choices...", count));
201 
202  /*
203   * Return the array and number of choices; if 0, free the array since
204   * it isn't needed.
205   */
206 
207   if (count > 0)
208   {
209     *choices = collect;
210     return (count);
211   }
212   else
213   {
214     *choices = NULL;
215     free(collect);
216     return (0);
217   }
218 }
219 
220 
221 /*
222  * 'ppdEmit()' - Emit code for marked options to a file.
223  */
224 
225 int					/* O - 0 on success, -1 on failure */
ppdEmit(ppd_file_t * ppd,FILE * fp,ppd_section_t section)226 ppdEmit(ppd_file_t    *ppd,		/* I - PPD file record */
227         FILE          *fp,		/* I - File to write to */
228         ppd_section_t section)		/* I - Section to write */
229 {
230   return (ppdEmitAfterOrder(ppd, fp, section, 0, 0.0));
231 }
232 
233 
234 /*
235  * 'ppdEmitAfterOrder()' - Emit a subset of the code for marked options to a file.
236  *
237  * When "limit" is non-zero, this function only emits options whose
238  * OrderDependency value is greater than or equal to "min_order".
239  *
240  * When "limit" is zero, this function is identical to ppdEmit().
241  *
242  * @since CUPS 1.2/macOS 10.5@
243  */
244 
245 int					/* O - 0 on success, -1 on failure */
ppdEmitAfterOrder(ppd_file_t * ppd,FILE * fp,ppd_section_t section,int limit,float min_order)246 ppdEmitAfterOrder(
247     ppd_file_t    *ppd,			/* I - PPD file record */
248     FILE          *fp,			/* I - File to write to */
249     ppd_section_t section,		/* I - Section to write */
250     int		  limit,		/* I - Non-zero to use min_order */
251     float         min_order)		/* I - Lowest OrderDependency */
252 {
253   char	*buffer;			/* Option code */
254   int	status;				/* Return status */
255 
256 
257  /*
258   * Range check input...
259   */
260 
261   if (!ppd || !fp)
262     return (-1);
263 
264  /*
265   * Get the string...
266   */
267 
268   buffer = ppdEmitString(ppd, section, limit ? min_order : 0.0f);
269 
270  /*
271   * Write it as needed and return...
272   */
273 
274   if (buffer)
275   {
276     status = fputs(buffer, fp) < 0 ? -1 : 0;
277 
278     free(buffer);
279   }
280   else
281     status = 0;
282 
283   return (status);
284 }
285 
286 
287 /*
288  * 'ppdEmitFd()' - Emit code for marked options to a file.
289  */
290 
291 int					/* O - 0 on success, -1 on failure */
ppdEmitFd(ppd_file_t * ppd,int fd,ppd_section_t section)292 ppdEmitFd(ppd_file_t    *ppd,		/* I - PPD file record */
293           int           fd,		/* I - File to write to */
294           ppd_section_t section)	/* I - Section to write */
295 {
296   char		*buffer,		/* Option code */
297 		*bufptr;		/* Pointer into code */
298   size_t	buflength;		/* Length of option code */
299   ssize_t	bytes;			/* Bytes written */
300   int		status;			/* Return status */
301 
302 
303  /*
304   * Range check input...
305   */
306 
307   if (!ppd || fd < 0)
308     return (-1);
309 
310  /*
311   * Get the string...
312   */
313 
314   buffer = ppdEmitString(ppd, section, 0.0);
315 
316  /*
317   * Write it as needed and return...
318   */
319 
320   if (buffer)
321   {
322     buflength = strlen(buffer);
323     bufptr    = buffer;
324     bytes     = 0;
325 
326     while (buflength > 0)
327     {
328 #ifdef _WIN32
329       if ((bytes = (ssize_t)write(fd, bufptr, (unsigned)buflength)) < 0)
330 #else
331       if ((bytes = write(fd, bufptr, buflength)) < 0)
332 #endif /* _WIN32 */
333       {
334         if (errno == EAGAIN || errno == EINTR)
335 	  continue;
336 
337 	break;
338       }
339 
340       buflength -= (size_t)bytes;
341       bufptr    += bytes;
342     }
343 
344     status = bytes < 0 ? -1 : 0;
345 
346     free(buffer);
347   }
348   else
349     status = 0;
350 
351   return (status);
352 }
353 
354 
355 /*
356  * 'ppdEmitJCL()' - Emit code for JCL options to a file.
357  */
358 
359 int					/* O - 0 on success, -1 on failure */
ppdEmitJCL(ppd_file_t * ppd,FILE * fp,int job_id,const char * user,const char * title)360 ppdEmitJCL(ppd_file_t *ppd,		/* I - PPD file record */
361            FILE       *fp,		/* I - File to write to */
362            int        job_id,		/* I - Job ID */
363 	   const char *user,		/* I - Username */
364 	   const char *title)		/* I - Title */
365 {
366   char		*ptr;			/* Pointer into JCL string */
367   char		temp[65],		/* Local title string */
368 		displaymsg[33];		/* Local display string */
369 
370 
371  /*
372   * Range check the input...
373   */
374 
375   if (!ppd || !ppd->jcl_begin || !ppd->jcl_ps)
376     return (0);
377 
378  /*
379   * See if the printer supports HP PJL...
380   */
381 
382   if (!strncmp(ppd->jcl_begin, "\033%-12345X@", 10))
383   {
384    /*
385     * This printer uses HP PJL commands for output; filter the output
386     * so that we only have a single "@PJL JOB" command in the header...
387     *
388     * To avoid bugs in the PJL implementation of certain vendors' products
389     * (Xerox in particular), we add a dummy "@PJL" command at the beginning
390     * of the PJL commands to initialize PJL processing.
391     */
392 
393     ppd_attr_t	*charset;		/* PJL charset */
394     ppd_attr_t	*display;		/* PJL display command */
395 
396 
397     if ((charset = ppdFindAttr(ppd, "cupsPJLCharset", NULL)) != NULL)
398     {
399       if (!charset->value || _cups_strcasecmp(charset->value, "UTF-8"))
400         charset = NULL;
401     }
402 
403     if ((display = ppdFindAttr(ppd, "cupsPJLDisplay", NULL)) != NULL)
404     {
405       if (!display->value)
406         display = NULL;
407     }
408 
409     fputs("\033%-12345X@PJL\n", fp);
410     for (ptr = ppd->jcl_begin + 9; *ptr;)
411       if (!strncmp(ptr, "@PJL JOB", 8))
412       {
413        /*
414         * Skip job command...
415 	*/
416 
417         for (;*ptr; ptr ++)
418 	  if (*ptr == '\n')
419 	    break;
420 
421 	if (*ptr)
422 	  ptr ++;
423       }
424       else
425       {
426        /*
427         * Copy line...
428 	*/
429 
430         for (;*ptr; ptr ++)
431 	{
432 	  putc(*ptr, fp);
433 	  if (*ptr == '\n')
434 	    break;
435 	}
436 
437 	if (*ptr)
438 	  ptr ++;
439       }
440 
441    /*
442     * Clean up the job title...
443     */
444 
445     if (!title)
446       title = "Unknown";
447 
448     if ((ptr = strrchr(title, '/')) != NULL)
449     {
450      /*
451       * Only show basename of file path...
452       */
453 
454       title = ptr + 1;
455     }
456 
457     if (!strncmp(title, "smbprn.", 7))
458     {
459      /*
460       * Skip leading smbprn.######## from Samba jobs...
461       */
462 
463       for (title += 7; *title && isdigit(*title & 255); title ++);
464       while (_cups_isspace(*title))
465         title ++;
466 
467       if ((ptr = strstr(title, " - ")) != NULL)
468       {
469        /*
470 	* Skip application name in "Some Application - Title of job"...
471 	*/
472 
473 	title = ptr + 3;
474       }
475     }
476 
477    /*
478     * Replace double quotes with single quotes and UTF-8 characters with
479     * question marks so that the title does not cause a PJL syntax error.
480     */
481 
482     strlcpy(temp, title, sizeof(temp));
483 
484     for (ptr = temp; *ptr; ptr ++)
485       if (*ptr == '\"')
486         *ptr = '\'';
487       else if (!charset && (*ptr & 128))
488         *ptr = '?';
489 
490    /*
491     * CUPS STR #3125: Long PJL JOB NAME causes problems with some printers
492     *
493     * Generate the display message, truncating at 32 characters + nul to avoid
494     * issues with some printer's PJL implementations...
495     */
496 
497     if (!user)
498       user = "anonymous";
499 
500     snprintf(displaymsg, sizeof(displaymsg), "%d %s %s", job_id, user, temp);
501 
502    /*
503     * Send PJL JOB and PJL RDYMSG commands before we enter PostScript mode...
504     */
505 
506     if (display && strcmp(display->value, "job"))
507       fprintf(fp, "@PJL JOB NAME = \"%s\"\n", temp);
508     else if (display && !strcmp(display->value, "rdymsg"))
509       fprintf(fp, "@PJL RDYMSG DISPLAY = \"%s\"\n", displaymsg);
510     else
511       fprintf(fp, "@PJL JOB NAME = \"%s\" DISPLAY = \"%s\"\n", temp,
512 	      displaymsg);
513 
514    /*
515     * Replace double quotes with single quotes and UTF-8 characters with
516     * question marks so that the user does not cause a PJL syntax error.
517     */
518 
519     strlcpy(temp, user, sizeof(temp));
520 
521     for (ptr = temp; *ptr; ptr ++)
522       if (*ptr == '\"')
523         *ptr = '\'';
524       else if (!charset && (*ptr & 128))
525         *ptr = '?';
526 
527     fprintf(fp, "@PJL SET USERNAME = \"%s\"\n", temp);
528   }
529   else
530     fputs(ppd->jcl_begin, fp);
531 
532   ppdEmit(ppd, fp, PPD_ORDER_JCL);
533   fputs(ppd->jcl_ps, fp);
534 
535   return (0);
536 }
537 
538 
539 /*
540  * 'ppdEmitJCLEnd()' - Emit JCLEnd code to a file.
541  *
542  * @since CUPS 1.2/macOS 10.5@
543  */
544 
545 int					/* O - 0 on success, -1 on failure */
ppdEmitJCLEnd(ppd_file_t * ppd,FILE * fp)546 ppdEmitJCLEnd(ppd_file_t *ppd,		/* I - PPD file record */
547               FILE       *fp)		/* I - File to write to */
548 {
549  /*
550   * Range check the input...
551   */
552 
553   if (!ppd)
554     return (0);
555 
556   if (!ppd->jcl_end)
557   {
558     if (ppd->num_filters == 0)
559       putc(0x04, fp);
560 
561     return (0);
562   }
563 
564  /*
565   * See if the printer supports HP PJL...
566   */
567 
568   if (!strncmp(ppd->jcl_end, "\033%-12345X@", 10))
569   {
570    /*
571     * This printer uses HP PJL commands for output; filter the output
572     * so that we only have a single "@PJL JOB" command in the header...
573     *
574     * To avoid bugs in the PJL implementation of certain vendors' products
575     * (Xerox in particular), we add a dummy "@PJL" command at the beginning
576     * of the PJL commands to initialize PJL processing.
577     */
578 
579     fputs("\033%-12345X@PJL\n", fp);
580     fputs("@PJL RDYMSG DISPLAY = \"\"\n", fp);
581     fputs(ppd->jcl_end + 9, fp);
582   }
583   else
584     fputs(ppd->jcl_end, fp);
585 
586   return (0);
587 }
588 
589 
590 /*
591  * 'ppdEmitString()' - Get a string containing the code for marked options.
592  *
593  * When "min_order" is greater than zero, this function only includes options
594  * whose OrderDependency value is greater than or equal to "min_order".
595  * Otherwise, all options in the specified section are included in the
596  * returned string.
597  *
598  * The return string is allocated on the heap and should be freed using
599  * @code free@ when you are done with it.
600  *
601  * @since CUPS 1.2/macOS 10.5@
602  */
603 
604 char *					/* O - String containing option code or @code NULL@ if there is no option code */
ppdEmitString(ppd_file_t * ppd,ppd_section_t section,float min_order)605 ppdEmitString(ppd_file_t    *ppd,	/* I - PPD file record */
606               ppd_section_t section,	/* I - Section to write */
607 	      float         min_order)	/* I - Lowest OrderDependency */
608 {
609   int		i, j,			/* Looping vars */
610 		count;			/* Number of choices */
611   ppd_choice_t	**choices;		/* Choices */
612   ppd_size_t	*size;			/* Custom page size */
613   ppd_coption_t	*coption;		/* Custom option */
614   ppd_cparam_t	*cparam;		/* Custom parameter */
615   size_t	bufsize;		/* Size of string buffer needed */
616   char		*buffer,		/* String buffer */
617 		*bufptr,		/* Pointer into buffer */
618 		*bufend;		/* End of buffer */
619   struct lconv	*loc;			/* Locale data */
620 
621 
622   DEBUG_printf(("ppdEmitString(ppd=%p, section=%d, min_order=%f)",
623                 ppd, section, min_order));
624 
625  /*
626   * Range check input...
627   */
628 
629   if (!ppd)
630     return (NULL);
631 
632  /*
633   * Use PageSize or PageRegion as required...
634   */
635 
636   ppd_handle_media(ppd);
637 
638  /*
639   * Collect the options we need to emit...
640   */
641 
642   if ((count = ppdCollect2(ppd, section, min_order, &choices)) == 0)
643     return (NULL);
644 
645  /*
646   * Count the number of bytes that are required to hold all of the
647   * option code...
648   */
649 
650   for (i = 0, bufsize = 1; i < count; i ++)
651   {
652     if (section == PPD_ORDER_JCL)
653     {
654       if (!_cups_strcasecmp(choices[i]->choice, "Custom") &&
655 	  (coption = ppdFindCustomOption(ppd, choices[i]->option->keyword))
656 	      != NULL)
657       {
658        /*
659         * Add space to account for custom parameter substitution...
660 	*/
661 
662         for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
663 	     cparam;
664 	     cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
665 	{
666           switch (cparam->type)
667 	  {
668 	    case PPD_CUSTOM_UNKNOWN :
669 	        break;
670 
671 	    case PPD_CUSTOM_CURVE :
672 	    case PPD_CUSTOM_INVCURVE :
673 	    case PPD_CUSTOM_POINTS :
674 	    case PPD_CUSTOM_REAL :
675 	    case PPD_CUSTOM_INT :
676 	        bufsize += 10;
677 	        break;
678 
679 	    case PPD_CUSTOM_PASSCODE :
680 	    case PPD_CUSTOM_PASSWORD :
681 	    case PPD_CUSTOM_STRING :
682 	        if (cparam->current.custom_string)
683 		  bufsize += strlen(cparam->current.custom_string);
684 	        break;
685           }
686 	}
687       }
688     }
689     else if (section != PPD_ORDER_EXIT)
690     {
691       bufsize += 3;			/* [{\n */
692 
693       if ((!_cups_strcasecmp(choices[i]->option->keyword, "PageSize") ||
694            !_cups_strcasecmp(choices[i]->option->keyword, "PageRegion")) &&
695           !_cups_strcasecmp(choices[i]->choice, "Custom"))
696       {
697         DEBUG_puts("2ppdEmitString: Custom size set!");
698 
699         bufsize += 37;			/* %%BeginFeature: *CustomPageSize True\n */
700         bufsize += 50;			/* Five 9-digit numbers + newline */
701       }
702       else if (!_cups_strcasecmp(choices[i]->choice, "Custom") &&
703                (coption = ppdFindCustomOption(ppd,
704 	                                      choices[i]->option->keyword))
705 	           != NULL)
706       {
707         bufsize += 23 + strlen(choices[i]->option->keyword) + 6;
708 					/* %%BeginFeature: *Customkeyword True\n */
709 
710 
711         for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
712 	     cparam;
713 	     cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
714 	{
715           switch (cparam->type)
716 	  {
717 	    case PPD_CUSTOM_UNKNOWN :
718 	        break;
719 
720 	    case PPD_CUSTOM_CURVE :
721 	    case PPD_CUSTOM_INVCURVE :
722 	    case PPD_CUSTOM_POINTS :
723 	    case PPD_CUSTOM_REAL :
724 	    case PPD_CUSTOM_INT :
725 	        bufsize += 10;
726 	        break;
727 
728 	    case PPD_CUSTOM_PASSCODE :
729 	    case PPD_CUSTOM_PASSWORD :
730 	    case PPD_CUSTOM_STRING :
731 		bufsize += 3;
732 	        if (cparam->current.custom_string)
733 		  bufsize += 4 * strlen(cparam->current.custom_string);
734 	        break;
735           }
736 	}
737       }
738       else
739         bufsize += 17 + strlen(choices[i]->option->keyword) + 1 +
740 	           strlen(choices[i]->choice) + 1;
741 					/* %%BeginFeature: *keyword choice\n */
742 
743       bufsize += 13;			/* %%EndFeature\n */
744       bufsize += 22;			/* } stopped cleartomark\n */
745     }
746 
747     if (choices[i]->code)
748       bufsize += strlen(choices[i]->code) + 1;
749     else
750       bufsize += strlen(ppd_custom_code);
751   }
752 
753  /*
754   * Allocate memory...
755   */
756 
757   DEBUG_printf(("2ppdEmitString: Allocating %d bytes for string...",
758                 (int)bufsize));
759 
760   if ((buffer = calloc(1, bufsize)) == NULL)
761   {
762     free(choices);
763     return (NULL);
764   }
765 
766   bufend = buffer + bufsize - 1;
767   loc    = localeconv();
768 
769  /*
770   * Copy the option code to the buffer...
771   */
772 
773   for (i = 0, bufptr = buffer; i < count; i ++, bufptr += strlen(bufptr))
774     if (section == PPD_ORDER_JCL)
775     {
776       if (!_cups_strcasecmp(choices[i]->choice, "Custom") &&
777 	  choices[i]->code &&
778           (coption = ppdFindCustomOption(ppd, choices[i]->option->keyword))
779 	      != NULL)
780       {
781        /*
782         * Handle substitutions in custom JCL options...
783 	*/
784 
785 	char	*cptr;			/* Pointer into code */
786 	int	pnum;			/* Parameter number */
787 
788 
789         for (cptr = choices[i]->code; *cptr && bufptr < bufend;)
790 	{
791 	  if (*cptr == '\\')
792 	  {
793 	    cptr ++;
794 
795 	    if (isdigit(*cptr & 255))
796 	    {
797 	     /*
798 	      * Substitute parameter...
799 	      */
800 
801               pnum = *cptr++ - '0';
802 	      while (isdigit(*cptr & 255))
803 	        pnum = pnum * 10 + *cptr++ - '0';
804 
805               for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
806 	           cparam;
807 		   cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
808 		if (cparam->order == pnum)
809 		  break;
810 
811               if (cparam)
812 	      {
813 	        switch (cparam->type)
814 		{
815 		  case PPD_CUSTOM_UNKNOWN :
816 		      break;
817 
818 		  case PPD_CUSTOM_CURVE :
819 		  case PPD_CUSTOM_INVCURVE :
820 		  case PPD_CUSTOM_POINTS :
821 		  case PPD_CUSTOM_REAL :
822 		      bufptr = _cupsStrFormatd(bufptr, bufend,
823 					       cparam->current.custom_real,
824 					       loc);
825 		      break;
826 
827 		  case PPD_CUSTOM_INT :
828 		      snprintf(bufptr, (size_t)(bufend - bufptr), "%d", cparam->current.custom_int);
829 		      bufptr += strlen(bufptr);
830 		      break;
831 
832 		  case PPD_CUSTOM_PASSCODE :
833 		  case PPD_CUSTOM_PASSWORD :
834 		  case PPD_CUSTOM_STRING :
835 		      if (cparam->current.custom_string)
836 		      {
837 			strlcpy(bufptr, cparam->current.custom_string, (size_t)(bufend - bufptr));
838 			bufptr += strlen(bufptr);
839 		      }
840 		      break;
841 		}
842 	      }
843 	    }
844 	    else if (*cptr)
845 	      *bufptr++ = *cptr++;
846 	  }
847 	  else
848 	    *bufptr++ = *cptr++;
849 	}
850       }
851       else if (choices[i]->code)
852       {
853        /*
854         * Otherwise just copy the option code directly...
855 	*/
856 
857         strlcpy(bufptr, choices[i]->code, (size_t)(bufend - bufptr + 1));
858         bufptr += strlen(bufptr);
859       }
860     }
861     else if (section != PPD_ORDER_EXIT)
862     {
863      /*
864       * Add wrapper commands to prevent printer errors for unsupported
865       * options...
866       */
867 
868       strlcpy(bufptr, "[{\n", (size_t)(bufend - bufptr + 1));
869       bufptr += 3;
870 
871      /*
872       * Send DSC comments with option...
873       */
874 
875       DEBUG_printf(("2ppdEmitString: Adding code for %s=%s...",
876 		    choices[i]->option->keyword, choices[i]->choice));
877 
878       if ((!_cups_strcasecmp(choices[i]->option->keyword, "PageSize") ||
879            !_cups_strcasecmp(choices[i]->option->keyword, "PageRegion")) &&
880           !_cups_strcasecmp(choices[i]->choice, "Custom"))
881       {
882        /*
883         * Variable size; write out standard size options, using the
884 	* parameter positions defined in the PPD file...
885 	*/
886 
887         ppd_attr_t	*attr;		/* PPD attribute */
888 	int		pos,		/* Position of custom value */
889 			orientation;	/* Orientation to use */
890 	float		values[5];	/* Values for custom command */
891 
892 
893         strlcpy(bufptr, "%%BeginFeature: *CustomPageSize True\n", (size_t)(bufend - bufptr + 1));
894         bufptr += 37;
895 
896         if ((size = ppdPageSize(ppd, "Custom")) == NULL)
897         {
898           free(buffer);
899           free(choices);
900           return (NULL);
901         }
902 
903         memset(values, 0, sizeof(values));
904 
905 	if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", "Width")) != NULL)
906 	{
907 	  pos = atoi(attr->value) - 1;
908 
909           if (pos < 0 || pos > 4)
910 	    pos = 0;
911 	}
912 	else
913 	  pos = 0;
914 
915 	values[pos] = size->width;
916 
917 	if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize", "Height")) != NULL)
918 	{
919 	  pos = atoi(attr->value) - 1;
920 
921           if (pos < 0 || pos > 4)
922 	    pos = 1;
923 	}
924 	else
925 	  pos = 1;
926 
927 	values[pos] = size->length;
928 
929        /*
930         * According to the Adobe PPD specification, an orientation of 1
931 	* will produce a print that comes out upside-down with the X
932 	* axis perpendicular to the direction of feed, which is exactly
933 	* what we want to be consistent with non-PS printers.
934 	*
935 	* We could also use an orientation of 3 to produce output that
936 	* comes out rightside-up (this is the default for many large format
937 	* printer PPDs), however for consistency we will stick with the
938 	* value 1.
939 	*
940 	* If we wanted to get fancy, we could use orientations of 0 or
941 	* 2 and swap the width and length, however we don't want to get
942 	* fancy, we just want it to work consistently.
943 	*
944 	* The orientation value is range limited by the Orientation
945 	* parameter definition, so certain non-PS printer drivers that
946 	* only support an Orientation of 0 will get the value 0 as
947 	* expected.
948 	*/
949 
950         orientation = 1;
951 
952 	if ((attr = ppdFindAttr(ppd, "ParamCustomPageSize",
953 	                        "Orientation")) != NULL)
954 	{
955 	  int min_orient, max_orient;	/* Minimum and maximum orientations */
956 
957 
958           if (sscanf(attr->value, "%d%*s%d%d", &pos, &min_orient,
959 	             &max_orient) != 3)
960 	    pos = 4;
961 	  else
962 	  {
963 	    pos --;
964 
965             if (pos < 0 || pos > 4)
966 	      pos = 4;
967 
968             if (orientation > max_orient)
969 	      orientation = max_orient;
970 	    else if (orientation < min_orient)
971 	      orientation = min_orient;
972 	  }
973 	}
974 	else
975 	  pos = 4;
976 
977 	values[pos] = (float)orientation;
978 
979         for (pos = 0; pos < 5; pos ++)
980 	{
981 	  bufptr    = _cupsStrFormatd(bufptr, bufend, values[pos], loc);
982 	  *bufptr++ = '\n';
983         }
984 
985 	if (!choices[i]->code)
986 	{
987 	 /*
988 	  * This can happen with certain buggy PPD files that don't include
989 	  * a CustomPageSize command sequence...  We just use a generic
990 	  * Level 2 command sequence...
991 	  */
992 
993 	  strlcpy(bufptr, ppd_custom_code, (size_t)(bufend - bufptr + 1));
994           bufptr += strlen(bufptr);
995 	}
996       }
997       else if (!_cups_strcasecmp(choices[i]->choice, "Custom") &&
998                (coption = ppdFindCustomOption(ppd, choices[i]->option->keyword))
999 	           != NULL)
1000       {
1001        /*
1002         * Custom option...
1003 	*/
1004 
1005         const char	*s;		/* Pointer into string value */
1006         cups_array_t	*params;	/* Parameters in the correct output order */
1007 
1008 
1009         params = cupsArrayNew((cups_array_func_t)ppd_compare_cparams, NULL);
1010 
1011         for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
1012 	     cparam;
1013 	     cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
1014           cupsArrayAdd(params, cparam);
1015 
1016         snprintf(bufptr, (size_t)(bufend - bufptr + 1), "%%%%BeginFeature: *Custom%s True\n", coption->keyword);
1017         bufptr += strlen(bufptr);
1018 
1019         for (cparam = (ppd_cparam_t *)cupsArrayFirst(params);
1020 	     cparam;
1021 	     cparam = (ppd_cparam_t *)cupsArrayNext(params))
1022 	{
1023           switch (cparam->type)
1024 	  {
1025 	    case PPD_CUSTOM_UNKNOWN :
1026 	        break;
1027 
1028 	    case PPD_CUSTOM_CURVE :
1029 	    case PPD_CUSTOM_INVCURVE :
1030 	    case PPD_CUSTOM_POINTS :
1031 	    case PPD_CUSTOM_REAL :
1032 	        bufptr    = _cupsStrFormatd(bufptr, bufend,
1033 		                            cparam->current.custom_real, loc);
1034                 *bufptr++ = '\n';
1035 	        break;
1036 
1037 	    case PPD_CUSTOM_INT :
1038 	        snprintf(bufptr, (size_t)(bufend - bufptr + 1), "%d\n", cparam->current.custom_int);
1039 		bufptr += strlen(bufptr);
1040 	        break;
1041 
1042 	    case PPD_CUSTOM_PASSCODE :
1043 	    case PPD_CUSTOM_PASSWORD :
1044 	    case PPD_CUSTOM_STRING :
1045 	        *bufptr++ = '(';
1046 
1047 		if (cparam->current.custom_string)
1048 		{
1049 		  for (s = cparam->current.custom_string; *s; s ++)
1050 		  {
1051 		    if (*s < ' ' || *s == '(' || *s == ')' || *s >= 127)
1052 		    {
1053 		      snprintf(bufptr, (size_t)(bufend - bufptr + 1), "\\%03o", *s & 255);
1054 		      bufptr += strlen(bufptr);
1055 		    }
1056 		    else
1057 		      *bufptr++ = *s;
1058 		  }
1059 		}
1060 
1061 	        *bufptr++ = ')';
1062 		*bufptr++ = '\n';
1063 	        break;
1064           }
1065 	}
1066 
1067 	cupsArrayDelete(params);
1068       }
1069       else
1070       {
1071         snprintf(bufptr, (size_t)(bufend - bufptr + 1), "%%%%BeginFeature: *%s %s\n", choices[i]->option->keyword, choices[i]->choice);
1072 	bufptr += strlen(bufptr);
1073       }
1074 
1075       if (choices[i]->code && choices[i]->code[0])
1076       {
1077         j = (int)strlen(choices[i]->code);
1078 	memcpy(bufptr, choices[i]->code, (size_t)j);
1079 	bufptr += j;
1080 
1081 	if (choices[i]->code[j - 1] != '\n')
1082 	  *bufptr++ = '\n';
1083       }
1084 
1085       strlcpy(bufptr, "%%EndFeature\n"
1086 		      "} stopped cleartomark\n", (size_t)(bufend - bufptr + 1));
1087       bufptr += strlen(bufptr);
1088 
1089       DEBUG_printf(("2ppdEmitString: Offset in string is %d...",
1090                     (int)(bufptr - buffer)));
1091     }
1092     else if (choices[i]->code)
1093     {
1094       strlcpy(bufptr, choices[i]->code, (size_t)(bufend - bufptr + 1));
1095       bufptr += strlen(bufptr);
1096     }
1097 
1098  /*
1099   * Nul-terminate, free, and return...
1100   */
1101 
1102   *bufptr = '\0';
1103 
1104   free(choices);
1105 
1106   return (buffer);
1107 }
1108 
1109 
1110 /*
1111  * 'ppd_compare_cparams()' - Compare the order of two custom parameters.
1112  */
1113 
1114 static int				/* O - Result of comparison */
ppd_compare_cparams(ppd_cparam_t * a,ppd_cparam_t * b)1115 ppd_compare_cparams(ppd_cparam_t *a,	/* I - First parameter */
1116                     ppd_cparam_t *b)	/* I - Second parameter */
1117 {
1118   return (a->order - b->order);
1119 }
1120 
1121 
1122 /*
1123  * 'ppd_handle_media()' - Handle media selection...
1124  */
1125 
1126 static void
ppd_handle_media(ppd_file_t * ppd)1127 ppd_handle_media(ppd_file_t *ppd)	/* I - PPD file */
1128 {
1129   ppd_choice_t	*manual_feed,		/* ManualFeed choice, if any */
1130 		*input_slot;		/* InputSlot choice, if any */
1131   ppd_size_t	*size;			/* Current media size */
1132   ppd_attr_t	*rpr;			/* RequiresPageRegion value */
1133 
1134 
1135  /*
1136   * This function determines what page size code to use, if any, for the
1137   * current media size, InputSlot, and ManualFeed selections.
1138   *
1139   * We use the PageSize code if:
1140   *
1141   * 1. A custom media size is selected.
1142   * 2. ManualFeed and InputSlot are not selected (or do not exist).
1143   * 3. ManualFeed is selected but is False and InputSlot is not selected or
1144   *    the selection has no code - the latter check done to support "auto" or
1145   *    "printer default" InputSlot options.
1146   *
1147   * We use the PageRegion code if:
1148   *
1149   * 4. RequiresPageRegion does not exist and the PPD contains cupsFilter
1150   *    keywords, indicating this is a CUPS-based driver.
1151   * 5. RequiresPageRegion exists for the selected InputSlot (or "All" for any
1152   *    InputSlot or ManualFeed selection) and is True.
1153   *
1154   * If none of the 5 conditions are true, no page size code is used and we
1155   * unmark any existing PageSize or PageRegion choices.
1156   */
1157 
1158   if ((size = ppdPageSize(ppd, NULL)) == NULL)
1159     return;
1160 
1161   manual_feed = ppdFindMarkedChoice(ppd, "ManualFeed");
1162   input_slot  = ppdFindMarkedChoice(ppd, "InputSlot");
1163 
1164   if (input_slot != NULL)
1165     rpr = ppdFindAttr(ppd, "RequiresPageRegion", input_slot->choice);
1166   else
1167     rpr = NULL;
1168 
1169   if (!rpr)
1170     rpr = ppdFindAttr(ppd, "RequiresPageRegion", "All");
1171 
1172   if (!_cups_strcasecmp(size->name, "Custom") ||
1173       (!manual_feed && !input_slot) ||
1174       (manual_feed && !_cups_strcasecmp(manual_feed->choice, "False") &&
1175        (!input_slot || (input_slot->code && !input_slot->code[0]))) ||
1176       (!rpr && ppd->num_filters > 0))
1177   {
1178    /*
1179     * Use PageSize code...
1180     */
1181 
1182     ppdMarkOption(ppd, "PageSize", size->name);
1183   }
1184   else if (rpr && rpr->value && !_cups_strcasecmp(rpr->value, "True"))
1185   {
1186    /*
1187     * Use PageRegion code...
1188     */
1189 
1190     ppdMarkOption(ppd, "PageRegion", size->name);
1191   }
1192   else
1193   {
1194    /*
1195     * Do not use PageSize or PageRegion code...
1196     */
1197 
1198     ppd_choice_t	*page;		/* PageSize/Region choice, if any */
1199 
1200     if ((page = ppdFindMarkedChoice(ppd, "PageSize")) != NULL)
1201     {
1202      /*
1203       * Unmark PageSize...
1204       */
1205 
1206       page->marked = 0;
1207       cupsArrayRemove(ppd->marked, page);
1208     }
1209 
1210     if ((page = ppdFindMarkedChoice(ppd, "PageRegion")) != NULL)
1211     {
1212      /*
1213       * Unmark PageRegion...
1214       */
1215 
1216       page->marked = 0;
1217       cupsArrayRemove(ppd->marked, page);
1218     }
1219   }
1220 }
1221