Rename e1000_main.c to e1000.c to so we can type 'make bin/e1000.dsk' instead of...
[gpxe.git] / src / core / uri.c
blob3b3cf85b9cb435e95345695ee9588f47d6e8624a
1 /*
2 * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 /** @file
21 * Uniform Resource Identifiers
25 #include <stdint.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <libgen.h>
29 #include <gpxe/vsprintf.h>
30 #include <gpxe/uri.h>
32 /**
33 * Dump URI for debugging
35 * @v uri URI
37 static void dump_uri ( struct uri *uri ) {
38 if ( ! uri )
39 return;
40 if ( uri->scheme )
41 DBG ( " scheme \"%s\"", uri->scheme );
42 if ( uri->opaque )
43 DBG ( " opaque \"%s\"", uri->opaque );
44 if ( uri->user )
45 DBG ( " user \"%s\"", uri->user );
46 if ( uri->password )
47 DBG ( " password \"%s\"", uri->password );
48 if ( uri->host )
49 DBG ( " host \"%s\"", uri->host );
50 if ( uri->port )
51 DBG ( " port \"%s\"", uri->port );
52 if ( uri->path )
53 DBG ( " path \"%s\"", uri->path );
54 if ( uri->query )
55 DBG ( " query \"%s\"", uri->query );
56 if ( uri->fragment )
57 DBG ( " fragment \"%s\"", uri->fragment );
60 /**
61 * Parse URI
63 * @v uri_string URI as a string
64 * @ret uri URI
66 * Splits a URI into its component parts. The return URI structure is
67 * dynamically allocated and must eventually be freed by calling
68 * uri_put().
70 struct uri * parse_uri ( const char *uri_string ) {
71 struct uri *uri;
72 char *raw;
73 char *tmp;
74 char *path = NULL;
75 char *authority = NULL;
76 size_t raw_len;
78 /* Allocate space for URI struct and a copy of the string */
79 raw_len = ( strlen ( uri_string ) + 1 /* NUL */ );
80 uri = zalloc ( sizeof ( *uri ) + raw_len );
81 if ( ! uri )
82 return NULL;
83 raw = ( ( ( char * ) uri ) + sizeof ( *uri ) );
85 /* Zero URI struct and copy in the raw string */
86 memcpy ( raw, uri_string, raw_len );
88 /* Start by chopping off the fragment, if it exists */
89 if ( ( tmp = strchr ( raw, '#' ) ) ) {
90 *(tmp++) = '\0';
91 uri->fragment = tmp;
94 /* Identify absolute/relative URI */
95 if ( ( tmp = strchr ( raw, ':' ) ) ) {
96 /* Absolute URI: identify hierarchical/opaque */
97 uri->scheme = raw;
98 *(tmp++) = '\0';
99 if ( *tmp == '/' ) {
100 /* Absolute URI with hierarchical part */
101 path = tmp;
102 } else {
103 /* Absolute URI with opaque part */
104 uri->opaque = tmp;
106 } else {
107 /* Relative URI */
108 path = raw;
111 /* If we don't have a path (i.e. we have an absolute URI with
112 * an opaque portion, we're already finished processing
114 if ( ! path )
115 goto done;
117 /* Chop off the query, if it exists */
118 if ( ( tmp = strchr ( path, '?' ) ) ) {
119 *(tmp++) = '\0';
120 uri->query = tmp;
123 /* Identify net/absolute/relative path */
124 if ( strncmp ( path, "//", 2 ) == 0 ) {
125 /* Net path. If this is terminated by the first '/'
126 * of an absolute path, then we have no space for a
127 * terminator after the authority field, so shuffle
128 * the authority down by one byte, overwriting one of
129 * the two slashes.
131 authority = ( path + 2 );
132 if ( ( tmp = strchr ( authority, '/' ) ) ) {
133 /* Shuffle down */
134 uri->path = tmp;
135 memmove ( ( authority - 1 ), authority,
136 ( tmp - authority ) );
137 authority--;
138 *(--tmp) = '\0';
140 } else {
141 /* Absolute/relative path */
142 uri->path = path;
145 /* Split authority into user[:password] and host[:port] portions */
146 if ( ( tmp = strchr ( authority, '@' ) ) ) {
147 /* Has user[:password] */
148 *(tmp++) = '\0';
149 uri->host = tmp;
150 uri->user = authority;
151 if ( ( tmp = strchr ( authority, ':' ) ) ) {
152 /* Has password */
153 *(tmp++) = '\0';
154 uri->password = tmp;
156 } else {
157 /* No user:password */
158 uri->host = authority;
161 /* Split host into host[:port] */
162 if ( ( tmp = strchr ( uri->host, ':' ) ) ) {
163 *(tmp++) = '\0';
164 uri->port = tmp;
167 done:
168 DBG ( "URI \"%s\" split into", uri_string );
169 dump_uri ( uri );
170 DBG ( "\n" );
172 return uri;
176 * Get port from URI
178 * @v uri URI, or NULL
179 * @v default_port Default port to use if none specified in URI
180 * @ret port Port
182 unsigned int uri_port ( struct uri *uri, unsigned int default_port ) {
183 if ( ( ! uri ) || ( ! uri->port ) )
184 return default_port;
185 return ( strtoul ( uri->port, NULL, 0 ) );
189 * Unparse URI
191 * @v buf Buffer to fill with URI string
192 * @v size Size of buffer
193 * @v uri URI to write into buffer, or NULL
194 * @ret len Length of URI string
196 int unparse_uri ( char *buf, size_t size, struct uri *uri ) {
197 int used = 0;
199 DBG ( "URI unparsing" );
200 dump_uri ( uri );
201 DBG ( "\n" );
203 /* Special-case NULL URI */
204 if ( ! uri ) {
205 if ( size )
206 buf[0] = '\0';
207 return 0;
210 /* Special-case opaque URIs */
211 if ( uri->opaque ) {
212 return ssnprintf ( ( buf + used ), ( size - used ),
213 "%s:%s", uri->scheme, uri->opaque );
216 /* scheme:// */
217 if ( uri->scheme ) {
218 used += ssnprintf ( ( buf + used ), ( size - used ),
219 "%s://", uri->scheme );
222 /* [user[:password]@]host[:port] */
223 if ( uri->host ) {
224 if ( uri->user ) {
225 used += ssnprintf ( ( buf + used ), ( size - used ),
226 "%s", uri->user );
227 if ( uri->password ) {
228 used += ssnprintf ( ( buf + used ),
229 ( size - used ),
230 ":%s", uri->password );
232 used += ssnprintf ( ( buf + used ), ( size - used ),
233 "@" );
235 used += ssnprintf ( ( buf + used ), ( size - used ), "%s",
236 uri->host );
237 if ( uri->port ) {
238 used += ssnprintf ( ( buf + used ), ( size - used ),
239 ":%s", uri->port );
243 /* /path */
244 if ( uri->path ) {
245 used += ssnprintf ( ( buf + used ), ( size - used ),
246 "%s", uri->path );
249 /* ?query */
250 if ( uri->query ) {
251 used += ssnprintf ( ( buf + used ), ( size - used ),
252 "?%s", uri->query );
255 /* #fragment */
256 if ( uri->fragment ) {
257 used += ssnprintf ( ( buf + used ), ( size - used ),
258 "#%s", uri->fragment );
261 return used;
265 * Duplicate URI
267 * @v uri URI
268 * @ret uri Duplicate URI
270 * Creates a modifiable copy of a URI.
272 struct uri * uri_dup ( struct uri *uri ) {
273 size_t len = ( unparse_uri ( NULL, 0, uri ) + 1 );
274 char buf[len];
276 unparse_uri ( buf, len, uri );
277 return parse_uri ( buf );
281 * Resolve base+relative path
283 * @v base_uri Base path
284 * @v relative_uri Relative path
285 * @ret resolved_uri Resolved path
287 * Takes a base path (e.g. "/var/lib/tftpboot/vmlinuz" and a relative
288 * path (e.g. "initrd.gz") and produces a new path
289 * (e.g. "/var/lib/tftpboot/initrd.gz"). Note that any non-directory
290 * portion of the base path will automatically be stripped; this
291 * matches the semantics used when resolving the path component of
292 * URIs.
294 char * resolve_path ( const char *base_path,
295 const char *relative_path ) {
296 size_t base_len = ( strlen ( base_path ) + 1 );
297 char base_path_copy[base_len];
298 char *base_tmp = base_path_copy;
299 char *resolved;
301 /* If relative path is absolute, just re-use it */
302 if ( relative_path[0] == '/' )
303 return strdup ( relative_path );
305 /* Create modifiable copy of path for dirname() */
306 memcpy ( base_tmp, base_path, base_len );
307 base_tmp = dirname ( base_tmp );
309 /* Process "./" and "../" elements */
310 while ( *relative_path == '.' ) {
311 relative_path++;
312 if ( *relative_path == 0 ) {
313 /* Do nothing */
314 } else if ( *relative_path == '/' ) {
315 relative_path++;
316 } else if ( *relative_path == '.' ) {
317 relative_path++;
318 if ( *relative_path == 0 ) {
319 base_tmp = dirname ( base_tmp );
320 } else if ( *relative_path == '/' ) {
321 base_tmp = dirname ( base_tmp );
322 relative_path++;
323 } else {
324 relative_path -= 2;
325 break;
327 } else {
328 relative_path--;
329 break;
333 /* Create and return new path */
334 if ( asprintf ( &resolved, "%s%s%s", base_tmp,
335 ( ( base_tmp[ strlen ( base_tmp ) - 1 ] == '/' ) ?
336 "" : "/" ), relative_path ) < 0 )
337 return NULL;
339 return resolved;
343 * Resolve base+relative URI
345 * @v base_uri Base URI, or NULL
346 * @v relative_uri Relative URI
347 * @ret resolved_uri Resolved URI
349 * Takes a base URI (e.g. "http://etherboot.org/kernels/vmlinuz" and a
350 * relative URI (e.g. "../initrds/initrd.gz") and produces a new URI
351 * (e.g. "http://etherboot.org/initrds/initrd.gz").
353 struct uri * resolve_uri ( struct uri *base_uri,
354 struct uri *relative_uri ) {
355 struct uri tmp_uri;
356 char *tmp_path = NULL;
357 struct uri *new_uri;
359 /* If relative URI is absolute, just re-use it */
360 if ( uri_is_absolute ( relative_uri ) || ( ! base_uri ) )
361 return uri_get ( relative_uri );
363 /* Mangle URI */
364 memcpy ( &tmp_uri, base_uri, sizeof ( tmp_uri ) );
365 if ( relative_uri->path ) {
366 tmp_path = resolve_path ( ( base_uri->path ?
367 base_uri->path : "/" ),
368 relative_uri->path );
369 tmp_uri.path = tmp_path;
370 tmp_uri.query = relative_uri->query;
371 tmp_uri.fragment = relative_uri->fragment;
372 } else if ( relative_uri->query ) {
373 tmp_uri.query = relative_uri->query;
374 tmp_uri.fragment = relative_uri->fragment;
375 } else if ( relative_uri->fragment ) {
376 tmp_uri.fragment = relative_uri->fragment;
379 /* Create demangled URI */
380 new_uri = uri_dup ( &tmp_uri );
381 free ( tmp_path );
382 return new_uri;