1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-08-01 00:19:27 +02:00
audacity/lib-src/libraptor/src/raptor_uri.c
2010-01-24 09:19:39 +00:00

1703 lines
47 KiB
C

/* -*- Mode: c; c-basic-offset: 2 -*-
*
* raptor_uri.c - Raptor URI resolving implementation
*
* Copyright (C) 2002-2006, David Beckett http://purl.org/net/dajobe/
* Copyright (C) 2002-2005, University of Bristol, UK http://www.bristol.ac.uk/
*
* This package is Free Software and part of Redland http://librdf.org/
*
* It is licensed under the following three licenses as alternatives:
* 1. GNU Lesser General Public License (LGPL) V2.1 or any newer version
* 2. GNU General Public License (GPL) V2 or any newer version
* 3. Apache License, V2.0 or any newer version
*
* You may not use this file except in compliance with at least one of
* the above three licenses.
*
* See LICENSE.html or LICENSE.txt at the top of this package for the
* complete terms and further detail along with the license texts for
* the licenses in COPYING.LIB, COPYING and LICENSE-2.0.txt respectively.
*
*
*/
#ifdef HAVE_CONFIG_H
#include <raptor_config.h>
#endif
#ifdef WIN32
#include <win32_raptor_config.h>
#endif
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
/* Raptor includes */
#include "raptor.h"
#include "raptor_internal.h"
#ifdef WIN32_URI_TEST
#define WIN32
#endif
static const raptor_uri_handler *raptor_uri_current_uri_handler;
static void *raptor_uri_current_uri_context;
/* Symbian OS uses similar path mappings as Windows but does not necessarily have the WIN32 flag defined */
#if defined(__SYMBIAN32__) && !defined(WIN32)
#define WIN32
#endif
/**
* raptor_uri_set_handler:
* @handler: URI handler structure
* @context: URI handler context
*
* Change the URI class implementation to the functions provided by the
*
* The URI interface in @handler->initialised should be either 1
* or 2 (if raptor_uri_compare_func is implemented).
**/
void
raptor_uri_set_handler(const raptor_uri_handler *handler, void *context)
{
RAPTOR_ASSERT_OBJECT_POINTER_RETURN(handler, raptor_uri_handler);
/* RAPTOR_ASSERT is the negative of ordinary asserts - it fails if the condition is true */
RAPTOR_ASSERT(!(handler->initialised >= 1 && handler->initialised <= 2),
"raptor_uri_handler->initialised not 1..2");
raptor_uri_current_uri_handler=handler;
raptor_uri_current_uri_context=context;
}
/**
* raptor_uri_get_handler:
* @handler: URI handler to return
* @context: URI context to return
*
* Return the current raptor URI class implementation @handler and @context
**/
void
raptor_uri_get_handler(const raptor_uri_handler **handler, void **context)
{
if(handler)
*handler=raptor_uri_current_uri_handler;
if(context)
*context=raptor_uri_current_uri_context;
}
static raptor_uri*
raptor_default_new_uri(void *context, const unsigned char *uri_string)
{
unsigned char *p;
size_t len;
/* If uri_string is "file:path-to-file", turn it into a correct file:URI */
if(raptor_uri_uri_string_is_file_uri(uri_string)) {
unsigned char *fragment=NULL;
char *filename;
raptor_uri* uri=NULL;
filename=raptor_uri_uri_string_to_filename_fragment(uri_string, &fragment);
if(filename && !access(filename, R_OK)) {
uri=(raptor_uri*)raptor_uri_filename_to_uri_string(filename);
/* If there was a fragment, reattach it to the new URI */
if(fragment) {
unsigned char *new_fragment;
raptor_uri* new_uri;
new_fragment=(unsigned char*)RAPTOR_MALLOC(cstring, strlen((const char*)fragment) + 1 + sizeof(char*));
if(!new_fragment)
return NULL;
*new_fragment='#';
strcpy((char*)new_fragment+1, (const char*)fragment);
new_uri=raptor_new_uri_relative_to_base(uri, new_fragment);
RAPTOR_FREE(cstring, new_fragment);
raptor_free_uri(uri);
uri=new_uri;
}
}
if(filename)
RAPTOR_FREE(cstring, filename);
if(fragment)
RAPTOR_FREE(cstring, fragment);
if(uri)
return uri;
}
len=strlen((const char*)uri_string);
p=(unsigned char*)RAPTOR_MALLOC(raptor_uri, len + sizeof(char*));
if(!p)
return NULL;
strcpy((char*)p, (const char*)uri_string);
return (raptor_uri*)p;
}
/**
* raptor_new_uri:
* @uri_string: URI string.
*
* Constructor - create a raptor URI from a UTF-8 encoded Unicode string.
*
* Return value: a new #raptor_uri object or NULL on failure.
**/
raptor_uri*
raptor_new_uri(const unsigned char *uri_string)
{
if(!uri_string || !*uri_string)
return NULL;
return (*raptor_uri_current_uri_handler->new_uri)(raptor_uri_current_uri_context, uri_string);
}
static raptor_uri*
raptor_default_new_uri_from_uri_local_name(void *context,
raptor_uri *uri,
const unsigned char *local_name)
{
int uri_length=strlen((char*)uri);
unsigned char *p=(unsigned char*)RAPTOR_MALLOC(cstring,
uri_length + strlen((const char*)local_name) + sizeof(char*));
if(!p)
return NULL;
strcpy((char*)p, (const char*)uri);
strcpy((char*)p + uri_length, (const char*)local_name);
return (raptor_uri*)p;
}
/**
* raptor_new_uri_from_uri_local_name:
* @uri: existing #raptor_uri
* @local_name: local name
*
* Constructor - create a raptor URI from an existing URI and a local name.
*
* Creates a new URI from the concatenation of the @local_name to the
* @uri. This is NOT relative URI resolution, which is done by the
* raptor_new_uri_relative_to_base() constructor.
*
* Return value: a new #raptor_uri object or NULL on failure.
**/
raptor_uri*
raptor_new_uri_from_uri_local_name(raptor_uri *uri, const unsigned char *local_name)
{
if(!uri || !local_name)
return NULL;
return (*raptor_uri_current_uri_handler->new_uri_from_uri_local_name)(raptor_uri_current_uri_context, uri, local_name);
}
static raptor_uri*
raptor_default_new_uri_relative_to_base(void *context,
raptor_uri *base_uri,
const unsigned char *uri_string)
{
raptor_uri* new_uri;
size_t new_uri_len=strlen((const char*)base_uri)+strlen((const char*)uri_string) + sizeof(char*);
/* +2 is for \0 plus an extra 1 for adding any missing URI path '/' */
new_uri=(raptor_uri*)RAPTOR_MALLOC(cstring, new_uri_len+2);
if(!new_uri)
return NULL;
/* If URI string is empty, just copy base URI */
if(!*uri_string) {
strcpy((char*)new_uri, (char*)base_uri);
return new_uri;
}
raptor_uri_resolve_uri_reference((const unsigned char*)base_uri, uri_string,
(unsigned char*)new_uri, new_uri_len);
return new_uri;
}
/**
* raptor_new_uri_relative_to_base:
* @base_uri: existing base URI
* @uri_string: relative URI string
*
* Constructor - create a raptor URI from a base URI and a relative URI string.
*
* Return value: a new #raptor_uri object or NULL on failure.
**/
raptor_uri*
raptor_new_uri_relative_to_base(raptor_uri *base_uri,
const unsigned char *uri_string)
{
if(!base_uri || !uri_string)
return NULL;
return (*raptor_uri_current_uri_handler->new_uri_relative_to_base)(raptor_uri_current_uri_context, base_uri, uri_string);
}
/**
* raptor_new_uri_from_id:
* @base_uri: existing base URI
* @id: RDF ID
*
* Constructor - create a new URI from a base URI and RDF ID.
*
* This creates a URI equivalent to concatenating @base_uri with
* ## and @id.
*
* Return value: a new #raptor_uri object or NULL on failure.
**/
raptor_uri*
raptor_new_uri_from_id(raptor_uri *base_uri, const unsigned char *id)
{
raptor_uri *new_uri;
unsigned char *local_name;
int len;
if(!base_uri || !id)
return NULL;
#if defined(RAPTOR_DEBUG) && RAPTOR_DEBUG > 1
RAPTOR_DEBUG2("Using ID %s\n", id);
#endif
/* "#id\0" */
len=1+strlen((char*)id) + sizeof(char*);
local_name=(unsigned char*)RAPTOR_MALLOC(cstring, len);
if(!local_name)
return NULL;
*local_name='#';
strcpy((char*)local_name+1, (char*)id);
new_uri=raptor_new_uri_relative_to_base(base_uri, local_name);
RAPTOR_FREE(cstring, local_name);
return new_uri;
}
static raptor_uri*
raptor_default_new_uri_for_rdf_concept(void *context, const char *name)
{
raptor_uri *new_uri;
const unsigned char *base_uri=raptor_rdf_namespace_uri;
unsigned int base_uri_len=raptor_rdf_namespace_uri_len;
unsigned int new_uri_len;
new_uri_len=base_uri_len+strlen(name) + sizeof(char*);
new_uri=(raptor_uri*)RAPTOR_MALLOC(cstring, new_uri_len);
if(!new_uri)
return NULL;
strcpy((char*)new_uri, (const char*)base_uri);
strcpy((char*)new_uri+base_uri_len, name);
return new_uri;
}
/**
* raptor_new_uri_for_rdf_concept:
* @name: RDF namespace concept
*
* Constructor - create a raptor URI for the RDF namespace concept name.
*
* Example: u=raptor_new_uri_for_rdf_concept("value") creates a new
* URI for the rdf:value term.
*
* Return value: a new #raptor_uri object or NULL on failure
**/
raptor_uri*
raptor_new_uri_for_rdf_concept(const char *name)
{
if(!name)
return NULL;
return (*raptor_uri_current_uri_handler->new_uri_for_rdf_concept)(raptor_uri_current_uri_context, name);
}
static void
raptor_default_free_uri(void *context, raptor_uri *uri)
{
RAPTOR_FREE(raptor_uri, uri);
}
/**
* raptor_free_uri:
* @uri: URI to destroy
*
* Destructor - destroy a #raptor_uri object
**/
void
raptor_free_uri(raptor_uri *uri)
{
(*raptor_uri_current_uri_handler->free_uri)(raptor_uri_current_uri_context, uri);
}
static int
raptor_default_uri_equals(void *context, raptor_uri* uri1, raptor_uri* uri2)
{
return strcmp((char*)uri1, (char*)uri2)==0;
}
static int
raptor_default_uri_compare(void *context, raptor_uri* uri1, raptor_uri* uri2)
{
return strcmp((char*)uri1, (char*)uri2);
}
/**
* raptor_uri_equals:
* @uri1: URI 1 (may be NULL)
* @uri2: URI 2 (may be NULL)
*
* Check if two URIs are equal.
*
* A NULL URI is not equal to a non-NULL URI.
*
* Return value: non-0 if the URIs are equal
**/
int
raptor_uri_equals(raptor_uri* uri1, raptor_uri* uri2)
{
if(uri1 && uri2)
/* Both not-NULL - check with handler */
return (*raptor_uri_current_uri_handler->uri_equals)(raptor_uri_current_uri_context, uri1, uri2);
else if(uri1 || uri2)
/* Only one is NULL - not equal */
return 0;
else
/* both NULL - equal */
return 1;
}
/**
* raptor_uri_compare:
* @uri1: URI 1 (may be NULL)
* @uri2: URI 2 (may be NULL)
*
* Compare two URIs, ala strcmp.
*
* A NULL URI is always less than (never equal to) a non-NULL URI.
*
* Return value: -1 if uri1 < uri2, 0 if equal, 1 if uri1 > uri2
**/
int
raptor_uri_compare(raptor_uri* uri1, raptor_uri* uri2)
{
if(uri1 && uri2) {
/* string compare function is available in API V2 or newer */
if(raptor_uri_current_uri_handler->initialised >= 2)
return (*raptor_uri_current_uri_handler->uri_compare)(raptor_uri_current_uri_context, uri1, uri2);
else
return raptor_default_uri_compare(raptor_uri_current_uri_context,
uri1, uri2);
} else if(uri1)
/* uri1 > uri2 (NULL) */
return 1;
else
/* uri1 (NULL) < uri2 */
return -1;
}
static raptor_uri*
raptor_default_uri_copy(void *context, raptor_uri *uri)
{
raptor_uri* new_uri=(raptor_uri*)RAPTOR_MALLOC(cstring, strlen((char*)uri) + sizeof(char*));
if(!new_uri)
return NULL;
strcpy((char*)new_uri, (char*)uri);
return new_uri;
}
/**
* raptor_uri_copy:
* @uri: URI object
*
* Constructor - get a copy of a URI.
*
*
* Return value: a new #raptor_uri object or NULL on failure
**/
raptor_uri*
raptor_uri_copy(raptor_uri *uri)
{
if(!uri)
return NULL;
return (*raptor_uri_current_uri_handler->uri_copy)(raptor_uri_current_uri_context, uri);
}
static unsigned char*
raptor_default_uri_as_string(void *context, raptor_uri *uri)
{
return (unsigned char*)uri;
}
/**
* raptor_uri_as_string:
* @uri: #raptor_uri object
*
* Get a string representation of a URI.
*
* Returns a shared pointer to a string representation of @uri. This
* string is shared and must not be freed, otherwise see use the
* raptor_uri_to_string() or raptor_uri_to_counted_string() methods.
*
* Return value: shared string representation of URI
**/
unsigned char*
raptor_uri_as_string(raptor_uri *uri)
{
if(!uri)
return NULL;
return (*raptor_uri_current_uri_handler->uri_as_string)(raptor_uri_current_uri_context, uri);
}
static unsigned char*
raptor_default_uri_as_counted_string(void *context, raptor_uri *uri,
size_t* len_p)
{
if(len_p)
*len_p=strlen((char*)uri);
return (unsigned char*)uri;
}
/**
* raptor_uri_as_counted_string:
* @uri: URI object
* @len_p: address of length variable or NULL
*
* Get a string representation of a URI with count.
*
* Returns a shared pointer to a string representation of @uri along
* with the length of the string in @len_p, if not NULL. This
* string is shared and must not be freed, otherwise see use the
* raptor_uri_to_string() or raptor_uri_to_counted_string() methods.
*
* Return value: shared string representation of URI
**/
unsigned char*
raptor_uri_as_counted_string(raptor_uri *uri, size_t* len_p)
{
if(!uri)
return NULL;
return (*raptor_uri_current_uri_handler->uri_as_counted_string)(raptor_uri_current_uri_context, uri, len_p);
}
/**
* raptor_uri_filename_to_uri_string:
* @filename: The filename to convert
*
* Converts a filename to a file: URI.
*
* Handles the OS-specific escaping on turning filenames into URIs
* and returns a new buffer that the caller must free(). Turns
* a space in the filname into %20 and '%' into %25.
*
* Return value: A newly allocated string with the URI or NULL on failure
**/
unsigned char *
raptor_uri_filename_to_uri_string(const char *filename)
{
unsigned char *buffer=NULL;
const char *from;
char *to;
#ifndef WIN32
char *path=NULL;
#endif
/* "file://" ... \0 */
size_t len=7 + sizeof(char*);
if(!filename)
return NULL;
#ifdef WIN32
/*
* On WIN32, filenames turn into
* "file://" + translated filename
* where the translation is \\ turns into / and ' ' into %20, '%' into %25
* and if the filename does not start with '\', it is relative
* in which case, a . is appended to the authority
*
* e.g
* FILENAME URI
* c:\windows\system file:///c:/windows/system
* \\server\dir\file.doc file://server/dir/file.doc
* a:foo file:///a:./foo
* C:\Documents and Settings\myapp\foo.bat
* file:///C:/Documents%20and%20Settings/myapp/foo.bat
*
* There are also UNC names \\server\share\blah
* that turn into file:///server/share/blah
* using the above algorithm.
*/
if(filename[1] == ':' && filename[2] != '\\')
len+=3; /* relative filename - add / and ./ */
else if(*filename == '\\')
len-=2; /* two // from not needed in filename */
else
len++; /* / at start of path */
#else
/* others - unix: turn spaces into %20, '%' into %25 */
if(*filename != '/') {
size_t path_max;
#ifdef PATH_MAX
path_max=PATH_MAX;
#else
path_max=1024; /* an initial guess at the length */
#endif
path=(char*)malloc(path_max);
while(1) {
/* malloc() failed or getcwd() succeeded */
if(!path || getcwd(path, path_max))
break;
/* failed */
if(errno != ERANGE)
break;
/* try again with a bigger buffer */
path_max *= 2;
path=(char*)realloc(path, path_max);
}
if(!path)
goto path_done;
strcat(path, "/");
strcat(path, filename);
filename=(const char*)path;
}
#endif
/* add URI-escaped filename length */
for(from=filename; *from ; from++) {
len++;
if(*from == ' ' || *from == '%')
len+=2; /* strlen(%xx)-1 */
}
buffer=(unsigned char*)RAPTOR_MALLOC(cstring, len);
if(!buffer)
goto path_done;
strcpy((char*)buffer, "file://");
from=filename;
to=(char*)(buffer+7);
#ifdef WIN32
if(*from == '\\' && from[1] == '\\')
from+=2;
else
*to++ ='/';
#endif
while(*from) {
char c=*from++;
#ifdef WIN32
if (c == '\\')
*to++ ='/';
else if(c == ':') {
*to++ = c;
if(*from != '\\') {
*to++ ='.';
*to++ ='/';
}
} else
#endif
if(c == ' ' || c == '%') {
*to++ ='%';
*to++ ='2';
*to++ =(c == ' ') ? '0' : '5';
} else
*to++ =c;
}
*to='\0';
path_done:
#ifndef WIN32
if(path)
free(path);
#endif
return buffer;
}
/**
* raptor_uri_uri_string_to_filename_fragment:
* @uri_string: The file: URI to convert
* @fragment_p: Address of pointer to store any URI fragment or NULL
*
* Convert a file: URI to a filename and fragment.
*
* Handles the OS-specific file: URIs to filename mappings. Returns
* a new buffer containing the filename that the caller must free.
*
* If @fragment_p is given, a new string containing the URI fragment
* is returned, or NULL if none is present
*
* Return value: A newly allocated string with the filename or NULL on failure
**/
char *
raptor_uri_uri_string_to_filename_fragment(const unsigned char *uri_string,
unsigned char **fragment_p)
{
char *filename;
size_t len=0;
raptor_uri_detail *ud=NULL;
unsigned char *from;
char *to;
#ifdef WIN32
unsigned char *p;
#endif
if(!uri_string || !*uri_string)
return NULL;
ud=raptor_new_uri_detail(uri_string);
if(!ud)
return NULL;
if(!ud->scheme || raptor_strcasecmp((const char*)ud->scheme, "file")) {
raptor_free_uri_detail(ud);
return NULL;
}
if(ud->authority) {
if(!*ud->authority)
ud->authority=NULL;
else if(!raptor_strcasecmp((const char*)ud->authority, "localhost"))
ud->authority=NULL;
}
/* Cannot do much if there is no path */
if(!ud->path || (ud->path && !*ud->path)) {
raptor_free_uri_detail(ud);
return NULL;
}
/* See raptor_uri_filename_to_uri_string for details of the mapping */
#ifdef WIN32
if(ud->authority)
len+=ud->authority_len+3;
p=ud->path;
/* remove leading slash from path if there is one */
if(*p && p[0] == '/') {
p++;
len--;
}
/* handle case where path starts with drive letter */
if(*p && (p[1] == '|' || p[1] == ':')) {
/* Either
* "a:" like in file://a|/... or file://a:/...
* or
* "a:." like in file://a:./foo
* giving device-relative path a:foo
*/
if(p[2]=='.') {
p[2]=*p;
p[3]=':';
p+= 2;
len-= 2; /* remove 2 for ./ */
} else
p[1]=':';
}
#endif
/* add URI-escaped filename length */
for(from=ud->path; *from ; from++) {
len++;
if(*from == '%')
from+= 2;
}
/* Something is wrong */
if(!len) {
raptor_free_uri_detail(ud);
return NULL;
}
filename=(char*)RAPTOR_MALLOC(cstring, len + sizeof(char*));
if(!filename) {
raptor_free_uri_detail(ud);
return NULL;
}
to=filename;
#ifdef WIN32
if(ud->authority) {
*to++ = '\\';
*to++ = '\\';
from=ud->authority;
while( (*to++ = *from++) )
;
to--;
*to++ = '\\';
}
/* copy path after all /s */
from=p;
#else
from=ud->path;
#endif
while(*from) {
char c=*from++;
#ifdef WIN32
if(c == '/')
*to++ ='\\';
else
#endif
if(c == '%') {
if(*from && from[1]) {
char hexbuf[3];
char *endptr=NULL;
hexbuf[0]=(char)*from;
hexbuf[1]=(char)from[1];
hexbuf[2]='\0';
c=(char)strtol((const char*)hexbuf, &endptr, 16);
if(endptr == &hexbuf[2])
*to++ = c;
}
from+= 2;
} else
*to++ =c;
}
*to='\0';
if(fragment_p) {
if(ud->fragment) {
len=ud->fragment_len;
*fragment_p=(unsigned char*)RAPTOR_MALLOC(cstring, len + sizeof(char*));
if(*fragment_p)
strncpy((char*)*fragment_p, (const char*)ud->fragment, len+1);
} else
*fragment_p=NULL;
}
raptor_free_uri_detail(ud);
return filename;
}
/**
* raptor_uri_uri_string_to_filename:
* @uri_string: The file: URI to convert
*
* Convert a file: URI to a filename.
*
* Handles the OS-specific file: URIs to filename mappings. Returns
* a new buffer containing the filename that the caller must free.
*
* Return value: A newly allocated string with the filename or NULL on failure
**/
char *
raptor_uri_uri_string_to_filename(const unsigned char *uri_string)
{
return raptor_uri_uri_string_to_filename_fragment(uri_string, NULL);
}
/**
* raptor_uri_is_file_uri:
* @uri_string: The URI string to check
*
* @Deprecated: use raptor_uri_uri_string_is_file_uri
*
* Check if a URI string is a file: URI.
*
* Return value: Non zero if URI string is a file: URI
**/
int
raptor_uri_is_file_uri(const unsigned char* uri_string) {
return raptor_uri_uri_string_is_file_uri(uri_string);
}
/**
* raptor_uri_uri_string_is_file_uri:
* @uri_string: The URI string to check
*
* Check if a URI string is a file: URI.
*
* Return value: Non zero if URI string is a file: URI
**/
int
raptor_uri_uri_string_is_file_uri(const unsigned char* uri_string) {
if(!uri_string || !*uri_string)
return 1;
return raptor_strncasecmp((const char*)uri_string, "file:", 5)==0;
}
/**
* raptor_new_uri_for_xmlbase:
* @old_uri: URI to transform
*
* Constructor - create a URI suitable for use as an XML Base.
*
* Takes an existing URI and ensures it has a path (default /) and has
* no fragment or query arguments - XML base does not use these.
*
* Return value: new #raptor_uri object or NULL on failure.
**/
raptor_uri*
raptor_new_uri_for_xmlbase(raptor_uri* old_uri)
{
unsigned char *uri_string;
unsigned char *new_uri_string;
raptor_uri* new_uri;
raptor_uri_detail *ud;
if(!old_uri)
return NULL;
uri_string=raptor_uri_as_string(old_uri);
ud=raptor_new_uri_detail(uri_string);
if(!ud)
return NULL;
if(!ud->path) {
ud->path=(unsigned char*)"/";
ud->path_len=1;
}
ud->query=NULL; ud->query_len=0;
ud->fragment=NULL; ud->fragment_len=0;
new_uri_string=raptor_uri_detail_to_string(ud, NULL);
raptor_free_uri_detail(ud);
if(!new_uri_string)
return NULL;
new_uri=raptor_new_uri(new_uri_string);
RAPTOR_FREE(cstring, new_uri_string);
return new_uri;
}
/**
* raptor_new_uri_for_retrieval:
* @old_uri: URI to transform
*
* Constructor - create a URI suitable for retrieval.
*
* Takes an existing URI and ensures it has a path (default /) and has
* no fragment - URI retrieval does not use the fragment part.
*
* Return value: new #raptor_uri object or NULL on failure.
**/
raptor_uri*
raptor_new_uri_for_retrieval(raptor_uri* old_uri)
{
unsigned char *uri_string;
unsigned char *new_uri_string;
raptor_uri* new_uri;
raptor_uri_detail *ud;
if(!old_uri)
return NULL;
uri_string=raptor_uri_as_string(old_uri);
ud=raptor_new_uri_detail(uri_string);
if(!ud)
return NULL;
if(!ud->path) {
ud->path=(unsigned char*)"/";
ud->path_len=1;
}
ud->fragment=NULL; ud->fragment_len=0;
new_uri_string=raptor_uri_detail_to_string(ud, NULL);
raptor_free_uri_detail(ud);
if(!new_uri_string)
return NULL;
new_uri=raptor_new_uri(new_uri_string);
RAPTOR_FREE(cstring, new_uri_string);
return new_uri;
}
static const raptor_uri_handler raptor_uri_default_handler = {
raptor_default_new_uri,
raptor_default_new_uri_from_uri_local_name,
raptor_default_new_uri_relative_to_base,
raptor_default_new_uri_for_rdf_concept,
raptor_default_free_uri,
raptor_default_uri_equals,
raptor_default_uri_copy,
raptor_default_uri_as_string,
raptor_default_uri_as_counted_string,
2, /* URI Interface Version */
raptor_default_uri_compare /* URI Interface V2 */
};
int
raptor_uri_init(void)
{
raptor_uri_set_handler(&raptor_uri_default_handler, NULL);
return 0;
}
/*
* raptor_uri_path_common_base_length:
* @first_path: The first path (path only, not a full URI)
* @first_path_len: Length of first_path
* @second_path: The second path (path only, not a full URI)
* @second_path_len: Length of second_path
*
* Find the common base length of two URI path components.
*
* Return value: Length of the common base path
**/
static int
raptor_uri_path_common_base_length(const unsigned char *first_path, size_t first_path_len,
const unsigned char *second_path, size_t second_path_len)
{
int common_len=0;
const unsigned char *cur_ptr=first_path;
const unsigned char *prev_ptr=first_path;
/* Compare each path component of first_path and second_path until there is
a mismatch. Then return the length from the start of the path to the last
successful match. */
while((cur_ptr=(const unsigned char*)memchr(cur_ptr, '/', first_path_len))) {
cur_ptr++;
if(strncmp((const char*)first_path+common_len,
(const char*)second_path+common_len, cur_ptr-prev_ptr))
break;
first_path_len -= cur_ptr - prev_ptr;
prev_ptr=cur_ptr;
common_len = prev_ptr - first_path;
}
return prev_ptr - first_path;
}
/*
* raptor_uri_path_make_relative_path:
* @from_path: The base path (path only, not a full URI)
* @from_path_len: Length of the base path
* @to_path: The reference path (path only, not a full URI)
* @to_path_len: Length of the reference path
* @suffix: String to be appended to the final relative path
* @suffix_len: Length of the suffix
* @result_length_p: Location to store the length of the string or NULL
*
* Make a relative URI path.
*
* Return value: A newly allocated relative path string or NULL on failure.
**/
static unsigned char *
raptor_uri_path_make_relative_path(const unsigned char *from_path, size_t from_path_len,
const unsigned char *to_path, size_t to_path_len,
const unsigned char *suffix, size_t suffix_len,
size_t *result_length_p)
{
int common_len, cur_len, final_len, up_dirs = 0, to_dir_len = 0;
const unsigned char *cur_ptr, *prev_ptr;
unsigned char *final_path, *final_path_cur;
common_len=raptor_uri_path_common_base_length(from_path, from_path_len,
to_path, to_path_len);
if(result_length_p)
*result_length_p=0;
/* Count how many directories we have to go up */
cur_ptr = from_path + common_len;
prev_ptr=cur_ptr;
cur_len = from_path_len - common_len;
while((cur_ptr = (const unsigned char*)memchr(cur_ptr, '/', cur_len))) {
cur_ptr++;
up_dirs++;
cur_len -= cur_ptr - prev_ptr;
prev_ptr=cur_ptr;
}
/* Calculate how many characters of to_path subdirs (counted from the
common base) we have to add. */
cur_ptr = to_path + common_len;
prev_ptr=cur_ptr;
cur_len = to_path_len - common_len;
while((cur_ptr = (const unsigned char*)memchr(cur_ptr, '/', cur_len))) {
cur_ptr++;
cur_len -= cur_ptr - prev_ptr;
prev_ptr=cur_ptr;
}
to_dir_len = prev_ptr - (to_path + common_len);
/* Create the final relative path */
final_len = up_dirs*3 + to_dir_len + suffix_len; /* 3 for each "../" */
final_path=(unsigned char*)RAPTOR_MALLOC(cstring, final_len + sizeof(char*));
if(!final_path)
return NULL;
*final_path=0;
/* First, add the necessary "../" parts */
final_path_cur=final_path;
while (up_dirs--) {
*final_path_cur++='.';
*final_path_cur++='.';
*final_path_cur++='/';
}
/* Then, add the path from the common base to the to_path */
memcpy(final_path_cur, to_path + common_len, to_dir_len);
final_path_cur+=to_dir_len;
/* Finally, add the suffix */
if(suffix && suffix_len) {
/* As a special case, if the suffix begins with a dot (".") and the final
output string so far is non-empty, skip the dot. */
if (*suffix == '.' && final_path_cur != final_path) {
/* Make sure that the dot really represents a directory and it's not
just part of a file name like ".foo". In other words, the dot must
either be the only character or the next character must be the
fragment or the query character. */
if ((suffix_len == 1) ||
(suffix_len > 1 && (suffix[1] == '#' || suffix[1] == '?'))) {
suffix++;
suffix_len--;
final_len--;
}
}
if(suffix_len)
memcpy(final_path_cur, suffix, suffix_len);
}
final_path[final_len]=0;
if (result_length_p)
*result_length_p=final_len;
return final_path;
}
/**
* raptor_uri_to_relative_counted_uri_string:
* @base_uri: The base absolute URI to resolve against (or NULL)
* @reference_uri: The reference absolute URI to use
* @length_p: Location to store the length of the relative URI string or NULL
*
* Get the counted relative URI string of a URI against a base URI.
*
* Return value: A newly allocated relative URI string or NULL on failure
**/
unsigned char*
raptor_uri_to_relative_counted_uri_string(raptor_uri *base_uri,
raptor_uri *reference_uri,
size_t *length_p) {
raptor_uri_detail *base_detail=NULL, *reference_detail;
const unsigned char *base, *reference_str, *base_file, *reference_file;
unsigned char *suffix, *cur_ptr;
size_t base_len, reference_len, reference_file_len, suffix_len;
unsigned char *result=NULL;
if(!reference_uri)
return NULL;
if(length_p)
*length_p=0;
reference_str=raptor_uri_as_counted_string(reference_uri, &reference_len);
reference_detail=raptor_new_uri_detail(reference_str);
if(!reference_detail)
goto err;
if(!base_uri)
goto buildresult;
base=raptor_uri_as_counted_string(base_uri, &base_len);
base_detail=raptor_new_uri_detail(base);
if(!base_detail)
goto err;
/* Check if the whole URIs are equal */
if(raptor_uri_equals(base_uri, reference_uri)) {
reference_len=0;
goto buildresult;
}
/* Check if scheme and authority of the URIs are equal */
if(base_detail->scheme_len == reference_detail->scheme_len &&
base_detail->authority_len == reference_detail->authority_len &&
!strncmp((const char*)base_detail->scheme,
(const char*)reference_detail->scheme,
base_detail->scheme_len) &&
!strncmp((const char*)base_detail->authority,
(const char*)reference_detail->authority,
base_detail->authority_len)) {
if(!base_detail->path)
goto buildresult;
/* Find the file name components */
base_file = (const unsigned char*)strrchr((const char*)base_detail->path, '/');
if(!base_file)
goto buildresult;
base_file++;
if(!reference_detail->path)
goto buildresult;
reference_file=(const unsigned char*)strrchr((const char*)reference_detail->path, '/');
if(!reference_file)
goto buildresult;
reference_file++;
reference_file_len=reference_detail->path_len -
(reference_file - reference_detail->path);
if(!strcmp((const char*)base_file, (const char*)reference_file)) {
/* If the file names are equal, don't put them in the relative URI */
reference_file=NULL;
reference_file_len=0;
} else if(*base_file && !*reference_file) {
/* If the base file is non-empty, but the reference file is
* empty, use "." as the file name.
*/
reference_file=(const unsigned char*)".";
reference_file_len=1;
}
/* Calculate the length of the suffix (file name + query + fragment) */
suffix_len=reference_file_len + reference_detail->query_len +
reference_detail->fragment_len;
if (reference_detail->query)
suffix_len++; /* add one char for the '?' */
if (reference_detail->fragment)
suffix_len++; /* add one char for the '#' */
/* Assemble the suffix */
suffix=(unsigned char*)RAPTOR_MALLOC(cstring, suffix_len + sizeof(char*));
if(!suffix)
goto err;
cur_ptr=suffix;
if(reference_file) {
memcpy(suffix, reference_file, reference_file_len);
cur_ptr+= reference_file_len;
}
if(reference_detail->query) {
*cur_ptr++='?';
memcpy(cur_ptr, reference_detail->query, reference_detail->query_len);
cur_ptr+= reference_detail->query_len;
}
if(reference_detail->fragment) {
*cur_ptr++='#';
memcpy(cur_ptr, reference_detail->fragment, reference_detail->fragment_len);
cur_ptr+= reference_detail->fragment_len;
}
*cur_ptr=0;
/* Finally, create the full relative path */
result = raptor_uri_path_make_relative_path(base_detail->path,
base_detail->path_len,
reference_detail->path,
reference_detail->path_len,
suffix,
suffix_len,
length_p);
RAPTOR_FREE(cstring, suffix);
}
buildresult:
/* If result is NULL at this point, it means that we were unable to find a
relative URI, so we'll return a full absolute URI instead. */
if(!result) {
result=(unsigned char*)RAPTOR_MALLOC(cstring, reference_len + sizeof(char*));
if(result) {
if(reference_len)
memcpy(result, reference_str, reference_len);
result[reference_len] = 0;
if(length_p)
*length_p=reference_len;
}
}
err:
if(base_detail)
raptor_free_uri_detail(base_detail);
raptor_free_uri_detail(reference_detail);
return result;
}
/**
* raptor_uri_to_relative_uri_string:
* @base_uri: The base absolute URI to resolve against
* @reference_uri: The reference absolute URI to use
*
* Get the relative URI string of a URI against a base URI.
*
* Return value: A newly allocated relative URI string or NULL on failure
**/
unsigned char*
raptor_uri_to_relative_uri_string(raptor_uri *base_uri,
raptor_uri *reference_uri) {
return raptor_uri_to_relative_counted_uri_string(base_uri,
reference_uri, NULL);
}
/**
* raptor_uri_print:
* @uri: URI to print
* @stream: The file handle to print to
*
* Print a URI to a file handle.
*
**/
void
raptor_uri_print(const raptor_uri* uri, FILE *stream) {
if(uri) {
size_t len;
unsigned char *string=raptor_uri_as_counted_string((raptor_uri*)uri, &len);
(void)fwrite(string, len, 1, stream);
} else
(void)fwrite("(NULL URI)", 10, 1, stream);
}
/**
* raptor_uri_to_counted_string:
* @uri: #raptor_uri object
* @len_p: Pointer to length (or NULL)
*
* Get a new counted string for a URI.
*
* If @len_p is not NULL, the length of the string is stored in it.
*
* The memory allocated must be freed by the caller and
* raptor_free_memory() should be used for best portability.
*
* Return value: new string or NULL on failure
**/
unsigned char*
raptor_uri_to_counted_string(raptor_uri *uri, size_t *len_p)
{
size_t len;
unsigned char *string;
unsigned char *new_string;
if(!uri)
return NULL;
string=raptor_uri_as_counted_string(uri, &len);
if(!string)
return NULL;
new_string=(unsigned char*)RAPTOR_MALLOC(cstring, len + 1); /* +1 for NUL termination */
if(!new_string)
return NULL;
memcpy(new_string, string, len+1);
if(len_p)
*len_p=len;
return new_string;
}
/**
* raptor_uri_to_string:
* @uri: #raptor_uri object
*
* Get a new string for a URI.
*
* The memory allocated must be freed by the caller and
* raptor_free_memory() should be used for best portability.
*
* Return value: new string or NULL on failure
**/
unsigned char*
raptor_uri_to_string(raptor_uri *uri)
{
return raptor_uri_to_counted_string(uri, NULL);
}
/**
* raptor_new_uri_from_rdf_ordinal:
* @ordinal: integer rdf:_n
*
* Internal - convert an integer rdf:_n ordinal to the resource URI
*
* Return value: new URI object or NULL on failure
**/
raptor_uri*
raptor_new_uri_from_rdf_ordinal(int ordinal)
{
/* 55 = strlen(rdf namespace URI) + _ + 10-digit number + \0 */
unsigned char uri_string[55];
strncpy((char*)uri_string, (const char*)raptor_rdf_namespace_uri,
raptor_rdf_namespace_uri_len);
sprintf((char*)uri_string+raptor_rdf_namespace_uri_len, "_%d",
ordinal);
return raptor_new_uri(uri_string);
}
#ifdef STANDALONE
#include <stdio.h>
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
/* one more prototype */
int main(int argc, char *argv[]);
static const char *program;
static int
assert_filename_to_uri (const char *filename, const char *reference_uri)
{
unsigned char *uri;
uri=raptor_uri_filename_to_uri_string(filename);
if (!uri || strcmp((const char*)uri, (const char*)reference_uri)) {
fprintf(stderr,
"%s: raptor_uri_filename_to_uri_string(%s) FAILED gaving URI %s != %s\n",
program, filename, uri, reference_uri);
if(uri)
RAPTOR_FREE(cstring, uri);
return 1;
}
RAPTOR_FREE(cstring, uri);
return 0;
}
static int
assert_uri_to_filename (const char *uri, const char *reference_filename)
{
char *filename;
filename=raptor_uri_uri_string_to_filename((const unsigned char*)uri);
if(filename && !reference_filename) {
fprintf(stderr,
"%s: raptor_uri_uri_string_to_filename(%s) FAILED giving filename %s != NULL\n",
program, uri, filename);
if(filename)
RAPTOR_FREE(cstring, filename);
return 1;
} else if (filename && strcmp(filename, reference_filename)) {
fprintf(stderr,
"%s: raptor_uri_uri_string_to_filename(%s) FAILED gaving filename %s != %s\n",
program, uri, filename, reference_filename);
if(filename)
RAPTOR_FREE(cstring, filename);
return 1;
}
RAPTOR_FREE(cstring, filename);
return 0;
}
static int
assert_uri_to_relative(const char *base, const char *uri, const char *relative)
{
unsigned char *output;
int result;
raptor_uri* base_uri=NULL;
raptor_uri* reference_uri=raptor_new_uri((const unsigned char*)uri);
size_t length=0;
if(base)
base_uri=raptor_new_uri((const unsigned char*)base);
output=raptor_uri_to_relative_counted_uri_string(base_uri, reference_uri,
&length);
result=strcmp(relative, (const char*)output);
if (result) {
fprintf(stderr,
"%s: raptor_uri_string_to_relative_uri_string FAILED: base='%s', uri='%s', expected='%s', got='%s'\n",
program, base, uri, relative, output);
RAPTOR_FREE(cstring, output);
return 1;
}
RAPTOR_FREE(cstring, output);
raptor_free_uri(base_uri);
raptor_free_uri(reference_uri);
return 0;
}
static int
raptor_test_uri_compare(void *context, raptor_uri* uri1, raptor_uri* uri2)
{
int* called_p=(int*)context;
*called_p=1;
return strcmp((char*)uri1, (char*)uri2);
}
int
main(int argc, char *argv[])
{
const char *base_uri = "http://example.org/bpath/cpath/d;p?querystr#frag";
const char *base_uri_xmlbase = "http://example.org/bpath/cpath/d;p";
const char *base_uri_retrievable = "http://example.org/bpath/cpath/d;p?querystr";
#ifndef WIN32
#if defined(HAVE_UNISTD_H) && defined(HAVE_SYS_STAT_H)
const char* dirs[6] = { "/etc", "/bin", "/tmp", "/lib", "/var", NULL };
unsigned char uri_buffer[16]; /* strlen("file:///DIR/foo")+1 */
int i;
const char *dir;
#endif
#endif
unsigned char *str;
raptor_uri *uri1, *uri2, *uri3;
int failures=0;
if((program=strrchr(argv[0], '/')))
program++;
else if((program=strrchr(argv[0], '\\')))
program++;
else
program=argv[0];
#ifdef WIN32
failures += assert_filename_to_uri ("c:\\windows\\system", "file:///c:/windows/system");
failures += assert_filename_to_uri ("\\\\server\\share\\file.doc", "file://server/share/file.doc");
failures += assert_filename_to_uri ("a:foo", "file:///a:./foo");
failures += assert_filename_to_uri ("C:\\Documents and Settings\\myapp\\foo.bat", "file:///C:/Documents%20and%20Settings/myapp/foo.bat");
failures += assert_filename_to_uri ("C:\\My Documents\\%age.txt", "file:///C:/My%20Documents/%25age.txt");
failures += assert_uri_to_filename ("file:///c|/windows/system", "c:\\windows\\system");
failures += assert_uri_to_filename ("file:///c:/windows/system", "c:\\windows\\system");
failures += assert_uri_to_filename ("file://server/share/file.doc", "\\\\server\\share\\file.doc");
failures += assert_uri_to_filename ("file:///a:./foo", "a:foo");
failures += assert_uri_to_filename ("file:///C:/Documents%20and%20Settings/myapp/foo.bat", "C:\\Documents and Settings\\myapp\\foo.bat");
failures += assert_uri_to_filename ("file:///C:/My%20Documents/%25age.txt", "C:\\My Documents\\%age.txt");
failures += assert_uri_to_filename ("file:c:\\thing", "c:\\thing");
failures += assert_uri_to_filename ("file:/c:\\thing", "c:\\thing");
failures += assert_uri_to_filename ("file://c:\\thing", NULL);
failures += assert_uri_to_filename ("file:///c:\\thing", "c:\\thing");
failures += assert_uri_to_filename ("file://localhost/", NULL);
failures += assert_uri_to_filename ("file://c:\\foo\\bar\\x.rdf", NULL);
#else
failures += assert_filename_to_uri ("/path/to/file", "file:///path/to/file");
failures += assert_filename_to_uri ("/path/to/file with spaces", "file:///path/to/file%20with%20spaces");
failures += assert_uri_to_filename ("file:///path/to/file", "/path/to/file");
failures += assert_uri_to_filename ("file:///path/to/file%20with%20spaces", "/path/to/file with spaces");
#if defined(HAVE_UNISTD_H) && defined(HAVE_SYS_STAT_H)
/* Need to test this with a real dir (preferably not /)
* This is just a test so pretty likely to work on all development systems
* that are not WIN32
*/
for(i=0; (dir=dirs[i]); i++) {
struct stat buf;
if(!lstat(dir, &buf) && S_ISDIR(buf.st_mode) && !S_ISLNK(buf.st_mode)) {
if(!chdir(dir))
break;
}
}
if(!dir)
fprintf(stderr,
"%s: WARNING: Found no convenient directory - not testing relative files\n",
program);
else {
sprintf((char*)uri_buffer, "file://%s/foo", dir);
fprintf(stderr,
"%s: Checking relative file name 'foo' in dir %s expecting URI %s\n",
program, dir, uri_buffer);
failures += assert_filename_to_uri ("foo", (const char*)uri_buffer);
}
#endif
#endif
raptor_uri_init();
uri1=raptor_new_uri((const unsigned char*)base_uri);
str=raptor_uri_as_string(uri1);
if(strcmp((const char*)str, base_uri)) {
fprintf(stderr,
"%s: raptor_uri_as_string(%s) FAILED gaving %s != %s\n",
program, base_uri, str, base_uri);
failures++;
}
uri2=raptor_new_uri_for_xmlbase(uri1);
str=raptor_uri_as_string(uri2);
if(strcmp((const char*)str, base_uri_xmlbase)) {
fprintf(stderr,
"%s: raptor_new_uri_for_xmlbase(URI %s) FAILED giving %s != %s\n",
program, base_uri, str, base_uri_xmlbase);
failures++;
}
uri3=raptor_new_uri_for_retrieval(uri1);
str=raptor_uri_as_string(uri3);
if(strcmp((const char*)str, base_uri_retrievable)) {
fprintf(stderr,
"%s: raptor_new_uri_for_retrievable(%s) FAILED gaving %s != %s\n",
program, base_uri, str, base_uri_retrievable);
failures++;
}
raptor_free_uri(uri3);
raptor_free_uri(uri2);
raptor_free_uri(uri1);
failures += assert_uri_to_relative(NULL, "http://example.com/foo/bar", "http://example.com/foo/bar");
failures += assert_uri_to_relative("", "http://example.com/foo/bar", "http://example.com/foo/bar");
failures += assert_uri_to_relative("foo:", "http://example.com/foo/bar", "http://example.com/foo/bar");
failures += assert_uri_to_relative("http://example.com/base/foo?foo#foo", "http://example.com/base/bar?bar#bar", "bar?bar#bar");
failures += assert_uri_to_relative("http://example.com/base/foo", "http://example.com/base/foo/", "foo/");
failures += assert_uri_to_relative("http://example.com/base/foo", "http://example.com/base/foo/.foo", "foo/.foo");
failures += assert_uri_to_relative("http://example.com/base/foo", "http://example.com/base/foo/.foo#bar", "foo/.foo#bar");
failures += assert_uri_to_relative("http://example.com/base/foo", "http://example.com/base/foo/bar", "foo/bar");
failures += assert_uri_to_relative("http://example.com/base/foo", "http://example.com/base/foo#bar", "#bar");
failures += assert_uri_to_relative("http://example.com/base/foo", "http://example.com/base/bar#foo", "bar#foo");
failures += assert_uri_to_relative("http://example.com/base/foo", "http://example.com/otherbase/bar", "../otherbase/bar");
failures += assert_uri_to_relative("http://example.com/base/foo", "http://example.com/base/#foo", ".#foo");
failures += assert_uri_to_relative("http://example.com/base/foo", "http://example2.com/base/bar", "http://example2.com/base/bar");
failures += assert_uri_to_relative("http://example.com/base/one?path=/should/be/ignored", "http://example.com/base/two?path=/should/be/ignored", "two?path=/should/be/ignored");
failures += assert_uri_to_relative("http://example.org/base#", "http://www.foo.org", "http://www.foo.org");
failures += assert_uri_to_relative("http://example.org", "http://a.example.org/", "http://a.example.org/");
failures += assert_uri_to_relative("http://example.org", "http://a.example.org", "http://a.example.org");
failures += assert_uri_to_relative("http://abcdefgh.example.org/foo/bar/", "http://ijklmnop.example.org/", "http://ijklmnop.example.org/");
if(1) {
raptor_uri_handler uri_handler;
int ret;
raptor_uri* u1;
raptor_uri* u2;
int called;
/* URI Interface V1 */
uri_handler.new_uri = raptor_default_new_uri;
uri_handler.free_uri = raptor_default_free_uri;
uri_handler.uri_compare = raptor_test_uri_compare;
uri_handler.initialised=1;
called=0;
raptor_uri_set_handler(&uri_handler, &called);
u1=raptor_new_uri((const unsigned char *)"http://example.org/abc");
u2=raptor_new_uri((const unsigned char *)"http://example.org/def");
ret=raptor_uri_compare(u1, u2);
if(!(ret < 0)) {
fprintf(stderr,
"%s: raptor_uri_compare(%s, %s) FAILED V1 gave %d expected <0\n",
program, raptor_uri_as_string(u1), raptor_uri_as_string(u2),
ret);
failures++;
}
if(called) {
fprintf(stderr,
"%s: raptor_uri_compare(%s, %s) FAILED V1 called user handler\n",
program, raptor_uri_as_string(u1), raptor_uri_as_string(u2));
failures++;
}
/* URI Interface V2 */
uri_handler.initialised=2;
called=0;
raptor_uri_set_handler(&uri_handler, &called);
ret=raptor_uri_compare(u1, u2);
if(!(ret < 0)) {
fprintf(stderr,
"%s: raptor_uri_compare(%s, %s) FAILED V2 gave %d expected <0\n",
program, raptor_uri_as_string(u1), raptor_uri_as_string(u2),
ret);
failures++;
}
if(!called) {
fprintf(stderr,
"%s: raptor_uri_compare(%s, %s) FAILED V2 did not call user handler\n",
program, raptor_uri_as_string(u1), raptor_uri_as_string(u2));
failures++;
}
raptor_free_uri(u1);
raptor_free_uri(u2);
}
return failures ;
}
#endif