+/*
+ * 'cups_collection_contains()' - Check whether test collection is contained in the matching collection.
+ */
+
+static int /* O - 1 on a match, 0 on a non-match */
+cups_collection_contains(ipp_t *test, /* I - Collection to test */
+ ipp_t *match) /* I - Matching values */
+{
+ int i, j, /* Looping vars */
+ mcount, /* Number of match values */
+ tcount; /* Number of test values */
+ ipp_attribute_t *tattr, /* Testing attribute */
+ *mattr; /* Matching attribute */
+ const char *tval; /* Testing string value */
+
+
+ for (mattr = ippFirstAttribute(match); mattr; mattr = ippNextAttribute(match))
+ {
+ if ((tattr = ippFindAttribute(test, ippGetName(mattr), IPP_TAG_ZERO)) == NULL)
+ return (0);
+
+ tcount = ippGetCount(tattr);
+
+ switch (ippGetValueTag(mattr))
+ {
+ case IPP_TAG_INTEGER :
+ case IPP_TAG_ENUM :
+ if (ippGetValueTag(tattr) != ippGetValueTag(mattr))
+ return (0);
+
+ for (i = 0; i < tcount; i ++)
+ {
+ if (!ippContainsInteger(mattr, ippGetInteger(tattr, i)))
+ return (0);
+ }
+ break;
+
+ case IPP_TAG_RANGE :
+ if (ippGetValueTag(tattr) != IPP_TAG_INTEGER)
+ return (0);
+
+ for (i = 0; i < tcount; i ++)
+ {
+ if (!ippContainsInteger(mattr, ippGetInteger(tattr, i)))
+ return (0);
+ }
+ break;
+
+ case IPP_TAG_BOOLEAN :
+ if (ippGetValueTag(tattr) != IPP_TAG_BOOLEAN || ippGetBoolean(tattr, 0) != ippGetBoolean(mattr, 0))
+ return (0);
+ break;
+
+ case IPP_TAG_TEXTLANG :
+ case IPP_TAG_NAMELANG :
+ case IPP_TAG_TEXT :
+ case IPP_TAG_NAME :
+ case IPP_TAG_KEYWORD :
+ case IPP_TAG_URI :
+ case IPP_TAG_URISCHEME :
+ case IPP_TAG_CHARSET :
+ case IPP_TAG_LANGUAGE :
+ case IPP_TAG_MIMETYPE :
+ for (i = 0; i < tcount; i ++)
+ {
+ if ((tval = ippGetString(tattr, i, NULL)) == NULL || !ippContainsString(mattr, tval))
+ return (0);
+ }
+ break;
+
+ case IPP_TAG_BEGIN_COLLECTION :
+ for (i = 0; i < tcount; i ++)
+ {
+ ipp_t *tcol = ippGetCollection(tattr, i);
+ /* Testing collection */
+
+ for (j = 0, mcount = ippGetCount(mattr); j < mcount; j ++)
+ if (!cups_collection_contains(tcol, ippGetCollection(mattr, j)))
+ return (0);
+ }
+ break;
+
+ default :
+ return (0);
+ }
+ }
+
+ return (1);
+}
+
+
+/*
+ * 'cups_collection_string()' - Convert an IPP collection to an option string.
+ */
+
+static size_t /* O - Number of bytes needed */
+cups_collection_string(
+ ipp_attribute_t *attr, /* I - Collection attribute */
+ char *buffer, /* I - String buffer */
+ size_t bufsize) /* I - Size of buffer */
+{
+ int i, j, /* Looping vars */
+ count, /* Number of collection values */
+ mcount; /* Number of member values */
+ ipp_t *col; /* Collection */
+ ipp_attribute_t *first, /* First member attribute */
+ *member; /* Member attribute */
+ char *bufptr, /* Pointer into buffer */
+ *bufend, /* End of buffer */
+ temp[100]; /* Temporary string */
+ const char *mptr; /* Pointer into member value */
+ int mlen; /* Length of octetString */
+
+
+ bufptr = buffer;
+ bufend = buffer + bufsize - 1;
+
+ for (i = 0, count = ippGetCount(attr); i < count; i ++)
+ {
+ col = ippGetCollection(attr, i);
+
+ if (i)
+ {
+ if (bufptr < bufend)
+ *bufptr++ = ',';
+ else
+ bufptr ++;
+ }
+
+ if (bufptr < bufend)
+ *bufptr++ = '{';
+ else
+ bufptr ++;
+
+ for (member = first = ippFirstAttribute(col); member; member = ippNextAttribute(col))
+ {
+ const char *mname = ippGetName(member);
+
+ if (member != first)
+ {
+ if (bufptr < bufend)
+ *bufptr++ = ' ';
+ else
+ bufptr ++;
+ }
+
+ if (ippGetValueTag(member) == IPP_TAG_BOOLEAN)
+ {
+ if (!ippGetBoolean(member, 0))
+ {
+ if (bufptr < bufend)
+ strlcpy(bufptr, "no", (size_t)(bufend - bufptr + 1));
+ bufptr += 2;
+ }
+
+ if (bufptr < bufend)
+ strlcpy(bufptr, mname, (size_t)(bufend - bufptr + 1));
+ bufptr += strlen(mname);
+ continue;
+ }
+
+ if (bufptr < bufend)
+ strlcpy(bufptr, mname, (size_t)(bufend - bufptr + 1));
+ bufptr += strlen(mname);
+
+ if (bufptr < bufend)
+ *bufptr++ = '=';
+ else
+ bufptr ++;
+
+ if (ippGetValueTag(member) == IPP_TAG_BEGIN_COLLECTION)
+ {
+ /*
+ * Convert sub-collection...
+ */
+
+ bufptr += cups_collection_string(member, bufptr, bufptr < bufend ? (size_t)(bufend - bufptr + 1) : 0);
+ }
+ else
+ {
+ /*
+ * Convert simple type...
+ */
+
+ for (j = 0, mcount = ippGetCount(member); j < mcount; j ++)
+ {
+ if (j)
+ {
+ if (bufptr < bufend)
+ *bufptr++ = ',';
+ else
+ bufptr ++;
+ }
+
+ switch (ippGetValueTag(member))
+ {
+ case IPP_TAG_INTEGER :
+ case IPP_TAG_ENUM :
+ bufptr += snprintf(bufptr, bufptr < bufend ? (size_t)(bufend - bufptr + 1) : 0, "%d", ippGetInteger(member, j));
+ break;
+
+ case IPP_TAG_STRING :
+ if (bufptr < bufend)
+ *bufptr++ = '\"';
+ else
+ bufptr ++;
+
+ for (mptr = (const char *)ippGetOctetString(member, j, &mlen); mlen > 0; mlen --, mptr ++)
+ {
+ if (*mptr == '\"' || *mptr == '\\')
+ {
+ if (bufptr < bufend)
+ *bufptr++ = '\\';
+ else
+ bufptr ++;
+ }
+
+ if (bufptr < bufend)
+ *bufptr++ = *mptr;
+ else
+ bufptr ++;
+ }
+
+ if (bufptr < bufend)
+ *bufptr++ = '\"';
+ else
+ bufptr ++;
+ break;
+
+ case IPP_TAG_DATE :
+ {
+ unsigned year; /* Year */
+ const ipp_uchar_t *date = ippGetDate(member, j);
+ /* Date value */
+
+ year = ((unsigned)date[0] << 8) + (unsigned)date[1];
+
+ if (date[9] == 0 && date[10] == 0)
+ snprintf(temp, sizeof(temp), "%04u-%02u-%02uT%02u:%02u:%02uZ", year, date[2], date[3], date[4], date[5], date[6]);
+ else
+ snprintf(temp, sizeof(temp), "%04u-%02u-%02uT%02u:%02u:%02u%c%02u%02u", year, date[2], date[3], date[4], date[5], date[6], date[8], date[9], date[10]);
+
+ if (bufptr < bufend)
+ strlcpy(bufptr, temp, (size_t)(bufend - bufptr + 1));
+
+ bufptr += strlen(temp);
+ }
+ break;
+
+ case IPP_TAG_RESOLUTION :
+ {
+ int xres, /* Horizontal resolution */
+ yres; /* Vertical resolution */
+ ipp_res_t units; /* Resolution units */
+
+ xres = ippGetResolution(member, j, &yres, &units);
+
+ if (xres == yres)
+ snprintf(temp, sizeof(temp), "%d%s", xres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
+ else
+ snprintf(temp, sizeof(temp), "%dx%d%s", xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
+
+ if (bufptr < bufend)
+ strlcpy(bufptr, temp, (size_t)(bufend - bufptr + 1));
+
+ bufptr += strlen(temp);
+ }
+ break;
+
+ case IPP_TAG_RANGE :
+ {
+ int lower, /* Lower bound */
+ upper; /* Upper bound */
+
+ lower = ippGetRange(member, j, &upper);
+
+ snprintf(temp, sizeof(temp), "%d-%d", lower, upper);
+
+ if (bufptr < bufend)
+ strlcpy(bufptr, temp, (size_t)(bufend - bufptr + 1));
+
+ bufptr += strlen(temp);
+ }
+ break;
+
+ case IPP_TAG_TEXTLANG :
+ case IPP_TAG_NAMELANG :
+ case IPP_TAG_TEXT :
+ case IPP_TAG_NAME :
+ case IPP_TAG_KEYWORD :
+ case IPP_TAG_URI :
+ case IPP_TAG_URISCHEME :
+ case IPP_TAG_CHARSET :
+ case IPP_TAG_LANGUAGE :
+ case IPP_TAG_MIMETYPE :
+ if (bufptr < bufend)
+ *bufptr++ = '\"';
+ else
+ bufptr ++;
+
+ for (mptr = ippGetString(member, j, NULL); *mptr; mptr ++)
+ {
+ if (*mptr == '\"' || *mptr == '\\')
+ {
+ if (bufptr < bufend)
+ *bufptr++ = '\\';
+ else
+ bufptr ++;
+ }
+
+ if (bufptr < bufend)
+ *bufptr++ = *mptr;
+ else
+ bufptr ++;
+ }
+
+ if (bufptr < bufend)
+ *bufptr++ = '\"';
+ else
+ bufptr ++;
+ break;
+
+ default :
+ break;
+ }
+ }
+ }
+ }
+
+ if (bufptr < bufend)
+ *bufptr++ = '}';
+ else
+ bufptr ++;
+ }
+
+ *bufptr = '\0';
+ return ((size_t)(bufptr - buffer + 1));
+}
+
+