summaryrefslogtreecommitdiffstats
path: root/include/uri.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/uri.h')
-rw-r--r--[-rwxr-xr-x]include/uri.h499
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