[xml] patch: Functions to parse and create URI query strings



Attached is a patch against libxml2 svn which provides functions for parsing up and creating query strings, like:

  <someurl>?field1=value1&field2=value2

into [(field1, value1), (field2, value2)].

The semantics of query strings don't seem to be very well defined. Where there could be ambiguities, I have looked at what Perl CGI.pm does and implemented that.

I've added a fairly comprehensive test suite for the code. With the patch we pass the old and new tests.

The current uri->query field is always unescaped during parsing. I have changed so it always stored in its raw form. This because otherwise it's impossible to parse query strings such as: file:///tmp/test.html?test=%26&second=%26 which can be generated by web browsers. If anyone was relying on the current semantics, then it seems to me that they cannot parse such query strings correctly.

Rich.

--
Emerging Technologies, Red Hat  http://et.redhat.com/~rjones/
64 Baker Street, London, W1U 7DF     Mobile: +44 7866 314 421

Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod
Street, Windsor, Berkshire, SL4 1TE, United Kingdom.
Registered in England and Wales under Company Registration No. 3798903
Directors: Michael Cunningham (USA), Charlie Peters (USA) and David
Owens (Ireland)
Index: testURIQuery.c
===================================================================
--- testURIQuery.c      (revision 0)
+++ testURIQuery.c      (revision 0)
@@ -0,0 +1,157 @@
+/*
+ * testURIQuery.c : test the xmlURIQuery* functions.
+ *
+ * See Copyright for the status of this software.
+ *
+ * Author: Richard W.M. Jones <rjones redhat com>
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "libxml.h"
+
+#include <libxml/xmlmemory.h>
+#include <libxml/uri.h>
+#include <libxml/globals.h>
+
+/* This code reads in the list of URLs, one per line, from stdin.
+ * For each one it writes out several variations which exercise
+ * all the xmlURIQuery* functions.
+ */
+int
+main (void)
+{
+    const char *query;
+    char line[1024], *str = NULL;
+    xmlURIPtr uri = NULL;
+    xmlURIQueryFieldsPtr fields = NULL, field;
+    int errors = 0;
+    int b, len;
+
+    while (fgets (line, sizeof line, stdin)) {
+       /* Strip trailing \n. */
+       len = xmlStrlen (BAD_CAST line);
+       if (len > 0 && line[len-1] == '\n')
+           line[len-1] = '\0';
+
+       /* Ignore lines which begin with '#' and blank lines. */
+       if (line[0] == '\0' || line[0] == '#')
+           continue;
+
+       /* Parse the URI. */
+       uri = xmlParseURI (line);
+       if (!uri) {
+           printf ("testURIQuery: xmlParseURI: %s: failed\n", line);
+           errors++;
+           goto next;
+       }
+
+       printf ("%s\n", line);
+       query = uri->query;
+       printf ("\tquery string = %s\n", query ? query : "NULL");
+
+       /* Parse the query string and write out the list of fields. */
+       if (xmlURIQueryParse (query, NULL, &fields) != 0) {
+           printf ("testURIQuery: xmlURIQueryParse: failed\n");
+           errors++;
+           goto next;
+       }
+       for (field = fields; field; field = field->next) {
+           printf ("\tname = %s\n\tvalue = %s\n",
+                   field->name ? field->name : "NULL",
+                   field->value ? field->value : "NULL");
+           if (field->ignore)
+               printf ("testURIQuery: field->ignore should be 0\n");
+       }
+
+       /* Recreate query string with all fields. */
+       if (xmlURIQueryCreate (fields, NULL, &str) != 0) {
+           printf ("testURIQuery: xmlURIQueryCreate: failed\n");
+           errors++;
+           goto next;
+       }
+       printf ("\treassembled query = %s\n", str ? str : "NULL");
+       if (str) xmlFree (str);
+       str = NULL;
+
+       /* Mark some of the fields to be ignored and reassemble. */
+       for (field = fields; field; field = field->next) {
+           if (xmlStrncasecmp (BAD_CAST field->name, BAD_CAST "del", 3) == 0)
+               field->ignore = 1;
+       }
+       if (xmlURIQueryCreate (fields, NULL, &str) != 0) {
+           printf ("testURIQuery: xmlURIQueryCreate: failed\n");
+           errors++;
+           goto next;
+       }
+       printf ("\treassembled query - del* = %s\n", str ? str : "NULL");
+       if (str) xmlFree (str);
+       str = NULL;
+
+       xmlFreeURIQueryFields (fields);
+       fields = NULL;
+
+       /* Try the high-level Exists function on the original URI. */
+       if (xmlURIQueryExists (query, "check", &b) != 0) {
+           printf ("testURIQuery: xmlURIQueryExists: failed\n");
+           errors++;
+           goto next;
+       }
+       printf ("\tcheck field exists = %s\n", b ? "true" : "false");
+
+       /* Try the high-level IsTrue function on the original URI. */
+       if (xmlURIQueryIsTrue (query, "check", &b) != 0) {
+           printf ("testURIQuery: xmlURIQueryIsTrue: failed\n");
+           errors++;
+           goto next;
+       }
+       printf ("\tcheck field is true = %s\n", b ? "true" : "false");
+
+       /* Try the high-level GetSingle function on the original URI. */
+       if (xmlURIQueryGetSingle (query, "check", &str) != 0) {
+           printf ("testURIQuery: xmlURIQueryGetSingle: failed\n");
+           errors++;
+           goto next;
+       }
+       printf ("\tcheck field = %s\n", str ? str : "not present");
+       if (str) xmlFree (str);
+       str = NULL;
+
+       /* Try the high-level GetMultiple function on the original URI. */
+       if (xmlURIQueryGetMultiple (query, "check", &fields) != 0) {
+           printf ("testURIQuery: xmlURIQueryGetMultiple: failed\n");
+           errors++;
+           goto next;
+       }
+       for (field = fields; field; field = field->next) {
+           printf ("\t\tcheck field name = %s\n\t\tvalue = %s\n",
+                   field->name ? field->name : "NULL",
+                   field->value ? field->value : "NULL");
+           if (field->ignore)
+               printf ("testURIQuery: field->ignore should be 0\n");
+       }
+       xmlFreeURIQueryFields (fields);
+       fields = NULL;
+
+       /* Try the high-level Remove function on the original URI. */
+       if (xmlURIQueryRemove (query, "remove", &str) != 0) {
+           printf ("testURIQuery: xmlURIQueryRemove: failed\n");
+           errors++;
+           goto next;
+       }
+       printf ("\treassembled query - remove = %s\n", str ? str : "NULL");
+       if (str) xmlFree (str);
+       str = NULL;
+
+    next:
+       if (str) xmlFree (str);
+       if (fields) xmlFreeURIQueryFields (fields);
+       if (uri) xmlFreeURI (uri);
+
+       fflush (stdout);
+    }
+
+    xmlMemoryDump();
+    return errors ? 1 : 0;
+}
Index: uri.c
===================================================================
--- uri.c       (revision 3604)
+++ uri.c       (working copy)
@@ -17,6 +17,7 @@
 #include <libxml/uri.h>
 #include <libxml/globals.h>
 #include <libxml/xmlerror.h>
+#include <libxml/tree.h>
 
 /************************************************************************
  *                                                                     *
@@ -1031,12 +1032,11 @@
     }
 
     if (uri->query) {
-        segment =
-            xmlURIEscapeStr(BAD_CAST uri->query, BAD_CAST ";/?:@&=+,$");
-        NULLCHK(segment)
+       /* Assume the query has already been escaped.  This is the only
+        * sensible way to deal with queries like: first=%26&second=%26
+        */
         ret = xmlStrcat(ret, BAD_CAST "?");
-        ret = xmlStrcat(ret, segment);
-        xmlFree(segment);
+        ret = xmlStrcat(ret, BAD_CAST uri->query);
     }
 
     if (uri->opaque) {
@@ -1129,10 +1129,10 @@
     if (uri != NULL) {
         if (uri->query != NULL)
             xmlFree(uri->query);
-       if (uri->cleanup & 2)
-           uri->query = STRNDUP(*str, cur - *str);
-       else
-           uri->query = xmlURIUnescapeString(*str, cur - *str, NULL);
+       /* Do not attempt to unescape the query.  See xmlURIQuery*
+        * functions instead.
+        */
+       uri->query = STRNDUP(*str, cur - *str);
     }
     *str = cur;
     return (0);
@@ -2510,5 +2510,507 @@
     xmlFree(cal);
     return(ret);
 }
+
+/************************************************************************
+ *                                                                     *
+ *        Low-level parse and create functions for query strings        *
+ *                                                                     *
+ ************************************************************************/
+
+/**
+ * xmlURIQueryCreate:
+ * @fields: Linked list of fields
+ * @separator: Separator, if NULL uses the default ("&")
+ * @query_out: The returned query string.
+ *
+ * This function concatenates the linked list of (field name, field value)
+ * pairs into a query string.
+ *
+ * If any field has the 'ignore' field set, it is ignored and not
+ * concatenated into the query string.
+ *
+ * If a field with the same name appears more than once, then it will
+ * appear multiple times in the query string.  Web browsers generate
+ * such query strings, and query string parsers handle them in
+ * various ways.  (See xmlURIQueryGetSingle and xmlURIQueryGetMultiple).
+ *
+ * The field name and value are URI-escaped properly before being
+ * added to the query.
+ *
+ * The returned query string should be freed by the caller.  If
+ * fields is NULL then query_out will be set to NULL.
+ *
+ * Returns 0 if successful or an error code.
+ */
+int
+xmlURIQueryCreate (xmlURIQueryFieldsPtr fields,
+                  const char *separator,
+                  char **query_out)
+{
+    /* List of characters which are safe inside names or values,
+     * apart from '@', IS_MARK and IS_ALPHANUM.  Best to escape
+     * as much as possible.  Certainly '=', '&' and '#' must NEVER
+     * be added to this list.
+     */
+    static const xmlChar *special_chars = BAD_CAST "";
+
+    int append_sep = 0, sep_len;
+    xmlBufferPtr buf;
+    xmlChar *str;
+    int rv;
+
+    if (query_out) *query_out = NULL;
+    if (!fields) return 0;
+
+    if (separator == NULL) {
+       separator = "&";
+       sep_len = 1;
+    } else
+       sep_len = xmlStrlen (BAD_CAST separator);
+
+    buf = xmlBufferCreate ();
+    if (!buf) return -1;
+
+    rv = 0;
+    while (fields) {
+       if (!fields->ignore) {
+           if (append_sep) {
+               rv = xmlBufferAdd (buf, BAD_CAST separator, sep_len);
+               if (rv != 0) goto error;
+           }
+           append_sep = 1;
+
+           str = xmlURIEscapeStr (BAD_CAST fields->name, special_chars);
+           if (!str) { rv = XML_ERR_NO_MEMORY; goto error; }
+           rv = xmlBufferAdd (buf, str, xmlStrlen (str));
+           xmlFree (str);
+           if (rv != 0) goto error;
+
+           rv = xmlBufferAdd (buf, BAD_CAST "=", 1);
+           if (rv != 0) goto error;
+           str = xmlURIEscapeStr (BAD_CAST fields->value, special_chars);
+           if (!str) { rv = XML_ERR_NO_MEMORY; goto error; }
+           rv = xmlBufferAdd (buf, str, xmlStrlen (str));
+           xmlFree (str);
+           if (rv != 0) goto error;
+       }
+
+       fields = fields->next;
+    }
+
+    if (query_out && buf->content) {
+       *query_out = (char *) xmlStrdup (buf->content);
+       if (!*query_out) {
+           rv = XML_ERR_NO_MEMORY;
+           goto error;
+       }
+    }
+
+ error:
+    if (buf)
+       xmlBufferFree (buf);
+    return rv;
+}
+
+/**
+ * xmlURIQueryParse:
+ * @query: Query string to parse.
+ * @separator: Separator, if NULL uses the default ("&")
+ * @fields_out: Returned linked list of fields, or NULL if query is empty.
+ *
+ * This function parses the query string and returns a list of
+ * (field name, field value) pairs of fields found in the query
+ * string.
+ *
+ * Field names are not unique, and for a given query string this
+ * function may return the same name several times
+ * (eg. "name=val1&name=val2").  Web browsers generate such query
+ * strings.
+ *
+ * The returned list should be freed by the caller (see
+ * xmlFreeURIQueryFields).  If the query string passed in is
+ * NULL or an empty string, then fields_out will be set to NULL.
+ *
+ * The field name and value are URI-unescaped properly when parsed
+ * out of the query.  If the value is missing, it is set to "".
+ * If the name is missing then the field is ignored.  This behaviour
+ * is consistent with Perl's CGI.pm.
+ *
+ * The ignore flags are set to 0 (see xmlURIQueryCreate).
+ *
+ * Returns 0 if successful or an error code.
+ */
+int
+xmlURIQueryParse (const char *query_,
+                 const char *separator,
+                 xmlURIQueryFieldsPtr *fields_out)
+{
+    xmlURIQueryFieldsPtr fields, field, *prev;
+    int sep_len;
+    const xmlChar *query = BAD_CAST query_, *end, *eq;
+    char *name, *value;
+
+    if (fields_out) *fields_out = NULL;
+    if (!query || query[0] == '\0') return 0;
+
+    if (separator == NULL) {
+       separator = "&";
+       sep_len = 1;
+    } else
+       sep_len = xmlStrlen (BAD_CAST separator);
+
+    fields = NULL;
+    prev = &fields;
+
+    while (*query) {
+       /* Find the next separator, or end of the string. */
+       end = xmlStrstr (query, BAD_CAST separator);
+       if (!end) end = query + xmlStrlen (query);
+
+       /* Find the first '=' character between here and end. */
+       eq = xmlStrchr (query, '=');
+       if (eq && eq >= end) eq = NULL;
+
+       /* Empty section (eg. "?&"). */
+       if (end == query)
+           goto next;
+       /* If there is no '=' character, then we have just "name"
+        * and consistent with CGI.pm we assume value is "".
+        */
+       else if (!eq) {
+           name = xmlURIUnescapeString ((const char *) query,
+                                        end - query, NULL);
+           value = (char *) xmlStrdup (BAD_CAST "");
+           if (!name || !value) goto out_of_memory;
+       }
+       /* Or if we have "name=" here (works around annoying
+        * problem when calling xmlURIUnescapeString with len = 0).
+        */
+       else if (eq+1 == end) {
+           name = xmlURIUnescapeString ((const char *) query,
+                                        eq - query, NULL);
+           value = (char *) xmlStrdup (BAD_CAST "");
+           if (!name || !value) goto out_of_memory;
+       }
+       /* If the '=' character is at the beginning then we have
+        * "=value" and consistent with CGI.pm we _ignore_ this.
+        */
+       else if (query == eq)
+           goto next;
+       /* Otherwise it's "name=value". */
+       else {
+           name = xmlURIUnescapeString ((const char *) query,
+                                        eq - query, NULL);
+           value = xmlURIUnescapeString ((const char *) eq+1,
+                                         end - (eq+1), NULL);
+           if (!name || !value) goto out_of_memory;
+       }
+
+       /* Allocate this field and append to the list. */
+       field = xmlMalloc (sizeof *field);
+       if (!field) goto out_of_memory;
+       field->next = NULL;
+       field->name = name;
+       field->value = value;
+       field->ignore = 0;
+       *prev = field;
+       prev = &field->next;
+
+    next:
+       query = end;
+       if (*query) query += sep_len; /* skip separator */
+    }
+
+    if (fields_out) *fields_out = fields;
+    return 0;
+
+ out_of_memory:
+    xmlFreeURIQueryFields (fields);
+    return XML_ERR_NO_MEMORY;
+}
+
+/**
+ * xmlFreeURIQueryFields:
+ * @fields: Linked list of fields
+ *
+ * This function frees the linked list of fields, and all strings
+ * contained within.
+ *
+ * If fields is NULL then this function does nothing.
+ */
+void
+xmlFreeURIQueryFields (xmlURIQueryFieldsPtr fields)
+{
+    xmlURIQueryFieldsPtr t;
+
+    while (fields) {
+       if (fields->name) xmlFree (fields->name);
+       if (fields->value) xmlFree (fields->value);
+       t = fields;
+       fields = fields->next;
+       xmlFree (t);
+    }
+}
+
+/************************************************************************
+ *                                                                     *
+ *        High-level convenience operations for query strings           *
+ *                                                                     *
+ ************************************************************************/
+
+/* NB. These functions generally parse the whole string each time
+ * they are called, so they are useful for occasional use, or code
+ * where you don't particularly care about raw speed.  Otherwise you
+ * should use the low-level parse function above and work on the
+ * xmlURIQueryFields structure directly.
+ */
+
+/**
+ * xmlURIQueryExists:
+ * @query: The query string (from uri->query)
+ * @name: The field name (unescaped)
+ * @bool_out: Boolean returned
+ *
+ * Sets bool_out to true if and only if the field name appears in the
+ * query string with any value (including an empty value).
+ *
+ * See also xmlURIQueryIsTrue.
+ *
+ * Notes:
+ * (1) this function parses the query string each time it is called.
+ * (2) field name comparisons are case-insensitive.
+ *
+ * Returns 0 if successful, or an error code.
+ */
+int
+xmlURIQueryExists (const char *query,
+                  const char *name,
+                  int *bool_out)
+{
+    xmlURIQueryFieldsPtr fields, field;
+    int rv;
+
+    if (bool_out) *bool_out = 0;
+
+    rv = xmlURIQueryParse (query, NULL, &fields);
+    if (rv != 0) return rv;
+
+    for (field = fields; field; field = field->next) {
+       if (xmlStrcasecmp (BAD_CAST field->name, BAD_CAST name) == 0) {
+           if (bool_out) *bool_out = 1;
+           break;
+       }
+    }
+
+    xmlFreeURIQueryFields (fields);
+
+    return 0;
+}
+
+/**
+ * xmlURIQueryIsTrue:
+ * @query: The query string (from uri->query)
+ * @name: The field name (unescaped)
+ * @bool_out: Boolean returned
+ *
+ * Sets bool_out to true if and only if the field name appears in the
+ * query string with a "non-false" value.  A false value means
+ * a value which is empty or "0".  Thus this function will return
+ * false for "..&name=&.." or "..&name=0&..", but will return true
+ * for "..&name=1&.." or "..&name=foo&..".
+ *
+ * If the name appears multiple times, then we check if any has a
+ * non-false value.
+ *
+ * (This is the same convention for truthfulness as used in Perl's
+ * CGI.pm).
+ *
+ * See also xmlURIQueryExists.
+ *
+ * Notes:
+ * (1) this function parses the query string each time it is called.
+ * (2) field name comparisons are case-insensitive.
+ *
+ * Returns 0 if successful, or an error code.
+ */
+static inline int
+is_false (const char *value)
+{
+    return value[0] == '\0' || (value[0] == '0' && value[1] == '\0');
+}
+
+int
+xmlURIQueryIsTrue (const char *query,
+                  const char *name,
+                  int *bool_out)
+{
+    xmlURIQueryFieldsPtr fields, field;
+    int rv;
+
+    if (bool_out) *bool_out = 0;
+
+    rv = xmlURIQueryParse (query, NULL, &fields);
+    if (rv != 0) return rv;
+
+    for (field = fields; field; field = field->next) {
+       if (xmlStrcasecmp (BAD_CAST field->name, BAD_CAST name) == 0 &&
+           !is_false (field->value)) {
+           if (bool_out) *bool_out = 1;
+           break;
+       }
+    }
+
+    xmlFreeURIQueryFields (fields);
+
+    return 0;
+}
+
+/**
+ * xmlURIQueryGetSingle:
+ * @query: The query string (from uri->query)
+ * @name: The field name (unescaped)
+ * @value_out: Value returned (if found) or set to NULL.
+ *
+ * Gets the value of the named field from the query string, unescaped.
+ * The returned value must be freed by the caller.
+ *
+ * If the named field cannot be found, sets value_out to NULL.
+ *
+ * If the named field appears more than once, the first value is
+ * the one returned.  (See also xmlURIQueryGetMultiple).
+ *
+ * Notes:
+ * (1) this function parses the query string each time it is called.
+ * (2) field name comparisons are case-insensitive.
+ *
+ * Returns 0 if successful, or an error code.
+ */
+int
+xmlURIQueryGetSingle (const char *query,
+                     const char *name,
+                     char **value_out)
+{
+    xmlURIQueryFieldsPtr fields, field;
+    int rv;
+
+    if (value_out) *value_out = NULL;
+
+    rv = xmlURIQueryParse (query, NULL, &fields);
+    if (rv != 0) return rv;
+
+    rv = 0;
+    for (field = fields; field; field = field->next) {
+       if (xmlStrcasecmp (BAD_CAST field->name, BAD_CAST name) == 0) {
+           if (value_out) {
+               *value_out = (char *) xmlStrdup (BAD_CAST field->value);
+               if (!*value_out) rv = XML_ERR_NO_MEMORY;
+           }
+           break;
+       }
+    }
+
+    xmlFreeURIQueryFields (fields);
+
+    return rv;
+}
+
+/**
+ * xmlURIQueryGetMultiple:
+ * @query: The query string (from uri->query)
+ * @name: The field name (unescaped)
+ * @fields_out: The fields with the given name (or NULL if no fields).
+ *
+ * Gets the value(s) of the named field from the query string, unescaped.
+ * The returned value must be freed by the caller by calling
+ * xmlFreeURIQueryFields.
+ *
+ * If the named field cannot be found, sets fields_out to NULL.
+ *
+ * Notes:
+ * (1) this function parses the query string each time it is called.
+ * (2) field name comparisons are case-insensitive.
+ *
+ * Returns 0 if successful, or an error code.
+ */
+int
+xmlURIQueryGetMultiple (const char *query,
+                       const char *name,
+                       xmlURIQueryFieldsPtr *fields_out)
+{
+    xmlURIQueryFieldsPtr fields, field, *prev;
+    int rv;
+
+    if (fields_out) *fields_out = NULL;
+
+    rv = xmlURIQueryParse (query, NULL, &fields);
+    if (rv != 0) return rv;
+
+    prev = &fields;
+    field = fields;
+    while (field) {
+       if (xmlStrcasecmp (BAD_CAST field->name, BAD_CAST name) == 0) {
+           /* Keep this field in the linked list. */
+           prev = &field->next;
+           field = field->next;
+       } else {
+           /* We're going to remove and free the current field (field).
+            * '*prev' is the pointer to the current field from the previous,
+            * and field->next points to the next one.
+            */
+           *prev = field->next;
+           field->next = NULL;
+           xmlFreeURIQueryFields (field);
+           field = *prev;
+       }
+    }
+
+    if (fields_out) *fields_out = fields;
+
+    return 0;
+}
+
+/**
+ * xmlURIQueryRemove:
+ * @query: The query string (from uri->query)
+ * @name: The field name (unescaped)
+ * @query_out: The returned query string.
+ *
+ * This function removes all fields named name from the query
+ * string, constructing a new query string and returning it in
+ * query_out.
+ *
+ * query_out must be freed by the caller.
+ *
+ * Notes:
+ * (1) this function parses the query string each time it is called.
+ * (2) field name comparisons are case-insensitive.
+ *
+ * Returns 0 if successful, or an error code.
+ */
+int
+xmlURIQueryRemove (const char *query,
+                  const char *name,
+                  char **query_out)
+{
+    xmlURIQueryFieldsPtr fields, field;
+    int rv;
+
+    if (query_out) *query_out = NULL;
+
+    rv = xmlURIQueryParse (query, NULL, &fields);
+    if (rv != 0) return rv;
+
+    for (field = fields; field; field = field->next) {
+       if (xmlStrcasecmp (BAD_CAST field->name, BAD_CAST name) == 0)
+           field->ignore = 1;
+    }
+
+    rv = xmlURIQueryCreate (fields, NULL, query_out);
+
+    xmlFreeURIQueryFields (fields);
+    return rv;
+}
+
+
 #define bottom_uri
 #include "elfgcchack.h"
Index: include/libxml/uri.h
===================================================================
--- include/libxml/uri.h        (revision 3604)
+++ include/libxml/uri.h        (working copy)
@@ -83,6 +83,60 @@
 XMLPUBFUN xmlChar* XMLCALL     
                xmlPathToURI            (const xmlChar *path);
 
+/**
+ * xmlURIQueryFields:
+ *
+ * A query string parsed into a linked list of (field name, field value) pairs.
+ *
+ * The name and value strings are stored here without %-escaping.  In
+ * the final query string they may be escaped if necessary.
+ *
+ * The ignore field is useful when parsing a query string, removing
+ * some fields, and leaving the rest.  xmlURIQueryParse always sets
+ * this to zero.  xmlURIQueryCreate checks this, and if it is non-zero
+ * it will ignore that field when constructing the query string.
+ */
+typedef struct _xmlURIQueryFields xmlURIQueryFields;
+typedef xmlURIQueryFields *xmlURIQueryFieldsPtr;
+struct _xmlURIQueryFields {
+    xmlURIQueryFieldsPtr next; /* Linked list chain. */
+    char *name;                        /* Field name (unescaped). */
+    char *value;               /* Field value (unescaped). */
+    int ignore;                        /* Ignore field in xmlURIQueryCreate. */
+};
+
+XMLPUBFUN int XMLCALL
+                xmlURIQueryParse        (const char *query,
+                                        const char *separator,
+                                        xmlURIQueryFieldsPtr *fields_out);
+XMLPUBFUN int XMLCALL
+                xmlURIQueryCreate       (const xmlURIQueryFieldsPtr fields,
+                                        const char *separator,
+                                        char **query_out);
+XMLPUBFUN void XMLCALL
+                xmlFreeURIQueryFields   (xmlURIQueryFieldsPtr fields);
+
+XMLPUBFUN int XMLCALL
+                xmlURIQueryExists       (const char *query,
+                                        const char *name,
+                                        int *bool_out);
+XMLPUBFUN int XMLCALL
+                xmlURIQueryIsTrue       (const char *query,
+                                        const char *name,
+                                        int *bool_out);
+XMLPUBFUN int XMLCALL
+                xmlURIQueryGetSingle    (const char *query,
+                                        const char *name,
+                                        char **value_out);
+XMLPUBFUN int XMLCALL
+                xmlURIQueryGetMultiple  (const char *query,
+                                        const char *name,
+                                        xmlURIQueryFieldsPtr *fields_out);
+XMLPUBFUN int XMLCALL
+                xmlURIQueryRemove       (const char *query,
+                                        const char *name,
+                                        char **query_out);
+
 #ifdef __cplusplus
 }
 #endif
Index: result/URIQuery/test1.data
===================================================================
--- result/URIQuery/test1.data  (revision 0)
+++ result/URIQuery/test1.data  (revision 0)
@@ -0,0 +1,1524 @@
+/
+       query string = NULL
+       reassembled query = NULL
+       reassembled query - del* = NULL
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = NULL
+/?
+       query string = 
+       reassembled query = NULL
+       reassembled query - del* = NULL
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = NULL
+/?field1=value1
+       query string = field1=value1
+       name = field1
+       value = value1
+       reassembled query = field1=value1
+       reassembled query - del* = field1=value1
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field1=value1
+/?field1=value1&field2=value2
+       query string = field1=value1&field2=value2
+       name = field1
+       value = value1
+       name = field2
+       value = value2
+       reassembled query = field1=value1&field2=value2
+       reassembled query - del* = field1=value1&field2=value2
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field1=value1&field2=value2
+/?field1=value1&field2=value2&field3=value3
+       query string = field1=value1&field2=value2&field3=value3
+       name = field1
+       value = value1
+       name = field2
+       value = value2
+       name = field3
+       value = value3
+       reassembled query = field1=value1&field2=value2&field3=value3
+       reassembled query - del* = field1=value1&field2=value2&field3=value3
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field1=value1&field2=value2&field3=value3
+/?field1=value1&field2=value2&field3=value3&field4=value4
+       query string = field1=value1&field2=value2&field3=value3&field4=value4
+       name = field1
+       value = value1
+       name = field2
+       value = value2
+       name = field3
+       value = value3
+       name = field4
+       value = value4
+       reassembled query = field1=value1&field2=value2&field3=value3&field4=value4
+       reassembled query - del* = field1=value1&field2=value2&field3=value3&field4=value4
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field1=value1&field2=value2&field3=value3&field4=value4
+/?field1=
+       query string = field1=
+       name = field1
+       value = 
+       reassembled query = field1=
+       reassembled query - del* = field1=
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field1=
+/?=value1
+       query string = =value1
+       reassembled query = NULL
+       reassembled query - del* = NULL
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = NULL
+/?field1=&field2=value2
+       query string = field1=&field2=value2
+       name = field1
+       value = 
+       name = field2
+       value = value2
+       reassembled query = field1=&field2=value2
+       reassembled query - del* = field1=&field2=value2
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field1=&field2=value2
+/?=value1&field2=value2
+       query string = =value1&field2=value2
+       name = field2
+       value = value2
+       reassembled query = field2=value2
+       reassembled query - del* = field2=value2
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field2=value2
+/?field1=value1&field2=
+       query string = field1=value1&field2=
+       name = field1
+       value = value1
+       name = field2
+       value = 
+       reassembled query = field1=value1&field2=
+       reassembled query - del* = field1=value1&field2=
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field1=value1&field2=
+/?field1=value1&=value2
+       query string = field1=value1&=value2
+       name = field1
+       value = value1
+       reassembled query = field1=value1
+       reassembled query - del* = field1=value1
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field1=value1
+/?field1=value1&field2=&field3=value3
+       query string = field1=value1&field2=&field3=value3
+       name = field1
+       value = value1
+       name = field2
+       value = 
+       name = field3
+       value = value3
+       reassembled query = field1=value1&field2=&field3=value3
+       reassembled query - del* = field1=value1&field2=&field3=value3
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field1=value1&field2=&field3=value3
+/?field1=value1&=value2&field3=value3
+       query string = field1=value1&=value2&field3=value3
+       name = field1
+       value = value1
+       name = field3
+       value = value3
+       reassembled query = field1=value1&field3=value3
+       reassembled query - del* = field1=value1&field3=value3
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field1=value1&field3=value3
+/?&
+       query string = &
+       reassembled query = NULL
+       reassembled query - del* = NULL
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = NULL
+/?&&
+       query string = &&
+       reassembled query = NULL
+       reassembled query - del* = NULL
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = NULL
+/?&&&
+       query string = &&&
+       reassembled query = NULL
+       reassembled query - del* = NULL
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = NULL
+/?field1=value1&&field2=value2
+       query string = field1=value1&&field2=value2
+       name = field1
+       value = value1
+       name = field2
+       value = value2
+       reassembled query = field1=value1&field2=value2
+       reassembled query - del* = field1=value1&field2=value2
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field1=value1&field2=value2
+/?&field1=value1&field2=value2
+       query string = &field1=value1&field2=value2
+       name = field1
+       value = value1
+       name = field2
+       value = value2
+       reassembled query = field1=value1&field2=value2
+       reassembled query - del* = field1=value1&field2=value2
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field1=value1&field2=value2
+/?&&field1=value1&field2=value2
+       query string = &&field1=value1&field2=value2
+       name = field1
+       value = value1
+       name = field2
+       value = value2
+       reassembled query = field1=value1&field2=value2
+       reassembled query - del* = field1=value1&field2=value2
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field1=value1&field2=value2
+/?field1=value1&field2=value2&
+       query string = field1=value1&field2=value2&
+       name = field1
+       value = value1
+       name = field2
+       value = value2
+       reassembled query = field1=value1&field2=value2
+       reassembled query - del* = field1=value1&field2=value2
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field1=value1&field2=value2
+/?field1=value1&field2=value2&&
+       query string = field1=value1&field2=value2&&
+       name = field1
+       value = value1
+       name = field2
+       value = value2
+       reassembled query = field1=value1&field2=value2
+       reassembled query - del* = field1=value1&field2=value2
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field1=value1&field2=value2
+/?field1=value1&delme=value2&field3=value3
+       query string = field1=value1&delme=value2&field3=value3
+       name = field1
+       value = value1
+       name = delme
+       value = value2
+       name = field3
+       value = value3
+       reassembled query = field1=value1&delme=value2&field3=value3
+       reassembled query - del* = field1=value1&field3=value3
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field1=value1&delme=value2&field3=value3
+/?del=delete
+       query string = del=delete
+       name = del
+       value = delete
+       reassembled query = del=delete
+       reassembled query - del* = 
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = del=delete
+/?field1=del
+       query string = field1=del
+       name = field1
+       value = del
+       reassembled query = field1=del
+       reassembled query - del* = field1=del
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field1=del
+/?del1=value1&del2=value2&del3=value3
+       query string = del1=value1&del2=value2&del3=value3
+       name = del1
+       value = value1
+       name = del2
+       value = value2
+       name = del3
+       value = value3
+       reassembled query = del1=value1&del2=value2&del3=value3
+       reassembled query - del* = 
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = del1=value1&del2=value2&del3=value3
+/?field1=value1&del2=value2
+       query string = field1=value1&del2=value2
+       name = field1
+       value = value1
+       name = del2
+       value = value2
+       reassembled query = field1=value1&del2=value2
+       reassembled query - del* = field1=value1
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field1=value1&del2=value2
+/?del1=value1&field2=value2
+       query string = del1=value1&field2=value2
+       name = del1
+       value = value1
+       name = field2
+       value = value2
+       reassembled query = del1=value1&field2=value2
+       reassembled query - del* = field2=value2
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = del1=value1&field2=value2
+/?field1=value1&del2=value2&del3=value3&del4=value4
+       query string = field1=value1&del2=value2&del3=value3&del4=value4
+       name = field1
+       value = value1
+       name = del2
+       value = value2
+       name = del3
+       value = value3
+       name = del4
+       value = value4
+       reassembled query = field1=value1&del2=value2&del3=value3&del4=value4
+       reassembled query - del* = field1=value1
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field1=value1&del2=value2&del3=value3&del4=value4
+/?del1=value1&del2=value2&del3=value3&field4=value4
+       query string = del1=value1&del2=value2&del3=value3&field4=value4
+       name = del1
+       value = value1
+       name = del2
+       value = value2
+       name = del3
+       value = value3
+       name = field4
+       value = value4
+       reassembled query = del1=value1&del2=value2&del3=value3&field4=value4
+       reassembled query - del* = field4=value4
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = del1=value1&del2=value2&del3=value3&field4=value4
+/?field1=value1&remove=value2&field3=value3
+       query string = field1=value1&remove=value2&field3=value3
+       name = field1
+       value = value1
+       name = remove
+       value = value2
+       name = field3
+       value = value3
+       reassembled query = field1=value1&remove=value2&field3=value3
+       reassembled query - del* = field1=value1&remove=value2&field3=value3
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field1=value1&field3=value3
+/?field1=remove
+       query string = field1=remove
+       name = field1
+       value = remove
+       reassembled query = field1=remove
+       reassembled query - del* = field1=remove
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field1=remove
+/?remove=value1
+       query string = remove=value1
+       name = remove
+       value = value1
+       reassembled query = remove=value1
+       reassembled query - del* = remove=value1
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = 
+/?remove=value1&remove=value2
+       query string = remove=value1&remove=value2
+       name = remove
+       value = value1
+       name = remove
+       value = value2
+       reassembled query = remove=value1&remove=value2
+       reassembled query - del* = remove=value1&remove=value2
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = 
+/?remove=value1&remove=value2&remove=value3
+       query string = remove=value1&remove=value2&remove=value3
+       name = remove
+       value = value1
+       name = remove
+       value = value2
+       name = remove
+       value = value3
+       reassembled query = remove=value1&remove=value2&remove=value3
+       reassembled query - del* = remove=value1&remove=value2&remove=value3
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = 
+/?remove=value1&field2=value2
+       query string = remove=value1&field2=value2
+       name = remove
+       value = value1
+       name = field2
+       value = value2
+       reassembled query = remove=value1&field2=value2
+       reassembled query - del* = remove=value1&field2=value2
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field2=value2
+/?field1=value1&remove=value2&remove=value3&remove=value4
+       query string = field1=value1&remove=value2&remove=value3&remove=value4
+       name = field1
+       value = value1
+       name = remove
+       value = value2
+       name = remove
+       value = value3
+       name = remove
+       value = value4
+       reassembled query = field1=value1&remove=value2&remove=value3&remove=value4
+       reassembled query - del* = field1=value1&remove=value2&remove=value3&remove=value4
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field1=value1
+/?remove=value1&remove=value2&remove=value3&field4=value4
+       query string = remove=value1&remove=value2&remove=value3&field4=value4
+       name = remove
+       value = value1
+       name = remove
+       value = value2
+       name = remove
+       value = value3
+       name = field4
+       value = value4
+       reassembled query = remove=value1&remove=value2&remove=value3&field4=value4
+       reassembled query - del* = remove=value1&remove=value2&remove=value3&field4=value4
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field4=value4
+/?field1=value1&remove=value2&remove=value3&field4=value4
+       query string = field1=value1&remove=value2&remove=value3&field4=value4
+       name = field1
+       value = value1
+       name = remove
+       value = value2
+       name = remove
+       value = value3
+       name = field4
+       value = value4
+       reassembled query = field1=value1&remove=value2&remove=value3&field4=value4
+       reassembled query - del* = field1=value1&remove=value2&remove=value3&field4=value4
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field1=value1&field4=value4
+/?check=0
+       query string = check=0
+       name = check
+       value = 0
+       reassembled query = check=0
+       reassembled query - del* = check=0
+       check field exists = true
+       check field is true = false
+       check field = 0
+               check field name = check
+               value = 0
+       reassembled query - remove = check=0
+/?check=0&check=0
+       query string = check=0&check=0
+       name = check
+       value = 0
+       name = check
+       value = 0
+       reassembled query = check=0&check=0
+       reassembled query - del* = check=0&check=0
+       check field exists = true
+       check field is true = false
+       check field = 0
+               check field name = check
+               value = 0
+               check field name = check
+               value = 0
+       reassembled query - remove = check=0&check=0
+/?check=0&check=0&check=1
+       query string = check=0&check=0&check=1
+       name = check
+       value = 0
+       name = check
+       value = 0
+       name = check
+       value = 1
+       reassembled query = check=0&check=0&check=1
+       reassembled query - del* = check=0&check=0&check=1
+       check field exists = true
+       check field is true = true
+       check field = 0
+               check field name = check
+               value = 0
+               check field name = check
+               value = 0
+               check field name = check
+               value = 1
+       reassembled query - remove = check=0&check=0&check=1
+/?check=
+       query string = check=
+       name = check
+       value = 
+       reassembled query = check=
+       reassembled query - del* = check=
+       check field exists = true
+       check field is true = false
+       check field = 
+               check field name = check
+               value = 
+       reassembled query - remove = check=
+/?check=&check=
+       query string = check=&check=
+       name = check
+       value = 
+       name = check
+       value = 
+       reassembled query = check=&check=
+       reassembled query - del* = check=&check=
+       check field exists = true
+       check field is true = false
+       check field = 
+               check field name = check
+               value = 
+               check field name = check
+               value = 
+       reassembled query - remove = check=&check=
+/?check=&check=&check=foo
+       query string = check=&check=&check=foo
+       name = check
+       value = 
+       name = check
+       value = 
+       name = check
+       value = foo
+       reassembled query = check=&check=&check=foo
+       reassembled query - del* = check=&check=&check=foo
+       check field exists = true
+       check field is true = true
+       check field = 
+               check field name = check
+               value = 
+               check field name = check
+               value = 
+               check field name = check
+               value = foo
+       reassembled query - remove = check=&check=&check=foo
+/?check
+       query string = check
+       name = check
+       value = 
+       reassembled query = check=
+       reassembled query - del* = check=
+       check field exists = true
+       check field is true = false
+       check field = 
+               check field name = check
+               value = 
+       reassembled query - remove = check=
+/?check&check
+       query string = check&check
+       name = check
+       value = 
+       name = check
+       value = 
+       reassembled query = check=&check=
+       reassembled query - del* = check=&check=
+       check field exists = true
+       check field is true = false
+       check field = 
+               check field name = check
+               value = 
+               check field name = check
+               value = 
+       reassembled query - remove = check=&check=
+/?check&check&check=BAR
+       query string = check&check&check=BAR
+       name = check
+       value = 
+       name = check
+       value = 
+       name = check
+       value = BAR
+       reassembled query = check=&check=&check=BAR
+       reassembled query - del* = check=&check=&check=BAR
+       check field exists = true
+       check field is true = true
+       check field = 
+               check field name = check
+               value = 
+               check field name = check
+               value = 
+               check field name = check
+               value = BAR
+       reassembled query - remove = check=&check=&check=BAR
+/?ChecK=0
+       query string = ChecK=0
+       name = ChecK
+       value = 0
+       reassembled query = ChecK=0
+       reassembled query - del* = ChecK=0
+       check field exists = true
+       check field is true = false
+       check field = 0
+               check field name = ChecK
+               value = 0
+       reassembled query - remove = ChecK=0
+/?ChecK=0&ChecK=0
+       query string = ChecK=0&ChecK=0
+       name = ChecK
+       value = 0
+       name = ChecK
+       value = 0
+       reassembled query = ChecK=0&ChecK=0
+       reassembled query - del* = ChecK=0&ChecK=0
+       check field exists = true
+       check field is true = false
+       check field = 0
+               check field name = ChecK
+               value = 0
+               check field name = ChecK
+               value = 0
+       reassembled query - remove = ChecK=0&ChecK=0
+/?ChecK=0&ChecK=0&ChecK=1
+       query string = ChecK=0&ChecK=0&ChecK=1
+       name = ChecK
+       value = 0
+       name = ChecK
+       value = 0
+       name = ChecK
+       value = 1
+       reassembled query = ChecK=0&ChecK=0&ChecK=1
+       reassembled query - del* = ChecK=0&ChecK=0&ChecK=1
+       check field exists = true
+       check field is true = true
+       check field = 0
+               check field name = ChecK
+               value = 0
+               check field name = ChecK
+               value = 0
+               check field name = ChecK
+               value = 1
+       reassembled query - remove = ChecK=0&ChecK=0&ChecK=1
+/?ChecK=
+       query string = ChecK=
+       name = ChecK
+       value = 
+       reassembled query = ChecK=
+       reassembled query - del* = ChecK=
+       check field exists = true
+       check field is true = false
+       check field = 
+               check field name = ChecK
+               value = 
+       reassembled query - remove = ChecK=
+/?ChecK=&ChecK=
+       query string = ChecK=&ChecK=
+       name = ChecK
+       value = 
+       name = ChecK
+       value = 
+       reassembled query = ChecK=&ChecK=
+       reassembled query - del* = ChecK=&ChecK=
+       check field exists = true
+       check field is true = false
+       check field = 
+               check field name = ChecK
+               value = 
+               check field name = ChecK
+               value = 
+       reassembled query - remove = ChecK=&ChecK=
+/?ChecK=&ChecK=&ChecK=foo
+       query string = ChecK=&ChecK=&ChecK=foo
+       name = ChecK
+       value = 
+       name = ChecK
+       value = 
+       name = ChecK
+       value = foo
+       reassembled query = ChecK=&ChecK=&ChecK=foo
+       reassembled query - del* = ChecK=&ChecK=&ChecK=foo
+       check field exists = true
+       check field is true = true
+       check field = 
+               check field name = ChecK
+               value = 
+               check field name = ChecK
+               value = 
+               check field name = ChecK
+               value = foo
+       reassembled query - remove = ChecK=&ChecK=&ChecK=foo
+/?ChecK
+       query string = ChecK
+       name = ChecK
+       value = 
+       reassembled query = ChecK=
+       reassembled query - del* = ChecK=
+       check field exists = true
+       check field is true = false
+       check field = 
+               check field name = ChecK
+               value = 
+       reassembled query - remove = ChecK=
+/?ChecK&ChecK
+       query string = ChecK&ChecK
+       name = ChecK
+       value = 
+       name = ChecK
+       value = 
+       reassembled query = ChecK=&ChecK=
+       reassembled query - del* = ChecK=&ChecK=
+       check field exists = true
+       check field is true = false
+       check field = 
+               check field name = ChecK
+               value = 
+               check field name = ChecK
+               value = 
+       reassembled query - remove = ChecK=&ChecK=
+/?ChecK&ChecK&ChecK=BAR
+       query string = ChecK&ChecK&ChecK=BAR
+       name = ChecK
+       value = 
+       name = ChecK
+       value = 
+       name = ChecK
+       value = BAR
+       reassembled query = ChecK=&ChecK=&ChecK=BAR
+       reassembled query - del* = ChecK=&ChecK=&ChecK=BAR
+       check field exists = true
+       check field is true = true
+       check field = 
+               check field name = ChecK
+               value = 
+               check field name = ChecK
+               value = 
+               check field name = ChecK
+               value = BAR
+       reassembled query - remove = ChecK=&ChecK=&ChecK=BAR
+/?check
+       query string = check
+       name = check
+       value = 
+       reassembled query = check=
+       reassembled query - del* = check=
+       check field exists = true
+       check field is true = false
+       check field = 
+               check field name = check
+               value = 
+       reassembled query - remove = check=
+/?check=0
+       query string = check=0
+       name = check
+       value = 0
+       reassembled query = check=0
+       reassembled query - del* = check=0
+       check field exists = true
+       check field is true = false
+       check field = 0
+               check field name = check
+               value = 0
+       reassembled query - remove = check=0
+/?check=1
+       query string = check=1
+       name = check
+       value = 1
+       reassembled query = check=1
+       reassembled query - del* = check=1
+       check field exists = true
+       check field is true = true
+       check field = 1
+               check field name = check
+               value = 1
+       reassembled query - remove = check=1
+/?check=foo
+       query string = check=foo
+       name = check
+       value = foo
+       reassembled query = check=foo
+       reassembled query - del* = check=foo
+       check field exists = true
+       check field is true = true
+       check field = foo
+               check field name = check
+               value = foo
+       reassembled query - remove = check=foo
+/?check&check=foo
+       query string = check&check=foo
+       name = check
+       value = 
+       name = check
+       value = foo
+       reassembled query = check=&check=foo
+       reassembled query - del* = check=&check=foo
+       check field exists = true
+       check field is true = true
+       check field = 
+               check field name = check
+               value = 
+               check field name = check
+               value = foo
+       reassembled query - remove = check=&check=foo
+/?check=&check=foo
+       query string = check=&check=foo
+       name = check
+       value = 
+       name = check
+       value = foo
+       reassembled query = check=&check=foo
+       reassembled query - del* = check=&check=foo
+       check field exists = true
+       check field is true = true
+       check field = 
+               check field name = check
+               value = 
+               check field name = check
+               value = foo
+       reassembled query - remove = check=&check=foo
+/?check=0&check=foo
+       query string = check=0&check=foo
+       name = check
+       value = 0
+       name = check
+       value = foo
+       reassembled query = check=0&check=foo
+       reassembled query - del* = check=0&check=foo
+       check field exists = true
+       check field is true = true
+       check field = 0
+               check field name = check
+               value = 0
+               check field name = check
+               value = foo
+       reassembled query - remove = check=0&check=foo
+/?check=1&check=foo
+       query string = check=1&check=foo
+       name = check
+       value = 1
+       name = check
+       value = foo
+       reassembled query = check=1&check=foo
+       reassembled query - del* = check=1&check=foo
+       check field exists = true
+       check field is true = true
+       check field = 1
+               check field name = check
+               value = 1
+               check field name = check
+               value = foo
+       reassembled query - remove = check=1&check=foo
+/?CHEck
+       query string = CHEck
+       name = CHEck
+       value = 
+       reassembled query = CHEck=
+       reassembled query - del* = CHEck=
+       check field exists = true
+       check field is true = false
+       check field = 
+               check field name = CHEck
+               value = 
+       reassembled query - remove = CHEck=
+/?CHEck=0
+       query string = CHEck=0
+       name = CHEck
+       value = 0
+       reassembled query = CHEck=0
+       reassembled query - del* = CHEck=0
+       check field exists = true
+       check field is true = false
+       check field = 0
+               check field name = CHEck
+               value = 0
+       reassembled query - remove = CHEck=0
+/?CHEck=1
+       query string = CHEck=1
+       name = CHEck
+       value = 1
+       reassembled query = CHEck=1
+       reassembled query - del* = CHEck=1
+       check field exists = true
+       check field is true = true
+       check field = 1
+               check field name = CHEck
+               value = 1
+       reassembled query - remove = CHEck=1
+/?CHEck=foo
+       query string = CHEck=foo
+       name = CHEck
+       value = foo
+       reassembled query = CHEck=foo
+       reassembled query - del* = CHEck=foo
+       check field exists = true
+       check field is true = true
+       check field = foo
+               check field name = CHEck
+               value = foo
+       reassembled query - remove = CHEck=foo
+/?CHEck&CHEck=foo
+       query string = CHEck&CHEck=foo
+       name = CHEck
+       value = 
+       name = CHEck
+       value = foo
+       reassembled query = CHEck=&CHEck=foo
+       reassembled query - del* = CHEck=&CHEck=foo
+       check field exists = true
+       check field is true = true
+       check field = 
+               check field name = CHEck
+               value = 
+               check field name = CHEck
+               value = foo
+       reassembled query - remove = CHEck=&CHEck=foo
+/?CHEck=&CHEck=foo
+       query string = CHEck=&CHEck=foo
+       name = CHEck
+       value = 
+       name = CHEck
+       value = foo
+       reassembled query = CHEck=&CHEck=foo
+       reassembled query - del* = CHEck=&CHEck=foo
+       check field exists = true
+       check field is true = true
+       check field = 
+               check field name = CHEck
+               value = 
+               check field name = CHEck
+               value = foo
+       reassembled query - remove = CHEck=&CHEck=foo
+/?CHEck=0&CHEck=foo
+       query string = CHEck=0&CHEck=foo
+       name = CHEck
+       value = 0
+       name = CHEck
+       value = foo
+       reassembled query = CHEck=0&CHEck=foo
+       reassembled query - del* = CHEck=0&CHEck=foo
+       check field exists = true
+       check field is true = true
+       check field = 0
+               check field name = CHEck
+               value = 0
+               check field name = CHEck
+               value = foo
+       reassembled query - remove = CHEck=0&CHEck=foo
+/?CHEck=1&CHEck=foo
+       query string = CHEck=1&CHEck=foo
+       name = CHEck
+       value = 1
+       name = CHEck
+       value = foo
+       reassembled query = CHEck=1&CHEck=foo
+       reassembled query - del* = CHEck=1&CHEck=foo
+       check field exists = true
+       check field is true = true
+       check field = 1
+               check field name = CHEck
+               value = 1
+               check field name = CHEck
+               value = foo
+       reassembled query - remove = CHEck=1&CHEck=foo
+/?check
+       query string = check
+       name = check
+       value = 
+       reassembled query = check=
+       reassembled query - del* = check=
+       check field exists = true
+       check field is true = false
+       check field = 
+               check field name = check
+               value = 
+       reassembled query - remove = check=
+/?check&check
+       query string = check&check
+       name = check
+       value = 
+       name = check
+       value = 
+       reassembled query = check=&check=
+       reassembled query - del* = check=&check=
+       check field exists = true
+       check field is true = false
+       check field = 
+               check field name = check
+               value = 
+               check field name = check
+               value = 
+       reassembled query - remove = check=&check=
+/?check&check&check
+       query string = check&check&check
+       name = check
+       value = 
+       name = check
+       value = 
+       name = check
+       value = 
+       reassembled query = check=&check=&check=
+       reassembled query - del* = check=&check=&check=
+       check field exists = true
+       check field is true = false
+       check field = 
+               check field name = check
+               value = 
+               check field name = check
+               value = 
+               check field name = check
+               value = 
+       reassembled query - remove = check=&check=&check=
+/?foo&check
+       query string = foo&check
+       name = foo
+       value = 
+       name = check
+       value = 
+       reassembled query = foo=&check=
+       reassembled query - del* = foo=&check=
+       check field exists = true
+       check field is true = false
+       check field = 
+               check field name = check
+               value = 
+       reassembled query - remove = foo=&check=
+/?foo&check&check
+       query string = foo&check&check
+       name = foo
+       value = 
+       name = check
+       value = 
+       name = check
+       value = 
+       reassembled query = foo=&check=&check=
+       reassembled query - del* = foo=&check=&check=
+       check field exists = true
+       check field is true = false
+       check field = 
+               check field name = check
+               value = 
+               check field name = check
+               value = 
+       reassembled query - remove = foo=&check=&check=
+/?foo&check&check&check
+       query string = foo&check&check&check
+       name = foo
+       value = 
+       name = check
+       value = 
+       name = check
+       value = 
+       name = check
+       value = 
+       reassembled query = foo=&check=&check=&check=
+       reassembled query - del* = foo=&check=&check=&check=
+       check field exists = true
+       check field is true = false
+       check field = 
+               check field name = check
+               value = 
+               check field name = check
+               value = 
+               check field name = check
+               value = 
+       reassembled query - remove = foo=&check=&check=&check=
+/?check&bar
+       query string = check&bar
+       name = check
+       value = 
+       name = bar
+       value = 
+       reassembled query = check=&bar=
+       reassembled query - del* = check=&bar=
+       check field exists = true
+       check field is true = false
+       check field = 
+               check field name = check
+               value = 
+       reassembled query - remove = check=&bar=
+/?check&check&bar
+       query string = check&check&bar
+       name = check
+       value = 
+       name = check
+       value = 
+       name = bar
+       value = 
+       reassembled query = check=&check=&bar=
+       reassembled query - del* = check=&check=&bar=
+       check field exists = true
+       check field is true = false
+       check field = 
+               check field name = check
+               value = 
+               check field name = check
+               value = 
+       reassembled query - remove = check=&check=&bar=
+/?check&check&check&bar
+       query string = check&check&check&bar
+       name = check
+       value = 
+       name = check
+       value = 
+       name = check
+       value = 
+       name = bar
+       value = 
+       reassembled query = check=&check=&check=&bar=
+       reassembled query - del* = check=&check=&check=&bar=
+       check field exists = true
+       check field is true = false
+       check field = 
+               check field name = check
+               value = 
+               check field name = check
+               value = 
+               check field name = check
+               value = 
+       reassembled query - remove = check=&check=&check=&bar=
+/?foo&check&bar
+       query string = foo&check&bar
+       name = foo
+       value = 
+       name = check
+       value = 
+       name = bar
+       value = 
+       reassembled query = foo=&check=&bar=
+       reassembled query - del* = foo=&check=&bar=
+       check field exists = true
+       check field is true = false
+       check field = 
+               check field name = check
+               value = 
+       reassembled query - remove = foo=&check=&bar=
+/?foo&check&check&bar
+       query string = foo&check&check&bar
+       name = foo
+       value = 
+       name = check
+       value = 
+       name = check
+       value = 
+       name = bar
+       value = 
+       reassembled query = foo=&check=&check=&bar=
+       reassembled query - del* = foo=&check=&check=&bar=
+       check field exists = true
+       check field is true = false
+       check field = 
+               check field name = check
+               value = 
+               check field name = check
+               value = 
+       reassembled query - remove = foo=&check=&check=&bar=
+/?check&foo&check
+       query string = check&foo&check
+       name = check
+       value = 
+       name = foo
+       value = 
+       name = check
+       value = 
+       reassembled query = check=&foo=&check=
+       reassembled query - del* = check=&foo=&check=
+       check field exists = true
+       check field is true = false
+       check field = 
+               check field name = check
+               value = 
+               check field name = check
+               value = 
+       reassembled query - remove = check=&foo=&check=
+/?check&foo&foo&check
+       query string = check&foo&foo&check
+       name = check
+       value = 
+       name = foo
+       value = 
+       name = foo
+       value = 
+       name = check
+       value = 
+       reassembled query = check=&foo=&foo=&check=
+       reassembled query - del* = check=&foo=&foo=&check=
+       check field exists = true
+       check field is true = false
+       check field = 
+               check field name = check
+               value = 
+               check field name = check
+               value = 
+       reassembled query - remove = check=&foo=&foo=&check=
+/?check=value1
+       query string = check=value1
+       name = check
+       value = value1
+       reassembled query = check=value1
+       reassembled query - del* = check=value1
+       check field exists = true
+       check field is true = true
+       check field = value1
+               check field name = check
+               value = value1
+       reassembled query - remove = check=value1
+/?check=value1&check=value2
+       query string = check=value1&check=value2
+       name = check
+       value = value1
+       name = check
+       value = value2
+       reassembled query = check=value1&check=value2
+       reassembled query - del* = check=value1&check=value2
+       check field exists = true
+       check field is true = true
+       check field = value1
+               check field name = check
+               value = value1
+               check field name = check
+               value = value2
+       reassembled query - remove = check=value1&check=value2
+/?check&check=value1&check=value2
+       query string = check&check=value1&check=value2
+       name = check
+       value = 
+       name = check
+       value = value1
+       name = check
+       value = value2
+       reassembled query = check=&check=value1&check=value2
+       reassembled query - del* = check=&check=value1&check=value2
+       check field exists = true
+       check field is true = true
+       check field = 
+               check field name = check
+               value = 
+               check field name = check
+               value = value1
+               check field name = check
+               value = value2
+       reassembled query - remove = check=&check=value1&check=value2
+/?foo&check=value1
+       query string = foo&check=value1
+       name = foo
+       value = 
+       name = check
+       value = value1
+       reassembled query = foo=&check=value1
+       reassembled query - del* = foo=&check=value1
+       check field exists = true
+       check field is true = true
+       check field = value1
+               check field name = check
+               value = value1
+       reassembled query - remove = foo=&check=value1
+/?foo&check=value1&check=value2
+       query string = foo&check=value1&check=value2
+       name = foo
+       value = 
+       name = check
+       value = value1
+       name = check
+       value = value2
+       reassembled query = foo=&check=value1&check=value2
+       reassembled query - del* = foo=&check=value1&check=value2
+       check field exists = true
+       check field is true = true
+       check field = value1
+               check field name = check
+               value = value1
+               check field name = check
+               value = value2
+       reassembled query - remove = foo=&check=value1&check=value2
+/?foo&check=value1&check=value2&check=value3
+       query string = foo&check=value1&check=value2&check=value3
+       name = foo
+       value = 
+       name = check
+       value = value1
+       name = check
+       value = value2
+       name = check
+       value = value3
+       reassembled query = foo=&check=value1&check=value2&check=value3
+       reassembled query - del* = foo=&check=value1&check=value2&check=value3
+       check field exists = true
+       check field is true = true
+       check field = value1
+               check field name = check
+               value = value1
+               check field name = check
+               value = value2
+               check field name = check
+               value = value3
+       reassembled query - remove = foo=&check=value1&check=value2&check=value3
+/?check=value1&bar
+       query string = check=value1&bar
+       name = check
+       value = value1
+       name = bar
+       value = 
+       reassembled query = check=value1&bar=
+       reassembled query - del* = check=value1&bar=
+       check field exists = true
+       check field is true = true
+       check field = value1
+               check field name = check
+               value = value1
+       reassembled query - remove = check=value1&bar=
+/?check=value1&check=value2&bar
+       query string = check=value1&check=value2&bar
+       name = check
+       value = value1
+       name = check
+       value = value2
+       name = bar
+       value = 
+       reassembled query = check=value1&check=value2&bar=
+       reassembled query - del* = check=value1&check=value2&bar=
+       check field exists = true
+       check field is true = true
+       check field = value1
+               check field name = check
+               value = value1
+               check field name = check
+               value = value2
+       reassembled query - remove = check=value1&check=value2&bar=
+/?check=value1&check=value2&check=value3&bar
+       query string = check=value1&check=value2&check=value3&bar
+       name = check
+       value = value1
+       name = check
+       value = value2
+       name = check
+       value = value3
+       name = bar
+       value = 
+       reassembled query = check=value1&check=value2&check=value3&bar=
+       reassembled query - del* = check=value1&check=value2&check=value3&bar=
+       check field exists = true
+       check field is true = true
+       check field = value1
+               check field name = check
+               value = value1
+               check field name = check
+               value = value2
+               check field name = check
+               value = value3
+       reassembled query - remove = check=value1&check=value2&check=value3&bar=
+/?foo&check=value1&bar
+       query string = foo&check=value1&bar
+       name = foo
+       value = 
+       name = check
+       value = value1
+       name = bar
+       value = 
+       reassembled query = foo=&check=value1&bar=
+       reassembled query - del* = foo=&check=value1&bar=
+       check field exists = true
+       check field is true = true
+       check field = value1
+               check field name = check
+               value = value1
+       reassembled query - remove = foo=&check=value1&bar=
+/?foo&check=value1&check=value2&bar
+       query string = foo&check=value1&check=value2&bar
+       name = foo
+       value = 
+       name = check
+       value = value1
+       name = check
+       value = value2
+       name = bar
+       value = 
+       reassembled query = foo=&check=value1&check=value2&bar=
+       reassembled query - del* = foo=&check=value1&check=value2&bar=
+       check field exists = true
+       check field is true = true
+       check field = value1
+               check field name = check
+               value = value1
+               check field name = check
+               value = value2
+       reassembled query - remove = foo=&check=value1&check=value2&bar=
+/?check=value1&foo&check=value2
+       query string = check=value1&foo&check=value2
+       name = check
+       value = value1
+       name = foo
+       value = 
+       name = check
+       value = value2
+       reassembled query = check=value1&foo=&check=value2
+       reassembled query - del* = check=value1&foo=&check=value2
+       check field exists = true
+       check field is true = true
+       check field = value1
+               check field name = check
+               value = value1
+               check field name = check
+               value = value2
+       reassembled query - remove = check=value1&foo=&check=value2
+/?check=value1&foo&foo&check=value2
+       query string = check=value1&foo&foo&check=value2
+       name = check
+       value = value1
+       name = foo
+       value = 
+       name = foo
+       value = 
+       name = check
+       value = value2
+       reassembled query = check=value1&foo=&foo=&check=value2
+       reassembled query - del* = check=value1&foo=&foo=&check=value2
+       check field exists = true
+       check field is true = true
+       check field = value1
+               check field name = check
+               value = value1
+               check field name = check
+               value = value2
+       reassembled query - remove = check=value1&foo=&foo=&check=value2
+/?field1=%26
+       query string = field1=%26
+       name = field1
+       value = &
+       reassembled query = field1=%26
+       reassembled query - del* = field1=%26
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field1=%26
+/?field1=%26&field2=%26
+       query string = field1=%26&field2=%26
+       name = field1
+       value = &
+       name = field2
+       value = &
+       reassembled query = field1=%26&field2=%26
+       reassembled query - del* = field1=%26&field2=%26
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field1=%26&field2=%26
+/?field1=%3d
+       query string = field1=%3d
+       name = field1
+       value = =
+       reassembled query = field1=%3D
+       reassembled query - del* = field1=%3D
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field1=%3D
+/?field1=%3d&field2=%3d
+       query string = field1=%3d&field2=%3d
+       name = field1
+       value = =
+       name = field2
+       value = =
+       reassembled query = field1=%3D&field2=%3D
+       reassembled query - del* = field1=%3D&field2=%3D
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field1=%3D&field2=%3D
+/?field%26=value1
+       query string = field%26=value1
+       name = field&
+       value = value1
+       reassembled query = field%26=value1
+       reassembled query - del* = field%26=value1
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field%26=value1
+/?field%26=value1&field%26=value2
+       query string = field%26=value1&field%26=value2
+       name = field&
+       value = value1
+       name = field&
+       value = value2
+       reassembled query = field%26=value1&field%26=value2
+       reassembled query - del* = field%26=value1&field%26=value2
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field%26=value1&field%26=value2
+/?field%3d=value1
+       query string = field%3d=value1
+       name = field=
+       value = value1
+       reassembled query = field%3D=value1
+       reassembled query - del* = field%3D=value1
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field%3D=value1
+/?field%3d=value1&field%3d=value2
+       query string = field%3d=value1&field%3d=value2
+       name = field=
+       value = value1
+       name = field=
+       value = value2
+       reassembled query = field%3D=value1&field%3D=value2
+       reassembled query - del* = field%3D=value1&field%3D=value2
+       check field exists = false
+       check field is true = false
+       check field = not present
+       reassembled query - remove = field%3D=value1&field%3D=value2
Index: Makefile.am
===================================================================
--- Makefile.am (revision 3604)
+++ Makefile.am (working copy)
@@ -6,7 +6,8 @@
 
 INCLUDES = -I$(top_builddir)/include -I srcdir@/include @THREAD_CFLAGS@ @Z_CFLAGS@ 
 
-noinst_PROGRAMS=testSchemas testRelax testSAX testHTML testXPath testURI \
+noinst_PROGRAMS=testSchemas testRelax testSAX testHTML testXPath \
+               testURI testURIQuery \
                 testThreads testC14N testAutomata testRegexp \
                 testReader testapi testModule runtest runsuite
 
@@ -100,6 +101,11 @@
 testURI_DEPENDENCIES = $(DEPS)
 testURI_LDADD= $(LDADDS)
 
+testURIQuery_SOURCES=testURIQuery.c
+testURIQuery_LDFLAGS = 
+testURIQuery_DEPENDENCIES = $(DEPS)
+testURIQuery_LDADD= $(LDADDS)
+
 testRegexp_SOURCES=testRegexp.c
 testRegexp_LDFLAGS = 
 testRegexp_DEPENDENCIES = $(DEPS)
@@ -159,7 +165,7 @@
 
 testall : tests SVGtests SAXtests
 
-tests: XMLtests XMLenttests NStests IDtests Errtests APItests @READER_TEST@ @TEST_SAX@ @TEST_PUSH@ 
@TEST_HTML@ @TEST_PHTML@  @TEST_VALID@ URItests @TEST_PATTERN@ @TEST_XPATH@ @TEST_XPTR@ @TEST_XINCLUDE@ 
@TEST_C14N@ @TEST_DEBUG@ @TEST_CATALOG@ @TEST_REGEXPS@ @TEST_SCHEMAS@ @TEST_SCHEMATRON@ @TEST_THREADS@ 
Timingtests @TEST_VTIME@ @PYTHON_TESTS@ @TEST_MODULES@
+tests: XMLtests XMLenttests NStests IDtests Errtests APItests @READER_TEST@ @TEST_SAX@ @TEST_PUSH@ 
@TEST_HTML@ @TEST_PHTML@  @TEST_VALID@ URItests URIQuerytests @TEST_PATTERN@ @TEST_XPATH@ @TEST_XPTR@ 
@TEST_XINCLUDE@ @TEST_C14N@ @TEST_DEBUG@ @TEST_CATALOG@ @TEST_REGEXPS@ @TEST_SCHEMAS@ @TEST_SCHEMATRON@ 
@TEST_THREADS@ Timingtests @TEST_VTIME@ @PYTHON_TESTS@ @TEST_MODULES@
        @(if [ "@PYTHON_SUBDIR@" != "" ] ; then cd python ; \
            $(MAKE) MAKEFLAGS+=--silent tests ; fi)
        @(cd doc/examples ; $(MAKE) MAKEFLAGS+=--silent tests)
@@ -437,6 +443,24 @@
              rm result.$$name ; \
          fi ; fi ; done)
 
+URIQuerytests : testURIQuery$(EXEEXT)
+       @(echo > .memdump)
+       @echo "## URI query string parser regression tests"
+       -@(for i in $(srcdir)/test/URIQuery/*.data ; do \
+         name=`basename $$i`; \
+         if [ ! -d $$i ] ; then \
+         if [ ! -f $(srcdir)/result/URIQuery/$$name ] ; then \
+             echo New test file $$name ; \
+             $(CHECKER) $(top_builddir)/testURIQuery < $$i > $(srcdir)/result/URIQuery/$$name ; \
+             grep "MORY ALLO" .memdump  | grep -v "MEMORY ALLOCATED : 0"; \
+         else \
+             log=`$(CHECKER) $(top_builddir)/testURIQuery < $$i 2>&1 > result.$$name ; \
+             grep "MORY ALLO" .memdump  | grep -v "MEMORY ALLOCATED : 0";\
+             diff $(srcdir)/result/URIQuery/$$name result.$$name` ; \
+             if [ -n "$$log" ] ; then echo $$name result ; echo $$log ; fi ; \
+             rm result.$$name ; \
+         fi ; fi ; done)
+
 XPathtests : testXPath$(EXEEXT)
        @(echo > .memdump)
        @echo "## XPath regression tests"
Index: .cvsignore
===================================================================
--- .cvsignore  (revision 3604)
+++ .cvsignore  (working copy)
@@ -34,6 +34,7 @@
 xmlversion.h
 xmllint
 testURI
+testURIQuery
 testDocbook
 testCatalog
 xmlcatalog
Index: test/URIQuery/test1.data
===================================================================
--- test/URIQuery/test1.data    (revision 0)
+++ test/URIQuery/test1.data    (revision 0)
@@ -0,0 +1,144 @@
+# List of tests URIs supplied to the testURIQuery.c program.
+# That program takes each URL, extracts the query string (if any)
+# and performs a series of xmlURIQuery* functions on that
+# query string.  You can find out more by looking at the test
+# program and the result file (result/URIQuery/test1.data).
+
+# No query string:
+
+/
+/?
+
+# Some simple query strings just to test parsing:
+
+/?field1=value1
+/?field1=value1&field2=value2
+/?field1=value1&field2=value2&field3=value3
+/?field1=value1&field2=value2&field3=value3&field4=value4
+
+# Test name= and =value
+
+/?field1=
+/?=value1
+/?field1=&field2=value2
+/?=value1&field2=value2
+/?field1=value1&field2=
+/?field1=value1&=value2
+/?field1=value1&field2=&field3=value3
+/?field1=value1&=value2&field3=value3
+
+# Empty fields
+
+/?&
+/?&&
+/?&&&
+/?field1=value1&&field2=value2
+/?&field1=value1&field2=value2
+/?&&field1=value1&field2=value2
+/?field1=value1&field2=value2&
+/?field1=value1&field2=value2&&
+
+# Any field names matching "del*" should be removed (tests the
+# "ignore" feature of xmlURIQueryCreate):
+
+/?field1=value1&delme=value2&field3=value3
+/?del=delete
+/?field1=del
+/?del1=value1&del2=value2&del3=value3
+/?field1=value1&del2=value2
+/?del1=value1&field2=value2
+/?field1=value1&del2=value2&del3=value3&del4=value4
+/?del1=value1&del2=value2&del3=value3&field4=value4
+
+# Any field names called "remove" should be removed (tests
+# xmlURIQueryRemove):
+
+/?field1=value1&remove=value2&field3=value3
+/?field1=remove
+/?remove=value1
+/?remove=value1&remove=value2
+/?remove=value1&remove=value2&remove=value3
+/?remove=value1&field2=value2
+/?field1=value1&remove=value2&remove=value3&remove=value4
+/?remove=value1&remove=value2&remove=value3&field4=value4
+/?field1=value1&remove=value2&remove=value3&field4=value4
+
+# Check Exists/IsTrue
+
+/?check=0
+/?check=0&check=0
+/?check=0&check=0&check=1
+/?check=
+/?check=&check=
+/?check=&check=&check=foo
+/?check
+/?check&check
+/?check&check&check=BAR
+/?ChecK=0
+/?ChecK=0&ChecK=0
+/?ChecK=0&ChecK=0&ChecK=1
+/?ChecK=
+/?ChecK=&ChecK=
+/?ChecK=&ChecK=&ChecK=foo
+/?ChecK
+/?ChecK&ChecK
+/?ChecK&ChecK&ChecK=BAR
+
+# Check GetSingle
+
+/?check
+/?check=0
+/?check=1
+/?check=foo
+/?check&check=foo
+/?check=&check=foo
+/?check=0&check=foo
+/?check=1&check=foo
+/?CHEck
+/?CHEck=0
+/?CHEck=1
+/?CHEck=foo
+/?CHEck&CHEck=foo
+/?CHEck=&CHEck=foo
+/?CHEck=0&CHEck=foo
+/?CHEck=1&CHEck=foo
+
+# Check GetMultiple
+
+/?check
+/?check&check
+/?check&check&check
+/?foo&check
+/?foo&check&check
+/?foo&check&check&check
+/?check&bar
+/?check&check&bar
+/?check&check&check&bar
+/?foo&check&bar
+/?foo&check&check&bar
+/?check&foo&check
+/?check&foo&foo&check
+/?check=value1
+/?check=value1&check=value2
+/?check&check=value1&check=value2
+/?foo&check=value1
+/?foo&check=value1&check=value2
+/?foo&check=value1&check=value2&check=value3
+/?check=value1&bar
+/?check=value1&check=value2&bar
+/?check=value1&check=value2&check=value3&bar
+/?foo&check=value1&bar
+/?foo&check=value1&check=value2&bar
+/?check=value1&foo&check=value2
+/?check=value1&foo&foo&check=value2
+
+# %-sequences in URLs
+
+/?field1=%26
+/?field1=%26&field2=%26
+/?field1=%3d
+/?field1=%3d&field2=%3d
+/?field%26=value1
+/?field%26=value1&field%26=value2
+/?field%3d=value1
+/?field%3d=value1&field%3d=value2

Attachment: smime.p7s
Description: S/MIME Cryptographic Signature



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]