/* Generated by re2c 3.1 on Thu Aug  1 03:02:26 2024 */
/*
 * Copyright (C) Tildeslash Ltd. All rights reserved.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * In addition, as a special exception, the copyright holders give
 * permission to link the code of portions of this program with the
 * OpenSSL library under certain conditions as described in each
 * individual source file, and distribute linked combinations
 * including the two.
 *
 * You must obey the GNU General Public License in all respects
 * for all of the code used other than OpenSSL.
 */


#include "Config.h"

#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>

#include "URL.h"


/**
 * Implementation of the URL interface. The scanner handle 
 * ISO Latin 1 or UTF-8 encoded url's transparently. 
 *
 * @file
 */


/* ----------------------------------------------------------- Definitions */


typedef struct param_t {
        char *name;
        char *value;
        struct param_t *next;
} *param_t;

#define T URL_T
struct URL_S {
        int ip6;
	int port;
       	char *ref;
	char *path;
	char *host;
	char *user;
        char *qptr;
	char *query;
	char *portStr;
	char *protocol;
	char *password;
	char *toString;
        param_t params;
        char **paramNames;
	uchar_t *data;
	uchar_t *buffer;
	uchar_t *marker, *ctx, *limit, *token;
        /* Keep the above align with zild URL_T */
};

/* Unsafe URL characters: [00-1F, 7F-FF] <>\"#%}{|\\^[] ` */
static const uchar_t urlunsafe[256] = {
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
	1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 
	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
};

#define UNKNOWN_PORT -1
#define YYCURSOR     U->buffer
#define YYLIMIT      U->limit
#define YYTOKEN      U->token
#define SET_PROTOCOL(PORT) *(YYCURSOR-3)=0; U->protocol=U->token; U->port=PORT; goto authority


/* ------------------------------------------------------- Private methods */


static bool _parseURL(T U) {
        param_t param = NULL;
	
proto:
	if (YYCURSOR >= YYLIMIT)
		return false;
	YYTOKEN = YYCURSOR;
	
{
	unsigned char yych;
	yych = *U->buffer;
	switch (yych) {
		case '\t':
		case '\n':
		case '\r':
		case ' ': goto yy3;
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
		case 'A':
		case 'B':
		case 'C':
		case 'D':
		case 'E':
		case 'F':
		case 'G':
		case 'H':
		case 'I':
		case 'J':
		case 'K':
		case 'L':
		case 'M':
		case 'N':
		case 'O':
		case 'P':
		case 'Q':
		case 'R':
		case 'S':
		case 'T':
		case 'U':
		case 'V':
		case 'W':
		case 'X':
		case 'Y':
		case 'Z':
		case 'a':
		case 'b':
		case 'c':
		case 'd':
		case 'e':
		case 'f':
		case 'g':
		case 'h':
		case 'i':
		case 'j':
		case 'k':
		case 'l':
		case 'n':
		case 'q':
		case 'r':
		case 's':
		case 't':
		case 'u':
		case 'v':
		case 'w':
		case 'x':
		case 'y':
		case 'z': goto yy4;
		case 'm': goto yy5;
		case 'o': goto yy6;
		case 'p': goto yy7;
		default: goto yy1;
	}
yy1:
	++U->buffer;
yy2:
	{
                goto proto;
         }
yy3:
	++U->buffer;
	{
                goto proto;
         }
yy4:
	yych = *(U->marker = ++U->buffer);
	switch (yych) {
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
		case ':':
		case 'A':
		case 'B':
		case 'C':
		case 'D':
		case 'E':
		case 'F':
		case 'G':
		case 'H':
		case 'I':
		case 'J':
		case 'K':
		case 'L':
		case 'M':
		case 'N':
		case 'O':
		case 'P':
		case 'Q':
		case 'R':
		case 'S':
		case 'T':
		case 'U':
		case 'V':
		case 'W':
		case 'X':
		case 'Y':
		case 'Z':
		case 'a':
		case 'b':
		case 'c':
		case 'd':
		case 'e':
		case 'f':
		case 'g':
		case 'h':
		case 'i':
		case 'j':
		case 'k':
		case 'l':
		case 'm':
		case 'n':
		case 'o':
		case 'p':
		case 'q':
		case 'r':
		case 's':
		case 't':
		case 'u':
		case 'v':
		case 'w':
		case 'x':
		case 'y':
		case 'z': goto yy9;
		default: goto yy2;
	}
yy5:
	yych = *(U->marker = ++U->buffer);
	switch (yych) {
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
		case ':':
		case 'A':
		case 'B':
		case 'C':
		case 'D':
		case 'E':
		case 'F':
		case 'G':
		case 'H':
		case 'I':
		case 'J':
		case 'K':
		case 'L':
		case 'M':
		case 'N':
		case 'O':
		case 'P':
		case 'Q':
		case 'R':
		case 'S':
		case 'T':
		case 'U':
		case 'V':
		case 'W':
		case 'X':
		case 'Y':
		case 'Z':
		case 'a':
		case 'b':
		case 'c':
		case 'd':
		case 'e':
		case 'f':
		case 'g':
		case 'h':
		case 'i':
		case 'j':
		case 'k':
		case 'l':
		case 'm':
		case 'n':
		case 'o':
		case 'p':
		case 'q':
		case 'r':
		case 's':
		case 't':
		case 'u':
		case 'v':
		case 'w':
		case 'x':
		case 'z': goto yy9;
		case 'y': goto yy12;
		default: goto yy2;
	}
yy6:
	yych = *(U->marker = ++U->buffer);
	switch (yych) {
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
		case ':':
		case 'A':
		case 'B':
		case 'C':
		case 'D':
		case 'E':
		case 'F':
		case 'G':
		case 'H':
		case 'I':
		case 'J':
		case 'K':
		case 'L':
		case 'M':
		case 'N':
		case 'O':
		case 'P':
		case 'Q':
		case 'R':
		case 'S':
		case 'T':
		case 'U':
		case 'V':
		case 'W':
		case 'X':
		case 'Y':
		case 'Z':
		case 'a':
		case 'b':
		case 'c':
		case 'd':
		case 'e':
		case 'f':
		case 'g':
		case 'h':
		case 'i':
		case 'j':
		case 'k':
		case 'l':
		case 'm':
		case 'n':
		case 'o':
		case 'p':
		case 'q':
		case 's':
		case 't':
		case 'u':
		case 'v':
		case 'w':
		case 'x':
		case 'y':
		case 'z': goto yy9;
		case 'r': goto yy13;
		default: goto yy2;
	}
yy7:
	yych = *(U->marker = ++U->buffer);
	switch (yych) {
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
		case ':':
		case 'A':
		case 'B':
		case 'C':
		case 'D':
		case 'E':
		case 'F':
		case 'G':
		case 'H':
		case 'I':
		case 'J':
		case 'K':
		case 'L':
		case 'M':
		case 'N':
		case 'O':
		case 'P':
		case 'Q':
		case 'R':
		case 'S':
		case 'T':
		case 'U':
		case 'V':
		case 'W':
		case 'X':
		case 'Y':
		case 'Z':
		case 'a':
		case 'b':
		case 'c':
		case 'd':
		case 'e':
		case 'f':
		case 'g':
		case 'h':
		case 'i':
		case 'j':
		case 'k':
		case 'l':
		case 'm':
		case 'n':
		case 'p':
		case 'q':
		case 'r':
		case 's':
		case 't':
		case 'u':
		case 'v':
		case 'w':
		case 'x':
		case 'y':
		case 'z': goto yy9;
		case 'o': goto yy14;
		default: goto yy2;
	}
yy8:
	yych = *++U->buffer;
yy9:
	switch (yych) {
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
		case 'A':
		case 'B':
		case 'C':
		case 'D':
		case 'E':
		case 'F':
		case 'G':
		case 'H':
		case 'I':
		case 'J':
		case 'K':
		case 'L':
		case 'M':
		case 'N':
		case 'O':
		case 'P':
		case 'Q':
		case 'R':
		case 'S':
		case 'T':
		case 'U':
		case 'V':
		case 'W':
		case 'X':
		case 'Y':
		case 'Z':
		case 'a':
		case 'b':
		case 'c':
		case 'd':
		case 'e':
		case 'f':
		case 'g':
		case 'h':
		case 'i':
		case 'j':
		case 'k':
		case 'l':
		case 'm':
		case 'n':
		case 'o':
		case 'p':
		case 'q':
		case 'r':
		case 's':
		case 't':
		case 'u':
		case 'v':
		case 'w':
		case 'x':
		case 'y':
		case 'z': goto yy8;
		case ':': goto yy11;
		default: goto yy10;
	}
yy10:
	U->buffer = U->marker;
	goto yy2;
yy11:
	yych = *++U->buffer;
	switch (yych) {
		case '/': goto yy15;
		default: goto yy10;
	}
yy12:
	yych = *++U->buffer;
	switch (yych) {
		case 's': goto yy16;
		default: goto yy9;
	}
yy13:
	yych = *++U->buffer;
	switch (yych) {
		case 'a': goto yy17;
		default: goto yy9;
	}
yy14:
	yych = *++U->buffer;
	switch (yych) {
		case 's': goto yy18;
		default: goto yy9;
	}
yy15:
	yych = *++U->buffer;
	switch (yych) {
		case '/': goto yy19;
		default: goto yy10;
	}
yy16:
	yych = *++U->buffer;
	switch (yych) {
		case 'q': goto yy20;
		default: goto yy9;
	}
yy17:
	yych = *++U->buffer;
	switch (yych) {
		case 'c': goto yy21;
		default: goto yy9;
	}
yy18:
	yych = *++U->buffer;
	switch (yych) {
		case 't': goto yy22;
		default: goto yy9;
	}
yy19:
	++U->buffer;
	{
                SET_PROTOCOL(UNKNOWN_PORT);
         }
yy20:
	yych = *++U->buffer;
	switch (yych) {
		case 'l': goto yy23;
		default: goto yy9;
	}
yy21:
	yych = *++U->buffer;
	switch (yych) {
		case 'l': goto yy24;
		default: goto yy9;
	}
yy22:
	yych = *++U->buffer;
	switch (yych) {
		case 'g': goto yy25;
		default: goto yy9;
	}
yy23:
	yych = *++U->buffer;
	switch (yych) {
		case ':': goto yy26;
		default: goto yy9;
	}
yy24:
	yych = *++U->buffer;
	switch (yych) {
		case 'e': goto yy27;
		default: goto yy9;
	}
yy25:
	yych = *++U->buffer;
	switch (yych) {
		case 'r': goto yy28;
		default: goto yy9;
	}
yy26:
	yych = *++U->buffer;
	switch (yych) {
		case '/': goto yy29;
		default: goto yy10;
	}
yy27:
	yych = *++U->buffer;
	switch (yych) {
		case ':': goto yy30;
		default: goto yy9;
	}
yy28:
	yych = *++U->buffer;
	switch (yych) {
		case 'e': goto yy31;
		default: goto yy9;
	}
yy29:
	yych = *++U->buffer;
	switch (yych) {
		case '/': goto yy32;
		default: goto yy10;
	}
yy30:
	yych = *++U->buffer;
	switch (yych) {
		case '/': goto yy33;
		default: goto yy10;
	}
yy31:
	yych = *++U->buffer;
	switch (yych) {
		case 's': goto yy34;
		default: goto yy9;
	}
yy32:
	++U->buffer;
	{
                SET_PROTOCOL(MYSQL_DEFAULT_PORT);
         }
yy33:
	yych = *++U->buffer;
	switch (yych) {
		case '/': goto yy35;
		default: goto yy10;
	}
yy34:
	yych = *++U->buffer;
	switch (yych) {
		case 'q': goto yy36;
		default: goto yy9;
	}
yy35:
	++U->buffer;
	{
                SET_PROTOCOL(ORACLE_DEFAULT_PORT);
         }
yy36:
	yych = *++U->buffer;
	switch (yych) {
		case 'l': goto yy37;
		default: goto yy9;
	}
yy37:
	yych = *++U->buffer;
	switch (yych) {
		case ':': goto yy38;
		default: goto yy9;
	}
yy38:
	yych = *++U->buffer;
	switch (yych) {
		case '/': goto yy39;
		default: goto yy10;
	}
yy39:
	yych = *++U->buffer;
	switch (yych) {
		case '/': goto yy40;
		default: goto yy10;
	}
yy40:
	++U->buffer;
	{
                SET_PROTOCOL(POSTGRESQL_DEFAULT_PORT);
         }
}

authority:
	if (YYCURSOR >= YYLIMIT)
		return true;
	YYTOKEN = YYCURSOR;
	
{
	unsigned char yych;
	unsigned int yyaccept = 0;
	yych = *U->buffer;
	switch (yych) {
		case 0x00:
		case 0x01:
		case 0x02:
		case 0x03:
		case 0x04:
		case 0x05:
		case 0x06:
		case 0x07:
		case 0x08:
		case '\v':
		case '\f':
		case 0x0E:
		case 0x0F:
		case 0x10:
		case 0x11:
		case 0x12:
		case 0x13:
		case 0x14:
		case 0x15:
		case 0x16:
		case 0x17:
		case 0x18:
		case 0x19:
		case 0x1A:
		case 0x1B:
		case 0x1C:
		case 0x1D:
		case 0x1E:
		case 0x1F:
		case '@':
		case ']': goto yy42;
		case '\t':
		case '\n':
		case '\r': goto yy44;
		case ' ': goto yy46;
		case '-':
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
		case 'A':
		case 'B':
		case 'C':
		case 'D':
		case 'E':
		case 'F':
		case 'G':
		case 'H':
		case 'I':
		case 'J':
		case 'K':
		case 'L':
		case 'M':
		case 'N':
		case 'O':
		case 'P':
		case 'Q':
		case 'R':
		case 'S':
		case 'T':
		case 'U':
		case 'V':
		case 'W':
		case 'X':
		case 'Y':
		case 'Z':
		case 'a':
		case 'b':
		case 'c':
		case 'd':
		case 'e':
		case 'f':
		case 'g':
		case 'h':
		case 'i':
		case 'j':
		case 'k':
		case 'l':
		case 'm':
		case 'n':
		case 'o':
		case 'p':
		case 'q':
		case 'r':
		case 's':
		case 't':
		case 'u':
		case 'v':
		case 'w':
		case 'x':
		case 'y':
		case 'z': goto yy48;
		case '/': goto yy50;
		case ':': goto yy52;
		case '[': goto yy53;
		default: goto yy47;
	}
yy42:
	++U->buffer;
yy43:
	{
                return true;
         }
yy44:
	++U->buffer;
yy45:
	{
                goto authority;
         }
yy46:
	yyaccept = 0;
	yych = *(U->marker = ++U->buffer);
	switch (yych) {
		case 0x00:
		case 0x01:
		case 0x02:
		case 0x03:
		case 0x04:
		case 0x05:
		case 0x06:
		case 0x07:
		case 0x08:
		case '\t':
		case '\n':
		case '\v':
		case '\f':
		case '\r':
		case 0x0E:
		case 0x0F:
		case 0x10:
		case 0x11:
		case 0x12:
		case 0x13:
		case 0x14:
		case 0x15:
		case 0x16:
		case 0x17:
		case 0x18:
		case 0x19:
		case 0x1A:
		case 0x1B:
		case 0x1C:
		case 0x1D:
		case 0x1E:
		case 0x1F:
		case '[':
		case ']': goto yy45;
		default: goto yy55;
	}
yy47:
	yyaccept = 1;
	yych = *(U->marker = ++U->buffer);
	switch (yych) {
		case 0x00:
		case 0x01:
		case 0x02:
		case 0x03:
		case 0x04:
		case 0x05:
		case 0x06:
		case 0x07:
		case 0x08:
		case '\t':
		case '\n':
		case '\v':
		case '\f':
		case '\r':
		case 0x0E:
		case 0x0F:
		case 0x10:
		case 0x11:
		case 0x12:
		case 0x13:
		case 0x14:
		case 0x15:
		case 0x16:
		case 0x17:
		case 0x18:
		case 0x19:
		case 0x1A:
		case 0x1B:
		case 0x1C:
		case 0x1D:
		case 0x1E:
		case 0x1F:
		case '[':
		case ']': goto yy43;
		default: goto yy55;
	}
yy48:
	yyaccept = 2;
	yych = *(U->marker = ++U->buffer);
	switch (yych) {
		case 0x00:
		case 0x01:
		case 0x02:
		case 0x03:
		case 0x04:
		case 0x05:
		case 0x06:
		case 0x07:
		case 0x08:
		case '\t':
		case '\n':
		case '\v':
		case '\f':
		case '\r':
		case 0x0E:
		case 0x0F:
		case 0x10:
		case 0x11:
		case 0x12:
		case 0x13:
		case 0x14:
		case 0x15:
		case 0x16:
		case 0x17:
		case 0x18:
		case 0x19:
		case 0x1A:
		case 0x1B:
		case 0x1C:
		case 0x1D:
		case 0x1E:
		case 0x1F:
		case '[':
		case ']': goto yy49;
		case '-':
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
		case 'A':
		case 'B':
		case 'C':
		case 'D':
		case 'E':
		case 'F':
		case 'G':
		case 'H':
		case 'I':
		case 'J':
		case 'K':
		case 'L':
		case 'M':
		case 'N':
		case 'O':
		case 'P':
		case 'Q':
		case 'R':
		case 'S':
		case 'T':
		case 'U':
		case 'V':
		case 'W':
		case 'X':
		case 'Y':
		case 'Z':
		case 'a':
		case 'b':
		case 'c':
		case 'd':
		case 'e':
		case 'f':
		case 'g':
		case 'h':
		case 'i':
		case 'j':
		case 'k':
		case 'l':
		case 'm':
		case 'n':
		case 'o':
		case 'p':
		case 'q':
		case 'r':
		case 's':
		case 't':
		case 'u':
		case 'v':
		case 'w':
		case 'x':
		case 'y':
		case 'z': goto yy48;
		case '.': goto yy59;
		case '@': goto yy57;
		default: goto yy54;
	}
yy49:
	{
                U->host = Str_ndup(YYTOKEN, (int)(YYCURSOR - YYTOKEN));
                goto authority;
         }
yy50:
	yyaccept = 3;
	yych = *(U->marker = ++U->buffer);
	switch (yych) {
		case 0x00:
		case 0x01:
		case 0x02:
		case 0x03:
		case 0x04:
		case 0x05:
		case 0x06:
		case 0x07:
		case 0x08:
		case '\t':
		case '\n':
		case '\v':
		case '\f':
		case '\r':
		case 0x0E:
		case 0x0F:
		case 0x10:
		case 0x11:
		case 0x12:
		case 0x13:
		case 0x14:
		case 0x15:
		case 0x16:
		case 0x17:
		case 0x18:
		case 0x19:
		case 0x1A:
		case 0x1B:
		case 0x1C:
		case 0x1D:
		case 0x1E:
		case 0x1F: goto yy51;
		case ' ':
		case '#':
		case ';': goto yy54;
		case '?': goto yy60;
		case '@': goto yy62;
		case '[':
		case ']': goto yy63;
		default: goto yy50;
	}
yy51:
	{
                *YYCURSOR = 0;
                U->path = URL_unescape(YYTOKEN);
                return true;
         }
yy52:
	yyaccept = 1;
	yych = *(U->marker = ++U->buffer);
	switch (yych) {
		case 0x00:
		case 0x01:
		case 0x02:
		case 0x03:
		case 0x04:
		case 0x05:
		case 0x06:
		case 0x07:
		case 0x08:
		case '\t':
		case '\n':
		case '\v':
		case '\f':
		case '\r':
		case 0x0E:
		case 0x0F:
		case 0x10:
		case 0x11:
		case 0x12:
		case 0x13:
		case 0x14:
		case 0x15:
		case 0x16:
		case 0x17:
		case 0x18:
		case 0x19:
		case 0x1A:
		case 0x1B:
		case 0x1C:
		case 0x1D:
		case 0x1E:
		case 0x1F:
		case '[':
		case ']': goto yy43;
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9': goto yy65;
		default: goto yy55;
	}
yy53:
	yyaccept = 1;
	yych = *(U->marker = ++U->buffer);
	switch (yych) {
		case '%':
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
		case ':':
		case 'A':
		case 'B':
		case 'C':
		case 'D':
		case 'E':
		case 'F':
		case 'G':
		case 'H':
		case 'I':
		case 'J':
		case 'K':
		case 'L':
		case 'M':
		case 'N':
		case 'O':
		case 'P':
		case 'Q':
		case 'R':
		case 'S':
		case 'T':
		case 'U':
		case 'V':
		case 'W':
		case 'X':
		case 'Y':
		case 'Z':
		case 'a':
		case 'b':
		case 'c':
		case 'd':
		case 'e':
		case 'f':
		case 'g':
		case 'h':
		case 'i':
		case 'j':
		case 'k':
		case 'l':
		case 'm':
		case 'n':
		case 'o':
		case 'p':
		case 'q':
		case 'r':
		case 's':
		case 't':
		case 'u':
		case 'v':
		case 'w':
		case 'x':
		case 'y':
		case 'z': goto yy67;
		default: goto yy43;
	}
yy54:
	yych = *++U->buffer;
yy55:
	switch (yych) {
		case 0x00:
		case 0x01:
		case 0x02:
		case 0x03:
		case 0x04:
		case 0x05:
		case 0x06:
		case 0x07:
		case 0x08:
		case '\t':
		case '\n':
		case '\v':
		case '\f':
		case '\r':
		case 0x0E:
		case 0x0F:
		case 0x10:
		case 0x11:
		case 0x12:
		case 0x13:
		case 0x14:
		case 0x15:
		case 0x16:
		case 0x17:
		case 0x18:
		case 0x19:
		case 0x1A:
		case 0x1B:
		case 0x1C:
		case 0x1D:
		case 0x1E:
		case 0x1F:
		case '[':
		case ']': goto yy56;
		case '@': goto yy57;
		default: goto yy54;
	}
yy56:
	U->buffer = U->marker;
	switch (yyaccept) {
		case 0: goto yy45;
		case 1: goto yy43;
		case 2: goto yy49;
		case 3: goto yy51;
		case 4: goto yy61;
		default: goto yy66;
	}
yy57:
	++U->buffer;
yy58:
	{
                *(YYCURSOR - 1) = 0;
                U->user = YYTOKEN;
                char *p = strchr(U->user, ':');
                if (p) {
                        *(p++) = 0;
                        U->password = URL_unescape(p);
                }
                URL_unescape(U->user);
                goto authority;
         }
yy59:
	yych = *++U->buffer;
	switch (yych) {
		case 0x00:
		case 0x01:
		case 0x02:
		case 0x03:
		case 0x04:
		case 0x05:
		case 0x06:
		case 0x07:
		case 0x08:
		case '\t':
		case '\n':
		case '\v':
		case '\f':
		case '\r':
		case 0x0E:
		case 0x0F:
		case 0x10:
		case 0x11:
		case 0x12:
		case 0x13:
		case 0x14:
		case 0x15:
		case 0x16:
		case 0x17:
		case 0x18:
		case 0x19:
		case 0x1A:
		case 0x1B:
		case 0x1C:
		case 0x1D:
		case 0x1E:
		case 0x1F:
		case '[':
		case ']': goto yy56;
		case '-':
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
		case 'A':
		case 'B':
		case 'C':
		case 'D':
		case 'E':
		case 'F':
		case 'G':
		case 'H':
		case 'I':
		case 'J':
		case 'K':
		case 'L':
		case 'M':
		case 'N':
		case 'O':
		case 'P':
		case 'Q':
		case 'R':
		case 'S':
		case 'T':
		case 'U':
		case 'V':
		case 'W':
		case 'X':
		case 'Y':
		case 'Z':
		case 'a':
		case 'b':
		case 'c':
		case 'd':
		case 'e':
		case 'f':
		case 'g':
		case 'h':
		case 'i':
		case 'j':
		case 'k':
		case 'l':
		case 'm':
		case 'n':
		case 'o':
		case 'p':
		case 'q':
		case 'r':
		case 's':
		case 't':
		case 'u':
		case 'v':
		case 'w':
		case 'x':
		case 'y':
		case 'z': goto yy48;
		case '@': goto yy57;
		default: goto yy54;
	}
yy60:
	yyaccept = 4;
	yych = *(U->marker = ++U->buffer);
	switch (yych) {
		case 0x00:
		case 0x01:
		case 0x02:
		case 0x03:
		case 0x04:
		case 0x05:
		case 0x06:
		case 0x07:
		case 0x08:
		case '\t':
		case '\n':
		case '\v':
		case '\f':
		case '\r':
		case 0x0E:
		case 0x0F:
		case 0x10:
		case 0x11:
		case 0x12:
		case 0x13:
		case 0x14:
		case 0x15:
		case 0x16:
		case 0x17:
		case 0x18:
		case 0x19:
		case 0x1A:
		case 0x1B:
		case 0x1C:
		case 0x1D:
		case 0x1E:
		case 0x1F:
		case '[':
		case ']': goto yy61;
		default: goto yy55;
	}
yy61:
	{
                *(YYCURSOR-1) = 0;
                U->path = URL_unescape(YYTOKEN);
                goto query;
         }
yy62:
	yych = *++U->buffer;
	switch (yych) {
		case 0x00:
		case 0x01:
		case 0x02:
		case 0x03:
		case 0x04:
		case 0x05:
		case 0x06:
		case 0x07:
		case 0x08:
		case '\t':
		case '\n':
		case '\v':
		case '\f':
		case '\r':
		case 0x0E:
		case 0x0F:
		case 0x10:
		case 0x11:
		case 0x12:
		case 0x13:
		case 0x14:
		case 0x15:
		case 0x16:
		case 0x17:
		case 0x18:
		case 0x19:
		case 0x1A:
		case 0x1B:
		case 0x1C:
		case 0x1D:
		case 0x1E:
		case 0x1F:
		case ' ':
		case '#':
		case ';': goto yy58;
		default: goto yy64;
	}
yy63:
	yych = *++U->buffer;
yy64:
	switch (yych) {
		case 0x00:
		case 0x01:
		case 0x02:
		case 0x03:
		case 0x04:
		case 0x05:
		case 0x06:
		case 0x07:
		case 0x08:
		case '\t':
		case '\n':
		case '\v':
		case '\f':
		case '\r':
		case 0x0E:
		case 0x0F:
		case 0x10:
		case 0x11:
		case 0x12:
		case 0x13:
		case 0x14:
		case 0x15:
		case 0x16:
		case 0x17:
		case 0x18:
		case 0x19:
		case 0x1A:
		case 0x1B:
		case 0x1C:
		case 0x1D:
		case 0x1E:
		case 0x1F:
		case ' ':
		case '#':
		case ';': goto yy51;
		case '?': goto yy68;
		default: goto yy63;
	}
yy65:
	yyaccept = 5;
	yych = *(U->marker = ++U->buffer);
	switch (yych) {
		case 0x00:
		case 0x01:
		case 0x02:
		case 0x03:
		case 0x04:
		case 0x05:
		case 0x06:
		case 0x07:
		case 0x08:
		case '\t':
		case '\n':
		case '\v':
		case '\f':
		case '\r':
		case 0x0E:
		case 0x0F:
		case 0x10:
		case 0x11:
		case 0x12:
		case 0x13:
		case 0x14:
		case 0x15:
		case 0x16:
		case 0x17:
		case 0x18:
		case 0x19:
		case 0x1A:
		case 0x1B:
		case 0x1C:
		case 0x1D:
		case 0x1E:
		case 0x1F:
		case '[':
		case ']': goto yy66;
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9': goto yy65;
		case '@': goto yy57;
		default: goto yy54;
	}
yy66:
	{
                U->portStr = YYTOKEN + 1; // read past ':'
                U->port = Str_parseInt(U->portStr);
                goto authority;
         }
yy67:
	yych = *++U->buffer;
	switch (yych) {
		case '%':
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
		case ':':
		case 'A':
		case 'B':
		case 'C':
		case 'D':
		case 'E':
		case 'F':
		case 'G':
		case 'H':
		case 'I':
		case 'J':
		case 'K':
		case 'L':
		case 'M':
		case 'N':
		case 'O':
		case 'P':
		case 'Q':
		case 'R':
		case 'S':
		case 'T':
		case 'U':
		case 'V':
		case 'W':
		case 'X':
		case 'Y':
		case 'Z':
		case 'a':
		case 'b':
		case 'c':
		case 'd':
		case 'e':
		case 'f':
		case 'g':
		case 'h':
		case 'i':
		case 'j':
		case 'k':
		case 'l':
		case 'm':
		case 'n':
		case 'o':
		case 'p':
		case 'q':
		case 'r':
		case 's':
		case 't':
		case 'u':
		case 'v':
		case 'w':
		case 'x':
		case 'y':
		case 'z': goto yy67;
		case ']': goto yy69;
		default: goto yy56;
	}
yy68:
	++U->buffer;
	goto yy61;
yy69:
	++U->buffer;
	{
                U->ip6 = true;
                U->host = Str_ndup(YYTOKEN + 1, (int)(YYCURSOR - YYTOKEN - 2));
                goto authority;
         }
}

query:
        if (YYCURSOR >= YYLIMIT)
		return true;
	YYTOKEN =  YYCURSOR;
	
{
	unsigned char yych;
	yych = *U->buffer;
	switch (yych) {
		case 0x00:
		case 0x01:
		case 0x02:
		case 0x03:
		case 0x04:
		case 0x05:
		case 0x06:
		case 0x07:
		case 0x08:
		case '\t':
		case '\n':
		case '\v':
		case '\f':
		case '\r':
		case 0x0E:
		case 0x0F:
		case 0x10:
		case 0x11:
		case 0x12:
		case 0x13:
		case 0x14:
		case 0x15:
		case 0x16:
		case 0x17:
		case 0x18:
		case 0x19:
		case 0x1A:
		case 0x1B:
		case 0x1C:
		case 0x1D:
		case 0x1E:
		case 0x1F:
		case '#': goto yy71;
		default: goto yy72;
	}
yy71:
	++U->buffer;
	{
                return true;
         }
yy72:
	yych = *++U->buffer;
	switch (yych) {
		case 0x00:
		case 0x01:
		case 0x02:
		case 0x03:
		case 0x04:
		case 0x05:
		case 0x06:
		case 0x07:
		case 0x08:
		case '\t':
		case '\n':
		case '\v':
		case '\f':
		case '\r':
		case 0x0E:
		case 0x0F:
		case 0x10:
		case 0x11:
		case 0x12:
		case 0x13:
		case 0x14:
		case 0x15:
		case 0x16:
		case 0x17:
		case 0x18:
		case 0x19:
		case 0x1A:
		case 0x1B:
		case 0x1C:
		case 0x1D:
		case 0x1E:
		case 0x1F:
		case '#': goto yy73;
		default: goto yy72;
	}
yy73:
	{
                *YYCURSOR = 0;
                U->query = Str_ndup(YYTOKEN, (int)(YYCURSOR - YYTOKEN));
                YYCURSOR = YYTOKEN; // backtrack to start of query string after terminating it and
                goto params;
         }
}

params:
	if (YYCURSOR >= YYLIMIT)
		return true;
	YYTOKEN =  YYCURSOR;
	
{
	unsigned char yych;
	yych = *U->buffer;
	switch (yych) {
		case 0x00:
		case 0x01:
		case 0x02:
		case 0x03:
		case 0x04:
		case 0x05:
		case 0x06:
		case 0x07:
		case 0x08:
		case '\t':
		case '\n':
		case '\v':
		case '\f':
		case '\r':
		case 0x0E:
		case 0x0F:
		case 0x10:
		case 0x11:
		case 0x12:
		case 0x13:
		case 0x14:
		case 0x15:
		case 0x16:
		case 0x17:
		case 0x18:
		case 0x19:
		case 0x1A:
		case 0x1B:
		case 0x1C:
		case 0x1D:
		case 0x1E:
		case 0x1F:
		case ' ': goto yy75;
		case '=': goto yy78;
		default: goto yy77;
	}
yy75:
	++U->buffer;
yy76:
	{
                return true;
         }
yy77:
	yych = *(U->marker = ++U->buffer);
	if (yych <= ' ') goto yy76;
	goto yy81;
yy78:
	yych = *++U->buffer;
	switch (yych) {
		case 0x00:
		case 0x01:
		case 0x02:
		case 0x03:
		case 0x04:
		case 0x05:
		case 0x06:
		case 0x07:
		case 0x08:
		case '\t':
		case '\n':
		case '\v':
		case '\f':
		case '\r':
		case 0x0E:
		case 0x0F:
		case 0x10:
		case 0x11:
		case 0x12:
		case 0x13:
		case 0x14:
		case 0x15:
		case 0x16:
		case 0x17:
		case 0x18:
		case 0x19:
		case 0x1A:
		case 0x1B:
		case 0x1C:
		case 0x1D:
		case 0x1E:
		case 0x1F: goto yy79;
		case '&': goto yy84;
		default: goto yy78;
	}
yy79:
	{
                *YYTOKEN++ = 0;
                if (*(YYCURSOR - 1) == '&')
                        *(YYCURSOR - 1) = 0;
                if (! param) // format error
                        return true; 
                param->value = URL_unescape(YYTOKEN);
                goto params;
         }
yy80:
	yych = *++U->buffer;
yy81:
	switch (yych) {
		case 0x00:
		case 0x01:
		case 0x02:
		case 0x03:
		case 0x04:
		case 0x05:
		case 0x06:
		case 0x07:
		case 0x08:
		case '\t':
		case '\n':
		case '\v':
		case '\f':
		case '\r':
		case 0x0E:
		case 0x0F:
		case 0x10:
		case 0x11:
		case 0x12:
		case 0x13:
		case 0x14:
		case 0x15:
		case 0x16:
		case 0x17:
		case 0x18:
		case 0x19:
		case 0x1A:
		case 0x1B:
		case 0x1C:
		case 0x1D:
		case 0x1E:
		case 0x1F:
		case ' ': goto yy82;
		case '=': goto yy83;
		default: goto yy80;
	}
yy82:
	U->buffer = U->marker;
	goto yy76;
yy83:
	++U->buffer;
	U->buffer -= 1;
	{
                NEW(param);
                param->name = YYTOKEN;
                param->next = U->params;
                U->params = param;
                goto params;
         }
yy84:
	++U->buffer;
	goto yy79;
}

        return false;
}


static inline int _x2b(uchar_t *x) {
	register int b;
	b = ((x[0] >= 'A') ? ((x[0] & 0xdf) - 'A')+10 : (x[0] - '0'));
	b *= 16;
	b += (x[1] >= 'A' ? ((x[1] & 0xdf) - 'A')+10 : (x[1] - '0'));
	return b;
}


static inline uchar_t *_b2x(uchar_t b, uchar_t *x) {
        static const char _b2x_table[] = "0123456789ABCDEF";
        *x++ = '%';
        *x++ = _b2x_table[b >> 4];
        *x = _b2x_table[b & 0xf];
        return x;
}


static void _freeParams(param_t p) {
        for (param_t q = NULL; p; p = q) {
                q = p->next;
                FREE(p);
        }
}


static T _ctor(uchar_t *data) {
        T U;
	NEW(U);
	U->data = data;
	YYCURSOR = U->data;
	U->port = UNKNOWN_PORT;
	YYLIMIT = U->data + strlen(U->data);
	if (! _parseURL(U))
                URL_free(&U);
	return U;
}


/* -------------------------------------------------------- Public methods */


T URL_new(const char *url) {
        if (STR_UNDEF(url))
                return NULL;
        Exception_init();
        return _ctor((uchar_t*)Str_dup(url));
}


T URL_create(const char *url, ...) {
        if (STR_UNDEF(url))
                return NULL;
        Exception_init();
	va_list ap;
        va_start(ap, url);
	T U = _ctor((uchar_t*)Str_vcat(url, ap));
  	va_end(ap);
        return U;
}

void URL_free(T *U) {
	assert(U && *U);
        _freeParams((*U)->params);
        FREE((*U)->paramNames);
	FREE((*U)->toString);
	FREE((*U)->query);
	FREE((*U)->data);
	FREE((*U)->host);
	FREE(*U);
}


/* ------------------------------------------------------------ Properties */


const char *URL_getProtocol(T U) {
	assert(U);
	return U->protocol;
}


const char *URL_getUser(T U) {
	assert(U);
	return U->user;
}


const char *URL_getPassword(T U) {
	assert(U);
	return U->password;
}


const char *URL_getHost(T U) {
	assert(U);
	return U->host;
}


int URL_getPort(T U) {
	assert(U);
	return U->port;
}


const char *URL_getPath(T U) {
	assert(U);
	return U->path;
}


const char *URL_getQueryString(T U) {
	assert(U);
	return U->query;
}


const char **URL_getParameterNames(T U) {
        assert(U);
        if (U->params && (U->paramNames == NULL)) {
                param_t p;
                int i = 0, len = 0;
                for (p = U->params; p; p = p->next) len++;
                U->paramNames = ALLOC((len + 1) * sizeof *(U->paramNames));
                for (p = U->params; p; p = p->next)
                        U->paramNames[i++] = p->name;
                U->paramNames[i] = NULL;
        }
	return (const char **)U->paramNames;
}


const char *URL_getParameter(T U, const char *name) {
	assert(U);
        assert(name);
        for (param_t p = U->params; p; p = p->next) {
                if (Str_isByteEqual(p->name, name))
                        return p->value;
        }
        return NULL;
}


/* ---------------------------------------------------------------- Public */


const char *URL_toString(T U) {
	assert(U);
	if (! U->toString) {
                uchar_t port[11] = {};
                if (U->portStr) // port seen in URL
                        snprintf(port, 10, ":%d", U->port);
		U->toString = Str_cat("%s://%s%s%s%s%s%s%s%s%s%s%s",
                                      U->protocol,
                                      U->user ? U->user : "",
                                      U->password ? ":" : "",
                                      U->password ? U->password : "",
                                      U->user ? "@" : "",
                                      U->ip6 ? "[" : "",
                                      U->host ? U->host : "",
                                      U->ip6 ? "]" : "",
                                      port,
                                      U->path ? U->path : "",
                                      U->query ? "?" : "",
                                      U->query ? U->query : ""); 
	}
	return U->toString;
}


/* --------------------------------------------------------- Class methods */


char *URL_unescape(char *url) {
	if (STR_DEF(url)) {
                register int x, y;
                for (x = 0, y = 0; url[y]; x++, y++) {
                        if ((url[x] = url[y]) == '+')
                                url[x] = ' ';
                        else if (url[x] == '%') {
                                if (! (url[y + 1] && url[y + 2]))
                                        break;
                                url[x] = _x2b(url + y + 1);
                                y += 2;
                        }
                }
                url[x] = 0;
        }
	return url;
}


char *URL_escape(const char *url) {
        char *escaped = 0;
        if (url) {
                char *p;
                int i, n;
                for (n = i = 0; url[i]; i++) 
                        if (urlunsafe[(unsigned char)(url[i])]) 
                                n += 2;
                p = escaped = ALLOC(i + n + 1);
                for (; *url; url++, p++) {
                        if (urlunsafe[(unsigned char)(*p = *url)])
                                p = _b2x(*url, p);
                }
                *p = 0;
        }
        return escaped;
}

