summaryrefslogtreecommitdiffstats
path: root/include/uri.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/uri.h')
-rwxr-xr-xinclude/uri.h328
1 files changed, 328 insertions, 0 deletions
diff --git a/include/uri.h b/include/uri.h
new file mode 100755
index 0000000..a00400d
--- /dev/null
+++ b/include/uri.h
@@ -0,0 +1,328 @@
+// uri.h - Minimal URI (de)serializer
+
+// Ethan Werner
+//
+// URI Parser and Serializer
+//
+// The uri_t structure is intended to be used as a black box. The primary use
+// of the header as a whole is to enable the efficient creation of
+// related URIs.
+//
+// Usage Notes
+// The internally held strings should remain opaque and only interacted with
+// through the given functions
+//
+// 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.
+
+
+#ifndef URI_H
+#define URI_H
+
+
+typedef enum
+{
+ BUILD,
+ SCHEME,
+ USERINFO,
+ HOST,
+ PORT,
+ PATH,
+ QUERY,
+ FRAGMENT,
+ URI_END
+} uri_element_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 * );
+
+
+#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>
+
+
+#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; \
+ } \
+}
+
+
+void uri_set( uri_t *u, uri_element_t e, char const *data )
+{
+ char *c;
+ copy_string( c, data );
+
+ if( u->data[e] )
+ free( u->data[e] );
+
+ u->data[e] = c;
+}
+
+
+char * uri_get( uri_t *u )
+{
+ uri_build( u );
+ char *c;
+ copy_string( c, u->data[BUILD] );
+
+ return c;
+}
+
+
+char * uri_remove( uri_t *u, uri_element_t e )
+{
+ char *data = u->data[e];
+ uri_set( u, e, NULL );
+
+ return data;
+}
+
+// TODO: Account for / when path is missing in query and fragment
+void uri_build( uri_t *u )
+{
+ int n = 0;
+
+ // TODO: Convert to a for loop
+ if( u->data[BUILD] )
+ free( u->data[BUILD] );
+
+ // 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; // #
+
+ char *build = ( char * ) malloc( n + 1 );
+ build[n] = '\0';
+
+ // Copy strings to BUILD
+ strcpy( build, u->data[SCHEME] );
+ strcat( build, ":" );
+
+ if( u->data[HOST] )
+ {
+ strcat( build, "//" );
+
+ if( u->data[USERINFO] )
+ {
+ strcat( build, u->data[USERINFO] );
+ strcat( build, "@" );
+ }
+
+ strcat( build, u->data[HOST] );
+
+ if( u->data[PORT] )
+ {
+ strcat( build, ":" );
+ strcat( build, u->data[PORT] );
+ }
+ }
+ if( u->data[PATH] )
+ {
+ strcat( build, u->data[PATH] );
+ }
+ if( u->data[QUERY] )
+ {
+ strcat( build, "?" );
+ strcat( build, u->data[QUERY] );
+ }
+ if( u->data[FRAGMENT] )
+ {
+ strcat( build, "#" );
+ strcat( build, u->data[FRAGMENT] );
+ }
+
+ u->data[BUILD] = build;
+}
+
+
+char * copy_substring( char const *from, char const *to )
+{
+ char *s = ( char * ) malloc( to - from + 1 );
+ s[to - from] = 0;
+ strncpy( s, from, to - from );
+
+ return s;
+}
+
+
+
+uri_t * uri_parse( char const * uri_s )
+{
+ uri_t *uri = ( uri_t * ) calloc( 1, sizeof( uri_t ) );
+
+ char const *current = uri_s;
+ char const *working;
+ char *token;
+
+ // SCHEME
+ working = strchr( current, ( int ) ':' );
+
+ if( !working )
+ return NULL;
+
+ token = copy_substring( current, working );
+ uri_set( uri, SCHEME, token );
+ current = ++working;
+
+ // AUTHORITY
+ if( ( *( working ) == '/' ) && ( *( working + 1 ) == '/' ) )
+ {
+ current = working += 2;
+
+ while( *working && *working != '@' && *working != ':' &&*working != '/' )
+ working++;
+
+ // USERINFO
+ if( *working == '@' )
+ {
+ token = copy_substring( current, working );
+ uri_set( uri, USERINFO, token );
+
+ current = ++working;
+
+ while( *working && *working != ':' && *working != '/' )
+ working++;
+ }
+
+ // HOST
+ token = copy_substring( current, working );
+ uri_set( uri, HOST, token );
+
+ if( !( *working ) )
+ return uri;
+
+ // PORT
+ if( *working == ':' )
+ {
+ current = ++working;
+
+ while( *working && *working != '/' )
+ working++;
+
+ token = copy_substring( current, working );
+ uri_set( uri, PORT, token );
+ }
+
+
+ if( !( *working ) )
+ return uri;
+
+ current = working;
+
+ while( *working && *working != '?' && *working != '#' )
+ working++;
+ }
+ else
+ {
+ while( *working && *working != '/' )
+ working++;
+ }
+
+ // PATH
+ token = copy_substring( current, working );
+ uri_set( uri, PATH, token );
+
+ if( !( *working ) )
+ return uri;
+
+ current = working;
+
+ // QUERY
+ if( *working == '?' )
+ {
+ while( *working && *working != '#' )
+ working++;
+
+ token = copy_substring( current, working );
+ uri_set( uri, QUERY, token );
+
+ current = working;
+ }
+
+ // FRAGMENT
+ if( *working == '#' )
+ {
+ while( *working )
+ working++;
+
+ token = copy_substring( current, working );
+ uri_set( uri, FRAGMENT, token );
+ }
+
+ uri_build( uri );
+
+ return uri;
+}
+
+
+void uri_print( uri_t const *u )
+{
+ if( u->data[BUILD] )
+ fputs( u->data[BUILD], stdout );
+}
+
+
+void uri_print_elements( uri_t const *u )
+{
+ for( int i = 0; i < URI_END; i++ )
+ {
+
+ printf( "%d", i );
+
+ if( u->data[i] )
+ {
+ printf( " - %s", u->data[i] );
+ }
+
+ putchar('\n');
+
+ }
+}
+
+
+#endif // URI_IMPLEMENTATION