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