"Fossies" - the Fresh Open Source Software Archive

Member "cups-2.3rc1/cups/ppd-emit.c" (21 May 2019, 29336 Bytes) of package /linux/misc/cups-2.3rc1-source.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ppd-emit.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 2.2.11_vs_2.3b8.

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