git.y1.nz

okra

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

merge.c

      1 /* This file implements algorithms on already-parsed URIs. */
      2 
      3 #include <string.h> /* strlen */
      4 #include <stdlib.h> /* malloc, calloc */
      5 #include <assert.h> /* assert */
      6 
      7 #include "okra.h"
      8 
      9 /* takes any string and allocates a fresh copy of it. */
     10 char *
     11 okra_strclone(const char *buffer)
     12 {
     13 	if (buffer == NULL) return NULL;
     14 	char *ret = malloc((strlen(buffer)+1) * sizeof(char));
     15 	strcpy(ret, buffer);
     16 	return ret;
     17 }
     18 
     19 /* described in section 5.2.3 */
     20 char *
     21 okra_merge_paths(const OkraUri *base, const char *relative_path)
     22 {
     23 	char *ret;
     24 	int relative_length, i, last_slash_index = 0;
     25 
     26 	relative_length = strlen(relative_path);
     27 
     28 	if (base->authority != NULL && base->path[0] == '\0') {
     29 		ret = malloc((relative_length+1) * sizeof(char));
     30 		ret[0] = '/';
     31 		strcpy(ret+1, relative_path);
     32 		return ret;
     33 	}
     34 
     35 	/* calculate the index of the right-most slash in base->path */
     36 	i = 0;
     37 	for (;;) {
     38 		if (base->path[i] == '/') last_slash_index = i;
     39 		if (base->path[i] == '\0') break;
     40 		i++;
     41 	}
     42 
     43 	ret = malloc((last_slash_index + relative_length + 1) * sizeof(char));
     44 	for (i = 0; i <= last_slash_index; i++)
     45 		ret[i] = base->path[i];
     46 
     47 	strcpy(ret + last_slash_index + 1, relative_path);
     48 
     49 	return ret;
     50 }
     51 
     52 /* described in section 5.2.4 */
     53 #define IS_SEGEND(c)  
     54 char *
     55 okra_remove_dot_segments(char *path)
     56 {
     57 	int length, i, ri;
     58 	int *starts, start_count = 0;
     59 	char *ret;
     60 	char ch, ch1, ch2, ch3;
     61 
     62 	length = strlen(path);
     63 	starts = malloc((length+1) * sizeof(int));
     64 	ret = calloc(1, (length+1) * sizeof(char));
     65 	starts[start_count++] = path[0] == '/' ? 1 : 0;
     66 
     67 	i = ri = 0;
     68 	ch = ch1 = ch2 = ch3 = '/';
     69 	for (;;) {
     70 		ch3 = ch2; ch2 = ch1; ch1 = ch;
     71 		ch = path[i++];
     72 		ret[ri++] = ch;
     73 		ret[ri] = '\0';
     74 
     75 		/* History looks like "/../" or "/..\0": */
     76 		if (
     77 			(ch == '/' || ch == '\0') &&
     78 			ch1 == '.' &&
     79 			ch2 == '.' &&
     80 			ch3 == '/'
     81 		) {
     82 			if (start_count > 1) start_count--;
     83 			ri = starts[start_count-1];
     84 			ret[ri] = '\0';
     85 			if (ch == '\0') break;
     86 			ch3 = ch2 = ch1 = ch = '/';
     87 			continue;
     88 		}
     89 
     90 		/* History looks like "/./" or "/.\0": */
     91 		if (
     92 			(ch == '/' || ch == '\0') &&
     93 			ch1 == '.' &&
     94 			ch2 == '/'
     95 		) {
     96 			ri = starts[start_count-1];
     97 			ret[ri] = '\0';
     98 			if (ch == '\0') break;
     99 			ch3 = ch2 = ch1 = ch = '/';
    100 			continue;
    101 		}
    102 
    103 		if (ch == '/') starts[start_count++] = ri;
    104 		if (ch == '\0') break;
    105 	}
    106 	ret[ri] = '\0';
    107 	free(starts);
    108 	return ret;
    109 }
    110 
    111 /* refactored from psuedocode provided in section 5.2.2 */
    112 OkraUri *
    113 okra_merge(const OkraUri *base, const OkraUri *ref)
    114 {
    115 	OkraUri *ret;
    116 
    117 	assert(base != NULL);
    118 	assert(ref != NULL);
    119 
    120 	ret = calloc(1, sizeof(OkraUri));
    121 	ret->fragment = okra_strclone(ref->fragment);
    122 
    123 	if (ref->scheme != NULL) {
    124 		ret->scheme = okra_strclone(ref->scheme);
    125 
    126 		ret->authority = okra_strclone(ref->authority);
    127 		ret->host = okra_strclone(ref->host);
    128 		ret->port = okra_strclone(ref->port);
    129 		ret->userinfo = okra_strclone(ref->userinfo);
    130 
    131 		ret->path = okra_strclone(ref->path);
    132 		ret->query = okra_strclone(ref->query);
    133 		goto finish_merge;
    134 	}
    135 	ret->scheme = okra_strclone(base->scheme);
    136 
    137 	if (ref->authority != NULL) {
    138 		ret->authority = okra_strclone(ref->authority);
    139 		ret->host = okra_strclone(ref->host);
    140 		ret->port = okra_strclone(ref->port);
    141 		ret->userinfo = okra_strclone(ref->userinfo);
    142 
    143 		ret->path = okra_remove_dot_segments(ref->path);
    144 		ret->query = okra_strclone(ref->query);
    145 		goto finish_merge;
    146 	}
    147 	ret->authority = okra_strclone(base->authority);
    148 	ret->host = okra_strclone(base->host);
    149 	ret->port = okra_strclone(base->port);
    150 	ret->userinfo = okra_strclone(base->userinfo);
    151 
    152 	if (ref->path[0] == '\0') {
    153 		ret->path = okra_strclone(base->path);
    154 		ret->query = okra_strclone(
    155 			ref->query == NULL ? base->query : ref->query
    156 		);
    157 		goto finish_merge;
    158 	}
    159 
    160 	ret->query = okra_strclone(ref->query);
    161 	if (ref->path[0] == '/') {
    162 		ret->path = okra_remove_dot_segments(ref->path);
    163 		goto finish_merge;
    164 	}
    165 
    166 	ret->path = okra_merge_paths(base, ref->path);
    167 	assert(ret->path != NULL);
    168 	ret->path = okra_remove_dot_segments(ret->path);
    169 
    170 finish_merge:
    171 	ret->uri = okra_recompose(ret);
    172 	return ret;
    173 }
    174 
    175 /* Refactored from section 5.3 of [RFC3986]. */
    176 char *
    177 okra_recompose(OkraUri *uri)
    178 {
    179 	char *ret, *cur;
    180 	int length = 0, i;
    181 	int scheme_length = 0;
    182 	int auth_length = 0;
    183 	int path_length = 0;
    184 	int query_length = 0;
    185 	int fragment_length = 0;
    186 
    187 	/* calculate length */
    188 	if (uri->scheme != NULL) {
    189 		scheme_length = strlen(uri->scheme) + 1; /* extra ":" */
    190 		length += scheme_length;
    191 	}
    192 	if (uri->authority != NULL) {
    193 		auth_length = strlen(uri->authority) + 2; /* extra "//" */
    194 		length += auth_length;
    195 	}
    196 	path_length = strlen(uri->path);
    197 	length += path_length;
    198 	if (uri->query != NULL) {
    199 		query_length = strlen(uri->query) + 1; /* extra "?" */
    200 		length += query_length;
    201 	}
    202 	if (uri->fragment != NULL) {
    203 		fragment_length = strlen(uri->fragment) + 1; /* extra "#" */
    204 		length += fragment_length;
    205 	}
    206 
    207 	ret = malloc((length+1) * sizeof(char));
    208 	cur = ret;
    209 
    210 	/* populate the buffer... */
    211 	if (uri->scheme != NULL) {
    212 		strcpy(cur, uri->scheme);
    213 		cur[scheme_length-1] = ':';
    214 		cur += scheme_length;
    215 	}
    216 	if (uri->authority != NULL) {
    217 		cur[0] = cur[1] = '/';
    218 		strcpy(cur+2, uri->authority);
    219 		cur += auth_length;
    220 	}
    221 	strcpy(cur, uri->path);
    222 	cur += path_length;
    223 	if (uri->query != NULL) {
    224 		cur[0] = '?';
    225 		strcpy(cur+1, uri->query);
    226 		cur += query_length;
    227 	}
    228 	if (uri->fragment != NULL) {
    229 		cur[0] = '#';
    230 		strcpy(cur+1, uri->fragment);
    231 		cur += fragment_length;
    232 	}
    233 
    234 	return ret;
    235 }
    236 
    237 void
    238 okra_free(OkraUri *uri)
    239 {
    240 	if (uri->scheme == NULL) free(uri->scheme);
    241 	if (uri->authority == NULL) free(uri->authority);
    242 	if (uri->host == NULL) free(uri->host);
    243 	if (uri->userinfo == NULL) free(uri->userinfo);
    244 	if (uri->port == NULL) free(uri->port);
    245 	if (uri->path == NULL) free(uri->path);
    246 	if (uri->query == NULL) free(uri->query);
    247 	if (uri->fragment == NULL) free(uri->fragment);
    248 	free(uri);
    249 }

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