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