[rrd-developers] [PATCH 2 of 7] * Use uriparser 0.7.5 and move needed stuff to uriparser directory

Tobias Oetiker tobi at oetiker.ch
Thu Mar 10 09:11:21 CET 2011


Hi Peter,

seems like a large junke of code you are introducing here ... do we
realy need all this ?

cheers
tobi


Monday Peter Stamfest wrote:

> # HG changeset patch
> # User Peter Stamfest <peter at stamfest.at>
> # Date 1299524476 -3600
> # Node ID c70c412d2399f71640bbb08adaff1037b2a13004
> # Parent  2e1ec35bef337eee041078d583cfe5aaa7068509
> * Use uriparser 0.7.5 and move needed stuff to uriparser directory
> * Used under the BSD license
> * Downloaded from http://downloads.sourceforge.net/project/uriparser/Sources/0.7.5/uriparser-0.7.5.tar.gz
>
> diff --git a/uriparser/include/uriparser/Uri.h b/uriparser/include/uriparser/Uri.h
> new file mode 100644
> --- /dev/null
> +++ b/uriparser/include/uriparser/Uri.h
> @@ -0,0 +1,752 @@
> +/*
> + * uriparser - RFC 3986 URI parsing library
> + *
> + * Copyright (C) 2007, Weijia Song <songweijia at gmail.com>
> + * Copyright (C) 2007, Sebastian Pipping <webmaster at hartwork.org>
> + * All rights reserved.
> + *
> + * Redistribution  and use in source and binary forms, with or without
> + * modification,  are permitted provided that the following conditions
> + * are met:
> + *
> + *     * Redistributions   of  source  code  must  retain  the   above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer.
> + *
> + *     * Redistributions  in  binary  form must  reproduce  the  above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer   in  the  documentation  and/or  other  materials
> + *       provided with the distribution.
> + *
> + *     * Neither  the name of the <ORGANIZATION> nor the names of  its
> + *       contributors  may  be  used to endorse  or  promote  products
> + *       derived  from  this software without specific  prior  written
> + *       permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT  NOT
> + * LIMITED  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS
> + * FOR  A  PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT  SHALL  THE
> + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL,    SPECIAL,   EXEMPLARY,   OR   CONSEQUENTIAL   DAMAGES
> + * (INCLUDING,  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> + * SERVICES;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
> + * STRICT  LIABILITY,  OR  TORT (INCLUDING  NEGLIGENCE  OR  OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
> + * OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +/**
> + * @file Uri.h
> + * Holds the RFC 3986 %URI parser interface.
> + * NOTE: This header includes itself twice.
> + */
> +
> +#if (defined(URI_PASS_ANSI) && !defined(URI_H_ANSI)) \
> +	|| (defined(URI_PASS_UNICODE) && !defined(URI_H_UNICODE)) \
> +	|| (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
> +/* What encodings are enabled? */
> +#include "UriDefsConfig.h"
> +#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
> +/* Include SELF twice */
> +# define URI_PASS_ANSI 1
> +# include "Uri.h"
> +# undef URI_PASS_ANSI
> +# define URI_PASS_UNICODE 1
> +# include "Uri.h"
> +# undef URI_PASS_UNICODE
> +/* Only one pass for each encoding */
> +#elif (defined(URI_PASS_ANSI) && !defined(URI_H_ANSI) \
> +	&& defined(URI_ENABLE_ANSI)) || (defined(URI_PASS_UNICODE) \
> +	&& !defined(URI_H_UNICODE) && defined(URI_ENABLE_UNICODE))
> +# ifdef URI_PASS_ANSI
> +#  define URI_H_ANSI 1
> +#  include "UriDefsAnsi.h"
> +# else
> +#  define URI_H_UNICODE 1
> +#  include "UriDefsUnicode.h"
> +# endif
> +
> +
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +
> +
> +#ifndef URI_DOXYGEN
> +# include "UriBase.h"
> +#endif
> +
> +
> +
> +/**
> + * Specifies a range of characters within a string.
> + * The range includes all characters from <c>first</c>
> + * to one before <c>afterLast</c>. So if both are
> + * non-NULL the difference is the length of the text range.
> + *
> + * @see UriUriA
> + * @see UriPathSegmentA
> + * @see UriHostDataA
> + * @since 0.3.0
> + */
> +typedef struct URI_TYPE(TextRangeStruct) {
> +	const URI_CHAR * first; /**< Pointer to first character */
> +	const URI_CHAR * afterLast; /**< Pointer to character after the last one still in */
> +} URI_TYPE(TextRange); /**< @copydoc UriTextRangeStructA */
> +
> +
> +
> +/**
> + * Represents a path segment within a %URI path.
> + * More precisely it is a node in a linked
> + * list of path segments.
> + *
> + * @see UriUriA
> + * @since 0.3.0
> + */
> +typedef struct URI_TYPE(PathSegmentStruct) {
> +	URI_TYPE(TextRange) text; /**< Path segment name */
> +	struct URI_TYPE(PathSegmentStruct) * next; /**< Pointer to the next path segment in the list, can be NULL if last already */
> +
> +	void * reserved; /**< Reserved to the parser */
> +} URI_TYPE(PathSegment); /**< @copydoc UriPathSegmentStructA */
> +
> +
> +
> +/**
> + * Holds structured host information.
> + * This is either a IPv4, IPv6, plain
> + * text for IPvFuture or all zero for
> + * a registered name.
> + *
> + * @see UriUriA
> + * @since 0.3.0
> + */
> +typedef struct URI_TYPE(HostDataStruct) {
> +	UriIp4 * ip4; /**< IPv4 address */
> +	UriIp6 * ip6; /**< IPv6 address */
> +	URI_TYPE(TextRange) ipFuture; /**< IPvFuture address */
> +} URI_TYPE(HostData); /**< @copydoc UriHostDataStructA */
> +
> +
> +
> +/**
> + * Represents an RFC 3986 %URI.
> + * Missing components can be {NULL, NULL} ranges.
> + *
> + * @see uriParseUriA
> + * @see uriFreeUriMembersA
> + * @see UriParserStateA
> + * @since 0.3.0
> + */
> +typedef struct URI_TYPE(UriStruct) {
> +	URI_TYPE(TextRange) scheme; /**< Scheme (e.g. "http") */
> +	URI_TYPE(TextRange) userInfo; /**< User info (e.g. "user:pass") */
> +	URI_TYPE(TextRange) hostText; /**< Host text (set for all hosts, excluding square brackets) */
> +	URI_TYPE(HostData) hostData; /**< Structured host type specific data */
> +	URI_TYPE(TextRange) portText; /**< Port (e.g. "80") */
> +	URI_TYPE(PathSegment) * pathHead; /**< Head of a linked list of path segments */
> +	URI_TYPE(PathSegment) * pathTail; /**< Tail of the list behind pathHead */
> +	URI_TYPE(TextRange) query; /**< Query without leading "?" */
> +	URI_TYPE(TextRange) fragment; /**< Query without leading "#" */
> +	UriBool absolutePath; /**< Absolute path flag, distincting "a" and "/a" */
> +	UriBool owner; /**< Memory owner flag */
> +
> +	void * reserved; /**< Reserved to the parser */
> +} URI_TYPE(Uri); /**< @copydoc UriUriStructA */
> +
> +
> +
> +/**
> + * Represents a state of the %URI parser.
> + * Missing components can be NULL to reflect
> + * a components absence.
> + *
> + * @see uriFreeUriMembersA
> + * @since 0.3.0
> + */
> +typedef struct URI_TYPE(ParserStateStruct) {
> +	URI_TYPE(Uri) * uri; /**< Plug in the %URI structure to be filled while parsing here */
> +	int errorCode; /**< Code identifying the occured error */
> +	const URI_CHAR * errorPos; /**< Pointer to position in case of a syntax error */
> +
> +	void * reserved; /**< Reserved to the parser */
> +} URI_TYPE(ParserState); /**< @copydoc UriParserStateStructA */
> +
> +
> +
> +/**
> + * Represents a query element.
> + * More precisely it is a node in a linked
> + * list of query elements.
> + *
> + * @since 0.7.0
> + */
> +typedef struct URI_TYPE(QueryListStruct) {
> +	const URI_CHAR * key; /**< Key of the query element */
> +	const URI_CHAR * value; /**< Value of the query element, can be NULL */
> +
> +	struct URI_TYPE(QueryListStruct) * next; /**< Pointer to the next key/value pair in the list, can be NULL if last already */
> +} URI_TYPE(QueryList); /**< @copydoc UriQueryListStructA */
> +
> +
> +
> +/**
> + * Parses a RFC 3986 URI.
> + *
> + * @param state       <b>INOUT</b>: Parser state with set output %URI, must not be NULL
> + * @param first       <b>IN</b>: Pointer to the first character to parse, must not be NULL
> + * @param afterLast   <b>IN</b>: Pointer to the character after the last to parse, must not be NULL
> + * @return            0 on success, error code otherwise
> + *
> + * @see uriParseUriA
> + * @see uriToStringA
> + * @since 0.3.0
> + */
> +int URI_FUNC(ParseUriEx)(URI_TYPE(ParserState) * state,
> +		const URI_CHAR * first, const URI_CHAR * afterLast);
> +
> +
> +
> +/**
> + * Parses a RFC 3986 %URI.
> + *
> + * @param state   <b>INOUT</b>: Parser state with set output %URI, must not be NULL
> + * @param text    <b>IN</b>: Text to parse, must not be NULL
> + * @return        0 on success, error code otherwise
> + *
> + * @see uriParseUriExA
> + * @see uriToStringA
> + * @since 0.3.0
> + */
> +int URI_FUNC(ParseUri)(URI_TYPE(ParserState) * state,
> +		const URI_CHAR * text);
> +
> +
> +
> +/**
> + * Frees all memory associated with the members
> + * of the %URI structure. Note that the structure
> + * itself is not freed, only its members.
> + *
> + * @param uri   <b>INOUT</b>: %URI structure whose members should be freed
> + *
> + * @since 0.3.0
> + */
> +void URI_FUNC(FreeUriMembers)(URI_TYPE(Uri) * uri);
> +
> +
> +
> +/**
> + * Percent-encodes all unreserved characters from the input string and
> + * writes the encoded version to the output string.
> + * Be sure to allocate <b>3 times</b> the space of the input buffer for
> + * the output buffer for <c>normalizeBreaks == URI_FALSE</c> and <b>6 times</b>
> + * the space for <c>normalizeBreaks == URI_TRUE</c>
> + * (since e.g. "\x0d" becomes "%0D%0A" in that case)
> + *
> + * @param inFirst           <b>IN</b>: Pointer to first character of the input text
> + * @param inAfterLast       <b>IN</b>: Pointer after the last character of the input text
> + * @param out               <b>OUT</b>: Encoded text destination
> + * @param spaceToPlus       <b>IN</b>: Wether to convert ' ' to '+' or not
> + * @param normalizeBreaks   <b>IN</b>: Wether to convert CR and LF to CR-LF or not.
> + * @return                  Position of terminator in output string
> + *
> + * @see uriEscapeA
> + * @see uriUnescapeInPlaceExA
> + * @since 0.5.2
> + */
> +URI_CHAR * URI_FUNC(EscapeEx)(const URI_CHAR * inFirst,
> +		const URI_CHAR * inAfterLast, URI_CHAR * out,
> +		UriBool spaceToPlus, UriBool normalizeBreaks);
> +
> +
> +
> +/**
> + * Percent-encodes all unreserved characters from the input string and
> + * writes the encoded version to the output string.
> + * Be sure to allocate <b>3 times</b> the space of the input buffer for
> + * the output buffer for <c>normalizeBreaks == URI_FALSE</c> and <b>6 times</b>
> + * the space for <c>normalizeBreaks == URI_FALSE</c>
> + * (since e.g. "\x0d" becomes "%0D%0A" in that case)
> + *
> + * @param in                <b>IN</b>: Text source
> + * @param out               <b>OUT</b>: Encoded text destination
> + * @param spaceToPlus       <b>IN</b>: Wether to convert ' ' to '+' or not
> + * @param normalizeBreaks   <b>IN</b>: Wether to convert CR and LF to CR-LF or not.
> + * @return                  Position of terminator in output string
> + *
> + * @see uriEscapeExA
> + * @see uriUnescapeInPlaceA
> + * @since 0.5.0
> + */
> +URI_CHAR * URI_FUNC(Escape)(const URI_CHAR * in, URI_CHAR * out,
> +		UriBool spaceToPlus, UriBool normalizeBreaks);
> +
> +
> +
> +/**
> + * Unescapes percent-encoded groups in a given string.
> + * E.g. "%20" will become " ". Unescaping is done in place.
> + * The return value will be point to the new position
> + * of the terminating zero. Use this value to get the new
> + * length of the string. NULL is only returned if <c>inout</c>
> + * is NULL.
> + *
> + * @param inout             <b>INOUT</b>: Text to unescape/decode
> + * @param plusToSpace       <b>IN</b>: Whether to convert '+' to ' ' or not
> + * @param breakConversion   <b>IN</b>: Line break conversion mode
> + * @return                  Pointer to new position of the terminating zero
> + *
> + * @see uriUnescapeInPlaceA
> + * @see uriEscapeExA
> + * @since 0.5.0
> + */
> +const URI_CHAR * URI_FUNC(UnescapeInPlaceEx)(URI_CHAR * inout,
> +		UriBool plusToSpace, UriBreakConversion breakConversion);
> +
> +
> +
> +/**
> + * Unescapes percent-encoded groups in a given string.
> + * E.g. "%20" will become " ". Unescaping is done in place.
> + * The return value will be point to the new position
> + * of the terminating zero. Use this value to get the new
> + * length of the string. NULL is only returned if <c>inout</c>
> + * is NULL.
> + *
> + * NOTE: '+' is not decoded to ' ' and line breaks are not converted.
> + * Use the more advanced UnescapeInPlaceEx for that features instead.
> + *
> + * @param inout   <b>INOUT</b>: Text to unescape/decode
> + * @return        Pointer to new position of the terminating zero
> + *
> + * @see uriUnescapeInPlaceExA
> + * @see uriEscapeA
> + * @since 0.3.0
> + */
> +const URI_CHAR * URI_FUNC(UnescapeInPlace)(URI_CHAR * inout);
> +
> +
> +
> +/**
> + * Performs reference resolution as described in
> + * <a href="http://tools.ietf.org/html/rfc3986#section-5.2.2">section 5.2.2 of RFC 3986</a>.
> + * NOTE: On success you have to call uriFreeUriMembersA on \p absoluteDest manually later.
> + *
> + * @param absoluteDest     <b>OUT</b>: Result %URI
> + * @param relativeSource   <b>IN</b>: Reference to resolve
> + * @param absoluteBase     <b>IN</b>: Base %URI to apply
> + * @return                 Error code or 0 on success
> + *
> + * @see uriRemoveBaseUriA
> + * @since 0.4.0
> + */
> +int URI_FUNC(AddBaseUri)(URI_TYPE(Uri) * absoluteDest,
> +		const URI_TYPE(Uri) * relativeSource,
> +		const URI_TYPE(Uri) * absoluteBase);
> +
> +
> +
> +/**
> + * Tries to make a relative %URI (a reference) from an
> + * absolute %URI and a given base %URI. This can only work if
> + * the absolute %URI shares scheme and authority with
> + * the base %URI. If it does not the result will still be
> + * an absolute URI (with scheme part if necessary).
> + * NOTE: On success you have to call uriFreeUriMembersA on
> + * \p dest manually later.
> + *
> + * @param dest             <b>OUT</b>: Result %URI
> + * @param absoluteSource   <b>IN</b>: Absolute %URI to make relative
> + * @param absoluteBase     <b>IN</b>: Base %URI
> + * @param domainRootMode   <b>IN</b>: Create %URI with path relative to domain root
> + * @return                 Error code or 0 on success
> + *
> + * @see uriAddBaseUriA
> + * @since 0.5.2
> + */
> +int URI_FUNC(RemoveBaseUri)(URI_TYPE(Uri) * dest,
> +		const URI_TYPE(Uri) * absoluteSource,
> +		const URI_TYPE(Uri) * absoluteBase,
> +		UriBool domainRootMode);
> +
> +
> +
> +/**
> + * Checks two URIs for equivalence. Comparison is done
> + * the naive way, without prior normalization.
> + * NOTE: Two <c>NULL</c> URIs are equal as well.
> + *
> + * @param a   <b>IN</b>: First %URI
> + * @param b   <b>IN</b>: Second %URI
> + * @return    <c>URI_TRUE</c> when equal, <c>URI_FAlSE</c> else
> + *
> + * @since 0.4.0
> + */
> +UriBool URI_FUNC(EqualsUri)(const URI_TYPE(Uri) * a, const URI_TYPE(Uri) * b);
> +
> +
> +
> +/**
> + * Calculates the number of characters needed to store the
> + * string representation of the given %URI excluding the
> + * terminator.
> + *
> + * @param uri             <b>IN</b>: %URI to measure
> + * @param charsRequired   <b>OUT</b>: Length of the string representation in characters <b>excluding</b> terminator
> + * @return                Error code or 0 on success
> + *
> + * @see uriToStringA
> + * @since 0.5.0
> + */
> +int URI_FUNC(ToStringCharsRequired)(const URI_TYPE(Uri) * uri,
> +		int * charsRequired);
> +
> +
> +
> +/**
> + * Converts a %URI structure back to text as described in
> + * <a href="http://tools.ietf.org/html/rfc3986#section-5.3">section 5.3 of RFC 3986</a>.
> + *
> + * @param dest           <b>OUT</b>: Output destination
> + * @param uri            <b>IN</b>: %URI to convert
> + * @param maxChars       <b>IN</b>: Maximum number of characters to copy <b>including</b> terminator
> + * @param charsWritten   <b>OUT</b>: Number of characters written, can be lower than maxChars even if the %URI is too long!
> + * @return               Error code or 0 on success
> + *
> + * @see uriToStringCharsRequiredA
> + * @since 0.4.0
> + */
> +int URI_FUNC(ToString)(URI_CHAR * dest, const URI_TYPE(Uri) * uri, int maxChars, int * charsWritten);
> +
> +
> +
> +/**
> + * Determines the components of a %URI that are not normalized.
> + *
> + * @param uri   <b>IN</b>: %URI to check
> + * @return      Normalization job mask
> + *
> + * @see uriNormalizeSyntaxA
> + * @since 0.5.0
> + */
> +unsigned int URI_FUNC(NormalizeSyntaxMaskRequired)(const URI_TYPE(Uri) * uri);
> +
> +
> +
> +/**
> + * Normalizes a %URI using a normalization mask.
> + * The normalization mask decides what components are normalized.
> + *
> + * NOTE: If necessary the %URI becomes owner of all memory
> + * behind the text pointed to. Text is duplicated in that case.
> + *
> + * @param uri    <b>INOUT</b>: %URI to normalize
> + * @param mask   <b>IN</b>: Normalization mask
> + * @return       Error code or 0 on success
> + *
> + * @see uriNormalizeSyntaxA
> + * @see uriNormalizeSyntaxMaskRequiredA
> + * @since 0.5.0
> + */
> +int URI_FUNC(NormalizeSyntaxEx)(URI_TYPE(Uri) * uri, unsigned int mask);
> +
> +
> +
> +/**
> + * Normalizes all components of a %URI.
> + *
> + * NOTE: If necessary the %URI becomes owner of all memory
> + * behind the text pointed to. Text is duplicated in that case.
> + *
> + * @param uri   <b>INOUT</b>: %URI to normalize
> + * @return      Error code or 0 on success
> + *
> + * @see uriNormalizeSyntaxExA
> + * @see uriNormalizeSyntaxMaskRequiredA
> + * @since 0.5.0
> + */
> +int URI_FUNC(NormalizeSyntax)(URI_TYPE(Uri) * uri);
> +
> +
> +
> +/**
> + * Converts a Unix filename to a %URI string.
> + * The destination buffer must be large enough to hold 7 + 3 * len(filename) + 1
> + * characters in case of an absolute filename or 3 * len(filename) + 1 in case
> + * of a relative filename.
> + *
> + * EXAMPLE
> + *   Input:  "/bin/bash"
> + *   Output: "file:///bin/bash"
> + *
> + * @param filename     <b>IN</b>: Unix filename to convert
> + * @param uriString    <b>OUT</b>: Destination to write %URI string to
> + * @return             Error code or 0 on success
> + *
> + * @see uriUriStringToUnixFilenameA
> + * @see uriWindowsFilenameToUriStringA
> + * @since 0.5.2
> + */
> +int URI_FUNC(UnixFilenameToUriString)(const URI_CHAR * filename,
> +		URI_CHAR * uriString);
> +
> +
> +
> +/**
> + * Converts a Windows filename to a %URI string.
> + * The destination buffer must be large enough to hold 8 + 3 * len(filename) + 1
> + * characters in case of an absolute filename or 3 * len(filename) + 1 in case
> + * of a relative filename.
> + *
> + * EXAMPLE
> + *   Input:  "E:\\Documents and Settings"
> + *   Output: "file:///E:/Documents%20and%20Settings"
> + *
> + * @param filename     <b>IN</b>: Windows filename to convert
> + * @param uriString    <b>OUT</b>: Destination to write %URI string to
> + * @return             Error code or 0 on success
> + *
> + * @see uriUriStringToWindowsFilenameA
> + * @see uriUnixFilenameToUriStringA
> + * @since 0.5.2
> + */
> +int URI_FUNC(WindowsFilenameToUriString)(const URI_CHAR * filename,
> +		URI_CHAR * uriString);
> +
> +
> +
> +/**
> + * Extracts a Unix filename from a %URI string.
> + * The destination buffer must be large enough to hold len(uriString) + 1 - 7
> + * characters in case of an absolute %URI or len(uriString) + 1 in case
> + * of a relative %URI.
> + *
> + * @param uriString    <b>IN</b>: %URI string to convert
> + * @param filename     <b>OUT</b>: Destination to write filename to
> + * @return             Error code or 0 on success
> + *
> + * @see uriUnixFilenameToUriStringA
> + * @see uriUriStringToWindowsFilenameA
> + * @since 0.5.2
> + */
> +int URI_FUNC(UriStringToUnixFilename)(const URI_CHAR * uriString,
> +		URI_CHAR * filename);
> +
> +
> +
> +/**
> + * Extracts a Windows filename from a %URI string.
> + * The destination buffer must be large enough to hold len(uriString) + 1 - 8
> + * characters in case of an absolute %URI or len(uriString) + 1 in case
> + * of a relative %URI.
> + *
> + * @param uriString    <b>IN</b>: %URI string to convert
> + * @param filename     <b>OUT</b>: Destination to write filename to
> + * @return             Error code or 0 on success
> + *
> + * @see uriWindowsFilenameToUriStringA
> + * @see uriUriStringToUnixFilenameA
> + * @since 0.5.2
> + */
> +int URI_FUNC(UriStringToWindowsFilename)(const URI_CHAR * uriString,
> +		URI_CHAR * filename);
> +
> +
> +
> +/**
> + * Calculates the number of characters needed to store the
> + * string representation of the given query list excluding the
> + * terminator. It is assumed that line breaks are will be
> + * normalized to "%0D%0A".
> + *
> + * @param queryList         <b>IN</b>: Query list to measure
> + * @param charsRequired     <b>OUT</b>: Length of the string representation in characters <b>excluding</b> terminator
> + * @return                  Error code or 0 on success
> + *
> + * @see uriComposeQueryCharsRequiredExA
> + * @see uriComposeQueryA
> + * @since 0.7.0
> + */
> +int URI_FUNC(ComposeQueryCharsRequired)(const URI_TYPE(QueryList) * queryList,
> +		int * charsRequired);
> +
> +
> +
> +/**
> + * Calculates the number of characters needed to store the
> + * string representation of the given query list excluding the
> + * terminator.
> + *
> + * @param queryList         <b>IN</b>: Query list to measure
> + * @param charsRequired     <b>OUT</b>: Length of the string representation in characters <b>excluding</b> terminator
> + * @param spaceToPlus       <b>IN</b>: Wether to convert ' ' to '+' or not
> + * @param normalizeBreaks   <b>IN</b>: Wether to convert CR and LF to CR-LF or not.
> + * @return                  Error code or 0 on success
> + *
> + * @see uriComposeQueryCharsRequiredA
> + * @see uriComposeQueryExA
> + * @since 0.7.0
> + */
> +int URI_FUNC(ComposeQueryCharsRequiredEx)(const URI_TYPE(QueryList) * queryList,
> +		int * charsRequired, UriBool spaceToPlus, UriBool normalizeBreaks);
> +
> +
> +
> +/**
> + * Converts a query list structure back to a query string.
> + * The composed string does not start with '?',
> + * on the way ' ' is converted to '+' and line breaks are
> + * normalized to "%0D%0A".
> + *
> + * @param dest              <b>OUT</b>: Output destination
> + * @param queryList         <b>IN</b>: Query list to convert
> + * @param maxChars          <b>IN</b>: Maximum number of characters to copy <b>including</b> terminator
> + * @param charsWritten      <b>OUT</b>: Number of characters written, can be lower than maxChars even if the query list is too long!
> + * @return                  Error code or 0 on success
> + *
> + * @see uriComposeQueryExA
> + * @see uriComposeQueryMallocA
> + * @see uriComposeQueryCharsRequiredA
> + * @see uriDissectQueryMallocA
> + * @since 0.7.0
> + */
> +int URI_FUNC(ComposeQuery)(URI_CHAR * dest,
> +		const URI_TYPE(QueryList) * queryList, int maxChars, int * charsWritten);
> +
> +
> +
> +/**
> + * Converts a query list structure back to a query string.
> + * The composed string does not start with '?'.
> + *
> + * @param dest              <b>OUT</b>: Output destination
> + * @param queryList         <b>IN</b>: Query list to convert
> + * @param maxChars          <b>IN</b>: Maximum number of characters to copy <b>including</b> terminator
> + * @param charsWritten      <b>OUT</b>: Number of characters written, can be lower than maxChars even if the query list is too long!
> + * @param spaceToPlus       <b>IN</b>: Wether to convert ' ' to '+' or not
> + * @param normalizeBreaks   <b>IN</b>: Wether to convert CR and LF to CR-LF or not.
> + * @return                  Error code or 0 on success
> + *
> + * @see uriComposeQueryA
> + * @see uriComposeQueryMallocExA
> + * @see uriComposeQueryCharsRequiredExA
> + * @see uriDissectQueryMallocExA
> + * @since 0.7.0
> + */
> +int URI_FUNC(ComposeQueryEx)(URI_CHAR * dest,
> +		const URI_TYPE(QueryList) * queryList, int maxChars, int * charsWritten,
> +		UriBool spaceToPlus, UriBool normalizeBreaks);
> +
> +
> +
> +/**
> + * Converts a query list structure back to a query string.
> + * Memory for this string is allocated internally.
> + * The composed string does not start with '?',
> + * on the way ' ' is converted to '+' and line breaks are
> + * normalized to "%0D%0A".
> + *
> + * @param dest              <b>OUT</b>: Output destination
> + * @param queryList         <b>IN</b>: Query list to convert
> + * @return                  Error code or 0 on success
> + *
> + * @see uriComposeQueryMallocExA
> + * @see uriComposeQueryA
> + * @see uriDissectQueryMallocA
> + * @since 0.7.0
> + */
> +int URI_FUNC(ComposeQueryMalloc)(URI_CHAR ** dest,
> +		const URI_TYPE(QueryList) * queryList);
> +
> +
> +
> +/**
> + * Converts a query list structure back to a query string.
> + * Memory for this string is allocated internally.
> + * The composed string does not start with '?'.
> + *
> + * @param dest              <b>OUT</b>: Output destination
> + * @param queryList         <b>IN</b>: Query list to convert
> + * @param spaceToPlus       <b>IN</b>: Wether to convert ' ' to '+' or not
> + * @param normalizeBreaks   <b>IN</b>: Wether to convert CR and LF to CR-LF or not.
> + * @return                  Error code or 0 on success
> + *
> + * @see uriComposeQueryMallocA
> + * @see uriComposeQueryExA
> + * @see uriDissectQueryMallocExA
> + * @since 0.7.0
> + */
> +int URI_FUNC(ComposeQueryMallocEx)(URI_CHAR ** dest,
> +		const URI_TYPE(QueryList) * queryList,
> +		UriBool spaceToPlus, UriBool normalizeBreaks);
> +
> +
> +
> +/**
> + * Constructs a query list from the raw query string of a given URI.
> + * On the way '+' is converted back to ' ', line breaks are not modified.
> + *
> + * @param dest              <b>OUT</b>: Output destination
> + * @param itemCount         <b>OUT</b>: Number of items found, can be NULL
> + * @param first             <b>IN</b>: Pointer to first character <b>after</b> '?'
> + * @param afterLast         <b>IN</b>: Pointer to character after the last one still in
> + * @return                  Error code or 0 on success
> + *
> + * @see uriDissectQueryMallocExA
> + * @see uriComposeQueryA
> + * @see uriFreeQueryListA
> + * @since 0.7.0
> + */
> +int URI_FUNC(DissectQueryMalloc)(URI_TYPE(QueryList) ** dest, int * itemCount,
> +		const URI_CHAR * first, const URI_CHAR * afterLast);
> +
> +
> +
> +/**
> + * Constructs a query list from the raw query string of a given URI.
> + *
> + * @param dest              <b>OUT</b>: Output destination
> + * @param itemCount         <b>OUT</b>: Number of items found, can be NULL
> + * @param first             <b>IN</b>: Pointer to first character <b>after</b> '?'
> + * @param afterLast         <b>IN</b>: Pointer to character after the last one still in
> + * @param plusToSpace       <b>IN</b>: Whether to convert '+' to ' ' or not
> + * @param breakConversion   <b>IN</b>: Line break conversion mode
> + * @return                  Error code or 0 on success
> + *
> + * @see uriDissectQueryMallocA
> + * @see uriComposeQueryExA
> + * @see uriFreeQueryListA
> + * @since 0.7.0
> + */
> +int URI_FUNC(DissectQueryMallocEx)(URI_TYPE(QueryList) ** dest, int * itemCount,
> +		const URI_CHAR * first, const URI_CHAR * afterLast,
> +		UriBool plusToSpace, UriBreakConversion breakConversion);
> +
> +
> +
> +/**
> + * Frees all memory associated with the given query list.
> + * The structure itself is freed as well.
> + *
> + * @param queryList   <b>INOUT</b>: Query list to free
> + *
> + * @since 0.7.0
> + */
> +void URI_FUNC(FreeQueryList)(URI_TYPE(QueryList) * queryList);
> +
> +
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +
> +
> +#endif
> +#endif
> diff --git a/uriparser/include/uriparser/UriBase.h b/uriparser/include/uriparser/UriBase.h
> new file mode 100644
> --- /dev/null
> +++ b/uriparser/include/uriparser/UriBase.h
> @@ -0,0 +1,188 @@
> +/*
> + * uriparser - RFC 3986 URI parsing library
> + *
> + * Copyright (C) 2007, Weijia Song <songweijia at gmail.com>
> + * Copyright (C) 2007, Sebastian Pipping <webmaster at hartwork.org>
> + * All rights reserved.
> + *
> + * Redistribution  and use in source and binary forms, with or without
> + * modification,  are permitted provided that the following conditions
> + * are met:
> + *
> + *     * Redistributions   of  source  code  must  retain  the   above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer.
> + *
> + *     * Redistributions  in  binary  form must  reproduce  the  above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer   in  the  documentation  and/or  other  materials
> + *       provided with the distribution.
> + *
> + *     * Neither  the name of the <ORGANIZATION> nor the names of  its
> + *       contributors  may  be  used to endorse  or  promote  products
> + *       derived  from  this software without specific  prior  written
> + *       permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT  NOT
> + * LIMITED  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS
> + * FOR  A  PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT  SHALL  THE
> + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL,    SPECIAL,   EXEMPLARY,   OR   CONSEQUENTIAL   DAMAGES
> + * (INCLUDING,  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> + * SERVICES;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
> + * STRICT  LIABILITY,  OR  TORT (INCLUDING  NEGLIGENCE  OR  OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
> + * OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +/**
> + * @file UriBase.h
> + * Holds definitions independent of the encoding pass.
> + */
> +
> +#ifndef URI_BASE_H
> +#define URI_BASE_H 1
> +
> +
> +
> +/* Version helper macro */
> +#define URI_ANSI_TO_UNICODE(x) L##x
> +
> +
> +
> +/* Version */
> +#define URI_VER_MAJOR           0
> +#define URI_VER_MINOR           7
> +#define URI_VER_RELEASE         5
> +#define URI_VER_SUFFIX_ANSI     ""
> +#define URI_VER_SUFFIX_UNICODE  URI_ANSI_TO_UNICODE(URI_VER_SUFFIX_ANSI)
> +
> +
> +
> +/* More version helper macros */
> +#define URI_INT_TO_ANSI_HELPER(x) #x
> +#define URI_INT_TO_ANSI(x) URI_INT_TO_ANSI_HELPER(x)
> +
> +#define URI_INT_TO_UNICODE_HELPER(x) URI_ANSI_TO_UNICODE(#x)
> +#define URI_INT_TO_UNICODE(x) URI_INT_TO_UNICODE_HELPER(x)
> +
> +#define URI_VER_ANSI_HELPER(ma, mi, r, s) \
> +	URI_INT_TO_ANSI(ma) "." \
> +	URI_INT_TO_ANSI(mi) "." \
> +	URI_INT_TO_ANSI(r) \
> +	s
> +
> +#define URI_VER_UNICODE_HELPER(ma, mi, r, s) \
> +	URI_INT_TO_UNICODE(ma) L"." \
> +	URI_INT_TO_UNICODE(mi) L"." \
> +	URI_INT_TO_UNICODE(r) \
> +	s
> +
> +
> +
> +/* Full version strings */
> +#define URI_VER_ANSI     URI_VER_ANSI_HELPER(URI_VER_MAJOR, URI_VER_MINOR, URI_VER_RELEASE, URI_VER_SUFFIX_ANSI)
> +#define URI_VER_UNICODE  URI_VER_UNICODE_HELPER(URI_VER_MAJOR, URI_VER_MINOR, URI_VER_RELEASE, URI_VER_SUFFIX_UNICODE)
> +
> +
> +
> +/* Unused parameter macro */
> +#ifdef __GNUC__
> +# define URI_UNUSED(x) unused_##x __attribute__((unused))
> +#else
> +# define URI_UNUSED(x) x
> +#endif
> +
> +
> +
> +typedef int UriBool; /**< Boolean type */
> +
> +#define URI_TRUE     1
> +#define URI_FALSE    0
> +
> +
> +
> +/* Shared errors */
> +#define URI_SUCCESS                        0
> +#define URI_ERROR_SYNTAX                   1 /* Parsed text violates expected format */
> +#define URI_ERROR_NULL                     2 /* One of the params passed was NULL
> +                                                although it mustn't be */
> +#define URI_ERROR_MALLOC                   3 /* Requested memory could not be allocated */
> +#define URI_ERROR_OUTPUT_TOO_LARGE         4 /* Some output is to large for the receiving buffer */
> +#define URI_ERROR_NOT_IMPLEMENTED          8 /* The called function is not implemented yet */
> +#define URI_ERROR_RANGE_INVALID            9 /* The parameters passed contained invalid ranges */
> +
> +
> +/* Errors specific to ToString */
> +#define URI_ERROR_TOSTRING_TOO_LONG        URI_ERROR_OUTPUT_TOO_LARGE /* Deprecated, test for URI_ERROR_OUTPUT_TOO_LARGE instead */
> +
> +/* Errors specific to AddBaseUri */
> +#define URI_ERROR_ADDBASE_REL_BASE         5 /* Given base is not absolute */
> +
> +/* Errors specific to RemoveBaseUri */
> +#define URI_ERROR_REMOVEBASE_REL_BASE      6 /* Given base is not absolute */
> +#define URI_ERROR_REMOVEBASE_REL_SOURCE    7 /* Given base is not absolute */
> +
> +
> +
> +#ifndef URI_DOXYGEN
> +# include <stdio.h> /* For NULL, snprintf */
> +# include <ctype.h> /* For wchar_t */
> +# include <string.h> /* For strlen, memset, memcpy */
> +# include <stdlib.h> /* For malloc */
> +#endif /* URI_DOXYGEN */
> +
> +
> +
> +/**
> + * Holds an IPv4 address.
> + */
> +typedef struct UriIp4Struct {
> +	unsigned char data[4]; /**< Each octet in one byte */
> +} UriIp4; /**< @copydoc UriIp4Struct */
> +
> +
> +
> +/**
> + * Holds an IPv6 address.
> + */
> +typedef struct UriIp6Struct {
> +	unsigned char data[16]; /**< Each quad in two bytes */
> +} UriIp6; /**< @copydoc UriIp6Struct */
> +
> +
> +
> +/**
> + * Specifies a line break conversion mode
> + */
> +typedef enum UriBreakConversionEnum {
> +	URI_BR_TO_LF, /**< Convert to Unix line breaks ("\\x0a") */
> +	URI_BR_TO_CRLF, /**< Convert to Windows line breaks ("\\x0d\\x0a") */
> +	URI_BR_TO_CR, /**< Convert to Macintosh line breaks ("\\x0d") */
> +	URI_BR_TO_UNIX = URI_BR_TO_LF, /**< @copydoc UriBreakConversionEnum::URI_BR_TO_LF */
> +	URI_BR_TO_WINDOWS = URI_BR_TO_CRLF, /**< @copydoc UriBreakConversionEnum::URI_BR_TO_CRLF */
> +	URI_BR_TO_MAC = URI_BR_TO_CR, /**< @copydoc UriBreakConversionEnum::URI_BR_TO_CR */
> +	URI_BR_DONT_TOUCH /**< Copy line breaks unmodified */
> +} UriBreakConversion; /**< @copydoc UriBreakConversionEnum */
> +
> +
> +
> +/**
> + * Specifies which component of a %URI has to be normalized.
> + */
> +typedef enum UriNormalizationMaskEnum {
> +	URI_NORMALIZED = 0, /**< Do not normalize anything */
> +	URI_NORMALIZE_SCHEME = 1 << 0, /**< Normalize scheme (fix uppercase letters) */
> +	URI_NORMALIZE_USER_INFO = 1 << 1, /**< Normalize user info (fix uppercase percent-encodings) */
> +	URI_NORMALIZE_HOST = 1 << 2, /**< Normalize host (fix uppercase letters) */
> +	URI_NORMALIZE_PATH = 1 << 3, /**< Normalize path (fix uppercase percent-encodings and redundant dot segments) */
> +	URI_NORMALIZE_QUERY = 1 << 4, /**< Normalize query (fix uppercase percent-encodings) */
> +	URI_NORMALIZE_FRAGMENT = 1 << 5 /**< Normalize fragment (fix uppercase percent-encodings) */
> +} UriNormalizationMask; /**< @copydoc UriNormalizationMaskEnum */
> +
> +
> +
> +#endif /* URI_BASE_H */
> +
> diff --git a/uriparser/include/uriparser/UriDefsAnsi.h b/uriparser/include/uriparser/UriDefsAnsi.h
> new file mode 100644
> --- /dev/null
> +++ b/uriparser/include/uriparser/UriDefsAnsi.h
> @@ -0,0 +1,82 @@
> +/*
> + * uriparser - RFC 3986 URI parsing library
> + *
> + * Copyright (C) 2007, Weijia Song <songweijia at gmail.com>
> + * Copyright (C) 2007, Sebastian Pipping <webmaster at hartwork.org>
> + * All rights reserved.
> + *
> + * Redistribution  and use in source and binary forms, with or without
> + * modification,  are permitted provided that the following conditions
> + * are met:
> + *
> + *     * Redistributions   of  source  code  must  retain  the   above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer.
> + *
> + *     * Redistributions  in  binary  form must  reproduce  the  above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer   in  the  documentation  and/or  other  materials
> + *       provided with the distribution.
> + *
> + *     * Neither  the name of the <ORGANIZATION> nor the names of  its
> + *       contributors  may  be  used to endorse  or  promote  products
> + *       derived  from  this software without specific  prior  written
> + *       permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT  NOT
> + * LIMITED  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS
> + * FOR  A  PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT  SHALL  THE
> + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL,    SPECIAL,   EXEMPLARY,   OR   CONSEQUENTIAL   DAMAGES
> + * (INCLUDING,  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> + * SERVICES;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
> + * STRICT  LIABILITY,  OR  TORT (INCLUDING  NEGLIGENCE  OR  OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
> + * OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +/**
> + * @file UriDefsAnsi.h
> + * Holds definitions for the ANSI pass.
> + * NOTE: This header is included N times, not once.
> + */
> +
> +/* Allow multi inclusion */
> +#include "UriDefsConfig.h"
> +
> +
> +
> +#undef URI_CHAR
> +#define URI_CHAR char
> +
> +#undef _UT
> +#define _UT(x) x
> +
> +
> +
> +#undef URI_FUNC
> +#define URI_FUNC(x) uri##x##A
> +
> +#undef URI_TYPE
> +#define URI_TYPE(x) Uri##x##A
> +
> +
> +
> +#undef URI_STRLEN
> +#define URI_STRLEN strlen
> +#undef URI_STRCPY
> +#define URI_STRCPY strcpy
> +#undef URI_STRCMP
> +#define URI_STRCMP strcmp
> +#undef URI_STRNCMP
> +#define URI_STRNCMP strncmp
> +
> +/* TODO Remove on next source-compatibility break */
> +#undef URI_SNPRINTF
> +#if (defined(__WIN32__) || defined(_WIN32) || defined(WIN32))
> +# define URI_SNPRINTF _snprintf
> +#else
> +# define URI_SNPRINTF snprintf
> +#endif
> diff --git a/uriparser/include/uriparser/UriDefsConfig.h b/uriparser/include/uriparser/UriDefsConfig.h
> new file mode 100644
> --- /dev/null
> +++ b/uriparser/include/uriparser/UriDefsConfig.h
> @@ -0,0 +1,105 @@
> +/*
> + * uriparser - RFC 3986 URI parsing library
> + *
> + * Copyright (C) 2007, Weijia Song <songweijia at gmail.com>
> + * Copyright (C) 2007, Sebastian Pipping <webmaster at hartwork.org>
> + * All rights reserved.
> + *
> + * Redistribution  and use in source and binary forms, with or without
> + * modification,  are permitted provided that the following conditions
> + * are met:
> + *
> + *     * Redistributions   of  source  code  must  retain  the   above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer.
> + *
> + *     * Redistributions  in  binary  form must  reproduce  the  above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer   in  the  documentation  and/or  other  materials
> + *       provided with the distribution.
> + *
> + *     * Neither  the name of the <ORGANIZATION> nor the names of  its
> + *       contributors  may  be  used to endorse  or  promote  products
> + *       derived  from  this software without specific  prior  written
> + *       permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT  NOT
> + * LIMITED  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS
> + * FOR  A  PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT  SHALL  THE
> + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL,    SPECIAL,   EXEMPLARY,   OR   CONSEQUENTIAL   DAMAGES
> + * (INCLUDING,  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> + * SERVICES;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
> + * STRICT  LIABILITY,  OR  TORT (INCLUDING  NEGLIGENCE  OR  OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
> + * OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +/**
> + * @file UriDefsConfig.h
> + * Adjusts the internal configuration after processing external definitions.
> + */
> +
> +#ifndef URI_DEFS_CONFIG_H
> +#define URI_DEFS_CONFIG_H 1
> +
> +
> +
> +/* Deny external overriding */
> +#undef URI_ENABLE_ANSI      /* Internal for !URI_NO_ANSI */
> +#undef URI_ENABLE_UNICODE   /* Internal for !URI_NO_UNICODE */
> +
> +
> +
> +/* Encoding */
> +#ifdef URI_NO_ANSI
> +# ifdef URI_NO_UNICODE
> +/* No encoding at all */
> +#  error URI_NO_ANSI and URI_NO_UNICODE cannot go together.
> +# else
> +/* Unicode only */
> +#  define URI_ENABLE_UNICODE   1
> +# endif
> +#else
> +# ifdef URI_NO_UNICODE
> +/* ANSI only */
> +#  define URI_ENABLE_ANSI       1
> +# else
> +/* Both ANSI and Unicode */
> +#  define URI_ENABLE_ANSI       1
> +#  define URI_ENABLE_UNICODE    1
> +# endif
> +#endif
> +
> +
> +
> +/* Function inlining, not ANSI/ISO C! */
> +#if (defined(URI_DOXYGEN) || defined(URI_SIZEDOWN))
> +# define URI_INLINE
> +#elif defined(__INTEL_COMPILER)
> +/* Intel C/C++ */
> +/* http://predef.sourceforge.net/precomp.html#sec20 */
> +/* http://www.intel.com/support/performancetools/c/windows/sb/CS-007751.htm#2 */
> +# define URI_INLINE __force_inline
> +#elif defined(_MSC_VER)
> +/* Microsoft Visual C++ */
> +/* http://predef.sourceforge.net/precomp.html#sec32 */
> +/* http://msdn2.microsoft.com/en-us/library/ms882281.aspx */
> +# define URI_INLINE __forceinline
> +#elif (__GNUC__ >= 4)
> +/* GCC C/C++ 4.x.x */
> +/* http://predef.sourceforge.net/precomp.html#sec13 */
> +# define URI_INLINE __attribute__((always_inline))
> +#elif (__STDC_VERSION__ >= 199901L)
> +/* C99, "inline" is a keyword */
> +# define URI_INLINE inline
> +#else
> +/* No inlining */
> +# define URI_INLINE
> +#endif
> +
> +
> +
> +#endif /* URI_DEFS_CONFIG_H */
> diff --git a/uriparser/include/uriparser/UriDefsUnicode.h b/uriparser/include/uriparser/UriDefsUnicode.h
> new file mode 100644
> --- /dev/null
> +++ b/uriparser/include/uriparser/UriDefsUnicode.h
> @@ -0,0 +1,82 @@
> +/*
> + * uriparser - RFC 3986 URI parsing library
> + *
> + * Copyright (C) 2007, Weijia Song <songweijia at gmail.com>
> + * Copyright (C) 2007, Sebastian Pipping <webmaster at hartwork.org>
> + * All rights reserved.
> + *
> + * Redistribution  and use in source and binary forms, with or without
> + * modification,  are permitted provided that the following conditions
> + * are met:
> + *
> + *     * Redistributions   of  source  code  must  retain  the   above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer.
> + *
> + *     * Redistributions  in  binary  form must  reproduce  the  above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer   in  the  documentation  and/or  other  materials
> + *       provided with the distribution.
> + *
> + *     * Neither  the name of the <ORGANIZATION> nor the names of  its
> + *       contributors  may  be  used to endorse  or  promote  products
> + *       derived  from  this software without specific  prior  written
> + *       permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT  NOT
> + * LIMITED  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS
> + * FOR  A  PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT  SHALL  THE
> + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL,    SPECIAL,   EXEMPLARY,   OR   CONSEQUENTIAL   DAMAGES
> + * (INCLUDING,  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> + * SERVICES;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
> + * STRICT  LIABILITY,  OR  TORT (INCLUDING  NEGLIGENCE  OR  OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
> + * OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +/**
> + * @file UriDefsUnicode.h
> + * Holds definitions for the Unicode pass.
> + * NOTE: This header is included N times, not once.
> + */
> +
> +/* Allow multi inclusion */
> +#include "UriDefsConfig.h"
> +
> +
> +
> +#undef URI_CHAR
> +#define URI_CHAR wchar_t
> +
> +#undef _UT
> +#define _UT(x) L##x
> +
> +
> +
> +#undef URI_FUNC
> +#define URI_FUNC(x) uri##x##W
> +
> +#undef URI_TYPE
> +#define URI_TYPE(x) Uri##x##W
> +
> +
> +
> +#undef URI_STRLEN
> +#define URI_STRLEN wcslen
> +#undef URI_STRCPY
> +#define URI_STRCPY wcscpy
> +#undef URI_STRCMP
> +#define URI_STRCMP wcscmp
> +#undef URI_STRNCMP
> +#define URI_STRNCMP wcsncmp
> +
> +/* TODO Remove on next source-compatibility break */
> +#undef URI_SNPRINTF
> +#if (defined(__WIN32__) || defined(_WIN32) || defined(WIN32))
> +# define URI_SNPRINTF _snwprintf
> +#else
> +# define URI_SNPRINTF swprintf
> +#endif
> diff --git a/uriparser/include/uriparser/UriIp4.h b/uriparser/include/uriparser/UriIp4.h
> new file mode 100644
> --- /dev/null
> +++ b/uriparser/include/uriparser/UriIp4.h
> @@ -0,0 +1,87 @@
> +/*
> + * uriparser - RFC 3986 URI parsing library
> + *
> + * Copyright (C) 2007, Weijia Song <songweijia at gmail.com>
> + * Copyright (C) 2007, Sebastian Pipping <webmaster at hartwork.org>
> + * All rights reserved.
> + *
> + * Redistribution  and use in source and binary forms, with or without
> + * modification,  are permitted provided that the following conditions
> + * are met:
> + *
> + *     * Redistributions   of  source  code  must  retain  the   above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer.
> + *
> + *     * Redistributions  in  binary  form must  reproduce  the  above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer   in  the  documentation  and/or  other  materials
> + *       provided with the distribution.
> + *
> + *     * Neither  the name of the <ORGANIZATION> nor the names of  its
> + *       contributors  may  be  used to endorse  or  promote  products
> + *       derived  from  this software without specific  prior  written
> + *       permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT  NOT
> + * LIMITED  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS
> + * FOR  A  PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT  SHALL  THE
> + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL,    SPECIAL,   EXEMPLARY,   OR   CONSEQUENTIAL   DAMAGES
> + * (INCLUDING,  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> + * SERVICES;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
> + * STRICT  LIABILITY,  OR  TORT (INCLUDING  NEGLIGENCE  OR  OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
> + * OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +/**
> + * @file UriIp4.h
> + * Holds the IPv4 parser interface.
> + * NOTE: This header includes itself twice.
> + */
> +
> +#if (defined(URI_PASS_ANSI) && !defined(URI_IP4_TWICE_H_ANSI)) \
> +	|| (defined(URI_PASS_UNICODE) && !defined(URI_IP4_TWICE_H_UNICODE)) \
> +	|| (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
> +/* What encodings are enabled? */
> +#include "UriDefsConfig.h"
> +#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
> +/* Include SELF twice */
> +# define URI_PASS_ANSI 1
> +# include "UriIp4.h"
> +# undef URI_PASS_ANSI
> +# define URI_PASS_UNICODE 1
> +# include "UriIp4.h"
> +# undef URI_PASS_UNICODE
> +/* Only one pass for each encoding */
> +#elif (defined(URI_PASS_ANSI) && !defined(URI_IP4_TWICE_H_ANSI) \
> +	&& defined(URI_ENABLE_ANSI)) || (defined(URI_PASS_UNICODE) \
> +	&& !defined(URI_IP4_TWICE_H_UNICODE) && defined(URI_ENABLE_UNICODE))
> +# ifdef URI_PASS_ANSI
> +#  define URI_IP4_TWICE_H_ANSI 1
> +#  include "UriDefsAnsi.h"
> +# else
> +#  define URI_IP4_TWICE_H_UNICODE 1
> +#  include "UriDefsUnicode.h"
> +# endif
> +
> +
> +
> +/**
> + * Converts a IPv4 text representation into four bytes.
> + *
> + * @param octetOutput  Output destination
> + * @param first        First character of IPv4 text to parse
> + * @param afterLast    Position to stop parsing at
> + * @return Error code or 0 on success
> + */
> +int URI_FUNC(ParseIpFourAddress)(unsigned char * octetOutput,
> +		const URI_CHAR * first, const URI_CHAR * afterLast);
> +
> +
> +
> +#endif
> +#endif
> diff --git a/uriparser/lib/UriCommon.c b/uriparser/lib/UriCommon.c
> new file mode 100644
> --- /dev/null
> +++ b/uriparser/lib/UriCommon.c
> @@ -0,0 +1,527 @@
> +/*
> + * uriparser - RFC 3986 URI parsing library
> + *
> + * Copyright (C) 2007, Weijia Song <songweijia at gmail.com>
> + * Copyright (C) 2007, Sebastian Pipping <webmaster at hartwork.org>
> + * All rights reserved.
> + *
> + * Redistribution  and use in source and binary forms, with or without
> + * modification,  are permitted provided that the following conditions
> + * are met:
> + *
> + *     * Redistributions   of  source  code  must  retain  the   above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer.
> + *
> + *     * Redistributions  in  binary  form must  reproduce  the  above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer   in  the  documentation  and/or  other  materials
> + *       provided with the distribution.
> + *
> + *     * Neither  the name of the <ORGANIZATION> nor the names of  its
> + *       contributors  may  be  used to endorse  or  promote  products
> + *       derived  from  this software without specific  prior  written
> + *       permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT  NOT
> + * LIMITED  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS
> + * FOR  A  PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT  SHALL  THE
> + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL,    SPECIAL,   EXEMPLARY,   OR   CONSEQUENTIAL   DAMAGES
> + * (INCLUDING,  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> + * SERVICES;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
> + * STRICT  LIABILITY,  OR  TORT (INCLUDING  NEGLIGENCE  OR  OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
> + * OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +/* What encodings are enabled? */
> +#include <uriparser/UriDefsConfig.h>
> +#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
> +/* Include SELF twice */
> +# define URI_PASS_ANSI 1
> +# include "UriCommon.c"
> +# undef URI_PASS_ANSI
> +# define URI_PASS_UNICODE 1
> +# include "UriCommon.c"
> +# undef URI_PASS_UNICODE
> +#else
> +# ifdef URI_PASS_ANSI
> +#  include <uriparser/UriDefsAnsi.h>
> +# else
> +#  include <uriparser/UriDefsUnicode.h>
> +#  include <wchar.h>
> +# endif
> +
> +
> +
> +#ifndef URI_DOXYGEN
> +# include <uriparser/Uri.h>
> +# include "UriCommon.h"
> +#endif
> +
> +
> +
> +/*extern*/ const URI_CHAR * const URI_FUNC(SafeToPointTo) = _UT("X");
> +/*extern*/ const URI_CHAR * const URI_FUNC(ConstPwd) = _UT(".");
> +/*extern*/ const URI_CHAR * const URI_FUNC(ConstParent) = _UT("..");
> +
> +
> +
> +void URI_FUNC(ResetUri)(URI_TYPE(Uri) * uri) {
> +	memset(uri, 0, sizeof(URI_TYPE(Uri)));
> +}
> +
> +
> +
> +/* Properly removes "." and ".." path segments */
> +UriBool URI_FUNC(RemoveDotSegments)(URI_TYPE(Uri) * uri,
> +		UriBool relative) {
> +	if (uri == NULL) {
> +		return URI_TRUE;
> +	}
> +	return URI_FUNC(RemoveDotSegmentsEx)(uri, relative, uri->owner);
> +}
> +
> +
> +
> +UriBool URI_FUNC(RemoveDotSegmentsEx)(URI_TYPE(Uri) * uri,
> +        UriBool relative, UriBool pathOwned) {
> +	URI_TYPE(PathSegment) * walker;
> +	if ((uri == NULL) || (uri->pathHead == NULL)) {
> +		return URI_TRUE;
> +	}
> +
> +	walker = uri->pathHead;
> +	walker->reserved = NULL; /* Prev pointer */
> +	do {
> +		UriBool removeSegment = URI_FALSE;
> +		int len = (int)(walker->text.afterLast - walker->text.first);
> +		switch (len) {
> +		case 1:
> +			if ((walker->text.first)[0] == _UT('.')) {
> +				/* "." segment -> remove if not essential */
> +				URI_TYPE(PathSegment) * const prev = walker->reserved;
> +				URI_TYPE(PathSegment) * const nextBackup = walker->next;
> +
> +				/* Is this dot segment essential? */
> +				removeSegment = URI_TRUE;
> +				if (relative && (walker == uri->pathHead) && (walker->next != NULL)) {
> +					const URI_CHAR * ch = walker->next->text.first;
> +					for (; ch < walker->next->text.afterLast; ch++) {
> +						if (*ch == _UT(':')) {
> +							removeSegment = URI_FALSE;
> +							break;
> +						}
> +					}
> +				}
> +
> +				if (removeSegment) {
> +					/* Last segment? */
> +					if (walker->next != NULL) {
> +						/* Not last segment */
> +						walker->next->reserved = prev;
> +
> +						if (prev == NULL) {
> +							/* First but not last segment */
> +							uri->pathHead = walker->next;
> +						} else {
> +							/* Middle segment */
> +							prev->next = walker->next;
> +						}
> +
> +						if (pathOwned && (walker->text.first != walker->text.afterLast)) {
> +							free((URI_CHAR *)walker->text.first);
> +						}
> +						free(walker);
> +					} else {
> +						/* Last segment */
> +						if (pathOwned && (walker->text.first != walker->text.afterLast)) {
> +							free((URI_CHAR *)walker->text.first);
> +						}
> +
> +						if (prev == NULL) {
> +							/* Last and first */
> +							if (URI_FUNC(IsHostSet)(uri)) {
> +								/* Replace "." with empty segment to represent trailing slash */
> +								walker->text.first = URI_FUNC(SafeToPointTo);
> +								walker->text.afterLast = URI_FUNC(SafeToPointTo);
> +							} else {
> +								free(walker);
> +
> +								uri->pathHead = NULL;
> +								uri->pathTail = NULL;
> +							}
> +						} else {
> +							/* Last but not first, replace "." with empty segment to represent trailing slash */
> +							walker->text.first = URI_FUNC(SafeToPointTo);
> +							walker->text.afterLast = URI_FUNC(SafeToPointTo);
> +						}
> +					}
> +
> +					walker = nextBackup;
> +				}
> +			}
> +			break;
> +
> +		case 2:
> +			if (((walker->text.first)[0] == _UT('.'))
> +					&& ((walker->text.first)[1] == _UT('.'))) {
> +				/* Path ".." -> remove this and the previous segment */
> +				URI_TYPE(PathSegment) * const prev = walker->reserved;
> +				URI_TYPE(PathSegment) * prevPrev;
> +				URI_TYPE(PathSegment) * const nextBackup = walker->next;
> +
> +				removeSegment = URI_TRUE;
> +				if (relative) {
> +					if (prev == NULL) {
> +						removeSegment = URI_FALSE;
> +					} else if ((prev != NULL)
> +							&& ((prev->text.afterLast - prev->text.first) == 2)
> +							&& ((prev->text.first)[0] == _UT('.'))
> +							&& ((prev->text.first)[1] == _UT('.'))) {
> +						removeSegment = URI_FALSE;
> +					}
> +				}
> +
> +				if (removeSegment) {
> +					if (prev != NULL) {
> +						/* Not first segment */
> +						prevPrev = prev->reserved;
> +						if (prevPrev != NULL) {
> +							/* Not even prev is the first one */
> +							prevPrev->next = walker->next;
> +							if (walker->next != NULL) {
> +								walker->next->reserved = prevPrev;
> +							} else {
> +								/* Last segment -> insert "" segment to represent trailing slash, update tail */
> +								URI_TYPE(PathSegment) * const segment = malloc(1 * sizeof(URI_TYPE(PathSegment)));
> +								if (segment == NULL) {
> +									if (pathOwned && (walker->text.first != walker->text.afterLast)) {
> +										free((URI_CHAR *)walker->text.first);
> +									}
> +									free(walker);
> +
> +									if (pathOwned && (prev->text.first != prev->text.afterLast)) {
> +										free((URI_CHAR *)prev->text.first);
> +									}
> +									free(prev);
> +
> +									return URI_FALSE; /* Raises malloc error */
> +								}
> +								memset(segment, 0, sizeof(URI_TYPE(PathSegment)));
> +								segment->text.first = URI_FUNC(SafeToPointTo);
> +								segment->text.afterLast = URI_FUNC(SafeToPointTo);
> +								prevPrev->next = segment;
> +								uri->pathTail = segment;
> +							}
> +
> +							if (pathOwned && (walker->text.first != walker->text.afterLast)) {
> +								free((URI_CHAR *)walker->text.first);
> +							}
> +							free(walker);
> +
> +							if (pathOwned && (prev->text.first != prev->text.afterLast)) {
> +								free((URI_CHAR *)prev->text.first);
> +							}
> +							free(prev);
> +
> +							walker = nextBackup;
> +						} else {
> +							/* Prev is the first segment */
> +							if (walker->next != NULL) {
> +								uri->pathHead = walker->next;
> +								walker->next->reserved = NULL;
> +
> +								if (pathOwned && (walker->text.first != walker->text.afterLast)) {
> +									free((URI_CHAR *)walker->text.first);
> +								}
> +								free(walker);
> +							} else {
> +								/* Re-use segment for "" path segment to represent trailing slash, update tail */
> +								URI_TYPE(PathSegment) * const segment = walker;
> +								if (pathOwned && (segment->text.first != segment->text.afterLast)) {
> +									free((URI_CHAR *)segment->text.first);
> +								}
> +								segment->text.first = URI_FUNC(SafeToPointTo);
> +								segment->text.afterLast = URI_FUNC(SafeToPointTo);
> +								uri->pathHead = segment;
> +								uri->pathTail = segment;
> +							}
> +
> +							if (pathOwned && (prev->text.first != prev->text.afterLast)) {
> +								free((URI_CHAR *)prev->text.first);
> +							}
> +							free(prev);
> +
> +							walker = nextBackup;
> +						}
> +					} else {
> +						URI_TYPE(PathSegment) * const nextBackup = walker->next;
> +						/* First segment -> update head pointer */
> +						uri->pathHead = walker->next;
> +						if (walker->next != NULL) {
> +							walker->next->reserved = NULL;
> +						} else {
> +							/* Last segment -> update tail */
> +							uri->pathTail = NULL;
> +						}
> +
> +						if (pathOwned && (walker->text.first != walker->text.afterLast)) {
> +							free((URI_CHAR *)walker->text.first);
> +						}
> +						free(walker);
> +
> +						walker = nextBackup;
> +					}
> +				}
> +			}
> +			break;
> +
> +		}
> +
> +		if (!removeSegment) {
> +			if (walker->next != NULL) {
> +				walker->next->reserved = walker;
> +			} else {
> +				/* Last segment -> update tail */
> +				uri->pathTail = walker;
> +			}
> +			walker = walker->next;
> +		}
> +	} while (walker != NULL);
> +
> +	return URI_TRUE;
> +}
> +
> +
> +
> +/* Properly removes "." and ".." path segments */
> +UriBool URI_FUNC(RemoveDotSegmentsAbsolute)(URI_TYPE(Uri) * uri) {
> +	const UriBool ABSOLUTE = URI_FALSE;
> +	return URI_FUNC(RemoveDotSegments)(uri, ABSOLUTE);
> +}
> +
> +
> +
> +unsigned char URI_FUNC(HexdigToInt)(URI_CHAR hexdig) {
> +	switch (hexdig) {
> +	case _UT('0'):
> +	case _UT('1'):
> +	case _UT('2'):
> +	case _UT('3'):
> +	case _UT('4'):
> +	case _UT('5'):
> +	case _UT('6'):
> +	case _UT('7'):
> +	case _UT('8'):
> +	case _UT('9'):
> +		return (unsigned char)(9 + hexdig - _UT('9'));
> +
> +	case _UT('a'):
> +	case _UT('b'):
> +	case _UT('c'):
> +	case _UT('d'):
> +	case _UT('e'):
> +	case _UT('f'):
> +		return (unsigned char)(15 + hexdig - _UT('f'));
> +
> +	case _UT('A'):
> +	case _UT('B'):
> +	case _UT('C'):
> +	case _UT('D'):
> +	case _UT('E'):
> +	case _UT('F'):
> +		return (unsigned char)(15 + hexdig - _UT('F'));
> +
> +	default:
> +		return 0;
> +	}
> +}
> +
> +
> +
> +URI_CHAR URI_FUNC(HexToLetter)(unsigned int value) {
> +	/* Uppercase recommended in section 2.1. of RFC 3986 *
> +	 * http://tools.ietf.org/html/rfc3986#section-2.1    */
> +	return URI_FUNC(HexToLetterEx)(value, URI_TRUE);
> +}
> +
> +
> +
> +URI_CHAR URI_FUNC(HexToLetterEx)(unsigned int value, UriBool uppercase) {
> +	switch (value) {
> +	case  0: return _UT('0');
> +	case  1: return _UT('1');
> +	case  2: return _UT('2');
> +	case  3: return _UT('3');
> +	case  4: return _UT('4');
> +	case  5: return _UT('5');
> +	case  6: return _UT('6');
> +	case  7: return _UT('7');
> +	case  8: return _UT('8');
> +	case  9: return _UT('9');
> +
> +	case 10: return (uppercase == URI_TRUE) ? _UT('A') : _UT('a');
> +	case 11: return (uppercase == URI_TRUE) ? _UT('B') : _UT('b');
> +	case 12: return (uppercase == URI_TRUE) ? _UT('C') : _UT('c');
> +	case 13: return (uppercase == URI_TRUE) ? _UT('D') : _UT('d');
> +	case 14: return (uppercase == URI_TRUE) ? _UT('E') : _UT('e');
> +	default: return (uppercase == URI_TRUE) ? _UT('F') : _UT('f');
> +	}
> +}
> +
> +
> +
> +/* Checks if a URI has the host component set. */
> +UriBool URI_FUNC(IsHostSet)(const URI_TYPE(Uri) * uri) {
> +	return (uri != NULL)
> +			&& ((uri->hostText.first != NULL)
> +				|| (uri->hostData.ip4 != NULL)
> +				|| (uri->hostData.ip6 != NULL)
> +				|| (uri->hostData.ipFuture.first != NULL)
> +			);
> +}
> +
> +
> +
> +/* Copies the path segment list from one URI to another. */
> +UriBool URI_FUNC(CopyPath)(URI_TYPE(Uri) * dest,
> +		const URI_TYPE(Uri) * source) {
> +	if (source->pathHead == NULL) {
> +		/* No path component */
> +		dest->pathHead = NULL;
> +		dest->pathTail = NULL;
> +	} else {
> +		/* Copy list but not the text contained */
> +		URI_TYPE(PathSegment) * sourceWalker = source->pathHead;
> +		URI_TYPE(PathSegment) * destPrev = NULL;
> +		do {
> +			URI_TYPE(PathSegment) * cur = malloc(sizeof(URI_TYPE(PathSegment)));
> +			if (cur == NULL) {
> +				/* Fix broken list */
> +				if (destPrev != NULL) {
> +					destPrev->next = NULL;
> +				}
> +				return URI_FALSE; /* Raises malloc error */
> +			}
> +
> +			/* From this functions usage we know that *
> +			 * the dest URI cannot be uri->owner      */
> +			cur->text = sourceWalker->text;
> +			if (destPrev == NULL) {
> +				/* First segment ever */
> +				dest->pathHead = cur;
> +			} else {
> +				destPrev->next = cur;
> +			}
> +			destPrev = cur;
> +			sourceWalker = sourceWalker->next;
> +		} while (sourceWalker != NULL);
> +		dest->pathTail = destPrev;
> +		dest->pathTail->next = NULL;
> +	}
> +
> +	dest->absolutePath = source->absolutePath;
> +	return URI_TRUE;
> +}
> +
> +
> +
> +/* Copies the authority part of an URI over to another. */
> +UriBool URI_FUNC(CopyAuthority)(URI_TYPE(Uri) * dest,
> +		const URI_TYPE(Uri) * source) {
> +	/* From this functions usage we know that *
> +	 * the dest URI cannot be uri->owner      */
> +
> +	/* Copy userInfo */
> +	dest->userInfo = source->userInfo;
> +
> +	/* Copy hostText */
> +	dest->hostText = source->hostText;
> +
> +	/* Copy hostData */
> +	if (source->hostData.ip4 != NULL) {
> +		dest->hostData.ip4 = malloc(sizeof(UriIp4));
> +		if (dest->hostData.ip4 == NULL) {
> +			return URI_FALSE; /* Raises malloc error */
> +		}
> +		*(dest->hostData.ip4) = *(source->hostData.ip4);
> +		dest->hostData.ip6 = NULL;
> +		dest->hostData.ipFuture.first = NULL;
> +		dest->hostData.ipFuture.afterLast = NULL;
> +	} else if (source->hostData.ip6 != NULL) {
> +		dest->hostData.ip4 = NULL;
> +		dest->hostData.ip6 = malloc(sizeof(UriIp6));
> +		if (dest->hostData.ip6 == NULL) {
> +			return URI_FALSE; /* Raises malloc error */
> +		}
> +		*(dest->hostData.ip6) = *(source->hostData.ip6);
> +		dest->hostData.ipFuture.first = NULL;
> +		dest->hostData.ipFuture.afterLast = NULL;
> +	} else {
> +		dest->hostData.ip4 = NULL;
> +		dest->hostData.ip6 = NULL;
> +		dest->hostData.ipFuture = source->hostData.ipFuture;
> +	}
> +
> +	/* Copy portText */
> +	dest->portText = source->portText;
> +
> +	return URI_TRUE;
> +}
> +
> +
> +
> +UriBool URI_FUNC(FixAmbiguity)(URI_TYPE(Uri) * uri) {
> +	URI_TYPE(PathSegment) * segment;
> +
> +	if (	/* Case 1: absolute path, empty first segment */
> +			(uri->absolutePath
> +			&& (uri->pathHead != NULL)
> +			&& (uri->pathHead->text.afterLast == uri->pathHead->text.first))
> +
> +			/* Case 2: relative path, empty first and second segment */
> +			|| (!uri->absolutePath
> +			&& (uri->pathHead != NULL)
> +			&& (uri->pathHead->next != NULL)
> +			&& (uri->pathHead->text.afterLast == uri->pathHead->text.first)
> +			&& (uri->pathHead->next->text.afterLast == uri->pathHead->next->text.first))) {
> +		/* NOOP */
> +	} else {
> +		return URI_TRUE;
> +	}
> +
> +	segment = malloc(1 * sizeof(URI_TYPE(PathSegment)));
> +	if (segment == NULL) {
> +		return URI_FALSE; /* Raises malloc error */
> +	}
> +
> +	/* Insert "." segment in front */
> +	segment->next = uri->pathHead;
> +	segment->text.first = URI_FUNC(ConstPwd);
> +	segment->text.afterLast = URI_FUNC(ConstPwd) + 1;
> +	uri->pathHead = segment;
> +	return URI_TRUE;
> +}
> +
> +
> +
> +void URI_FUNC(FixEmptyTrailSegment)(URI_TYPE(Uri) * uri) {
> +	/* Fix path if only one empty segment */
> +	if (!uri->absolutePath
> +			&& !URI_FUNC(IsHostSet)(uri)
> +			&& (uri->pathHead != NULL)
> +			&& (uri->pathHead->next == NULL)
> +			&& (uri->pathHead->text.first == uri->pathHead->text.afterLast)) {
> +		free(uri->pathHead);
> +		uri->pathHead = NULL;
> +		uri->pathTail = NULL;
> +	}
> +}
> +
> +
> +
> +#endif
> diff --git a/uriparser/lib/UriCommon.h b/uriparser/lib/UriCommon.h
> new file mode 100644
> --- /dev/null
> +++ b/uriparser/lib/UriCommon.h
> @@ -0,0 +1,96 @@
> +/*
> + * uriparser - RFC 3986 URI parsing library
> + *
> + * Copyright (C) 2007, Weijia Song <songweijia at gmail.com>
> + * Copyright (C) 2007, Sebastian Pipping <webmaster at hartwork.org>
> + * All rights reserved.
> + *
> + * Redistribution  and use in source and binary forms, with or without
> + * modification,  are permitted provided that the following conditions
> + * are met:
> + *
> + *     * Redistributions   of  source  code  must  retain  the   above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer.
> + *
> + *     * Redistributions  in  binary  form must  reproduce  the  above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer   in  the  documentation  and/or  other  materials
> + *       provided with the distribution.
> + *
> + *     * Neither  the name of the <ORGANIZATION> nor the names of  its
> + *       contributors  may  be  used to endorse  or  promote  products
> + *       derived  from  this software without specific  prior  written
> + *       permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT  NOT
> + * LIMITED  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS
> + * FOR  A  PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT  SHALL  THE
> + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL,    SPECIAL,   EXEMPLARY,   OR   CONSEQUENTIAL   DAMAGES
> + * (INCLUDING,  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> + * SERVICES;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
> + * STRICT  LIABILITY,  OR  TORT (INCLUDING  NEGLIGENCE  OR  OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
> + * OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +#if (defined(URI_PASS_ANSI) && !defined(URI_COMMON_H_ANSI)) \
> +	|| (defined(URI_PASS_UNICODE) && !defined(URI_COMMON_H_UNICODE)) \
> +	|| (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
> +/* What encodings are enabled? */
> +#include <uriparser/UriDefsConfig.h>
> +#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
> +/* Include SELF twice */
> +# define URI_PASS_ANSI 1
> +# include "UriCommon.h"
> +# undef URI_PASS_ANSI
> +# define URI_PASS_UNICODE 1
> +# include "UriCommon.h"
> +# undef URI_PASS_UNICODE
> +/* Only one pass for each encoding */
> +#elif (defined(URI_PASS_ANSI) && !defined(URI_COMMON_H_ANSI) \
> +	&& defined(URI_ENABLE_ANSI)) || (defined(URI_PASS_UNICODE) \
> +	&& !defined(URI_COMMON_H_UNICODE) && defined(URI_ENABLE_UNICODE))
> +# ifdef URI_PASS_ANSI
> +#  define URI_COMMON_H_ANSI 1
> +#  include <uriparser/UriDefsAnsi.h>
> +# else
> +#  define URI_COMMON_H_UNICODE 1
> +#  include <uriparser/UriDefsUnicode.h>
> +# endif
> +
> +
> +
> +/* Used to point to from empty path segments.
> + * X.first and X.afterLast must be the same non-NULL value then. */
> +extern const URI_CHAR * const URI_FUNC(SafeToPointTo);
> +extern const URI_CHAR * const URI_FUNC(ConstPwd);
> +extern const URI_CHAR * const URI_FUNC(ConstParent);
> +
> +
> +
> +void URI_FUNC(ResetUri)(URI_TYPE(Uri) * uri);
> +
> +UriBool URI_FUNC(RemoveDotSegmentsAbsolute)(URI_TYPE(Uri) * uri);
> +UriBool URI_FUNC(RemoveDotSegments)(URI_TYPE(Uri) * uri, UriBool relative);
> +UriBool URI_FUNC(RemoveDotSegmentsEx)(URI_TYPE(Uri) * uri,
> +        UriBool relative, UriBool pathOwned);
> +
> +unsigned char URI_FUNC(HexdigToInt)(URI_CHAR hexdig);
> +URI_CHAR URI_FUNC(HexToLetter)(unsigned int value);
> +URI_CHAR URI_FUNC(HexToLetterEx)(unsigned int value, UriBool uppercase);
> +
> +UriBool URI_FUNC(IsHostSet)(const URI_TYPE(Uri) * uri);
> +
> +UriBool URI_FUNC(CopyPath)(URI_TYPE(Uri) * dest, const URI_TYPE(Uri) * source);
> +UriBool URI_FUNC(CopyAuthority)(URI_TYPE(Uri) * dest, const URI_TYPE(Uri) * source);
> +
> +UriBool URI_FUNC(FixAmbiguity)(URI_TYPE(Uri) * uri);
> +void URI_FUNC(FixEmptyTrailSegment)(URI_TYPE(Uri) * uri);
> +
> +
> +#endif
> +#endif
> diff --git a/uriparser/lib/UriCompare.c b/uriparser/lib/UriCompare.c
> new file mode 100644
> --- /dev/null
> +++ b/uriparser/lib/UriCompare.c
> @@ -0,0 +1,191 @@
> +/*
> + * uriparser - RFC 3986 URI parsing library
> + *
> + * Copyright (C) 2007, Weijia Song <songweijia at gmail.com>
> + * Copyright (C) 2007, Sebastian Pipping <webmaster at hartwork.org>
> + * All rights reserved.
> + *
> + * Redistribution  and use in source and binary forms, with or without
> + * modification,  are permitted provided that the following conditions
> + * are met:
> + *
> + *     * Redistributions   of  source  code  must  retain  the   above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer.
> + *
> + *     * Redistributions  in  binary  form must  reproduce  the  above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer   in  the  documentation  and/or  other  materials
> + *       provided with the distribution.
> + *
> + *     * Neither  the name of the <ORGANIZATION> nor the names of  its
> + *       contributors  may  be  used to endorse  or  promote  products
> + *       derived  from  this software without specific  prior  written
> + *       permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT  NOT
> + * LIMITED  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS
> + * FOR  A  PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT  SHALL  THE
> + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL,    SPECIAL,   EXEMPLARY,   OR   CONSEQUENTIAL   DAMAGES
> + * (INCLUDING,  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> + * SERVICES;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
> + * STRICT  LIABILITY,  OR  TORT (INCLUDING  NEGLIGENCE  OR  OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
> + * OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +/* What encodings are enabled? */
> +#include <uriparser/UriDefsConfig.h>
> +#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
> +/* Include SELF twice */
> +# define URI_PASS_ANSI 1
> +# include "UriCompare.c"
> +# undef URI_PASS_ANSI
> +# define URI_PASS_UNICODE 1
> +# include "UriCompare.c"
> +# undef URI_PASS_UNICODE
> +#else
> +# ifdef URI_PASS_ANSI
> +#  include <uriparser/UriDefsAnsi.h>
> +# else
> +#  include <uriparser/UriDefsUnicode.h>
> +#  include <wchar.h>
> +# endif
> +
> +
> +
> +#ifndef URI_DOXYGEN
> +# include <uriparser/Uri.h>
> +# include <uriparser/UriIp4.h>
> +# include "UriCommon.h"
> +#endif
> +
> +
> +
> +static int URI_FUNC(CompareRange)(const URI_TYPE(TextRange) * a,
> +		const URI_TYPE(TextRange) * b);
> +
> +
> +
> +/* Compares two text ranges for equal text content */
> +static URI_INLINE int URI_FUNC(CompareRange)(const URI_TYPE(TextRange) * a,
> +		const URI_TYPE(TextRange) * b) {
> +	int diff;
> +
> +	/* NOTE: Both NULL means equal! */
> +	if ((a == NULL) || (b == NULL)) {
> +		return ((a == NULL) && (b == NULL)) ? URI_TRUE : URI_FALSE;
> +	}
> +
> +	diff = ((int)(a->afterLast - a->first) - (int)(b->afterLast - b->first));
> +	if (diff > 0) {
> +		return 1;
> +	} else if (diff < 0) {
> +		return -1;
> +	}
> +
> +	return URI_STRNCMP(a->first, b->first, (a->afterLast - a->first));
> +}
> +
> +
> +
> +UriBool URI_FUNC(EqualsUri)(const URI_TYPE(Uri) * a,
> +		const URI_TYPE(Uri) * b) {
> +	/* NOTE: Both NULL means equal! */
> +	if ((a == NULL) || (b == NULL)) {
> +		return ((a == NULL) && (b == NULL)) ? URI_TRUE : URI_FALSE;
> +	}
> +
> +	/* scheme */
> +	if (URI_FUNC(CompareRange)(&(a->scheme), &(b->scheme))) {
> +		return URI_FALSE;
> +	}
> +
> +	/* absolutePath */
> +	if ((a->scheme.first == NULL)&& (a->absolutePath != b->absolutePath)) {
> +		return URI_FALSE;
> +	}
> +
> +	/* userInfo */
> +	if (URI_FUNC(CompareRange)(&(a->userInfo), &(b->userInfo))) {
> +		return URI_FALSE;
> +	}
> +
> +	/* Host */
> +	if (((a->hostData.ip4 == NULL) != (b->hostData.ip4 == NULL))
> +			|| ((a->hostData.ip6 == NULL) != (b->hostData.ip6 == NULL))
> +			|| ((a->hostData.ipFuture.first == NULL)
> +				!= (b->hostData.ipFuture.first == NULL))) {
> +		return URI_FALSE;
> +	}
> +
> +	if (a->hostData.ip4 != NULL) {
> +		if (memcmp(a->hostData.ip4->data, b->hostData.ip4->data, 4)) {
> +			return URI_FALSE;
> +		}
> +	}
> +
> +	if (a->hostData.ip6 != NULL) {
> +		if (memcmp(a->hostData.ip6->data, b->hostData.ip6->data, 16)) {
> +			return URI_FALSE;
> +		}
> +	}
> +
> +	if (a->hostData.ipFuture.first != NULL) {
> +		if (URI_FUNC(CompareRange)(&(a->hostData.ipFuture), &(b->hostData.ipFuture))) {
> +			return URI_FALSE;
> +		}
> +	}
> +
> +	if ((a->hostData.ip4 == NULL)
> +			&& (a->hostData.ip6 == NULL)
> +			&& (a->hostData.ipFuture.first == NULL)) {
> +		if (URI_FUNC(CompareRange)(&(a->hostText), &(b->hostText))) {
> +			return URI_FALSE;
> +		}
> +	}
> +
> +	/* portText */
> +	if (URI_FUNC(CompareRange)(&(a->portText), &(b->portText))) {
> +		return URI_FALSE;
> +	}
> +
> +	/* Path */
> +	if ((a->pathHead == NULL) != (b->pathHead == NULL)) {
> +		return URI_FALSE;
> +	}
> +
> +	if (a->pathHead != NULL) {
> +		URI_TYPE(PathSegment) * walkA = a->pathHead;
> +		URI_TYPE(PathSegment) * walkB = b->pathHead;
> +		do {
> +			if (URI_FUNC(CompareRange)(&(walkA->text), &(walkB->text))) {
> +				return URI_FALSE;
> +			}
> +			if ((walkA->next == NULL) != (walkB->next == NULL)) {
> +				return URI_FALSE;
> +			}
> +			walkA = walkA->next;
> +			walkB = walkB->next;
> +		} while (walkA != NULL);
> +	}
> +
> +	/* query */
> +	if (URI_FUNC(CompareRange)(&(a->query), &(b->query))) {
> +		return URI_FALSE;
> +	}
> +
> +	/* fragment */
> +	if (URI_FUNC(CompareRange)(&(a->fragment), &(b->fragment))) {
> +		return URI_FALSE;
> +	}
> +
> +	return URI_TRUE; /* Equal*/
> +}
> +
> +
> +
> +#endif
> diff --git a/uriparser/lib/UriEscape.c b/uriparser/lib/UriEscape.c
> new file mode 100644
> --- /dev/null
> +++ b/uriparser/lib/UriEscape.c
> @@ -0,0 +1,449 @@
> +/*
> + * uriparser - RFC 3986 URI parsing library
> + *
> + * Copyright (C) 2007, Weijia Song <songweijia at gmail.com>
> + * Copyright (C) 2007, Sebastian Pipping <webmaster at hartwork.org>
> + * All rights reserved.
> + *
> + * Redistribution  and use in source and binary forms, with or without
> + * modification,  are permitted provided that the following conditions
> + * are met:
> + *
> + *     * Redistributions   of  source  code  must  retain  the   above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer.
> + *
> + *     * Redistributions  in  binary  form must  reproduce  the  above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer   in  the  documentation  and/or  other  materials
> + *       provided with the distribution.
> + *
> + *     * Neither  the name of the <ORGANIZATION> nor the names of  its
> + *       contributors  may  be  used to endorse  or  promote  products
> + *       derived  from  this software without specific  prior  written
> + *       permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT  NOT
> + * LIMITED  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS
> + * FOR  A  PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT  SHALL  THE
> + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL,    SPECIAL,   EXEMPLARY,   OR   CONSEQUENTIAL   DAMAGES
> + * (INCLUDING,  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> + * SERVICES;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
> + * STRICT  LIABILITY,  OR  TORT (INCLUDING  NEGLIGENCE  OR  OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
> + * OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +/* What encodings are enabled? */
> +#include <uriparser/UriDefsConfig.h>
> +#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
> +/* Include SELF twice */
> +# define URI_PASS_ANSI 1
> +# include "UriEscape.c"
> +# undef URI_PASS_ANSI
> +# define URI_PASS_UNICODE 1
> +# include "UriEscape.c"
> +# undef URI_PASS_UNICODE
> +#else
> +# ifdef URI_PASS_ANSI
> +#  include <uriparser/UriDefsAnsi.h>
> +# else
> +#  include <uriparser/UriDefsUnicode.h>
> +#  include <wchar.h>
> +# endif
> +
> +
> +
> +#ifndef URI_DOXYGEN
> +# include <uriparser/Uri.h>
> +# include "UriCommon.h"
> +#endif
> +
> +
> +
> +URI_CHAR * URI_FUNC(Escape)(const URI_CHAR * in, URI_CHAR * out,
> +		UriBool spaceToPlus, UriBool normalizeBreaks) {
> +	return URI_FUNC(EscapeEx)(in, NULL, out, spaceToPlus, normalizeBreaks);
> +}
> +
> +
> +
> +URI_CHAR * URI_FUNC(EscapeEx)(const URI_CHAR * inFirst,
> +		const URI_CHAR * inAfterLast, URI_CHAR * out,
> +		UriBool spaceToPlus, UriBool normalizeBreaks) {
> +	const URI_CHAR * read = inFirst;
> +	URI_CHAR * write = out;
> +	UriBool prevWasCr = URI_FALSE;
> +	if ((out == NULL) || (inFirst == out)) {
> +		return NULL;
> +	} else if (inFirst == NULL) {
> +		if (out != NULL) {
> +			out[0] = _UT('\0');
> +		}
> +		return out;
> +	}
> +
> +	for (;;) {
> +		if ((inAfterLast != NULL) && (read >= inAfterLast)) {
> +			write[0] = _UT('\0');
> +			return write;
> +		}
> +
> +		switch (read[0]) {
> +		case _UT('\0'):
> +			write[0] = _UT('\0');
> +			return write;
> +
> +		case _UT(' '):
> +			if (spaceToPlus) {
> +				write[0] = _UT('+');
> +				write++;
> +			} else {
> +				write[0] = _UT('%');
> +				write[1] = _UT('2');
> +				write[2] = _UT('0');
> +				write += 3;
> +			}
> +			prevWasCr = URI_FALSE;
> +			break;
> +
> +		case _UT('a'): /* ALPHA */
> +		case _UT('A'):
> +		case _UT('b'):
> +		case _UT('B'):
> +		case _UT('c'):
> +		case _UT('C'):
> +		case _UT('d'):
> +		case _UT('D'):
> +		case _UT('e'):
> +		case _UT('E'):
> +		case _UT('f'):
> +		case _UT('F'):
> +		case _UT('g'):
> +		case _UT('G'):
> +		case _UT('h'):
> +		case _UT('H'):
> +		case _UT('i'):
> +		case _UT('I'):
> +		case _UT('j'):
> +		case _UT('J'):
> +		case _UT('k'):
> +		case _UT('K'):
> +		case _UT('l'):
> +		case _UT('L'):
> +		case _UT('m'):
> +		case _UT('M'):
> +		case _UT('n'):
> +		case _UT('N'):
> +		case _UT('o'):
> +		case _UT('O'):
> +		case _UT('p'):
> +		case _UT('P'):
> +		case _UT('q'):
> +		case _UT('Q'):
> +		case _UT('r'):
> +		case _UT('R'):
> +		case _UT('s'):
> +		case _UT('S'):
> +		case _UT('t'):
> +		case _UT('T'):
> +		case _UT('u'):
> +		case _UT('U'):
> +		case _UT('v'):
> +		case _UT('V'):
> +		case _UT('w'):
> +		case _UT('W'):
> +		case _UT('x'):
> +		case _UT('X'):
> +		case _UT('y'):
> +		case _UT('Y'):
> +		case _UT('z'):
> +		case _UT('Z'):
> +		case _UT('0'): /* DIGIT */
> +		case _UT('1'):
> +		case _UT('2'):
> +		case _UT('3'):
> +		case _UT('4'):
> +		case _UT('5'):
> +		case _UT('6'):
> +		case _UT('7'):
> +		case _UT('8'):
> +		case _UT('9'):
> +		case _UT('-'): /* "-" / "." / "_" / "~" */
> +		case _UT('.'):
> +		case _UT('_'):
> +		case _UT('~'):
> +			/* Copy unmodified */
> +			write[0] = read[0];
> +			write++;
> +
> +			prevWasCr = URI_FALSE;
> +			break;
> +
> +		case _UT('\x0a'):
> +			if (normalizeBreaks) {
> +				if (!prevWasCr) {
> +					write[0] = _UT('%');
> +					write[1] = _UT('0');
> +					write[2] = _UT('D');
> +					write[3] = _UT('%');
> +					write[4] = _UT('0');
> +					write[5] = _UT('A');
> +					write += 6;
> +				}
> +			} else {
> +				write[0] = _UT('%');
> +				write[1] = _UT('0');
> +				write[2] = _UT('A');
> +				write += 3;
> +			}
> +			prevWasCr = URI_FALSE;
> +			break;
> +
> +		case _UT('\x0d'):
> +			if (normalizeBreaks) {
> +				write[0] = _UT('%');
> +				write[1] = _UT('0');
> +				write[2] = _UT('D');
> +				write[3] = _UT('%');
> +				write[4] = _UT('0');
> +				write[5] = _UT('A');
> +				write += 6;
> +			} else {
> +				write[0] = _UT('%');
> +				write[1] = _UT('0');
> +				write[2] = _UT('D');
> +				write += 3;
> +			}
> +			prevWasCr = URI_TRUE;
> +			break;
> +
> +		default:
> +			/* Percent encode */
> +			{
> +				const unsigned char code = (unsigned char)read[0];
> +				write[0] = _UT('%');
> +				write[1] = URI_FUNC(HexToLetter)(code >> 4);
> +				write[2] = URI_FUNC(HexToLetter)(code & 0x0f);
> +				write += 3;
> +			}
> +			prevWasCr = URI_FALSE;
> +			break;
> +		}
> +
> +		read++;
> +	}
> +}
> +
> +
> +
> +const URI_CHAR * URI_FUNC(UnescapeInPlace)(URI_CHAR * inout) {
> +	return URI_FUNC(UnescapeInPlaceEx)(inout, URI_FALSE, URI_BR_DONT_TOUCH);
> +}
> +
> +
> +
> +const URI_CHAR * URI_FUNC(UnescapeInPlaceEx)(URI_CHAR * inout,
> +		UriBool plusToSpace, UriBreakConversion breakConversion) {
> +	URI_CHAR * read = inout;
> +	URI_CHAR * write = inout;
> +	UriBool prevWasCr = URI_FALSE;
> +
> +	if (inout == NULL) {
> +		return NULL;
> +	}
> +
> +	for (;;) {
> +		switch (read[0]) {
> +		case _UT('\0'):
> +			if (read > write) {
> +				write[0] = _UT('\0');
> +			}
> +			return write;
> +
> +		case _UT('%'):
> +			switch (read[1]) {
> +			case _UT('0'):
> +			case _UT('1'):
> +			case _UT('2'):
> +			case _UT('3'):
> +			case _UT('4'):
> +			case _UT('5'):
> +			case _UT('6'):
> +			case _UT('7'):
> +			case _UT('8'):
> +			case _UT('9'):
> +			case _UT('a'):
> +			case _UT('b'):
> +			case _UT('c'):
> +			case _UT('d'):
> +			case _UT('e'):
> +			case _UT('f'):
> +			case _UT('A'):
> +			case _UT('B'):
> +			case _UT('C'):
> +			case _UT('D'):
> +			case _UT('E'):
> +			case _UT('F'):
> +				switch (read[2]) {
> +				case _UT('0'):
> +				case _UT('1'):
> +				case _UT('2'):
> +				case _UT('3'):
> +				case _UT('4'):
> +				case _UT('5'):
> +				case _UT('6'):
> +				case _UT('7'):
> +				case _UT('8'):
> +				case _UT('9'):
> +				case _UT('a'):
> +				case _UT('b'):
> +				case _UT('c'):
> +				case _UT('d'):
> +				case _UT('e'):
> +				case _UT('f'):
> +				case _UT('A'):
> +				case _UT('B'):
> +				case _UT('C'):
> +				case _UT('D'):
> +				case _UT('E'):
> +				case _UT('F'):
> +					{
> +						/* Percent group found */
> +						const unsigned char left = URI_FUNC(HexdigToInt)(read[1]);
> +						const unsigned char right = URI_FUNC(HexdigToInt)(read[2]);
> +						const int code = 16 * left + right;
> +						switch (code) {
> +						case 10:
> +							switch (breakConversion) {
> +							case URI_BR_TO_LF:
> +								if (!prevWasCr) {
> +									write[0] = (URI_CHAR)10;
> +									write++;
> +								}
> +								break;
> +
> +							case URI_BR_TO_CRLF:
> +								if (!prevWasCr) {
> +									write[0] = (URI_CHAR)13;
> +									write[1] = (URI_CHAR)10;
> +									write += 2;
> +								}
> +								break;
> +
> +							case URI_BR_TO_CR:
> +								if (!prevWasCr) {
> +									write[0] = (URI_CHAR)13;
> +									write++;
> +								}
> +								break;
> +
> +							case URI_BR_DONT_TOUCH:
> +							default:
> +								write[0] = (URI_CHAR)10;
> +								write++;
> +
> +							}
> +							prevWasCr = URI_FALSE;
> +							break;
> +
> +						case 13:
> +							switch (breakConversion) {
> +							case URI_BR_TO_LF:
> +								write[0] = (URI_CHAR)10;
> +								write++;
> +								break;
> +
> +							case URI_BR_TO_CRLF:
> +								write[0] = (URI_CHAR)13;
> +								write[1] = (URI_CHAR)10;
> +								write += 2;
> +								break;
> +
> +							case URI_BR_TO_CR:
> +								write[0] = (URI_CHAR)13;
> +								write++;
> +								break;
> +
> +							case URI_BR_DONT_TOUCH:
> +							default:
> +								write[0] = (URI_CHAR)13;
> +								write++;
> +
> +							}
> +							prevWasCr = URI_TRUE;
> +							break;
> +
> +						default:
> +							write[0] = (URI_CHAR)(code);
> +							write++;
> +
> +							prevWasCr = URI_FALSE;
> +
> +						}
> +						read += 3;
> +					}
> +					break;
> +
> +				default:
> +					/* Copy two chars unmodified and */
> +					/* look at this char again */
> +					if (read > write) {
> +						write[0] = read[0];
> +						write[1] = read[1];
> +					}
> +					read += 2;
> +					write += 2;
> +
> +					prevWasCr = URI_FALSE;
> +				}
> +				break;
> +
> +			default:
> +				/* Copy one char unmodified and */
> +				/* look at this char again */
> +				if (read > write) {
> +					write[0] = read[0];
> +				}
> +				read++;
> +				write++;
> +
> +				prevWasCr = URI_FALSE;
> +			}
> +			break;
> +
> +		case _UT('+'):
> +			if (plusToSpace) {
> +				/* Convert '+' to ' ' */
> +				write[0] = _UT(' ');
> +			} else {
> +				/* Copy one char unmodified */
> +				if (read > write) {
> +					write[0] = read[0];
> +				}
> +			}
> +			read++;
> +			write++;
> +
> +			prevWasCr = URI_FALSE;
> +			break;
> +
> +		default:
> +			/* Copy one char unmodified */
> +			if (read > write) {
> +				write[0] = read[0];
> +			}
> +			read++;
> +			write++;
> +
> +			prevWasCr = URI_FALSE;
> +		}
> +	}
> +}
> +
> +
> +
> +#endif
> diff --git a/uriparser/lib/UriFile.c b/uriparser/lib/UriFile.c
> new file mode 100644
> --- /dev/null
> +++ b/uriparser/lib/UriFile.c
> @@ -0,0 +1,182 @@
> +/*
> + * uriparser - RFC 3986 URI parsing library
> + *
> + * Copyright (C) 2007, Weijia Song <songweijia at gmail.com>
> + * Copyright (C) 2007, Sebastian Pipping <webmaster at hartwork.org>
> + * All rights reserved.
> + *
> + * Redistribution  and use in source and binary forms, with or without
> + * modification,  are permitted provided that the following conditions
> + * are met:
> + *
> + *     * Redistributions   of  source  code  must  retain  the   above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer.
> + *
> + *     * Redistributions  in  binary  form must  reproduce  the  above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer   in  the  documentation  and/or  other  materials
> + *       provided with the distribution.
> + *
> + *     * Neither  the name of the <ORGANIZATION> nor the names of  its
> + *       contributors  may  be  used to endorse  or  promote  products
> + *       derived  from  this software without specific  prior  written
> + *       permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT  NOT
> + * LIMITED  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS
> + * FOR  A  PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT  SHALL  THE
> + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL,    SPECIAL,   EXEMPLARY,   OR   CONSEQUENTIAL   DAMAGES
> + * (INCLUDING,  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> + * SERVICES;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
> + * STRICT  LIABILITY,  OR  TORT (INCLUDING  NEGLIGENCE  OR  OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
> + * OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +/* What encodings are enabled? */
> +#include <uriparser/UriDefsConfig.h>
> +#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
> +/* Include SELF twice */
> +# define URI_PASS_ANSI 1
> +# include "UriFile.c"
> +# undef URI_PASS_ANSI
> +# define URI_PASS_UNICODE 1
> +# include "UriFile.c"
> +# undef URI_PASS_UNICODE
> +#else
> +# ifdef URI_PASS_ANSI
> +#  include <uriparser/UriDefsAnsi.h>
> +# else
> +#  include <uriparser/UriDefsUnicode.h>
> +#  include <wchar.h>
> +# endif
> +
> +
> +
> +#ifndef URI_DOXYGEN
> +# include <uriparser/Uri.h>
> +#endif
> +
> +
> +
> +static URI_INLINE int URI_FUNC(FilenameToUriString)(const URI_CHAR * filename,
> +		URI_CHAR * uriString, UriBool fromUnix) {
> +	const URI_CHAR * input = filename;
> +	const URI_CHAR * lastSep = input - 1;
> +	UriBool firstSegment = URI_TRUE;
> +	URI_CHAR * output = uriString;
> +	const UriBool absolute = (filename != NULL) && ((fromUnix && (filename[0] == _UT('/')))
> +			|| (!fromUnix && (filename[0] != _UT('\0')) && (filename[1] == _UT(':'))));
> +
> +	if ((filename == NULL) || (uriString == NULL)) {
> +		return URI_ERROR_NULL;
> +	}
> +
> +	if (absolute) {
> +		const URI_CHAR * const prefix = fromUnix ? _UT("file://") : _UT("file:///");
> +		const int prefixLen = fromUnix ? 7 : 8;
> +
> +		/* Copy prefix */
> +		memcpy(uriString, prefix, prefixLen * sizeof(URI_CHAR));
> +		output += prefixLen;
> +	}
> +
> +	/* Copy and escape on the fly */
> +	for (;;) {
> +		if ((input[0] == _UT('\0'))
> +				|| (fromUnix && input[0] == _UT('/'))
> +				|| (!fromUnix && input[0] == _UT('\\'))) {
> +			/* Copy text after last seperator */
> +			if (lastSep + 1 < input) {
> +				if (!fromUnix && absolute && (firstSegment == URI_TRUE)) {
> +					/* Quick hack to not convert "C:" to "C%3A" */
> +					const int charsToCopy = (int)(input - (lastSep + 1));
> +					memcpy(output, lastSep + 1, charsToCopy * sizeof(URI_CHAR));
> +					output += charsToCopy;
> +				} else {
> +					output = URI_FUNC(EscapeEx)(lastSep + 1, input, output,
> +							URI_FALSE, URI_FALSE);
> +				}
> +			}
> +			firstSegment = URI_FALSE;
> +		}
> +
> +		if (input[0] == _UT('\0')) {
> +			output[0] = _UT('\0');
> +			break;
> +		} else if (fromUnix && (input[0] == _UT('/'))) {
> +			/* Copy separators unmodified */
> +			output[0] = _UT('/');
> +			output++;
> +			lastSep = input;
> +		} else if (!fromUnix && (input[0] == _UT('\\'))) {
> +			/* Convert backslashes to forward slashes */
> +			output[0] = _UT('/');
> +			output++;
> +			lastSep = input;
> +		}
> +		input++;
> +	}
> +
> +	return URI_SUCCESS;
> +}
> +
> +
> +
> +static URI_INLINE int URI_FUNC(UriStringToFilename)(const URI_CHAR * uriString,
> +		URI_CHAR * filename, UriBool toUnix) {
> +	const URI_CHAR * const prefix = toUnix ? _UT("file://") : _UT("file:///");
> +	const int prefixLen = toUnix ? 7 : 8;
> +	URI_CHAR * walker = filename;
> +	size_t charsToCopy;
> +	const UriBool absolute = (URI_STRNCMP(uriString, prefix, prefixLen) == 0);
> +	const int charsToSkip = (absolute ? prefixLen : 0);
> +
> +	charsToCopy = URI_STRLEN(uriString + charsToSkip) + 1;
> +	memcpy(filename, uriString + charsToSkip, charsToCopy * sizeof(URI_CHAR));
> +	URI_FUNC(UnescapeInPlaceEx)(filename, URI_FALSE, URI_BR_DONT_TOUCH);
> +
> +	/* Convert forward slashes to backslashes */
> +	if (!toUnix) {
> +		while (walker[0] != _UT('\0')) {
> +			if (walker[0] == _UT('/')) {
> +				walker[0] = _UT('\\');
> +			}
> +			walker++;
> +		}
> +	}
> +
> +	return URI_SUCCESS;
> +}
> +
> +
> +
> +int URI_FUNC(UnixFilenameToUriString)(const URI_CHAR * filename, URI_CHAR * uriString) {
> +	return URI_FUNC(FilenameToUriString)(filename, uriString, URI_TRUE);
> +}
> +
> +
> +
> +int URI_FUNC(WindowsFilenameToUriString)(const URI_CHAR * filename, URI_CHAR * uriString) {
> +	return URI_FUNC(FilenameToUriString)(filename, uriString, URI_FALSE);
> +}
> +
> +
> +
> +int URI_FUNC(UriStringToUnixFilename)(const URI_CHAR * uriString, URI_CHAR * filename) {
> +	return URI_FUNC(UriStringToFilename)(uriString, filename, URI_TRUE);
> +}
> +
> +
> +
> +int URI_FUNC(UriStringToWindowsFilename)(const URI_CHAR * uriString, URI_CHAR * filename) {
> +	return URI_FUNC(UriStringToFilename)(uriString, filename, URI_FALSE);
> +}
> +
> +
> +
> +#endif
> diff --git a/uriparser/lib/UriIp4.c b/uriparser/lib/UriIp4.c
> new file mode 100644
> --- /dev/null
> +++ b/uriparser/lib/UriIp4.c
> @@ -0,0 +1,325 @@
> +/*
> + * uriparser - RFC 3986 URI parsing library
> + *
> + * Copyright (C) 2007, Weijia Song <songweijia at gmail.com>
> + * Copyright (C) 2007, Sebastian Pipping <webmaster at hartwork.org>
> + * All rights reserved.
> + *
> + * Redistribution  and use in source and binary forms, with or without
> + * modification,  are permitted provided that the following conditions
> + * are met:
> + *
> + *     * Redistributions   of  source  code  must  retain  the   above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer.
> + *
> + *     * Redistributions  in  binary  form must  reproduce  the  above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer   in  the  documentation  and/or  other  materials
> + *       provided with the distribution.
> + *
> + *     * Neither  the name of the <ORGANIZATION> nor the names of  its
> + *       contributors  may  be  used to endorse  or  promote  products
> + *       derived  from  this software without specific  prior  written
> + *       permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT  NOT
> + * LIMITED  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS
> + * FOR  A  PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT  SHALL  THE
> + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL,    SPECIAL,   EXEMPLARY,   OR   CONSEQUENTIAL   DAMAGES
> + * (INCLUDING,  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> + * SERVICES;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
> + * STRICT  LIABILITY,  OR  TORT (INCLUDING  NEGLIGENCE  OR  OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
> + * OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +/**
> + * @file UriIp4.c
> + * Holds the IPv4 parser implementation.
> + * NOTE: This source file includes itself twice.
> + */
> +
> +/* What encodings are enabled? */
> +#include <uriparser/UriDefsConfig.h>
> +#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
> +/* Include SELF twice */
> +# define URI_PASS_ANSI 1
> +# include "UriIp4.c"
> +# undef URI_PASS_ANSI
> +# define URI_PASS_UNICODE 1
> +# include "UriIp4.c"
> +# undef URI_PASS_UNICODE
> +#else
> +# ifdef URI_PASS_ANSI
> +#  include <uriparser/UriDefsAnsi.h>
> +# else
> +#  include <uriparser/UriDefsUnicode.h>
> +# endif
> +
> +
> +
> +#ifndef URI_DOXYGEN
> +# include <uriparser/UriIp4.h>
> +# include "UriIp4Base.h"
> +# include <uriparser/UriBase.h>
> +#endif
> +
> +
> +
> +/* Prototypes */
> +static const URI_CHAR * URI_FUNC(ParseDecOctet)(UriIp4Parser * parser,
> +		const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParseDecOctetOne)(UriIp4Parser * parser,
> +		const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParseDecOctetTwo)(UriIp4Parser * parser,
> +		const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParseDecOctetThree)(UriIp4Parser * parser,
> +		const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParseDecOctetFour)(UriIp4Parser * parser,
> +		const URI_CHAR * first, const URI_CHAR * afterLast);
> +
> +
> +
> +/*
> + * [ipFourAddress]->[decOctet]<.>[decOctet]<.>[decOctet]<.>[decOctet]
> + */
> +int URI_FUNC(ParseIpFourAddress)(unsigned char * octetOutput,
> +		const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	const URI_CHAR * after;
> +	UriIp4Parser parser;
> +
> +	/* Essential checks */
> +	if ((octetOutput == NULL) || (first == NULL)
> +			|| (afterLast <= first)) {
> +		return URI_ERROR_SYNTAX;
> +	}
> +
> +	/* Reset parser */
> +	parser.stackCount = 0;
> +
> +	/* Octet #1 */
> +	after = URI_FUNC(ParseDecOctet)(&parser, first, afterLast);
> +	if ((after == NULL) || (after >= afterLast) || (*after != _UT('.'))) {
> +		return URI_ERROR_SYNTAX;
> +	}
> +	uriStackToOctet(&parser, octetOutput);
> +
> +	/* Octet #2 */
> +	after = URI_FUNC(ParseDecOctet)(&parser, after + 1, afterLast);
> +	if ((after == NULL) || (after >= afterLast) || (*after != _UT('.'))) {
> +		return URI_ERROR_SYNTAX;
> +	}
> +	uriStackToOctet(&parser, octetOutput + 1);
> +
> +	/* Octet #3 */
> +	after = URI_FUNC(ParseDecOctet)(&parser, after + 1, afterLast);
> +	if ((after == NULL) || (after >= afterLast) || (*after != _UT('.'))) {
> +		return URI_ERROR_SYNTAX;
> +	}
> +	uriStackToOctet(&parser, octetOutput + 2);
> +
> +	/* Octet #4 */
> +	after = URI_FUNC(ParseDecOctet)(&parser, after + 1, afterLast);
> +	if (after != afterLast) {
> +		return URI_ERROR_SYNTAX;
> +	}
> +	uriStackToOctet(&parser, octetOutput + 3);
> +
> +	return URI_SUCCESS;
> +}
> +
> +
> +
> +/*
> + * [decOctet]-><0>
> + * [decOctet]-><1>[decOctetOne]
> + * [decOctet]-><2>[decOctetTwo]
> + * [decOctet]-><3>[decOctetThree]
> + * [decOctet]-><4>[decOctetThree]
> + * [decOctet]-><5>[decOctetThree]
> + * [decOctet]-><6>[decOctetThree]
> + * [decOctet]-><7>[decOctetThree]
> + * [decOctet]-><8>[decOctetThree]
> + * [decOctet]-><9>[decOctetThree]
> + */
> +static URI_INLINE const URI_CHAR * URI_FUNC(ParseDecOctet)(UriIp4Parser * parser,
> +		const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		return NULL;
> +	}
> +
> +	switch (*first) {
> +	case _UT('0'):
> +		uriPushToStack(parser, 0);
> +		return first + 1;
> +
> +	case _UT('1'):
> +		uriPushToStack(parser, 1);
> +		return (const URI_CHAR *)URI_FUNC(ParseDecOctetOne)(parser, first + 1, afterLast);
> +
> +	case _UT('2'):
> +		uriPushToStack(parser, 2);
> +		return (const URI_CHAR *)URI_FUNC(ParseDecOctetTwo)(parser, first + 1, afterLast);
> +
> +	case _UT('3'):
> +	case _UT('4'):
> +	case _UT('5'):
> +	case _UT('6'):
> +	case _UT('7'):
> +	case _UT('8'):
> +	case _UT('9'):
> +		uriPushToStack(parser, (unsigned char)(9 + *first - _UT('9')));
> +		return (const URI_CHAR *)URI_FUNC(ParseDecOctetThree)(parser, first + 1, afterLast);
> +
> +	default:
> +		return NULL;
> +	}
> +}
> +
> +
> +
> +/*
> + * [decOctetOne]-><NULL>
> + * [decOctetOne]->[DIGIT][decOctetThree]
> + */
> +static URI_INLINE const URI_CHAR * URI_FUNC(ParseDecOctetOne)(UriIp4Parser * parser,
> +		const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		return afterLast;
> +	}
> +
> +	switch (*first) {
> +	case _UT('0'):
> +	case _UT('1'):
> +	case _UT('2'):
> +	case _UT('3'):
> +	case _UT('4'):
> +	case _UT('5'):
> +	case _UT('6'):
> +	case _UT('7'):
> +	case _UT('8'):
> +	case _UT('9'):
> +		uriPushToStack(parser, (unsigned char)(9 + *first - _UT('9')));
> +		return (const URI_CHAR *)URI_FUNC(ParseDecOctetThree)(parser, first + 1, afterLast);
> +
> +	default:
> +		return first;
> +	}
> +}
> +
> +
> +
> +/*
> + * [decOctetTwo]-><NULL>
> + * [decOctetTwo]-><0>[decOctetThree]
> + * [decOctetTwo]-><1>[decOctetThree]
> + * [decOctetTwo]-><2>[decOctetThree]
> + * [decOctetTwo]-><3>[decOctetThree]
> + * [decOctetTwo]-><4>[decOctetThree]
> + * [decOctetTwo]-><5>[decOctetFour]
> + * [decOctetTwo]-><6>
> + * [decOctetTwo]-><7>
> + * [decOctetTwo]-><8>
> + * [decOctetTwo]-><9>
> +*/
> +static URI_INLINE const URI_CHAR * URI_FUNC(ParseDecOctetTwo)(UriIp4Parser * parser,
> +		const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		return afterLast;
> +	}
> +
> +	switch (*first) {
> +	case _UT('0'):
> +	case _UT('1'):
> +	case _UT('2'):
> +	case _UT('3'):
> +	case _UT('4'):
> +		uriPushToStack(parser, (unsigned char)(9 + *first - _UT('9')));
> +		return (const URI_CHAR *)URI_FUNC(ParseDecOctetThree)(parser, first + 1, afterLast);
> +
> +	case _UT('5'):
> +		uriPushToStack(parser, 5);
> +		return (const URI_CHAR *)URI_FUNC(ParseDecOctetFour)(parser, first + 1, afterLast);
> +
> +	case _UT('6'):
> +	case _UT('7'):
> +	case _UT('8'):
> +	case _UT('9'):
> +		uriPushToStack(parser, (unsigned char)(9 + *first - _UT('9')));
> +		return first + 1;
> +
> +	default:
> +		return first;
> +	}
> +}
> +
> +
> +
> +/*
> + * [decOctetThree]-><NULL>
> + * [decOctetThree]->[DIGIT]
> + */
> +static URI_INLINE const URI_CHAR * URI_FUNC(ParseDecOctetThree)(UriIp4Parser * parser,
> +		const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		return afterLast;
> +	}
> +
> +	switch (*first) {
> +	case _UT('0'):
> +	case _UT('1'):
> +	case _UT('2'):
> +	case _UT('3'):
> +	case _UT('4'):
> +	case _UT('5'):
> +	case _UT('6'):
> +	case _UT('7'):
> +	case _UT('8'):
> +	case _UT('9'):
> +		uriPushToStack(parser, (unsigned char)(9 + *first - _UT('9')));
> +		return first + 1;
> +
> +	default:
> +		return first;
> +	}
> +}
> +
> +
> +
> +/*
> + * [decOctetFour]-><NULL>
> + * [decOctetFour]-><0>
> + * [decOctetFour]-><1>
> + * [decOctetFour]-><2>
> + * [decOctetFour]-><3>
> + * [decOctetFour]-><4>
> + * [decOctetFour]-><5>
> + */
> +static URI_INLINE const URI_CHAR * URI_FUNC(ParseDecOctetFour)(UriIp4Parser * parser,
> +		const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		return afterLast;
> +	}
> +
> +	switch (*first) {
> +	case _UT('0'):
> +	case _UT('1'):
> +	case _UT('2'):
> +	case _UT('3'):
> +	case _UT('4'):
> +	case _UT('5'):
> +		uriPushToStack(parser, (unsigned char)(9 + *first - _UT('9')));
> +		return first + 1;
> +
> +	default:
> +		return first;
> +	}
> +}
> +
> +
> +
> +#endif
> diff --git a/uriparser/lib/UriIp4Base.c b/uriparser/lib/UriIp4Base.c
> new file mode 100644
> --- /dev/null
> +++ b/uriparser/lib/UriIp4Base.c
> @@ -0,0 +1,96 @@
> +/*
> + * uriparser - RFC 3986 URI parsing library
> + *
> + * Copyright (C) 2007, Weijia Song <songweijia at gmail.com>
> + * Copyright (C) 2007, Sebastian Pipping <webmaster at hartwork.org>
> + * All rights reserved.
> + *
> + * Redistribution  and use in source and binary forms, with or without
> + * modification,  are permitted provided that the following conditions
> + * are met:
> + *
> + *     * Redistributions   of  source  code  must  retain  the   above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer.
> + *
> + *     * Redistributions  in  binary  form must  reproduce  the  above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer   in  the  documentation  and/or  other  materials
> + *       provided with the distribution.
> + *
> + *     * Neither  the name of the <ORGANIZATION> nor the names of  its
> + *       contributors  may  be  used to endorse  or  promote  products
> + *       derived  from  this software without specific  prior  written
> + *       permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT  NOT
> + * LIMITED  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS
> + * FOR  A  PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT  SHALL  THE
> + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL,    SPECIAL,   EXEMPLARY,   OR   CONSEQUENTIAL   DAMAGES
> + * (INCLUDING,  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> + * SERVICES;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
> + * STRICT  LIABILITY,  OR  TORT (INCLUDING  NEGLIGENCE  OR  OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
> + * OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +/**
> + * @file UriIp4Base.c
> + * Holds code independent of the encoding pass.
> + */
> +
> +#ifndef URI_DOXYGEN
> +# include "UriIp4Base.h"
> +#endif
> +
> +
> +
> +void uriStackToOctet(UriIp4Parser * parser, unsigned char * octet) {
> +	switch (parser->stackCount) {
> +	case 1:
> +		*octet = parser->stackOne;
> +		break;
> +
> +	case 2:
> +		*octet = parser->stackOne * 10
> +				+ parser->stackTwo;
> +		break;
> +
> +	case 3:
> +		*octet = parser->stackOne * 100
> +				+ parser->stackTwo * 10
> +				+ parser->stackThree;
> +		break;
> +
> +	default:
> +		;
> +	}
> +	parser->stackCount = 0;
> +}
> +
> +
> +
> +void uriPushToStack(UriIp4Parser * parser, unsigned char digit) {
> +	switch (parser->stackCount) {
> +	case 0:
> +		parser->stackOne = digit;
> +		parser->stackCount = 1;
> +		break;
> +
> +	case 1:
> +		parser->stackTwo = digit;
> +		parser->stackCount = 2;
> +		break;
> +
> +	case 2:
> +		parser->stackThree = digit;
> +		parser->stackCount = 3;
> +		break;
> +
> +	default:
> +		;
> +	}
> +}
> diff --git a/uriparser/lib/UriIp4Base.h b/uriparser/lib/UriIp4Base.h
> new file mode 100644
> --- /dev/null
> +++ b/uriparser/lib/UriIp4Base.h
> @@ -0,0 +1,59 @@
> +/*
> + * uriparser - RFC 3986 URI parsing library
> + *
> + * Copyright (C) 2007, Weijia Song <songweijia at gmail.com>
> + * Copyright (C) 2007, Sebastian Pipping <webmaster at hartwork.org>
> + * All rights reserved.
> + *
> + * Redistribution  and use in source and binary forms, with or without
> + * modification,  are permitted provided that the following conditions
> + * are met:
> + *
> + *     * Redistributions   of  source  code  must  retain  the   above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer.
> + *
> + *     * Redistributions  in  binary  form must  reproduce  the  above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer   in  the  documentation  and/or  other  materials
> + *       provided with the distribution.
> + *
> + *     * Neither  the name of the <ORGANIZATION> nor the names of  its
> + *       contributors  may  be  used to endorse  or  promote  products
> + *       derived  from  this software without specific  prior  written
> + *       permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT  NOT
> + * LIMITED  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS
> + * FOR  A  PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT  SHALL  THE
> + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL,    SPECIAL,   EXEMPLARY,   OR   CONSEQUENTIAL   DAMAGES
> + * (INCLUDING,  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> + * SERVICES;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
> + * STRICT  LIABILITY,  OR  TORT (INCLUDING  NEGLIGENCE  OR  OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
> + * OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +#ifndef URI_IP4_BASE_H
> +#define URI_IP4_BASE_H 1
> +
> +
> +
> +typedef struct UriIp4ParserStruct {
> +	unsigned char stackCount;
> +	unsigned char stackOne;
> +	unsigned char stackTwo;
> +	unsigned char stackThree;
> +} UriIp4Parser;
> +
> +
> +
> +void uriPushToStack(UriIp4Parser * parser, unsigned char digit);
> +void uriStackToOctet(UriIp4Parser * parser, unsigned char * octet);
> +
> +
> +
> +#endif /* URI_IP4_BASE_H */
> diff --git a/uriparser/lib/UriNormalize.c b/uriparser/lib/UriNormalize.c
> new file mode 100644
> --- /dev/null
> +++ b/uriparser/lib/UriNormalize.c
> @@ -0,0 +1,722 @@
> +/*
> + * uriparser - RFC 3986 URI parsing library
> + *
> + * Copyright (C) 2007, Weijia Song <songweijia at gmail.com>
> + * Copyright (C) 2007, Sebastian Pipping <webmaster at hartwork.org>
> + * All rights reserved.
> + *
> + * Redistribution  and use in source and binary forms, with or without
> + * modification,  are permitted provided that the following conditions
> + * are met:
> + *
> + *     * Redistributions   of  source  code  must  retain  the   above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer.
> + *
> + *     * Redistributions  in  binary  form must  reproduce  the  above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer   in  the  documentation  and/or  other  materials
> + *       provided with the distribution.
> + *
> + *     * Neither  the name of the <ORGANIZATION> nor the names of  its
> + *       contributors  may  be  used to endorse  or  promote  products
> + *       derived  from  this software without specific  prior  written
> + *       permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT  NOT
> + * LIMITED  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS
> + * FOR  A  PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT  SHALL  THE
> + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL,    SPECIAL,   EXEMPLARY,   OR   CONSEQUENTIAL   DAMAGES
> + * (INCLUDING,  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> + * SERVICES;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
> + * STRICT  LIABILITY,  OR  TORT (INCLUDING  NEGLIGENCE  OR  OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
> + * OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +/**
> + * @file UriNormalize.c
> + * Holds the RFC 3986 %URI normalization implementation.
> + * NOTE: This source file includes itself twice.
> + */
> +
> +/* What encodings are enabled? */
> +#include <uriparser/UriDefsConfig.h>
> +#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
> +/* Include SELF twice */
> +# define URI_PASS_ANSI 1
> +# include "UriNormalize.c"
> +# undef URI_PASS_ANSI
> +# define URI_PASS_UNICODE 1
> +# include "UriNormalize.c"
> +# undef URI_PASS_UNICODE
> +#else
> +# ifdef URI_PASS_ANSI
> +#  include <uriparser/UriDefsAnsi.h>
> +# else
> +#  include <uriparser/UriDefsUnicode.h>
> +#  include <wchar.h>
> +# endif
> +
> +
> +
> +#ifndef URI_DOXYGEN
> +# include <uriparser/Uri.h>
> +# include "UriNormalizeBase.h"
> +# include "UriCommon.h"
> +#endif
> +
> +
> +
> +static int URI_FUNC(NormalizeSyntaxEngine)(URI_TYPE(Uri) * uri, unsigned int inMask,
> +		unsigned int * outMask);
> +
> +static UriBool URI_FUNC(MakeRangeOwner)(unsigned int * doneMask,
> +		unsigned int maskTest, URI_TYPE(TextRange) * range);
> +static UriBool URI_FUNC(MakeOwner)(URI_TYPE(Uri) * uri,
> +		unsigned int * doneMask);
> +
> +static void URI_FUNC(FixPercentEncodingInplace)(const URI_CHAR * first,
> +		const URI_CHAR ** afterLast);
> +static UriBool URI_FUNC(FixPercentEncodingMalloc)(const URI_CHAR ** first,
> +		const URI_CHAR ** afterLast);
> +static void URI_FUNC(FixPercentEncodingEngine)(
> +		const URI_CHAR * inFirst, const URI_CHAR * inAfterLast,
> +		const URI_CHAR * outFirst, const URI_CHAR ** outAfterLast);
> +
> +static UriBool URI_FUNC(ContainsUppercaseLetters)(const URI_CHAR * first,
> +		const URI_CHAR * afterLast);
> +static UriBool URI_FUNC(ContainsUglyPercentEncoding)(const URI_CHAR * first,
> +		const URI_CHAR * afterLast);
> +
> +static void URI_FUNC(LowercaseInplace)(const URI_CHAR * first,
> +		const URI_CHAR * afterLast);
> +static UriBool URI_FUNC(LowercaseMalloc)(const URI_CHAR ** first,
> +		const URI_CHAR ** afterLast);
> +
> +static void URI_FUNC(PreventLeakage)(URI_TYPE(Uri) * uri,
> +		unsigned int revertMask);
> +
> +
> +
> +static URI_INLINE void URI_FUNC(PreventLeakage)(URI_TYPE(Uri) * uri,
> +		unsigned int revertMask) {
> +	if (revertMask & URI_NORMALIZE_SCHEME) {
> +		free((URI_CHAR *)uri->scheme.first);
> +		uri->scheme.first = NULL;
> +		uri->scheme.afterLast = NULL;
> +	}
> +
> +	if (revertMask & URI_NORMALIZE_USER_INFO) {
> +		free((URI_CHAR *)uri->userInfo.first);
> +		uri->userInfo.first = NULL;
> +		uri->userInfo.afterLast = NULL;
> +	}
> +
> +	if (revertMask & URI_NORMALIZE_HOST) {
> +		if (uri->hostData.ipFuture.first != NULL) {
> +			/* IPvFuture */
> +			free((URI_CHAR *)uri->hostData.ipFuture.first);
> +			uri->hostData.ipFuture.first = NULL;
> +			uri->hostData.ipFuture.afterLast = NULL;
> +			uri->hostText.first = NULL;
> +			uri->hostText.afterLast = NULL;
> +		} else if ((uri->hostText.first != NULL)
> +				&& (uri->hostData.ip4 == NULL)
> +				&& (uri->hostData.ip6 == NULL)) {
> +			/* Regname */
> +			free((URI_CHAR *)uri->hostText.first);
> +			uri->hostText.first = NULL;
> +			uri->hostText.afterLast = NULL;
> +		}
> +	}
> +
> +	/* NOTE: Port cannot happen! */
> +
> +	if (revertMask & URI_NORMALIZE_PATH) {
> +		URI_TYPE(PathSegment) * walker = uri->pathHead;
> +		while (walker != NULL) {
> +			URI_TYPE(PathSegment) * const next = walker->next;
> +			if (walker->text.afterLast > walker->text.first) {
> +				free((URI_CHAR *)walker->text.first);
> +			}
> +			free(walker);
> +			walker = next;
> +		}
> +		uri->pathHead = NULL;
> +		uri->pathTail = NULL;
> +	}
> +
> +	if (revertMask & URI_NORMALIZE_QUERY) {
> +		free((URI_CHAR *)uri->query.first);
> +		uri->query.first = NULL;
> +		uri->query.afterLast = NULL;
> +	}
> +
> +	if (revertMask & URI_NORMALIZE_FRAGMENT) {
> +		free((URI_CHAR *)uri->fragment.first);
> +		uri->fragment.first = NULL;
> +		uri->fragment.afterLast = NULL;
> +	}
> +}
> +
> +
> +
> +static URI_INLINE UriBool URI_FUNC(ContainsUppercaseLetters)(const URI_CHAR * first,
> +		const URI_CHAR * afterLast) {
> +	if ((first != NULL) && (afterLast != NULL) && (afterLast > first)) {
> +		const URI_CHAR * i = first;
> +		for (; i < afterLast; i++) {
> +			/* 6.2.2.1 Case Normalization: uppercase letters in scheme or host */
> +			if ((*i >= _UT('A')) && (*i <= _UT('Z'))) {
> +				return URI_TRUE;
> +			}
> +		}
> +	}
> +	return URI_FALSE;
> +}
> +
> +
> +
> +static URI_INLINE UriBool URI_FUNC(ContainsUglyPercentEncoding)(const URI_CHAR * first,
> +		const URI_CHAR * afterLast) {
> +	if ((first != NULL) && (afterLast != NULL) && (afterLast > first)) {
> +		const URI_CHAR * i = first;
> +		for (; i + 2 < afterLast; i++) {
> +			if (i[0] == _UT('%')) {
> +				/* 6.2.2.1 Case Normalization: *
> +				 * lowercase percent-encodings */
> +				if (((i[1] >= _UT('a')) && (i[1] <= _UT('f')))
> +						|| ((i[2] >= _UT('a')) && (i[2] <= _UT('f')))) {
> +					return URI_TRUE;
> +				} else {
> +					/* 6.2.2.2 Percent-Encoding Normalization: *
> +					 * percent-encoded unreserved characters   */
> +					const unsigned char left = URI_FUNC(HexdigToInt)(i[1]);
> +					const unsigned char right = URI_FUNC(HexdigToInt)(i[2]);
> +					const int code = 16 * left + right;
> +					if (uriIsUnreserved(code)) {
> +						return URI_TRUE;
> +					}
> +				}
> +			}
> +		}
> +	}
> +	return URI_FALSE;
> +}
> +
> +
> +
> +static URI_INLINE void URI_FUNC(LowercaseInplace)(const URI_CHAR * first,
> +		const URI_CHAR * afterLast) {
> +	if ((first != NULL) && (afterLast != NULL) && (afterLast > first)) {
> +		URI_CHAR * i = (URI_CHAR *)first;
> +		const int lowerUpperDiff = (_UT('a') - _UT('A'));
> +		for (; i < afterLast; i++) {
> +			if ((*i >= _UT('A')) && (*i <=_UT('Z'))) {
> +				*i = (URI_CHAR)(*i + lowerUpperDiff);
> +			}
> +		}
> +	}
> +}
> +
> +
> +
> +static URI_INLINE UriBool URI_FUNC(LowercaseMalloc)(const URI_CHAR ** first,
> +		const URI_CHAR ** afterLast) {
> +	int lenInChars;
> +	const int lowerUpperDiff = (_UT('a') - _UT('A'));
> +	URI_CHAR * buffer;
> +	int i = 0;
> +
> +	if ((first == NULL) || (afterLast == NULL) || (*first == NULL)
> +			|| (*afterLast == NULL)) {
> +		return URI_FALSE;
> +	}
> +
> +	lenInChars = (int)(*afterLast - *first);
> +	if (lenInChars == 0) {
> +		return URI_TRUE;
> +	} else if (lenInChars < 0) {
> +		return URI_FALSE;
> +	}
> +
> +	buffer = malloc(lenInChars * sizeof(URI_CHAR));
> +	if (buffer == NULL) {
> +		return URI_FALSE;
> +	}
> +
> +	for (; i < lenInChars; i++) {
> +		if (((*first)[i] >= _UT('A')) && ((*first)[i] <=_UT('Z'))) {
> +			buffer[i] = (URI_CHAR)((*first)[i] + lowerUpperDiff);
> +		} else {
> +			buffer[i] = (*first)[i];
> +		}
> +	}
> +
> +	*first = buffer;
> +	*afterLast = buffer + lenInChars;
> +	return URI_TRUE;
> +}
> +
> +
> +
> +/* NOTE: Implementation must stay inplace-compatible */
> +static URI_INLINE void URI_FUNC(FixPercentEncodingEngine)(
> +		const URI_CHAR * inFirst, const URI_CHAR * inAfterLast,
> +		const URI_CHAR * outFirst, const URI_CHAR ** outAfterLast) {
> +	URI_CHAR * write = (URI_CHAR *)outFirst;
> +	const int lenInChars = (int)(inAfterLast - inFirst);
> +	int i = 0;
> +
> +	/* All but last two */
> +	for (; i + 2 < lenInChars; i++) {
> +		if (inFirst[i] != _UT('%')) {
> +			write[0] = inFirst[i];
> +			write++;
> +		} else {
> +			/* 6.2.2.2 Percent-Encoding Normalization: *
> +			 * percent-encoded unreserved characters   */
> +			const URI_CHAR one = inFirst[i + 1];
> +			const URI_CHAR two = inFirst[i + 2];
> +			const unsigned char left = URI_FUNC(HexdigToInt)(one);
> +			const unsigned char right = URI_FUNC(HexdigToInt)(two);
> +			const int code = 16 * left + right;
> +			if (uriIsUnreserved(code)) {
> +				write[0] = (URI_CHAR)(code);
> +				write++;
> +			} else {
> +				/* 6.2.2.1 Case Normalization: *
> +				 * lowercase percent-encodings */
> +				write[0] = _UT('%');
> +				write[1] = URI_FUNC(HexToLetter)(left);
> +				write[2] = URI_FUNC(HexToLetter)(right);
> +				write += 3;
> +			}
> +
> +			i += 2; /* For the two chars of the percent group we just ate */
> +		}
> +	}
> +
> +	/* Last two */
> +	for (; i < lenInChars; i++) {
> +		write[0] = inFirst[i];
> +		write++;
> +	}
> +
> +	*outAfterLast = write;
> +}
> +
> +
> +
> +static URI_INLINE void URI_FUNC(FixPercentEncodingInplace)(const URI_CHAR * first,
> +		const URI_CHAR ** afterLast) {
> +	/* Death checks */
> +	if ((first == NULL) || (afterLast == NULL) || (*afterLast == NULL)) {
> +		return;
> +	}
> +
> +	/* Fix inplace */
> +	URI_FUNC(FixPercentEncodingEngine)(first, *afterLast, first, afterLast);
> +}
> +
> +
> +
> +static URI_INLINE UriBool URI_FUNC(FixPercentEncodingMalloc)(const URI_CHAR ** first,
> +		const URI_CHAR ** afterLast) {
> +	int lenInChars;
> +	URI_CHAR * buffer;
> +
> +	/* Death checks */
> +	if ((first == NULL) || (afterLast == NULL)
> +			|| (*first == NULL) || (*afterLast == NULL)) {
> +		return URI_FALSE;
> +	}
> +
> +	/* Old text length */
> +	lenInChars = (int)(*afterLast - *first);
> +	if (lenInChars == 0) {
> +		return URI_TRUE;
> +	} else if (lenInChars < 0) {
> +		return URI_FALSE;
> +	}
> +
> +	/* New buffer */
> +	buffer = malloc(lenInChars * sizeof(URI_CHAR));
> +	if (buffer == NULL) {
> +		return URI_FALSE;
> +	}
> +
> +	/* Fix on copy */
> +	URI_FUNC(FixPercentEncodingEngine)(*first, *afterLast, buffer, afterLast);
> +	*first = buffer;
> +	return URI_TRUE;
> +}
> +
> +
> +
> +static URI_INLINE UriBool URI_FUNC(MakeRangeOwner)(unsigned int * doneMask,
> +		unsigned int maskTest, URI_TYPE(TextRange) * range) {
> +	if (((*doneMask & maskTest) == 0)
> +			&& (range->first != NULL)
> +			&& (range->afterLast != NULL)
> +			&& (range->afterLast > range->first)) {
> +		const int lenInChars = (int)(range->afterLast - range->first);
> +		const int lenInBytes = lenInChars * sizeof(URI_CHAR);
> +		URI_CHAR * dup = malloc(lenInBytes);
> +		if (dup == NULL) {
> +			return URI_FALSE; /* Raises malloc error */
> +		}
> +		memcpy(dup, range->first, lenInBytes);
> +		range->first = dup;
> +		range->afterLast = dup + lenInChars;
> +		*doneMask |= maskTest;
> +	}
> +	return URI_TRUE;
> +}
> +
> +
> +
> +static URI_INLINE UriBool URI_FUNC(MakeOwner)(URI_TYPE(Uri) * uri,
> +		unsigned int * doneMask) {
> +	URI_TYPE(PathSegment) * walker = uri->pathHead;
> +	if (!URI_FUNC(MakeRangeOwner)(doneMask, URI_NORMALIZE_SCHEME,
> +				&(uri->scheme))
> +			|| !URI_FUNC(MakeRangeOwner)(doneMask, URI_NORMALIZE_USER_INFO,
> +				&(uri->userInfo))
> +			|| !URI_FUNC(MakeRangeOwner)(doneMask, URI_NORMALIZE_QUERY,
> +				&(uri->query))
> +			|| !URI_FUNC(MakeRangeOwner)(doneMask, URI_NORMALIZE_FRAGMENT,
> +				&(uri->fragment))) {
> +		return URI_FALSE; /* Raises malloc error */
> +	}
> +
> +	/* Host */
> +	if ((*doneMask & URI_NORMALIZE_HOST) == 0) {
> +		if ((uri->hostData.ip4 == NULL)
> +				&& (uri->hostData.ip6 == NULL)) {
> +			if (uri->hostData.ipFuture.first != NULL) {
> +				/* IPvFuture */
> +				if (!URI_FUNC(MakeRangeOwner)(doneMask, URI_NORMALIZE_HOST,
> +						&(uri->hostData.ipFuture))) {
> +					return URI_FALSE; /* Raises malloc error */
> +				}
> +				uri->hostText.first = uri->hostData.ipFuture.first;
> +				uri->hostText.afterLast = uri->hostData.ipFuture.afterLast;
> +			} else if (uri->hostText.first != NULL) {
> +				/* Regname */
> +				if (!URI_FUNC(MakeRangeOwner)(doneMask, URI_NORMALIZE_HOST,
> +						&(uri->hostText))) {
> +					return URI_FALSE; /* Raises malloc error */
> +				}
> +			}
> +		}
> +	}
> +
> +	/* Path */
> +	if ((*doneMask & URI_NORMALIZE_PATH) == 0) {
> +		while (walker != NULL) {
> +			if (!URI_FUNC(MakeRangeOwner)(doneMask, 0, &(walker->text))) {
> +				/* Kill path to one before walker */
> +				URI_TYPE(PathSegment) * ranger = uri->pathHead;
> +				while (ranger->next != walker) {
> +					URI_TYPE(PathSegment) * const next = ranger->next;
> +					if ((ranger->text.first != NULL)
> +							&& (ranger->text.afterLast != NULL)
> +							&& (ranger->text.afterLast > ranger->text.first)) {
> +						free((URI_CHAR *)ranger->text.first);
> +						free(ranger);
> +					}
> +					ranger = next;
> +				}
> +
> +				/* Kill path from walker */
> +				while (walker != NULL) {
> +					URI_TYPE(PathSegment) * const next = walker->next;
> +					free(walker);
> +					walker = next;
> +				}
> +
> +				uri->pathHead = NULL;
> +				uri->pathTail = NULL;
> +				return URI_FALSE; /* Raises malloc error */
> +			}
> +			walker = walker->next;
> +		}
> +		*doneMask |= URI_NORMALIZE_PATH;
> +	}
> +
> +	/* Port text, must come last so we don't have to undo that one if it fails. *
> +	 * Otherwise we would need and extra enum flag for it although the port      *
> +	 * cannot go unnormalized...                                                */
> +	if (!URI_FUNC(MakeRangeOwner)(doneMask, 0, &(uri->portText))) {
> +		return URI_FALSE; /* Raises malloc error */
> +	}
> +
> +	return URI_TRUE;
> +}
> +
> +
> +
> +unsigned int URI_FUNC(NormalizeSyntaxMaskRequired)(const URI_TYPE(Uri) * uri) {
> +	unsigned int res;
> +#if defined(__GNUC__) && ((__GNUC__ > 4) \
> +        || ((__GNUC__ == 4) && defined(__GNUC_MINOR__) && (__GNUC_MINOR__ >= 2)))
> +    /* Slower code that fixes a warning, not sure if this is a smart idea */
> +	URI_TYPE(Uri) writeableClone;
> +	memcpy(&writeableClone, uri, 1 * sizeof(URI_TYPE(Uri)));
> +	URI_FUNC(NormalizeSyntaxEngine)(&writeableClone, 0, &res);
> +#else
> +	URI_FUNC(NormalizeSyntaxEngine)((URI_TYPE(Uri) *)uri, 0, &res);
> +#endif
> +	return res;
> +}
> +
> +
> +
> +int URI_FUNC(NormalizeSyntaxEx)(URI_TYPE(Uri) * uri, unsigned int mask) {
> +	return URI_FUNC(NormalizeSyntaxEngine)(uri, mask, NULL);
> +}
> +
> +
> +
> +int URI_FUNC(NormalizeSyntax)(URI_TYPE(Uri) * uri) {
> +	return URI_FUNC(NormalizeSyntaxEx)(uri, (unsigned int)-1);
> +}
> +
> +
> +
> +static URI_INLINE int URI_FUNC(NormalizeSyntaxEngine)(URI_TYPE(Uri) * uri, unsigned int inMask, unsigned int * outMask) {
> +	unsigned int doneMask = URI_NORMALIZED;
> +	if (uri == NULL) {
> +		if (outMask != NULL) {
> +			*outMask = URI_NORMALIZED;
> +			return URI_SUCCESS;
> +		} else {
> +			return URI_ERROR_NULL;
> +		}
> +	}
> +
> +	if (outMask != NULL) {
> +		/* Reset mask */
> +		*outMask = URI_NORMALIZED;
> +	} else if (inMask == URI_NORMALIZED) {
> +		/* Nothing to do */
> +		return URI_SUCCESS;
> +	}
> +
> +	/* Scheme, host */
> +	if (outMask != NULL) {
> +		const UriBool normalizeScheme = URI_FUNC(ContainsUppercaseLetters)(
> +				uri->scheme.first, uri->scheme.afterLast);
> +		const UriBool normalizeHostCase = URI_FUNC(ContainsUppercaseLetters)(
> +			uri->hostText.first, uri->hostText.afterLast);
> +		if (normalizeScheme) {
> +			*outMask |= URI_NORMALIZE_SCHEME;
> +		}
> +
> +		if (normalizeHostCase) {
> +			*outMask |= URI_NORMALIZE_HOST;
> +		} else {
> +			const UriBool normalizeHostPrecent = URI_FUNC(ContainsUglyPercentEncoding)(
> +					uri->hostText.first, uri->hostText.afterLast);
> +			if (normalizeHostPrecent) {
> +				*outMask |= URI_NORMALIZE_HOST;
> +			}
> +		}
> +	} else {
> +		/* Scheme */
> +		if ((inMask & URI_NORMALIZE_SCHEME) && (uri->scheme.first != NULL)) {
> +			if (uri->owner) {
> +				URI_FUNC(LowercaseInplace)(uri->scheme.first, uri->scheme.afterLast);
> +			} else {
> +				if (!URI_FUNC(LowercaseMalloc)(&(uri->scheme.first), &(uri->scheme.afterLast))) {
> +					URI_FUNC(PreventLeakage)(uri, doneMask);
> +					return URI_ERROR_MALLOC;
> +				}
> +				doneMask |= URI_NORMALIZE_SCHEME;
> +			}
> +		}
> +
> +		/* Host */
> +		if (inMask & URI_NORMALIZE_HOST) {
> +			if (uri->hostData.ipFuture.first != NULL) {
> +				/* IPvFuture */
> +				if (uri->owner) {
> +					URI_FUNC(LowercaseInplace)(uri->hostData.ipFuture.first,
> +							uri->hostData.ipFuture.afterLast);
> +				} else {
> +					if (!URI_FUNC(LowercaseMalloc)(&(uri->hostData.ipFuture.first),
> +							&(uri->hostData.ipFuture.afterLast))) {
> +						URI_FUNC(PreventLeakage)(uri, doneMask);
> +						return URI_ERROR_MALLOC;
> +					}
> +					doneMask |= URI_NORMALIZE_HOST;
> +				}
> +				uri->hostText.first = uri->hostData.ipFuture.first;
> +				uri->hostText.afterLast = uri->hostData.ipFuture.afterLast;
> +			} else if ((uri->hostText.first != NULL)
> +					&& (uri->hostData.ip4 == NULL)
> +					&& (uri->hostData.ip6 == NULL)) {
> +				/* Regname */
> +				if (uri->owner) {
> +					URI_FUNC(FixPercentEncodingInplace)(uri->hostText.first,
> +							&(uri->hostText.afterLast));
> +				} else {
> +					if (!URI_FUNC(FixPercentEncodingMalloc)(
> +							&(uri->hostText.first),
> +							&(uri->hostText.afterLast))) {
> +						URI_FUNC(PreventLeakage)(uri, doneMask);
> +						return URI_ERROR_MALLOC;
> +					}
> +					doneMask |= URI_NORMALIZE_HOST;
> +				}
> +
> +				URI_FUNC(LowercaseInplace)(uri->hostText.first,
> +						uri->hostText.afterLast);
> +			}
> +		}
> +	}
> +
> +	/* User info */
> +	if (outMask != NULL) {
> +		const UriBool normalizeUserInfo = URI_FUNC(ContainsUglyPercentEncoding)(
> +			uri->userInfo.first, uri->userInfo.afterLast);
> +		if (normalizeUserInfo) {
> +			*outMask |= URI_NORMALIZE_USER_INFO;
> +		}
> +	} else {
> +		if ((inMask & URI_NORMALIZE_USER_INFO) && (uri->userInfo.first != NULL)) {
> +			if (uri->owner) {
> +				URI_FUNC(FixPercentEncodingInplace)(uri->userInfo.first, &(uri->userInfo.afterLast));
> +			} else {
> +				if (!URI_FUNC(FixPercentEncodingMalloc)(&(uri->userInfo.first),
> +						&(uri->userInfo.afterLast))) {
> +					URI_FUNC(PreventLeakage)(uri, doneMask);
> +					return URI_ERROR_MALLOC;
> +				}
> +				doneMask |= URI_NORMALIZE_USER_INFO;
> +			}
> +		}
> +	}
> +
> +	/* Path */
> +	if (outMask != NULL) {
> +		const URI_TYPE(PathSegment) * walker = uri->pathHead;
> +		while (walker != NULL) {
> +			const URI_CHAR * const first = walker->text.first;
> +			const URI_CHAR * const afterLast = walker->text.afterLast;
> +			if ((first != NULL)
> +					&& (afterLast != NULL)
> +					&& (afterLast > first)
> +					&& (
> +						(((afterLast - first) == 1)
> +							&& (first[0] == _UT('.')))
> +						||
> +						(((afterLast - first) == 2)
> +							&& (first[0] == _UT('.'))
> +							&& (first[1] == _UT('.')))
> +						||
> +						URI_FUNC(ContainsUglyPercentEncoding)(first, afterLast)
> +					)) {
> +				*outMask |= URI_NORMALIZE_PATH;
> +				break;
> +			}
> +			walker = walker->next;
> +		}
> +	} else if (inMask & URI_NORMALIZE_PATH) {
> +		URI_TYPE(PathSegment) * walker;
> +		const UriBool relative = ((uri->scheme.first == NULL)
> +				&& !uri->absolutePath) ? URI_TRUE : URI_FALSE;
> +
> +		/* Fix percent-encoding for each segment */
> +		walker = uri->pathHead;
> +		if (uri->owner) {
> +			while (walker != NULL) {
> +				URI_FUNC(FixPercentEncodingInplace)(walker->text.first, &(walker->text.afterLast));
> +				walker = walker->next;
> +			}
> +		} else {
> +			while (walker != NULL) {
> +				if (!URI_FUNC(FixPercentEncodingMalloc)(&(walker->text.first),
> +						&(walker->text.afterLast))) {
> +					URI_FUNC(PreventLeakage)(uri, doneMask);
> +					return URI_ERROR_MALLOC;
> +				}
> +				walker = walker->next;
> +			}
> +			doneMask |= URI_NORMALIZE_PATH;
> +		}
> +
> +		/* 6.2.2.3 Path Segment Normalization */
> +		if (!URI_FUNC(RemoveDotSegmentsEx)(uri, relative,
> +				(uri->owner == URI_TRUE)
> +				|| ((doneMask & URI_NORMALIZE_PATH) != 0)
> +				)) {
> +			URI_FUNC(PreventLeakage)(uri, doneMask);
> +			return URI_ERROR_MALLOC;
> +		}
> +		URI_FUNC(FixEmptyTrailSegment)(uri);
> +	}
> +
> +	/* Query, fragment */
> +	if (outMask != NULL) {
> +		const UriBool normalizeQuery = URI_FUNC(ContainsUglyPercentEncoding)(
> +				uri->query.first, uri->query.afterLast);
> +		const UriBool normalizeFragment = URI_FUNC(ContainsUglyPercentEncoding)(
> +				uri->fragment.first, uri->fragment.afterLast);
> +		if (normalizeQuery) {
> +			*outMask |= URI_NORMALIZE_QUERY;
> +		}
> +
> +		if (normalizeFragment) {
> +			*outMask |= URI_NORMALIZE_FRAGMENT;
> +		}
> +	} else {
> +		/* Query */
> +		if ((inMask & URI_NORMALIZE_QUERY) && (uri->query.first != NULL)) {
> +			if (uri->owner) {
> +				URI_FUNC(FixPercentEncodingInplace)(uri->query.first, &(uri->query.afterLast));
> +			} else {
> +				if (!URI_FUNC(FixPercentEncodingMalloc)(&(uri->query.first),
> +						&(uri->query.afterLast))) {
> +					URI_FUNC(PreventLeakage)(uri, doneMask);
> +					return URI_ERROR_MALLOC;
> +				}
> +				doneMask |= URI_NORMALIZE_QUERY;
> +			}
> +		}
> +
> +		/* Fragment */
> +		if ((inMask & URI_NORMALIZE_FRAGMENT) && (uri->fragment.first != NULL)) {
> +			if (uri->owner) {
> +				URI_FUNC(FixPercentEncodingInplace)(uri->fragment.first, &(uri->fragment.afterLast));
> +			} else {
> +				if (!URI_FUNC(FixPercentEncodingMalloc)(&(uri->fragment.first),
> +						&(uri->fragment.afterLast))) {
> +					URI_FUNC(PreventLeakage)(uri, doneMask);
> +					return URI_ERROR_MALLOC;
> +				}
> +				doneMask |= URI_NORMALIZE_FRAGMENT;
> +			}
> +		}
> +	}
> +
> +	/* Dup all not duped yet */
> +	if ((outMask == NULL) && !uri->owner) {
> +		if (!URI_FUNC(MakeOwner)(uri, &doneMask)) {
> +			URI_FUNC(PreventLeakage)(uri, doneMask);
> +			return URI_ERROR_MALLOC;
> +		}
> +		uri->owner = URI_TRUE;
> +	}
> +
> +	return URI_SUCCESS;
> +}
> +
> +
> +
> +#endif
> diff --git a/uriparser/lib/UriNormalizeBase.c b/uriparser/lib/UriNormalizeBase.c
> new file mode 100644
> --- /dev/null
> +++ b/uriparser/lib/UriNormalizeBase.c
> @@ -0,0 +1,119 @@
> +/*
> + * uriparser - RFC 3986 URI parsing library
> + *
> + * Copyright (C) 2007, Weijia Song <songweijia at gmail.com>
> + * Copyright (C) 2007, Sebastian Pipping <webmaster at hartwork.org>
> + * All rights reserved.
> + *
> + * Redistribution  and use in source and binary forms, with or without
> + * modification,  are permitted provided that the following conditions
> + * are met:
> + *
> + *     * Redistributions   of  source  code  must  retain  the   above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer.
> + *
> + *     * Redistributions  in  binary  form must  reproduce  the  above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer   in  the  documentation  and/or  other  materials
> + *       provided with the distribution.
> + *
> + *     * Neither  the name of the <ORGANIZATION> nor the names of  its
> + *       contributors  may  be  used to endorse  or  promote  products
> + *       derived  from  this software without specific  prior  written
> + *       permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT  NOT
> + * LIMITED  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS
> + * FOR  A  PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT  SHALL  THE
> + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL,    SPECIAL,   EXEMPLARY,   OR   CONSEQUENTIAL   DAMAGES
> + * (INCLUDING,  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> + * SERVICES;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
> + * STRICT  LIABILITY,  OR  TORT (INCLUDING  NEGLIGENCE  OR  OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
> + * OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +#ifndef URI_DOXYGEN
> +# include "UriNormalizeBase.h"
> +#endif
> +
> +
> +
> +UriBool uriIsUnreserved(int code) {
> +	switch (code) {
> +	case L'a': /* ALPHA */
> +	case L'A':
> +	case L'b':
> +	case L'B':
> +	case L'c':
> +	case L'C':
> +	case L'd':
> +	case L'D':
> +	case L'e':
> +	case L'E':
> +	case L'f':
> +	case L'F':
> +	case L'g':
> +	case L'G':
> +	case L'h':
> +	case L'H':
> +	case L'i':
> +	case L'I':
> +	case L'j':
> +	case L'J':
> +	case L'k':
> +	case L'K':
> +	case L'l':
> +	case L'L':
> +	case L'm':
> +	case L'M':
> +	case L'n':
> +	case L'N':
> +	case L'o':
> +	case L'O':
> +	case L'p':
> +	case L'P':
> +	case L'q':
> +	case L'Q':
> +	case L'r':
> +	case L'R':
> +	case L's':
> +	case L'S':
> +	case L't':
> +	case L'T':
> +	case L'u':
> +	case L'U':
> +	case L'v':
> +	case L'V':
> +	case L'w':
> +	case L'W':
> +	case L'x':
> +	case L'X':
> +	case L'y':
> +	case L'Y':
> +	case L'z':
> +	case L'Z':
> +	case L'0': /* DIGIT */
> +	case L'1':
> +	case L'2':
> +	case L'3':
> +	case L'4':
> +	case L'5':
> +	case L'6':
> +	case L'7':
> +	case L'8':
> +	case L'9':
> +	case L'-': /* "-" / "." / "_" / "~" */
> +	case L'.':
> +	case L'_':
> +	case L'~':
> +		return URI_TRUE;
> +
> +	default:
> +		return URI_FALSE;
> +	}
> +}
> diff --git a/uriparser/lib/UriNormalizeBase.h b/uriparser/lib/UriNormalizeBase.h
> new file mode 100644
> --- /dev/null
> +++ b/uriparser/lib/UriNormalizeBase.h
> @@ -0,0 +1,53 @@
> +/*
> + * uriparser - RFC 3986 URI parsing library
> + *
> + * Copyright (C) 2007, Weijia Song <songweijia at gmail.com>
> + * Copyright (C) 2007, Sebastian Pipping <webmaster at hartwork.org>
> + * All rights reserved.
> + *
> + * Redistribution  and use in source and binary forms, with or without
> + * modification,  are permitted provided that the following conditions
> + * are met:
> + *
> + *     * Redistributions   of  source  code  must  retain  the   above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer.
> + *
> + *     * Redistributions  in  binary  form must  reproduce  the  above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer   in  the  documentation  and/or  other  materials
> + *       provided with the distribution.
> + *
> + *     * Neither  the name of the <ORGANIZATION> nor the names of  its
> + *       contributors  may  be  used to endorse  or  promote  products
> + *       derived  from  this software without specific  prior  written
> + *       permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT  NOT
> + * LIMITED  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS
> + * FOR  A  PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT  SHALL  THE
> + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL,    SPECIAL,   EXEMPLARY,   OR   CONSEQUENTIAL   DAMAGES
> + * (INCLUDING,  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> + * SERVICES;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
> + * STRICT  LIABILITY,  OR  TORT (INCLUDING  NEGLIGENCE  OR  OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
> + * OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +#ifndef URI_NORMALIZE_BASE_H
> +#define URI_NORMALIZE_BASE_H 1
> +
> +
> +
> +#include <uriparser/UriBase.h>
> +
> +
> +
> +UriBool uriIsUnreserved(int code);
> +
> +
> +
> +#endif /* URI_NORMALIZE_BASE_H */
> diff --git a/uriparser/lib/UriParse.c b/uriparser/lib/UriParse.c
> new file mode 100644
> --- /dev/null
> +++ b/uriparser/lib/UriParse.c
> @@ -0,0 +1,2205 @@
> +/*
> + * uriparser - RFC 3986 URI parsing library
> + *
> + * Copyright (C) 2007, Weijia Song <songweijia at gmail.com>
> + * Copyright (C) 2007, Sebastian Pipping <webmaster at hartwork.org>
> + * All rights reserved.
> + *
> + * Redistribution  and use in source and binary forms, with or without
> + * modification,  are permitted provided that the following conditions
> + * are met:
> + *
> + *     * Redistributions   of  source  code  must  retain  the   above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer.
> + *
> + *     * Redistributions  in  binary  form must  reproduce  the  above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer   in  the  documentation  and/or  other  materials
> + *       provided with the distribution.
> + *
> + *     * Neither  the name of the <ORGANIZATION> nor the names of  its
> + *       contributors  may  be  used to endorse  or  promote  products
> + *       derived  from  this software without specific  prior  written
> + *       permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT  NOT
> + * LIMITED  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS
> + * FOR  A  PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT  SHALL  THE
> + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL,    SPECIAL,   EXEMPLARY,   OR   CONSEQUENTIAL   DAMAGES
> + * (INCLUDING,  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> + * SERVICES;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
> + * STRICT  LIABILITY,  OR  TORT (INCLUDING  NEGLIGENCE  OR  OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
> + * OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +/**
> + * @file UriParse.c
> + * Holds the RFC 3986 %URI parsing implementation.
> + * NOTE: This source file includes itself twice.
> + */
> +
> +/* What encodings are enabled? */
> +#include <uriparser/UriDefsConfig.h>
> +#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
> +/* Include SELF twice */
> +# define URI_PASS_ANSI 1
> +# include "UriParse.c"
> +# undef URI_PASS_ANSI
> +# define URI_PASS_UNICODE 1
> +# include "UriParse.c"
> +# undef URI_PASS_UNICODE
> +#else
> +# ifdef URI_PASS_ANSI
> +#  include <uriparser/UriDefsAnsi.h>
> +# else
> +#  include <uriparser/UriDefsUnicode.h>
> +#  include <wchar.h>
> +# endif
> +
> +
> +
> +#ifndef URI_DOXYGEN
> +# include <uriparser/Uri.h>
> +# include <uriparser/UriIp4.h>
> +# include "UriCommon.h"
> +# include "UriParseBase.h"
> +#endif
> +
> +
> +
> +#define URI_SET_DIGIT \
> +	     _UT('0'): \
> +	case _UT('1'): \
> +	case _UT('2'): \
> +	case _UT('3'): \
> +	case _UT('4'): \
> +	case _UT('5'): \
> +	case _UT('6'): \
> +	case _UT('7'): \
> +	case _UT('8'): \
> +	case _UT('9')
> +
> +#define URI_SET_HEX_LETTER_UPPER \
> +	     _UT('A'): \
> +	case _UT('B'): \
> +	case _UT('C'): \
> +	case _UT('D'): \
> +	case _UT('E'): \
> +	case _UT('F')
> +
> +#define URI_SET_HEX_LETTER_LOWER \
> +	     _UT('a'): \
> +	case _UT('b'): \
> +	case _UT('c'): \
> +	case _UT('d'): \
> +	case _UT('e'): \
> +	case _UT('f')
> +
> +#define URI_SET_HEXDIG \
> +	URI_SET_DIGIT: \
> +	case URI_SET_HEX_LETTER_UPPER: \
> +	case URI_SET_HEX_LETTER_LOWER
> +
> +#define URI_SET_ALPHA \
> +	URI_SET_HEX_LETTER_UPPER: \
> +	case URI_SET_HEX_LETTER_LOWER: \
> +	case _UT('g'): \
> +	case _UT('G'): \
> +	case _UT('h'): \
> +	case _UT('H'): \
> +	case _UT('i'): \
> +	case _UT('I'): \
> +	case _UT('j'): \
> +	case _UT('J'): \
> +	case _UT('k'): \
> +	case _UT('K'): \
> +	case _UT('l'): \
> +	case _UT('L'): \
> +	case _UT('m'): \
> +	case _UT('M'): \
> +	case _UT('n'): \
> +	case _UT('N'): \
> +	case _UT('o'): \
> +	case _UT('O'): \
> +	case _UT('p'): \
> +	case _UT('P'): \
> +	case _UT('q'): \
> +	case _UT('Q'): \
> +	case _UT('r'): \
> +	case _UT('R'): \
> +	case _UT('s'): \
> +	case _UT('S'): \
> +	case _UT('t'): \
> +	case _UT('T'): \
> +	case _UT('u'): \
> +	case _UT('U'): \
> +	case _UT('v'): \
> +	case _UT('V'): \
> +	case _UT('w'): \
> +	case _UT('W'): \
> +	case _UT('x'): \
> +	case _UT('X'): \
> +	case _UT('y'): \
> +	case _UT('Y'): \
> +	case _UT('z'): \
> +	case _UT('Z')
> +
> +
> +
> +static const URI_CHAR * URI_FUNC(ParseAuthority)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParseAuthorityTwo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParseHexZero)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParseHierPart)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParseIpFutLoop)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParseIpFutStopGo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParseIpLit2)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParseIPv6address2)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParseMustBeSegmentNzNc)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParseOwnHost)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParseOwnHost2)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParseOwnHostUserInfo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParseOwnHostUserInfoNz)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParseOwnPortUserInfo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParseOwnUserInfo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParsePartHelperTwo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParsePathAbsEmpty)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParsePathAbsNoLeadSlash)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParsePathRootless)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParsePchar)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParsePctEncoded)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParsePctSubUnres)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParsePort)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParseQueryFrag)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParseSegment)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParseSegmentNz)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParseSegmentNzNcOrScheme2)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParseUriReference)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParseUriTail)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParseUriTailTwo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +static const URI_CHAR * URI_FUNC(ParseZeroMoreSlashSegs)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +
> +static UriBool URI_FUNC(OnExitOwnHost2)(URI_TYPE(ParserState) * state, const URI_CHAR * first);
> +static UriBool URI_FUNC(OnExitOwnHostUserInfo)(URI_TYPE(ParserState) * state, const URI_CHAR * first);
> +static UriBool URI_FUNC(OnExitOwnPortUserInfo)(URI_TYPE(ParserState) * state, const URI_CHAR * first);
> +static UriBool URI_FUNC(OnExitSegmentNzNcOrScheme2)(URI_TYPE(ParserState) * state, const URI_CHAR * first);
> +static void URI_FUNC(OnExitPartHelperTwo)(URI_TYPE(ParserState) * state);
> +
> +static void URI_FUNC(ResetParserState)(URI_TYPE(ParserState) * state);
> +
> +static UriBool URI_FUNC(PushPathSegment)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
> +
> +static void URI_FUNC(StopSyntax)(URI_TYPE(ParserState) * state, const URI_CHAR * errorPos);
> +static void URI_FUNC(StopMalloc)(URI_TYPE(ParserState) * state);
> +
> +
> +
> +static URI_INLINE void URI_FUNC(StopSyntax)(URI_TYPE(ParserState) * state,
> +		const URI_CHAR * errorPos) {
> +	URI_FUNC(FreeUriMembers)(state->uri);
> +	state->errorPos = errorPos;
> +	state->errorCode = URI_ERROR_SYNTAX;
> +}
> +
> +
> +
> +static URI_INLINE void URI_FUNC(StopMalloc)(URI_TYPE(ParserState) * state) {
> +	URI_FUNC(FreeUriMembers)(state->uri);
> +	state->errorPos = NULL;
> +	state->errorCode = URI_ERROR_MALLOC;
> +}
> +
> +
> +
> +/*
> + * [authority]-><[>[ipLit2][authorityTwo]
> + * [authority]->[ownHostUserInfoNz]
> + * [authority]-><NULL>
> + */
> +static URI_INLINE const URI_CHAR * URI_FUNC(ParseAuthority)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		/* "" regname host */
> +		state->uri->hostText.first = URI_FUNC(SafeToPointTo);
> +		state->uri->hostText.afterLast = URI_FUNC(SafeToPointTo);
> +		return afterLast;
> +	}
> +
> +	switch (*first) {
> +	case _UT('['):
> +		{
> +			const URI_CHAR * const afterIpLit2
> +					= URI_FUNC(ParseIpLit2)(state, first + 1, afterLast);
> +			if (afterIpLit2 == NULL) {
> +				return NULL;
> +			}
> +			state->uri->hostText.first = first + 1; /* HOST BEGIN */
> +			return URI_FUNC(ParseAuthorityTwo)(state, afterIpLit2, afterLast);
> +		}
> +
> +	case _UT('!'):
> +	case _UT('$'):
> +	case _UT('%'):
> +	case _UT('&'):
> +	case _UT('('):
> +	case _UT(')'):
> +	case _UT('-'):
> +	case _UT('*'):
> +	case _UT(','):
> +	case _UT('.'):
> +	case _UT(':'):
> +	case _UT(';'):
> +	case _UT('@'):
> +	case _UT('\''):
> +	case _UT('_'):
> +	case _UT('~'):
> +	case _UT('+'):
> +	case _UT('='):
> +	case URI_SET_DIGIT:
> +	case URI_SET_ALPHA:
> +		state->uri->userInfo.first = first; /* USERINFO BEGIN */
> +		return URI_FUNC(ParseOwnHostUserInfoNz)(state, first, afterLast);
> +
> +	default:
> +		/* "" regname host */
> +		state->uri->hostText.first = URI_FUNC(SafeToPointTo);
> +		state->uri->hostText.afterLast = URI_FUNC(SafeToPointTo);
> +		return first;
> +	}
> +}
> +
> +
> +
> +/*
> + * [authorityTwo]-><:>[port]
> + * [authorityTwo]-><NULL>
> + */
> +static URI_INLINE const URI_CHAR * URI_FUNC(ParseAuthorityTwo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		return afterLast;
> +	}
> +
> +	switch (*first) {
> +	case _UT(':'):
> +		{
> +			const URI_CHAR * const afterPort = URI_FUNC(ParsePort)(state, first + 1, afterLast);
> +			if (afterPort == NULL) {
> +				return NULL;
> +			}
> +			state->uri->portText.first = first + 1; /* PORT BEGIN */
> +			state->uri->portText.afterLast = afterPort; /* PORT END */
> +			return afterPort;
> +		}
> +
> +	default:
> +		return first;
> +	}
> +}
> +
> +
> +
> +/*
> + * [hexZero]->[HEXDIG][hexZero]
> + * [hexZero]-><NULL>
> + */
> +static const URI_CHAR * URI_FUNC(ParseHexZero)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		return afterLast;
> +	}
> +
> +	switch (*first) {
> +	case URI_SET_HEXDIG:
> +		return URI_FUNC(ParseHexZero)(state, first + 1, afterLast);
> +
> +	default:
> +		return first;
> +	}
> +}
> +
> +
> +
> +/*
> + * [hierPart]->[pathRootless]
> + * [hierPart]-></>[partHelperTwo]
> + * [hierPart]-><NULL>
> + */
> +static URI_INLINE const URI_CHAR * URI_FUNC(ParseHierPart)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		return afterLast;
> +	}
> +
> +	switch (*first) {
> +	case _UT('!'):
> +	case _UT('$'):
> +	case _UT('%'):
> +	case _UT('&'):
> +	case _UT('('):
> +	case _UT(')'):
> +	case _UT('-'):
> +	case _UT('*'):
> +	case _UT(','):
> +	case _UT('.'):
> +	case _UT(':'):
> +	case _UT(';'):
> +	case _UT('@'):
> +	case _UT('\''):
> +	case _UT('_'):
> +	case _UT('~'):
> +	case _UT('+'):
> +	case _UT('='):
> +	case URI_SET_DIGIT:
> +	case URI_SET_ALPHA:
> +		return URI_FUNC(ParsePathRootless)(state, first, afterLast);
> +
> +	case _UT('/'):
> +		return URI_FUNC(ParsePartHelperTwo)(state, first + 1, afterLast);
> +
> +	default:
> +		return first;
> +	}
> +}
> +
> +
> +
> +/*
> + * [ipFutLoop]->[subDelims][ipFutStopGo]
> + * [ipFutLoop]->[unreserved][ipFutStopGo]
> + * [ipFutLoop]-><:>[ipFutStopGo]
> + */
> +static const URI_CHAR * URI_FUNC(ParseIpFutLoop)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		URI_FUNC(StopSyntax)(state, first);
> +		return NULL;
> +	}
> +
> +	switch (*first) {
> +	case _UT('!'):
> +	case _UT('$'):
> +	case _UT('&'):
> +	case _UT('('):
> +	case _UT(')'):
> +	case _UT('-'):
> +	case _UT('*'):
> +	case _UT(','):
> +	case _UT('.'):
> +	case _UT(':'):
> +	case _UT(';'):
> +	case _UT('\''):
> +	case _UT('_'):
> +	case _UT('~'):
> +	case _UT('+'):
> +	case _UT('='):
> +	case URI_SET_DIGIT:
> +	case URI_SET_ALPHA:
> +		return URI_FUNC(ParseIpFutStopGo)(state, first + 1, afterLast);
> +
> +	default:
> +		URI_FUNC(StopSyntax)(state, first);
> +		return NULL;
> +	}
> +}
> +
> +
> +
> +/*
> + * [ipFutStopGo]->[ipFutLoop]
> + * [ipFutStopGo]-><NULL>
> + */
> +static const URI_CHAR * URI_FUNC(ParseIpFutStopGo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		return afterLast;
> +	}
> +
> +	switch (*first) {
> +	case _UT('!'):
> +	case _UT('$'):
> +	case _UT('&'):
> +	case _UT('('):
> +	case _UT(')'):
> +	case _UT('-'):
> +	case _UT('*'):
> +	case _UT(','):
> +	case _UT('.'):
> +	case _UT(':'):
> +	case _UT(';'):
> +	case _UT('\''):
> +	case _UT('_'):
> +	case _UT('~'):
> +	case _UT('+'):
> +	case _UT('='):
> +	case URI_SET_DIGIT:
> +	case URI_SET_ALPHA:
> +		return URI_FUNC(ParseIpFutLoop)(state, first, afterLast);
> +
> +	default:
> +		return first;
> +	}
> +}
> +
> +
> +
> +/*
> + * [ipFuture]-><v>[HEXDIG][hexZero]<.>[ipFutLoop]
> + */
> +static const URI_CHAR * URI_FUNC(ParseIpFuture)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		URI_FUNC(StopSyntax)(state, first);
> +		return NULL;
> +	}
> +
> +	/*
> +	First character has already been
> +	checked before entering this rule.
> +
> +	switch (*first) {
> +	case _UT('v'):
> +	*/
> +		if (first + 1 >= afterLast) {
> +			URI_FUNC(StopSyntax)(state, first + 1);
> +			return NULL;
> +		}
> +
> +		switch (first[1]) {
> +		case URI_SET_HEXDIG:
> +			{
> +				const URI_CHAR * afterIpFutLoop;
> +				const URI_CHAR * const afterHexZero
> +						= URI_FUNC(ParseHexZero)(state, first + 2, afterLast);
> +				if (afterHexZero == NULL) {
> +					return NULL;
> +				}
> +				if ((afterHexZero >= afterLast)
> +						|| (*afterHexZero != _UT('.'))) {
> +					URI_FUNC(StopSyntax)(state, afterHexZero);
> +					return NULL;
> +				}
> +				state->uri->hostText.first = first; /* HOST BEGIN */
> +				state->uri->hostData.ipFuture.first = first; /* IPFUTURE BEGIN */
> +				afterIpFutLoop = URI_FUNC(ParseIpFutLoop)(state, afterHexZero + 1, afterLast);
> +				if (afterIpFutLoop == NULL) {
> +					return NULL;
> +				}
> +				state->uri->hostText.afterLast = afterIpFutLoop; /* HOST END */
> +				state->uri->hostData.ipFuture.afterLast = afterIpFutLoop; /* IPFUTURE END */
> +				return afterIpFutLoop;
> +			}
> +
> +		default:
> +			URI_FUNC(StopSyntax)(state, first + 1);
> +			return NULL;
> +		}
> +
> +	/*
> +	default:
> +		URI_FUNC(StopSyntax)(state, first);
> +		return NULL;
> +	}
> +	*/
> +}
> +
> +
> +
> +/*
> + * [ipLit2]->[ipFuture]<]>
> + * [ipLit2]->[IPv6address2]
> + */
> +static URI_INLINE const URI_CHAR * URI_FUNC(ParseIpLit2)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		URI_FUNC(StopSyntax)(state, first);
> +		return NULL;
> +	}
> +
> +	switch (*first) {
> +	case _UT('v'):
> +		{
> +			const URI_CHAR * const afterIpFuture
> +					= URI_FUNC(ParseIpFuture)(state, first, afterLast);
> +			if (afterIpFuture == NULL) {
> +				return NULL;
> +			}
> +			if ((afterIpFuture >= afterLast)
> +					|| (*afterIpFuture != _UT(']'))) {
> +				URI_FUNC(StopSyntax)(state, first);
> +				return NULL;
> +			}
> +			return afterIpFuture + 1;
> +		}
> +
> +	case _UT(':'):
> +	case _UT(']'):
> +	case URI_SET_HEXDIG:
> +		state->uri->hostData.ip6 = malloc(1 * sizeof(UriIp6)); /* Freed when stopping on parse error */
> +		if (state->uri->hostData.ip6 == NULL) {
> +			URI_FUNC(StopMalloc)(state);
> +			return NULL;
> +		}
> +		return URI_FUNC(ParseIPv6address2)(state, first, afterLast);
> +
> +	default:
> +		URI_FUNC(StopSyntax)(state, first);
> +		return NULL;
> +	}
> +}
> +
> +
> +
> +/*
> + * [IPv6address2]->..<]>
> + */
> +static const URI_CHAR * URI_FUNC(ParseIPv6address2)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	int zipperEver = 0;
> +	int quadsDone = 0;
> +	int digitCount = 0;
> +	unsigned char digitHistory[4];
> +	int ip4OctetsDone = 0;
> +
> +	unsigned char quadsAfterZipper[14];
> +	int quadsAfterZipperCount = 0;
> +
> +
> +	for (;;) {
> +		if (first >= afterLast) {
> +			URI_FUNC(StopSyntax)(state, first);
> +			return NULL;
> +		}
> +
> +		/* Inside IPv4 part? */
> +		if (ip4OctetsDone > 0) {
> +			/* Eat rest of IPv4 address */
> +			for (;;) {
> +				switch (*first) {
> +				case URI_SET_DIGIT:
> +					if (digitCount == 4) {
> +						URI_FUNC(StopSyntax)(state, first);
> +						return NULL;
> +					}
> +					digitHistory[digitCount++] = (unsigned char)(9 + *first - _UT('9'));
> +					break;
> +
> +				case _UT('.'):
> +					if ((ip4OctetsDone == 4) /* NOTE! */
> +							|| (digitCount == 0)
> +							|| (digitCount == 4)) {
> +						/* Invalid digit or octet count */
> +						URI_FUNC(StopSyntax)(state, first);
> +						return NULL;
> +					} else if ((digitCount > 1)
> +							&& (digitHistory[0] == 0)) {
> +						/* Leading zero */
> +						URI_FUNC(StopSyntax)(state, first - digitCount);
> +						return NULL;
> +					} else if ((digitCount > 2)
> +							&& (digitHistory[1] == 0)) {
> +						/* Leading zero */
> +						URI_FUNC(StopSyntax)(state, first - digitCount + 1);
> +						return NULL;
> +					} else if ((digitCount == 3)
> +							&& (100 * digitHistory[0]
> +								+ 10 * digitHistory[1]
> +								+ digitHistory[2] > 255)) {
> +						/* Octet value too large */
> +						if (digitHistory[0] > 2) {
> +							URI_FUNC(StopSyntax)(state, first - 3);
> +						} else if (digitHistory[1] > 5) {
> +							URI_FUNC(StopSyntax)(state, first - 2);
> +						} else {
> +							URI_FUNC(StopSyntax)(state, first - 1);
> +						}
> +						return NULL;
> +					}
> +
> +					/* Copy IPv4 octet */
> +					state->uri->hostData.ip6->data[16 - 4 + ip4OctetsDone] = uriGetOctetValue(digitHistory, digitCount);
> +					digitCount = 0;
> +					ip4OctetsDone++;
> +					break;
> +
> +				case _UT(']'):
> +					if ((ip4OctetsDone != 3) /* NOTE! */
> +							|| (digitCount == 0)
> +							|| (digitCount == 4)) {
> +						/* Invalid digit or octet count */
> +						URI_FUNC(StopSyntax)(state, first);
> +						return NULL;
> +					} else if ((digitCount > 1)
> +							&& (digitHistory[0] == 0)) {
> +						/* Leading zero */
> +						URI_FUNC(StopSyntax)(state, first - digitCount);
> +						return NULL;
> +					} else if ((digitCount > 2)
> +							&& (digitHistory[1] == 0)) {
> +						/* Leading zero */
> +						URI_FUNC(StopSyntax)(state, first - digitCount + 1);
> +						return NULL;
> +					} else if ((digitCount == 3)
> +							&& (100 * digitHistory[0]
> +								+ 10 * digitHistory[1]
> +								+ digitHistory[2] > 255)) {
> +						/* Octet value too large */
> +						if (digitHistory[0] > 2) {
> +							URI_FUNC(StopSyntax)(state, first - 3);
> +						} else if (digitHistory[1] > 5) {
> +							URI_FUNC(StopSyntax)(state, first - 2);
> +						} else {
> +							URI_FUNC(StopSyntax)(state, first - 1);
> +						}
> +						return NULL;
> +					}
> +
> +					state->uri->hostText.afterLast = first; /* HOST END */
> +
> +					/* Copy missing quads right before IPv4 */
> +					memcpy(state->uri->hostData.ip6->data + 16 - 4 - 2 * quadsAfterZipperCount,
> +								quadsAfterZipper, 2 * quadsAfterZipperCount);
> +
> +					/* Copy last IPv4 octet */
> +					state->uri->hostData.ip6->data[16 - 4 + 3] = uriGetOctetValue(digitHistory, digitCount);
> +
> +					return first + 1;
> +
> +				default:
> +					URI_FUNC(StopSyntax)(state, first);
> +					return NULL;
> +				}
> +				first++;
> +			}
> +		} else {
> +			/* Eat while no dot in sight */
> +			int letterAmong = 0;
> +			int walking = 1;
> +			do {
> +				switch (*first) {
> +				case URI_SET_HEX_LETTER_LOWER:
> +					letterAmong = 1;
> +					if (digitCount == 4) {
> +						URI_FUNC(StopSyntax)(state, first);
> +						return NULL;
> +					}
> +					digitHistory[digitCount] = (unsigned char)(15 + *first - _UT('f'));
> +					digitCount++;
> +					break;
> +
> +				case URI_SET_HEX_LETTER_UPPER:
> +					letterAmong = 1;
> +					if (digitCount == 4) {
> +						URI_FUNC(StopSyntax)(state, first);
> +						return NULL;
> +					}
> +					digitHistory[digitCount] = (unsigned char)(15 + *first - _UT('F'));
> +					digitCount++;
> +					break;
> +
> +				case URI_SET_DIGIT:
> +					if (digitCount == 4) {
> +						URI_FUNC(StopSyntax)(state, first);
> +						return NULL;
> +					}
> +					digitHistory[digitCount] = (unsigned char)(9 + *first - _UT('9'));
> +					digitCount++;
> +					break;
> +
> +				case _UT(':'):
> +					{
> +						int setZipper = 0;
> +
> +						/* Too many quads? */
> +						if (quadsDone > 8 - zipperEver) {
> +							URI_FUNC(StopSyntax)(state, first);
> +							return NULL;
> +						}
> +
> +						/* "::"? */
> +						if (first + 1 >= afterLast) {
> +							URI_FUNC(StopSyntax)(state, first + 1);
> +							return NULL;
> +						}
> +						if (first[1] == _UT(':')) {
> +							const int resetOffset = 2 * (quadsDone + (digitCount > 0));
> +
> +							first++;
> +							if (zipperEver) {
> +								URI_FUNC(StopSyntax)(state, first);
> +								return NULL; /* "::.+::" */
> +							}
> +
> +							/* Zero everything after zipper */
> +							memset(state->uri->hostData.ip6->data + resetOffset, 0, 16 - resetOffset);
> +							setZipper = 1;
> +
> +							/* ":::+"? */
> +							if (first + 1 >= afterLast) {
> +								URI_FUNC(StopSyntax)(state, first + 1);
> +								return NULL; /* No ']' yet */
> +							}
> +							if (first[1] == _UT(':')) {
> +								URI_FUNC(StopSyntax)(state, first + 1);
> +								return NULL; /* ":::+ "*/
> +							}
> +						}
> +						if (digitCount > 0) {
> +							if (zipperEver) {
> +								uriWriteQuadToDoubleByte(digitHistory, digitCount, quadsAfterZipper + 2 * quadsAfterZipperCount);
> +								quadsAfterZipperCount++;
> +							} else {
> +								uriWriteQuadToDoubleByte(digitHistory, digitCount, state->uri->hostData.ip6->data + 2 * quadsDone);
> +							}
> +							quadsDone++;
> +							digitCount = 0;
> +						}
> +						letterAmong = 0;
> +
> +						if (setZipper) {
> +							zipperEver = 1;
> +						}
> +					}
> +					break;
> +
> +				case _UT('.'):
> +					if ((quadsDone > 6) /* NOTE */
> +							|| (!zipperEver && (quadsDone < 6))
> +							|| letterAmong
> +							|| (digitCount == 0)
> +							|| (digitCount == 4)) {
> +						/* Invalid octet before */
> +						URI_FUNC(StopSyntax)(state, first);
> +						return NULL;
> +					} else if ((digitCount > 1)
> +							&& (digitHistory[0] == 0)) {
> +						/* Leading zero */
> +						URI_FUNC(StopSyntax)(state, first - digitCount);
> +						return NULL;
> +					} else if ((digitCount > 2)
> +							&& (digitHistory[1] == 0)) {
> +						/* Leading zero */
> +						URI_FUNC(StopSyntax)(state, first - digitCount + 1);
> +						return NULL;
> +					} else if ((digitCount == 3)
> +							&& (100 * digitHistory[0]
> +								+ 10 * digitHistory[1]
> +								+ digitHistory[2] > 255)) {
> +						/* Octet value too large */
> +						if (digitHistory[0] > 2) {
> +							URI_FUNC(StopSyntax)(state, first - 3);
> +						} else if (digitHistory[1] > 5) {
> +							URI_FUNC(StopSyntax)(state, first - 2);
> +						} else {
> +							URI_FUNC(StopSyntax)(state, first - 1);
> +						}
> +						return NULL;
> +					}
> +
> +					/* Copy first IPv4 octet */
> +					state->uri->hostData.ip6->data[16 - 4] = uriGetOctetValue(digitHistory, digitCount);
> +					digitCount = 0;
> +
> +					/* Switch over to IPv4 loop */
> +					ip4OctetsDone = 1;
> +					walking = 0;
> +					break;
> +
> +				case _UT(']'):
> +					/* Too little quads? */
> +					if (!zipperEver && !((quadsDone == 7) && (digitCount > 0))) {
> +						URI_FUNC(StopSyntax)(state, first);
> +						return NULL;
> +					}
> +
> +					if (digitCount > 0) {
> +						if (zipperEver) {
> +							uriWriteQuadToDoubleByte(digitHistory, digitCount, quadsAfterZipper + 2 * quadsAfterZipperCount);
> +							quadsAfterZipperCount++;
> +						} else {
> +							uriWriteQuadToDoubleByte(digitHistory, digitCount, state->uri->hostData.ip6->data + 2 * quadsDone);
> +						}
> +						/*
> +						quadsDone++;
> +						digitCount = 0;
> +						*/
> +					}
> +
> +					/* Copy missing quads to the end */
> +					memcpy(state->uri->hostData.ip6->data + 16 - 2 * quadsAfterZipperCount,
> +								quadsAfterZipper, 2 * quadsAfterZipperCount);
> +
> +					state->uri->hostText.afterLast = first; /* HOST END */
> +					return first + 1; /* Fine */
> +
> +				default:
> +					URI_FUNC(StopSyntax)(state, first);
> +					return NULL;
> +				}
> +				first++;
> +
> +				if (first >= afterLast) {
> +					URI_FUNC(StopSyntax)(state, first);
> +					return NULL; /* No ']' yet */
> +				}
> +			} while (walking);
> +		}
> +	}
> +}
> +
> +
> +
> +/*
> + * [mustBeSegmentNzNc]->[pctEncoded][mustBeSegmentNzNc]
> + * [mustBeSegmentNzNc]->[subDelims][mustBeSegmentNzNc]
> + * [mustBeSegmentNzNc]->[unreserved][mustBeSegmentNzNc]
> + * [mustBeSegmentNzNc]->[uriTail] // can take <NULL>
> + * [mustBeSegmentNzNc]-></>[segment][zeroMoreSlashSegs][uriTail]
> + * [mustBeSegmentNzNc]-><@>[mustBeSegmentNzNc]
> + */
> +static const URI_CHAR * URI_FUNC(ParseMustBeSegmentNzNc)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		if (!URI_FUNC(PushPathSegment)(state, state->uri->scheme.first, first)) { /* SEGMENT BOTH */
> +			URI_FUNC(StopMalloc)(state);
> +			return NULL;
> +		}
> +		state->uri->scheme.first = NULL; /* Not a scheme, reset */
> +		return afterLast;
> +	}
> +
> +	switch (*first) {
> +	case _UT('%'):
> +		{
> +			const URI_CHAR * const afterPctEncoded
> +					= URI_FUNC(ParsePctEncoded)(state, first, afterLast);
> +			if (afterPctEncoded == NULL) {
> +				return NULL;
> +			}
> +			return URI_FUNC(ParseMustBeSegmentNzNc)(state, afterPctEncoded, afterLast);
> +		}
> +
> +	case _UT('@'):
> +	case _UT('!'):
> +	case _UT('$'):
> +	case _UT('&'):
> +	case _UT('('):
> +	case _UT(')'):
> +	case _UT('*'):
> +	case _UT(','):
> +	case _UT(';'):
> +	case _UT('\''):
> +	case _UT('+'):
> +	case _UT('='):
> +	case _UT('-'):
> +	case _UT('.'):
> +	case _UT('_'):
> +	case _UT('~'):
> +	case URI_SET_DIGIT:
> +	case URI_SET_ALPHA:
> +		return URI_FUNC(ParseMustBeSegmentNzNc)(state, first + 1, afterLast);
> +
> +	case _UT('/'):
> +		{
> +			const URI_CHAR * afterZeroMoreSlashSegs;
> +			const URI_CHAR * afterSegment;
> +			if (!URI_FUNC(PushPathSegment)(state, state->uri->scheme.first, first)) { /* SEGMENT BOTH */
> +				URI_FUNC(StopMalloc)(state);
> +				return NULL;
> +			}
> +			state->uri->scheme.first = NULL; /* Not a scheme, reset */
> +			afterSegment = URI_FUNC(ParseSegment)(state, first + 1, afterLast);
> +			if (afterSegment == NULL) {
> +				return NULL;
> +			}
> +			if (!URI_FUNC(PushPathSegment)(state, first + 1, afterSegment)) { /* SEGMENT BOTH */
> +				URI_FUNC(StopMalloc)(state);
> +				return NULL;
> +			}
> +			afterZeroMoreSlashSegs
> +					= URI_FUNC(ParseZeroMoreSlashSegs)(state, afterSegment, afterLast);
> +			if (afterZeroMoreSlashSegs == NULL) {
> +				return NULL;
> +			}
> +			return URI_FUNC(ParseUriTail)(state, afterZeroMoreSlashSegs, afterLast);
> +		}
> +
> +	default:
> +		if (!URI_FUNC(PushPathSegment)(state, state->uri->scheme.first, first)) { /* SEGMENT BOTH */
> +			URI_FUNC(StopMalloc)(state);
> +			return NULL;
> +		}
> +		state->uri->scheme.first = NULL; /* Not a scheme, reset */
> +		return URI_FUNC(ParseUriTail)(state, first, afterLast);
> +	}
> +}
> +
> +
> +
> +/*
> + * [ownHost]-><[>[ipLit2][authorityTwo]
> + * [ownHost]->[ownHost2] // can take <NULL>
> + */
> +static URI_INLINE const URI_CHAR * URI_FUNC(ParseOwnHost)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		return afterLast;
> +	}
> +
> +	switch (*first) {
> +	case _UT('['):
> +		{
> +			const URI_CHAR * const afterIpLit2
> +					= URI_FUNC(ParseIpLit2)(state, first + 1, afterLast);
> +			if (afterIpLit2 == NULL) {
> +				return NULL;
> +			}
> +			state->uri->hostText.first = first + 1; /* HOST BEGIN */
> +			return URI_FUNC(ParseAuthorityTwo)(state, afterIpLit2, afterLast);
> +		}
> +
> +	default:
> +		return URI_FUNC(ParseOwnHost2)(state, first, afterLast);
> +	}
> +}
> +
> +
> +
> +static URI_INLINE UriBool URI_FUNC(OnExitOwnHost2)(URI_TYPE(ParserState) * state, const URI_CHAR * first) {
> +	state->uri->hostText.afterLast = first; /* HOST END */
> +
> +	/* Valid IPv4 or just a regname? */
> +	state->uri->hostData.ip4 = malloc(1 * sizeof(UriIp4)); /* Freed when stopping on parse error */
> +	if (state->uri->hostData.ip4 == NULL) {
> +		return URI_FALSE; /* Raises malloc error */
> +	}
> +	if (URI_FUNC(ParseIpFourAddress)(state->uri->hostData.ip4->data,
> +			state->uri->hostText.first, state->uri->hostText.afterLast)) {
> +		/* Not IPv4 */
> +		free(state->uri->hostData.ip4);
> +		state->uri->hostData.ip4 = NULL;
> +	}
> +	return URI_TRUE; /* Success */
> +}
> +
> +
> +
> +/*
> + * [ownHost2]->[authorityTwo] // can take <NULL>
> + * [ownHost2]->[pctSubUnres][ownHost2]
> + */
> +static const URI_CHAR * URI_FUNC(ParseOwnHost2)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		if (!URI_FUNC(OnExitOwnHost2)(state, first)) {
> +			URI_FUNC(StopMalloc)(state);
> +			return NULL;
> +		}
> +		return afterLast;
> +	}
> +
> +	switch (*first) {
> +	case _UT('!'):
> +	case _UT('$'):
> +	case _UT('%'):
> +	case _UT('&'):
> +	case _UT('('):
> +	case _UT(')'):
> +	case _UT('-'):
> +	case _UT('*'):
> +	case _UT(','):
> +	case _UT('.'):
> +	case _UT(';'):
> +	case _UT('\''):
> +	case _UT('_'):
> +	case _UT('~'):
> +	case _UT('+'):
> +	case _UT('='):
> +	case URI_SET_DIGIT:
> +	case URI_SET_ALPHA:
> +		{
> +			const URI_CHAR * const afterPctSubUnres
> +					= URI_FUNC(ParsePctSubUnres)(state, first, afterLast);
> +			if (afterPctSubUnres == NULL) {
> +				return NULL;
> +			}
> +			return URI_FUNC(ParseOwnHost2)(state, afterPctSubUnres, afterLast);
> +		}
> +
> +	default:
> +		if (!URI_FUNC(OnExitOwnHost2)(state, first)) {
> +			URI_FUNC(StopMalloc)(state);
> +			return NULL;
> +		}
> +		return URI_FUNC(ParseAuthorityTwo)(state, first, afterLast);
> +	}
> +}
> +
> +
> +
> +static URI_INLINE UriBool URI_FUNC(OnExitOwnHostUserInfo)(URI_TYPE(ParserState) * state, const URI_CHAR * first) {
> +	state->uri->hostText.first = state->uri->userInfo.first; /* Host instead of userInfo, update */
> +	state->uri->userInfo.first = NULL; /* Not a userInfo, reset */
> +	state->uri->hostText.afterLast = first; /* HOST END */
> +
> +	/* Valid IPv4 or just a regname? */
> +	state->uri->hostData.ip4 = malloc(1 * sizeof(UriIp4)); /* Freed when stopping on parse error */
> +	if (state->uri->hostData.ip4 == NULL) {
> +		return URI_FALSE; /* Raises malloc error */
> +	}
> +	if (URI_FUNC(ParseIpFourAddress)(state->uri->hostData.ip4->data,
> +			state->uri->hostText.first, state->uri->hostText.afterLast)) {
> +		/* Not IPv4 */
> +		free(state->uri->hostData.ip4);
> +		state->uri->hostData.ip4 = NULL;
> +	}
> +	return URI_TRUE; /* Success */
> +}
> +
> +
> +
> +/*
> + * [ownHostUserInfo]->[ownHostUserInfoNz]
> + * [ownHostUserInfo]-><NULL>
> + */
> +static URI_INLINE const URI_CHAR * URI_FUNC(ParseOwnHostUserInfo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		if (!URI_FUNC(OnExitOwnHostUserInfo)(state, first)) {
> +			URI_FUNC(StopMalloc)(state);
> +			return NULL;
> +		}
> +		return afterLast;
> +	}
> +
> +	switch (*first) {
> +	case _UT('!'):
> +	case _UT('$'):
> +	case _UT('%'):
> +	case _UT('&'):
> +	case _UT('('):
> +	case _UT(')'):
> +	case _UT('-'):
> +	case _UT('*'):
> +	case _UT(','):
> +	case _UT('.'):
> +	case _UT(':'):
> +	case _UT(';'):
> +	case _UT('@'):
> +	case _UT('\''):
> +	case _UT('_'):
> +	case _UT('~'):
> +	case _UT('+'):
> +	case _UT('='):
> +	case URI_SET_DIGIT:
> +	case URI_SET_ALPHA:
> +		return URI_FUNC(ParseOwnHostUserInfoNz)(state, first, afterLast);
> +
> +	default:
> +		if (!URI_FUNC(OnExitOwnHostUserInfo)(state, first)) {
> +			URI_FUNC(StopMalloc)(state);
> +			return NULL;
> +		}
> +		return first;
> +	}
> +}
> +
> +
> +
> +/*
> + * [ownHostUserInfoNz]->[pctSubUnres][ownHostUserInfo]
> + * [ownHostUserInfoNz]-><:>[ownPortUserInfo]
> + * [ownHostUserInfoNz]-><@>[ownHost]
> + */
> +static const URI_CHAR * URI_FUNC(ParseOwnHostUserInfoNz)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		URI_FUNC(StopSyntax)(state, first);
> +		return NULL;
> +	}
> +
> +	switch (*first) {
> +	case _UT('!'):
> +	case _UT('$'):
> +	case _UT('%'):
> +	case _UT('&'):
> +	case _UT('('):
> +	case _UT(')'):
> +	case _UT('-'):
> +	case _UT('*'):
> +	case _UT(','):
> +	case _UT('.'):
> +	case _UT(';'):
> +	case _UT('\''):
> +	case _UT('_'):
> +	case _UT('~'):
> +	case _UT('+'):
> +	case _UT('='):
> +	case URI_SET_DIGIT:
> +	case URI_SET_ALPHA:
> +		{
> +			const URI_CHAR * const afterPctSubUnres
> +					= URI_FUNC(ParsePctSubUnres)(state, first, afterLast);
> +			if (afterPctSubUnres == NULL) {
> +				return NULL;
> +			}
> +			return URI_FUNC(ParseOwnHostUserInfo)(state, afterPctSubUnres, afterLast);
> +		}
> +
> +	case _UT(':'):
> +		state->uri->hostText.afterLast = first; /* HOST END */
> +		state->uri->portText.first = first + 1; /* PORT BEGIN */
> +		return URI_FUNC(ParseOwnPortUserInfo)(state, first + 1, afterLast);
> +
> +	case _UT('@'):
> +		state->uri->userInfo.afterLast = first; /* USERINFO END */
> +		state->uri->hostText.first = first + 1; /* HOST BEGIN */
> +		return URI_FUNC(ParseOwnHost)(state, first + 1, afterLast);
> +
> +	default:
> +		URI_FUNC(StopSyntax)(state, first);
> +		return NULL;
> +	}
> +}
> +
> +
> +
> +static URI_INLINE UriBool URI_FUNC(OnExitOwnPortUserInfo)(URI_TYPE(ParserState) * state, const URI_CHAR * first) {
> +	state->uri->hostText.first = state->uri->userInfo.first; /* Host instead of userInfo, update */
> +	state->uri->userInfo.first = NULL; /* Not a userInfo, reset */
> +	state->uri->portText.afterLast = first; /* PORT END */
> +
> +	/* Valid IPv4 or just a regname? */
> +	state->uri->hostData.ip4 = malloc(1 * sizeof(UriIp4)); /* Freed when stopping on parse error */
> +	if (state->uri->hostData.ip4 == NULL) {
> +		return URI_FALSE; /* Raises malloc error */
> +	}
> +	if (URI_FUNC(ParseIpFourAddress)(state->uri->hostData.ip4->data,
> +			state->uri->hostText.first, state->uri->hostText.afterLast)) {
> +		/* Not IPv4 */
> +		free(state->uri->hostData.ip4);
> +		state->uri->hostData.ip4 = NULL;
> +	}
> +	return URI_TRUE; /* Success */
> +}
> +
> +
> +
> +/*
> + * [ownPortUserInfo]->[ALPHA][ownUserInfo]
> + * [ownPortUserInfo]->[DIGIT][ownPortUserInfo]
> + * [ownPortUserInfo]-><.>[ownUserInfo]
> + * [ownPortUserInfo]-><_>[ownUserInfo]
> + * [ownPortUserInfo]-><~>[ownUserInfo]
> + * [ownPortUserInfo]-><->[ownUserInfo]
> + * [ownPortUserInfo]-><@>[ownHost]
> + * [ownPortUserInfo]-><NULL>
> + */
> +static const URI_CHAR * URI_FUNC(ParseOwnPortUserInfo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		if (!URI_FUNC(OnExitOwnPortUserInfo)(state, first)) {
> +			URI_FUNC(StopMalloc)(state);
> +			return NULL;
> +		}
> +		return afterLast;
> +	}
> +
> +	switch (*first) {
> +	case _UT('.'):
> +	case _UT('_'):
> +	case _UT('~'):
> +	case _UT('-'):
> +	case URI_SET_ALPHA:
> +		state->uri->hostText.afterLast = NULL; /* Not a host, reset */
> +		state->uri->portText.first = NULL; /* Not a port, reset */
> +		return URI_FUNC(ParseOwnUserInfo)(state, first + 1, afterLast);
> +
> +	case URI_SET_DIGIT:
> +		return URI_FUNC(ParseOwnPortUserInfo)(state, first + 1, afterLast);
> +
> +	case _UT('@'):
> +		state->uri->hostText.afterLast = NULL; /* Not a host, reset */
> +		state->uri->portText.first = NULL; /* Not a port, reset */
> +		state->uri->userInfo.afterLast = first; /* USERINFO END */
> +		state->uri->hostText.first = first + 1; /* HOST BEGIN */
> +		return URI_FUNC(ParseOwnHost)(state, first + 1, afterLast);
> +
> +	default:
> +		if (!URI_FUNC(OnExitOwnPortUserInfo)(state, first)) {
> +			URI_FUNC(StopMalloc)(state);
> +			return NULL;
> +		}
> +		return first;
> +	}
> +}
> +
> +
> +
> +/*
> + * [ownUserInfo]->[pctSubUnres][ownUserInfo]
> + * [ownUserInfo]-><:>[ownUserInfo]
> + * [ownUserInfo]-><@>[ownHost]
> + */
> +static const URI_CHAR * URI_FUNC(ParseOwnUserInfo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		URI_FUNC(StopSyntax)(state, first);
> +		return NULL;
> +	}
> +
> +	switch (*first) {
> +	case _UT('!'):
> +	case _UT('$'):
> +	case _UT('%'):
> +	case _UT('&'):
> +	case _UT('('):
> +	case _UT(')'):
> +	case _UT('-'):
> +	case _UT('*'):
> +	case _UT(','):
> +	case _UT('.'):
> +	case _UT(';'):
> +	case _UT('\''):
> +	case _UT('_'):
> +	case _UT('~'):
> +	case _UT('+'):
> +	case _UT('='):
> +	case URI_SET_DIGIT:
> +	case URI_SET_ALPHA:
> +		{
> +			const URI_CHAR * const afterPctSubUnres
> +					= URI_FUNC(ParsePctSubUnres)(state, first, afterLast);
> +			if (afterPctSubUnres == NULL) {
> +				return NULL;
> +			}
> +			return URI_FUNC(ParseOwnUserInfo)(state, afterPctSubUnres, afterLast);
> +		}
> +
> +	case _UT(':'):
> +		return URI_FUNC(ParseOwnUserInfo)(state, first + 1, afterLast);
> +
> +	case _UT('@'):
> +		/* SURE */
> +		state->uri->userInfo.afterLast = first; /* USERINFO END */
> +		state->uri->hostText.first = first + 1; /* HOST BEGIN */
> +		return URI_FUNC(ParseOwnHost)(state, first + 1, afterLast);
> +
> +	default:
> +		URI_FUNC(StopSyntax)(state, first);
> +		return NULL;
> +	}
> +}
> +
> +
> +
> +static URI_INLINE void URI_FUNC(OnExitPartHelperTwo)(URI_TYPE(ParserState) * state) {
> +	state->uri->absolutePath = URI_TRUE;
> +}
> +
> +
> +
> +/*
> + * [partHelperTwo]->[pathAbsNoLeadSlash] // can take <NULL>
> + * [partHelperTwo]-></>[authority][pathAbsEmpty]
> + */
> +static URI_INLINE const URI_CHAR * URI_FUNC(ParsePartHelperTwo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		URI_FUNC(OnExitPartHelperTwo)(state);
> +		return afterLast;
> +	}
> +
> +	switch (*first) {
> +	case _UT('/'):
> +		{
> +			const URI_CHAR * const afterAuthority
> +					= URI_FUNC(ParseAuthority)(state, first + 1, afterLast);
> +			const URI_CHAR * afterPathAbsEmpty;
> +			if (afterAuthority == NULL) {
> +				return NULL;
> +			}
> +			afterPathAbsEmpty = URI_FUNC(ParsePathAbsEmpty)(state, afterAuthority, afterLast);
> +
> +			URI_FUNC(FixEmptyTrailSegment)(state->uri);
> +
> +			return afterPathAbsEmpty;
> +		}
> +
> +	default:
> +		URI_FUNC(OnExitPartHelperTwo)(state);
> +		return URI_FUNC(ParsePathAbsNoLeadSlash)(state, first, afterLast);
> +	}
> +}
> +
> +
> +
> +/*
> + * [pathAbsEmpty]-></>[segment][pathAbsEmpty]
> + * [pathAbsEmpty]-><NULL>
> + */
> +static const URI_CHAR * URI_FUNC(ParsePathAbsEmpty)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		return afterLast;
> +	}
> +
> +	switch (*first) {
> +	case _UT('/'):
> +		{
> +			const URI_CHAR * const afterSegment
> +					= URI_FUNC(ParseSegment)(state, first + 1, afterLast);
> +			if (afterSegment == NULL) {
> +				return NULL;
> +			}
> +			if (!URI_FUNC(PushPathSegment)(state, first + 1, afterSegment)) { /* SEGMENT BOTH */
> +				URI_FUNC(StopMalloc)(state);
> +				return NULL;
> +			}
> +			return URI_FUNC(ParsePathAbsEmpty)(state, afterSegment, afterLast);
> +		}
> +
> +	default:
> +		return first;
> +	}
> +}
> +
> +
> +
> +/*
> + * [pathAbsNoLeadSlash]->[segmentNz][zeroMoreSlashSegs]
> + * [pathAbsNoLeadSlash]-><NULL>
> + */
> +static URI_INLINE const URI_CHAR * URI_FUNC(ParsePathAbsNoLeadSlash)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		return afterLast;
> +	}
> +
> +	switch (*first) {
> +	case _UT('!'):
> +	case _UT('$'):
> +	case _UT('%'):
> +	case _UT('&'):
> +	case _UT('('):
> +	case _UT(')'):
> +	case _UT('-'):
> +	case _UT('*'):
> +	case _UT(','):
> +	case _UT('.'):
> +	case _UT(':'):
> +	case _UT(';'):
> +	case _UT('@'):
> +	case _UT('\''):
> +	case _UT('_'):
> +	case _UT('~'):
> +	case _UT('+'):
> +	case _UT('='):
> +	case URI_SET_DIGIT:
> +	case URI_SET_ALPHA:
> +		{
> +			const URI_CHAR * const afterSegmentNz
> +					= URI_FUNC(ParseSegmentNz)(state, first, afterLast);
> +			if (afterSegmentNz == NULL) {
> +				return NULL;
> +			}
> +			if (!URI_FUNC(PushPathSegment)(state, first, afterSegmentNz)) { /* SEGMENT BOTH */
> +				URI_FUNC(StopMalloc)(state);
> +				return NULL;
> +			}
> +			return URI_FUNC(ParseZeroMoreSlashSegs)(state, afterSegmentNz, afterLast);
> +		}
> +
> +	default:
> +		return first;
> +	}
> +}
> +
> +
> +
> +/*
> + * [pathRootless]->[segmentNz][zeroMoreSlashSegs]
> + */
> +static URI_INLINE const URI_CHAR * URI_FUNC(ParsePathRootless)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	const URI_CHAR * const afterSegmentNz
> +			= URI_FUNC(ParseSegmentNz)(state, first, afterLast);
> +	if (afterSegmentNz == NULL) {
> +		return NULL;
> +	} else {
> +		if (!URI_FUNC(PushPathSegment)(state, first, afterSegmentNz)) { /* SEGMENT BOTH */
> +			URI_FUNC(StopMalloc)(state);
> +			return NULL;
> +		}
> +	}
> +	return URI_FUNC(ParseZeroMoreSlashSegs)(state, afterSegmentNz, afterLast);
> +}
> +
> +
> +
> +/*
> + * [pchar]->[pctEncoded]
> + * [pchar]->[subDelims]
> + * [pchar]->[unreserved]
> + * [pchar]-><:>
> + * [pchar]-><@>
> + */
> +static const URI_CHAR * URI_FUNC(ParsePchar)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		URI_FUNC(StopSyntax)(state, first);
> +		return NULL;
> +	}
> +
> +	switch (*first) {
> +	case _UT('%'):
> +		return URI_FUNC(ParsePctEncoded)(state, first, afterLast);
> +
> +	case _UT(':'):
> +	case _UT('@'):
> +	case _UT('!'):
> +	case _UT('$'):
> +	case _UT('&'):
> +	case _UT('('):
> +	case _UT(')'):
> +	case _UT('*'):
> +	case _UT(','):
> +	case _UT(';'):
> +	case _UT('\''):
> +	case _UT('+'):
> +	case _UT('='):
> +	case _UT('-'):
> +	case _UT('.'):
> +	case _UT('_'):
> +	case _UT('~'):
> +	case URI_SET_DIGIT:
> +	case URI_SET_ALPHA:
> +		return first + 1;
> +
> +	default:
> +		URI_FUNC(StopSyntax)(state, first);
> +		return NULL;
> +	}
> +}
> +
> +
> +
> +/*
> + * [pctEncoded]-><%>[HEXDIG][HEXDIG]
> + */
> +static const URI_CHAR * URI_FUNC(ParsePctEncoded)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		URI_FUNC(StopSyntax)(state, first);
> +		return NULL;
> +	}
> +
> +	/*
> +	First character has already been
> +	checked before entering this rule.
> +
> +	switch (*first) {
> +	case _UT('%'):
> +	*/
> +		if (first + 1 >= afterLast) {
> +			URI_FUNC(StopSyntax)(state, first + 1);
> +			return NULL;
> +		}
> +
> +		switch (first[1]) {
> +		case URI_SET_HEXDIG:
> +			if (first + 2 >= afterLast) {
> +				URI_FUNC(StopSyntax)(state, first + 2);
> +				return NULL;
> +			}
> +
> +			switch (first[2]) {
> +			case URI_SET_HEXDIG:
> +				return first + 3;
> +
> +			default:
> +				URI_FUNC(StopSyntax)(state, first + 2);
> +				return NULL;
> +			}
> +
> +		default:
> +			URI_FUNC(StopSyntax)(state, first + 1);
> +			return NULL;
> +		}
> +
> +	/*
> +	default:
> +		URI_FUNC(StopSyntax)(state, first);
> +		return NULL;
> +	}
> +	*/
> +}
> +
> +
> +
> +/*
> + * [pctSubUnres]->[pctEncoded]
> + * [pctSubUnres]->[subDelims]
> + * [pctSubUnres]->[unreserved]
> + */
> +static const URI_CHAR * URI_FUNC(ParsePctSubUnres)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		URI_FUNC(StopSyntax)(state, first);
> +		return NULL;
> +	}
> +
> +	switch (*first) {
> +	case _UT('%'):
> +		return URI_FUNC(ParsePctEncoded)(state, first, afterLast);
> +
> +	case _UT('!'):
> +	case _UT('$'):
> +	case _UT('&'):
> +	case _UT('('):
> +	case _UT(')'):
> +	case _UT('*'):
> +	case _UT(','):
> +	case _UT(';'):
> +	case _UT('\''):
> +	case _UT('+'):
> +	case _UT('='):
> +	case _UT('-'):
> +	case _UT('.'):
> +	case _UT('_'):
> +	case _UT('~'):
> +	case URI_SET_DIGIT:
> +	case URI_SET_ALPHA:
> +		return first + 1;
> +
> +	default:
> +		URI_FUNC(StopSyntax)(state, first);
> +		return NULL;
> +	}
> +}
> +
> +
> +
> +/*
> + * [port]->[DIGIT][port]
> + * [port]-><NULL>
> + */
> +static const URI_CHAR * URI_FUNC(ParsePort)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		return afterLast;
> +	}
> +
> +	switch (*first) {
> +	case URI_SET_DIGIT:
> +		return URI_FUNC(ParsePort)(state, first + 1, afterLast);
> +
> +	default:
> +		return first;
> +	}
> +}
> +
> +
> +
> +/*
> + * [queryFrag]->[pchar][queryFrag]
> + * [queryFrag]-></>[queryFrag]
> + * [queryFrag]-><?>[queryFrag]
> + * [queryFrag]-><NULL>
> + */
> +static const URI_CHAR * URI_FUNC(ParseQueryFrag)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		return afterLast;
> +	}
> +
> +	switch (*first) {
> +	case _UT('!'):
> +	case _UT('$'):
> +	case _UT('%'):
> +	case _UT('&'):
> +	case _UT('('):
> +	case _UT(')'):
> +	case _UT('-'):
> +	case _UT('*'):
> +	case _UT(','):
> +	case _UT('.'):
> +	case _UT(':'):
> +	case _UT(';'):
> +	case _UT('@'):
> +	case _UT('\''):
> +	case _UT('_'):
> +	case _UT('~'):
> +	case _UT('+'):
> +	case _UT('='):
> +	case URI_SET_DIGIT:
> +	case URI_SET_ALPHA:
> +		{
> +			const URI_CHAR * const afterPchar
> +					= URI_FUNC(ParsePchar)(state, first, afterLast);
> +			if (afterPchar == NULL) {
> +				return NULL;
> +			}
> +			return URI_FUNC(ParseQueryFrag)(state, afterPchar, afterLast);
> +		}
> +
> +	case _UT('/'):
> +	case _UT('?'):
> +		return URI_FUNC(ParseQueryFrag)(state, first + 1, afterLast);
> +
> +	default:
> +		return first;
> +	}
> +}
> +
> +
> +
> +/*
> + * [segment]->[pchar][segment]
> + * [segment]-><NULL>
> + */
> +static const URI_CHAR * URI_FUNC(ParseSegment)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		return afterLast;
> +	}
> +
> +	switch (*first) {
> +	case _UT('!'):
> +	case _UT('$'):
> +	case _UT('%'):
> +	case _UT('&'):
> +	case _UT('('):
> +	case _UT(')'):
> +	case _UT('-'):
> +	case _UT('*'):
> +	case _UT(','):
> +	case _UT('.'):
> +	case _UT(':'):
> +	case _UT(';'):
> +	case _UT('@'):
> +	case _UT('\''):
> +	case _UT('_'):
> +	case _UT('~'):
> +	case _UT('+'):
> +	case _UT('='):
> +	case URI_SET_DIGIT:
> +	case URI_SET_ALPHA:
> +		{
> +			const URI_CHAR * const afterPchar
> +					= URI_FUNC(ParsePchar)(state, first, afterLast);
> +			if (afterPchar == NULL) {
> +				return NULL;
> +			}
> +			return URI_FUNC(ParseSegment)(state, afterPchar, afterLast);
> +		}
> +
> +	default:
> +		return first;
> +	}
> +}
> +
> +
> +
> +/*
> + * [segmentNz]->[pchar][segment]
> + */
> +static URI_INLINE const URI_CHAR * URI_FUNC(ParseSegmentNz)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	const URI_CHAR * const afterPchar
> +			= URI_FUNC(ParsePchar)(state, first, afterLast);
> +	if (afterPchar == NULL) {
> +		return NULL;
> +	}
> +	return URI_FUNC(ParseSegment)(state, afterPchar, afterLast);
> +}
> +
> +
> +
> +static URI_INLINE UriBool URI_FUNC(OnExitSegmentNzNcOrScheme2)(URI_TYPE(ParserState) * state, const URI_CHAR * first) {
> +	if (!URI_FUNC(PushPathSegment)(state, state->uri->scheme.first, first)) { /* SEGMENT BOTH */
> +		return URI_FALSE; /* Raises malloc error*/
> +	}
> +	state->uri->scheme.first = NULL; /* Not a scheme, reset */
> +	return URI_TRUE; /* Success */
> +}
> +
> +
> +
> +/*
> + * [segmentNzNcOrScheme2]->[ALPHA][segmentNzNcOrScheme2]
> + * [segmentNzNcOrScheme2]->[DIGIT][segmentNzNcOrScheme2]
> + * [segmentNzNcOrScheme2]->[pctEncoded][mustBeSegmentNzNc]
> + * [segmentNzNcOrScheme2]->[uriTail] // can take <NULL>
> + * [segmentNzNcOrScheme2]-><!>[mustBeSegmentNzNc]
> + * [segmentNzNcOrScheme2]-><$>[mustBeSegmentNzNc]
> + * [segmentNzNcOrScheme2]-><&>[mustBeSegmentNzNc]
> + * [segmentNzNcOrScheme2]-><(>[mustBeSegmentNzNc]
> + * [segmentNzNcOrScheme2]-><)>[mustBeSegmentNzNc]
> + * [segmentNzNcOrScheme2]-><*>[mustBeSegmentNzNc]
> + * [segmentNzNcOrScheme2]-><,>[mustBeSegmentNzNc]
> + * [segmentNzNcOrScheme2]-><.>[segmentNzNcOrScheme2]
> + * [segmentNzNcOrScheme2]-></>[segment][zeroMoreSlashSegs][uriTail]
> + * [segmentNzNcOrScheme2]-><:>[hierPart][uriTail]
> + * [segmentNzNcOrScheme2]-><;>[mustBeSegmentNzNc]
> + * [segmentNzNcOrScheme2]-><@>[mustBeSegmentNzNc]
> + * [segmentNzNcOrScheme2]-><_>[mustBeSegmentNzNc]
> + * [segmentNzNcOrScheme2]-><~>[mustBeSegmentNzNc]
> + * [segmentNzNcOrScheme2]-><+>[segmentNzNcOrScheme2]
> + * [segmentNzNcOrScheme2]-><=>[mustBeSegmentNzNc]
> + * [segmentNzNcOrScheme2]-><'>[mustBeSegmentNzNc]
> + * [segmentNzNcOrScheme2]-><->[segmentNzNcOrScheme2]
> + */
> +static const URI_CHAR * URI_FUNC(ParseSegmentNzNcOrScheme2)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		if (!URI_FUNC(OnExitSegmentNzNcOrScheme2)(state, first)) {
> +			URI_FUNC(StopMalloc)(state);
> +			return NULL;
> +		}
> +		return afterLast;
> +	}
> +
> +	switch (*first) {
> +	case _UT('.'):
> +	case _UT('+'):
> +	case _UT('-'):
> +	case URI_SET_ALPHA:
> +	case URI_SET_DIGIT:
> +		return URI_FUNC(ParseSegmentNzNcOrScheme2)(state, first + 1, afterLast);
> +
> +	case _UT('%'):
> +		{
> +			const URI_CHAR * const afterPctEncoded
> +					= URI_FUNC(ParsePctEncoded)(state, first, afterLast);
> +			if (afterPctEncoded == NULL) {
> +				return NULL;
> +			}
> +			return URI_FUNC(ParseMustBeSegmentNzNc)(state, afterPctEncoded, afterLast);
> +		}
> +
> +	case _UT('!'):
> +	case _UT('$'):
> +	case _UT('&'):
> +	case _UT('('):
> +	case _UT(')'):
> +	case _UT('*'):
> +	case _UT(','):
> +	case _UT(';'):
> +	case _UT('@'):
> +	case _UT('_'):
> +	case _UT('~'):
> +	case _UT('='):
> +	case _UT('\''):
> +		return URI_FUNC(ParseMustBeSegmentNzNc)(state, first + 1, afterLast);
> +
> +	case _UT('/'):
> +		{
> +			const URI_CHAR * afterZeroMoreSlashSegs;
> +			const URI_CHAR * const afterSegment
> +					= URI_FUNC(ParseSegment)(state, first + 1, afterLast);
> +			if (afterSegment == NULL) {
> +				return NULL;
> +			}
> +			if (!URI_FUNC(PushPathSegment)(state, state->uri->scheme.first, first)) { /* SEGMENT BOTH */
> +				URI_FUNC(StopMalloc)(state);
> +				return NULL;
> +			}
> +			state->uri->scheme.first = NULL; /* Not a scheme, reset */
> +			if (!URI_FUNC(PushPathSegment)(state, first + 1, afterSegment)) { /* SEGMENT BOTH */
> +				URI_FUNC(StopMalloc)(state);
> +				return NULL;
> +			}
> +			afterZeroMoreSlashSegs
> +					= URI_FUNC(ParseZeroMoreSlashSegs)(state, afterSegment, afterLast);
> +			if (afterZeroMoreSlashSegs == NULL) {
> +				return NULL;
> +			}
> +			return URI_FUNC(ParseUriTail)(state, afterZeroMoreSlashSegs, afterLast);
> +		}
> +
> +	case _UT(':'):
> +		{
> +			const URI_CHAR * const afterHierPart
> +					= URI_FUNC(ParseHierPart)(state, first + 1, afterLast);
> +			state->uri->scheme.afterLast = first; /* SCHEME END */
> +			if (afterHierPart == NULL) {
> +				return NULL;
> +			}
> +			return URI_FUNC(ParseUriTail)(state, afterHierPart, afterLast);
> +		}
> +
> +	default:
> +		if (!URI_FUNC(OnExitSegmentNzNcOrScheme2)(state, first)) {
> +			URI_FUNC(StopMalloc)(state);
> +			return NULL;
> +		}
> +		return URI_FUNC(ParseUriTail)(state, first, afterLast);
> +	}
> +}
> +
> +
> +
> +/*
> + * [uriReference]->[ALPHA][segmentNzNcOrScheme2]
> + * [uriReference]->[DIGIT][mustBeSegmentNzNc]
> + * [uriReference]->[pctEncoded][mustBeSegmentNzNc]
> + * [uriReference]->[subDelims][mustBeSegmentNzNc]
> + * [uriReference]->[uriTail] // can take <NULL>
> + * [uriReference]-><.>[mustBeSegmentNzNc]
> + * [uriReference]-></>[partHelperTwo][uriTail]
> + * [uriReference]-><@>[mustBeSegmentNzNc]
> + * [uriReference]-><_>[mustBeSegmentNzNc]
> + * [uriReference]-><~>[mustBeSegmentNzNc]
> + * [uriReference]-><->[mustBeSegmentNzNc]
> + */
> +static const URI_CHAR * URI_FUNC(ParseUriReference)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		return afterLast;
> +	}
> +
> +	switch (*first) {
> +	case URI_SET_ALPHA:
> +		state->uri->scheme.first = first; /* SCHEME BEGIN */
> +		return URI_FUNC(ParseSegmentNzNcOrScheme2)(state, first + 1, afterLast);
> +
> +	case URI_SET_DIGIT:
> +	case _UT('!'):
> +	case _UT('$'):
> +	case _UT('&'):
> +	case _UT('('):
> +	case _UT(')'):
> +	case _UT('*'):
> +	case _UT(','):
> +	case _UT(';'):
> +	case _UT('\''):
> +	case _UT('+'):
> +	case _UT('='):
> +	case _UT('.'):
> +	case _UT('_'):
> +	case _UT('~'):
> +	case _UT('-'):
> +	case _UT('@'):
> +		state->uri->scheme.first = first; /* SEGMENT BEGIN, ABUSE SCHEME POINTER */
> +		return URI_FUNC(ParseMustBeSegmentNzNc)(state, first + 1, afterLast);
> +
> +	case _UT('%'):
> +		{
> +			const URI_CHAR * const afterPctEncoded
> +					= URI_FUNC(ParsePctEncoded)(state, first, afterLast);
> +			if (afterPctEncoded == NULL) {
> +				return NULL;
> +			}
> +			state->uri->scheme.first = first; /* SEGMENT BEGIN, ABUSE SCHEME POINTER */
> +			return URI_FUNC(ParseMustBeSegmentNzNc)(state, afterPctEncoded, afterLast);
> +		}
> +
> +	case _UT('/'):
> +		{
> +			const URI_CHAR * const afterPartHelperTwo
> +					= URI_FUNC(ParsePartHelperTwo)(state, first + 1, afterLast);
> +			if (afterPartHelperTwo == NULL) {
> +				return NULL;
> +			}
> +			return URI_FUNC(ParseUriTail)(state, afterPartHelperTwo, afterLast);
> +		}
> +
> +	default:
> +		return URI_FUNC(ParseUriTail)(state, first, afterLast);
> +	}
> +}
> +
> +
> +
> +/*
> + * [uriTail]-><#>[queryFrag]
> + * [uriTail]-><?>[queryFrag][uriTailTwo]
> + * [uriTail]-><NULL>
> + */
> +static URI_INLINE const URI_CHAR * URI_FUNC(ParseUriTail)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		return afterLast;
> +	}
> +
> +	switch (*first) {
> +	case _UT('#'):
> +		{
> +			const URI_CHAR * const afterQueryFrag = URI_FUNC(ParseQueryFrag)(state, first + 1, afterLast);
> +			if (afterQueryFrag == NULL) {
> +				return NULL;
> +			}
> +			state->uri->fragment.first = first + 1; /* FRAGMENT BEGIN */
> +			state->uri->fragment.afterLast = afterQueryFrag; /* FRAGMENT END */
> +			return afterQueryFrag;
> +		}
> +
> +	case _UT('?'):
> +		{
> +			const URI_CHAR * const afterQueryFrag
> +					= URI_FUNC(ParseQueryFrag)(state, first + 1, afterLast);
> +			if (afterQueryFrag == NULL) {
> +				return NULL;
> +			}
> +			state->uri->query.first = first + 1; /* QUERY BEGIN */
> +			state->uri->query.afterLast = afterQueryFrag; /* QUERY END */
> +			return URI_FUNC(ParseUriTailTwo)(state, afterQueryFrag, afterLast);
> +		}
> +
> +	default:
> +		return first;
> +	}
> +}
> +
> +
> +
> +/*
> + * [uriTailTwo]-><#>[queryFrag]
> + * [uriTailTwo]-><NULL>
> + */
> +static URI_INLINE const URI_CHAR * URI_FUNC(ParseUriTailTwo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		return afterLast;
> +	}
> +
> +	switch (*first) {
> +	case _UT('#'):
> +		{
> +			const URI_CHAR * const afterQueryFrag = URI_FUNC(ParseQueryFrag)(state, first + 1, afterLast);
> +			if (afterQueryFrag == NULL) {
> +				return NULL;
> +			}
> +			state->uri->fragment.first = first + 1; /* FRAGMENT BEGIN */
> +			state->uri->fragment.afterLast = afterQueryFrag; /* FRAGMENT END */
> +			return afterQueryFrag;
> +		}
> +
> +	default:
> +		return first;
> +	}
> +}
> +
> +
> +
> +/*
> + * [zeroMoreSlashSegs]-></>[segment][zeroMoreSlashSegs]
> + * [zeroMoreSlashSegs]-><NULL>
> + */
> +static const URI_CHAR * URI_FUNC(ParseZeroMoreSlashSegs)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	if (first >= afterLast) {
> +		return afterLast;
> +	}
> +
> +	switch (*first) {
> +	case _UT('/'):
> +		{
> +			const URI_CHAR * const afterSegment
> +					= URI_FUNC(ParseSegment)(state, first + 1, afterLast);
> +			if (afterSegment == NULL) {
> +				return NULL;
> +			}
> +			if (!URI_FUNC(PushPathSegment)(state, first + 1, afterSegment)) { /* SEGMENT BOTH */
> +				URI_FUNC(StopMalloc)(state);
> +				return NULL;
> +			}
> +			return URI_FUNC(ParseZeroMoreSlashSegs)(state, afterSegment, afterLast);
> +		}
> +
> +	default:
> +		return first;
> +	}
> +}
> +
> +
> +
> +static URI_INLINE void URI_FUNC(ResetParserState)(URI_TYPE(ParserState) * state) {
> +	URI_TYPE(Uri) * const uriBackup = state->uri;
> +	memset(state, 0, sizeof(URI_TYPE(ParserState)));
> +	state->uri = uriBackup;
> +}
> +
> +
> +
> +static URI_INLINE UriBool URI_FUNC(PushPathSegment)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	URI_TYPE(PathSegment) * segment = malloc(1 * sizeof(URI_TYPE(PathSegment)));
> +	if (segment == NULL) {
> +		return URI_FALSE; /* Raises malloc error */
> +	}
> +	memset(segment, 0, sizeof(URI_TYPE(PathSegment)));
> +	if (first == afterLast) {
> +		segment->text.first = URI_FUNC(SafeToPointTo);
> +		segment->text.afterLast = URI_FUNC(SafeToPointTo);
> +	} else {
> +		segment->text.first = first;
> +		segment->text.afterLast = afterLast;
> +	}
> +
> +	/* First segment ever? */
> +	if (state->uri->pathHead == NULL) {
> +		/* First segement ever, set head and tail */
> +		state->uri->pathHead = segment;
> +		state->uri->pathTail = segment;
> +	} else {
> +		/* Append, update tail */
> +		state->uri->pathTail->next = segment;
> +		state->uri->pathTail = segment;
> +	}
> +
> +	return URI_TRUE; /* Success */
> +}
> +
> +
> +
> +int URI_FUNC(ParseUriEx)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	const URI_CHAR * afterUriReference;
> +	URI_TYPE(Uri) * uri;
> +
> +	/* Check params */
> +	if ((state == NULL) || (first == NULL) || (afterLast == NULL)) {
> +		return URI_ERROR_NULL;
> +	}
> +	uri = state->uri;
> +
> +	/* Init parser */
> +	URI_FUNC(ResetParserState)(state);
> +	URI_FUNC(ResetUri)(uri);
> +
> +	/* Parse */
> +	afterUriReference = URI_FUNC(ParseUriReference)(state, first, afterLast);
> +	if (afterUriReference == NULL) {
> +		return state->errorCode;
> +	}
> +	if (afterUriReference != afterLast) {
> +		return URI_ERROR_SYNTAX;
> +	}
> +	return URI_SUCCESS;
> +}
> +
> +
> +
> +int URI_FUNC(ParseUri)(URI_TYPE(ParserState) * state, const URI_CHAR * text) {
> +	if ((state == NULL) || (text == NULL)) {
> +		return URI_ERROR_NULL;
> +	}
> +	return URI_FUNC(ParseUriEx)(state, text, text + URI_STRLEN(text));
> +}
> +
> +
> +
> +void URI_FUNC(FreeUriMembers)(URI_TYPE(Uri) * uri) {
> +	if (uri == NULL) {
> +		return;
> +	}
> +
> +	if (uri->owner) {
> +		/* Scheme */
> +		if (uri->scheme.first != NULL) {
> +			if (uri->scheme.first != uri->scheme.afterLast) {
> +				free((URI_CHAR *)uri->scheme.first);
> +			}
> +			uri->scheme.first = NULL;
> +			uri->scheme.afterLast = NULL;
> +		}
> +
> +		/* User info */
> +		if (uri->userInfo.first != NULL) {
> +			if (uri->userInfo.first != uri->userInfo.afterLast) {
> +				free((URI_CHAR *)uri->userInfo.first);
> +			}
> +			uri->userInfo.first = NULL;
> +			uri->userInfo.afterLast = NULL;
> +		}
> +
> +		/* Host data - IPvFuture */
> +		if (uri->hostData.ipFuture.first != NULL) {
> +			if (uri->hostData.ipFuture.first != uri->hostData.ipFuture.afterLast) {
> +				free((URI_CHAR *)uri->hostData.ipFuture.first);
> +			}
> +			uri->hostData.ipFuture.first = NULL;
> +			uri->hostData.ipFuture.afterLast = NULL;
> +			uri->hostText.first = NULL;
> +			uri->hostText.afterLast = NULL;
> +		}
> +
> +		/* Host text (if regname, after IPvFuture!) */
> +		if ((uri->hostText.first != NULL)
> +				&& (uri->hostData.ip4 == NULL)
> +				&& (uri->hostData.ip6 == NULL)) {
> +			/* Real regname */
> +			if (uri->hostText.first != uri->hostText.afterLast) {
> +				free((URI_CHAR *)uri->hostText.first);
> +			}
> +			uri->hostText.first = NULL;
> +			uri->hostText.afterLast = NULL;
> +		}
> +	}
> +
> +	/* Host data - IPv4 */
> +	if (uri->hostData.ip4 != NULL) {
> +		free(uri->hostData.ip4);
> +		uri->hostData.ip4 = NULL;
> +	}
> +
> +	/* Host data - IPv6 */
> +	if (uri->hostData.ip6 != NULL) {
> +		free(uri->hostData.ip6);
> +		uri->hostData.ip6 = NULL;
> +	}
> +
> +	/* Port text */
> +	if (uri->owner && (uri->portText.first != NULL)) {
> +		if (uri->portText.first != uri->portText.afterLast) {
> +			free((URI_CHAR *)uri->portText.first);
> +		}
> +		uri->portText.first = NULL;
> +		uri->portText.afterLast = NULL;
> +	}
> +
> +	/* Path */
> +	if (uri->pathHead != NULL) {
> +		URI_TYPE(PathSegment) * segWalk = uri->pathHead;
> +		while (segWalk != NULL) {
> +			URI_TYPE(PathSegment) * const next = segWalk->next;
> +			if (uri->owner && (segWalk->text.first != NULL)
> +					&& (segWalk->text.first < segWalk->text.afterLast)) {
> +				free((URI_CHAR *)segWalk->text.first);
> +			}
> +			free(segWalk);
> +			segWalk = next;
> +		}
> +		uri->pathHead = NULL;
> +		uri->pathTail = NULL;
> +	}
> +
> +	if (uri->owner) {
> +		/* Query */
> +		if (uri->query.first != NULL) {
> +			if (uri->query.first != uri->query.afterLast) {
> +				free((URI_CHAR *)uri->query.first);
> +			}
> +			uri->query.first = NULL;
> +			uri->query.afterLast = NULL;
> +		}
> +
> +		/* Fragment */
> +		if (uri->fragment.first != NULL) {
> +			if (uri->fragment.first != uri->fragment.afterLast) {
> +				free((URI_CHAR *)uri->fragment.first);
> +			}
> +			uri->fragment.first = NULL;
> +			uri->fragment.afterLast = NULL;
> +		}
> +	}
> +}
> +
> +
> +
> +UriBool URI_FUNC(_TESTING_ONLY_ParseIpSix)(const URI_CHAR * text) {
> +	URI_TYPE(Uri) uri;
> +	URI_TYPE(ParserState) parser;
> +	const URI_CHAR * const afterIpSix = text + URI_STRLEN(text);
> +	const URI_CHAR * res;
> +
> +	URI_FUNC(ResetParserState)(&parser);
> +	URI_FUNC(ResetUri)(&uri);
> +	parser.uri = &uri;
> +	parser.uri->hostData.ip6 = malloc(1 * sizeof(UriIp6));
> +	res = URI_FUNC(ParseIPv6address2)(&parser, text, afterIpSix);
> +	URI_FUNC(FreeUriMembers)(&uri);
> +	return res == afterIpSix ? URI_TRUE : URI_FALSE;
> +}
> +
> +
> +
> +UriBool URI_FUNC(_TESTING_ONLY_ParseIpFour)(const URI_CHAR * text) {
> +	unsigned char octets[4];
> +	int res = URI_FUNC(ParseIpFourAddress)(octets, text, text + URI_STRLEN(text));
> +	return (res == URI_SUCCESS) ? URI_TRUE : URI_FALSE;
> +}
> +
> +
> +
> +#undef URI_SET_DIGIT
> +#undef URI_SET_HEX_LETTER_UPPER
> +#undef URI_SET_HEX_LETTER_LOWER
> +#undef URI_SET_HEXDIG
> +#undef URI_SET_ALPHA
> +
> +
> +
> +#endif
> diff --git a/uriparser/lib/UriParseBase.c b/uriparser/lib/UriParseBase.c
> new file mode 100644
> --- /dev/null
> +++ b/uriparser/lib/UriParseBase.c
> @@ -0,0 +1,90 @@
> +/*
> + * uriparser - RFC 3986 URI parsing library
> + *
> + * Copyright (C) 2007, Weijia Song <songweijia at gmail.com>
> + * Copyright (C) 2007, Sebastian Pipping <webmaster at hartwork.org>
> + * All rights reserved.
> + *
> + * Redistribution  and use in source and binary forms, with or without
> + * modification,  are permitted provided that the following conditions
> + * are met:
> + *
> + *     * Redistributions   of  source  code  must  retain  the   above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer.
> + *
> + *     * Redistributions  in  binary  form must  reproduce  the  above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer   in  the  documentation  and/or  other  materials
> + *       provided with the distribution.
> + *
> + *     * Neither  the name of the <ORGANIZATION> nor the names of  its
> + *       contributors  may  be  used to endorse  or  promote  products
> + *       derived  from  this software without specific  prior  written
> + *       permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT  NOT
> + * LIMITED  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS
> + * FOR  A  PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT  SHALL  THE
> + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL,    SPECIAL,   EXEMPLARY,   OR   CONSEQUENTIAL   DAMAGES
> + * (INCLUDING,  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> + * SERVICES;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
> + * STRICT  LIABILITY,  OR  TORT (INCLUDING  NEGLIGENCE  OR  OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
> + * OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +#ifndef URI_DOXYGEN
> +# include "UriParseBase.h"
> +#endif
> +
> +
> +
> +void uriWriteQuadToDoubleByte(const unsigned char * hexDigits, int digitCount, unsigned char * output) {
> +	switch (digitCount) {
> +	case 1:
> +		/* 0x___? -> \x00 \x0? */
> +		output[0] = 0;
> +		output[1] = hexDigits[0];
> +		break;
> +
> +	case 2:
> +		/* 0x__?? -> \0xx \x?? */
> +		output[0] = 0;
> +		output[1] = 16 * hexDigits[0] + hexDigits[1];
> +		break;
> +
> +	case 3:
> +		/* 0x_??? -> \0x? \x?? */
> +		output[0] = hexDigits[0];
> +		output[1] = 16 * hexDigits[1] + hexDigits[2];
> +		break;
> +
> +	case 4:
> +		/* 0x???? -> \0?? \x?? */
> +		output[0] = 16 * hexDigits[0] + hexDigits[1];
> +		output[1] = 16 * hexDigits[2] + hexDigits[3];
> +		break;
> +
> +	}
> +}
> +
> +
> +
> +unsigned char uriGetOctetValue(const unsigned char * digits, int digitCount) {
> +	switch (digitCount) {
> +	case 1:
> +		return digits[0];
> +
> +	case 2:
> +		return 10 * digits[0] + digits[1];
> +
> +	case 3:
> +	default:
> +		return 100 * digits[0] + 10 * digits[1] + digits[2];
> +
> +	}
> +}
> diff --git a/uriparser/lib/UriParseBase.h b/uriparser/lib/UriParseBase.h
> new file mode 100644
> --- /dev/null
> +++ b/uriparser/lib/UriParseBase.h
> @@ -0,0 +1,55 @@
> +/*
> + * uriparser - RFC 3986 URI parsing library
> + *
> + * Copyright (C) 2007, Weijia Song <songweijia at gmail.com>
> + * Copyright (C) 2007, Sebastian Pipping <webmaster at hartwork.org>
> + * All rights reserved.
> + *
> + * Redistribution  and use in source and binary forms, with or without
> + * modification,  are permitted provided that the following conditions
> + * are met:
> + *
> + *     * Redistributions   of  source  code  must  retain  the   above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer.
> + *
> + *     * Redistributions  in  binary  form must  reproduce  the  above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer   in  the  documentation  and/or  other  materials
> + *       provided with the distribution.
> + *
> + *     * Neither  the name of the <ORGANIZATION> nor the names of  its
> + *       contributors  may  be  used to endorse  or  promote  products
> + *       derived  from  this software without specific  prior  written
> + *       permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT  NOT
> + * LIMITED  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS
> + * FOR  A  PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT  SHALL  THE
> + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL,    SPECIAL,   EXEMPLARY,   OR   CONSEQUENTIAL   DAMAGES
> + * (INCLUDING,  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> + * SERVICES;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
> + * STRICT  LIABILITY,  OR  TORT (INCLUDING  NEGLIGENCE  OR  OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
> + * OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +#ifndef URI_PARSE_BASE_H
> +#define URI_PARSE_BASE_H 1
> +
> +
> +
> +#include <uriparser/UriBase.h>
> +
> +
> +
> +void uriWriteQuadToDoubleByte(const unsigned char * hexDigits, int digitCount,
> +		unsigned char * output);
> +unsigned char uriGetOctetValue(const unsigned char * digits, int digitCount);
> +
> +
> +
> +#endif /* URI_PARSE_BASE_H */
> diff --git a/uriparser/lib/UriQuery.c b/uriparser/lib/UriQuery.c
> new file mode 100644
> --- /dev/null
> +++ b/uriparser/lib/UriQuery.c
> @@ -0,0 +1,456 @@
> +/*
> + * uriparser - RFC 3986 URI parsing library
> + *
> + * Copyright (C) 2007, Weijia Song <songweijia at gmail.com>
> + * Copyright (C) 2007, Sebastian Pipping <webmaster at hartwork.org>
> + * All rights reserved.
> + *
> + * Redistribution  and use in source and binary forms, with or without
> + * modification,  are permitted provided that the following conditions
> + * are met:
> + *
> + *     * Redistributions   of  source  code  must  retain  the   above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer.
> + *
> + *     * Redistributions  in  binary  form must  reproduce  the  above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer   in  the  documentation  and/or  other  materials
> + *       provided with the distribution.
> + *
> + *     * Neither  the name of the <ORGANIZATION> nor the names of  its
> + *       contributors  may  be  used to endorse  or  promote  products
> + *       derived  from  this software without specific  prior  written
> + *       permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT  NOT
> + * LIMITED  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS
> + * FOR  A  PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT  SHALL  THE
> + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL,    SPECIAL,   EXEMPLARY,   OR   CONSEQUENTIAL   DAMAGES
> + * (INCLUDING,  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> + * SERVICES;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
> + * STRICT  LIABILITY,  OR  TORT (INCLUDING  NEGLIGENCE  OR  OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
> + * OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +/* What encodings are enabled? */
> +#include <uriparser/UriDefsConfig.h>
> +#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
> +/* Include SELF twice */
> +# define URI_PASS_ANSI 1
> +# include "UriQuery.c"
> +# undef URI_PASS_ANSI
> +# define URI_PASS_UNICODE 1
> +# include "UriQuery.c"
> +# undef URI_PASS_UNICODE
> +#else
> +# ifdef URI_PASS_ANSI
> +#  include <uriparser/UriDefsAnsi.h>
> +# else
> +#  include <uriparser/UriDefsUnicode.h>
> +#  include <wchar.h>
> +# endif
> +
> +
> +
> +#ifndef URI_DOXYGEN
> +# include <uriparser/Uri.h>
> +# include "UriCommon.h"
> +#endif
> +
> +
> +
> +static int URI_FUNC(ComposeQueryEngine)(URI_CHAR * dest,
> +		const URI_TYPE(QueryList) * queryList,
> +		int maxChars, int * charsWritten, int * charsRequired,
> +		UriBool spaceToPlus, UriBool normalizeBreaks);
> +
> +static UriBool URI_FUNC(AppendQueryItem)(URI_TYPE(QueryList) ** prevNext,
> +		int * itemCount, const URI_CHAR * keyFirst, const URI_CHAR * keyAfter,
> +		const URI_CHAR * valueFirst, const URI_CHAR * valueAfter,
> +		UriBool plusToSpace, UriBreakConversion breakConversion);
> +
> +
> +
> +int URI_FUNC(ComposeQueryCharsRequired)(const URI_TYPE(QueryList) * queryList,
> +		int * charsRequired) {
> +	const UriBool spaceToPlus = URI_TRUE;
> +	const UriBool normalizeBreaks = URI_TRUE;
> +
> +	return URI_FUNC(ComposeQueryCharsRequiredEx)(queryList, charsRequired,
> +			spaceToPlus, normalizeBreaks);
> +}
> +
> +
> +
> +int URI_FUNC(ComposeQueryCharsRequiredEx)(const URI_TYPE(QueryList) * queryList,
> +		int * charsRequired, UriBool spaceToPlus, UriBool normalizeBreaks) {
> +	if ((queryList == NULL) || (charsRequired == NULL)) {
> +		return URI_ERROR_NULL;
> +	}
> +
> +	return URI_FUNC(ComposeQueryEngine)(NULL, queryList, 0, NULL,
> +			charsRequired, spaceToPlus, normalizeBreaks);
> +}
> +
> +
> +
> +int URI_FUNC(ComposeQuery)(URI_CHAR * dest,
> +						   const URI_TYPE(QueryList) * queryList, int maxChars, int * charsWritten) {
> +	const UriBool spaceToPlus = URI_TRUE;
> +	const UriBool normalizeBreaks = URI_TRUE;
> +
> +	return URI_FUNC(ComposeQueryEx)(dest, queryList, maxChars, charsWritten,
> +			spaceToPlus, normalizeBreaks);
> +}
> +
> +
> +
> +int URI_FUNC(ComposeQueryEx)(URI_CHAR * dest,
> +		const URI_TYPE(QueryList) * queryList, int maxChars, int * charsWritten,
> +		UriBool spaceToPlus, UriBool normalizeBreaks) {
> +	if ((dest == NULL) || (queryList == NULL)) {
> +		return URI_ERROR_NULL;
> +	}
> +
> +	if (maxChars < 1) {
> +		return URI_ERROR_OUTPUT_TOO_LARGE;
> +	}
> +
> +	return URI_FUNC(ComposeQueryEngine)(dest, queryList, maxChars,
> +			charsWritten, NULL, spaceToPlus, normalizeBreaks);
> +}
> +
> +
> +
> +int URI_FUNC(ComposeQueryMalloc)(URI_CHAR ** dest,
> +		const URI_TYPE(QueryList) * queryList) {
> +	const UriBool spaceToPlus = URI_TRUE;
> +	const UriBool normalizeBreaks = URI_TRUE;
> +
> +	return URI_FUNC(ComposeQueryMallocEx)(dest, queryList,
> +			spaceToPlus, normalizeBreaks);
> +}
> +
> +
> +
> +int URI_FUNC(ComposeQueryMallocEx)(URI_CHAR ** dest,
> +		const URI_TYPE(QueryList) * queryList,
> +		UriBool spaceToPlus, UriBool normalizeBreaks) {
> +	int charsRequired;
> +	int res;
> +	URI_CHAR * queryString;
> +
> +	if (dest == NULL) {
> +		return URI_ERROR_NULL;
> +	}
> +
> +	/* Calculate space */
> +	res = URI_FUNC(ComposeQueryCharsRequiredEx)(queryList, &charsRequired,
> +			spaceToPlus, normalizeBreaks);
> +	if (res != URI_SUCCESS) {
> +		return res;
> +	}
> +	charsRequired++;
> +
> +	/* Allocate space */
> +	queryString = malloc(charsRequired * sizeof(URI_CHAR));
> +	if (queryString == NULL) {
> +		return URI_ERROR_MALLOC;
> +	}
> +
> +	/* Put query in */
> +	res = URI_FUNC(ComposeQueryEx)(queryString, queryList, charsRequired,
> +			NULL, spaceToPlus, normalizeBreaks);
> +	if (res != URI_SUCCESS) {
> +		free(queryString);
> +		return res;
> +	}
> +
> +	*dest = queryString;
> +	return URI_SUCCESS;
> +}
> +
> +
> +
> +int URI_FUNC(ComposeQueryEngine)(URI_CHAR * dest,
> +		const URI_TYPE(QueryList) * queryList,
> +		int maxChars, int * charsWritten, int * charsRequired,
> +		UriBool spaceToPlus, UriBool normalizeBreaks) {
> +	UriBool firstItem = URI_TRUE;
> +	int ampersandLen = 0;
> +	URI_CHAR * write = dest;
> +
> +	/* Subtract terminator */
> +	if (dest == NULL) {
> +		*charsRequired = 0;
> +	} else {
> +		maxChars--;
> +	}
> +
> +	while (queryList != NULL) {
> +		const URI_CHAR * const key = queryList->key;
> +		const URI_CHAR * const value = queryList->value;
> +		const int worstCase = (normalizeBreaks == URI_TRUE ? 6 : 3);
> +		const int keyLen = (key == NULL) ? 0 : (int)URI_STRLEN(key);
> +		const int keyRequiredChars = worstCase * keyLen;
> +		const int valueLen = (value == NULL) ? 0 : (int)URI_STRLEN(value);
> +		const int valueRequiredChars = worstCase * valueLen;
> +
> +		if (dest == NULL) {
> +			if (firstItem == URI_TRUE) {
> +				ampersandLen = 1;
> +				firstItem = URI_FALSE;
> +			}
> +
> +			(*charsRequired) += ampersandLen + keyRequiredChars + ((value == NULL)
> +						? 0
> +						: 1 + valueRequiredChars);
> +		} else {
> +			URI_CHAR * afterKey;
> +
> +			if ((write - dest) + ampersandLen + keyRequiredChars > maxChars) {
> +				return URI_ERROR_OUTPUT_TOO_LARGE;
> +			}
> +
> +			/* Copy key */
> +			if (firstItem == URI_TRUE) {
> +				firstItem = URI_FALSE;
> +			} else {
> +				write[0] = _UT('&');
> +				write++;
> +			}
> +			afterKey = URI_FUNC(EscapeEx)(key, key + keyLen,
> +					write, spaceToPlus, normalizeBreaks);
> +			write += (afterKey - write);
> +
> +			if (value != NULL) {
> +				URI_CHAR * afterValue;
> +
> +				if ((write - dest) + 1 + valueRequiredChars > maxChars) {
> +					return URI_ERROR_OUTPUT_TOO_LARGE;
> +				}
> +
> +				/* Copy value */
> +				write[0] = _UT('=');
> +				write++;
> +				afterValue = URI_FUNC(EscapeEx)(value, value + valueLen,
> +						write, spaceToPlus, normalizeBreaks);
> +				write += (afterValue - write);
> +			}
> +		}
> +
> +		queryList = queryList->next;
> +	}
> +
> +	if (dest != NULL) {
> +		write[0] = _UT('\0');
> +		if (charsWritten != NULL) {
> +			*charsWritten = (int)(write - dest) + 1; /* .. for terminator */
> +		}
> +	}
> +
> +	return URI_SUCCESS;
> +}
> +
> +
> +
> +UriBool URI_FUNC(AppendQueryItem)(URI_TYPE(QueryList) ** prevNext,
> +		int * itemCount, const URI_CHAR * keyFirst, const URI_CHAR * keyAfter,
> +		const URI_CHAR * valueFirst, const URI_CHAR * valueAfter,
> +		UriBool plusToSpace, UriBreakConversion breakConversion) {
> +	const int keyLen = (int)(keyAfter - keyFirst);
> +	const int valueLen = (int)(valueAfter - valueFirst);
> +	URI_CHAR * key;
> +	URI_CHAR * value;
> +
> +	if ((prevNext == NULL) || (itemCount == NULL)
> +			|| (keyFirst == NULL) || (keyAfter == NULL)
> +			|| (keyFirst > keyAfter) || (valueFirst > valueAfter)
> +			|| ((keyFirst == keyAfter)
> +				&& (valueFirst == NULL) && (valueAfter == NULL))) {
> +		return URI_TRUE;
> +	}
> +
> +	/* Append new empty item */
> +	*prevNext = malloc(1 * sizeof(URI_TYPE(QueryList)));
> +	if (*prevNext == NULL) {
> +		return URI_FALSE; /* Raises malloc error */
> +	}
> +	(*prevNext)->next = NULL;
> +
> +
> +	/* Fill key */
> +	key = malloc((keyLen + 1) * sizeof(URI_CHAR));
> +	if (key == NULL) {
> +		free(*prevNext);
> +		*prevNext = NULL;
> +		return URI_FALSE; /* Raises malloc error */
> +	}
> +
> +	key[keyLen] = _UT('\0');
> +	if (keyLen > 0) {
> +		/* Copy 1:1 */
> +		memcpy(key, keyFirst, keyLen * sizeof(URI_CHAR));
> +
> +		/* Unescape */
> +		URI_FUNC(UnescapeInPlaceEx)(key, plusToSpace, breakConversion);
> +	}
> +	(*prevNext)->key = key;
> +
> +
> +	/* Fill value */
> +	if (valueFirst != NULL) {
> +		value = malloc((valueLen + 1) * sizeof(URI_CHAR));
> +		if (value == NULL) {
> +			free(key);
> +			free(*prevNext);
> +			*prevNext = NULL;
> +			return URI_FALSE; /* Raises malloc error */
> +		}
> +
> +		value[valueLen] = _UT('\0');
> +		if (valueLen > 0) {
> +			/* Copy 1:1 */
> +			memcpy(value, valueFirst, valueLen * sizeof(URI_CHAR));
> +
> +			/* Unescape */
> +			URI_FUNC(UnescapeInPlaceEx)(value, plusToSpace, breakConversion);
> +		}
> +		(*prevNext)->value = value;
> +	} else {
> +		value = NULL;
> +	}
> +	(*prevNext)->value = value;
> +
> +	(*itemCount)++;
> +	return URI_TRUE;
> +}
> +
> +
> +
> +void URI_FUNC(FreeQueryList)(URI_TYPE(QueryList) * queryList) {
> +	while (queryList != NULL) {
> +		URI_TYPE(QueryList) * nextBackup = queryList->next;
> +		free(queryList->key);
> +		free(queryList->value);
> +		free(queryList);
> +		queryList = nextBackup;
> +	}
> +}
> +
> +
> +
> +int URI_FUNC(DissectQueryMalloc)(URI_TYPE(QueryList) ** dest, int * itemCount,
> +		const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	const UriBool plusToSpace = URI_TRUE;
> +	const UriBreakConversion breakConversion = URI_BR_DONT_TOUCH;
> +
> +	return URI_FUNC(DissectQueryMallocEx)(dest, itemCount, first, afterLast,
> +			plusToSpace, breakConversion);
> +}
> +
> +
> +
> +int URI_FUNC(DissectQueryMallocEx)(URI_TYPE(QueryList) ** dest, int * itemCount,
> +		const URI_CHAR * first, const URI_CHAR * afterLast,
> +		UriBool plusToSpace, UriBreakConversion breakConversion) {
> +	const URI_CHAR * walk = first;
> +	const URI_CHAR * keyFirst = first;
> +	const URI_CHAR * keyAfter = NULL;
> +	const URI_CHAR * valueFirst = NULL;
> +	const URI_CHAR * valueAfter = NULL;
> +	URI_TYPE(QueryList) ** prevNext = dest;
> +	int nullCounter;
> +	int * itemsAppended = (itemCount == NULL) ? &nullCounter : itemCount;
> +
> +	if ((dest == NULL) || (first == NULL) || (afterLast == NULL)) {
> +		return URI_ERROR_NULL;
> +	}
> +
> +	if (first > afterLast) {
> +		return URI_ERROR_RANGE_INVALID;
> +	}
> +
> +	*dest = NULL;
> +	*itemsAppended = 0;
> +
> +	/* Parse query string */
> +	for (; walk < afterLast; walk++) {
> +		switch (*walk) {
> +		case _UT('&'):
> +			if (valueFirst != NULL) {
> +				valueAfter = walk;
> +			} else {
> +				keyAfter = walk;
> +			}
> +
> +			if (URI_FUNC(AppendQueryItem)(prevNext, itemsAppended,
> +					keyFirst, keyAfter, valueFirst, valueAfter,
> +					plusToSpace, breakConversion)
> +					== URI_FALSE) {
> +				/* Free list we built */
> +				*itemsAppended = 0;
> +				URI_FUNC(FreeQueryList)(*dest);
> +				return URI_ERROR_MALLOC;
> +			}
> +
> +			/* Make future items children of the current */
> +			if ((prevNext != NULL) && (*prevNext != NULL)) {
> +				prevNext = &((*prevNext)->next);
> +			}
> +
> +			if (walk + 1 < afterLast) {
> +				keyFirst = walk + 1;
> +			} else {
> +				keyFirst = NULL;
> +			}
> +			keyAfter = NULL;
> +			valueFirst = NULL;
> +			valueAfter = NULL;
> +			break;
> +
> +		case _UT('='):
> +			/* NOTE: WE treat the first '=' as a separator, */
> +			/*       all following go into the value part   */
> +			if (keyAfter == NULL) {
> +				keyAfter = walk;
> +				if (walk + 1 < afterLast) {
> +					valueFirst = walk + 1;
> +					valueAfter = walk + 1;
> +				}
> +			}
> +			break;
> +
> +		default:
> +			break;
> +		}
> +	}
> +
> +	if (valueFirst != NULL) {
> +		/* Must be key/value pair */
> +		valueAfter = walk;
> +	} else {
> +		/* Must be key only */
> +		keyAfter = walk;
> +	}
> +
> +	if (URI_FUNC(AppendQueryItem)(prevNext, itemsAppended, keyFirst, keyAfter,
> +			valueFirst, valueAfter, plusToSpace, breakConversion)
> +			== URI_FALSE) {
> +		/* Free list we built */
> +		*itemsAppended = 0;
> +		URI_FUNC(FreeQueryList)(*dest);
> +		return URI_ERROR_MALLOC;
> +	}
> +
> +	return URI_SUCCESS;
> +}
> +
> +
> +
> +#endif
> diff --git a/uriparser/lib/UriRecompose.c b/uriparser/lib/UriRecompose.c
> new file mode 100644
> --- /dev/null
> +++ b/uriparser/lib/UriRecompose.c
> @@ -0,0 +1,573 @@
> +/*
> + * uriparser - RFC 3986 URI parsing library
> + *
> + * Copyright (C) 2007, Weijia Song <songweijia at gmail.com>
> + * Copyright (C) 2007, Sebastian Pipping <webmaster at hartwork.org>
> + * All rights reserved.
> + *
> + * Redistribution  and use in source and binary forms, with or without
> + * modification,  are permitted provided that the following conditions
> + * are met:
> + *
> + *     * Redistributions   of  source  code  must  retain  the   above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer.
> + *
> + *     * Redistributions  in  binary  form must  reproduce  the  above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer   in  the  documentation  and/or  other  materials
> + *       provided with the distribution.
> + *
> + *     * Neither  the name of the <ORGANIZATION> nor the names of  its
> + *       contributors  may  be  used to endorse  or  promote  products
> + *       derived  from  this software without specific  prior  written
> + *       permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT  NOT
> + * LIMITED  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS
> + * FOR  A  PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT  SHALL  THE
> + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL,    SPECIAL,   EXEMPLARY,   OR   CONSEQUENTIAL   DAMAGES
> + * (INCLUDING,  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> + * SERVICES;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
> + * STRICT  LIABILITY,  OR  TORT (INCLUDING  NEGLIGENCE  OR  OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
> + * OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +/* What encodings are enabled? */
> +#include <uriparser/UriDefsConfig.h>
> +#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
> +/* Include SELF twice */
> +# define URI_PASS_ANSI 1
> +# include "UriRecompose.c"
> +# undef URI_PASS_ANSI
> +# define URI_PASS_UNICODE 1
> +# include "UriRecompose.c"
> +# undef URI_PASS_UNICODE
> +#else
> +# ifdef URI_PASS_ANSI
> +#  include <uriparser/UriDefsAnsi.h>
> +# else
> +#  include <uriparser/UriDefsUnicode.h>
> +#  include <wchar.h>
> +# endif
> +
> +
> +
> +#ifndef URI_DOXYGEN
> +# include <uriparser/Uri.h>
> +# include "UriCommon.h"
> +#endif
> +
> +
> +
> +static int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(Uri) * uri,
> +		int maxChars, int * charsWritten, int * charsRequired);
> +
> +
> +
> +int URI_FUNC(ToStringCharsRequired)(const URI_TYPE(Uri) * uri,
> +		int * charsRequired) {
> +	const int MAX_CHARS = ((unsigned int)-1) >> 1;
> +	return URI_FUNC(ToStringEngine)(NULL, uri, MAX_CHARS, NULL, charsRequired);
> +}
> +
> +
> +
> +int URI_FUNC(ToString)(URI_CHAR * dest, const URI_TYPE(Uri) * uri,
> +		int maxChars, int * charsWritten) {
> +	return URI_FUNC(ToStringEngine)(dest, uri, maxChars, charsWritten, NULL);
> +}
> +
> +
> +
> +static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest,
> +		const URI_TYPE(Uri) * uri, int maxChars, int * charsWritten,
> +		int * charsRequired) {
> +	int written = 0;
> +	if ((uri == NULL) || ((dest == NULL) && (charsRequired == NULL))) {
> +		if (charsWritten != NULL) {
> +			*charsWritten = 0;
> +		}
> +		return URI_ERROR_NULL;
> +	}
> +
> +	if (maxChars < 1) {
> +		if (charsWritten != NULL) {
> +			*charsWritten = 0;
> +		}
> +		return URI_ERROR_TOSTRING_TOO_LONG;
> +	}
> +	maxChars--; /* So we don't have to substract 1 for '\0' all the time */
> +
> +	/* [01/19]	result = "" */
> +				if (dest != NULL) {
> +					dest[0] = _UT('\0');
> +				} else {
> +					(*charsRequired) = 0;
> +				}
> +	/* [02/19]	if defined(scheme) then */
> +				if (uri->scheme.first != NULL) {
> +	/* [03/19]		append scheme to result; */
> +					const int charsToWrite
> +							= (int)(uri->scheme.afterLast - uri->scheme.first);
> +					if (dest != NULL) {
> +						if (written + charsToWrite <= maxChars) {
> +							memcpy(dest + written, uri->scheme.first,
> +									charsToWrite * sizeof(URI_CHAR));
> +							written += charsToWrite;
> +						} else {
> +							dest[0] = _UT('\0');
> +							if (charsWritten != NULL) {
> +								*charsWritten = 0;
> +							}
> +							return URI_ERROR_TOSTRING_TOO_LONG;
> +						}
> +					} else {
> +						(*charsRequired) += charsToWrite;
> +					}
> +	/* [04/19]		append ":" to result; */
> +					if (dest != NULL) {
> +						if (written + 1 <= maxChars) {
> +							memcpy(dest + written, _UT(":"),
> +									1 * sizeof(URI_CHAR));
> +							written += 1;
> +						} else {
> +							dest[0] = _UT('\0');
> +							if (charsWritten != NULL) {
> +								*charsWritten = 0;
> +							}
> +							return URI_ERROR_TOSTRING_TOO_LONG;
> +						}
> +					} else {
> +						(*charsRequired) += 1;
> +					}
> +	/* [05/19]	endif; */
> +				}
> +	/* [06/19]	if defined(authority) then */
> +				if (URI_FUNC(IsHostSet)(uri)) {
> +	/* [07/19]		append "//" to result; */
> +					if (dest != NULL) {
> +						if (written + 2 <= maxChars) {
> +							memcpy(dest + written, _UT("//"),
> +									2 * sizeof(URI_CHAR));
> +							written += 2;
> +						} else {
> +							dest[0] = _UT('\0');
> +							if (charsWritten != NULL) {
> +								*charsWritten = 0;
> +							}
> +							return URI_ERROR_TOSTRING_TOO_LONG;
> +						}
> +					} else {
> +						(*charsRequired) += 2;
> +					}
> +	/* [08/19]		append authority to result; */
> +					/* UserInfo */
> +					if (uri->userInfo.first != NULL) {
> +						const int charsToWrite = (int)(uri->userInfo.afterLast - uri->userInfo.first);
> +						if (dest != NULL) {
> +							if (written + charsToWrite <= maxChars) {
> +								memcpy(dest + written, uri->userInfo.first,
> +										charsToWrite * sizeof(URI_CHAR));
> +								written += charsToWrite;
> +							} else {
> +								dest[0] = _UT('\0');
> +								if (charsWritten != NULL) {
> +									*charsWritten = 0;
> +								}
> +								return URI_ERROR_TOSTRING_TOO_LONG;
> +							}
> +
> +							if (written + 1 <= maxChars) {
> +								memcpy(dest + written, _UT("@"),
> +										1 * sizeof(URI_CHAR));
> +								written += 1;
> +							} else {
> +								dest[0] = _UT('\0');
> +								if (charsWritten != NULL) {
> +									*charsWritten = 0;
> +								}
> +								return URI_ERROR_TOSTRING_TOO_LONG;
> +							}
> +						} else {
> +							(*charsRequired) += charsToWrite + 1;
> +						}
> +					}
> +
> +					/* Host */
> +					if (uri->hostData.ip4 != NULL) {
> +						/* IPv4 */
> +						int i = 0;
> +						for (; i < 4; i++) {
> +							const unsigned char value = uri->hostData.ip4->data[i];
> +							const int charsToWrite = (value > 99) ? 3 : ((value > 9) ? 2 : 1);
> +							if (dest != NULL) {
> +								if (written + charsToWrite <= maxChars) {
> +									URI_CHAR text[4];
> +									if (value > 99) {
> +										text[0] = _UT('0') + (value / 100);
> +										text[1] = _UT('0') + ((value % 100) / 10);
> +										text[2] = _UT('0') + (value % 10);
> +									} else if (value > 9)  {
> +										text[0] = _UT('0') + (value / 10);
> +										text[1] = _UT('0') + (value % 10);
> +									} else {
> +										text[0] = _UT('0') + value;
> +									}
> +									text[charsToWrite] = _UT('\0');
> +									memcpy(dest + written, text, charsToWrite * sizeof(URI_CHAR));
> +									written += charsToWrite;
> +								} else {
> +									dest[0] = _UT('\0');
> +									if (charsWritten != NULL) {
> +										*charsWritten = 0;
> +									}
> +									return URI_ERROR_TOSTRING_TOO_LONG;
> +								}
> +								if (i < 3) {
> +									if (written + 1 <= maxChars) {
> +										memcpy(dest + written, _UT("."),
> +												1 * sizeof(URI_CHAR));
> +										written += 1;
> +									} else {
> +										dest[0] = _UT('\0');
> +										if (charsWritten != NULL) {
> +											*charsWritten = 0;
> +										}
> +										return URI_ERROR_TOSTRING_TOO_LONG;
> +									}
> +								}
> +							} else {
> +								(*charsRequired) += charsToWrite + 1;
> +							}
> +						}
> +					} else if (uri->hostData.ip6 != NULL) {
> +						/* IPv6 */
> +						int i = 0;
> +						if (dest != NULL) {
> +							if (written + 1 <= maxChars) {
> +								memcpy(dest + written, _UT("["),
> +										1 * sizeof(URI_CHAR));
> +								written += 1;
> +							} else {
> +								dest[0] = _UT('\0');
> +								if (charsWritten != NULL) {
> +									*charsWritten = 0;
> +								}
> +								return URI_ERROR_TOSTRING_TOO_LONG;
> +							}
> +						} else {
> +							(*charsRequired) += 1;
> +						}
> +
> +						for (; i < 16; i++) {
> +							const unsigned char value = uri->hostData.ip6->data[i];
> +							if (dest != NULL) {
> +								if (written + 2 <= maxChars) {
> +									URI_CHAR text[3];
> +									text[0] = URI_FUNC(HexToLetterEx)(value / 16, URI_FALSE);
> +									text[1] = URI_FUNC(HexToLetterEx)(value % 16, URI_FALSE);
> +									text[2] = _UT('\0');
> +									memcpy(dest + written, text, 2 * sizeof(URI_CHAR));
> +									written += 2;
> +								} else {
> +									dest[0] = _UT('\0');
> +									if (charsWritten != NULL) {
> +										*charsWritten = 0;
> +									}
> +									return URI_ERROR_TOSTRING_TOO_LONG;
> +								}
> +							} else {
> +								(*charsRequired) += 2;
> +							}
> +							if (((i & 1) == 1) && (i < 15)) {
> +								if (dest != NULL) {
> +									if (written + 1 <= maxChars) {
> +										memcpy(dest + written, _UT(":"),
> +												1 * sizeof(URI_CHAR));
> +										written += 1;
> +									} else {
> +										dest[0] = _UT('\0');
> +										if (charsWritten != NULL) {
> +											*charsWritten = 0;
> +										}
> +										return URI_ERROR_TOSTRING_TOO_LONG;
> +									}
> +								} else {
> +									(*charsRequired) += 1;
> +								}
> +							}
> +						}
> +
> +						if (dest != NULL) {
> +							if (written + 1 <= maxChars) {
> +								memcpy(dest + written, _UT("]"),
> +										1 * sizeof(URI_CHAR));
> +								written += 1;
> +							} else {
> +								dest[0] = _UT('\0');
> +								if (charsWritten != NULL) {
> +									*charsWritten = 0;
> +								}
> +								return URI_ERROR_TOSTRING_TOO_LONG;
> +							}
> +						} else {
> +							(*charsRequired) += 1;
> +						}
> +					} else if (uri->hostData.ipFuture.first != NULL) {
> +						/* IPvFuture */
> +						const int charsToWrite = (int)(uri->hostData.ipFuture.afterLast
> +								- uri->hostData.ipFuture.first);
> +						if (dest != NULL) {
> +							if (written + 1 <= maxChars) {
> +								memcpy(dest + written, _UT("["),
> +										1 * sizeof(URI_CHAR));
> +								written += 1;
> +							} else {
> +								dest[0] = _UT('\0');
> +								if (charsWritten != NULL) {
> +									*charsWritten = 0;
> +								}
> +								return URI_ERROR_TOSTRING_TOO_LONG;
> +							}
> +
> +							if (written + charsToWrite <= maxChars) {
> +								memcpy(dest + written, uri->hostData.ipFuture.first, charsToWrite * sizeof(URI_CHAR));
> +								written += charsToWrite;
> +							} else {
> +								dest[0] = _UT('\0');
> +								if (charsWritten != NULL) {
> +									*charsWritten = 0;
> +								}
> +								return URI_ERROR_TOSTRING_TOO_LONG;
> +							}
> +
> +							if (written + 1 <= maxChars) {
> +								memcpy(dest + written, _UT("]"),
> +										1 * sizeof(URI_CHAR));
> +								written += 1;
> +							} else {
> +								dest[0] = _UT('\0');
> +								if (charsWritten != NULL) {
> +									*charsWritten = 0;
> +								}
> +								return URI_ERROR_TOSTRING_TOO_LONG;
> +							}
> +						} else {
> +							(*charsRequired) += 1 + charsToWrite + 1;
> +						}
> +					} else if (uri->hostText.first != NULL) {
> +						/* Regname */
> +						const int charsToWrite = (int)(uri->hostText.afterLast - uri->hostText.first);
> +						if (dest != NULL) {
> +							if (written + charsToWrite <= maxChars) {
> +								memcpy(dest + written, uri->hostText.first,
> +										charsToWrite * sizeof(URI_CHAR));
> +								written += charsToWrite;
> +							} else {
> +								dest[0] = _UT('\0');
> +								if (charsWritten != NULL) {
> +									*charsWritten = 0;
> +								}
> +								return URI_ERROR_TOSTRING_TOO_LONG;
> +							}
> +						} else {
> +							(*charsRequired) += charsToWrite;
> +						}
> +					}
> +
> +					/* Port */
> +					if (uri->portText.first != NULL) {
> +						const int charsToWrite = (int)(uri->portText.afterLast - uri->portText.first);
> +						if (dest != NULL) {
> +							/* Leading ':' */
> +							if (written + 1 <= maxChars) {
> +									memcpy(dest + written, _UT(":"),
> +											1 * sizeof(URI_CHAR));
> +									written += 1;
> +							} else {
> +								dest[0] = _UT('\0');
> +								if (charsWritten != NULL) {
> +									*charsWritten = 0;
> +								}
> +								return URI_ERROR_TOSTRING_TOO_LONG;
> +							}
> +
> +							/* Port number */
> +							if (written + charsToWrite <= maxChars) {
> +								memcpy(dest + written, uri->portText.first,
> +										charsToWrite * sizeof(URI_CHAR));
> +								written += charsToWrite;
> +							} else {
> +								dest[0] = _UT('\0');
> +								if (charsWritten != NULL) {
> +									*charsWritten = 0;
> +								}
> +								return URI_ERROR_TOSTRING_TOO_LONG;
> +							}
> +						} else {
> +							(*charsRequired) += 1 + charsToWrite;
> +						}
> +					}
> +	/* [09/19]	endif; */
> +				}
> +	/* [10/19]	append path to result; */
> +				/* Slash needed here? */
> +				if (uri->absolutePath || ((uri->pathHead != NULL)
> +						&& URI_FUNC(IsHostSet)(uri))) {
> +					if (dest != NULL) {
> +						if (written + 1 <= maxChars) {
> +							memcpy(dest + written, _UT("/"),
> +									1 * sizeof(URI_CHAR));
> +							written += 1;
> +						} else {
> +							dest[0] = _UT('\0');
> +							if (charsWritten != NULL) {
> +								*charsWritten = 0;
> +							}
> +							return URI_ERROR_TOSTRING_TOO_LONG;
> +						}
> +					} else {
> +						(*charsRequired) += 1;
> +					}
> +				}
> +
> +				if (uri->pathHead != NULL) {
> +					URI_TYPE(PathSegment) * walker = uri->pathHead;
> +					do {
> +						const int charsToWrite = (int)(walker->text.afterLast - walker->text.first);
> +						if (dest != NULL) {
> +							if (written + charsToWrite <= maxChars) {
> +								memcpy(dest + written, walker->text.first,
> +										charsToWrite * sizeof(URI_CHAR));
> +								written += charsToWrite;
> +							} else {
> +								dest[0] = _UT('\0');
> +								if (charsWritten != NULL) {
> +									*charsWritten = 0;
> +								}
> +								return URI_ERROR_TOSTRING_TOO_LONG;
> +							}
> +						} else {
> +							(*charsRequired) += charsToWrite;
> +						}
> +
> +						/* Not last segment -> append slash */
> +						if (walker->next != NULL) {
> +							if (dest != NULL) {
> +								if (written + 1 <= maxChars) {
> +									memcpy(dest + written, _UT("/"),
> +											1 * sizeof(URI_CHAR));
> +									written += 1;
> +								} else {
> +									dest[0] = _UT('\0');
> +									if (charsWritten != NULL) {
> +										*charsWritten = 0;
> +									}
> +									return URI_ERROR_TOSTRING_TOO_LONG;
> +								}
> +							} else {
> +								(*charsRequired) += 1;
> +							}
> +						}
> +
> +						walker = walker->next;
> +					} while (walker != NULL);
> +				}
> +	/* [11/19]	if defined(query) then */
> +				if (uri->query.first != NULL) {
> +	/* [12/19]		append "?" to result; */
> +					if (dest != NULL) {
> +						if (written + 1 <= maxChars) {
> +							memcpy(dest + written, _UT("?"),
> +									1 * sizeof(URI_CHAR));
> +							written += 1;
> +						} else {
> +							dest[0] = _UT('\0');
> +							if (charsWritten != NULL) {
> +								*charsWritten = 0;
> +							}
> +							return URI_ERROR_TOSTRING_TOO_LONG;
> +						}
> +					} else {
> +						(*charsRequired) += 1;
> +					}
> +	/* [13/19]		append query to result; */
> +					{
> +						const int charsToWrite
> +								= (int)(uri->query.afterLast - uri->query.first);
> +						if (dest != NULL) {
> +							if (written + charsToWrite <= maxChars) {
> +								memcpy(dest + written, uri->query.first,
> +										charsToWrite * sizeof(URI_CHAR));
> +								written += charsToWrite;
> +							} else {
> +								dest[0] = _UT('\0');
> +								if (charsWritten != NULL) {
> +									*charsWritten = 0;
> +								}
> +								return URI_ERROR_TOSTRING_TOO_LONG;
> +							}
> +						} else {
> +							(*charsRequired) += charsToWrite;
> +						}
> +					}
> +	/* [14/19]	endif; */
> +				}
> +	/* [15/19]	if defined(fragment) then */
> +				if (uri->fragment.first != NULL) {
> +	/* [16/19]		append "#" to result; */
> +					if (dest != NULL) {
> +						if (written + 1 <= maxChars) {
> +							memcpy(dest + written, _UT("#"),
> +									1 * sizeof(URI_CHAR));
> +							written += 1;
> +						} else {
> +							dest[0] = _UT('\0');
> +							if (charsWritten != NULL) {
> +								*charsWritten = 0;
> +							}
> +							return URI_ERROR_TOSTRING_TOO_LONG;
> +						}
> +					} else {
> +						(*charsRequired) += 1;
> +					}
> +	/* [17/19]		append fragment to result; */
> +					{
> +						const int charsToWrite
> +								= (int)(uri->fragment.afterLast - uri->fragment.first);
> +						if (dest != NULL) {
> +							if (written + charsToWrite <= maxChars) {
> +								memcpy(dest + written, uri->fragment.first,
> +										charsToWrite * sizeof(URI_CHAR));
> +								written += charsToWrite;
> +							} else {
> +								dest[0] = _UT('\0');
> +								if (charsWritten != NULL) {
> +									*charsWritten = 0;
> +								}
> +								return URI_ERROR_TOSTRING_TOO_LONG;
> +							}
> +						} else {
> +							(*charsRequired) += charsToWrite;
> +						}
> +					}
> +	/* [18/19]	endif; */
> +				}
> +	/* [19/19]	return result; */
> +				if (dest != NULL) {
> +					dest[written++] = _UT('\0');
> +					if (charsWritten != NULL) {
> +						*charsWritten = written;
> +					}
> +				}
> +				return URI_SUCCESS;
> +}
> +
> +
> +
> +#endif
> diff --git a/uriparser/lib/UriResolve.c b/uriparser/lib/UriResolve.c
> new file mode 100644
> --- /dev/null
> +++ b/uriparser/lib/UriResolve.c
> @@ -0,0 +1,255 @@
> +/*
> + * uriparser - RFC 3986 URI parsing library
> + *
> + * Copyright (C) 2007, Weijia Song <songweijia at gmail.com>
> + * Copyright (C) 2007, Sebastian Pipping <webmaster at hartwork.org>
> + * All rights reserved.
> + *
> + * Redistribution  and use in source and binary forms, with or without
> + * modification,  are permitted provided that the following conditions
> + * are met:
> + *
> + *     * Redistributions   of  source  code  must  retain  the   above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer.
> + *
> + *     * Redistributions  in  binary  form must  reproduce  the  above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer   in  the  documentation  and/or  other  materials
> + *       provided with the distribution.
> + *
> + *     * Neither  the name of the <ORGANIZATION> nor the names of  its
> + *       contributors  may  be  used to endorse  or  promote  products
> + *       derived  from  this software without specific  prior  written
> + *       permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT  NOT
> + * LIMITED  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS
> + * FOR  A  PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT  SHALL  THE
> + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL,    SPECIAL,   EXEMPLARY,   OR   CONSEQUENTIAL   DAMAGES
> + * (INCLUDING,  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> + * SERVICES;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
> + * STRICT  LIABILITY,  OR  TORT (INCLUDING  NEGLIGENCE  OR  OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
> + * OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +/* What encodings are enabled? */
> +#include <uriparser/UriDefsConfig.h>
> +#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
> +/* Include SELF twice */
> +# define URI_PASS_ANSI 1
> +# include "UriResolve.c"
> +# undef URI_PASS_ANSI
> +# define URI_PASS_UNICODE 1
> +# include "UriResolve.c"
> +# undef URI_PASS_UNICODE
> +#else
> +# ifdef URI_PASS_ANSI
> +#  include <uriparser/UriDefsAnsi.h>
> +# else
> +#  include <uriparser/UriDefsUnicode.h>
> +#  include <wchar.h>
> +# endif
> +
> +
> +
> +#ifndef URI_DOXYGEN
> +# include <uriparser/Uri.h>
> +# include "UriCommon.h"
> +#endif
> +
> +
> +
> +/* Appends a relative URI to an absolute. The last path segement of
> + * the absolute URI is replaced. */
> +static URI_INLINE UriBool URI_FUNC(MergePath)(URI_TYPE(Uri) * absWork,
> +		const URI_TYPE(Uri) * relAppend) {
> +	URI_TYPE(PathSegment) * sourceWalker;
> +	URI_TYPE(PathSegment) * destPrev;
> +	if (relAppend->pathHead == NULL) {
> +		return URI_TRUE;
> +	}
> +
> +	/* Replace last segment ("" if trailing slash) with first of append chain */
> +	if (absWork->pathHead == NULL) {
> +		URI_TYPE(PathSegment) * const dup = malloc(sizeof(URI_TYPE(PathSegment)));
> +		if (dup == NULL) {
> +			return URI_FALSE; /* Raises malloc error */
> +		}
> +		dup->next = NULL;
> +		absWork->pathHead = dup;
> +		absWork->pathTail = dup;
> +	}
> +	absWork->pathTail->text.first = relAppend->pathHead->text.first;
> +	absWork->pathTail->text.afterLast = relAppend->pathHead->text.afterLast;
> +
> +	/* Append all the others */
> +	sourceWalker = relAppend->pathHead->next;
> +	if (sourceWalker == NULL) {
> +		return URI_TRUE;
> +	}
> +	destPrev = absWork->pathTail;
> +
> +	for (;;) {
> +		URI_TYPE(PathSegment) * const dup = malloc(sizeof(URI_TYPE(PathSegment)));
> +		if (dup == NULL) {
> +			destPrev->next = NULL;
> +			absWork->pathTail = destPrev;
> +			return URI_FALSE; /* Raises malloc error */
> +		}
> +		dup->text = sourceWalker->text;
> +		destPrev->next = dup;
> +
> +		if (sourceWalker->next == NULL) {
> +			absWork->pathTail = dup;
> +			absWork->pathTail->next = NULL;
> +			break;
> +		}
> +		destPrev = dup;
> +		sourceWalker = sourceWalker->next;
> +	}
> +
> +	return URI_TRUE;
> +}
> +
> +
> +
> +static int URI_FUNC(AddBaseUriImpl)(URI_TYPE(Uri) * absDest,
> +		const URI_TYPE(Uri) * relSource,
> +		const URI_TYPE(Uri) * absBase) {
> +	if (absDest == NULL) {
> +		return URI_ERROR_NULL;
> +	}
> +	URI_FUNC(ResetUri)(absDest);
> +
> +	if ((relSource == NULL) || (absBase == NULL)) {
> +		return URI_ERROR_NULL;
> +	}
> +
> +	/* absBase absolute? */
> +	if (absBase->scheme.first == NULL) {
> +		return URI_ERROR_ADDBASE_REL_BASE;
> +	}
> +
> +	/* [01/32]	if defined(R.scheme) then */
> +				if (relSource->scheme.first != NULL) {
> +	/* [02/32]		T.scheme = R.scheme; */
> +					absDest->scheme = relSource->scheme;
> +	/* [03/32]		T.authority = R.authority; */
> +					if (!URI_FUNC(CopyAuthority)(absDest, relSource)) {
> +						return URI_ERROR_MALLOC;
> +					}
> +	/* [04/32]		T.path = remove_dot_segments(R.path); */
> +					if (!URI_FUNC(CopyPath)(absDest, relSource)) {
> +						return URI_ERROR_MALLOC;
> +					}
> +					if (!URI_FUNC(RemoveDotSegmentsAbsolute)(absDest)) {
> +						return URI_ERROR_MALLOC;
> +					}
> +	/* [05/32]		T.query = R.query; */
> +					absDest->query = relSource->query;
> +	/* [06/32]	else */
> +				} else {
> +	/* [07/32]		if defined(R.authority) then */
> +					if (URI_FUNC(IsHostSet)(relSource)) {
> +	/* [08/32]			T.authority = R.authority; */
> +						if (!URI_FUNC(CopyAuthority)(absDest, relSource)) {
> +							return URI_ERROR_MALLOC;
> +						}
> +	/* [09/32]			T.path = remove_dot_segments(R.path); */
> +						if (!URI_FUNC(CopyPath)(absDest, relSource)) {
> +							return URI_ERROR_MALLOC;
> +						}
> +						if (!URI_FUNC(RemoveDotSegmentsAbsolute)(absDest)) {
> +							return URI_ERROR_MALLOC;
> +						}
> +	/* [10/32]			T.query = R.query; */
> +						absDest->query = relSource->query;
> +	/* [11/32]		else */
> +					} else {
> +	/* [28/32]			T.authority = Base.authority; */
> +						if (!URI_FUNC(CopyAuthority)(absDest, absBase)) {
> +							return URI_ERROR_MALLOC;
> +						}
> +	/* [12/32]			if (R.path == "") then */
> +						if (relSource->pathHead == NULL) {
> +	/* [13/32]				T.path = Base.path; */
> +							if (!URI_FUNC(CopyPath)(absDest, absBase)) {
> +								return URI_ERROR_MALLOC;
> +							}
> +	/* [14/32]				if defined(R.query) then */
> +							if (relSource->query.first != NULL) {
> +	/* [15/32]					T.query = R.query; */
> +								absDest->query = relSource->query;
> +	/* [16/32]				else */
> +							} else {
> +	/* [17/32]					T.query = Base.query; */
> +								absDest->query = absBase->query;
> +	/* [18/32]				endif; */
> +							}
> +	/* [19/32]			else */
> +						} else {
> +	/* [20/32]				if (R.path starts-with "/") then */
> +							if (relSource->absolutePath) {
> +	/* [21/32]					T.path = remove_dot_segments(R.path); */
> +								if (!URI_FUNC(CopyPath)(absDest, relSource)) {
> +									return URI_ERROR_MALLOC;
> +								}
> +								if (!URI_FUNC(RemoveDotSegmentsAbsolute)(absDest)) {
> +									return URI_ERROR_MALLOC;
> +								}
> +	/* [22/32]				else */
> +							} else {
> +	/* [23/32]					T.path = merge(Base.path, R.path); */
> +								if (!URI_FUNC(CopyPath)(absDest, absBase)) {
> +									return URI_ERROR_MALLOC;
> +								}
> +								if (!URI_FUNC(MergePath)(absDest, relSource)) {
> +									return URI_ERROR_MALLOC;
> +								}
> +	/* [24/32]					T.path = remove_dot_segments(T.path); */
> +								if (!URI_FUNC(RemoveDotSegmentsAbsolute)(absDest)) {
> +									return URI_ERROR_MALLOC;
> +								}
> +
> +								if (!URI_FUNC(FixAmbiguity)(absDest)) {
> +									return URI_ERROR_MALLOC;
> +								}
> +	/* [25/32]				endif; */
> +							}
> +	/* [26/32]				T.query = R.query; */
> +							absDest->query = relSource->query;
> +	/* [27/32]			endif; */
> +						}
> +						URI_FUNC(FixEmptyTrailSegment)(absDest);
> +	/* [29/32]		endif; */
> +					}
> +	/* [30/32]		T.scheme = Base.scheme; */
> +					absDest->scheme = absBase->scheme;
> +	/* [31/32]	endif; */
> +				}
> +	/* [32/32]	T.fragment = R.fragment; */
> +				absDest->fragment = relSource->fragment;
> +
> +	return URI_SUCCESS;
> +
> +}
> +
> +
> +
> +int URI_FUNC(AddBaseUri)(URI_TYPE(Uri) * absDest,
> +		const URI_TYPE(Uri) * relSource, const URI_TYPE(Uri) * absBase) {
> +	const int res = URI_FUNC(AddBaseUriImpl)(absDest, relSource, absBase);
> +	if ((res != URI_SUCCESS) && (absDest != NULL)) {
> +		URI_FUNC(FreeUriMembers)(absDest);
> +	}
> +	return res;
> +}
> +
> +
> +
> +#endif
> diff --git a/uriparser/lib/UriShorten.c b/uriparser/lib/UriShorten.c
> new file mode 100644
> --- /dev/null
> +++ b/uriparser/lib/UriShorten.c
> @@ -0,0 +1,316 @@
> +/*
> + * uriparser - RFC 3986 URI parsing library
> + *
> + * Copyright (C) 2007, Weijia Song <songweijia at gmail.com>
> + * Copyright (C) 2007, Sebastian Pipping <webmaster at hartwork.org>
> + * All rights reserved.
> + *
> + * Redistribution  and use in source and binary forms, with or without
> + * modification,  are permitted provided that the following conditions
> + * are met:
> + *
> + *     * Redistributions   of  source  code  must  retain  the   above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer.
> + *
> + *     * Redistributions  in  binary  form must  reproduce  the  above
> + *       copyright  notice, this list of conditions and the  following
> + *       disclaimer   in  the  documentation  and/or  other  materials
> + *       provided with the distribution.
> + *
> + *     * Neither  the name of the <ORGANIZATION> nor the names of  its
> + *       contributors  may  be  used to endorse  or  promote  products
> + *       derived  from  this software without specific  prior  written
> + *       permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT  NOT
> + * LIMITED  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS
> + * FOR  A  PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT  SHALL  THE
> + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> + * INCIDENTAL,    SPECIAL,   EXEMPLARY,   OR   CONSEQUENTIAL   DAMAGES
> + * (INCLUDING,  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> + * SERVICES;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
> + * STRICT  LIABILITY,  OR  TORT (INCLUDING  NEGLIGENCE  OR  OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
> + * OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +/* What encodings are enabled? */
> +#include <uriparser/UriDefsConfig.h>
> +#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
> +/* Include SELF twice */
> +# define URI_PASS_ANSI 1
> +# include "UriShorten.c"
> +# undef URI_PASS_ANSI
> +# define URI_PASS_UNICODE 1
> +# include "UriShorten.c"
> +# undef URI_PASS_UNICODE
> +#else
> +# ifdef URI_PASS_ANSI
> +#  include <uriparser/UriDefsAnsi.h>
> +# else
> +#  include <uriparser/UriDefsUnicode.h>
> +#  include <wchar.h>
> +# endif
> +
> +
> +
> +#ifndef URI_DOXYGEN
> +# include <uriparser/Uri.h>
> +# include "UriCommon.h"
> +#endif
> +
> +
> +
> +static URI_INLINE UriBool URI_FUNC(AppendSegment)(URI_TYPE(Uri) * uri,
> +		const URI_CHAR * first, const URI_CHAR * afterLast) {
> +	/* Create segment */
> +	URI_TYPE(PathSegment) * segment = malloc(1 * sizeof(URI_TYPE(PathSegment)));
> +	if (segment == NULL) {
> +		return URI_FALSE; /* Raises malloc error */
> +	}
> +	segment->next = NULL;
> +	segment->text.first = first;
> +	segment->text.afterLast = afterLast;
> +
> +	/* Put into chain */
> +	if (uri->pathTail == NULL) {
> +		uri->pathHead = segment;
> +	} else {
> +		uri->pathTail->next = segment;
> +	}
> +	uri->pathTail = segment;
> +
> +	return URI_TRUE;
> +}
> +
> +
> +
> +static URI_INLINE UriBool URI_FUNC(EqualsAuthority)(const URI_TYPE(Uri) * first,
> +		const URI_TYPE(Uri) * second) {
> +	/* IPv4 */
> +	if (first->hostData.ip4 != NULL) {
> +		return ((second->hostData.ip4 != NULL)
> +				&& !memcmp(first->hostData.ip4->data,
> +					second->hostData.ip4->data, 4)) ? URI_TRUE : URI_FALSE;
> +	}
> +
> +	/* IPv6 */
> +	if (first->hostData.ip6 != NULL) {
> +		return ((second->hostData.ip6 != NULL)
> +				&& !memcmp(first->hostData.ip6->data,
> +					second->hostData.ip6->data, 16)) ? URI_TRUE : URI_FALSE;
> +	}
> +
> +	/* IPvFuture */
> +	if (first->hostData.ipFuture.first != NULL) {
> +		return ((second->hostData.ipFuture.first != NULL)
> +				&& !URI_STRNCMP(first->hostData.ipFuture.first,
> +					second->hostData.ipFuture.first,
> +					first->hostData.ipFuture.afterLast
> +					- first->hostData.ipFuture.first))
> +						? URI_TRUE : URI_FALSE;
> +	}
> +
> +	if (first->hostText.first != NULL) {
> +		return ((second->hostText.first != NULL)
> +				&& !URI_STRNCMP(first->hostText.first,
> +					second->hostText.first,
> +					first->hostText.afterLast
> +					- first->hostText.first)) ? URI_TRUE : URI_FALSE;
> +	}
> +
> +	return (second->hostText.first == NULL);
> +}
> +
> +
> +
> +int URI_FUNC(RemoveBaseUriImpl)(URI_TYPE(Uri) * dest,
> +		const URI_TYPE(Uri) * absSource,
> +		const URI_TYPE(Uri) * absBase,
> +		UriBool domainRootMode) {
> +	if (dest == NULL) {
> +		return URI_ERROR_NULL;
> +	}
> +	URI_FUNC(ResetUri)(dest);
> +
> +	if ((absSource == NULL) || (absBase == NULL)) {
> +		return URI_ERROR_NULL;
> +	}
> +
> +	/* absBase absolute? */
> +	if (absBase->scheme.first == NULL) {
> +		return URI_ERROR_REMOVEBASE_REL_BASE;
> +	}
> +
> +	/* absSource absolute? */
> +	if (absSource->scheme.first == NULL) {
> +		return URI_ERROR_REMOVEBASE_REL_SOURCE;
> +	}
> +
> +	/* [01/50]	if (A.scheme != Base.scheme) then */
> +				if (URI_STRNCMP(absSource->scheme.first, absBase->scheme.first,
> +						absSource->scheme.afterLast - absSource->scheme.first)) {
> +	/* [02/50]	   T.scheme    = A.scheme; */
> +					dest->scheme = absSource->scheme;
> +	/* [03/50]	   T.authority = A.authority; */
> +					if (!URI_FUNC(CopyAuthority)(dest, absSource)) {
> +						return URI_ERROR_MALLOC;
> +					}
> +	/* [04/50]	   T.path      = A.path; */
> +					if (!URI_FUNC(CopyPath)(dest, absSource)) {
> +						return URI_ERROR_MALLOC;
> +					}
> +	/* [05/50]	else */
> +				} else {
> +	/* [06/50]	   undef(T.scheme); */
> +					/* NOOP */
> +	/* [07/50]	   if (A.authority != Base.authority) then */
> +					if (!URI_FUNC(EqualsAuthority)(absSource, absBase)) {
> +	/* [08/50]	      T.authority = A.authority; */
> +						if (!URI_FUNC(CopyAuthority)(dest, absSource)) {
> +							return URI_ERROR_MALLOC;
> +						}
> +	/* [09/50]	      T.path      = A.path; */
> +						if (!URI_FUNC(CopyPath)(dest, absSource)) {
> +							return URI_ERROR_MALLOC;
> +						}
> +	/* [10/50]	   else */
> +					} else {
> +	/* [11/50]	      if domainRootMode then */
> +						if (domainRootMode == URI_TRUE) {
> +	/* [12/50]	         undef(T.authority); */
> +							/* NOOP */
> +	/* [13/50]	         if (first(A.path) == "") then */
> +							/* GROUPED */
> +	/* [14/50]	            T.path   = "/." + A.path; */
> +								/* GROUPED */
> +	/* [15/50]	         else */
> +								/* GROUPED */
> +	/* [16/50]	            T.path   = A.path; */
> +								/* GROUPED */
> +	/* [17/50]	         endif; */
> +							if (!URI_FUNC(CopyPath)(dest, absSource)) {
> +								return URI_ERROR_MALLOC;
> +							}
> +							dest->absolutePath = URI_TRUE;
> +
> +							if (!URI_FUNC(FixAmbiguity)(dest)) {
> +								return URI_ERROR_MALLOC;
> +							}
> +	/* [18/50]	      else */
> +						} else {
> +							const URI_TYPE(PathSegment) * sourceSeg = absSource->pathHead;
> +							const URI_TYPE(PathSegment) * baseSeg = absBase->pathHead;
> +	/* [19/50]	         bool pathNaked = true; */
> +							UriBool pathNaked = URI_TRUE;
> +	/* [20/50]	         undef(last(Base.path)); */
> +							/* NOOP */
> +	/* [21/50]	         T.path = ""; */
> +							dest->absolutePath = URI_FALSE;
> +	/* [22/50]	         while (first(A.path) == first(Base.path)) do */
> +							while ((sourceSeg != NULL) && (baseSeg != NULL)
> +									&& !URI_STRNCMP(sourceSeg->text.first, baseSeg->text.first,
> +									sourceSeg->text.afterLast - sourceSeg->text.first)
> +									&& !((sourceSeg->text.first == sourceSeg->text.afterLast)
> +										&& ((sourceSeg->next == NULL) != (baseSeg->next == NULL)))) {
> +	/* [23/50]	            A.path++; */
> +								sourceSeg = sourceSeg->next;
> +	/* [24/50]	            Base.path++; */
> +								baseSeg = baseSeg->next;
> +	/* [25/50]	         endwhile; */
> +							}
> +	/* [26/50]	         while defined(first(Base.path)) do */
> +							while ((baseSeg != NULL) && (baseSeg->next != NULL)) {
> +	/* [27/50]	            Base.path++; */
> +								baseSeg = baseSeg->next;
> +	/* [28/50]	            T.path += "../"; */
> +								if (!URI_FUNC(AppendSegment)(dest, URI_FUNC(ConstParent),
> +										URI_FUNC(ConstParent) + 2)) {
> +									return URI_ERROR_MALLOC;
> +								}
> +	/* [29/50]	            pathNaked = false; */
> +								pathNaked = URI_FALSE;
> +	/* [30/50]	         endwhile; */
> +							}
> +	/* [31/50]	         while defined(first(A.path)) do */
> +							while (sourceSeg != NULL) {
> +	/* [32/50]	            if pathNaked then */
> +								if (pathNaked == URI_TRUE) {
> +	/* [33/50]	               if (first(A.path) contains ":") then */
> +									UriBool containsColon = URI_FALSE;
> +									const URI_CHAR * ch = sourceSeg->text.first;
> +									for (; ch < sourceSeg->text.afterLast; ch++) {
> +										if (*ch == _UT(':')) {
> +											containsColon = URI_TRUE;
> +											break;
> +										}
> +									}
> +
> +									if (containsColon) {
> +	/* [34/50]	                  T.path += "./"; */
> +										if (!URI_FUNC(AppendSegment)(dest, URI_FUNC(ConstPwd),
> +												URI_FUNC(ConstPwd) + 1)) {
> +											return URI_ERROR_MALLOC;
> +										}
> +	/* [35/50]	               elseif (first(A.path) == "") then */
> +									} else if (sourceSeg->text.first == sourceSeg->text.afterLast) {
> +	/* [36/50]	                  T.path += "/."; */
> +										if (!URI_FUNC(AppendSegment)(dest, URI_FUNC(ConstPwd),
> +												URI_FUNC(ConstPwd) + 1)) {
> +											return URI_ERROR_MALLOC;
> +										}
> +	/* [37/50]	               endif; */
> +									}
> +	/* [38/50]	            endif; */
> +								}
> +	/* [39/50]	            T.path += first(A.path); */
> +								if (!URI_FUNC(AppendSegment)(dest, sourceSeg->text.first,
> +										sourceSeg->text.afterLast)) {
> +									return URI_ERROR_MALLOC;
> +								}
> +	/* [40/50]	            pathNaked = false; */
> +								pathNaked = URI_FALSE;
> +	/* [41/50]	            A.path++; */
> +								sourceSeg = sourceSeg->next;
> +	/* [42/50]	            if defined(first(A.path)) then */
> +								/* NOOP */
> +	/* [43/50]	               T.path += + "/"; */
> +								/* NOOP */
> +	/* [44/50]	            endif; */
> +								/* NOOP */
> +	/* [45/50]	         endwhile; */
> +							}
> +	/* [46/50]	      endif; */
> +						}
> +	/* [47/50]	   endif; */
> +					}
> +	/* [48/50]	endif; */
> +				}
> +	/* [49/50]	T.query     = A.query; */
> +				dest->query = absSource->query;
> +	/* [50/50]	T.fragment  = A.fragment; */
> +				dest->fragment = absSource->fragment;
> +
> +	return URI_SUCCESS;
> +}
> +
> +
> +
> +int URI_FUNC(RemoveBaseUri)(URI_TYPE(Uri) * dest,
> +		const URI_TYPE(Uri) * absSource,
> +		const URI_TYPE(Uri) * absBase,
> +		UriBool domainRootMode) {
> +	const int res = URI_FUNC(RemoveBaseUriImpl)(dest, absSource,
> +			absBase, domainRootMode);
> +	if ((res != URI_SUCCESS) && (dest != NULL)) {
> +		URI_FUNC(FreeUriMembers)(dest);
> +	}
> +	return res;
> +}
> +
> +
> +
> +#endif
>
> _______________________________________________
> rrd-developers mailing list
> rrd-developers at lists.oetiker.ch
> https://lists.oetiker.ch/cgi-bin/listinfo/rrd-developers
>
>

-- 
Tobi Oetiker, OETIKER+PARTNER AG, Aarweg 15 CH-4600 Olten, Switzerland
http://it.oetiker.ch tobi at oetiker.ch ++41 62 775 9902 / sb: -9900



More information about the rrd-developers mailing list