git.y1.nz

okra

C99 URI parser library.
download: http://git.y1.nz/archives/okra.tar.gz
README | Files | Log | Refs

base.c

      1 /* This file contains the core parser functionality, primarily */
      2 /* translated from the ABNF given in appendix A of [RFC3986]. */
      3 
      4 #include <stdlib.h> /* malloc/calloc */
      5 #include <assert.h> /* assert */
      6 #include <stdio.h> /* printf */
      7 #include <string.h> /* strcpy, strlen */
      8 
      9 #include "okra.h"
     10 
     11 /* parser-internal data shapes */
     12 typedef struct {
     13 	int start;
     14 	int end;
     15 } OkraStringPiece;
     16 
     17 typedef struct {
     18 	OkraStringPiece *value;
     19 	OkraStringPiece *userinfo;
     20 	OkraStringPiece *host;
     21 	OkraStringPiece *port;
     22 } OkraAuthorityComponent;
     23 
     24 typedef struct {
     25 	OkraStringPiece *value;
     26 
     27 	// TODO: this
     28 	// OkraVector /* OkraStringPiece */ segments;
     29 	// OkraStringPiece *segments[MAX_SEGMENT_COUNT];
     30 } OkraPathComponent;
     31 
     32 typedef struct {
     33 	/* reference to the original input buffer. */
     34 	const char *buffer;
     35 
     36 	/* data that is populated as the URI is constructed, */
     37 	/* all NULLable. */
     38 	OkraStringPiece *value;
     39 	OkraStringPiece *scheme;
     40 	OkraAuthorityComponent *authority;
     41 	OkraPathComponent *path;
     42 	OkraStringPiece *query;
     43 	OkraStringPiece *fragment;
     44 } OkraRawUri;
     45 
     46 typedef struct {
     47 	OkraAuthorityComponent *authority;
     48 	OkraPathComponent *path;
     49 } OkraRelativePart;
     50 
     51 #define DEF_SINGLE(name, cond)  \
     52 int \
     53 name(const char *s, int i) \
     54 { \
     55 	return (cond) ? i+1 : -1; \
     56 }
     57 
     58 #define RANGE(lo, c, hi)  (lo <= c && c <= hi)
     59 #define IS_DIGIT(c)  (c >= '0' && c <= '9')
     60 #define IS_ALPHA(c)  (RANGE('a', c, 'z') || RANGE('A', c, 'Z'))
     61 #define IS_HEXDIG(c)  (IS_DIGIT(c) || RANGE('a', c, 'f') || RANGE('A', c, 'F'))
     62 #define IS_GEN_DELIM(c)   ( \
     63 	s[i] == ':' || s[i] == '/' || s[i] == '?' || s[i] == '#' || \
     64 	s[i] == '[' || s[i] == ']' || s[i] == '@' \
     65 )
     66 #define IS_SUB_DELIM(c)  ( \
     67 	s[i] == '!' || s[i] == '$' || s[i] == '&' || s[i] == '\'' || \
     68 	s[i] == '(' || s[i] == ')' || s[i] == '*' || s[i] == '+' || \
     69 	s[i] == ',' || s[i] == ';' || s[i] == '=' \
     70 )
     71 #define IS_UNRESERVED(c)  ( \
     72 	(s[i] == '-' || s[i] == '.' || s[i] == '_' || s[i] == '~') || \
     73 	IS_ALPHA(s[i]) || IS_DIGIT(s[i]) \
     74 )
     75 #define IS_RESERVED(c)  (IS_GEN_DELIM(s[i]) || IS_SUB_DELIM(s[i]))
     76 
     77 int okra_parse_digit(const char *s, int i) { return IS_DIGIT(s[i]) ? i+1 : -1; }
     78 int okra_parse_alpha(const char *s, int i) { return IS_ALPHA(s[i]) ? i+1 : -1; }
     79 int okra_parse_hexdig(const char *s, int i) { return IS_HEXDIG(s[i]) ? i+1 : -1; }
     80 int okra_parse_gen_delim(const char *s, int i) {
     81 	return IS_GEN_DELIM(s[i]) ? i+1 : -1;
     82 }
     83 int okra_parse_sub_delim(const char *s, int i) {
     84 	return IS_SUB_DELIM(s[i]) ? i+1 : -1;
     85 }
     86 
     87 int okra_parse_unreserved(const char *s, int i) {
     88 	return IS_UNRESERVED(s[i]) ? i+1 : -1;
     89 }
     90 int okra_parse_reserved(const char *s, int i) {
     91 	return IS_RESERVED(s[i]) ? i+1 : -1;
     92 }
     93 int okra_parse_pct_encoded(const char *s, int i) {
     94 	return (
     95 		s[i] == '%' && IS_HEXDIG(s[i+1]) && IS_HEXDIG(s[i+2])
     96 	) ? i+3 : -1;
     97 }
     98 
     99 int
    100 okra_parse_pchar(const char *s, int i)
    101 {
    102 	int ret = okra_parse_unreserved(s, i);
    103 	if (ret == -1) ret = okra_parse_pct_encoded(s, i);
    104 	if (ret == -1) ret = okra_parse_sub_delim(s, i);
    105 	if (ret == -1) ret = (s[i] == ':' || s[i] == '@') ? i+1 : -1;
    106 	return ret;
    107 }
    108 
    109 OkraStringPiece *
    110 okra_parse_fragment(const char *s, int i)
    111 {
    112 	OkraStringPiece *ret = NULL;
    113 
    114 	int current = i, next;
    115 	for (;;) {
    116 		next = okra_parse_pchar(s, current);
    117 		if (next == -1)
    118 			next = (s[current] == '/' || s[current] == '?') ?
    119 				current+1 :
    120 				-1;
    121 
    122 		if (next == -1) break;
    123 		current = next;
    124 	}
    125 
    126 	ret = malloc(sizeof(OkraStringPiece));
    127 	ret->start = i;
    128 	ret->end = current;
    129 	return ret;
    130 }
    131 
    132 OkraStringPiece *
    133 okra_parse_query(const char *s, int i)
    134 {
    135 	return okra_parse_fragment(s, i);
    136 }
    137 
    138 int
    139 okra_parse_segment_nz_nc(const char *s, int i)
    140 {
    141 	char c;
    142 	int current = i, next;
    143 	for (;;) {
    144 		next = okra_parse_unreserved(s, current);
    145 		if (next == -1) next = okra_parse_sub_delim(s, current);
    146 		if (next == -1) next = s[current] == '@' ? current+1 : -1;
    147 		if (next == -1) next = okra_parse_pct_encoded(s, current);
    148 
    149 		if (next == -1) break;
    150 		current = next;
    151 	}
    152 	if (current == i) return -1;
    153 	return current;
    154 }
    155 
    156 int
    157 okra_parse_segment_nz(const char *s, int i)
    158 {
    159 	int current = i, next;
    160 	for (;;) {
    161 		next = okra_parse_pchar(s, current);
    162 
    163 		if (next == -1) break;
    164 		current = next;
    165 	}
    166 	if (current == i) return -1;
    167 	return current;
    168 }
    169 
    170 int
    171 okra_parse_segment(const char *s, int i)
    172 {
    173 	int current = i, next;
    174 	for (;;) {
    175 		next = okra_parse_pchar(s, current);
    176 
    177 		if (next == -1) break;
    178 		current = next;
    179 	}
    180 	return current;
    181 }
    182 
    183 OkraPathComponent *
    184 okra_parse_path_empty(const char *s, int i)
    185 {
    186 	OkraPathComponent *ret = NULL;
    187 	int next = okra_parse_pchar(s, i);
    188 	if (next != -1) return NULL;
    189 
    190 	ret = calloc(1, sizeof(OkraPathComponent));
    191 	ret->value = calloc(1, sizeof(OkraStringPiece));
    192 	ret->value->start = ret->value->end = i;
    193 	return ret;
    194 }
    195 
    196 OkraPathComponent *
    197 okra_parse_path_abempty(const char *s, int i)
    198 {
    199 	OkraPathComponent *ret = calloc(1, sizeof(OkraPathComponent));
    200 	int current = i, next;
    201 
    202 	ret->value = calloc(1, sizeof(OkraStringPiece));
    203 	ret->value->start = i;
    204 
    205 	for (;;) {
    206 		next = s[current] == '/' ? current+1 : -1;
    207 		// TODO convert okra_parse_segment to return OkraStringPiece,
    208 		// TODO insert them into ret->segments
    209 		if (next != -1) next = okra_parse_segment(s, next);
    210 
    211 		if (next == -1) break;
    212 		current = next;
    213 	}
    214 	ret->value->end = current;
    215 	return ret;
    216 }
    217 
    218 OkraPathComponent *
    219 okra_parse_path_rootless(const char *s, int i)
    220 {
    221 	OkraPathComponent *ret = NULL;
    222 	int current = i, next;
    223 	next = okra_parse_segment_nz(s, current);
    224 	if (next != -1) ret = okra_parse_path_abempty(s, next);
    225 	if (ret == NULL) return NULL;
    226 
    227 	// TODO pass the segment_nz to okra_parse_path_abempty...
    228 	ret->value->start = i;
    229 
    230 	return ret;
    231 }
    232 
    233 OkraPathComponent *
    234 okra_parse_path_noscheme(const char *s, int i)
    235 {
    236 	OkraPathComponent *ret = NULL;
    237 	int current = i, next;
    238 	next = okra_parse_segment_nz_nc(s, current);
    239 	if (next != -1) ret = okra_parse_path_abempty(s, next);
    240 	if (ret == NULL) return NULL;
    241 	
    242 	ret->value->start = i;
    243 	return ret;
    244 }
    245 
    246 OkraPathComponent *
    247 okra_parse_path_absolute(const char *s, int i)
    248 {
    249 	OkraPathComponent *ret = NULL;
    250 	int current = i, next;
    251 
    252 	if (s[current] != '/') return NULL;
    253 	current++;
    254 
    255 	ret = okra_parse_path_rootless(s, current);
    256 	if (ret != NULL) {
    257 		ret->value->start = i;
    258 		return ret;
    259 	}
    260 
    261 	ret = calloc(1, sizeof(OkraPathComponent));
    262 	ret->value = calloc(1, sizeof(OkraStringPiece));
    263 	ret->value->start = i;
    264 	ret->value->end = i + 1;
    265 	return ret;
    266 }
    267 
    268 OkraPathComponent *
    269 okra_parse_path(const char *s, int i)
    270 {
    271 	OkraPathComponent *ret = NULL, *other;
    272 	ret = okra_parse_path_abempty(s, i);
    273 	if (ret == NULL) ret = okra_parse_path_absolute(s, i);
    274 	if (ret == NULL) ret = okra_parse_path_noscheme(s, i);
    275 	if (ret == NULL) ret = okra_parse_path_rootless(s, i);
    276 	if (ret == NULL) ret = okra_parse_path_empty(s, i);
    277 	return ret;
    278 }
    279 
    280 int
    281 okra_parse_reg_name(const char *s, int i)
    282 {
    283 	int current = i, next;
    284 	for (;;) {
    285 		next = okra_parse_unreserved(s, current);
    286 		if (next == -1) next = okra_parse_sub_delim(s, current);
    287 		if (next == -1) next = okra_parse_pct_encoded(s, current);
    288 		if (next == -1) break;
    289 		current = next;
    290 	}
    291 	return current;
    292 }
    293 
    294 int
    295 okra_parse_dec_octet(const char *s, int i)
    296 {
    297 	if (IS_DIGIT(s[i]) && !IS_DIGIT(s[i+1])) return i+1;
    298 	if (s[i] >= '1' && s[i] <= '9' && IS_DIGIT(s[i+1])) return i+2;
    299 	if (
    300 		(s[i] == '1' && IS_DIGIT(s[i+1]) && IS_DIGIT(s[i+2])) ||
    301 		(s[i] == '2' && RANGE('0', s[i+1], '4') && IS_DIGIT(s[i+2])) ||
    302 		(s[i] == '2' && s[i+1] == '5' && RANGE('0', s[i+2], '5'))
    303 	) return i+3;
    304 	return -1;
    305 }
    306 
    307 int
    308 okra_parse_ipv4_address(const char *s, int i)
    309 {
    310 	int next = i;
    311 	next = okra_parse_dec_octet(s, next);
    312 	if (next != -1) next = s[next] == '.' ? next+1 : -1;
    313 	if (next != -1) next = okra_parse_dec_octet(s, next);
    314 	if (next != -1) next = s[next] == '.' ? next+1 : -1;
    315 	if (next != -1) next = okra_parse_dec_octet(s, next);
    316 	if (next != -1) next = s[next] == '.' ? next+1 : -1;
    317 	if (next != -1) next = okra_parse_dec_octet(s, next);
    318 	return next;
    319 }
    320 
    321 int
    322 okra_parse_h16(const char *s, int i)
    323 {
    324 	int matches = 0;
    325 	int current = i, next;
    326 	for (;;) {
    327 		next = okra_parse_hexdig(s, current);
    328 		if (next == -1) break;
    329 		current = next;
    330 		matches++;
    331 		if (matches == 4) break;
    332 	}
    333 	if (current == i) return -1;
    334 	return current;
    335 }
    336 
    337 int
    338 okra_parse_ls32(const char *s, int i)
    339 {
    340 	int next = okra_parse_h16(s, i);
    341 	if (next != -1) next = s[next] == ':' ? next+1 : -1;
    342 	if (next != -1) next = okra_parse_h16(s, next);
    343 	if (next != -1) return next;
    344 
    345 	return okra_parse_ipv4_address(s, i);
    346 }
    347 
    348 int
    349 okra_parse_ipv6_address(const char *s, int i)
    350 {
    351 	return -1; // TODO this rule is really ugly :(
    352 }
    353 
    354 int
    355 okra_parse_ipvfuture(const char *s, int i)
    356 {
    357 	if (s[i] != 'v') return -1;
    358 	i++;
    359 	if (okra_parse_hexdig(s, i) == -1) return -1;
    360 	i++;
    361 	if (s[i] != '.') return -1;
    362 	i++;
    363 	
    364 	int current = i, next;
    365 	for (;;) {
    366 		next = okra_parse_unreserved(s, next);
    367 		if (next == -1) next = okra_parse_sub_delim(s, next);
    368 		if (next == -1) next = s[next] == ':' ? next+1 : -1;
    369 		if (next == -1) break;
    370 		current = next;
    371 	}
    372 	if (current == i) return -1;
    373 	return current;
    374 }
    375 
    376 int
    377 okra_parse_ip_literal(const char *s, int i)
    378 {
    379 	int next;
    380 	if (s[i] != '[') return -1;
    381 	i++;
    382 
    383 	next = okra_parse_ipv6_address(s, i);
    384 	if (next == -1) next = okra_parse_ipvfuture(s, i);
    385 	i = next;
    386 	if (s[i] != ']') return -1;
    387 	i++;
    388 	return i;
    389 }
    390 
    391 OkraStringPiece *
    392 okra_parse_port(const char *s, int i)
    393 {
    394 	OkraStringPiece *ret = NULL;
    395 	int current = i, next;
    396 	for (;;) {
    397 		next = okra_parse_digit(s, current);
    398 		if (next == -1) break;
    399 		current = next;
    400 	}
    401 	ret = malloc(sizeof(OkraStringPiece));
    402 	ret->start = i;
    403 	ret->end = current;
    404 	return ret;
    405 }
    406 
    407 OkraStringPiece *
    408 okra_parse_host(const char *s, int i)
    409 {
    410 	OkraStringPiece *ret = NULL;
    411 	int next = okra_parse_ip_literal(s, i);
    412 	if (next == -1) next = okra_parse_ipv4_address(s, i);
    413 	if (next == -1) next = okra_parse_reg_name(s, i);
    414 
    415 	if (next != -1) {
    416 		ret = malloc(sizeof(OkraStringPiece));
    417 		ret->start = i;
    418 		ret->end = next;
    419 	}
    420 	return ret;
    421 }
    422 
    423 OkraStringPiece *
    424 okra_parse_userinfo(const char *s, int i)
    425 {
    426 	OkraStringPiece *ret = NULL;
    427 	int current = i, next;
    428 	for (;;) {
    429 		next = okra_parse_unreserved(s, current);
    430 		if (next == -1) next = okra_parse_pct_encoded(s, current);
    431 		if (next == -1) next = okra_parse_sub_delim(s, current);
    432 		if (next == -1) next = s[current] == ':' ? current+1 : -1;
    433 		if (next == -1) break;
    434 		current = next;
    435 	}
    436 	if (current == -1) return NULL;
    437 
    438 	ret = malloc(sizeof(OkraStringPiece));
    439 	ret->start = i;
    440 	ret->end = current;
    441 	return ret;
    442 }
    443 
    444 OkraAuthorityComponent *
    445 okra_parse_authority(const char *s, int i)
    446 {
    447 	int i0 = i, next;
    448 	OkraAuthorityComponent *ret = NULL;
    449 	OkraStringPiece *userinfo = NULL;
    450 	OkraStringPiece *host = NULL;
    451 	OkraStringPiece *port = NULL;
    452 	OkraStringPiece *value = NULL;
    453 
    454 	// TODO clean this logic up...
    455 	userinfo = okra_parse_userinfo(s, i);
    456 	if (userinfo != NULL) {
    457 		next = userinfo->end;
    458 		next = s[next] == '@' ? next+1 : -1;
    459 		if (next != -1) i = next;
    460 		else {
    461 			free(userinfo);
    462 			userinfo = NULL;
    463 		}
    464 	}
    465 	
    466 	host = okra_parse_host(s, i);
    467 	if (host == NULL) {
    468 		if (userinfo != NULL) free(userinfo);
    469 		return NULL;
    470 	}
    471 	i = host->end;
    472 
    473 	if (s[i] == ':') {
    474 		next = i+1;
    475 		port = okra_parse_port(s, next);
    476 		if (port != NULL) i = port->end;
    477 	}
    478 
    479 	value = malloc(sizeof(OkraStringPiece));
    480 	value->start = i0;
    481 	value->end = i;
    482 
    483 	ret = malloc(sizeof(OkraAuthorityComponent));
    484 	ret->userinfo = userinfo;
    485 	ret->host = host;
    486 	ret->port = port;
    487 	ret->value = value;
    488 	return ret;
    489 }
    490 
    491 OkraStringPiece *
    492 okra_parse_scheme(const char *s, int i)
    493 {
    494 	OkraStringPiece *ret = NULL;
    495 	int current = i, next = okra_parse_alpha(s, current);
    496 	if (next == -1) return NULL;
    497 	current = next;
    498 
    499 	for (;;) {
    500 		next = okra_parse_alpha(s, current);
    501 		if (next == -1) next = okra_parse_digit(s, current);
    502 		if (next == -1) next = (s[current] == '+' || s[current] == '-' || s[current] == '.') ? current+1 : -1;
    503 		if (next == -1) break;
    504 		current = next;
    505 	}
    506 
    507 	ret = malloc(sizeof(OkraStringPiece));
    508 	ret->start = i;
    509 	ret->end = current;
    510 	return ret;
    511 }
    512 
    513 OkraRelativePart *
    514 okra_parse_relative_part(const char *s, int i)
    515 {
    516 	OkraRelativePart *ret = NULL;
    517 	OkraPathComponent *path = NULL;
    518 	OkraAuthorityComponent *authority = NULL;
    519 	int current = i, next;
    520 
    521 	/* "//" authority path-abempty */
    522 	next = (s[current] == '/' && s[current+1] == '/') ? current+2 : -1;
    523 	if (next != -1) {
    524 		authority = okra_parse_authority(s, next);
    525 		next = authority == NULL ? -1 : authority->value->end;
    526 	}
    527 	if (next != -1) path = okra_parse_path_abempty(s, next);
    528 	if (path != NULL) {
    529 		ret = malloc(sizeof(OkraRelativePart));
    530 		ret->authority = authority;
    531 		ret->path = path;
    532 		return ret;
    533 	} else if (authority != NULL) free(authority); /* clean up */
    534 
    535 	path = okra_parse_path_absolute(s, i);
    536 	if (path == NULL) path = okra_parse_path_noscheme(s, i);
    537 	if (path == NULL) path = okra_parse_path_empty(s, i);
    538 	if (path == NULL) return NULL;
    539 
    540 	ret = malloc(sizeof(OkraRelativePart));
    541 	ret->authority = authority;
    542 	ret->path = path;
    543 	assert(ret->path != NULL);
    544 	return ret;
    545 }
    546 
    547 #define SAFE_FREE(name)  if (name != NULL) free(name);
    548 void
    549 okra_free_raw_uri(OkraRawUri *raw)
    550 {
    551 	SAFE_FREE(raw->scheme);
    552 	if (raw->authority != NULL) {
    553 		SAFE_FREE(raw->authority->value);
    554 		SAFE_FREE(raw->authority->userinfo);
    555 		SAFE_FREE(raw->authority->host);
    556 		SAFE_FREE(raw->authority->port);
    557 		SAFE_FREE(raw->authority);
    558 	}
    559 	if (raw->path != NULL) {
    560 		SAFE_FREE(raw->path->value);
    561 		SAFE_FREE(raw->path);
    562 		// TODO list of segments
    563 	}
    564 	if (raw->query != NULL) SAFE_FREE(raw->query);
    565 	if (raw->fragment != NULL) SAFE_FREE(raw->fragment);
    566 	SAFE_FREE(raw->value);
    567 	free(raw);
    568 
    569 }
    570 
    571 OkraRawUri *
    572 okra_parse_relative_ref(const char *s, int i)
    573 {
    574 	int i0 = i;
    575 	OkraRawUri *ret = NULL;
    576 	OkraStringPiece *query = NULL;
    577 	OkraStringPiece *fragment = NULL;
    578 	OkraRelativePart *rel_part = NULL;
    579 
    580 	rel_part = okra_parse_relative_part(s, i);
    581 	if (rel_part == NULL) return NULL;
    582 	i = rel_part->path->value->end; /* end of path is always end of rel_path. */
    583 
    584 	int next = i;
    585 	next = s[next] == '?' ? next+1 : -1;
    586 	if (next != -1) {
    587 		query = okra_parse_query(s, next);
    588 		next = query == NULL ? -1 : query->end;
    589 	}
    590 	if (next != -1) i = next; // TODO clean this up.. yuck...
    591 	next = i;
    592 
    593 	next = s[next] == '#' ? next+1 : -1;
    594 	if (next != -1) {
    595 		fragment = okra_parse_fragment(s, next);
    596 		next = fragment == NULL ? -1 : fragment->end;
    597 	}
    598 	if (next != -1) i = next;
    599 
    600 	ret = calloc(1, sizeof(OkraRawUri));
    601 	ret->buffer = s;
    602 	ret->authority = rel_part->authority;
    603 	ret->path = rel_part->path;
    604 	free(rel_part);
    605 	ret->query = query;
    606 	ret->fragment = fragment;
    607 	ret->value = malloc(sizeof(OkraStringPiece));
    608 	ret->value->start = i0;
    609 	ret->value->end = i;
    610 	return ret;
    611 }
    612 
    613 OkraRelativePart *
    614 okra_parse_hier_part(const char *s, int i)
    615 {
    616 	OkraRelativePart *ret = NULL;
    617 	OkraPathComponent *path = NULL;
    618 	OkraAuthorityComponent *authority = NULL;
    619 	int current = i, next;
    620 
    621 	/* "//" authority path-abempty */
    622 	next = (s[current] == '/' && s[current+1] == '/') ? current+2 : -1;
    623 	if (next != -1) {
    624 		authority = okra_parse_authority(s, next);
    625 		next = authority == NULL ? -1 : authority->value->end;
    626 	}
    627 	if (next != -1) path = okra_parse_path_abempty(s, next);
    628 	if (path != NULL) {
    629 		ret = malloc(sizeof(OkraRelativePart));
    630 		ret->authority = authority;
    631 		ret->path = path;
    632 		return ret;
    633 	} else if (authority != NULL) free(authority); /* clean up */
    634 
    635 	path = okra_parse_path_absolute(s, i);
    636 	if (path == NULL) path = okra_parse_path_rootless(s, i);
    637 	if (path == NULL) path = okra_parse_path_empty(s, i);
    638 	if (path == NULL) return NULL;
    639 
    640 	ret = malloc(sizeof(OkraRelativePart));
    641 	ret->authority = authority;
    642 	ret->path = path;
    643 	assert(ret->path != NULL);
    644 	return ret;
    645 }
    646 
    647 OkraRawUri *
    648 okra_parse_absolute_uri(const char *s, int i)
    649 {
    650 	int i0 = i;
    651 	OkraRawUri *ret = NULL;
    652 	OkraRelativePart *rel_part = NULL; 
    653 	OkraStringPiece *scheme = NULL;
    654 	OkraStringPiece *query = NULL;
    655 	OkraStringPiece *value = NULL;
    656 
    657 	scheme = okra_parse_scheme(s, i);
    658 	if (scheme == NULL) return NULL;
    659 	i = scheme->end;
    660 
    661 	if (s[i] != ':') {
    662 		free(scheme);
    663 		return NULL;
    664 	}
    665 	i++;
    666 
    667 	// TODO extract components from hier_part? ew...
    668 	
    669 	rel_part = okra_parse_hier_part(s, i);
    670 	if (rel_part == NULL) {
    671 		free(scheme);
    672 		return NULL;
    673 	}
    674 	i = rel_part->path->value->end;
    675 
    676 	if (s[i] == '?') {
    677 		int next = i+1;
    678 		query = okra_parse_query(s, next);
    679 		if (query != NULL) i = query->end;
    680 	}
    681 
    682 	ret = calloc(1, sizeof(OkraRawUri));
    683 	ret->buffer = s;
    684 	ret->scheme = scheme;
    685 	ret->query = query;
    686 
    687 	value = malloc(sizeof(OkraStringPiece));
    688 	value->start = i0;
    689 	value->end = i;
    690 	ret->value = value;
    691 
    692 	assert(rel_part != NULL);
    693 	ret->authority = rel_part->authority;
    694 	ret->path = rel_part->path;
    695 	free(rel_part);
    696 
    697 	return ret;
    698 }
    699 
    700 OkraRawUri *
    701 okra_parse_uri(const char *s, int i)
    702 {
    703 	OkraRawUri *ret = NULL;
    704 	OkraStringPiece *fragment = NULL;
    705 	ret = okra_parse_absolute_uri(s, i);
    706 	if (ret == NULL) return NULL;
    707 	assert(ret->value != NULL);
    708 	i = ret->value->end;
    709 
    710 	if (s[i] == '#') {
    711 		int next = i+1;
    712 		fragment = okra_parse_fragment(s, next);
    713 		if (fragment != NULL) i = fragment->end;
    714 	}
    715 
    716 	// if (s[i] != '\0') return NULL;
    717 	ret->fragment = fragment;
    718 	ret->value->end = i;
    719 
    720 	return ret;
    721 }
    722 
    723 OkraRawUri *
    724 okra_parse_uri_ref(const char *s, int i)
    725 {
    726 	OkraRawUri *ret = NULL;
    727 
    728 	ret = okra_parse_uri(s, i);
    729 	if (ret != NULL && s[ret->value->end] != '\0') {
    730 		okra_free_raw_uri(ret);
    731 		ret = NULL;
    732 	}
    733 	if (ret != NULL) return ret;
    734 
    735 	ret = okra_parse_relative_ref(s, i);
    736 	if (ret != NULL)
    737 		if (s[ret->value->end] != '\0') {
    738 			okra_free_raw_uri(ret);
    739 			ret = NULL;
    740 		}
    741 
    742 	return ret;
    743 }
    744 
    745 char *
    746 okra_stralloc(OkraRawUri *uri, OkraStringPiece *piece)
    747 {
    748 	int length;
    749 	char *ret;
    750 
    751 	if (piece == NULL) return NULL;
    752 
    753 	length = piece->end - piece->start;
    754 	ret = malloc((length+1) * sizeof(char));
    755 	for (int i = 0; i < length; i++)
    756 		ret[i] = uri->buffer[piece->start + i];
    757 	ret[length] = '\0';
    758 	return ret;
    759 }
    760 
    761 OkraUri *
    762 okra_parse(const char *s)
    763 {
    764 	OkraRawUri *raw = okra_parse_uri_ref(s, 0);
    765 	OkraUri *ret;
    766 
    767 	if (raw == NULL) return NULL;
    768 
    769 	/* transfer raw data into freshly allocated buffers, free as we go */
    770 	ret = calloc(1, sizeof(OkraUri));
    771 	if (raw->scheme != NULL) ret->scheme = okra_stralloc(raw, raw->scheme);
    772 	if (raw->authority != NULL) {
    773 		ret->authority = okra_stralloc(raw, raw->authority->value);
    774 		ret->userinfo = okra_stralloc(raw, raw->authority->userinfo);
    775 		ret->host = okra_stralloc(raw, raw->authority->host);
    776 		ret->port = okra_stralloc(raw, raw->authority->port);
    777 	}
    778 	if (raw->path != NULL) ret->path = okra_stralloc(raw, raw->path->value);
    779 	if (raw->query != NULL) ret->query = okra_stralloc(raw, raw->query);
    780 	if (raw->fragment != NULL) ret->fragment = okra_stralloc(raw, raw->fragment);
    781 	okra_free_raw_uri(raw);
    782 
    783 	/* recompose the uri into ret->uri */
    784 	ret->uri = okra_recompose(ret);
    785 
    786 	return ret;
    787 }

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.