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 ¶m->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 _log("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