--- /dev/null
+#define _POSIX_C_SOURCE 200809L
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+/*!max:re2c*/
+
+struct input {
+ /*!stags:re2c format = "const char *@@;\n"; */
+};
+
+static void init_input(struct input *in) {
+ /*!stags:re2c format = "in->@@ = 0;\n"; */
+}
+
+struct tls_uri {
+ char *scheme;
+ char *userinfo;
+ char *host;
+ char *port;
+ char *path;
+ char *query;
+ char *fragment;
+};
+
+#define YYCTYPE char
+#define YYPEEK() *cur
+#define YYSKIP() if (++cur > lim) return 3;
+#define YYBACKUP() mar = cur
+#define YYRESTORE() cur = mar
+#define YYSTAGP(p) p = cur
+#define YYSTAGN(p) p = 0
+
+static int parse_uri(const char *cur, struct tls_uri *uri) {
+ const char
+ *s1, *u1, *h1, *h3, *h5, *r1, *p1, *p3, *q1, *f1,
+ *s2, *u2, *h2, *h4, *h6, *r2, *p2, *p4, *q2, *f2;
+ long c;
+ const char *mar, *lim;
+ struct input inp;
+ struct input *in = &inp;
+ init_input(in);
+
+ lim = cur + strlen(cur);
+
+ c = 0;
+loop:
+/*!re2c
+ re2c:yyfill:enable = 0;
+ re2c:tags:expression = "in->@@";
+
+ end = "\x00";
+ eol = "\n";
+
+ alpha = [a-zA-Z];
+ digit = [0-9];
+ hexdigit = [0-9a-fA-F];
+ unreserved = alpha | digit | [-._~];
+ pct_encoded = "%" hexdigit{2};
+ sub_delims = [!$&'()*+,;=];
+ pchar = unreserved | pct_encoded | sub_delims | [:@];
+
+ scheme = @s1 alpha (alpha | digit | [-+.])* @s2;
+ userinfo = @u1 (unreserved | pct_encoded | sub_delims | ":")* @u2;
+ dec_octet
+ = digit
+ | [\x31-\x39] digit
+ | "1" digit{2}
+ | "2" [\x30-\x34] digit
+ | "25" [\x30-\x35];
+ ipv4address = dec_octet "." dec_octet "." dec_octet "." dec_octet;
+ h16 = hexdigit{1,4};
+ ls32 = h16 ":" h16 | ipv4address;
+ ipv6address
+ = (h16 ":"){6} ls32
+ | "::" (h16 ":"){5} ls32
+ | ( h16)? "::" (h16 ":"){4} ls32
+ | ((h16 ":"){0,1} h16)? "::" (h16 ":"){3} ls32
+ | ((h16 ":"){0,2} h16)? "::" (h16 ":"){2} ls32
+ | ((h16 ":"){0,3} h16)? "::" h16 ":" ls32
+ | ((h16 ":"){0,4} h16)? "::" ls32
+ | ((h16 ":"){0,5} h16)? "::" h16
+ | ((h16 ":"){0,6} h16)? "::";
+ ipvfuture = "v" hexdigit+ "." (unreserved | sub_delims | ":" )+;
+ ip_literal = "[" ( ipv6address | ipvfuture ) "]";
+ reg_name = (unreserved | pct_encoded | sub_delims)*;
+ host
+ = @h1 ip_literal @h2
+ | @h3 ipv4address @h4
+ | @h5 reg_name @h6;
+ port = @r1 digit* @r2;
+ authority = (userinfo "@")? host (":" port)?;
+ path_abempty = ("/" pchar*)*;
+ path_absolute = "/" (pchar+ ("/" pchar*)*)?;
+ path_rootless = pchar+ ("/" pchar*)*;
+ path_empty = "";
+ hier_part
+ = "//" authority @p1 path_abempty @p2
+ | @p3 (path_absolute | path_rootless | path_empty) @p4;
+ query = @q1 (pchar | [/?])* @q2;
+ fragment = @f1 (pchar | [/?])* @f2;
+ uri = scheme ":" hier_part ("?" query)? ("#" fragment)?;
+
+ * { return 1; }
+ end { return 0; }
+ eol { goto loop; }
+ uri {
+ uri->scheme = strndup(s1, (size_t)(s2 - s1));
+ if (u1) uri->userinfo = strndup(u1, (size_t)(u2 - u1));
+ if (h1) uri->host = strndup(h1, (size_t)(h2 - h1));
+ if (h3) uri->host = strndup(h3, (size_t)(h4 - h3));
+ if (h5) uri->host = strndup(h5, (size_t)(h6 - h5));
+ if (r1) uri->port = strndup(r1, (size_t)(r2 - r1));
+ if (p1) uri->path = strndup(p1, (size_t)(p2 - p1));
+ if (p3) uri->path = strndup(p3, (size_t)(p4 - p3));
+ if (q1) uri->query = strndup(q1, (size_t)(q2 - q1));
+ if (f1) uri->fragment = strndup(f1, (size_t)(f2 - f1));
+
+ return 0;
+ }
+*/
+}
+
+static int hex(char *s) {
+ int r = 0;
+ int i;
+ for (i = 0; i < 2; i++) {
+ r *= 16;
+ switch (s[i]) {
+ case '0': break;
+ case '1': r += 1; break;
+ case '2': r += 2; break;
+ case '3': r += 3; break;
+ case '4': r += 4; break;
+ case '5': r += 5; break;
+ case '6': r += 6; break;
+ case '7': r += 7; break;
+ case '8': r += 8; break;
+ case '9': r += 9; break;
+ case 'A':
+ case 'a': r += 11; break;
+ case 'B':
+ case 'b': r += 11; break;
+ case 'C':
+ case 'c': r += 12; break;
+ case 'D':
+ case 'd': r += 13; break;
+ case 'E':
+ case 'e': r += 14; break;
+ case 'F':
+ case 'f': r += 15; break;
+ default:
+ /* This is an error, but should have
+ been caught at the lexing stage */
+ break;
+ }
+ }
+ return r;
+}
+
+static void percent_decode(char *s) {
+ char *d = s;
+
+ if (!s) {
+ return;
+ }
+
+ while (*s) {
+ switch (*s) {
+ case '%':
+ if (*(s+1) && *(s+2)) {
+ *d++ = hex(s+1);
+ s += 3;
+ }
+ break;
+ default:
+ *d++ = *s++;
+ break;
+ }
+ }
+ *d = 0;
+}
+
+int tls_parse_uri(char *s, struct tls_uri *uri) {
+ int rv;
+
+ rv = parse_uri(s, uri);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (uri->port == NULL) {
+ if (!strcmp(uri->scheme, "http")) {
+ uri->port = strdup("80");
+ } else if (!strcmp(uri->scheme, "https")) {
+ uri->port = strdup("443");
+ } else if (!strcmp(uri->scheme, "ftp")) {
+ uri->port = strdup("21");
+ } else {
+ uri->port = strdup("0");
+ }
+ }
+
+ percent_decode(uri->host);
+ percent_decode(uri->path);
+ percent_decode(uri->query);
+
+
+ if (uri->path == NULL || uri->path[0] == 0) {
+ free(uri->path);
+ uri->path = strdup("/");
+ }
+
+ return rv;
+}
+
+void tls_free_uri(struct tls_uri *uri) {
+ free(uri->host);
+ free(uri->path);
+ free(uri->query);
+ free(uri->port);
+ free(uri->scheme);
+}