"Fossies" - the Fresh Open Source Software Archive

Member "foomatic-filters-4.0-20160212/options.c" (12 Feb 2016, 69927 Bytes) of package /linux/misc/foomatic-filters-4.0-20160212.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 "options.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 4.0.17_vs_4.0-20160212.

    1 /* options.c
    2  *
    3  * Copyright (C) 2008 Till Kamppeter <till.kamppeter@gmail.com>
    4  * Copyright (C) 2008 Lars Uebernickel <larsuebernickel@gmx.de>
    5  *
    6  * This file is part of foomatic-rip.
    7  *
    8  * Foomatic-rip is free software; you can redistribute it and/or modify
    9  * it under the terms of the GNU General Public License as published by
   10  * the Free Software Foundation; either version 2 of the License, or
   11  * (at your option) any later version.
   12  *
   13  * Foomatic-rip is distributed in the hope that it will be useful, but
   14  * WITHOUT ANY WARRANTY; without even the implied warranty of
   15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   16  * General Public License for more details.
   17  *
   18  * You should have received a copy of the GNU Lesser General Public
   19  * License along with this library; if not, write to the
   20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   21  * Boston, MA 02111-1307, USA.
   22  */
   23 
   24 #include "foomaticrip.h"
   25 #include "options.h"
   26 #include "util.h"
   27 #include <stdlib.h>
   28 #include <ctype.h>
   29 #include <assert.h>
   30 #include <regex.h>
   31 #include <string.h>
   32 #include <math.h>
   33 
   34 /* qualifier -> filename mapping entry */
   35 typedef struct icc_mapping_entry_s {
   36     char *qualifier;
   37     char *filename;
   38 } icc_mapping_entry_t;
   39 
   40 /* Values from foomatic keywords in the ppd file */
   41 char printer_model [256];
   42 char printer_id [256];
   43 char driver [128];
   44 char cmd [4096];
   45 char cmd_pdf [4096];
   46 dstr_t *postpipe = NULL;  /* command into which the output of this
   47                              filter should be piped */
   48 int ps_accounting = 1;
   49 char cupsfilter [256];
   50 int jobentitymaxlen = 0;
   51 int userentitymaxlen = 0;
   52 int hostentitymaxlen = 0;
   53 int titleentitymaxlen = 0;
   54 int optionsentitymaxlen = 0;
   55 
   56 /* JCL prefix to put before the JCL options
   57  (Can be modified by a "*JCLBegin:" keyword in the ppd file): */
   58 char jclbegin[256] = "\033%-12345X@PJL\n";
   59 
   60 /* JCL command to switch the printer to the PostScript interpreter
   61  (Can be modified by a "*JCLToPSInterpreter:" keyword in the PPD file): */
   62 char jcltointerpreter[256] = "";
   63 
   64 /* JCL command to close a print job
   65  (Can be modified by a "*JCLEnd:" keyword in the PPD file): */
   66 char jclend[256] = "\033%-12345X@PJL RESET\n";
   67 
   68 /* Prefix for starting every JCL command
   69  (Can be modified by "*FoomaticJCLPrefix:" keyword in the PPD file): */
   70 char jclprefix[256] = "@PJL ";
   71 int jclprefixset = 0;
   72 
   73 dstr_t *prologprepend;
   74 dstr_t *setupprepend;
   75 dstr_t *pagesetupprepend;
   76 
   77 
   78 list_t *qualifier_data = NULL;
   79 char **qualifier = NULL;
   80 
   81 option_t *optionlist = NULL;
   82 option_t *optionlist_sorted_by_order = NULL;
   83 
   84 int optionset_alloc, optionset_count;
   85 char **optionsets;
   86 
   87 
   88 const char * get_icc_profile_for_qualifier(const char **qualifier)
   89 {
   90     char tmp[1024];
   91     char *profile = NULL;
   92     listitem_t *i;
   93     icc_mapping_entry_t *entry;
   94 
   95     /* no data */
   96     if (qualifier_data == NULL)
   97         goto out;
   98 
   99     /* search list for qualifier */
  100     snprintf(tmp, sizeof(tmp), "%s.%s.%s",
  101              qualifier[0], qualifier[1], qualifier[2]);
  102     for (i = qualifier_data->first; i != NULL; i = i->next) {
  103         entry = (icc_mapping_entry_t *) i->data;
  104         if (strcmp(entry->qualifier, tmp) == 0) {
  105             profile = entry->filename;
  106             break;
  107         }
  108     }
  109 out:
  110     return profile;
  111 }
  112 
  113 /* a selector is a general tri-dotted specification.
  114  * The 2nd and 3rd elements of the qualifier are optionally modified by
  115  * cupsICCQualifier2 and cupsICCQualifier3:
  116  *
  117  * [Colorspace].[{cupsICCQualifier2}].[{cupsICCQualifier3}]
  118  */
  119 const char **
  120 get_ppd_qualifier ()
  121 {
  122   return (const char**) qualifier;
  123 }
  124 
  125 const char * type_name(int type)
  126 {
  127     switch (type) {
  128         case TYPE_NONE:
  129             return "none";
  130         case TYPE_ENUM:
  131             return "enum";
  132         case TYPE_PICKMANY:
  133             return "pickmany";
  134         case TYPE_BOOL:
  135             return "bool";
  136         case TYPE_INT:
  137             return "int";
  138         case TYPE_FLOAT:
  139             return "float";
  140         case TYPE_STRING:
  141             return "string";
  142     };
  143     _log("type '%d' does not exist\n", type);
  144     return NULL;
  145 }
  146 
  147 int type_from_string(const char *typestr)
  148 {
  149     int type = TYPE_NONE;
  150 
  151     /* Official PPD options */
  152     if (!strcmp(typestr, "PickOne"))
  153         type = TYPE_ENUM;
  154     else if (!strcmp(typestr, "PickMany"))
  155         type = TYPE_PICKMANY;
  156     else if (!strcmp(typestr, "Boolean"))
  157         type = TYPE_BOOL;
  158 
  159     /* FoomaticRIPOption */
  160     else if (strcasecmp(typestr, "enum") == 0)
  161         type = TYPE_ENUM;
  162     else if (strcasecmp(typestr, "pickmany") == 0)
  163         type = TYPE_PICKMANY;
  164     else if (strcasecmp(typestr, "bool") == 0)
  165         type = TYPE_BOOL;
  166     else if (strcasecmp(typestr, "int") == 0)
  167         type = TYPE_INT;
  168     else if (strcasecmp(typestr, "float") == 0)
  169         type = TYPE_FLOAT;
  170     else if (strcasecmp(typestr, "string") == 0)
  171         type = TYPE_STRING;
  172     else if (strcasecmp(typestr, "password") == 0)
  173         type = TYPE_PASSWORD;
  174 
  175     return type;
  176 }
  177 
  178 char style_from_string(const char *style)
  179 {
  180     char r = '\0';
  181     if (strcmp(style, "PS") == 0)
  182         r = 'G';
  183     else if (strcmp(style, "CmdLine") == 0)
  184         r = 'C';
  185     else if (strcmp(style, "JCL") == 0)
  186         r = 'J';
  187     else if (strcmp(style, "Composite") == 0)
  188         r = 'X';
  189     return r;
  190 }
  191 
  192 int section_from_string(const char *value)
  193 {
  194     if (!strcasecmp(value, "AnySetup"))
  195         return SECTION_ANYSETUP;
  196     else if (!strcasecmp(value, "PageSetup"))
  197         return SECTION_PAGESETUP;
  198     else if (!strcasecmp(value, "Prolog"))
  199         return SECTION_PROLOG;
  200     else if (!strcasecmp(value, "DocumentSetup"))
  201         return SECTION_DOCUMENTSETUP;
  202     else if (!strcasecmp(value, "JCLSetup"))
  203         return SECTION_JCLSETUP;
  204 
  205     _log("Unknown section: \"%s\"\n", value);
  206     return 0;
  207 }
  208 
  209 void options_init()
  210 {
  211     optionset_alloc = 8;
  212     optionset_count = 0;
  213     optionsets = calloc(optionset_alloc, sizeof(char *));
  214 
  215     prologprepend = create_dstr();
  216     setupprepend = create_dstr();
  217     pagesetupprepend = create_dstr();
  218 }
  219 
  220 static void free_param(param_t *param)
  221 {
  222     if (param->allowedchars) {
  223         regfree(param->allowedchars);
  224         free(param->allowedchars);
  225     }
  226 
  227     if (param->allowedregexp) {
  228         regfree(param->allowedregexp);
  229         free(param->allowedregexp);
  230     }
  231 
  232     free(param);
  233 }
  234 
  235 /*
  236  *  Values
  237  */
  238 
  239 static void free_value(value_t *val)
  240 {
  241     if (val->value)
  242         free(val->value);
  243     free(val);
  244 }
  245 
  246 
  247 /*
  248  *  Options
  249  */
  250 static void free_option(option_t *opt)
  251 {
  252     choice_t *choice;
  253     param_t *param;
  254     value_t *value;
  255 
  256     free(opt->custom_command);
  257     free(opt->proto);
  258 
  259     while (opt->valuelist) {
  260         value = opt->valuelist;
  261         opt->valuelist = opt->valuelist->next;
  262         free_value(value);
  263     }
  264     while (opt->choicelist) {
  265         choice = opt->choicelist;
  266         opt->choicelist = opt->choicelist->next;
  267         free(choice);
  268     }
  269     while (opt->paramlist) {
  270         param = opt->paramlist;
  271         opt->paramlist = opt->paramlist->next;
  272         free_param(param);
  273     }
  274     if (opt->foomatic_param)
  275         free_param(opt->foomatic_param);
  276 
  277     free(opt);
  278 }
  279 
  280 void options_free()
  281 {
  282     option_t *opt;
  283     int i;
  284     listitem_t *item;
  285     icc_mapping_entry_t *entry;
  286 
  287     for (i = 0; i < optionset_count; i++)
  288         free(optionsets[i]);
  289     free(optionsets);
  290     optionsets = NULL;
  291     optionset_alloc = 0;
  292     optionset_count = 0;
  293 
  294     if (qualifier_data) {
  295         for (item = qualifier_data->first; item != NULL; item = item->next) {
  296             entry = (icc_mapping_entry_t *) item->data;
  297             free(entry->qualifier);
  298             free(entry->filename);
  299             free(entry);
  300         }
  301         list_free(qualifier_data);
  302     }
  303 
  304     for (i=0; i<3; i++)
  305       free(qualifier[i]);
  306     free(qualifier);
  307 
  308     while (optionlist) {
  309         opt = optionlist;
  310         optionlist = optionlist->next;
  311         free_option(opt);
  312     }
  313 
  314     if (postpipe)
  315         free_dstr(postpipe);
  316 
  317     free_dstr(prologprepend);
  318     free_dstr(setupprepend);
  319     free_dstr(pagesetupprepend);
  320 }
  321 
  322 size_t option_count()
  323 {
  324     option_t *opt;
  325     size_t cnt = 0;
  326 
  327     for (opt = optionlist; opt; opt = opt->next)
  328         cnt++;
  329     return cnt;
  330 }
  331 
  332 option_t * find_option(const char *name)
  333 {
  334     option_t *opt;
  335 
  336     /* PageRegion and PageSize are the same options, just store one of them */
  337     if (!strcasecmp(name, "PageRegion"))
  338         return find_option("PageSize");
  339 
  340     for (opt = optionlist; opt; opt = opt->next) {
  341       if ((!strcasecmp(opt->name, name)) ||
  342       ((!strcasecmp(opt->name, &name[2])) &&
  343        (!prefixcasecmp(name, "no"))))
  344             return opt;
  345     }
  346     return NULL;
  347 }
  348 
  349 option_t * assure_option(const char *name)
  350 {
  351     option_t *opt, *last;
  352 
  353     if ((opt = find_option(name)))
  354         return opt;
  355 
  356     opt = calloc(1, sizeof(option_t));
  357 
  358     /* PageRegion and PageSize are the same options, just store one of them */
  359     if (!strcmp(name, "PageRegion"))
  360         strlcpy(opt->name, "PageSize", 128);
  361     else
  362         strlcpy(opt->name, name, 128);
  363 
  364     /* set varname */
  365     strcpy(opt->varname, opt->name);
  366     strrepl(opt->varname, "-/.", '_');
  367 
  368     /* Default execution style is 'G' (PostScript) since all arguments for
  369     which we don't find "*Foomatic..." keywords are usual PostScript options */
  370     opt->style = 'G';
  371 
  372     opt->type = TYPE_NONE;
  373 
  374     /* append opt to optionlist */
  375     if (optionlist) {
  376         for (last = optionlist; last->next; last = last->next);
  377         last->next = opt;
  378     }
  379     else
  380         optionlist = opt;
  381 
  382     /* prepend opt to optionlist_sorted_by_order
  383        (0 is always at the beginning) */
  384     if (optionlist_sorted_by_order) {
  385         opt->next_by_order = optionlist_sorted_by_order;
  386         optionlist_sorted_by_order = opt;
  387     }
  388     else {
  389         optionlist_sorted_by_order = opt;
  390     }
  391 
  392     _log("Added option %s\n", opt->name);
  393     return opt;
  394 }
  395 
  396 /* This functions checks if "opt" is named "name", or if it has any
  397    alternative names "name" (e.g. PageSize / PageRegion) */
  398 int option_has_name(option_t *opt, const char *name)
  399 {
  400     if (!strcmp(opt->name, name))
  401         return 1;
  402 
  403     if (!strcmp(opt->name, "PageSize") && !strcmp(name, "PageRegion"))
  404         return 1;
  405 
  406     return 0;
  407 }
  408 
  409 int option_is_composite(option_t *opt)
  410 {
  411     return opt ? (opt->style == 'X') : 0;
  412 }
  413 
  414 int option_is_ps_command(option_t *opt)
  415 {
  416     return opt->style == 'G';
  417 }
  418 
  419 int option_is_jcl_arg(option_t *opt)
  420 {
  421     return opt->style == 'J';
  422 }
  423 
  424 int option_is_commandline_arg(option_t *opt)
  425 {
  426     return opt->style == 'C';
  427 }
  428 
  429 int option_get_section(option_t *opt)
  430 {
  431     return opt->section;
  432 }
  433 
  434 static value_t * option_find_value(option_t *opt, int optionset)
  435 {
  436     value_t *val;
  437 
  438     if (!opt)
  439         return NULL;
  440 
  441     for (val = opt->valuelist; val; val = val->next) {
  442         if (val->optionset == optionset)
  443             return val;
  444     }
  445     return NULL;
  446 }
  447 
  448 static value_t * option_assure_value(option_t *opt, int optionset)
  449 {
  450     value_t *val, *last;
  451     val = option_find_value(opt, optionset);
  452     if (!val) {
  453         val = calloc(1, sizeof(value_t));
  454         val->optionset = optionset;
  455 
  456         /* append to opt->valuelist */
  457         if (opt->valuelist) {
  458             for (last = opt->valuelist; last->next; last = last->next);
  459             last->next = val;
  460         }
  461         else
  462             opt->valuelist = val;
  463     }
  464     return val;
  465 }
  466 
  467 static param_t * option_find_param_index(option_t *opt, const char *name, int *idx)
  468 {
  469     param_t *param;
  470     int i;
  471     for (param = opt->paramlist, i = 0; param; param = param->next, i += 1) {
  472         if (!strcasecmp(param->name, name)) {
  473             if (idx)
  474                 *idx = i;
  475             return param;
  476         }
  477     }
  478     if (idx)
  479         *idx = -1;
  480     return 0;
  481 }
  482 
  483 static choice_t * option_find_choice(option_t *opt, const char *name)
  484 {
  485     choice_t *choice;
  486     assert(opt && name);
  487     for (choice = opt->choicelist; choice; choice = choice->next) {
  488         if (!strcasecmp(choice->value, name))
  489             return choice;
  490     }
  491     return NULL;
  492 }
  493 
  494 void free_paramvalues(option_t *opt, char **paramvalues)
  495 {
  496     int i;
  497     if (!paramvalues)
  498         return;
  499     for (i = 0; i < opt->param_count; i++)
  500         free(paramvalues[i]);
  501     free(paramvalues);
  502 }
  503 
  504 char * get_valid_param_string(option_t *opt, param_t *param, const char *str)
  505 {
  506     char *result;
  507     int i, imin, imax;
  508     float f, fmin, fmax;
  509     size_t len;
  510 
  511     switch (param->type) {
  512         case TYPE_INT:
  513             i = atoi(str);
  514             imin = !isempty(param->min) ? atoi(param->min) : -999999;
  515             imax = !isempty(param->max) ? atoi(param->max) : 1000000;
  516             if (i < imin) {
  517                 _log("Value \"%s\" for option \"%s\", parameter \"%s\" is smaller than the minimum value \"%d\"\n",
  518                      str, opt->name, param->name, imin);
  519                 return NULL;
  520             }
  521             else if (i > imax) {
  522                 _log("Value \"%s\" for option \"%s\", parameter \"%s\" is larger than the maximum value \"%d\"\n",
  523                      str, opt->name, param->name, imax);
  524                 return NULL;
  525             }
  526             result = malloc(32);
  527             snprintf(result, 32, "%d", i);
  528             return result;
  529 
  530         case TYPE_FLOAT:
  531         case TYPE_CURVE:
  532         case TYPE_INVCURVE:
  533         case TYPE_POINTS:
  534             f = atof(str);
  535             fmin = !isempty(param->min) ? atof(param->min) : -999999.0;
  536             fmax = !isempty(param->max) ? atof(param->max) : 1000000.0;
  537             if (f < fmin) {
  538                 _log("Value \"%s\" for option \"%s\", parameter \"%s\" is smaller than the minimum value \"%d\"\n",
  539                      str, opt->name, param->name, fmin);
  540                 return NULL;
  541             }
  542             else if (f > fmax) {
  543                 _log("Value \"%s\" for option \"%s\", parameter \"%s\" is larger than the maximum value \"%d\"\n",
  544                      str, opt->name, param->name, fmax);
  545                 return NULL;
  546              }
  547             result = malloc(32);
  548             snprintf(result, 32, "%f", f);
  549             return result;
  550 
  551         case TYPE_STRING:
  552         case TYPE_PASSWORD:
  553         case TYPE_PASSCODE:
  554             if (param->allowedchars &&
  555                     regexec(param->allowedchars, str, 0, NULL, 0) != 0) {
  556                 _log("Custom string \"%s\" for \"%s\", parameter \"%s\" contains illegal characters.\n",
  557                     str, opt->name, param->name);
  558                 return NULL;
  559             }
  560             if (param->allowedregexp &&
  561                     regexec(param->allowedregexp, str, 0, NULL, 0) != 0) {
  562                 _log("Custom string \"%s\" for \"%s\", parameter \"%s\" does not match the allowed regexp.\n",
  563                     str, opt->name, param->name);
  564                 return NULL;
  565             }
  566             len = strlen(str);
  567             if (!isempty(param->min) && len < atoi(param->min)) {
  568                 _log("Custom value \"%s\" is too short for option \"%s\", parameter \"%s\".\n",
  569                     str, opt->name, param->name);
  570                 return NULL;
  571             }
  572             if (!isempty(param->max) && len > atoi(param->max)) {
  573                 _log("Custom value \"%s\" is too long for option \"%s\", parameter \"%s\".\n",
  574                     str, opt->name, param->name);
  575                 return NULL;
  576             }
  577             return strdup(str);
  578     }
  579     return NULL;
  580 }
  581 
  582 char * get_valid_param_string_int(option_t *opt, param_t *param, int value)
  583 {
  584     char str[20];
  585     snprintf(str, 20, "%d", value);
  586     return get_valid_param_string(opt, param, str);
  587 }
  588 
  589 char * get_valid_param_string_float(option_t *opt, param_t *param, float value)
  590 {
  591     char str[20];
  592     snprintf(str, 20, "%f", value);
  593     return get_valid_param_string(opt, param, str);
  594 }
  595 
  596 float convert_to_points(float f, const char *unit)
  597 {
  598     if (!strcasecmp(unit, "pt"))
  599         return roundf(f);
  600     if (!strcasecmp(unit, "in"))
  601         return roundf(f * 72.0);
  602     if (!strcasecmp(unit, "cm"))
  603         return roundf(f * 72.0 / 2.54);
  604     if (!strcasecmp(unit, "mm"))
  605         return roundf(f * 72.0 / 25.4);
  606 
  607     _log("Unknown unit: \"%s\"\n", unit);
  608     return roundf(f);
  609 }
  610 
  611 static char ** paramvalues_from_string(option_t *opt, const char *str)
  612 {
  613     char ** paramvalues;
  614     int n, i;
  615     param_t *param;
  616     char *copy, *cur, *p;
  617     float width, height;
  618     char unit[3];
  619 
  620     if (!strcmp(opt->name, "PageSize"))
  621     {
  622         if (startswith(str, "Custom."))
  623             str = &str[7];
  624         /* 'unit' is optional, if it is not given, 'pt' is assumed */
  625         n = sscanf(str, "%fx%f%2s", &width, &height, unit);
  626         if (n > 1) {
  627             if (n == 3) {
  628                 width = convert_to_points(width, unit);
  629                 height = convert_to_points(height, unit);
  630             }
  631             paramvalues = calloc(opt->param_count, sizeof(char*));
  632             for (param = opt->paramlist, i = 0; param; param = param->next, i++) {
  633                 if (!strcasecmp(param->name, "width"))
  634                     paramvalues[i] = get_valid_param_string_int(opt, param, (int)width);
  635                 else if (!strcasecmp(param->name, "height"))
  636                     paramvalues[i] = get_valid_param_string_int(opt, param, (int)height);
  637                 else
  638                     paramvalues[i] = !isempty(param->min) ? param->min : "-999999";
  639                 if (!paramvalues[i]) {
  640                     free_paramvalues(opt, paramvalues);
  641                     return NULL;
  642                 }
  643             }
  644             return paramvalues;
  645         }
  646     }
  647 
  648     if (opt->param_count == 1) {
  649         paramvalues = malloc(sizeof(char*));
  650         paramvalues[0] = get_valid_param_string(opt, opt->paramlist,
  651             startswith(str, "Custom.") ? &str[7] : str);
  652         if (!paramvalues[0]) {
  653             free(paramvalues);
  654             return NULL;
  655         }
  656     }
  657     else {
  658         if (!(p = strchr(str, '{')))
  659             return NULL;
  660         paramvalues = calloc(opt->param_count, sizeof(char*));
  661         copy = strdup(p +1);
  662         for (cur = strtok(copy, " \t}"); cur; cur = strtok(NULL, " \t}")) {
  663             p = strchr(cur, '=');
  664             if (!p)
  665                 continue;
  666             *p++ = '\0';
  667             if ((param = option_find_param_index(opt, cur, &i)))
  668                 paramvalues[i] = get_valid_param_string(opt, param, p);
  669             else
  670                 _log("Could not find param \"%s\" for option \"%s\"\n",
  671                     cur, opt->name);
  672         }
  673         free(copy);
  674 
  675         /* check if all params have been set */
  676         for (i = 0; i < opt->param_count; i++) {
  677             if (!paramvalues[i]) {
  678                 free_paramvalues(opt, paramvalues);
  679                 return NULL;
  680             }
  681         }
  682     }
  683     return paramvalues;
  684 }
  685 
  686 char * paramvalues_to_string(option_t *opt, char **paramvalues)
  687 {
  688     int i;
  689     param_t *param;
  690     dstr_t *res = create_dstr();
  691     char *data;
  692 
  693     if (opt->param_count < 1) {
  694         free (res);
  695         return NULL;
  696     }
  697 
  698     if (opt->param_count == 1) {
  699         param = opt->paramlist;
  700         dstrcpyf(res, "Custom.%s", paramvalues[0]);
  701     }
  702     else {
  703         dstrcpyf(res, "{%s=%s", opt->paramlist->name, paramvalues[0]);
  704         param = opt->paramlist->next;
  705         i = 1;
  706         while (param) {
  707             dstrcatf(res, " %s=%s", param->name, paramvalues[i]);
  708             i++;
  709             param = param->next;
  710         }
  711         dstrcat(res, "}");
  712     }
  713     /* only free dstr struct, NOT the string data */
  714     data = res->data;
  715     free(res);
  716     return data;
  717 }
  718 
  719 char * get_valid_value_string(option_t *opt, const char *value)
  720 {
  721     char *res;
  722     choice_t *choice;
  723     char **paramvalues;
  724 
  725     if (!value)
  726         return NULL;
  727 
  728     if (startswith(value, "From") && option_is_composite(find_option(&value[4])))
  729         return strdup(value);
  730 
  731     if (opt->type == TYPE_BOOL) {
  732         if (is_true_string(value))
  733             return strdup("1");
  734         else if (is_false_string(value))
  735             return strdup("0");
  736         else {
  737             _log("Could not interpret \"%s\" as boolean value for option \"%s\".\n", value, opt->name);
  738             return NULL;
  739         }
  740     }
  741 
  742     /* Check if "value" is a predefined choice (except for "Custom", which is
  743      * not really a predefined choice, but an error if used without further
  744      * parameters) */
  745     if ((strcmp(value, "Custom") != 0 || strcmp(opt->name, "PageSize") == 0) &&
  746     (choice = option_find_choice(opt, value)))
  747         return strdup(choice->value);
  748 
  749     if (opt->type == TYPE_ENUM) {
  750         if (!strcasecmp(value, "none"))
  751             return strdup("None");
  752 
  753         /*
  754          * CUPS assumes that options with the choices "Yes", "No", "On", "Off",
  755          * "True", or "False" are boolean options and maps "-o Option=On" to
  756          * "-o Option" and "-o Option=Off" to "-o noOption", which foomatic-rip
  757          * maps to "0" and "1".  So when "0" or "1" is unavailable in the
  758          * option, we try "Yes", "No", "On", "Off", "True", and "False". 
  759          */
  760         if (is_true_string(value)) {
  761             for (choice = opt->choicelist; choice; choice = choice->next) {
  762                 if (is_true_string(choice->value))
  763                     return strdup(choice->value);
  764             }
  765         }
  766         else if (is_false_string(value)) {
  767             for (choice = opt->choicelist; choice; choice = choice->next) {
  768                 if (is_false_string(choice->value))
  769                     return strdup(choice->value);
  770             }
  771         }
  772     }
  773 
  774     /* Custom value */
  775     if (opt->paramlist) {
  776         paramvalues = paramvalues_from_string(opt, value);
  777         if (paramvalues) {
  778             res = paramvalues_to_string(opt, paramvalues);
  779             free(paramvalues);
  780             return (startswith(res, "Custom.") ? strdup(&res[7]) : strdup(res));
  781         }
  782     }
  783     else if (opt->foomatic_param)
  784         return get_valid_param_string(opt, opt->foomatic_param,
  785                               startswith(value, "Custom.") ? &value[7] : value);
  786 
  787     /* Return the default value */
  788     return NULL;
  789 }
  790 
  791 /* Returns the current value for 'opt' in 'optionset'. */
  792 const char * option_get_value(option_t *opt, int optionset)
  793 {
  794     value_t *val = option_find_value(opt, optionset);
  795     return val ? val->value : NULL;
  796 }
  797 
  798 /* Returns non-zero if the foomatic prototype should be used for that
  799  * optionset, otherwise the custom_command will be used */
  800 int option_use_foomatic_prototype(option_t *opt)
  801 {
  802     /* Only PostScript and JCL options can be CUPS custom options */
  803     if (!option_is_ps_command(opt) && !option_is_jcl_arg(opt))
  804         return 1;
  805 
  806     /* if only one of them exists, take that one */
  807     if (opt->custom_command && !opt->proto)
  808         return 0;
  809     if (!opt->custom_command && opt->proto)
  810         return 1;
  811     return 0;
  812 }
  813 
  814 void build_foomatic_custom_command(dstr_t *cmd, option_t *opt, const char *values)
  815 {
  816     if (!opt->proto && !strcmp(opt->name, "PageSize"))
  817     {
  818         choice_t *choice = option_find_choice(opt, "Custom");
  819         char ** paramvalues = paramvalues_from_string(opt, values);
  820         char width[30], height[30];
  821         int pos;
  822 
  823         assert(choice);
  824 
  825         /* Get rid of the trailing ".00000", it confuses ghostscript */
  826         snprintf(width, 20, "%d", atoi(paramvalues[0]));
  827         snprintf(height, 20, "%d", atoi(paramvalues[1]));
  828 
  829         dstrcpy(cmd, choice->command);
  830 
  831         if ((pos = dstrreplace(cmd, "%0", width, 0)) < 0)
  832             pos = dstrreplace(cmd, "0", width, 0);
  833 
  834         if (dstrreplace(cmd, "%1", height, pos) < 0)
  835             dstrreplace(cmd, "0", height, pos);
  836 
  837         free_paramvalues(opt, paramvalues);
  838     }
  839     else
  840     {
  841         dstrcpy(cmd, opt->proto);
  842         /* use replace instead of printf-style because opt->proto could contain
  843            other format strings */
  844         dstrreplace(cmd, "%s", values, 0);
  845     }
  846 }
  847 
  848 void build_cups_custom_ps_command(dstr_t *cmd, option_t *opt, const char *values)
  849 {
  850     param_t *param;
  851     int i;
  852     char **paramvalues = paramvalues_from_string(opt, values);
  853 
  854     dstrclear(cmd);
  855     for (param = opt->paramlist, i = 0; param; param = param->next, i++)
  856         dstrcatf(cmd, "%s ", paramvalues[i]);
  857     dstrcat(cmd, opt->custom_command);
  858     free_paramvalues(opt, paramvalues);
  859 }
  860 
  861 void build_cups_custom_jcl_command(dstr_t *cmd, option_t *opt, const char *values)
  862 {
  863     param_t *param;
  864     int i;
  865     char orderstr[8];
  866     char **paramvalues = paramvalues_from_string(opt, values);
  867 
  868     dstrcpy(cmd, opt->custom_command);
  869     for (param = opt->paramlist, i = 0; param; param = param->next, i++) {
  870         snprintf(orderstr, 8, "\\%d", param->order);
  871         dstrreplace(cmd, orderstr, paramvalues[i], 0);
  872     }
  873     free_paramvalues(opt, paramvalues);
  874 }
  875 
  876 int composite_get_command(dstr_t *cmd, option_t *opt, int optionset, int section)
  877 {
  878     char *copy, *cur, *p;
  879     option_t *dep;
  880     const char * valstr;
  881     dstr_t *depcmd;
  882 
  883     dstrclear(cmd);
  884     if (!option_is_composite(opt))
  885         return 0;
  886 
  887     if (!(valstr = option_get_value(opt, optionset)))
  888         return 0;
  889 
  890     depcmd = create_dstr();
  891     copy = strdup(valstr);
  892     /* Dependent options have been set to the right value in composite_set_values,
  893        so just find out which options depend on this composite and get their commands
  894        for "optionset" with option_get_command() */
  895     for (cur = strtok(copy, " \t"); cur; cur = strtok(NULL, " \t")) {
  896         dstrclear(depcmd);
  897         if ((p = strchr(cur, '='))) {
  898             *p++ = '\0';
  899             if ((dep = find_option(cur)))
  900                 option_get_command(depcmd, dep, optionset, section);
  901         }
  902         else if (startswith(cur, "no") || startswith(cur, "No")) {
  903             if ((dep = find_option(&cur[2])))
  904                 option_get_command(depcmd, dep, optionset, section);
  905             }
  906         else {
  907             if ((dep = find_option(cur)))
  908                 option_get_command(depcmd, dep, optionset, section);
  909         }
  910         if (depcmd->len)
  911             dstrcatf(cmd, "%s\n", depcmd->data);
  912     }
  913     free(copy);
  914     free_dstr(depcmd);
  915     return cmd->len != 0;
  916 }
  917 
  918 int option_is_in_section(option_t *opt, int section)
  919 {
  920     if (opt->section == section)
  921         return 1;
  922     if (opt->section == SECTION_ANYSETUP && (section == SECTION_PAGESETUP || section == SECTION_DOCUMENTSETUP))
  923         return 1;
  924     return 0;
  925 }
  926 
  927 int option_is_custom_value(option_t *opt, const char *value)
  928 {
  929     if (opt->type == TYPE_BOOL || opt->type == TYPE_ENUM)
  930         return 0;
  931 
  932     return !option_has_choice(opt, value);
  933 }
  934 
  935 int option_get_command(dstr_t *cmd, option_t *opt, int optionset, int section)
  936 {
  937     const char *valstr;
  938     choice_t *choice = NULL;
  939 
  940     dstrclear(cmd);
  941 
  942     if (option_is_composite(opt))
  943         return composite_get_command(cmd, opt, optionset, section);
  944 
  945     if (section >= 0 && !option_is_in_section(opt, section))
  946         return 1; /* empty command for this section */
  947 
  948     valstr = option_get_value(opt, optionset);
  949     if (!valstr)
  950         return 0;
  951 
  952     /* If the value is set to a predefined choice */
  953     choice = option_find_choice(opt, valstr);
  954     if (choice && (*choice->command ||
  955            ((opt->type != TYPE_INT) && (opt->type != TYPE_FLOAT)))) {
  956         dstrcpy(cmd, choice->command);
  957         return 1;
  958     }
  959 
  960     /* Consider "None" as the empty string for string and password options */
  961     if ((opt->type == TYPE_STRING || opt->type == TYPE_PASSWORD) &&
  962     !strcasecmp(valstr, "None"))
  963         valstr = "";
  964 
  965     /* Custom value */
  966     if (option_use_foomatic_prototype(opt))
  967     build_foomatic_custom_command(cmd, opt, valstr);
  968     else {
  969     dstrcpy(cmd, opt->custom_command);
  970     if ((option_get_section(opt) == SECTION_JCLSETUP) ||
  971         (opt->style == 'J'))
  972         build_cups_custom_jcl_command(cmd, opt, valstr);
  973     else
  974       build_cups_custom_ps_command(cmd, opt, valstr);
  975     }
  976 
  977     return cmd->len != 0;
  978 }
  979 
  980 void composite_set_values(option_t *opt, int optionset, const char *values)
  981 {
  982     char *copy, *cur, *p;
  983     option_t *dep;
  984     value_t *val;
  985 
  986     copy = strdup(values);
  987     for (cur = strtok(copy, " \t"); cur; cur = strtok(NULL, " \t")) {
  988         if ((p = strchr(cur, '='))) {
  989             *p++ = '\0';
  990             if ((dep = find_option(cur))) {
  991                 val = option_assure_value(dep, optionset);
  992                 val->fromoption = opt;
  993                 val->value = get_valid_value_string(dep, p);
  994             }
  995             else
  996                 _log("Could not find option \"%s\" (set from composite \"%s\")", cur, opt->name);
  997         }
  998         else if (startswith(cur, "no") || startswith(cur, "No")) {
  999             if ((dep = find_option(&cur[2]))) {
 1000                 val = option_assure_value(dep, optionset);
 1001                 val->fromoption = opt;
 1002                 val->value = get_valid_value_string(dep, "0");
 1003             }
 1004         }
 1005         else {
 1006             if ((dep = find_option(cur))) {
 1007                 val = option_assure_value(dep, optionset);
 1008                 val->fromoption = opt;
 1009                 val->value = get_valid_value_string(dep, "1");
 1010             }
 1011         }
 1012     }
 1013     free(copy);
 1014 }
 1015 
 1016 int option_set_value(option_t *opt, int optionset, const char *value)
 1017 {
 1018     value_t *val = option_assure_value(opt, optionset);
 1019     char *newvalue;
 1020     choice_t *choice;
 1021     option_t *fromopt;
 1022 
 1023     newvalue = get_valid_value_string(opt, value);
 1024     if (!newvalue)
 1025         return 0;
 1026 
 1027     free(val->value);
 1028     val->value = NULL;
 1029 
 1030     if (startswith(newvalue, "From") && (fromopt = find_option(&newvalue[4])) &&
 1031                 option_is_composite(fromopt)) {
 1032         /* TODO only set the changed option, not all of them */
 1033         choice = option_find_choice(fromopt, 
 1034                                     option_get_value(fromopt, optionset));
 1035 
 1036         composite_set_values(fromopt, optionset, choice->command);
 1037     }
 1038     else {
 1039         val->value = newvalue;
 1040     }
 1041 
 1042     if (option_is_composite(opt)) {
 1043         /* set dependent values */
 1044         choice = option_find_choice(opt, value);
 1045         if (choice && !isempty(choice->command))
 1046             composite_set_values(opt, optionset, choice->command);
 1047     }
 1048     return 1;
 1049 }
 1050 
 1051 int option_accepts_value(option_t *opt, const char *value)
 1052 {
 1053     char *val = get_valid_value_string(opt, value);
 1054     if (!val)
 1055         return 0;
 1056     free(val);
 1057     return 1;
 1058 }
 1059 
 1060 int option_has_choice(option_t *opt, const char *choice)
 1061 {
 1062     return option_find_choice(opt, choice) != NULL;
 1063 }
 1064 
 1065 const char * option_text(option_t *opt)
 1066 {
 1067     if (isempty(opt->text))
 1068         return opt->text;
 1069     return opt->text;
 1070 }
 1071 
 1072 int option_type(option_t *opt)
 1073 {
 1074     return opt->type;
 1075 }
 1076 
 1077 void option_set_order(option_t *opt, double order)
 1078 {
 1079     option_t *prev;
 1080 
 1081     /* remove opt from old position */
 1082     if (opt == optionlist_sorted_by_order)
 1083         optionlist_sorted_by_order = opt->next_by_order;
 1084     else {
 1085         for (prev = optionlist_sorted_by_order;
 1086              prev && prev->next_by_order != opt;
 1087              prev = prev->next_by_order);
 1088         prev->next_by_order = opt->next_by_order;
 1089     }
 1090 
 1091     opt->order = order;
 1092 
 1093     /* insert into new position */
 1094     if (!optionlist_sorted_by_order)
 1095         optionlist_sorted_by_order = opt;
 1096     else if (optionlist_sorted_by_order->order > opt->order) {
 1097         opt->next_by_order = optionlist_sorted_by_order;
 1098         optionlist_sorted_by_order = opt;
 1099     }
 1100     else {
 1101         for (prev = optionlist_sorted_by_order;
 1102             prev->next_by_order && prev->next_by_order->order < opt->order;
 1103             prev = prev->next_by_order);
 1104         opt->next_by_order = prev->next_by_order;
 1105         prev->next_by_order = opt;
 1106     }
 1107 }
 1108 
 1109 /* Set option from *FoomaticRIPOption keyword */
 1110 void option_set_from_string(option_t *opt, const char *str)
 1111 {
 1112     char type[32], style[32];
 1113     double order;
 1114     int matches;
 1115 
 1116     matches = sscanf(str, "%31s %31s %c %lf", type, style, &opt->spot, &order);
 1117     if (matches < 3) {
 1118         _log("Can't read the value of *FoomaticRIPOption for \"%s\"", opt->name);
 1119         return;
 1120     }
 1121     opt->type = type_from_string(type);
 1122     opt->style = style_from_string(style);
 1123 
 1124     if (matches == 4)
 1125         option_set_order(opt, order);
 1126 }
 1127 
 1128 static choice_t * option_assure_choice(option_t *opt, const char *name)
 1129 {
 1130     choice_t *choice, *last = NULL;
 1131 
 1132     for (choice = opt->choicelist; choice; choice = choice->next) {
 1133         if (!strcasecmp(choice->value, name))
 1134             return choice;
 1135         last = choice;
 1136     }
 1137     if (!choice) {
 1138         choice = calloc(1, sizeof(choice_t));
 1139         if (last)
 1140             last->next = choice;
 1141         else
 1142             opt->choicelist = choice;
 1143         strlcpy(choice->value, name, 128);
 1144     }
 1145     return choice;
 1146 }
 1147 
 1148 static void unhtmlify(char *dest, size_t size, const char *src)
 1149 {
 1150     jobparams_t *job = get_current_job();
 1151     char *pdest = dest;
 1152     const char *psrc = src, *p = NULL;
 1153     const char *repl;
 1154     struct tm *t = localtime(&job->time);
 1155     char tmpstr[10];
 1156     size_t s, l, n;
 1157 
 1158     while (*psrc && pdest - dest < size - 1) {
 1159 
 1160         if (*psrc == '&') {
 1161             psrc++;
 1162             repl = NULL;
 1163             p = NULL;
 1164             l = 0;
 1165 
 1166             /* Replace HTML/XML entities by the original characters */
 1167             if (!prefixcmp(psrc, "apos")) {
 1168                 repl = "\'";
 1169                 p = psrc + 4;
 1170             } else if (!prefixcmp(psrc, "quot")) {
 1171                 repl = "\"";
 1172                 p = psrc + 4;
 1173             } else if (!prefixcmp(psrc, "gt")) {
 1174                 repl = ">";
 1175                 p = psrc + 2;
 1176             } else if (!prefixcmp(psrc, "lt")) {
 1177                 repl = "<";
 1178                 p = psrc + 2;
 1179             } else if (!prefixcmp(psrc, "amp")) {
 1180                 repl = "&";
 1181                 p = psrc + 3;
 1182 
 1183             /* Replace special entities by job->data */
 1184             } else if (!prefixcmp(psrc, "job")) {
 1185                 repl = job->id;
 1186                 p = psrc + 3;
 1187                 if (jobentitymaxlen != 0)
 1188                     l = jobentitymaxlen;
 1189             } else if (!prefixcmp(psrc, "user")) {
 1190                 repl = job->user;
 1191                 p = psrc + 4;
 1192                 if (userentitymaxlen != 0)
 1193                     l = userentitymaxlen;
 1194             } else if (!prefixcmp(psrc, "host")) {
 1195                 repl = job->host;
 1196                 p = psrc + 4;
 1197                 if (hostentitymaxlen != 0)
 1198                     l = hostentitymaxlen;
 1199             } else if (!prefixcmp(psrc, "title")) {
 1200                 repl = job->title;
 1201                 p = psrc + 5;
 1202                 if (titleentitymaxlen != 0)
 1203                     l = titleentitymaxlen;
 1204             } else if (!prefixcmp(psrc, "copies")) {
 1205                 repl = job->copies;
 1206                 p = psrc + 6;
 1207             } else if (!prefixcmp(psrc, "rbinumcopies")) {
 1208                 if (job->rbinumcopies > 0) {
 1209                     snprintf(tmpstr, 10, "%d", job->rbinumcopies);
 1210                     repl = tmpstr;
 1211                 }
 1212                 else
 1213                     repl = job->copies;
 1214                 p = psrc + 12;
 1215             }
 1216             else if (!prefixcmp(psrc, "options")) {
 1217                 repl = job->optstr->data;
 1218                 p = psrc + 7;
 1219                 if (optionsentitymaxlen != 0)
 1220                     l = optionsentitymaxlen;
 1221             } else if (!prefixcmp(psrc, "year")) {
 1222                 sprintf(tmpstr, "%04d", t->tm_year + 1900);
 1223                 repl = tmpstr;
 1224                 p = psrc + 4;
 1225             }
 1226             else if (!prefixcmp(psrc, "month")) {
 1227                 sprintf(tmpstr, "%02d", t->tm_mon + 1);
 1228                 repl = tmpstr;
 1229                 p = psrc + 5;
 1230             }
 1231             else if (!prefixcmp(psrc, "date")) {
 1232                 sprintf(tmpstr, "%02d", t->tm_mday);
 1233                 repl = tmpstr;
 1234                 p = psrc + 4;
 1235             }
 1236             else if (!prefixcmp(psrc, "hour")) {
 1237                 sprintf(tmpstr, "%02d", t->tm_hour);
 1238                 repl = tmpstr;
 1239                 p = psrc + 4;
 1240             }
 1241             else if (!prefixcmp(psrc, "min")) {
 1242                 sprintf(tmpstr, "%02d", t->tm_min);
 1243                 repl = tmpstr;
 1244                 p = psrc + 3;
 1245             }
 1246             else if (!prefixcmp(psrc, "sec")) {
 1247                 sprintf(tmpstr, "%02d", t->tm_sec);
 1248                 repl = tmpstr;
 1249                 p = psrc + 3;
 1250             }
 1251             if (p) {
 1252                 n = strtol(p, (char **)(&p), 0);
 1253                 if (n != 0)
 1254                     l = n;
 1255                 if (*p != ';')
 1256                     repl = NULL;
 1257             } else
 1258                 repl = NULL;
 1259             if (repl) {
 1260                 if ((l == 0) || (l > strlen(repl)))
 1261                     l = strlen(repl);
 1262                 s = size - (pdest - dest) - 1;
 1263                 strncpy(pdest, repl, s);
 1264                 if (s < l)
 1265                     pdest += s;
 1266                 else
 1267                     pdest += l;
 1268                 psrc = p + 1;
 1269             }
 1270             else {
 1271                 *pdest = '&';
 1272                 pdest++;
 1273             }
 1274         }
 1275         else {
 1276             *pdest = *psrc;
 1277             pdest++;
 1278             psrc++;
 1279         }
 1280     }
 1281     *pdest = '\0';
 1282 }
 1283 
 1284 /*
 1285  * Checks whether 'code' contains active PostScript, i.e. not only comments
 1286  */
 1287 static int contains_active_postscript(const char *code)
 1288 {
 1289     char **line, **lines;
 1290     int contains_ps = 0;
 1291 
 1292     if (!(lines = argv_split(code, "\n", NULL)))
 1293         return 0;
 1294 
 1295     for (line = lines; *line && !contains_ps; line++)
 1296         contains_ps = !isempty(*line) && 
 1297                       !startswith(skip_whitespace(*line), "%");
 1298 
 1299     argv_free(lines);
 1300     return contains_ps;
 1301 }
 1302 
 1303 void option_set_choice(option_t *opt, const char *name, const char *text,
 1304                        const char *code)
 1305 {
 1306     choice_t *choice;
 1307 
 1308     if (opt->type == TYPE_BOOL) {
 1309         if (is_true_string(name))
 1310             choice = option_assure_choice(opt, "1");
 1311         else
 1312             choice = option_assure_choice(opt, "0");
 1313     }
 1314     else
 1315         choice = option_assure_choice(opt, name);
 1316 
 1317     if (text)
 1318         strlcpy(choice->text, text, 128);
 1319 
 1320     if (!code)
 1321     {
 1322         _log("Warning: No code for choice \"%s\" of option \"%s\"\n",
 1323              choice->text, opt->name);
 1324         return;
 1325     }
 1326 
 1327     if (!startswith(code, "%% FoomaticRIPOptionSetting"))
 1328         unhtmlify(choice->command, 65536, code);
 1329 }
 1330 
 1331 /*
 1332  *  Parameters
 1333  */
 1334 
 1335 int param_set_allowed_chars(param_t *param, const char *value)
 1336 {
 1337     char rxstr[128], tmp[128];
 1338 
 1339     param->allowedchars = malloc(sizeof(regex_t));
 1340     unhtmlify(tmp, 128, value);
 1341     snprintf(rxstr, 128, "^[%s]*$", tmp);
 1342     if (regcomp(param->allowedchars, rxstr, 0) != 0) {
 1343         regfree(param->allowedchars);
 1344         param->allowedchars = NULL;
 1345         return 0;
 1346     }
 1347     return 1;
 1348 }
 1349 
 1350 int param_set_allowed_regexp(param_t *param, const char *value)
 1351 {
 1352     char tmp[128];
 1353 
 1354     param->allowedregexp = malloc(sizeof(regex_t));
 1355     unhtmlify(tmp, 128, value);
 1356     if (regcomp(param->allowedregexp, tmp, 0) != 0) {
 1357         regfree(param->allowedregexp);
 1358         param->allowedregexp = NULL;
 1359         return 0;
 1360     }
 1361     return 1;
 1362 }
 1363 
 1364 void option_set_custom_command(option_t *opt, const char *cmd)
 1365 {
 1366     size_t len = strlen(cmd) + 50;
 1367     free(opt->custom_command);
 1368     opt->custom_command = malloc(len);
 1369     unhtmlify(opt->custom_command, len, cmd);
 1370 }
 1371 
 1372 param_t * option_add_custom_param_from_string(option_t *opt,
 1373     const char *name, const char *text, const char *str)
 1374 {
 1375     param_t *param = calloc(1, sizeof(param_t));
 1376     param_t *p;
 1377     char typestr[33];
 1378     int n;
 1379 
 1380     strlcpy(param->name, name, 128);
 1381     strlcpy(param->text, text, 128);
 1382 
 1383     n = sscanf(str, "%d%15s%19s%19s",
 1384         &param->order, typestr, param->min, param->max);
 1385 
 1386     if (n != 4) {
 1387         _log("Could not parse custom parameter for '%s'!\n", opt->name);
 1388         free(param);
 1389         return NULL;
 1390     }
 1391 
 1392     if (!strcmp(typestr, "curve"))
 1393         param->type = TYPE_CURVE;
 1394     else if (!strcmp(typestr, "invcurve"))
 1395         param->type = TYPE_INVCURVE;
 1396     else if (!strcmp(typestr, "int"))
 1397         param->type = TYPE_INT;
 1398     else if (!strcmp(typestr, "real"))
 1399         param->type = TYPE_FLOAT;
 1400     else if (!strcmp(typestr, "passcode"))
 1401         param->type = TYPE_PASSCODE;
 1402     else if (!strcmp(typestr, "password"))
 1403         param->type = TYPE_PASSWORD;
 1404     else if (!strcmp(typestr, "points"))
 1405         param->type = TYPE_POINTS;
 1406     else if (!strcmp(typestr, "string"))
 1407         param->type = TYPE_STRING;
 1408     else {
 1409         _log("Unknown custom parameter type for param '%s' for option '%s'\n", param->name, opt->name);
 1410         free(param);
 1411         return NULL;
 1412     }
 1413 
 1414     param->next = NULL;
 1415 
 1416     /* Insert param into opt->paramlist, sorted by order */
 1417     if (!opt->paramlist)
 1418         opt->paramlist = param;
 1419     else if (opt->paramlist->order > param->order) {
 1420         param->next = opt->paramlist;
 1421         opt->paramlist = param;
 1422     }
 1423     else {
 1424         for (p = opt->paramlist;
 1425              p->next && p->next->order < param->order;
 1426              p = p->next);
 1427         param->next = p->next;
 1428         p->next = param;
 1429     }
 1430 
 1431     opt->param_count++;
 1432     return param;
 1433 }
 1434 
 1435 param_t * option_assure_foomatic_param(option_t *opt)
 1436 {
 1437     param_t *param;
 1438 
 1439     if (opt->foomatic_param)
 1440         return opt->foomatic_param;
 1441 
 1442     param = calloc(1, sizeof(param_t));
 1443     strcpy(param->name, "foomatic-param");
 1444     param->order = 0;
 1445     param->type = opt->type;
 1446 
 1447     opt->foomatic_param = param;
 1448     return param;
 1449 }
 1450 
 1451 
 1452 /*
 1453  *  Optionsets
 1454  */
 1455 
 1456 const char * optionset_name(int idx)
 1457 {
 1458     if (idx < 0 || idx >= optionset_count) {
 1459         _log("Optionset with index %d does not exist\n", idx);
 1460         return NULL;
 1461     }
 1462     return optionsets[idx];
 1463 }
 1464 
 1465 int optionset(const char * name)
 1466 {
 1467     int i;
 1468 
 1469     for (i = 0; i < optionset_count; i++) {
 1470         if (!strcmp(optionsets[i], name))
 1471             return i;
 1472     }
 1473 
 1474     if (optionset_count == optionset_alloc) {
 1475         optionset_alloc *= 2;
 1476         optionsets = realloc(optionsets, optionset_alloc * sizeof(char *));
 1477         for (i = optionset_count; i < optionset_alloc; i++)
 1478             optionsets[i] = NULL;
 1479     }
 1480 
 1481     optionsets[optionset_count] = strdup(name);
 1482     optionset_count++;
 1483     return optionset_count -1;
 1484 }
 1485 
 1486 void optionset_copy_values(int src_optset, int dest_optset)
 1487 {
 1488     option_t *opt;
 1489     value_t *val;
 1490 
 1491     for (opt = optionlist; opt; opt = opt->next) {
 1492         for (val = opt->valuelist; val; val = val->next) {
 1493             if (val->optionset == src_optset) {
 1494                 option_set_value(opt, dest_optset, val->value);
 1495                 break;
 1496             }
 1497         }
 1498     }
 1499 }
 1500 
 1501 void optionset_delete_values(int optionset)
 1502 {
 1503     option_t *opt;
 1504     value_t *val, *prev_val;
 1505 
 1506     for (opt = optionlist; opt; opt = opt->next) {
 1507         val = opt->valuelist;
 1508         prev_val = NULL;
 1509         while (val) {
 1510             if (val->optionset == optionset) {
 1511                 if (prev_val)
 1512                     prev_val->next = val->next;
 1513                 else
 1514                     opt->valuelist = val->next;
 1515                 free_value(val);
 1516                 val = prev_val ? prev_val->next : opt->valuelist;
 1517                 break;
 1518             } else {
 1519                 prev_val = val;
 1520                 val = val->next;
 1521             }
 1522         }
 1523     }
 1524 }
 1525 
 1526 int optionset_equal(int optset1, int optset2, int exceptPS)
 1527 {
 1528     option_t *opt;
 1529     const char *val1, *val2;
 1530 
 1531     for (opt = optionlist; opt; opt = opt->next) {
 1532         if (exceptPS && opt->style == 'G')
 1533             continue;
 1534 
 1535         val1 = option_get_value(opt, optset1);
 1536         val2 = option_get_value(opt, optset2);
 1537 
 1538         if (val1 && val2) { /* both entries exist */
 1539             if (strcmp(val1, val2) != 0)
 1540                 return 0; /* but aren't equal */
 1541         }
 1542         else if (val1 || val2) /* one entry exists --> can't be equal */
 1543             return 0;
 1544         /* If no extry exists, the non-existing entries
 1545          * are considered as equal */
 1546     }
 1547     return 1;
 1548 }
 1549 
 1550 /*
 1551  *  read_ppd_file()
 1552  */
 1553 void read_ppd_file(const char *filename)
 1554 {
 1555     FILE *fh;
 1556     const char *tmp;
 1557     char *icc_qual2 = NULL;
 1558     char *icc_qual3 = NULL;
 1559     char line [256];            /* PPD line length is max 255 (excl. \0) */
 1560     char *p;
 1561     char key[128], name[64], text[64];
 1562     dstr_t *value = create_dstr(); /* value can span multiple lines */
 1563     double order;
 1564     value_t *val;
 1565     option_t *opt, *current_opt = NULL;
 1566     param_t *param;
 1567     icc_mapping_entry_t *entry;
 1568 
 1569     fh = fopen(filename, "r");
 1570     if (!fh) {
 1571         _log("error opening %s\n", filename);
 1572         exit(EXIT_PRNERR_NORETRY_BAD_SETTINGS);
 1573     }
 1574     _log("Parsing PPD file ...\n");
 1575 
 1576     dstrassure(value, 256);
 1577 
 1578     qualifier_data = list_create();
 1579     while (!feof(fh)) {
 1580         fgets(line, 256, fh);
 1581 
 1582         if (line[0] != '*' || startswith(line, "*%"))
 1583             continue;
 1584 
 1585         /* get the key */
 1586         if (!(p = strchr(line, ':')))
 1587             continue;
 1588         *p = '\0';
 1589 
 1590         key[0] = name[0] = text[0] = '\0';
 1591         sscanf(line, "*%127s%*[ \t]%63[^ \t/=)]%*1[/=]%63[^\n]", key, name, text);
 1592 
 1593         /* get the value */
 1594         dstrclear(value);
 1595         sscanf(p +1, " %255[^\r\n]", value->data);
 1596         value->len = strlen(value->data);
 1597         if (!value->len)
 1598             _log("PPD: Missing value for key \"%s\"\n", line);
 1599 
 1600         while (1) {
 1601             /* "&&" is the continue-on-next-line marker */
 1602             if (dstrendswith(value, "&&")) {
 1603                 value->len -= 2;
 1604                 value->data[value->len] = '\0';
 1605             }
 1606             /* quoted but quotes are not yet closed */
 1607             else if (value->data[0] == '\"' && !strchr(value->data +1, '\"'))
 1608                 dstrcat(value, "\n"); /* keep newlines in quoted string*/
 1609             /* not quoted, or quotes already closed */
 1610             else
 1611                 break;
 1612 
 1613             fgets(line, 256, fh);
 1614             dstrcat(value, line);
 1615             dstrremovenewline(value);
 1616         }
 1617 
 1618         /* remove quotes */
 1619         if (value->data[0] == '\"') {
 1620             memmove(value->data, value->data +1, value->len +1);
 1621             p = strrchr(value->data, '\"');
 1622             if (!p) {
 1623                 _log("Invalid line: \"%s: ...\"\n", key);
 1624                 continue;
 1625             }
 1626             *p = '\0';
 1627         }
 1628         /* remove last newline */
 1629         dstrremovenewline(value);
 1630 
 1631         /* remove last whitespace */
 1632         dstrtrim_right(value);
 1633 
 1634         /* process key/value pairs */
 1635         if (strcmp(key, "NickName") == 0) {
 1636             unhtmlify(printer_model, 256, value->data);
 1637         }
 1638         else if (strcmp(key, "FoomaticIDs") == 0) {
 1639             /* *FoomaticIDs: <printer ID> <driver ID> */
 1640             sscanf(value->data, "%*[ \t]%127[^ \t]%*[ \t]%127[^ \t\n]",
 1641                 printer_id, driver);
 1642         }
 1643         else if (strcmp(key, "FoomaticRIPPostPipe") == 0) {
 1644             if (!postpipe)
 1645                 postpipe = create_dstr();
 1646             dstrassure(postpipe, value->len +128);
 1647             unhtmlify(postpipe->data, postpipe->alloc, value->data);
 1648         }
 1649         else if (strcmp(key, "FoomaticRIPCommandLine") == 0) {
 1650             unhtmlify(cmd, 4096, value->data);
 1651         }
 1652         else if (strcmp(key, "FoomaticRIPCommandLinePDF") == 0) {
 1653             unhtmlify(cmd_pdf, 4096, value->data);
 1654         }
 1655         else if (strcmp(key, "FoomaticRIPNoPageAccounting") == 0) {
 1656             /* Boolean value */
 1657             if (strcasecmp(value->data, "true") == 0) {
 1658                 /* Driver is not compatible with page accounting according to the
 1659                    Foomatic database, so turn it off for this driver */
 1660                 ps_accounting = 0;
 1661                 _log("CUPS page accounting disabled by driver.\n");
 1662             }
 1663         }
 1664         else if (!strcmp(key, "cupsFilter")) {
 1665             /* cupsFilter: <code> */
 1666             /* only save the filter for "application/vnd.cups-raster" */
 1667             if (prefixcmp(value->data, "application/vnd.cups-raster") == 0) {
 1668                 p = strrchr(value->data, ' ');
 1669                 if (p)
 1670                     unhtmlify(cupsfilter, 256, p +1);
 1671             }
 1672         }
 1673         else if (startswith(key, "Custom") && !strcasecmp(name, "true")) {
 1674             /* Cups custom option: *CustomFoo True: "command" */
 1675             if (startswith(&key[6], "JCL")) {
 1676                 opt = assure_option(&key[9]);
 1677                 opt->style = 'J';
 1678             }
 1679             else
 1680                 opt = assure_option(&key[6]);
 1681             option_set_custom_command(opt, value->data);
 1682             if (!strcmp(key, "CustomPageSize"))
 1683                 option_set_custom_command(assure_option("PageRegion"), value->data);
 1684         }
 1685         else if (startswith(key, "ParamCustom")) {
 1686             /* Cups custom parameter:
 1687                *ParamCustomFoo Name/Text: order type minimum maximum */
 1688             if (startswith(&key[11], "JCL"))
 1689                 opt = assure_option(&key[14]);
 1690             else
 1691                 opt = assure_option(&key[11]);
 1692             option_add_custom_param_from_string(opt, name, text, value->data);
 1693         }
 1694         else if (!strcmp(key, "OpenUI") || !strcmp(key, "JCLOpenUI")) {
 1695             /* "*[JCL]OpenUI *<option>[/<translation>]: <type>" */
 1696             current_opt = assure_option(&name[1]);
 1697             if (!isempty(text))
 1698                 strlcpy(current_opt->text, text, 128);
 1699             if (startswith(key, "JCL"))
 1700                 current_opt->style = 'J';
 1701             /* Set the argument type only if not defined yet,
 1702             a definition in "*FoomaticRIPOption" has priority */
 1703             if (current_opt->type == TYPE_NONE)
 1704                 current_opt->type = type_from_string(value->data);
 1705         }
 1706         else if (!strcmp(key, "CloseUI") || !strcmp(key, "JCLCloseUI")) {
 1707             /* *[JCL]CloseUI: *<option> */
 1708             if (!current_opt || !option_has_name(current_opt, value->data +1))
 1709                 _log("CloseUI found without corresponding OpenUI (%s).\n", value->data +1);
 1710             current_opt = NULL;
 1711         }
 1712         else if (!strcmp(key, "FoomaticRIPOption")) {
 1713             /* "*FoomaticRIPOption <option>: <type> <style> <spot> [<order>]"
 1714                <order> only used for 1-choice enum options */
 1715             option_set_from_string(assure_option(name), value->data);
 1716         }
 1717         else if (!strcmp(key, "FoomaticRIPOptionPrototype")) {
 1718             /* "*FoomaticRIPOptionPrototype <option>: <code>"
 1719                Used for numerical and string options only */
 1720             opt = assure_option(name);
 1721             opt->proto = malloc(65536);
 1722             unhtmlify(opt->proto, 65536, value->data);
 1723         }
 1724         else if (!strcmp(key, "FoomaticRIPOptionRange")) {
 1725             /* *FoomaticRIPOptionRange <option>: <min> <max>
 1726                Used for numerical options only */
 1727             param = option_assure_foomatic_param(assure_option(name));
 1728             sscanf(value->data, "%19s %19s", param->min, param->max);
 1729         }
 1730         else if (!strcmp(key, "FoomaticRIPOptionMaxLength")) {
 1731             /*  "*FoomaticRIPOptionMaxLength <option>: <length>"
 1732                 Used for string options only */
 1733             param = option_assure_foomatic_param(assure_option(name));
 1734             sscanf(value->data, "%19s", param->max);
 1735         }
 1736         else if (!strcmp(key, "FoomaticRIPOptionAllowedChars")) {
 1737             /* *FoomaticRIPOptionAllowedChars <option>: <code>
 1738                 Used for string options only */
 1739             param = option_assure_foomatic_param(assure_option(name));
 1740             param_set_allowed_chars(param, value->data);
 1741         }
 1742         else if (!strcmp(key, "FoomaticRIPOptionAllowedRegExp")) {
 1743             /* "*FoomaticRIPOptionAllowedRegExp <option>: <code>"
 1744                Used for string options only */
 1745             param = option_assure_foomatic_param(assure_option(name));
 1746             param_set_allowed_regexp(param, value->data);
 1747         }
 1748         else if (!strcmp(key, "OrderDependency")) {
 1749             /* OrderDependency: <order> <section> *<option> */
 1750             /* use 'text' to read <section> */
 1751             sscanf(value->data, "%lf %63s *%63s", &order, text, name);
 1752             opt = assure_option(name);
 1753             opt->section = section_from_string(text);
 1754             option_set_order(opt, order);
 1755         }
 1756 
 1757         /* Default options are not yet validated (not all options/choices
 1758            have been read yet) */
 1759         else if (!prefixcmp(key, "Default")) {
 1760             /* Default<option>: <value> */
 1761 
 1762             opt = assure_option(&key[7]);
 1763             val = option_assure_value(opt, optionset("default"));
 1764             free(val->value);
 1765             val->value = strdup(value->data);
 1766         }
 1767         else if (!prefixcmp(key, "FoomaticRIPDefault")) {
 1768             /* FoomaticRIPDefault<option>: <value>
 1769                Used for numerical options only */
 1770             opt = assure_option(&key[18]);
 1771             val = option_assure_value(opt, optionset("default"));
 1772             free(val->value);
 1773             val->value = strdup(value->data);
 1774         }
 1775 
 1776         /* Current argument */
 1777         else if (current_opt && !strcmp(key, current_opt->name)) {
 1778             /* *<option> <choice>[/translation]: <code> */
 1779             option_set_choice(current_opt, name, text, value->data);
 1780         }
 1781         else if (!strcmp(key, "FoomaticRIPOptionSetting")) {
 1782             /* "*FoomaticRIPOptionSetting <option>[=<choice>]: <code>
 1783                For boolean options <choice> is not given */
 1784             option_set_choice(assure_option(name),
 1785                 isempty(text) ? "true" : text, NULL, value->data);
 1786         }
 1787 
 1788         /* "*(Foomatic|)JCL(Begin|ToPSInterpreter|End|Prefix): <code>"
 1789            The printer supports PJL/JCL when there is such a line */
 1790         else if (!prefixcmp(key, "JCLBegin") ||
 1791                  !prefixcmp(key, "FoomaticJCLBegin")) {
 1792             unhexify(jclbegin, 256, value->data);
 1793             if (!jclprefixset && strstr(jclbegin, "PJL") == NULL)
 1794                 jclprefix[0] = '\0';
 1795         }
 1796         else if (!prefixcmp(key, "JCLToPSInterpreter") ||
 1797                  !prefixcmp(key, "FoomaticJCLToPSInterpreter")) {
 1798              unhexify(jcltointerpreter, 256, value->data);
 1799         }
 1800         else if (!prefixcmp(key, "JCLEnd") ||
 1801                  !prefixcmp(key, "FoomaticJCLEnd")) {
 1802              unhexify(jclend, 256, value->data);
 1803         }
 1804         else if (!prefixcmp(key, "JCLPrefix") ||
 1805                  !prefixcmp(key, "FoomaticJCLPrefix")) {
 1806             unhexify(jclprefix, 256, value->data);
 1807             jclprefixset = 1;
 1808         }
 1809         else if (!prefixcmp(key, "% COMDATA #")) {
 1810             /* old foomtic 2.0.x PPD file */
 1811             _log("You are using an old Foomatic 2.0 PPD file, which is no "
 1812                  "longer supported by Foomatic >4.0. Exiting.\n");
 1813             exit(1); /* TODO exit more gracefully */
 1814         }
 1815         else if (!strcmp(key, "FoomaticRIPJobEntityMaxLength")) {
 1816             /*  "*FoomaticRIPJobEntityMaxLength: <length>" */
 1817             sscanf(value->data, "%d", &jobentitymaxlen);
 1818         }
 1819         else if (!strcmp(key, "FoomaticRIPUserEntityMaxLength")) {
 1820             /*  "*FoomaticRIPUserEntityMaxLength: <length>" */
 1821             sscanf(value->data, "%d", &userentitymaxlen);
 1822         }
 1823         else if (!strcmp(key, "FoomaticRIPHostEntityMaxLength")) {
 1824             /*  "*FoomaticRIPHostEntityMaxLength: <length>" */
 1825             sscanf(value->data, "%d", &hostentitymaxlen);
 1826         }
 1827         else if (!strcmp(key, "FoomaticRIPTitleEntityMaxLength")) {
 1828             /*  "*FoomaticRIPTitleEntityMaxLength: <length>" */
 1829             sscanf(value->data, "%d", &titleentitymaxlen);
 1830         }
 1831         else if (!strcmp(key, "FoomaticRIPOptionsEntityMaxLength")) {
 1832             /*  "*FoomaticRIPOptionsEntityMaxLength: <length>" */
 1833             sscanf(value->data, "%d", &optionsentitymaxlen);
 1834         }
 1835         else if (!strcmp(key, "cupsICCProfile")) {
 1836             /*  "*cupsICCProfile: <qualifier/Title> <filename>" */
 1837             entry = calloc(1, sizeof(icc_mapping_entry_t));
 1838             entry->qualifier = strdup(name);
 1839             entry->filename = strdup(value->data);
 1840             list_append (qualifier_data, entry);
 1841         }
 1842         else if (!strcmp(key, "cupsICCQualifier2")) {
 1843             /*  "*cupsICCQualifier2: <value>" */
 1844             icc_qual2 = strdup(value->data);
 1845         }
 1846         else if (!strcmp(key, "cupsICCQualifier3")) {
 1847             /*  "*cupsICCQualifier3: <value>" */
 1848             icc_qual3 = strdup(value->data);
 1849         }
 1850     }
 1851 
 1852     fclose(fh);
 1853     free_dstr(value);
 1854 
 1855     /* Validate default options by resetting them with option_set_value() */
 1856     for (opt = optionlist; opt; opt = opt->next) {
 1857         val = option_find_value(opt, optionset("default"));
 1858         if (val) {
 1859             /* if fromopt is set, this value has already been validated */
 1860             if (!val->fromoption)
 1861                 option_set_value(opt, optionset("default"), val->value);
 1862         }
 1863         else
 1864             /* Make sure that this option has a default choice, even if none is
 1865                defined in the PPD file */
 1866             option_set_value(opt, optionset("default"), opt->choicelist->value);
 1867     }
 1868 
 1869     /* create qualifier for this PPD */
 1870     qualifier = calloc(4, sizeof(char*));
 1871 
 1872     /* get colorspace */
 1873     tmp = option_get_value(find_option("ColorSpace"), optionset("default"));
 1874     if (tmp == NULL)
 1875       tmp = option_get_value(find_option("ColorModel"), optionset("default"));
 1876     if (tmp == NULL)
 1877       tmp = "";
 1878     qualifier[0] = strdup(tmp);
 1879 
 1880     /* get selector2 */
 1881     if (icc_qual2 == NULL)
 1882         icc_qual2 = strdup("MediaType");
 1883     tmp = option_get_value(find_option(icc_qual2), optionset("default"));
 1884     if (tmp == NULL)
 1885       tmp = "";
 1886     qualifier[1] = strdup(tmp);
 1887 
 1888     /* get selectors */
 1889     if (icc_qual3 == NULL)
 1890         icc_qual3 = strdup("Resolution");
 1891     tmp = option_get_value(find_option(icc_qual3), optionset("default"));
 1892     if (tmp == NULL)
 1893       tmp = "";
 1894     qualifier[2] = strdup(tmp);
 1895 
 1896     free (icc_qual2);
 1897     free (icc_qual3);
 1898 }
 1899 
 1900 int ppd_supports_pdf()
 1901 {
 1902     option_t *opt;
 1903 
 1904     /* If at least one option inserts PostScript code, we cannot support PDF */
 1905     for (opt = optionlist; opt; opt = opt->next)
 1906     {
 1907         choice_t *choice;
 1908 
 1909         if (!option_is_ps_command(opt) || option_is_composite(opt) ||
 1910         (opt->type == TYPE_NONE))
 1911       continue;
 1912 
 1913         for (choice = opt->choicelist; choice; choice = choice->next)
 1914       if (contains_active_postscript(choice->command)) {
 1915         _log("  PostScript option found: %s=%s: \"%s\"\n",
 1916          opt->name, choice->value, choice->command);
 1917         return 0;
 1918       }
 1919     }
 1920 
 1921     if (!isempty(cmd_pdf))
 1922         return 1;
 1923 
 1924     /* Ghostscript also accepts PDF, use that if it is in the normal command
 1925      * line */
 1926     if (startswith(cmd, "gs"))
 1927     {
 1928         strncpy(cmd_pdf, cmd, 4096);
 1929         return 1;
 1930     }
 1931 
 1932     _log("  Neither PDF renderer command line nor Ghostscript-based renderer command line found\n");
 1933     return 0;
 1934 }
 1935 
 1936 /* build a renderer command line, based on the given option set */
 1937 int build_commandline(int optset, dstr_t *cmdline, int pdfcmdline)
 1938 {
 1939     option_t *opt;
 1940     const char *userval;
 1941     char *s, *p;
 1942     dstr_t *cmdvar = create_dstr();
 1943     dstr_t *open = create_dstr();
 1944     dstr_t *close = create_dstr();
 1945     char letters[] = "%A %B %C %D %E %F %G %H %I %J %K %L %M %W %X %Y %Z";
 1946     int jcl = 0;
 1947 
 1948     dstr_t *local_jclprepend = create_dstr();
 1949 
 1950     dstrclear(prologprepend);
 1951     dstrclear(setupprepend);
 1952     dstrclear(pagesetupprepend);
 1953 
 1954     if (cmdline)
 1955         dstrcpy(cmdline, pdfcmdline ? cmd_pdf : cmd);
 1956 
 1957     for (opt = optionlist_sorted_by_order; opt; opt = opt->next_by_order) {
 1958         /* composite options have no direct influence, and all their dependents
 1959            have already been set */
 1960         if (option_is_composite(opt))
 1961             continue;
 1962 
 1963         userval = option_get_value(opt, optset);
 1964         option_get_command(cmdvar, opt, optset, -1);
 1965 
 1966         /* Insert the built snippet at the correct place */
 1967         if (option_is_ps_command(opt)) {
 1968             /* Place this Postscript command onto the prepend queue
 1969                for the appropriate section. */
 1970             if (cmdvar->len) {
 1971                 dstrcpyf(open, "[{\n%%%%BeginFeature: *%s ", opt->name);
 1972                 if (opt->type == TYPE_BOOL)
 1973                     dstrcatf(open, is_true_string(userval) ? "True\n" : "False\n");
 1974                 else
 1975                     dstrcatf(open, "%s\n", userval);
 1976                 dstrcpyf(close, "\n%%%%EndFeature\n} stopped cleartomark\n");
 1977 
 1978                 switch (option_get_section(opt)) {
 1979                     case SECTION_PROLOG:
 1980                         dstrcatf(prologprepend, "%s%s%s", open->data, cmdvar->data, close->data);
 1981                         break;
 1982 
 1983                     case SECTION_ANYSETUP:
 1984                         if (optset != optionset("currentpage"))
 1985                             dstrcatf(setupprepend, "%s%s%s", open->data, cmdvar->data, close->data);
 1986                         else if (strcmp(option_get_value(opt, optionset("header")), userval) != 0)
 1987                             dstrcatf(pagesetupprepend, "%s%s%s", open->data, cmdvar->data, close->data);
 1988                         break;
 1989 
 1990                     case SECTION_DOCUMENTSETUP:
 1991                         dstrcatf(setupprepend, "%s%s%s", open->data, cmdvar->data, close->data);
 1992                         break;
 1993 
 1994                     case SECTION_PAGESETUP:
 1995                         dstrcatf(pagesetupprepend, "%s%s%s", open->data, cmdvar->data, close->data);
 1996                         break;
 1997 
 1998                     case SECTION_JCLSETUP:          /* PCL/JCL argument */
 1999                         s = malloc(cmdvar->len +1);
 2000                         unhexify(s, cmdvar->len +1, cmdvar->data);
 2001                         dstrcatf(local_jclprepend, "%s", s);
 2002                         free(s);
 2003                         break;
 2004 
 2005                     default:
 2006                         dstrcatf(setupprepend, "%s%s%s", open->data, cmdvar->data, close->data);
 2007                 }
 2008             }
 2009         }
 2010         else if (option_is_jcl_arg(opt)) {
 2011             jcl = 1;
 2012             /* Put JCL commands onto JCL stack */
 2013             if (cmdvar->len) {
 2014                 char *s = malloc(cmdvar->len +1);
 2015                 unhexify(s, cmdvar->len +1, cmdvar->data);
 2016                 if (!startswith(cmdvar->data, jclprefix))
 2017                     dstrcatf(local_jclprepend, "%s%s\n", jclprefix, s);
 2018                 else
 2019                     dstrcat(local_jclprepend, s);
 2020                 free(s);
 2021             }
 2022         }
 2023         else if (option_is_commandline_arg(opt) && cmdline) {
 2024             /* Insert the processed argument in the command line
 2025             just before every occurrence of the spot marker. */
 2026             p = malloc(3);
 2027             snprintf(p, 3, "%%%c", opt->spot);
 2028             s = malloc(cmdvar->len +3);
 2029             snprintf(s, cmdvar->len +3, "%s%%%c", cmdvar->data, opt->spot);
 2030             dstrreplace(cmdline, p, s, 0);
 2031             free(p);
 2032             free(s);
 2033         }
 2034 
 2035         /* Insert option into command line of CUPS raster driver */
 2036         if (cmdline && strstr(cmdline->data, "%Y")) {
 2037             if (isempty(userval))
 2038                 continue;
 2039             s = malloc(strlen(opt->name) + strlen(userval) + 20);
 2040             sprintf(s, "%s=%s %%Y", opt->name, userval);
 2041             dstrreplace(cmdline, "%Y", s, 0);
 2042             free(s);
 2043         }
 2044     }
 2045 
 2046     /* Tidy up after computing option statements for all of P, J, and C types: */
 2047 
 2048     /* C type finishing */
 2049     /* Pluck out all of the %n's from the command line prototype */
 2050     if (cmdline) {
 2051         s = strtok(letters, " ");
 2052         do {
 2053             dstrreplace(cmdline, s, "", 0);
 2054         } while ((s = strtok(NULL, " ")));
 2055     }
 2056 
 2057     /* J type finishing */
 2058     /* Compute the proper stuff to say around the job */
 2059     if (jcl && !jobhasjcl) {
 2060         /* command to switch to the interpreter */
 2061         dstrcatf(local_jclprepend, "%s", jcltointerpreter);
 2062 
 2063         /* Arrange for JCL RESET command at the end of job */
 2064         dstrcpy(jclappend, jclend);
 2065 
 2066         argv_free(jclprepend);
 2067         jclprepend = argv_split(local_jclprepend->data, "\r\n", NULL);
 2068     }
 2069 
 2070     free_dstr(cmdvar);
 2071     free_dstr(open);
 2072     free_dstr(close);
 2073     free_dstr(local_jclprepend);
 2074 
 2075     return !isempty(cmd);
 2076 }
 2077 
 2078 /* if "comments" is set, add "%%BeginProlog...%%EndProlog" */
 2079 void append_prolog_section(dstr_t *str, int optset, int comments)
 2080 {
 2081     /* Start comment */
 2082     if (comments) {
 2083         _log("\"Prolog\" section is missing, inserting it.\n");
 2084         dstrcat(str, "%%BeginProlog\n");
 2085     }
 2086 
 2087     /* Generate the option code (not necessary when CUPS is spooler and
 2088        PostScript data is not converted from PDF) */
 2089     if ((spooler != SPOOLER_CUPS) || pdfconvertedtops) {
 2090         _log("Inserting option code into \"Prolog\" section.\n");
 2091         build_commandline(optset, NULL, 0);
 2092         dstrcat(str, prologprepend->data);
 2093     }
 2094 
 2095     /* End comment */
 2096     if (comments)
 2097         dstrcat(str, "%%EndProlog\n");
 2098 }
 2099 
 2100 void append_setup_section(dstr_t *str, int optset, int comments)
 2101 {
 2102     /* Start comment */
 2103     if (comments) {
 2104         _log("\"Setup\" section is missing, inserting it.\n");
 2105         dstrcat(str, "%%BeginSetup\n");
 2106     }
 2107 
 2108     /* PostScript code to generate accounting messages for CUPS */
 2109     if (spooler == SPOOLER_CUPS && ps_accounting == 1) {
 2110         _log("Inserting PostScript code for CUPS' page accounting\n");
 2111         dstrcat(str, accounting_prolog);
 2112     }
 2113 
 2114     /* Generate the option code (not necessary when CUPS is spooler and
 2115        PostScript data is not converted from PDF) */
 2116     if ((spooler != SPOOLER_CUPS) || pdfconvertedtops) {
 2117         _log("Inserting option code into \"Setup\" section.\n");
 2118         build_commandline(optset, NULL, 0);
 2119         dstrcat(str, setupprepend->data);
 2120     }
 2121 
 2122     /* End comment */
 2123     if (comments)
 2124         dstrcat(str, "%%EndSetup\n");
 2125 }
 2126 
 2127 void append_page_setup_section(dstr_t *str, int optset, int comments)
 2128 {
 2129     /* Start comment */
 2130     if (comments) {
 2131         _log("\"PageSetup\" section is missing, inserting it.\n");
 2132         dstrcat(str, "%%BeginPageSetup\n");
 2133     }
 2134 
 2135     /* Generate the option code (not necessary when CUPS is spooler) */
 2136     _log("Inserting option code into \"PageSetup\" section.\n");
 2137     build_commandline(optset, NULL, 0);
 2138     dstrcat(str, pagesetupprepend->data);
 2139 
 2140     /* End comment */
 2141     if (comments)
 2142         dstrcat(str, "%%EndPageSetup\n");
 2143 }
 2144 
 2145 
 2146 typedef struct page_range {
 2147     short even, odd;
 2148     unsigned first, last;
 2149     struct page_range *next;
 2150 } page_range_t;
 2151 
 2152 static page_range_t * parse_page_ranges(const char *ranges)
 2153 {
 2154     page_range_t *head, *tail = NULL;
 2155     char *tokens, *tok;
 2156     int cnt;
 2157 
 2158     tokens = strdup(ranges);
 2159     for (tok = strtok(tokens, ","); tok; tok = strtok(NULL, ",")) {
 2160         page_range_t *pr = calloc(1, sizeof(page_range_t));
 2161 
 2162         if (startswith(tok, "even"))
 2163             pr->even = 1;
 2164         else if (startswith(tok, "odd"))
 2165             pr->odd = 1;
 2166         else if ((cnt = sscanf(tok, "%d-%d", &pr->first, &pr->last))) {
 2167             /* If 'last' has not been read, this could mean only one page (no
 2168              * hyphen) or all pages to the end */
 2169             if (cnt == 1 && !endswith(tok, "-"))
 2170                 pr->last = pr->first;
 2171             else if (cnt == 2 && pr->first > pr->last) {
 2172                 unsigned tmp = pr->first;
 2173                 pr->first = pr->last;
 2174                 pr->last = tmp;
 2175             }
 2176         }
 2177         else {
 2178             printf("Invalid page range: %s\n", tok);
 2179             free(pr);
 2180             continue;
 2181         }
 2182 
 2183         if (tail) {
 2184             tail->next = pr;
 2185             tail = pr;
 2186         }
 2187         else
 2188             tail = head = pr;
 2189     }
 2190 
 2191     free(tokens);
 2192     return head;
 2193 }
 2194 
 2195 static void free_page_ranges(page_range_t *ranges)
 2196 {
 2197     page_range_t *pr;
 2198     while (ranges) {
 2199         pr = ranges;
 2200         ranges = ranges->next;
 2201         free(pr);
 2202     }
 2203 }
 2204 
 2205 /* Parse a string containing page ranges and either check whether a
 2206    given page is in the ranges or, if the given page number is zero,
 2207    determine the score how specific this page range string is.*/
 2208 int get_page_score(const char *pages, int page)
 2209 {
 2210     page_range_t *ranges = parse_page_ranges(pages);
 2211     page_range_t *pr;
 2212     int totalscore = 0;
 2213     int pageinside = 0;
 2214 
 2215     for (pr = ranges; pr; pr = pr->next) {
 2216         if (pr->even) {
 2217             totalscore += 50000;
 2218             if (page % 2 == 0)
 2219                 pageinside = 1;
 2220         }
 2221         else if (pr->odd) {
 2222             totalscore += 50000;
 2223             if (page % 2 == 1)
 2224                 pageinside = 1;
 2225         }
 2226         else if (pr->first == pr->last) {   /* Single page */
 2227             totalscore += 1;
 2228             if (page == pr->first)
 2229                 pageinside = 1;
 2230         }
 2231         else if (pr->last == 0) {           /* To the end of the document */
 2232             totalscore += 100000;
 2233             if (page >= pr->first)
 2234                 pageinside = 1;
 2235         }
 2236         else {                              /* Sequence of pages */
 2237             totalscore += pr->last - pr->first +1;
 2238             if (page >= pr->first && page <= pr->last)
 2239                 pageinside = 1;
 2240         }
 2241     }
 2242 
 2243     free_page_ranges(ranges);
 2244 
 2245     if (page == 0 || pageinside)
 2246         return totalscore;
 2247 
 2248     return 0;
 2249 }
 2250 
 2251 /* Set the options for a given page */
 2252 void set_options_for_page(int optset, int page)
 2253 {
 2254     int score, bestscore;
 2255     option_t *opt;
 2256     value_t *val, *bestvalue;
 2257     const char *ranges;
 2258     const char *optsetname;
 2259 
 2260     for (opt = optionlist; opt; opt = opt->next) {
 2261 
 2262         bestscore = 10000000;
 2263         bestvalue = NULL;
 2264         for (val = opt->valuelist; val; val = val->next) {
 2265 
 2266             optsetname = optionset_name(val->optionset);
 2267             if (!startswith(optsetname, "pages:"))
 2268                 continue;
 2269 
 2270             ranges = &optsetname[6]; /* after "pages:" */
 2271             score = get_page_score(ranges, page);
 2272             if (score && score < bestscore) {
 2273                 bestscore = score;
 2274                 bestvalue = val;
 2275             }
 2276         }
 2277 
 2278         if (bestvalue)
 2279             option_set_value(opt, optset, bestvalue->value);
 2280     }
 2281 }
 2282