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