diff options
Diffstat (limited to 'include/uri.h')
| -rw-r--r--[-rwxr-xr-x] | include/uri.h | 499 |
1 files changed, 313 insertions, 186 deletions
diff --git a/include/uri.h b/include/uri.h index a00400d..80df641 100755..100644 --- a/include/uri.h +++ b/include/uri.h @@ -14,12 +14,15 @@ // // Any string interacting with a uri_t is copied upon both entry and // exit, thus, all strings except those must be (de)allocated by the user. +// +// Thread safety: Not thread-safe. External synchronization required. +// Limitations: uri_parse only accepts absolute URIs with a scheme. Relative +// URIs (e.g., "/path/to/resource") will cause uri_parse to return NULL. #ifndef URI_H #define URI_H - typedef enum { BUILD, @@ -33,296 +36,420 @@ typedef enum URI_END } uri_element_t; +typedef struct uri_t +{ + char *data[URI_END]; +} uri_t; - -uri_t uri_init( void ); -void uri_set( uri_t *, uri_element_t, char const * ); -char * uri_get( uri_t * ); -uri_t * uri_parse( char const * ); -void uri_print( uri_t const * ); -void uri_build( uri_t * ); - +uri_t *uri_init(void); +void uri_free(uri_t *); +void uri_set(uri_t *, uri_element_t, char const *); +char *uri_get(uri_t *); +uri_t *uri_parse(char const *); +char *uri_remove(uri_t *, uri_element_t); +void uri_print(uri_t const *); +void uri_print_elements(uri_t const *); +void uri_build(uri_t *); #endif // URI_H #ifdef URI_IMPLEMENTATION - -typedef struct uri_t -{ - char *data[URI_END]; -} uri_t; - - #include <stdlib.h> #include <string.h> #include <stdio.h> +#include <stdint.h> +static void uri_copy_string(char **ptr, char const *source); -#define copy_string( ptr, source ) { \ - if( source ) \ - { \ - size_t n = strlen( source ); \ - ptr = malloc( n + 1 ); \ - ptr[n] = '\0'; \ - strcpy( ptr, source ); \ - } \ - else \ - { \ - ptr = NULL; \ - } \ +uri_t *uri_init(void) +{ + uri_t *u = calloc(1, sizeof(uri_t)); + if (!u) + return NULL; + return u; } - -void uri_set( uri_t *u, uri_element_t e, char const *data ) +void uri_free(uri_t *u) { - char *c; - copy_string( c, data ); - - if( u->data[e] ) - free( u->data[e] ); - - u->data[e] = c; + if (u == NULL) + return; + for(int i = 0; i < URI_END; i++) + { + if(u->data[i]) + free(u->data[i]); + } + free(u); } +static void uri_copy_string(char **ptr, char const *source) +{ + if (!ptr) + return; + if (*ptr) + free(*ptr); + if(source) { + size_t n = strlen(source); + if (n == SIZE_MAX) + return; + *ptr = malloc(n + 1); + if (!*ptr) + return; + memcpy(*ptr, source, n); + (*ptr)[n] = '\0'; + } + else { + *ptr = NULL; + } +} -char * uri_get( uri_t *u ) +void uri_set(uri_t *u, uri_element_t e, char const *data) { - uri_build( u ); - char *c; - copy_string( c, u->data[BUILD] ); + if (u == NULL) + return; + if ((int)e < 0 || (int)e >= URI_END) + return; + uri_copy_string(&u->data[e], data); +} +char *uri_get(uri_t *u) +{ + if (u == NULL) + return NULL; + uri_build(u); + char *c = NULL; + uri_copy_string(&c, u->data[BUILD]); return c; } - -char * uri_remove( uri_t *u, uri_element_t e ) +char *uri_remove(uri_t *u, uri_element_t e) { + if (u == NULL) + return NULL; + if ((int)e < 0 || (int)e >= URI_END) + return NULL; char *data = u->data[e]; - uri_set( u, e, NULL ); - + u->data[e] = NULL; return data; } -// TODO: Account for / when path is missing in query and fragment -void uri_build( uri_t *u ) +void uri_build(uri_t *u) { - int n = 0; + if (u == NULL) + return; + size_t n = 0; - // TODO: Convert to a for loop - if( u->data[BUILD] ) - free( u->data[BUILD] ); + if(u->data[BUILD]) + free(u->data[BUILD]); + u->data[BUILD] = NULL; // Length of built string - if( u->data[SCHEME] ) - n += strlen( u->data[SCHEME] ) + 1; // ":" - - if( u->data[USERINFO] ) - n += strlen( u->data[USERINFO] ) + 1; // "@" - - if( u->data[HOST] ) - n += strlen( u->data[HOST] ) + 2; // "//" - - if( u->data[PORT] ) - n += strlen( u->data[PORT] ) + 1; // ":" - - if( u->data[PATH] ) - n += strlen( u->data[PATH] ); - - if( u->data[QUERY] ) - n += strlen( u->data[QUERY] ) + 1; // ? - - if( u->data[FRAGMENT] ) - n += strlen( u->data[FRAGMENT] ) + 1; // # + { + size_t s; + if(u->data[SCHEME]) + { + s = strlen(u->data[SCHEME]) + 1; + if (n > SIZE_MAX - s) { u->data[BUILD] = NULL; return; } + n += s; + } + if(u->data[USERINFO]) + { + s = strlen(u->data[USERINFO]) + 1; + if (n > SIZE_MAX - s) { u->data[BUILD] = NULL; return; } + n += s; + } + if(u->data[HOST]) + { + s = strlen(u->data[HOST]) + 2; + if (n > SIZE_MAX - s) { u->data[BUILD] = NULL; return; } + n += s; + } + if(u->data[PORT]) + { + s = strlen(u->data[PORT]) + 1; + if (n > SIZE_MAX - s) { u->data[BUILD] = NULL; return; } + n += s; + } + if(u->data[PATH]) + { + s = strlen(u->data[PATH]); + if (n > SIZE_MAX - s) { u->data[BUILD] = NULL; return; } + n += s; + } + if(u->data[QUERY]) + { + s = strlen(u->data[QUERY]) + 1; + if (n > SIZE_MAX - s) { u->data[BUILD] = NULL; return; } + n += s; + } + if(u->data[FRAGMENT]) + { + s = strlen(u->data[FRAGMENT]) + 1; + if (n > SIZE_MAX - s) { u->data[BUILD] = NULL; return; } + n += s; + } + } - char *build = ( char * ) malloc( n + 1 ); - build[n] = '\0'; + if (n == SIZE_MAX) + return; + char *build = malloc(n + 1); + if (!build) + return; + build[0] = '\0'; + size_t pos = 0; // Copy strings to BUILD - strcpy( build, u->data[SCHEME] ); - strcat( build, ":" ); + if(u->data[SCHEME]) + { + size_t len = strlen(u->data[SCHEME]); + memcpy(build + pos, u->data[SCHEME], len); + pos += len; + build[pos++] = ':'; + } - if( u->data[HOST] ) + if(u->data[HOST]) { - strcat( build, "//" ); + memcpy(build + pos, "//", 2); + pos += 2; - if( u->data[USERINFO] ) + if(u->data[USERINFO]) { - strcat( build, u->data[USERINFO] ); - strcat( build, "@" ); + size_t len = strlen(u->data[USERINFO]); + memcpy(build + pos, u->data[USERINFO], len); + pos += len; + build[pos++] = '@'; } - strcat( build, u->data[HOST] ); + size_t len = strlen(u->data[HOST]); + memcpy(build + pos, u->data[HOST], len); + pos += len; - if( u->data[PORT] ) + if(u->data[PORT]) { - strcat( build, ":" ); - strcat( build, u->data[PORT] ); + build[pos++] = ':'; + len = strlen(u->data[PORT]); + memcpy(build + pos, u->data[PORT], len); + pos += len; } } - if( u->data[PATH] ) + if(u->data[PATH]) { - strcat( build, u->data[PATH] ); + size_t len = strlen(u->data[PATH]); + memcpy(build + pos, u->data[PATH], len); + pos += len; } - if( u->data[QUERY] ) + if(u->data[QUERY]) { - strcat( build, "?" ); - strcat( build, u->data[QUERY] ); + build[pos++] = '?'; + size_t len = strlen(u->data[QUERY]); + memcpy(build + pos, u->data[QUERY], len); + pos += len; } - if( u->data[FRAGMENT] ) + if(u->data[FRAGMENT]) { - strcat( build, "#" ); - strcat( build, u->data[FRAGMENT] ); + build[pos++] = '#'; + size_t len = strlen(u->data[FRAGMENT]); + memcpy(build + pos, u->data[FRAGMENT], len); + pos += len; } + build[pos] = '\0'; u->data[BUILD] = build; } - -char * copy_substring( char const *from, char const *to ) +static int uri_set_range(uri_t *u, uri_element_t e, char const *from, char const *to) { - char *s = ( char * ) malloc( to - from + 1 ); - s[to - from] = 0; - strncpy( s, from, to - from ); - - return s; -} + if (u == NULL || from == NULL || to == NULL) + return 0; + if ((int)e < 0 || (int)e >= URI_END) + return 0; + if (to <= from) + return 0; + size_t len = (size_t)(to - from); + if (len == SIZE_MAX) + return 0; + char *s = malloc(len + 1); + if (!s) + return 0; + s[len] = 0; + memcpy(s, from, len); + if(u->data[e]) + free(u->data[e]); + u->data[e] = s; + return 1; +} -uri_t * uri_parse( char const * uri_s ) +uri_t *uri_parse(char const *uri_s) { - uri_t *uri = ( uri_t * ) calloc( 1, sizeof( uri_t ) ); + if (uri_s == NULL) + return NULL; + uri_t *uri = calloc(1, sizeof(uri_t)); + if (!uri) + return NULL; char const *current = uri_s; char const *working; - char *token; // SCHEME - working = strchr( current, ( int ) ':' ); + working = strchr(current, ':'); - if( !working ) + if(!working) + { + uri_free(uri); return NULL; + } - token = copy_substring( current, working ); - uri_set( uri, SCHEME, token ); - current = ++working; + if (!uri_set_range(uri, SCHEME, current, working)) { + uri_free(uri); + return NULL; + } + current = ++working; - // AUTHORITY - if( ( *( working ) == '/' ) && ( *( working + 1 ) == '/' ) ) - { - current = working += 2; + // AUTHORITY + if(working[0] == '/' && working[1] == '/') + { + current = working += 2; - while( *working && *working != '@' && *working != ':' &&*working != '/' ) - working++; + char const *at = strchr(working, '@'); + if (at) { + if (!uri_set_range(uri, USERINFO, current, at)) { + uri_free(uri); + return NULL; + } + current = at + 1; - // USERINFO - if( *working == '@' ) - { - token = copy_substring( current, working ); - uri_set( uri, USERINFO, token ); + char const *host_end = strpbrk(current, ":/"); + if (!host_end) + host_end = current + strlen(current); - current = ++working; + if (!uri_set_range(uri, HOST, current, host_end)) { + uri_free(uri); + return NULL; + } + working = host_end; + } else { + while(*working && *working != ':' && *working != '/') + working++; - while( *working && *working != ':' && *working != '/' ) - working++; - } + if (!uri_set_range(uri, HOST, current, working)) { + uri_free(uri); + return NULL; + } + } - // HOST - token = copy_substring( current, working ); - uri_set( uri, HOST, token ); + if(!*working) + { + uri_build(uri); + return uri; + } - if( !( *working ) ) - return uri; + // PORT + if(*working == ':') + { + current = ++working; - // PORT - if( *working == ':' ) - { - current = ++working; + while(*working && *working != '/') + working++; - while( *working && *working != '/' ) - working++; + if (!uri_set_range(uri, PORT, current, working)) { + uri_free(uri); + return NULL; + } + } - token = copy_substring( current, working ); - uri_set( uri, PORT, token ); - } + if(!*working) + { + uri_build(uri); + return uri; + } + current = working; - if( !( *working ) ) - return uri; + while(*working && *working != '?' && *working != '#') + working++; + } + else + { + // No authority section - scan for query/fragment delimiters + while(*working && *working != '?' && *working != '#') + working++; + } - current = working; + // PATH + if (!uri_set_range(uri, PATH, current, working)) { + uri_free(uri); + return NULL; + } - while( *working && *working != '?' && *working != '#' ) - working++; - } - else - { - while( *working && *working != '/' ) - working++; - } + if(!*working) + { + uri_build(uri); + return uri; + } - // PATH - token = copy_substring( current, working ); - uri_set( uri, PATH, token ); + current = working; - if( !( *working ) ) - return uri; + // QUERY + if(*working == '?') + { + char const *query_start = ++working; - current = working; + while(*working && *working != '#') + working++; - // QUERY - if( *working == '?' ) - { - while( *working && *working != '#' ) - working++; + if (!uri_set_range(uri, QUERY, query_start, working)) { + uri_free(uri); + return NULL; + } - token = copy_substring( current, working ); - uri_set( uri, QUERY, token ); + current = working; + } - current = working; - } + // FRAGMENT + if(*working == '#') + { + char const *frag_start = ++working; - // FRAGMENT - if( *working == '#' ) - { - while( *working ) - working++; + while(*working) + working++; - token = copy_substring( current, working ); - uri_set( uri, FRAGMENT, token ); - } + if (!uri_set_range(uri, FRAGMENT, frag_start, working)) { + uri_free(uri); + return NULL; + } + } - uri_build( uri ); + uri_build(uri); - return uri; + return uri; } - -void uri_print( uri_t const *u ) +void uri_print(uri_t const *u) { - if( u->data[BUILD] ) - fputs( u->data[BUILD], stdout ); + if (u == NULL) + return; + if(u->data[BUILD]) + fputs(u->data[BUILD], stdout); } - -void uri_print_elements( uri_t const *u ) +void uri_print_elements(uri_t const *u) { - for( int i = 0; i < URI_END; i++ ) + if (u == NULL) + return; + for(int i = 0; i < URI_END; i++) { + printf("%d", i); - printf( "%d", i ); - - if( u->data[i] ) + if(u->data[i]) { - printf( " - %s", u->data[i] ); + printf(" - %s", u->data[i]); } putchar('\n'); - } } - #endif // URI_IMPLEMENTATION |