]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/dest-options.c
Save work on documentation.
[thirdparty/cups.git] / cups / dest-options.c
CommitLineData
dcb445bc 1/*
7e86f2f6 2 * Destination option/media support for CUPS.
dcb445bc 3 *
7536de1a 4 * Copyright 2012-2017 by Apple Inc.
dcb445bc 5 *
7e86f2f6
MS
6 * These coded instructions, statements, and computer programs are the
7 * property of Apple Inc. and are protected by Federal copyright
8 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
9 * which should have been included with this file. If this file is
57b7b66b 10 * missing or damaged, see the license at "http://www.cups.org/".
dcb445bc 11 *
7e86f2f6 12 * This file is subject to the Apple OS-Developed Software exception.
dcb445bc
MS
13 */
14
15/*
16 * Include necessary headers...
17 */
18
19#include "cups-private.h"
20
21
6961465f
MS
22/*
23 * Local constants...
24 */
25
26#define _CUPS_MEDIA_READY_TTL 30 /* Life of xxx-ready values */
27
28
dcb445bc
MS
29/*
30 * Local functions...
31 */
32
a29fd7dd
MS
33static void cups_add_dconstres(cups_array_t *a, ipp_t *collection);
34static int cups_compare_dconstres(_cups_dconstres_t *a,
35 _cups_dconstres_t *b);
dcb445bc
MS
36static int cups_compare_media_db(_cups_media_db_t *a,
37 _cups_media_db_t *b);
38static _cups_media_db_t *cups_copy_media_db(_cups_media_db_t *mdb);
6961465f
MS
39static void cups_create_cached(http_t *http, cups_dinfo_t *dinfo,
40 unsigned flags);
a29fd7dd
MS
41static void cups_create_constraints(cups_dinfo_t *dinfo);
42static void cups_create_defaults(cups_dinfo_t *dinfo);
6961465f
MS
43static void cups_create_media_db(cups_dinfo_t *dinfo,
44 unsigned flags);
dcb445bc 45static void cups_free_media_db(_cups_media_db_t *mdb);
6961465f
MS
46static int cups_get_media_db(http_t *http, cups_dinfo_t *dinfo,
47 pwg_media_t *pwg, unsigned flags,
dcb445bc
MS
48 cups_size_t *size);
49static int cups_is_close_media_db(_cups_media_db_t *a,
50 _cups_media_db_t *b);
a29fd7dd
MS
51static cups_array_t *cups_test_constraints(cups_dinfo_t *dinfo,
52 const char *new_option,
53 const char *new_value,
54 int num_options,
55 cups_option_t *options,
56 int *num_conflicts,
57 cups_option_t **conflicts);
6961465f 58static void cups_update_ready(http_t *http, cups_dinfo_t *dinfo);
a29fd7dd 59
dcb445bc
MS
60
61/*
62 * 'cupsCheckDestSupported()' - Check that the option and value are supported
63 * by the destination.
64 *
65 * Returns 1 if supported, 0 otherwise.
66 *
8072030b 67 * @since CUPS 1.6/macOS 10.8@
dcb445bc
MS
68 */
69
70int /* O - 1 if supported, 0 otherwise */
71cupsCheckDestSupported(
72 http_t *http, /* I - Connection to destination */
73 cups_dest_t *dest, /* I - Destination */
74 cups_dinfo_t *dinfo, /* I - Destination information */
75 const char *option, /* I - Option */
76 const char *value) /* I - Value */
77{
78 int i; /* Looping var */
79 char temp[1024]; /* Temporary string */
80 int int_value; /* Integer value */
81 int xres_value, /* Horizontal resolution */
82 yres_value; /* Vertical resolution */
83 ipp_res_t units_value; /* Resolution units */
84 ipp_attribute_t *attr; /* Attribute */
85 _ipp_value_t *attrval; /* Current attribute value */
86
87
7536de1a
MS
88 /*
89 * Get the default connection as needed...
90 */
91
92 if (!http)
93 http = _cupsConnect();
94
dcb445bc
MS
95 /*
96 * Range check input...
97 */
98
99 if (!http || !dest || !dinfo || !option || !value)
100 return (0);
101
102 /*
103 * Lookup the attribute...
104 */
105
106 if (strstr(option, "-supported"))
107 attr = ippFindAttribute(dinfo->attrs, option, IPP_TAG_ZERO);
108 else
109 {
110 snprintf(temp, sizeof(temp), "%s-supported", option);
111 attr = ippFindAttribute(dinfo->attrs, temp, IPP_TAG_ZERO);
112 }
113
114 if (!attr)
115 return (0);
116
117 /*
118 * Compare values...
119 */
120
121 if (!strcmp(option, "media") && !strncmp(value, "custom_", 7))
122 {
123 /*
124 * Check range of custom media sizes...
125 */
126
6961465f 127 pwg_media_t *pwg; /* Current PWG media size info */
dcb445bc
MS
128 int min_width, /* Minimum width */
129 min_length, /* Minimum length */
130 max_width, /* Maximum width */
131 max_length; /* Maximum length */
132
133 /*
134 * Get the minimum and maximum size...
135 */
136
137 min_width = min_length = INT_MAX;
138 max_width = max_length = 0;
139
140 for (i = attr->num_values, attrval = attr->values;
141 i > 0;
142 i --, attrval ++)
143 {
144 if (!strncmp(attrval->string.text, "custom_min_", 11) &&
6961465f 145 (pwg = pwgMediaForPWG(attrval->string.text)) != NULL)
dcb445bc
MS
146 {
147 min_width = pwg->width;
148 min_length = pwg->length;
149 }
150 else if (!strncmp(attrval->string.text, "custom_max_", 11) &&
6961465f 151 (pwg = pwgMediaForPWG(attrval->string.text)) != NULL)
dcb445bc
MS
152 {
153 max_width = pwg->width;
154 max_length = pwg->length;
155 }
156 }
157
158 /*
159 * Check the range...
160 */
161
162 if (min_width < INT_MAX && max_width > 0 &&
6961465f 163 (pwg = pwgMediaForPWG(value)) != NULL &&
dcb445bc
MS
164 pwg->width >= min_width && pwg->width <= max_width &&
165 pwg->length >= min_length && pwg->length <= max_length)
166 return (1);
167 }
168 else
169 {
170 /*
171 * Check literal values...
172 */
173
174 switch (attr->value_tag)
175 {
176 case IPP_TAG_INTEGER :
177 case IPP_TAG_ENUM :
178 int_value = atoi(value);
179
180 for (i = 0; i < attr->num_values; i ++)
181 if (attr->values[i].integer == int_value)
182 return (1);
183 break;
184
185 case IPP_TAG_BOOLEAN :
186 return (attr->values[0].boolean);
187
a29fd7dd
MS
188 case IPP_TAG_RANGE :
189 int_value = atoi(value);
190
191 for (i = 0; i < attr->num_values; i ++)
192 if (int_value >= attr->values[i].range.lower &&
193 int_value <= attr->values[i].range.upper)
194 return (1);
195 break;
196
dcb445bc
MS
197 case IPP_TAG_RESOLUTION :
198 if (sscanf(value, "%dx%d%15s", &xres_value, &yres_value, temp) != 3)
199 {
200 if (sscanf(value, "%d%15s", &xres_value, temp) != 2)
201 return (0);
202
203 yres_value = xres_value;
204 }
205
206 if (!strcmp(temp, "dpi"))
207 units_value = IPP_RES_PER_INCH;
3e7fe0ca 208 else if (!strcmp(temp, "dpc") || !strcmp(temp, "dpcm"))
dcb445bc
MS
209 units_value = IPP_RES_PER_CM;
210 else
211 return (0);
212
213 for (i = attr->num_values, attrval = attr->values;
214 i > 0;
215 i --, attrval ++)
216 {
217 if (attrval->resolution.xres == xres_value &&
218 attrval->resolution.yres == yres_value &&
219 attrval->resolution.units == units_value)
220 return (1);
221 }
222 break;
223
224 case IPP_TAG_TEXT :
225 case IPP_TAG_NAME :
226 case IPP_TAG_KEYWORD :
227 case IPP_TAG_CHARSET :
228 case IPP_TAG_URI :
229 case IPP_TAG_URISCHEME :
230 case IPP_TAG_MIMETYPE :
231 case IPP_TAG_LANGUAGE :
232 case IPP_TAG_TEXTLANG :
233 case IPP_TAG_NAMELANG :
234 for (i = 0; i < attr->num_values; i ++)
235 if (!strcmp(attr->values[i].string.text, value))
236 return (1);
237 break;
238
239 default :
240 break;
241 }
242 }
243
244 /*
245 * If we get there the option+value is not supported...
246 */
247
248 return (0);
249}
250
251
252/*
253 * 'cupsCopyDestConflicts()' - Get conflicts and resolutions for a new
254 * option/value pair.
255 *
256 * "num_options" and "options" represent the currently selected options by the
257 * user. "new_option" and "new_value" are the setting the user has just
258 * changed.
259 *
a29fd7dd
MS
260 * Returns 1 if there is a conflict, 0 if there are no conflicts, and -1 if
261 * there was an unrecoverable error such as a resolver loop.
dcb445bc 262 *
5a9febac
MS
263 * If "num_conflicts" and "conflicts" are not @code NULL@, they are set to
264 * contain the list of conflicting option/value pairs. Similarly, if
265 * "num_resolved" and "resolved" are not @code NULL@ they will be set to the
266 * list of changes needed to resolve the conflict.
dcb445bc
MS
267 *
268 * If cupsCopyDestConflicts returns 1 but "num_resolved" and "resolved" are set
5a9febac 269 * to 0 and @code NULL@, respectively, then the conflict cannot be resolved.
dcb445bc 270 *
8072030b 271 * @since CUPS 1.6/macOS 10.8@
dcb445bc
MS
272 */
273
a29fd7dd 274int /* O - 1 if there is a conflict, 0 if none, -1 on error */
dcb445bc
MS
275cupsCopyDestConflicts(
276 http_t *http, /* I - Connection to destination */
277 cups_dest_t *dest, /* I - Destination */
278 cups_dinfo_t *dinfo, /* I - Destination information */
279 int num_options, /* I - Number of current options */
280 cups_option_t *options, /* I - Current options */
281 const char *new_option, /* I - New option */
282 const char *new_value, /* I - New value */
283 int *num_conflicts, /* O - Number of conflicting options */
284 cups_option_t **conflicts, /* O - Conflicting options */
285 int *num_resolved, /* O - Number of options to resolve */
286 cups_option_t **resolved) /* O - Resolved options */
287{
a29fd7dd
MS
288 int i, /* Looping var */
289 have_conflicts = 0, /* Do we have conflicts? */
290 changed, /* Did we change something? */
291 tries, /* Number of tries for resolution */
292 num_myconf = 0, /* My number of conflicting options */
293 num_myres = 0; /* My number of resolved options */
294 cups_option_t *myconf = NULL, /* My conflicting options */
295 *myres = NULL, /* My resolved options */
296 *myoption, /* My current option */
297 *option; /* Current option */
7e86f2f6 298 cups_array_t *active = NULL, /* Active conflicts */
a29fd7dd
MS
299 *pass = NULL, /* Resolvers for this pass */
300 *resolvers = NULL, /* Resolvers we have used */
301 *test; /* Test array for conflicts */
302 _cups_dconstres_t *c, /* Current constraint */
303 *r; /* Current resolver */
304 ipp_attribute_t *attr; /* Current attribute */
305 char value[2048]; /* Current attribute value as string */
306 const char *myvalue; /* Current value of an option */
307
308
dcb445bc
MS
309 /*
310 * Clear returned values...
311 */
312
313 if (num_conflicts)
314 *num_conflicts = 0;
315
316 if (conflicts)
317 *conflicts = NULL;
318
319 if (num_resolved)
320 *num_resolved = 0;
321
322 if (resolved)
323 *resolved = NULL;
324
7536de1a
MS
325 /*
326 * Get the default connection as needed...
327 */
328
329 if (!http)
330 http = _cupsConnect();
331
dcb445bc
MS
332 /*
333 * Range check input...
334 */
335
a29fd7dd 336 if (!http || !dest || !dinfo ||
dcb445bc
MS
337 (num_conflicts != NULL) != (conflicts != NULL) ||
338 (num_resolved != NULL) != (resolved != NULL))
339 return (0);
340
341 /*
a29fd7dd 342 * Load constraints as needed...
dcb445bc
MS
343 */
344
a29fd7dd
MS
345 if (!dinfo->constraints)
346 cups_create_constraints(dinfo);
dcb445bc 347
a29fd7dd
MS
348 if (cupsArrayCount(dinfo->constraints) == 0)
349 return (0);
350
351 if (!dinfo->num_defaults)
352 cups_create_defaults(dinfo);
353
354 /*
355 * If we are resolving, create a shadow array...
356 */
357
358 if (num_resolved)
359 {
360 for (i = num_options, option = options; i > 0; i --, option ++)
361 num_myres = cupsAddOption(option->name, option->value, num_myres, &myres);
362
363 if (new_option && new_value)
364 num_myres = cupsAddOption(new_option, new_value, num_myres, &myres);
365 }
366 else
367 {
368 num_myres = num_options;
369 myres = options;
370 }
371
372 /*
373 * Check for any conflicts...
374 */
375
376 if (num_resolved)
377 pass = cupsArrayNew((cups_array_func_t)cups_compare_dconstres, NULL);
378
379 for (tries = 0; tries < 100; tries ++)
380 {
381 /*
382 * Check for any conflicts...
383 */
384
385 if (num_conflicts || num_resolved)
386 {
387 cupsFreeOptions(num_myconf, myconf);
388
389 num_myconf = 0;
390 myconf = NULL;
391 active = cups_test_constraints(dinfo, new_option, new_value,
392 num_myres, myres, &num_myconf,
393 &myconf);
394 }
395 else
396 active = cups_test_constraints(dinfo, new_option, new_value, num_myres,
397 myres, NULL, NULL);
398
399 have_conflicts = (active != NULL);
400
401 if (!active || !num_resolved)
402 break; /* All done */
403
404 /*
405 * Scan the constraints that were triggered to apply resolvers...
406 */
407
408 if (!resolvers)
409 resolvers = cupsArrayNew((cups_array_func_t)cups_compare_dconstres, NULL);
410
411 for (c = (_cups_dconstres_t *)cupsArrayFirst(active), changed = 0;
412 c;
413 c = (_cups_dconstres_t *)cupsArrayNext(active))
414 {
415 if (cupsArrayFind(pass, c))
416 continue; /* Already applied this resolver... */
417
418 if (cupsArrayFind(resolvers, c))
419 {
420 DEBUG_printf(("1cupsCopyDestConflicts: Resolver loop with %s.",
421 c->name));
422 have_conflicts = -1;
423 goto cleanup;
424 }
425
426 if ((r = cupsArrayFind(dinfo->resolvers, c)) == NULL)
427 {
428 DEBUG_printf(("1cupsCopyDestConflicts: Resolver %s not found.",
429 c->name));
430 have_conflicts = -1;
431 goto cleanup;
432 }
433
434 /*
435 * Add the options from the resolver...
436 */
437
438 cupsArrayAdd(pass, r);
439 cupsArrayAdd(resolvers, r);
440
441 for (attr = ippFirstAttribute(r->collection);
442 attr;
443 attr = ippNextAttribute(r->collection))
444 {
445 if (new_option && !strcmp(attr->name, new_option))
446 continue; /* Ignore this if we just changed it */
447
448 if (ippAttributeString(attr, value, sizeof(value)) >= sizeof(value))
449 continue; /* Ignore if the value is too long */
450
451 if ((test = cups_test_constraints(dinfo, attr->name, value, num_myres,
452 myres, NULL, NULL)) == NULL)
453 {
454 /*
455 * That worked, flag it...
456 */
457
458 changed = 1;
459 }
460 else
461 cupsArrayDelete(test);
462
463 /*
464 * Add the option/value from the resolver regardless of whether it
465 * worked; this makes sure that we can cascade several changes to
466 * make things resolve...
467 */
468
469 num_myres = cupsAddOption(attr->name, value, num_myres, &myres);
470 }
471 }
472
473 if (!changed)
474 {
475 DEBUG_puts("1cupsCopyDestConflicts: Unable to resolve constraints.");
476 have_conflicts = -1;
477 goto cleanup;
478 }
479
480 cupsArrayClear(pass);
481
482 cupsArrayDelete(active);
483 active = NULL;
484 }
485
0fa6c7fa 486 if (tries >= 100)
a29fd7dd
MS
487 {
488 DEBUG_puts("1cupsCopyDestConflicts: Unable to resolve after 100 tries.");
489 have_conflicts = -1;
490 goto cleanup;
491 }
492
493 /*
494 * Copy resolved options as needed...
495 */
496
497 if (num_resolved)
498 {
499 for (i = num_myres, myoption = myres; i > 0; i --, myoption ++)
500 {
501 if ((myvalue = cupsGetOption(myoption->name, num_options,
502 options)) == NULL ||
503 strcmp(myvalue, myoption->value))
504 {
505 if (new_option && !strcmp(new_option, myoption->name) &&
506 new_value && !strcmp(new_value, myoption->value))
507 continue;
508
509 *num_resolved = cupsAddOption(myoption->name, myoption->value,
510 *num_resolved, resolved);
511 }
512 }
513 }
514
515 /*
516 * Clean up...
517 */
518
519 cleanup:
520
521 cupsArrayDelete(active);
522 cupsArrayDelete(pass);
523 cupsArrayDelete(resolvers);
524
525 if (num_resolved)
526 {
527 /*
528 * Free shadow copy of options...
529 */
530
531 cupsFreeOptions(num_myres, myres);
532 }
533
534 if (num_conflicts)
535 {
536 /*
537 * Return conflicting options to caller...
538 */
539
540 *num_conflicts = num_myconf;
541 *conflicts = myconf;
542 }
543 else
544 {
545 /*
546 * Free conflicting options...
547 */
548
549 cupsFreeOptions(num_myconf, myconf);
550 }
551
552 return (have_conflicts);
dcb445bc
MS
553}
554
555
556/*
557 * 'cupsCopyDestInfo()' - Get the supported values/capabilities for the
558 * destination.
559 *
560 * The caller is responsible for calling @link cupsFreeDestInfo@ on the return
561 * value. @code NULL@ is returned on error.
562 *
8072030b 563 * @since CUPS 1.6/macOS 10.8@
dcb445bc
MS
564 */
565
566cups_dinfo_t * /* O - Destination information */
567cupsCopyDestInfo(
568 http_t *http, /* I - Connection to destination */
569 cups_dest_t *dest) /* I - Destination */
570{
571 cups_dinfo_t *dinfo; /* Destination information */
572 ipp_t *request, /* Get-Printer-Attributes request */
573 *response; /* Supported attributes */
a29fd7dd
MS
574 int tries, /* Number of tries so far */
575 delay, /* Current retry delay */
576 prev_delay; /* Next retry delay */
dcb445bc
MS
577 const char *uri; /* Printer URI */
578 char resource[1024]; /* Resource path */
579 int version; /* IPP version */
580 ipp_status_t status; /* Status of request */
581 static const char * const requested_attrs[] =
582 { /* Requested attributes */
583 "job-template",
584 "media-col-database",
585 "printer-description"
586 };
587
588
807315e6 589 DEBUG_printf(("cupsCopyDestSupported(http=%p, dest=%p(%s))", (void *)http, (void *)dest, dest ? dest->name : ""));
dcb445bc 590
7536de1a
MS
591 /*
592 * Get the default connection as needed...
593 */
594
595 if (!http)
596 http = _cupsConnect();
597
dcb445bc
MS
598 /*
599 * Range check input...
600 */
601
602 if (!http || !dest)
603 return (NULL);
604
605 /*
606 * Get the printer URI and resource path...
607 */
608
609 if ((uri = _cupsGetDestResource(dest, resource, sizeof(resource))) == NULL)
610 return (NULL);
611
612 /*
613 * Get the supported attributes...
614 */
615
a29fd7dd
MS
616 delay = 1;
617 prev_delay = 1;
618 tries = 0;
619 version = 20;
dcb445bc
MS
620
621 do
622 {
623 /*
624 * Send a Get-Printer-Attributes request...
625 */
626
cb7f98ee 627 request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
dcb445bc
MS
628 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
629 uri);
6961465f
MS
630 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
631 "requesting-user-name", NULL, cupsUser());
dcb445bc
MS
632 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
633 "requested-attributes",
634 (int)(sizeof(requested_attrs) / sizeof(requested_attrs[0])),
635 NULL, requested_attrs);
636 response = cupsDoRequest(http, request, resource);
637 status = cupsLastError();
638
cb7f98ee 639 if (status > IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED)
dcb445bc
MS
640 {
641 DEBUG_printf(("cupsCopyDestSupported: Get-Printer-Attributes for '%s' "
642 "returned %s (%s)", dest->name, ippErrorString(status),
643 cupsLastErrorString()));
644
645 ippDelete(response);
646 response = NULL;
647
cb7f98ee 648 if (status == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED && version > 11)
dcb445bc 649 version = 11;
cb7f98ee 650 else if (status == IPP_STATUS_ERROR_BUSY)
a29fd7dd 651 {
7e86f2f6 652 sleep((unsigned)delay);
a29fd7dd
MS
653
654 delay = _cupsNextDelay(delay, &prev_delay);
655 }
dcb445bc
MS
656 else
657 return (NULL);
658 }
a29fd7dd
MS
659
660 tries ++;
dcb445bc 661 }
a29fd7dd
MS
662 while (!response && tries < 10);
663
664 if (!response)
665 return (NULL);
dcb445bc
MS
666
667 /*
668 * Allocate a cups_dinfo_t structure and return it...
669 */
670
671 if ((dinfo = calloc(1, sizeof(cups_dinfo_t))) == NULL)
672 {
cb7f98ee 673 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
dcb445bc
MS
674 ippDelete(response);
675 return (NULL);
676 }
677
6961465f 678 dinfo->version = version;
dcb445bc
MS
679 dinfo->uri = uri;
680 dinfo->resource = _cupsStrAlloc(resource);
681 dinfo->attrs = response;
682
683 return (dinfo);
684}
685
686
6961465f
MS
687/*
688 * 'cupsFindDestDefault()' - Find the default value(s) for the given option.
689 *
690 * The returned value is an IPP attribute. Use the @code ippGetBoolean@,
691 * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@,
692 * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@,
693 * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@
694 * functions to inspect the default value(s) as needed.
695 *
8072030b 696 * @since CUPS 1.7/macOS 10.9@
6961465f
MS
697 */
698
699ipp_attribute_t * /* O - Default attribute or @code NULL@ for none */
700cupsFindDestDefault(
701 http_t *http, /* I - Connection to destination */
702 cups_dest_t *dest, /* I - Destination */
703 cups_dinfo_t *dinfo, /* I - Destination information */
704 const char *option) /* I - Option/attribute name */
705{
706 char name[IPP_MAX_NAME]; /* Attribute name */
707
708
7536de1a
MS
709 /*
710 * Get the default connection as needed...
711 */
712
713 if (!http)
714 http = _cupsConnect();
715
6961465f
MS
716 /*
717 * Range check input...
718 */
719
720 if (!http || !dest || !dinfo || !option)
721 {
722 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
723 return (NULL);
724 }
725
726 /*
727 * Find and return the attribute...
728 */
729
730 snprintf(name, sizeof(name), "%s-default", option);
731 return (ippFindAttribute(dinfo->attrs, name, IPP_TAG_ZERO));
732}
733
46385a1a 734
6961465f
MS
735/*
736 * 'cupsFindDestReady()' - Find the default value(s) for the given option.
737 *
738 * The returned value is an IPP attribute. Use the @code ippGetBoolean@,
739 * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@,
740 * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@,
741 * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@
742 * functions to inspect the default value(s) as needed.
743 *
8072030b 744 * @since CUPS 1.7/macOS 10.9@
6961465f
MS
745 */
746
747ipp_attribute_t * /* O - Default attribute or @code NULL@ for none */
748cupsFindDestReady(
749 http_t *http, /* I - Connection to destination */
750 cups_dest_t *dest, /* I - Destination */
751 cups_dinfo_t *dinfo, /* I - Destination information */
752 const char *option) /* I - Option/attribute name */
753{
754 char name[IPP_MAX_NAME]; /* Attribute name */
755
756
7536de1a
MS
757 /*
758 * Get the default connection as needed...
759 */
760
761 if (!http)
762 http = _cupsConnect();
763
6961465f
MS
764 /*
765 * Range check input...
766 */
767
768 if (!http || !dest || !dinfo || !option)
769 {
770 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
771 return (NULL);
772 }
773
774 /*
775 * Find and return the attribute...
776 */
777
778 cups_update_ready(http, dinfo);
779
780 snprintf(name, sizeof(name), "%s-ready", option);
781 return (ippFindAttribute(dinfo->ready_attrs, name, IPP_TAG_ZERO));
782}
783
46385a1a 784
6961465f
MS
785/*
786 * 'cupsFindDestSupported()' - Find the default value(s) for the given option.
787 *
788 * The returned value is an IPP attribute. Use the @code ippGetBoolean@,
789 * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@,
790 * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@,
791 * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@
792 * functions to inspect the default value(s) as needed.
793 *
8072030b 794 * @since CUPS 1.7/macOS 10.9@
6961465f
MS
795 */
796
797ipp_attribute_t * /* O - Default attribute or @code NULL@ for none */
798cupsFindDestSupported(
799 http_t *http, /* I - Connection to destination */
800 cups_dest_t *dest, /* I - Destination */
801 cups_dinfo_t *dinfo, /* I - Destination information */
802 const char *option) /* I - Option/attribute name */
803{
804 char name[IPP_MAX_NAME]; /* Attribute name */
805
806
7536de1a
MS
807 /*
808 * Get the default connection as needed...
809 */
810
811 if (!http)
812 http = _cupsConnect();
813
6961465f
MS
814 /*
815 * Range check input...
816 */
817
818 if (!http || !dest || !dinfo || !option)
819 {
820 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
821 return (NULL);
822 }
823
824 /*
825 * Find and return the attribute...
826 */
827
828 snprintf(name, sizeof(name), "%s-supported", option);
829 return (ippFindAttribute(dinfo->attrs, name, IPP_TAG_ZERO));
830}
831
832
dcb445bc
MS
833/*
834 * 'cupsFreeDestInfo()' - Free destination information obtained using
835 * @link cupsCopyDestInfo@.
98d88c8d
MS
836 *
837 * @since CUPS 1.6/macOS 10.8@
dcb445bc
MS
838 */
839
840void
841cupsFreeDestInfo(cups_dinfo_t *dinfo) /* I - Destination information */
842{
843 /*
844 * Range check input...
845 */
846
847 if (!dinfo)
848 return;
849
850 /*
851 * Free memory and return...
852 */
853
854 _cupsStrFree(dinfo->resource);
855
dcb445bc 856 cupsArrayDelete(dinfo->constraints);
a29fd7dd 857 cupsArrayDelete(dinfo->resolvers);
dcb445bc
MS
858
859 cupsArrayDelete(dinfo->localizations);
860
861 cupsArrayDelete(dinfo->media_db);
862
6961465f
MS
863 cupsArrayDelete(dinfo->cached_db);
864
865 ippDelete(dinfo->ready_attrs);
866 cupsArrayDelete(dinfo->ready_db);
867
a29fd7dd
MS
868 ippDelete(dinfo->attrs);
869
dcb445bc
MS
870 free(dinfo);
871}
872
873
6961465f
MS
874/*
875 * 'cupsGetDestMediaByIndex()' - Get a media name, dimension, and margins for a
876 * specific size.
877 *
878 * The @code flags@ parameter determines which set of media are indexed. For
879 * example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will get the Nth
880 * borderless size supported by the printer.
881 *
8072030b 882 * @since CUPS 1.7/macOS 10.9@
6961465f
MS
883 */
884
885int /* O - 1 on success, 0 on failure */
886cupsGetDestMediaByIndex(
887 http_t *http, /* I - Connection to destination */
888 cups_dest_t *dest, /* I - Destination */
889 cups_dinfo_t *dinfo, /* I - Destination information */
890 int n, /* I - Media size number (0-based) */
891 unsigned flags, /* I - Media flags */
892 cups_size_t *size) /* O - Media size information */
893{
ae26bf70
MS
894 _cups_media_db_t *nsize; /* Size for N */
895 pwg_media_t *pwg; /* PWG media name for size */
6961465f
MS
896
897
7536de1a
MS
898 /*
899 * Get the default connection as needed...
900 */
901
902 if (!http)
903 http = _cupsConnect();
904
6961465f
MS
905 /*
906 * Range check input...
907 */
908
909 if (size)
910 memset(size, 0, sizeof(cups_size_t));
911
912 if (!http || !dest || !dinfo || n < 0 || !size)
913 {
914 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
915 return (0);
916 }
917
918 /*
919 * Load media list as needed...
920 */
921
922 if (flags & CUPS_MEDIA_FLAGS_READY)
923 cups_update_ready(http, dinfo);
924
925 if (!dinfo->cached_db || dinfo->cached_flags != flags)
926 cups_create_cached(http, dinfo, flags);
927
928 /*
929 * Copy the size over and return...
930 */
931
ae26bf70
MS
932 if ((nsize = (_cups_media_db_t *)cupsArrayIndex(dinfo->cached_db, n)) == NULL)
933 {
934 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
935 return (0);
936 }
503b54c9 937
ae26bf70
MS
938 if (nsize->size_name)
939 strlcpy(size->media, nsize->size_name, sizeof(size->media));
940 else if (nsize->key)
941 strlcpy(size->media, nsize->key, sizeof(size->media));
942 else if ((pwg = pwgMediaForSize(nsize->width, nsize->length)) != NULL)
943 strlcpy(size->media, pwg->pwg, sizeof(size->media));
944 else
6961465f
MS
945 {
946 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
947 return (0);
948 }
949
ae26bf70
MS
950 size->width = nsize->width;
951 size->length = nsize->length;
952 size->bottom = nsize->bottom;
953 size->left = nsize->left;
954 size->right = nsize->right;
955 size->top = nsize->top;
6961465f
MS
956
957 return (1);
958}
959
960
dcb445bc
MS
961/*
962 * 'cupsGetDestMediaByName()' - Get media names, dimensions, and margins.
963 *
5a9febac 964 * The "media" string is a PWG media name. "Flags" provides some matching
dcb445bc
MS
965 * guidance (multiple flags can be combined):
966 *
5a9febac
MS
967 * CUPS_MEDIA_FLAGS_DEFAULT = find the closest size supported by the printer,
968 * CUPS_MEDIA_FLAGS_BORDERLESS = find a borderless size,
969 * CUPS_MEDIA_FLAGS_DUPLEX = find a size compatible with 2-sided printing,
970 * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size, and
dcb445bc
MS
971 * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the
972 * size amongst the "ready" media.
973 *
974 * The matching result (if any) is returned in the "cups_size_t" structure.
975 *
976 * Returns 1 when there is a match and 0 if there is not a match.
977 *
8072030b 978 * @since CUPS 1.6/macOS 10.8@
dcb445bc
MS
979 */
980
981int /* O - 1 on match, 0 on failure */
982cupsGetDestMediaByName(
983 http_t *http, /* I - Connection to destination */
984 cups_dest_t *dest, /* I - Destination */
985 cups_dinfo_t *dinfo, /* I - Destination information */
986 const char *media, /* I - Media name */
987 unsigned flags, /* I - Media matching flags */
988 cups_size_t *size) /* O - Media size information */
989{
6961465f 990 pwg_media_t *pwg; /* PWG media info */
dcb445bc
MS
991
992
7536de1a
MS
993 /*
994 * Get the default connection as needed...
995 */
996
997 if (!http)
998 http = _cupsConnect();
999
dcb445bc
MS
1000 /*
1001 * Range check input...
1002 */
1003
1004 if (size)
1005 memset(size, 0, sizeof(cups_size_t));
1006
1007 if (!http || !dest || !dinfo || !media || !size)
1008 {
cb7f98ee 1009 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
dcb445bc
MS
1010 return (0);
1011 }
1012
1013 /*
1014 * Lookup the media size name...
1015 */
1016
6961465f
MS
1017 if ((pwg = pwgMediaForPWG(media)) == NULL)
1018 if ((pwg = pwgMediaForLegacy(media)) == NULL)
dcb445bc
MS
1019 {
1020 DEBUG_printf(("1cupsGetDestMediaByName: Unknown size '%s'.", media));
cb7f98ee 1021 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unknown media size name."), 1);
dcb445bc
MS
1022 return (0);
1023 }
1024
1025 /*
1026 * Lookup the size...
1027 */
1028
6961465f 1029 return (cups_get_media_db(http, dinfo, pwg, flags, size));
dcb445bc
MS
1030}
1031
1032
1033/*
1034 * 'cupsGetDestMediaBySize()' - Get media names, dimensions, and margins.
1035 *
5a9febac
MS
1036 * "Width" and "length" are the dimensions in hundredths of millimeters.
1037 * "Flags" provides some matching guidance (multiple flags can be combined):
dcb445bc 1038 *
5a9febac
MS
1039 * CUPS_MEDIA_FLAGS_DEFAULT = find the closest size supported by the printer,
1040 * CUPS_MEDIA_FLAGS_BORDERLESS = find a borderless size,
1041 * CUPS_MEDIA_FLAGS_DUPLEX = find a size compatible with 2-sided printing,
1042 * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size, and
dcb445bc
MS
1043 * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the
1044 * size amongst the "ready" media.
1045 *
1046 * The matching result (if any) is returned in the "cups_size_t" structure.
1047 *
1048 * Returns 1 when there is a match and 0 if there is not a match.
1049 *
8072030b 1050 * @since CUPS 1.6/macOS 10.8@
dcb445bc
MS
1051 */
1052
1053int /* O - 1 on match, 0 on failure */
1054cupsGetDestMediaBySize(
1055 http_t *http, /* I - Connection to destination */
1056 cups_dest_t *dest, /* I - Destination */
1057 cups_dinfo_t *dinfo, /* I - Destination information */
1058 int width, /* I - Media width in hundredths of
1059 * of millimeters */
1060 int length, /* I - Media length in hundredths of
1061 * of millimeters */
1062 unsigned flags, /* I - Media matching flags */
1063 cups_size_t *size) /* O - Media size information */
1064{
6961465f 1065 pwg_media_t *pwg; /* PWG media info */
dcb445bc
MS
1066
1067
7536de1a
MS
1068 /*
1069 * Get the default connection as needed...
1070 */
1071
1072 if (!http)
1073 http = _cupsConnect();
1074
dcb445bc
MS
1075 /*
1076 * Range check input...
1077 */
1078
1079 if (size)
1080 memset(size, 0, sizeof(cups_size_t));
1081
1082 if (!http || !dest || !dinfo || width <= 0 || length <= 0 || !size)
1083 {
cb7f98ee 1084 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
dcb445bc
MS
1085 return (0);
1086 }
1087
1088 /*
1089 * Lookup the media size name...
1090 */
1091
6961465f 1092 if ((pwg = pwgMediaForSize(width, length)) == NULL)
dcb445bc
MS
1093 {
1094 DEBUG_printf(("1cupsGetDestMediaBySize: Invalid size %dx%d.", width,
1095 length));
cb7f98ee 1096 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Invalid media size."), 1);
dcb445bc
MS
1097 return (0);
1098 }
1099
1100 /*
1101 * Lookup the size...
1102 */
1103
6961465f
MS
1104 return (cups_get_media_db(http, dinfo, pwg, flags, size));
1105}
1106
1107
1108/*
1109 * 'cupsGetDestMediaCount()' - Get the number of sizes supported by a
1110 * destination.
1111 *
1112 * The @code flags@ parameter determines the set of media sizes that are
1113 * counted. For example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will return
1114 * the number of borderless sizes.
1115 *
8072030b 1116 * @since CUPS 1.7/macOS 10.9@
6961465f
MS
1117 */
1118
1119int /* O - Number of sizes */
1120cupsGetDestMediaCount(
1121 http_t *http, /* I - Connection to destination */
1122 cups_dest_t *dest, /* I - Destination */
1123 cups_dinfo_t *dinfo, /* I - Destination information */
1124 unsigned flags) /* I - Media flags */
1125{
7536de1a
MS
1126 /*
1127 * Get the default connection as needed...
1128 */
1129
1130 if (!http)
1131 http = _cupsConnect();
1132
6961465f
MS
1133 /*
1134 * Range check input...
1135 */
1136
1137 if (!http || !dest || !dinfo)
1138 {
1139 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
1140 return (0);
1141 }
1142
1143 /*
1144 * Load media list as needed...
1145 */
1146
1147 if (flags & CUPS_MEDIA_FLAGS_READY)
1148 cups_update_ready(http, dinfo);
1149
1150 if (!dinfo->cached_db || dinfo->cached_flags != flags)
1151 cups_create_cached(http, dinfo, flags);
1152
1153 return (cupsArrayCount(dinfo->cached_db));
1154}
1155
1156
1157/*
1158 * 'cupsGetDestMediaDefault()' - Get the default size for a destination.
1159 *
1160 * The @code flags@ parameter determines which default size is returned. For
1161 * example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will return the default
1162 * borderless size, typically US Letter or A4, but sometimes 4x6 photo media.
1163 *
8072030b 1164 * @since CUPS 1.7/macOS 10.9@
6961465f
MS
1165 */
1166
1167int /* O - 1 on success, 0 on failure */
1168cupsGetDestMediaDefault(
1169 http_t *http, /* I - Connection to destination */
1170 cups_dest_t *dest, /* I - Destination */
1171 cups_dinfo_t *dinfo, /* I - Destination information */
1172 unsigned flags, /* I - Media flags */
1173 cups_size_t *size) /* O - Media size information */
1174{
1175 const char *media; /* Default media size */
1176
1177
7536de1a
MS
1178 /*
1179 * Get the default connection as needed...
1180 */
1181
1182 if (!http)
1183 http = _cupsConnect();
1184
6961465f
MS
1185 /*
1186 * Range check input...
1187 */
1188
1189 if (size)
1190 memset(size, 0, sizeof(cups_size_t));
1191
1192 if (!http || !dest || !dinfo || !size)
1193 {
1194 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
1195 return (0);
1196 }
1197
1198 /*
1199 * Get the default media size, if any...
1200 */
1201
1202 if ((media = cupsGetOption("media", dest->num_options,
1203 dest->options)) == NULL)
1204 media = "na_letter_8.5x11in";
1205
1206 if (cupsGetDestMediaByName(http, dest, dinfo, media, flags, size))
1207 return (1);
1208
1209 if (strcmp(media, "na_letter_8.5x11in") &&
1210 cupsGetDestMediaByName(http, dest, dinfo, "iso_a4_210x297mm", flags,
1211 size))
1212 return (1);
1213
1214 if (strcmp(media, "iso_a4_210x297mm") &&
1215 cupsGetDestMediaByName(http, dest, dinfo, "na_letter_8.5x11in", flags,
1216 size))
1217 return (1);
1218
1219 if ((flags & CUPS_MEDIA_FLAGS_BORDERLESS) &&
1220 cupsGetDestMediaByName(http, dest, dinfo, "na_index_4x6in", flags, size))
1221 return (1);
1222
1223 /*
1224 * Fall back to the first matching media size...
1225 */
1226
7e86f2f6 1227 return (cupsGetDestMediaByIndex(http, dest, dinfo, 0, flags, size));
dcb445bc
MS
1228}
1229
1230
a29fd7dd
MS
1231/*
1232 * 'cups_add_dconstres()' - Add a constraint or resolver to an array.
1233 */
1234
1235static void
1236cups_add_dconstres(
1237 cups_array_t *a, /* I - Array */
1238 ipp_t *collection) /* I - Collection value */
1239{
1240 ipp_attribute_t *attr; /* Attribute */
1241 _cups_dconstres_t *temp; /* Current constraint/resolver */
1242
1243
1244 if ((attr = ippFindAttribute(collection, "resolver-name",
1245 IPP_TAG_NAME)) == NULL)
1246 return;
1247
1248 if ((temp = calloc(1, sizeof(_cups_dconstres_t))) == NULL)
1249 return;
1250
1251 temp->name = attr->values[0].string.text;
1252 temp->collection = collection;
1253
1254 cupsArrayAdd(a, temp);
1255}
1256
1257
1258/*
1259 * 'cups_compare_dconstres()' - Compare to resolver entries.
1260 */
1261
1262static int /* O - Result of comparison */
1263cups_compare_dconstres(
1264 _cups_dconstres_t *a, /* I - First resolver */
1265 _cups_dconstres_t *b) /* I - Second resolver */
1266{
1267 return (strcmp(a->name, b->name));
1268}
1269
1270
dcb445bc
MS
1271/*
1272 * 'cups_compare_media_db()' - Compare two media entries.
1273 */
1274
1275static int /* O - Result of comparison */
1276cups_compare_media_db(
1277 _cups_media_db_t *a, /* I - First media entries */
1278 _cups_media_db_t *b) /* I - Second media entries */
1279{
1280 int result; /* Result of comparison */
1281
1282
1283 if ((result = a->width - b->width) == 0)
1284 result = a->length - b->length;
1285
1286 return (result);
1287}
1288
1289
1290/*
1291 * 'cups_copy_media_db()' - Copy a media entry.
1292 */
1293
1294static _cups_media_db_t * /* O - New media entry */
1295cups_copy_media_db(
1296 _cups_media_db_t *mdb) /* I - Media entry to copy */
1297{
1298 _cups_media_db_t *temp; /* New media entry */
1299
1300
1301 if ((temp = calloc(1, sizeof(_cups_media_db_t))) == NULL)
1302 return (NULL);
1303
1304 if (mdb->color)
1305 temp->color = _cupsStrAlloc(mdb->color);
1306 if (mdb->key)
1307 temp->key = _cupsStrAlloc(mdb->key);
1308 if (mdb->info)
1309 temp->info = _cupsStrAlloc(mdb->info);
1310 if (mdb->size_name)
1311 temp->size_name = _cupsStrAlloc(mdb->size_name);
1312 if (mdb->source)
1313 temp->source = _cupsStrAlloc(mdb->source);
1314 if (mdb->type)
1315 temp->type = _cupsStrAlloc(mdb->type);
1316
1317 temp->width = mdb->width;
1318 temp->length = mdb->length;
1319 temp->bottom = mdb->bottom;
1320 temp->left = mdb->left;
1321 temp->right = mdb->right;
1322 temp->top = mdb->top;
1323
1324 return (temp);
1325}
1326
1327
6961465f
MS
1328/*
1329 * 'cups_create_cached()' - Create the media selection cache.
1330 */
1331
1332static void
1333cups_create_cached(http_t *http, /* I - Connection to destination */
1334 cups_dinfo_t *dinfo, /* I - Destination information */
1335 unsigned flags) /* I - Media selection flags */
1336{
1337 cups_array_t *db; /* Media database array to use */
1338 _cups_media_db_t *mdb, /* Media database entry */
1339 *first; /* First entry this size */
1340
1341
807315e6 1342 DEBUG_printf(("3cups_create_cached(http=%p, dinfo=%p, flags=%u)", (void *)http, (void *)dinfo, flags));
ae26bf70 1343
6961465f
MS
1344 if (dinfo->cached_db)
1345 cupsArrayDelete(dinfo->cached_db);
1346
1347 dinfo->cached_db = cupsArrayNew(NULL, NULL);
1348 dinfo->cached_flags = flags;
1349
1350 if (flags & CUPS_MEDIA_FLAGS_READY)
1351 {
ae26bf70
MS
1352 DEBUG_puts("4cups_create_cached: ready media");
1353
6961465f
MS
1354 cups_update_ready(http, dinfo);
1355 db = dinfo->ready_db;
1356 }
1357 else
1358 {
ae26bf70
MS
1359 DEBUG_puts("4cups_create_cached: supported media");
1360
6961465f
MS
1361 if (!dinfo->media_db)
1362 cups_create_media_db(dinfo, CUPS_MEDIA_FLAGS_DEFAULT);
1363
1364 db = dinfo->media_db;
1365 }
1366
1367 for (mdb = (_cups_media_db_t *)cupsArrayFirst(db), first = mdb;
1368 mdb;
1369 mdb = (_cups_media_db_t *)cupsArrayNext(db))
1370 {
807315e6 1371 DEBUG_printf(("4cups_create_cached: %p key=\"%s\", type=\"%s\", %dx%d, B%d L%d R%d T%d", (void *)mdb, mdb->key, mdb->type, mdb->width, mdb->length, mdb->bottom, mdb->left, mdb->right, mdb->top));
ae26bf70 1372
6961465f
MS
1373 if (flags & CUPS_MEDIA_FLAGS_BORDERLESS)
1374 {
1375 if (!mdb->left && !mdb->right && !mdb->top && !mdb->bottom)
ae26bf70 1376 {
807315e6 1377 DEBUG_printf(("4cups_create_cached: add %p", (void *)mdb));
6961465f 1378 cupsArrayAdd(dinfo->cached_db, mdb);
ae26bf70 1379 }
6961465f
MS
1380 }
1381 else if (flags & CUPS_MEDIA_FLAGS_DUPLEX)
1382 {
1383 if (first->width != mdb->width || first->length != mdb->length)
1384 {
807315e6 1385 DEBUG_printf(("4cups_create_cached: add %p", (void *)first));
6961465f
MS
1386 cupsArrayAdd(dinfo->cached_db, first);
1387 first = mdb;
1388 }
ae26bf70
MS
1389 else if (mdb->left >= first->left && mdb->right >= first->right && mdb->top >= first->top && mdb->bottom >= first->bottom &&
1390 (mdb->left != first->left || mdb->right != first->right || mdb->top != first->top || mdb->bottom != first->bottom))
6961465f
MS
1391 first = mdb;
1392 }
5b69e934 1393 else
ae26bf70 1394 {
807315e6 1395 DEBUG_printf(("4cups_create_cached: add %p", (void *)mdb));
5b69e934 1396 cupsArrayAdd(dinfo->cached_db, mdb);
ae26bf70 1397 }
6961465f
MS
1398 }
1399
1400 if (flags & CUPS_MEDIA_FLAGS_DUPLEX)
ae26bf70 1401 {
807315e6 1402 DEBUG_printf(("4cups_create_cached: add %p", (void *)first));
6961465f 1403 cupsArrayAdd(dinfo->cached_db, first);
ae26bf70 1404 }
6961465f
MS
1405}
1406
1407
a29fd7dd
MS
1408/*
1409 * 'cups_create_constraints()' - Create the constraints and resolvers arrays.
1410 */
1411
1412static void
1413cups_create_constraints(
1414 cups_dinfo_t *dinfo) /* I - Destination information */
1415{
1416 int i; /* Looping var */
1417 ipp_attribute_t *attr; /* Attribute */
1418 _ipp_value_t *val; /* Current value */
1419
1420
1421 dinfo->constraints = cupsArrayNew3(NULL, NULL, NULL, 0, NULL,
1422 (cups_afree_func_t)free);
1423 dinfo->resolvers = cupsArrayNew3((cups_array_func_t)cups_compare_dconstres,
1424 NULL, NULL, 0, NULL,
1425 (cups_afree_func_t)free);
1426
1427 if ((attr = ippFindAttribute(dinfo->attrs, "job-constraints-supported",
1428 IPP_TAG_BEGIN_COLLECTION)) != NULL)
1429 {
1430 for (i = attr->num_values, val = attr->values; i > 0; i --, val ++)
1431 cups_add_dconstres(dinfo->constraints, val->collection);
1432 }
1433
1434 if ((attr = ippFindAttribute(dinfo->attrs, "job-resolvers-supported",
1435 IPP_TAG_BEGIN_COLLECTION)) != NULL)
1436 {
1437 for (i = attr->num_values, val = attr->values; i > 0; i --, val ++)
1438 cups_add_dconstres(dinfo->resolvers, val->collection);
1439 }
1440}
1441
1442
1443/*
1444 * 'cups_create_defaults()' - Create the -default option array.
1445 *
1446 * TODO: Need to support collection defaults...
1447 */
1448
1449static void
1450cups_create_defaults(
1451 cups_dinfo_t *dinfo) /* I - Destination information */
1452{
1453 ipp_attribute_t *attr; /* Current attribute */
1454 char name[IPP_MAX_NAME + 1],
1455 /* Current name */
1456 *nameptr, /* Pointer into current name */
1457 value[2048]; /* Current value */
1458
1459
1460 /*
1461 * Iterate through the printer attributes looking for xxx-default and adding
1462 * xxx=value to the defaults option array.
1463 */
1464
1465 for (attr = ippFirstAttribute(dinfo->attrs);
1466 attr;
1467 attr = ippNextAttribute(dinfo->attrs))
1468 {
1469 if (!attr->name || attr->group_tag != IPP_TAG_PRINTER)
1470 continue;
1471
1472 if (attr->value_tag == IPP_TAG_BEGIN_COLLECTION)
1473 continue; /* TODO: STR #4096 */
1474
1475 if ((nameptr = attr->name + strlen(attr->name) - 8) <= attr->name ||
1476 strcmp(nameptr, "-default"))
1477 continue;
1478
1479 strlcpy(name, attr->name, sizeof(name));
1480 if ((nameptr = name + strlen(name) - 8) <= name ||
1481 strcmp(nameptr, "-default"))
1482 continue;
1483
1484 *nameptr = '\0';
1485
1486 if (ippAttributeString(attr, value, sizeof(value)) >= sizeof(value))
1487 continue;
1488
1489 dinfo->num_defaults = cupsAddOption(name, value, dinfo->num_defaults,
1490 &dinfo->defaults);
1491 }
1492}
1493
1494
dcb445bc
MS
1495/*
1496 * 'cups_create_media_db()' - Create the media database.
1497 */
1498
1499static void
1500cups_create_media_db(
6961465f
MS
1501 cups_dinfo_t *dinfo, /* I - Destination information */
1502 unsigned flags) /* I - Media flags */
dcb445bc
MS
1503{
1504 int i; /* Looping var */
1505 _ipp_value_t *val; /* Current value */
1506 ipp_attribute_t *media_col_db, /* media-col-database */
1507 *media_attr, /* media-xxx */
1508 *x_dimension, /* x-dimension */
1509 *y_dimension; /* y-dimension */
6961465f
MS
1510 pwg_media_t *pwg; /* PWG media info */
1511 cups_array_t *db; /* New media database array */
dcb445bc
MS
1512 _cups_media_db_t mdb; /* Media entry */
1513
1514
6961465f
MS
1515 db = cupsArrayNew3((cups_array_func_t)cups_compare_media_db,
1516 NULL, NULL, 0,
1517 (cups_acopy_func_t)cups_copy_media_db,
1518 (cups_afree_func_t)cups_free_media_db);
dcb445bc 1519
6961465f
MS
1520 if (flags == CUPS_MEDIA_FLAGS_READY)
1521 {
1522 dinfo->ready_db = db;
1523
1524 media_col_db = ippFindAttribute(dinfo->ready_attrs, "media-col-ready",
1525 IPP_TAG_BEGIN_COLLECTION);
1526 media_attr = ippFindAttribute(dinfo->ready_attrs, "media-ready",
1527 IPP_TAG_ZERO);
1528 }
1529 else
1530 {
1531 dinfo->media_db = db;
1532 dinfo->min_size.width = INT_MAX;
1533 dinfo->min_size.length = INT_MAX;
1534 dinfo->max_size.width = 0;
1535 dinfo->max_size.length = 0;
1536
1537 media_col_db = ippFindAttribute(dinfo->attrs, "media-col-database",
1538 IPP_TAG_BEGIN_COLLECTION);
1539 media_attr = ippFindAttribute(dinfo->attrs, "media-supported",
1540 IPP_TAG_ZERO);
1541 }
1542
1543 if (media_col_db)
dcb445bc
MS
1544 {
1545 _ipp_value_t *custom = NULL; /* Custom size range value */
1546
1547 for (i = media_col_db->num_values, val = media_col_db->values;
1548 i > 0;
1549 i --, val ++)
1550 {
1551 memset(&mdb, 0, sizeof(mdb));
1552
1553 if ((media_attr = ippFindAttribute(val->collection, "media-size",
1554 IPP_TAG_BEGIN_COLLECTION)) != NULL)
1555 {
1556 ipp_t *media_size = media_attr->values[0].collection;
1557 /* media-size collection value */
1558
1559 if ((x_dimension = ippFindAttribute(media_size, "x-dimension",
6961465f 1560 IPP_TAG_INTEGER)) != NULL &&
dcb445bc
MS
1561 (y_dimension = ippFindAttribute(media_size, "y-dimension",
1562 IPP_TAG_INTEGER)) != NULL)
1563 {
6961465f
MS
1564 /*
1565 * Fixed size...
1566 */
1567
dcb445bc
MS
1568 mdb.width = x_dimension->values[0].integer;
1569 mdb.length = y_dimension->values[0].integer;
1570 }
6961465f
MS
1571 else if ((x_dimension = ippFindAttribute(media_size, "x-dimension",
1572 IPP_TAG_INTEGER)) != NULL &&
1573 (y_dimension = ippFindAttribute(media_size, "y-dimension",
1574 IPP_TAG_RANGE)) != NULL)
1575 {
1576 /*
1577 * Roll limits...
1578 */
1579
1580 mdb.width = x_dimension->values[0].integer;
1581 mdb.length = y_dimension->values[0].range.upper;
1582 }
1583 else if (flags != CUPS_MEDIA_FLAGS_READY &&
1584 (x_dimension = ippFindAttribute(media_size, "x-dimension",
1585 IPP_TAG_RANGE)) != NULL &&
dcb445bc
MS
1586 (y_dimension = ippFindAttribute(media_size, "y-dimension",
1587 IPP_TAG_RANGE)) != NULL)
1588 {
1589 /*
1590 * Custom size range; save this as the custom size value with default
1591 * margins, then continue; we'll capture the real margins below...
1592 */
1593
1594 custom = val;
1595
1596 dinfo->min_size.width = x_dimension->values[0].range.lower;
1597 dinfo->min_size.length = y_dimension->values[0].range.lower;
1598 dinfo->min_size.left =
1599 dinfo->min_size.right = 635; /* Default 1/4" side margins */
1600 dinfo->min_size.top =
1601 dinfo->min_size.bottom = 1270; /* Default 1/2" top/bottom margins */
1602
1603 dinfo->max_size.width = x_dimension->values[0].range.upper;
1604 dinfo->max_size.length = y_dimension->values[0].range.upper;
1605 dinfo->max_size.left =
1606 dinfo->max_size.right = 635; /* Default 1/4" side margins */
1607 dinfo->max_size.top =
1608 dinfo->max_size.bottom = 1270; /* Default 1/2" top/bottom margins */
dcb445bc
MS
1609 continue;
1610 }
1611 }
1612
1613 if ((media_attr = ippFindAttribute(val->collection, "media-color",
1614 IPP_TAG_ZERO)) != NULL &&
1615 (media_attr->value_tag == IPP_TAG_NAME ||
1616 media_attr->value_tag == IPP_TAG_NAMELANG ||
1617 media_attr->value_tag == IPP_TAG_KEYWORD))
1618 mdb.color = media_attr->values[0].string.text;
1619
1620 if ((media_attr = ippFindAttribute(val->collection, "media-info",
1621 IPP_TAG_TEXT)) != NULL)
1622 mdb.info = media_attr->values[0].string.text;
1623
1624 if ((media_attr = ippFindAttribute(val->collection, "media-key",
1625 IPP_TAG_ZERO)) != NULL &&
1626 (media_attr->value_tag == IPP_TAG_NAME ||
1627 media_attr->value_tag == IPP_TAG_NAMELANG ||
1628 media_attr->value_tag == IPP_TAG_KEYWORD))
1629 mdb.key = media_attr->values[0].string.text;
1630
1631 if ((media_attr = ippFindAttribute(val->collection, "media-size-name",
1632 IPP_TAG_ZERO)) != NULL &&
1633 (media_attr->value_tag == IPP_TAG_NAME ||
1634 media_attr->value_tag == IPP_TAG_NAMELANG ||
1635 media_attr->value_tag == IPP_TAG_KEYWORD))
1636 mdb.size_name = media_attr->values[0].string.text;
1637
1638 if ((media_attr = ippFindAttribute(val->collection, "media-source",
1639 IPP_TAG_ZERO)) != NULL &&
1640 (media_attr->value_tag == IPP_TAG_NAME ||
1641 media_attr->value_tag == IPP_TAG_NAMELANG ||
1642 media_attr->value_tag == IPP_TAG_KEYWORD))
1643 mdb.source = media_attr->values[0].string.text;
1644
1645 if ((media_attr = ippFindAttribute(val->collection, "media-type",
1646 IPP_TAG_ZERO)) != NULL &&
1647 (media_attr->value_tag == IPP_TAG_NAME ||
1648 media_attr->value_tag == IPP_TAG_NAMELANG ||
1649 media_attr->value_tag == IPP_TAG_KEYWORD))
1650 mdb.type = media_attr->values[0].string.text;
1651
1652 if ((media_attr = ippFindAttribute(val->collection, "media-bottom-margin",
1653 IPP_TAG_INTEGER)) != NULL)
1654 mdb.bottom = media_attr->values[0].integer;
1655
1656 if ((media_attr = ippFindAttribute(val->collection, "media-left-margin",
1657 IPP_TAG_INTEGER)) != NULL)
1658 mdb.left = media_attr->values[0].integer;
1659
1660 if ((media_attr = ippFindAttribute(val->collection, "media-right-margin",
1661 IPP_TAG_INTEGER)) != NULL)
1662 mdb.right = media_attr->values[0].integer;
1663
1664 if ((media_attr = ippFindAttribute(val->collection, "media-top-margin",
1665 IPP_TAG_INTEGER)) != NULL)
1666 mdb.top = media_attr->values[0].integer;
1667
6961465f 1668 cupsArrayAdd(db, &mdb);
dcb445bc
MS
1669 }
1670
1671 if (custom)
1672 {
1673 if ((media_attr = ippFindAttribute(custom->collection,
1674 "media-bottom-margin",
1675 IPP_TAG_INTEGER)) != NULL)
1676 {
1677 dinfo->min_size.top =
1678 dinfo->max_size.top = media_attr->values[0].integer;
1679 }
1680
1681 if ((media_attr = ippFindAttribute(custom->collection,
1682 "media-left-margin",
1683 IPP_TAG_INTEGER)) != NULL)
1684 {
1685 dinfo->min_size.left =
1686 dinfo->max_size.left = media_attr->values[0].integer;
1687 }
1688
1689 if ((media_attr = ippFindAttribute(custom->collection,
1690 "media-right-margin",
1691 IPP_TAG_INTEGER)) != NULL)
1692 {
1693 dinfo->min_size.right =
1694 dinfo->max_size.right = media_attr->values[0].integer;
1695 }
1696
1697 if ((media_attr = ippFindAttribute(custom->collection,
1698 "media-top-margin",
1699 IPP_TAG_INTEGER)) != NULL)
1700 {
1701 dinfo->min_size.top =
1702 dinfo->max_size.top = media_attr->values[0].integer;
1703 }
1704 }
1705 }
6961465f 1706 else if (media_attr &&
dcb445bc
MS
1707 (media_attr->value_tag == IPP_TAG_NAME ||
1708 media_attr->value_tag == IPP_TAG_NAMELANG ||
1709 media_attr->value_tag == IPP_TAG_KEYWORD))
1710 {
1711 memset(&mdb, 0, sizeof(mdb));
1712
1713 mdb.left =
1714 mdb.right = 635; /* Default 1/4" side margins */
1715 mdb.top =
1716 mdb.bottom = 1270; /* Default 1/2" top/bottom margins */
1717
82cc1f9a 1718 for (i = media_attr->num_values, val = media_attr->values;
dcb445bc
MS
1719 i > 0;
1720 i --, val ++)
1721 {
6961465f
MS
1722 if ((pwg = pwgMediaForPWG(val->string.text)) == NULL)
1723 if ((pwg = pwgMediaForLegacy(val->string.text)) == NULL)
dcb445bc
MS
1724 {
1725 DEBUG_printf(("3cups_create_media_db: Ignoring unknown size '%s'.",
1726 val->string.text));
1727 continue;
1728 }
1729
1730 mdb.width = pwg->width;
1731 mdb.length = pwg->length;
1732
6961465f
MS
1733 if (flags != CUPS_MEDIA_FLAGS_READY &&
1734 !strncmp(val->string.text, "custom_min_", 11))
dcb445bc
MS
1735 {
1736 mdb.size_name = NULL;
1737 dinfo->min_size = mdb;
1738 }
6961465f
MS
1739 else if (flags != CUPS_MEDIA_FLAGS_READY &&
1740 !strncmp(val->string.text, "custom_max_", 11))
dcb445bc
MS
1741 {
1742 mdb.size_name = NULL;
1743 dinfo->max_size = mdb;
1744 }
1745 else
1746 {
1747 mdb.size_name = val->string.text;
1748
6961465f 1749 cupsArrayAdd(db, &mdb);
dcb445bc
MS
1750 }
1751 }
1752 }
1753}
1754
1755
1756/*
1757 * 'cups_free_media_cb()' - Free a media entry.
1758 */
1759
1760static void
1761cups_free_media_db(
1762 _cups_media_db_t *mdb) /* I - Media entry to free */
1763{
1764 if (mdb->color)
1765 _cupsStrFree(mdb->color);
1766 if (mdb->key)
1767 _cupsStrFree(mdb->key);
1768 if (mdb->info)
1769 _cupsStrFree(mdb->info);
1770 if (mdb->size_name)
1771 _cupsStrFree(mdb->size_name);
1772 if (mdb->source)
1773 _cupsStrFree(mdb->source);
1774 if (mdb->type)
1775 _cupsStrFree(mdb->type);
1776
1777 free(mdb);
1778}
1779
1780
1781/*
1782 * 'cups_get_media_db()' - Lookup the media entry for a given size.
1783 */
1784
1785static int /* O - 1 on match, 0 on failure */
6961465f
MS
1786cups_get_media_db(http_t *http, /* I - Connection to destination */
1787 cups_dinfo_t *dinfo, /* I - Destination information */
1788 pwg_media_t *pwg, /* I - PWG media info */
dcb445bc 1789 unsigned flags, /* I - Media matching flags */
6961465f 1790 cups_size_t *size) /* O - Media size/margin/name info */
dcb445bc 1791{
6961465f 1792 cups_array_t *db; /* Which media database to query */
dcb445bc
MS
1793 _cups_media_db_t *mdb, /* Current media database entry */
1794 *best = NULL, /* Best matching entry */
1795 key; /* Search key */
1796
1797
1798 /*
1799 * Create the media database as needed...
1800 */
1801
6961465f
MS
1802 if (flags & CUPS_MEDIA_FLAGS_READY)
1803 {
1804 cups_update_ready(http, dinfo);
1805 db = dinfo->ready_db;
1806 }
1807 else
1808 {
1809 if (!dinfo->media_db)
1810 cups_create_media_db(dinfo, CUPS_MEDIA_FLAGS_DEFAULT);
1811
1812 db = dinfo->media_db;
1813 }
dcb445bc
MS
1814
1815 /*
1816 * Find a match...
1817 */
1818
1819 memset(&key, 0, sizeof(key));
1820 key.width = pwg->width;
1821 key.length = pwg->length;
1822
6961465f 1823 if ((mdb = cupsArrayFind(db, &key)) != NULL)
dcb445bc
MS
1824 {
1825 /*
1826 * Found an exact match, let's figure out the best margins for the flags
1827 * supplied...
1828 */
1829
1830 best = mdb;
1831
1832 if (flags & CUPS_MEDIA_FLAGS_BORDERLESS)
1833 {
1834 /*
1835 * Look for the smallest margins...
1836 */
1837
ae26bf70 1838 if (best->left != 0 || best->right != 0 || best->top != 0 || best->bottom != 0)
dcb445bc 1839 {
6961465f 1840 for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
dcb445bc 1841 mdb && !cups_compare_media_db(mdb, &key);
6961465f 1842 mdb = (_cups_media_db_t *)cupsArrayNext(db))
dcb445bc
MS
1843 {
1844 if (mdb->left <= best->left && mdb->right <= best->right &&
1845 mdb->top <= best->top && mdb->bottom <= best->bottom)
1846 {
1847 best = mdb;
1848 if (mdb->left == 0 && mdb->right == 0 && mdb->bottom == 0 &&
1849 mdb->top == 0)
1850 break;
1851 }
1852 }
1853 }
1854
1855 /*
1856 * If we need an exact match, return no-match if the size is not
1857 * borderless.
1858 */
1859
1860 if ((flags & CUPS_MEDIA_FLAGS_EXACT) &&
1861 (best->left || best->right || best->top || best->bottom))
1862 return (0);
1863 }
1864 else if (flags & CUPS_MEDIA_FLAGS_DUPLEX)
1865 {
1866 /*
1867 * Look for the largest margins...
1868 */
1869
6961465f 1870 for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
dcb445bc 1871 mdb && !cups_compare_media_db(mdb, &key);
6961465f 1872 mdb = (_cups_media_db_t *)cupsArrayNext(db))
dcb445bc
MS
1873 {
1874 if (mdb->left >= best->left && mdb->right >= best->right &&
ae26bf70
MS
1875 mdb->top >= best->top && mdb->bottom >= best->bottom &&
1876 (mdb->bottom != best->bottom || mdb->left != best->left || mdb->right != best->right || mdb->top != best->top))
dcb445bc
MS
1877 best = mdb;
1878 }
1879 }
1880 else
1881 {
1882 /*
1883 * Look for the smallest non-zero margins...
1884 */
1885
6961465f 1886 for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
dcb445bc 1887 mdb && !cups_compare_media_db(mdb, &key);
6961465f 1888 mdb = (_cups_media_db_t *)cupsArrayNext(db))
dcb445bc
MS
1889 {
1890 if (((mdb->left > 0 && mdb->left <= best->left) || best->left == 0) &&
ae26bf70 1891 ((mdb->right > 0 && mdb->right <= best->right) || best->right == 0) &&
dcb445bc 1892 ((mdb->top > 0 && mdb->top <= best->top) || best->top == 0) &&
ae26bf70
MS
1893 ((mdb->bottom > 0 && mdb->bottom <= best->bottom) || best->bottom == 0) &&
1894 (mdb->bottom != best->bottom || mdb->left != best->left || mdb->right != best->right || mdb->top != best->top))
dcb445bc
MS
1895 best = mdb;
1896 }
1897 }
1898 }
1899 else if (flags & CUPS_MEDIA_FLAGS_EXACT)
1900 {
1901 /*
1902 * See if we can do this as a custom size...
1903 */
1904
1905 if (pwg->width < dinfo->min_size.width ||
1906 pwg->width > dinfo->max_size.width ||
1907 pwg->length < dinfo->min_size.length ||
1908 pwg->length > dinfo->max_size.length)
1909 return (0); /* Out of range */
1910
1911 if ((flags & CUPS_MEDIA_FLAGS_BORDERLESS) &&
1912 (dinfo->min_size.left > 0 || dinfo->min_size.right > 0 ||
1913 dinfo->min_size.top > 0 || dinfo->min_size.bottom > 0))
1914 return (0); /* Not borderless */
1915
1916 key.size_name = (char *)pwg->pwg;
1917 key.bottom = dinfo->min_size.bottom;
1918 key.left = dinfo->min_size.left;
1919 key.right = dinfo->min_size.right;
1920 key.top = dinfo->min_size.top;
1921
1922 best = &key;
1923 }
1924 else if (pwg->width >= dinfo->min_size.width &&
1925 pwg->width <= dinfo->max_size.width &&
1926 pwg->length >= dinfo->min_size.length &&
1927 pwg->length <= dinfo->max_size.length)
1928 {
1929 /*
1930 * Map to custom size...
1931 */
1932
1933 key.size_name = (char *)pwg->pwg;
1934 key.bottom = dinfo->min_size.bottom;
1935 key.left = dinfo->min_size.left;
1936 key.right = dinfo->min_size.right;
1937 key.top = dinfo->min_size.top;
1938
1939 best = &key;
1940 }
1941 else
1942 {
1943 /*
1944 * Find a close size...
1945 */
1946
6961465f 1947 for (mdb = (_cups_media_db_t *)cupsArrayFirst(db);
dcb445bc 1948 mdb;
6961465f 1949 mdb = (_cups_media_db_t *)cupsArrayNext(db))
dcb445bc
MS
1950 if (cups_is_close_media_db(mdb, &key))
1951 break;
1952
1953 if (!mdb)
1954 return (0);
1955
1956 best = mdb;
1957
1958 if (flags & CUPS_MEDIA_FLAGS_BORDERLESS)
1959 {
1960 /*
1961 * Look for the smallest margins...
1962 */
1963
1964 if (best->left != 0 || best->right != 0 || best->top != 0 ||
1965 best->bottom != 0)
1966 {
6961465f 1967 for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
dcb445bc 1968 mdb && cups_is_close_media_db(mdb, &key);
6961465f 1969 mdb = (_cups_media_db_t *)cupsArrayNext(db))
dcb445bc
MS
1970 {
1971 if (mdb->left <= best->left && mdb->right <= best->right &&
ae26bf70
MS
1972 mdb->top <= best->top && mdb->bottom <= best->bottom &&
1973 (mdb->bottom != best->bottom || mdb->left != best->left || mdb->right != best->right || mdb->top != best->top))
dcb445bc
MS
1974 {
1975 best = mdb;
1976 if (mdb->left == 0 && mdb->right == 0 && mdb->bottom == 0 &&
1977 mdb->top == 0)
1978 break;
1979 }
1980 }
1981 }
1982 }
1983 else if (flags & CUPS_MEDIA_FLAGS_DUPLEX)
1984 {
1985 /*
1986 * Look for the largest margins...
1987 */
1988
6961465f 1989 for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
dcb445bc 1990 mdb && cups_is_close_media_db(mdb, &key);
6961465f 1991 mdb = (_cups_media_db_t *)cupsArrayNext(db))
dcb445bc
MS
1992 {
1993 if (mdb->left >= best->left && mdb->right >= best->right &&
ae26bf70
MS
1994 mdb->top >= best->top && mdb->bottom >= best->bottom &&
1995 (mdb->bottom != best->bottom || mdb->left != best->left || mdb->right != best->right || mdb->top != best->top))
dcb445bc
MS
1996 best = mdb;
1997 }
1998 }
1999 else
2000 {
2001 /*
2002 * Look for the smallest non-zero margins...
2003 */
2004
6961465f 2005 for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
dcb445bc 2006 mdb && cups_is_close_media_db(mdb, &key);
6961465f 2007 mdb = (_cups_media_db_t *)cupsArrayNext(db))
dcb445bc
MS
2008 {
2009 if (((mdb->left > 0 && mdb->left <= best->left) || best->left == 0) &&
2010 ((mdb->right > 0 && mdb->right <= best->right) ||
2011 best->right == 0) &&
2012 ((mdb->top > 0 && mdb->top <= best->top) || best->top == 0) &&
2013 ((mdb->bottom > 0 && mdb->bottom <= best->bottom) ||
ae26bf70
MS
2014 best->bottom == 0) &&
2015 (mdb->bottom != best->bottom || mdb->left != best->left || mdb->right != best->right || mdb->top != best->top))
dcb445bc
MS
2016 best = mdb;
2017 }
2018 }
2019 }
2020
2021 if (best)
2022 {
2023 /*
2024 * Return the matching size...
2025 */
2026
2027 if (best->size_name)
2028 strlcpy(size->media, best->size_name, sizeof(size->media));
2029 else if (best->key)
2030 strlcpy(size->media, best->key, sizeof(size->media));
2031 else
2032 strlcpy(size->media, pwg->pwg, sizeof(size->media));
2033
2034 size->width = best->width;
2035 size->length = best->length;
2036 size->bottom = best->bottom;
2037 size->left = best->left;
2038 size->right = best->right;
2039 size->top = best->top;
2040
2041 return (1);
2042 }
2043
2044 return (0);
2045}
2046
2047
2048/*
2049 * 'cups_is_close_media_db()' - Compare two media entries to see if they are
2050 * close to the same size.
2051 *
2052 * Currently we use 5 points (from PostScript) as the matching range...
2053 */
2054
2055static int /* O - 1 if the sizes are close */
2056cups_is_close_media_db(
2057 _cups_media_db_t *a, /* I - First media entries */
2058 _cups_media_db_t *b) /* I - Second media entries */
2059{
2060 int dwidth, /* Difference in width */
2061 dlength; /* Difference in length */
2062
2063
2064 dwidth = a->width - b->width;
2065 dlength = a->length - b->length;
2066
2067 return (dwidth >= -176 && dwidth <= 176 &&
2068 dlength >= -176 && dlength <= 176);
2069}
2070
2071
a29fd7dd
MS
2072/*
2073 * 'cups_test_constraints()' - Test constraints.
2074 *
2075 * TODO: STR #4096 - Need to properly support media-col contraints...
2076 */
2077
2078static cups_array_t * /* O - Active constraints */
2079cups_test_constraints(
2080 cups_dinfo_t *dinfo, /* I - Destination information */
2081 const char *new_option, /* I - Newly selected option */
2082 const char *new_value, /* I - Newly selected value */
2083 int num_options, /* I - Number of options */
2084 cups_option_t *options, /* I - Options */
2085 int *num_conflicts, /* O - Number of conflicting options */
2086 cups_option_t **conflicts) /* O - Conflicting options */
2087{
2088 int i, /* Looping var */
2089 match; /* Value matches? */
2090 int num_matching; /* Number of matching options */
2091 cups_option_t *matching; /* Matching options */
2092 _cups_dconstres_t *c; /* Current constraint */
2093 cups_array_t *active = NULL; /* Active constraints */
2094 ipp_attribute_t *attr; /* Current attribute */
2095 _ipp_value_t *attrval; /* Current attribute value */
2096 const char *value; /* Current value */
2097 char temp[1024]; /* Temporary string */
2098 int int_value; /* Integer value */
2099 int xres_value, /* Horizontal resolution */
2100 yres_value; /* Vertical resolution */
2101 ipp_res_t units_value; /* Resolution units */
2102
2103
2104 for (c = (_cups_dconstres_t *)cupsArrayFirst(dinfo->constraints);
2105 c;
2106 c = (_cups_dconstres_t *)cupsArrayNext(dinfo->constraints))
2107 {
2108 num_matching = 0;
2109 matching = NULL;
2110
2111 for (attr = ippFirstAttribute(c->collection);
2112 attr;
2113 attr = ippNextAttribute(c->collection))
2114 {
2115 if (attr->value_tag == IPP_TAG_BEGIN_COLLECTION)
2116 break; /* TODO: STR #4096 */
2117
2118 /*
2119 * Get the value for the current attribute in the constraint...
2120 */
2121
2122 if (new_option && new_value && !strcmp(attr->name, new_option))
2123 value = new_value;
2124 else if ((value = cupsGetOption(attr->name, num_options,
2125 options)) == NULL)
2126 value = cupsGetOption(attr->name, dinfo->num_defaults, dinfo->defaults);
2127
2128 if (!value)
2129 {
2130 /*
2131 * Not set so this constraint does not apply...
2132 */
2133
2134 break;
2135 }
2136
2137 match = 0;
2138
2139 switch (attr->value_tag)
2140 {
2141 case IPP_TAG_INTEGER :
2142 case IPP_TAG_ENUM :
2143 int_value = atoi(value);
2144
2145 for (i = attr->num_values, attrval = attr->values;
2146 i > 0;
2147 i --, attrval ++)
2148 {
2149 if (attrval->integer == int_value)
2150 {
2151 match = 1;
2152 break;
2153 }
2154 }
2155 break;
2156
2157 case IPP_TAG_BOOLEAN :
2158 int_value = !strcmp(value, "true");
2159
2160 for (i = attr->num_values, attrval = attr->values;
2161 i > 0;
2162 i --, attrval ++)
2163 {
2164 if (attrval->boolean == int_value)
2165 {
2166 match = 1;
2167 break;
2168 }
2169 }
2170 break;
2171
2172 case IPP_TAG_RANGE :
2173 int_value = atoi(value);
2174
2175 for (i = attr->num_values, attrval = attr->values;
2176 i > 0;
2177 i --, attrval ++)
2178 {
2179 if (int_value >= attrval->range.lower &&
2180 int_value <= attrval->range.upper)
2181 {
2182 match = 1;
2183 break;
2184 }
2185 }
2186 break;
2187
2188 case IPP_TAG_RESOLUTION :
2189 if (sscanf(value, "%dx%d%15s", &xres_value, &yres_value, temp) != 3)
2190 {
2191 if (sscanf(value, "%d%15s", &xres_value, temp) != 2)
2192 break;
2193
2194 yres_value = xres_value;
2195 }
2196
2197 if (!strcmp(temp, "dpi"))
2198 units_value = IPP_RES_PER_INCH;
2199 else if (!strcmp(temp, "dpc") || !strcmp(temp, "dpcm"))
2200 units_value = IPP_RES_PER_CM;
2201 else
2202 break;
2203
2204 for (i = attr->num_values, attrval = attr->values;
2205 i > 0;
2206 i --, attrval ++)
2207 {
2208 if (attrval->resolution.xres == xres_value &&
2209 attrval->resolution.yres == yres_value &&
2210 attrval->resolution.units == units_value)
2211 {
2212 match = 1;
2213 break;
2214 }
2215 }
2216 break;
2217
2218 case IPP_TAG_TEXT :
2219 case IPP_TAG_NAME :
2220 case IPP_TAG_KEYWORD :
2221 case IPP_TAG_CHARSET :
2222 case IPP_TAG_URI :
2223 case IPP_TAG_URISCHEME :
2224 case IPP_TAG_MIMETYPE :
2225 case IPP_TAG_LANGUAGE :
2226 case IPP_TAG_TEXTLANG :
2227 case IPP_TAG_NAMELANG :
2228 for (i = attr->num_values, attrval = attr->values;
2229 i > 0;
2230 i --, attrval ++)
2231 {
2232 if (!strcmp(attrval->string.text, value))
2233 {
2234 match = 1;
2235 break;
2236 }
2237 }
2238 break;
2239
2240 default :
2241 break;
2242 }
2243
2244 if (!match)
2245 break;
2246
2247 num_matching = cupsAddOption(attr->name, value, num_matching, &matching);
2248 }
2249
2250 if (!attr)
2251 {
2252 if (!active)
2253 active = cupsArrayNew(NULL, NULL);
2254
2255 cupsArrayAdd(active, c);
2256
2257 if (num_conflicts && conflicts)
2258 {
2259 cups_option_t *moption; /* Matching option */
2260
2261 for (i = num_matching, moption = matching; i > 0; i --, moption ++)
2262 *num_conflicts = cupsAddOption(moption->name, moption->value,
2263 *num_conflicts, conflicts);
2264 }
2265 }
2266
2267 cupsFreeOptions(num_matching, matching);
2268 }
2269
2270 return (active);
2271}
2272
2273
6961465f
MS
2274/*
2275 * 'cups_update_ready()' - Update xxx-ready attributes for the printer.
2276 */
2277
2278static void
2279cups_update_ready(http_t *http, /* I - Connection to destination */
2280 cups_dinfo_t *dinfo) /* I - Destination information */
2281{
2282 ipp_t *request; /* Get-Printer-Attributes request */
2283 static const char * const pattrs[] = /* Printer attributes we want */
2284 {
2285 "finishings-col-ready",
2286 "finishings-ready",
2287 "job-finishings-col-ready",
2288 "job-finishings-ready",
2289 "media-col-ready",
2290 "media-ready"
2291 };
2292
2293
2294 /*
2295 * Don't update more than once every 30 seconds...
2296 */
2297
2298 if ((time(NULL) - dinfo->ready_time) < _CUPS_MEDIA_READY_TTL)
2299 return;
2300
2301 /*
2302 * Free any previous results...
2303 */
2304
2305 if (dinfo->cached_flags & CUPS_MEDIA_FLAGS_READY)
2306 {
2307 cupsArrayDelete(dinfo->cached_db);
2308 dinfo->cached_db = NULL;
2309 dinfo->cached_flags = CUPS_MEDIA_FLAGS_DEFAULT;
2310 }
2311
2312 ippDelete(dinfo->ready_attrs);
2313 dinfo->ready_attrs = NULL;
2314
2315 cupsArrayDelete(dinfo->ready_db);
2316 dinfo->ready_db = NULL;
2317
2318 /*
2319 * Query the xxx-ready values...
2320 */
2321
2322 request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
2323 ippSetVersion(request, dinfo->version / 10, dinfo->version % 10);
2324
2325 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
2326 dinfo->uri);
2327 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
2328 NULL, cupsUser());
7e86f2f6 2329 ippAddStrings(request, IPP_TAG_OPERATION, IPP_CONST_TAG(IPP_TAG_KEYWORD), "requested-attributes", (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
6961465f
MS
2330
2331 dinfo->ready_attrs = cupsDoRequest(http, request, dinfo->resource);
2332
2333 /*
2334 * Update the ready media database...
2335 */
2336
2337 cups_create_media_db(dinfo, CUPS_MEDIA_FLAGS_READY);
2338
2339 /*
2340 * Update last lookup time and return...
2341 */
2342
2343 dinfo->ready_time = time(NULL);
2344}