1223 lines
33 KiB
C++
1223 lines
33 KiB
C++
/*
|
|
* Copyright 2006 Sony Computer Entertainment Inc.
|
|
*
|
|
* Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this
|
|
* file except in compliance with the License. You may obtain a copy of the License at:
|
|
* http://research.scea.com/scea_shared_source_license.html
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing permissions and limitations under the
|
|
* License.
|
|
*/
|
|
|
|
#include <dae/daeURI.h>
|
|
#include <ctype.h>
|
|
#include <dae/daeDocument.h>
|
|
#include <dae/daeErrorHandler.h>
|
|
|
|
#ifdef _WIN32
|
|
#include <direct.h> // for getcwd (windows)
|
|
#else
|
|
#include <unistd.h> // for getcwd (linux)
|
|
#endif
|
|
|
|
daeString safeCreate(daeString src);
|
|
void safeDelete(daeString src);
|
|
daeString findCharacterReverse(daeString string, daeChar stopChar);
|
|
|
|
daeURIResolverPtrArray daeURIResolver::_KnownResolvers;
|
|
|
|
static daeURI ApplicationURI(1);
|
|
|
|
void
|
|
daeURI::setBaseURI(daeURI& uri)
|
|
{
|
|
ApplicationURI.reset();
|
|
ApplicationURI.setURI(uri.getURI());
|
|
}
|
|
|
|
daeURI*
|
|
daeURI::getBaseURI()
|
|
{
|
|
return &ApplicationURI;
|
|
}
|
|
|
|
void
|
|
daeURI::initialize()
|
|
{
|
|
// Initialize a URI to it's empty state, same as daeURI::reset but also clears out "container"
|
|
|
|
uriString = NULL;
|
|
originalURIString = NULL;
|
|
protocol = NULL;
|
|
authority = NULL;
|
|
filepath = NULL;
|
|
file = NULL;
|
|
id = NULL;
|
|
extension = NULL;
|
|
state = uri_empty;
|
|
element = NULL;
|
|
container = NULL;
|
|
external = false;
|
|
}
|
|
|
|
daeURI::~daeURI()
|
|
{
|
|
reset();
|
|
}
|
|
|
|
daeURI::daeURI()
|
|
{
|
|
initialize();
|
|
// reset(); // No need to call reset in the constructor, initialize does the same thing.
|
|
}
|
|
daeURI::daeURI(int dummy)
|
|
{
|
|
(void)dummy;
|
|
// This constructor builds a base URI from the current working directory
|
|
// This should work for windows or linux
|
|
// !!!GAC the buffers should probably be bigger
|
|
char buffer[1024], *b1;
|
|
strcpy(buffer, "file:///");
|
|
#ifdef NO_GETCWD
|
|
// The platform has no getcwd call, so leave the value as file:///
|
|
#else
|
|
#ifdef _WIN32
|
|
// Windows getcwd always returns a path beginning with a drive letter, so we add file:/// to the beginning
|
|
getcwd(&buffer[8],1024-8);
|
|
#else
|
|
// Linux getcwd always returns a path beginning with a slash, so we add file:// to the beginning
|
|
getcwd(&buffer[7],1024-7);
|
|
#endif
|
|
#endif
|
|
// If the path contains windows backslashes, flip them to forward slashes
|
|
for(b1 = buffer;*b1 != 0;b1++)
|
|
{
|
|
if(*b1 == '\\')
|
|
{
|
|
*b1 = '/';
|
|
}
|
|
}
|
|
// The path must end in a slash or the last part of it will be taken as a filename
|
|
if(*(b1-1) != '/')
|
|
{
|
|
*(b1++) = '/';
|
|
}
|
|
*b1 = '\0';
|
|
initialize();
|
|
setURI(buffer);
|
|
validate();
|
|
}
|
|
daeURI::daeURI(daeString uriString, daeBool nofrag)
|
|
{
|
|
initialize();
|
|
// !!!GAC this is inefficient as hell, but was the best way to isolate this functionality till the
|
|
// !!!GAC daeURI class can be written to support modifying URIs better (should be possible to make a URI,
|
|
// !!!GAC change any member and have getURI return the proper const string URI)
|
|
if(nofrag)
|
|
{
|
|
// Strip off the fragment part before constructing the URI
|
|
daeString temp = safeCreate(uriString);
|
|
daeChar* fragment = (daeChar*)findCharacterReverse(temp, '#');
|
|
if(fragment)
|
|
{
|
|
*fragment = 0;
|
|
}
|
|
setURI(temp);
|
|
safeDelete(temp);
|
|
}
|
|
else
|
|
{
|
|
// Generate the URI without changing the string
|
|
setURI(uriString);
|
|
}
|
|
if(nofrag)
|
|
validate();
|
|
}
|
|
daeURI::daeURI(daeURI& baseURI, daeString uriString)
|
|
{
|
|
initialize();
|
|
setURI(uriString);
|
|
validate(&baseURI);
|
|
}
|
|
daeURI::daeURI(daeURI& copyFrom)
|
|
{
|
|
initialize();
|
|
setURI(copyFrom.getOriginalURI());
|
|
element = copyFrom.element; // !!!GAC SetURI immediately clears element so we must do this after
|
|
state = copyFrom.state;
|
|
}
|
|
void
|
|
daeURI::copyFrom(daeURI& copyFrom)
|
|
{
|
|
setURI(copyFrom.getOriginalURI());
|
|
element = copyFrom.element; // !!!GAC SetURI immediately clears element so we must do this after
|
|
state = copyFrom.state;
|
|
// !!!GAC Should there be a call to validate in here?
|
|
}
|
|
void
|
|
daeURI::reset()
|
|
{
|
|
// Free up any allocated memory
|
|
|
|
if (uriString != NULL)
|
|
safeDelete(uriString);
|
|
|
|
if (originalURIString != NULL)
|
|
safeDelete(originalURIString);
|
|
|
|
if (protocol != NULL)
|
|
safeDelete(protocol);
|
|
|
|
if (authority != NULL)
|
|
safeDelete(authority);
|
|
|
|
if (filepath != NULL)
|
|
safeDelete(filepath);
|
|
|
|
if (file != NULL)
|
|
safeDelete(file);
|
|
|
|
if (id != NULL)
|
|
safeDelete(id);
|
|
|
|
if (extension != NULL)
|
|
safeDelete(extension);
|
|
|
|
// Set everything to the empty string
|
|
|
|
uriString = NULL;
|
|
originalURIString = NULL;
|
|
protocol = NULL;
|
|
authority = NULL;
|
|
filepath = NULL;
|
|
file = NULL;
|
|
id = NULL;
|
|
extension = NULL;
|
|
|
|
state = uri_empty;
|
|
element = NULL;
|
|
// container = NULL; // !!!GAC don't want to clear this, our container doesn't change once it's set
|
|
}
|
|
daeString
|
|
findCharacterReverse(daeString string, daeChar stopChar)
|
|
{
|
|
if (string == NULL)
|
|
return NULL;
|
|
daeString cur = string + strlen(string)-1;
|
|
while((cur >= string) && (*cur != stopChar))
|
|
cur--;
|
|
|
|
if ((cur >= string) && (*cur == stopChar))
|
|
return cur;
|
|
|
|
return NULL;
|
|
}
|
|
daeString
|
|
findCharacter(daeString string, daeChar stopChar)
|
|
{
|
|
if (string == NULL)
|
|
return NULL;
|
|
daeString end = string + strlen(string);
|
|
daeString cur = string;
|
|
while((*cur != stopChar) && (cur < end))
|
|
cur++;
|
|
|
|
if (*cur == stopChar)
|
|
return cur;
|
|
|
|
return NULL;
|
|
}
|
|
daeString safeCreate(daeString src)
|
|
{
|
|
if (src == NULL)
|
|
return NULL;
|
|
daeChar* ret = (daeChar*)daeMemorySystem::malloc("uri",strlen(src)+1);
|
|
if (ret == NULL)
|
|
return NULL;
|
|
strcpy(ret,src);
|
|
|
|
return ret;
|
|
}
|
|
void safeDelete(daeString src)
|
|
{
|
|
if(src != NULL)
|
|
{
|
|
daeMemorySystem::free("uri",(void*)src);
|
|
src = NULL;
|
|
}
|
|
|
|
}
|
|
void
|
|
daeURI::setURI(daeString _URIString)
|
|
{
|
|
originalURIString = safeCreate(_URIString);
|
|
internalSetURI(_URIString);
|
|
}
|
|
daeChar *validScheme(daeString uri_string)
|
|
{
|
|
// attempt to find a valid scheme in a string
|
|
// Failure to find a scheme returns a NULL, success returns the position of the terminating :
|
|
|
|
// First character must be alpha, fail if it's not
|
|
if(!isalpha(*uri_string))
|
|
return(NULL);
|
|
|
|
// Advance to the next character
|
|
uri_string++;
|
|
|
|
// Scheme must be at least two (valid) characters long, so go through this loop at least once
|
|
do
|
|
{
|
|
// If the character is NOT alpha, digit, +, - or . then this isn't a valid scheme
|
|
// Note this also fails if we encounter a null terminator before hitting the first :
|
|
if(!(isalpha(*uri_string) || isdigit(*uri_string) || *uri_string == '.' || *uri_string == '+' || *uri_string == '-'))
|
|
return(NULL);
|
|
uri_string++;
|
|
} while(*uri_string != ':' );
|
|
|
|
return((daeChar *)uri_string);
|
|
}
|
|
void
|
|
daeURI::internalSetURI(daeString _URIString)
|
|
{
|
|
daeChar* tmp;
|
|
|
|
// Store the originalURI so you can fix it post Reset
|
|
daeString oURI = originalURIString;
|
|
originalURIString = NULL;
|
|
|
|
// Reset everything
|
|
reset();
|
|
|
|
// Fix original URI String
|
|
originalURIString = oURI;
|
|
|
|
uriString = safeCreate(_URIString);
|
|
|
|
tmp = (daeChar*)daeMemorySystem::malloc("tmp",strlen(_URIString)+1);
|
|
if ((uriString == NULL)||(tmp == NULL))
|
|
return;
|
|
strcpy(tmp,uriString);
|
|
|
|
daeChar* curSrc = tmp;
|
|
|
|
#if 1
|
|
// Check for a scheme, two or more characters followed by a :
|
|
|
|
// daeChar* colon = (daeChar*)findCharacter(curSrc,':');
|
|
|
|
daeChar* colon = validScheme(curSrc);
|
|
|
|
// if(colon && (colon-tmp >= 2 ))
|
|
if(colon)
|
|
{
|
|
// Found a scheme, remember it (protocol should be named scheme)
|
|
*colon = '\0';
|
|
protocol = safeCreate(curSrc);
|
|
// Advance the current pointer past the colon
|
|
curSrc = colon+1;
|
|
}
|
|
|
|
// Check for a net path containing an authority, this would begin with //
|
|
|
|
if(curSrc[0] == '/' && curSrc[1] == '/')
|
|
{
|
|
// Advance past the double slash to where the authority would start, then find the next slash
|
|
curSrc = curSrc + 2;
|
|
daeChar* slash = (daeChar*)findCharacter(curSrc,'/');
|
|
// Temporarily remove that slash (avoids some branches)
|
|
if ( slash != NULL ) {
|
|
*slash = '\0';
|
|
}
|
|
// Save the text between the slashes as the authority
|
|
authority = safeCreate(curSrc);
|
|
// Put the slash back and advance the current pointer to it, this puts us at the start of the path
|
|
if (slash != NULL ) {
|
|
*slash = '/';
|
|
curSrc = slash;
|
|
}
|
|
}
|
|
|
|
// Search backwards from the end of the URI for the # which denotes the fragment (called ID here)
|
|
daeChar* idSymbol = (daeChar*)findCharacterReverse(curSrc,'#');
|
|
if (idSymbol != NULL)
|
|
{
|
|
// There was a fragment, isolate it by changing the # to a null
|
|
*idSymbol = '\0';
|
|
idSymbol++;
|
|
}
|
|
id = safeCreate(idSymbol);
|
|
|
|
// Search backwards for the last / in the path, everything after is the filename
|
|
|
|
daeChar* fname = (daeChar*)findCharacterReverse(curSrc,'/');
|
|
daeChar* dir;
|
|
if (fname == NULL)
|
|
{
|
|
// No / found, so the whole thing is the file name and there is no path
|
|
fname = curSrc;
|
|
dir = NULL;
|
|
}
|
|
else
|
|
{
|
|
// Found a slash, so the filename starts after it and dir starts at curSrc
|
|
fname++;
|
|
dir = curSrc;
|
|
}
|
|
file = safeCreate(fname);
|
|
|
|
// Pull the extension (if any) off of the filepath
|
|
|
|
daeString extStr = findCharacterReverse(fname,'.');
|
|
if (extStr != NULL)
|
|
extension = safeCreate(extStr+1);
|
|
|
|
// Now pull off the directory path if it exists by putting a zero at the beginning of fname, this insures filepath will end in a slash
|
|
|
|
if(fname != NULL) *fname = 0;
|
|
filepath = safeCreate(dir);
|
|
|
|
state = uri_loaded;
|
|
daeMemorySystem::free("tmp",tmp);
|
|
|
|
#else
|
|
daeBool isAbsolute;
|
|
daeChar* colon = (daeChar*)findCharacter(curSrc,':');
|
|
|
|
// IS ABSOLUTE REFERENCE
|
|
if (colon && (strlen(colon) > 2) && (colon[1] == '/') && (colon[2] == '/'))
|
|
{
|
|
*colon = '\0';
|
|
protocol = safeCreate(curSrc);
|
|
curSrc = colon+3;
|
|
daeString hosttmp = curSrc;
|
|
daeChar* slash = (daeChar*)findCharacter(curSrc,'/');
|
|
if (slash != NULL)
|
|
{
|
|
*slash = '\0';
|
|
authority = safeCreate(hosttmp);
|
|
curSrc = slash+1;
|
|
}
|
|
isAbsolute = true;
|
|
}
|
|
else {
|
|
protocol = NULL;
|
|
isAbsolute = false;
|
|
}
|
|
|
|
// Look for the # which denotes the fragment (called ID here)
|
|
daeChar* idSymbol = (daeChar*)findCharacterReverse(curSrc,'#');
|
|
if (idSymbol != NULL)
|
|
{
|
|
// There was a fragment, isolate it by changing the # to a null
|
|
*idSymbol = '\0';
|
|
idSymbol++;
|
|
}
|
|
|
|
daeChar* dir = NULL;
|
|
daeChar* fname = (daeChar*)findCharacterReverse(curSrc,'/');
|
|
if (fname == NULL)
|
|
fname = curSrc;
|
|
else {
|
|
*fname = '\0';
|
|
fname++;
|
|
dir = curSrc;
|
|
}
|
|
|
|
filepath = safeCreate(dir);
|
|
int dirLen = (int)strlen(filepath);
|
|
|
|
// append a '/'
|
|
if ((filepath != NULL) && (dirLen > 0) &&
|
|
(filepath[dirLen-1] != '/')) {
|
|
daeMemorySystem::free("uri",(void*)filepath);
|
|
filepath = (daeString)daeMemorySystem::malloc("uri", dirLen+2);
|
|
strcpy((daeChar*)filepath,dir);
|
|
*((daeChar*)filepath+dirLen) = '/';
|
|
*((daeChar*)filepath+dirLen+1) = '\0';
|
|
}
|
|
|
|
file = safeCreate(fname);
|
|
id = safeCreate(idSymbol);
|
|
|
|
daeString extStr = findCharacterReverse(fname,'.');
|
|
if (extStr != NULL)
|
|
extension = safeCreate(extStr+1);
|
|
|
|
state = uri_loaded;
|
|
daeMemorySystem::free("tmp",tmp);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
daeURI::print()
|
|
{
|
|
fprintf(stderr,"URI(%s)\n",uriString);
|
|
fprintf(stderr,"protocol = %s\n",protocol);
|
|
fprintf(stderr,"authority = %s\n",authority);
|
|
fprintf(stderr,"path = %s\n",filepath);
|
|
fprintf(stderr,"file = %s\n",file);
|
|
fprintf(stderr,"id = %s\n",id);
|
|
fprintf(stderr,"URI without base = %s\n",originalURIString);
|
|
fflush(stderr);
|
|
}
|
|
|
|
const char* protoString = "://";
|
|
const char* hostString = "/";
|
|
const char* queryString = "#";
|
|
const char* filepathString = "/";
|
|
|
|
daeString
|
|
daeURI::getURI() const
|
|
{
|
|
return uriString;
|
|
}
|
|
|
|
daeString
|
|
daeURI::getOriginalURI() const
|
|
{
|
|
return originalURIString;
|
|
}
|
|
|
|
void
|
|
daeURI::validate(daeURI* baseURI)
|
|
{
|
|
// If no base URI was supplied, get the application base and use it
|
|
if (baseURI == NULL)
|
|
{
|
|
if ( container == NULL || (baseURI = container->getDocumentURI()) == NULL ) {
|
|
baseURI = getBaseURI();
|
|
}
|
|
if (this == baseURI ) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
#if 1
|
|
// This is rewritten according to the updated rfc 3986
|
|
if((protocol != NULL) && (strlen(protocol)>0)) // if defined(R.scheme) then
|
|
{
|
|
// Everything stays the same except path which we normalize
|
|
// T.scheme = R.scheme;
|
|
// T.authority = R.authority;
|
|
// T.path = remove_dot_segments(R.path);
|
|
// T.query = R.query;
|
|
normalizeURIPath((char *)filepath);
|
|
if ( (file == NULL) || (strlen(file)==0) ) {
|
|
//safeDelete(file);
|
|
//safeDelete(extension);
|
|
//file = safeCreate(baseURI->file);
|
|
//extension = safeCreate(baseURI->extension);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if((authority != NULL) && (strlen(authority)>0)) // if defined(R.authority) then
|
|
{
|
|
// Authority and query stay the same, path is normalized
|
|
// T.authority = R.authority;
|
|
// T.path = remove_dot_segments(R.path);
|
|
// T.query = R.query;
|
|
normalizeURIPath((char *)filepath);
|
|
if ( (file == NULL) || (strlen(file)==0) ) {
|
|
//safeDelete(file);
|
|
//safeDelete(extension);
|
|
//file = safeCreate(baseURI->file);
|
|
//extension = safeCreate(baseURI->extension);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(((filepath == NULL) || (strlen(filepath)==0)) && ((file == NULL) || (strlen(file)==0))) // if (R.path == "") then
|
|
{
|
|
// T.path = Base.path;
|
|
safeDelete(filepath);
|
|
safeDelete(file);
|
|
safeDelete(extension);
|
|
filepath = safeCreate(baseURI->filepath);
|
|
file = safeCreate(baseURI->file);
|
|
extension = safeCreate(baseURI->extension);
|
|
// We don't handle querys, but if we did
|
|
//if defined(R.query) then
|
|
// T.query = R.query;
|
|
//else
|
|
// T.query = Base.query;
|
|
//endif;
|
|
}
|
|
else
|
|
{
|
|
if((filepath != NULL) && (*filepath == '/')) //if (R.path starts-with "/") then
|
|
{
|
|
// T.path = remove_dot_segments(R.path);
|
|
normalizeURIPath((char *)filepath);
|
|
}
|
|
else
|
|
{
|
|
//T.path = merge(Base.path, R.path);
|
|
daeChar* newPath;
|
|
if((strlen(baseURI->authority) != 0) && (strlen(baseURI->filepath)==0) && (strlen(baseURI->file) == 0)) //authority defined, path empty
|
|
{
|
|
newPath = (daeChar*)daeMemorySystem::malloc("uri", strlen(filepath) + 2);
|
|
*newPath = '/';
|
|
*(newPath+1) = 0;
|
|
strcat(newPath,filepath);
|
|
}
|
|
else
|
|
{
|
|
size_t l = 0;
|
|
if ( filepath != NULL ) {
|
|
l = strlen(filepath);
|
|
}
|
|
newPath = (daeChar*)daeMemorySystem::malloc("uri", strlen(baseURI->filepath) + l + 1);
|
|
*newPath = 0;
|
|
strcat(newPath,baseURI->filepath);
|
|
if ( filepath != NULL ) {
|
|
strcat(newPath,filepath);
|
|
}
|
|
else {
|
|
strcat(newPath,"");
|
|
}
|
|
}
|
|
//T.path = remove_dot_segments(T.path);
|
|
normalizeURIPath(newPath);
|
|
safeDelete(filepath);
|
|
filepath = newPath;
|
|
}
|
|
// T.query = R.query;
|
|
}
|
|
// T.authority = Base.authority;
|
|
safeDelete(authority);
|
|
authority = safeCreate(baseURI->authority);
|
|
}
|
|
// T.scheme = Base.scheme;
|
|
safeDelete(protocol);
|
|
protocol = safeCreate(baseURI->protocol);
|
|
}
|
|
// T.fragment = R.fragment;
|
|
|
|
// Now for the purpose of maintaining the class members, we reassemble all this into a string version of the URI
|
|
size_t len = 0;
|
|
if ( protocol != NULL ) {
|
|
len += strlen(protocol);
|
|
}
|
|
if ( authority != NULL ) {
|
|
len += strlen(authority);
|
|
}
|
|
if ( filepath != NULL ) {
|
|
len += strlen(filepath);
|
|
}
|
|
if ( file != NULL ) {
|
|
len += strlen(file);
|
|
}
|
|
if ( queryString != NULL ) {
|
|
len += strlen(queryString);
|
|
}
|
|
if ( id != NULL ) {
|
|
len += strlen(id);
|
|
}
|
|
daeChar* newURI = (daeChar*)
|
|
daeMemorySystem::malloc("uri", len + 4 );
|
|
*newURI = 0;
|
|
|
|
if(protocol != NULL && *protocol != 0)
|
|
{
|
|
strcat(newURI, protocol);
|
|
strcat(newURI, ":");
|
|
}
|
|
strcat(newURI, "//");
|
|
if(authority != NULL && *authority != 0)
|
|
{
|
|
strcat(newURI, authority);
|
|
}
|
|
if(filepath != NULL)
|
|
strcat(newURI, filepath);
|
|
if(file != NULL)
|
|
strcat(newURI, file);
|
|
|
|
if(id != NULL && *id != 0)
|
|
{
|
|
strcat(newURI,"#");
|
|
strcat(newURI,id);
|
|
}
|
|
// This becomes the new uriString, no need to call internalSetUri because all the class members are up to date
|
|
safeDelete(uriString);
|
|
uriString = newURI;
|
|
state = uri_pending;
|
|
|
|
if ( container != NULL ) {
|
|
daeString fp = container->getDocumentURI()->getFilepath();
|
|
daeString f = container->getDocumentURI()->getFile();
|
|
if ( strcmp( fp, filepath ) != 0 || strcmp( f, file ) != 0 ) {
|
|
//external reference
|
|
container->getDocument()->addExternalReference( *this );
|
|
external = true;
|
|
}
|
|
else if ( external ) {
|
|
//was external but now isn't
|
|
container->getDocument()->removeExternalReference( *this );
|
|
external = false;
|
|
}
|
|
}
|
|
|
|
#else
|
|
// RFC 2396 part 5.2 step 3, if the scheme (protocol here) is defined we are done, otherwise inherit the base URI's protocol
|
|
if ((protocol == NULL)||(strlen(protocol)==0))
|
|
{
|
|
// Make a copy of the base's protocol, not a reference to it
|
|
safeDelete(protocol);
|
|
protocol = safeCreate(baseURI->protocol);
|
|
// part 5.2 step 4, if the authority is defined we skip to step 7, otherwise inherit the base URI's authority
|
|
if((authority == NULL) || (strlen(authority)== 0))
|
|
{
|
|
// Make a copy of the base's authority, not a reference to it
|
|
safeDelete(authority);
|
|
authority = safeCreate(baseURI->authority);
|
|
// part 5.2 step 5 if the path part (filepath here) begins with a slash we skip to step 7, otherwise resolve the relative path against the base
|
|
if((filepath == NULL) || (*filepath != '/'))
|
|
{
|
|
// part 5.2 step 2, if scheme, authority and path are all empty, this is a reference to the current doc
|
|
// COLLADA DOM wants this to resolve to the URI of the document + the fragment
|
|
// To make this happen we have the URI inherit the file part of the base (if present) and the path
|
|
if( ((filepath == NULL) || (strlen(filepath)==0)) && // filepath empty
|
|
((file == NULL) || (strlen(file)==0)) && // file empty
|
|
((baseURI->file != NULL) && (strlen(baseURI->file) > 0))) // baseURI file NOT empty
|
|
{
|
|
// inherit the base's filename
|
|
safeDelete(file);
|
|
file = safeCreate(baseURI->file);
|
|
}
|
|
// part 5.2 step 6, resolving a relative path reference
|
|
// note that in this implementation the filepath does not contain the last segment (the filename.ext)
|
|
// Allocate enough memory to hold the merged path
|
|
daeChar* newPath = (daeChar*)daeMemorySystem::malloc(
|
|
"uri",
|
|
strlen(baseURI->filepath) +
|
|
strlen(filepath) + 1);
|
|
*newPath = 0;
|
|
// part 5.2 step 6(a) copy the baseURI filepath to the buffer
|
|
strcat(newPath,baseURI->filepath);
|
|
// part 5.2 step 6(b) copy this URI's filepath to the buffer
|
|
if(*filepath != 0)
|
|
{
|
|
strcat(newPath,filepath);
|
|
}
|
|
// part 5.2 step 6(c-g) normalize the new path
|
|
normalizeURIPath(newPath);
|
|
// part 5.2 step 6(h) replace the old filepath with the new path
|
|
safeDelete(filepath);
|
|
filepath = newPath;
|
|
}
|
|
}
|
|
// part 5.2 step 7 assemble the final complete URI
|
|
// Allocate memory to hold the assembled version of the URI
|
|
daeChar* newURI = (daeChar*)
|
|
daeMemorySystem::malloc(
|
|
"uri",
|
|
strlen(protocol) + // really scheme
|
|
1 + // space for ":"
|
|
strlen(authority) + // really authority
|
|
2 + // space for "//"
|
|
strlen(filepath) + // path without the filename
|
|
strlen(file) + // filename part of the path
|
|
strlen(queryString) + // "#"
|
|
strlen(id) + // really fragment
|
|
1); // terminating zero
|
|
*newURI = 0;
|
|
if(protocol != NULL && *protocol != 0)
|
|
{
|
|
strcat(newURI, protocol);
|
|
strcat(newURI, ":");
|
|
}
|
|
if(authority != NULL && *authority != 0)
|
|
{
|
|
strcat(newURI, authority);
|
|
}
|
|
strcat(newURI, "//");
|
|
if(filepath != NULL)
|
|
strcat(newURI, filepath);
|
|
if(file != NULL)
|
|
strcat(newURI, file);
|
|
if(id != NULL && *id != 0)
|
|
{
|
|
strcat(newURI,"#");
|
|
strcat(newURI,id);
|
|
}
|
|
// Reset the URI to the new one
|
|
// Just setting the uriString would probably be enough
|
|
internalSetURI(newURI);
|
|
daeMemorySystem::free("uri",newURI);
|
|
}
|
|
state = uri_pending;
|
|
|
|
#endif
|
|
#if 0
|
|
// If there is no protocol, assume this is a relative URI that needs to be resolved against the base
|
|
if ((protocol == NULL)||(strlen(protocol)==0))
|
|
{
|
|
// !!!GAC if the base URI contains a file and this uri does not, copy it over (why??)
|
|
if (((baseURI->file != NULL) && (strlen(baseURI->file)>0)) &&
|
|
((file == NULL)||(strlen(file)==0)))
|
|
{
|
|
if (file != NULL)
|
|
safeDelete(file);
|
|
file = safeCreate(baseURI->file);
|
|
}
|
|
|
|
// !!!GAC this is a quick and dirty attempt to get the relative URIs properly resolved and the internal
|
|
// !!!GAC paths normalized. This code should be rewritten when there's time, I wanted to get this up quick
|
|
// !!!GAC so we could test the rest of the system to make sure handing it "correct" URIs doesn't break things.
|
|
|
|
// Start by allocating memory and putting together just the path component
|
|
daeChar* newPath = (daeChar*)
|
|
daeMemorySystem::malloc(
|
|
"tmp",
|
|
strlen(baseURI->filepath) +
|
|
strlen(filepathString) +
|
|
strlen(filepath) +
|
|
strlen(filepathString) +
|
|
strlen(file)+1);
|
|
*newPath = 0;
|
|
strcat(newPath,baseURI->filepath);
|
|
strcat(newPath,filepathString); // !!!GAC this may put in an extra / but if it does the normalization will fix it
|
|
if(*filepath != 0)
|
|
{
|
|
strcat(newPath,filepath); // !!!GAC only do this if filepath is not empty
|
|
strcat(newPath,filepathString); // !!!GAC only do this if filepath is not empty
|
|
}
|
|
strcat(newPath,file);
|
|
// Normalize the path according to RFC 2396 (removes extra /, .., . and so on)
|
|
normalizeURIPath(newPath);
|
|
// !!!GAC Allocate memory for the complete URI and assemble it
|
|
daeChar* newURI = (daeChar*)
|
|
daeMemorySystem::malloc(
|
|
"tmp",
|
|
strlen(baseURI->protocol) +
|
|
strlen(protoString) +
|
|
strlen(authority) +
|
|
strlen(hostString) +
|
|
strlen(newPath) +
|
|
strlen(queryString) +
|
|
strlen(id)+1);
|
|
*newURI = 0;
|
|
strcat(newURI,baseURI->protocol); // Should be called "scheme" from RFC 2396
|
|
strcat(newURI,protoString);
|
|
strcat(newURI,authority); // Should be called "authority"
|
|
// strcat(newURI,hostString); // !!!GAC not necessary, path always begins with a /
|
|
strcat(newURI,newPath);
|
|
if(strlen(id) != 0)
|
|
{
|
|
// !!!GAC don't append the #id unless it's needed (bug 297)
|
|
strcat(newURI,queryString);
|
|
strcat(newURI,id);
|
|
}
|
|
internalSetURI(newURI);
|
|
daeMemorySystem::free("tmp",newPath);
|
|
daeMemorySystem::free("tmp",newURI);
|
|
}
|
|
state = uri_pending;
|
|
#endif
|
|
}
|
|
|
|
void
|
|
daeURI::resolveElement(daeString typeNameHint)
|
|
{
|
|
if (state == uri_empty)
|
|
return;
|
|
|
|
if (state == uri_loaded) {
|
|
if (container != NULL)
|
|
validate(container->getDocumentURI());
|
|
else
|
|
validate();
|
|
}
|
|
daeURIResolver::attemptResolveElement(*((daeURI*)this), typeNameHint );
|
|
}
|
|
|
|
void
|
|
daeURI::resolveURI()
|
|
{
|
|
// !!!GAC bug 486, there used to be code here that just returned if state was uri_empty or uri_resolve_local this has been removed.
|
|
if (element != NULL)
|
|
{
|
|
// !!!GAC bug 388 and 421, need to add a fragment (#) before the ID (was setURI(element->getID()))
|
|
if(element->getID() == NULL || element->getID()[0] == 0)
|
|
{
|
|
// We can't resolve to an element that has no ID, so if the ID is blank, fail and return
|
|
state = uri_failed_invalid_reference;
|
|
return;
|
|
}
|
|
daeChar* newID = (daeChar*)daeMemorySystem::malloc("tmp",strlen(element->getID())+2);
|
|
strcpy(newID,"#");
|
|
strcat(newID,element->getID());
|
|
// !!!GAC We have to save element and container because setURI clears them for some reason
|
|
daeElementRef elementSave = element;
|
|
setURI(newID);
|
|
// !!!GAC Hopefully, calling validate like below is the right thing to do to get the full URI resolution
|
|
element = elementSave;
|
|
validate(element->getDocumentURI());
|
|
element = elementSave;
|
|
daeMemorySystem::free("tmp",newID);
|
|
state = uri_success; // !!!GAC The element pointer and the URI should agree now, so set success
|
|
}
|
|
else
|
|
{
|
|
state = uri_failed_invalid_reference;
|
|
}
|
|
}
|
|
|
|
daeBool daeURI::getPath(daeChar *dest, daeInt size)
|
|
{
|
|
|
|
if( file == NULL )
|
|
{
|
|
//printf("****** %s : %s\n", uriString, originalURIString);
|
|
return false;
|
|
}
|
|
*dest = 0;
|
|
int lenPath = 0;
|
|
if ( filepath != NULL ) lenPath = (int)strlen(filepath);
|
|
int lenFile = (int)strlen(file);
|
|
|
|
int len = lenPath + lenFile;
|
|
if (len < size)
|
|
{
|
|
if ( filepath != NULL ) {
|
|
strcpy(dest,filepath);
|
|
}
|
|
strcat(dest,file);
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void
|
|
daeURIResolver::attemptResolveElement(daeURI& uri, daeString typeNameHint)
|
|
{
|
|
int i;
|
|
int cnt =(int) _KnownResolvers.getCount();
|
|
|
|
for(i=0;i<cnt;i++)
|
|
if ((_KnownResolvers[i]->isProtocolSupported(uri.getProtocol()))&&
|
|
((uri.getFile() == NULL) ||
|
|
(uri.getFile()[0] == '\0') ||
|
|
(_KnownResolvers[i]->isExtensionSupported(uri.getExtension()))) &&
|
|
(_KnownResolvers[i]->resolveElement(uri, typeNameHint)))
|
|
return;
|
|
}
|
|
|
|
void
|
|
daeURIResolver::attemptResolveURI(daeURI& uri)
|
|
{
|
|
int i,cnt = (int)_KnownResolvers.getCount();
|
|
|
|
daeBool foundProtocol = false;
|
|
for(i=0;i<cnt;i++)
|
|
if (_KnownResolvers[i]->isProtocolSupported(uri.getProtocol())) {
|
|
foundProtocol = true;
|
|
if (_KnownResolvers[i]->resolveURI(uri))
|
|
return;
|
|
}
|
|
#if defined(_DEBUG) && defined(WIN32)
|
|
char msg[256];
|
|
sprintf(msg,"daeURIResolver::attemptResolveURI(%s) - failed\n", uri.getURI());
|
|
daeErrorHandler::get()->handleWarning( msg );
|
|
#endif
|
|
|
|
if (!foundProtocol) {
|
|
uri.setState(daeURI::uri_failed_unsupported_protocol);
|
|
#if defined(_DEBUG) && defined(WIN32)
|
|
char msg[128];
|
|
sprintf(msg,"**protocol '%s' is not supported**\n",uri.getProtocol());
|
|
daeErrorHandler::get()->handleWarning( msg );
|
|
#endif
|
|
}
|
|
else {
|
|
#if defined(_DEBUG) && defined(WIN32)
|
|
char msg[256];
|
|
sprintf(msg,"**file(%s/%s) or id(%s) failed to resolve\n",
|
|
uri.getFilepath(),uri.getFile(),uri.getID());
|
|
daeErrorHandler::get()->handleWarning( msg );
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
daeBool daeURIResolver::_loadExternalDocuments = true;
|
|
|
|
daeURIResolver::daeURIResolver()
|
|
{
|
|
_KnownResolvers.append((daeURIResolver*)this);
|
|
}
|
|
|
|
daeURIResolver::~daeURIResolver()
|
|
{
|
|
_KnownResolvers.remove((daeURIResolver*)this);
|
|
}
|
|
// This code is loosely based on the RFC 2396 normalization code from
|
|
// libXML. Specifically it does the RFC steps 6.c->6.g from section 5.2
|
|
// The path is modified in place, there is no error return.
|
|
void daeURI::normalizeURIPath(char *path)
|
|
{
|
|
char
|
|
*cur, // location we are currently processing
|
|
*out; // Everything from this back we are done with
|
|
|
|
// Return if the path pointer is null
|
|
|
|
if (path == NULL) return;
|
|
|
|
// Skip any initial / characters to get us to the start of the first segment
|
|
|
|
for(cur=path; *cur == '/'; cur++);
|
|
|
|
// Return if we hit the end of the string
|
|
|
|
if (*cur == 0) return;
|
|
|
|
// Keep everything we've seen so far.
|
|
|
|
out = cur;
|
|
|
|
// Analyze each segment in sequence for cases (c) and (d).
|
|
|
|
while (*cur != 0)
|
|
{
|
|
// (c) All occurrences of "./", where "." is a complete path segment, are removed from the buffer string.
|
|
|
|
if ((*cur == '.') && (*(cur+1) == '/'))
|
|
{
|
|
cur += 2;
|
|
// If there were multiple slashes, skip them too
|
|
while (*cur == '/') cur++;
|
|
continue;
|
|
}
|
|
|
|
// (d) If the buffer string ends with "." as a complete path segment, that "." is removed.
|
|
|
|
if ((*cur == '.') && (*(cur+1) == 0))
|
|
break;
|
|
|
|
// If we passed the above tests copy the segment to the output side
|
|
|
|
while (*cur != '/' && *cur != 0)
|
|
{
|
|
*(out++) = *(cur++);
|
|
}
|
|
|
|
if(*cur != 0)
|
|
{
|
|
// Skip any occurrances of // at the end of the segment
|
|
|
|
while ((*cur == '/') && (*(cur+1) == '/')) cur++;
|
|
|
|
// Bring the last character in the segment (/ or a null terminator) into the output
|
|
|
|
*(out++) = *(cur++);
|
|
}
|
|
}
|
|
|
|
*out = 0;
|
|
|
|
// Restart at the beginning of the first segment for the next part
|
|
|
|
for(cur=path; *cur == '/'; cur++);
|
|
if (*cur == 0) return;
|
|
|
|
// Analyze each segment in sequence for cases (e) and (f).
|
|
//
|
|
// e) All occurrences of "<segment>/../", where <segment> is a
|
|
// complete path segment not equal to "..", are removed from the
|
|
// buffer string. Removal of these path segments is performed
|
|
// iteratively, removing the leftmost matching pattern on each
|
|
// iteration, until no matching pattern remains.
|
|
//
|
|
// f) If the buffer string ends with "<segment>/..", where <segment>
|
|
// is a complete path segment not equal to "..", that
|
|
// "<segment>/.." is removed.
|
|
//
|
|
// To satisfy the "iterative" clause in (e), we need to collapse the
|
|
// string every time we find something that needs to be removed. Thus,
|
|
// we don't need to keep two pointers into the string: we only need a
|
|
// "current position" pointer.
|
|
//
|
|
bool trew = true;
|
|
while (trew)
|
|
{
|
|
char *segp, *tmp;
|
|
|
|
// At the beginning of each iteration of this loop, "cur" points to
|
|
// the first character of the segment we want to examine.
|
|
|
|
// Find the end of the current segment.
|
|
|
|
for(segp = cur;(*segp != '/') && (*segp != 0); ++segp);
|
|
|
|
// If this is the last segment, we're done (we need at least two
|
|
// segments to meet the criteria for the (e) and (f) cases).
|
|
|
|
if (*segp == 0)
|
|
break;
|
|
|
|
// If the first segment is "..", or if the next segment _isn't_ "..",
|
|
// keep this segment and try the next one.
|
|
|
|
++segp;
|
|
if (((*cur == '.') && (cur[1] == '.') && (segp == cur+3))
|
|
|| ((*segp != '.') || (segp[1] != '.')
|
|
|| ((segp[2] != '/') && (segp[2] != 0))))
|
|
{
|
|
cur = segp;
|
|
continue;
|
|
}
|
|
|
|
// If we get here, remove this segment and the next one and back up
|
|
// to the previous segment (if there is one), to implement the
|
|
// "iteratively" clause. It's pretty much impossible to back up
|
|
// while maintaining two pointers into the buffer, so just compact
|
|
// the whole buffer now.
|
|
|
|
// If this is the end of the buffer, we're done.
|
|
|
|
if (segp[2] == 0)
|
|
{
|
|
*cur = 0;
|
|
break;
|
|
}
|
|
|
|
// Strings overlap during this copy, but not in a bad way, just avoid using strcpy
|
|
|
|
tmp = cur;
|
|
segp += 3;
|
|
while ((*(tmp++) = *(segp++)) != 0);
|
|
|
|
// If there are no previous segments, then keep going from here.
|
|
|
|
segp = cur;
|
|
while ((segp > path) && (*(--segp) == '/'));
|
|
|
|
if (segp == path)
|
|
continue;
|
|
|
|
// "segp" is pointing to the end of a previous segment; find it's
|
|
// start. We need to back up to the previous segment and start
|
|
// over with that to handle things like "foo/bar/../..". If we
|
|
// don't do this, then on the first pass we'll remove the "bar/..",
|
|
// but be pointing at the second ".." so we won't realize we can also
|
|
// remove the "foo/..".
|
|
|
|
for(cur = segp;(cur > path) && (*(cur-1) != '/'); cur--);
|
|
}
|
|
|
|
*out = 0;
|
|
|
|
// g) If the resulting buffer string still begins with one or more
|
|
// complete path segments of "..", then the reference is
|
|
// considered to be in error. Implementations may handle this
|
|
// error by retaining these components in the resolved path (i.e.,
|
|
// treating them as part of the final URI), by removing them from
|
|
// the resolved path (i.e., discarding relative levels above the
|
|
// root), or by avoiding traversal of the reference.
|
|
//
|
|
// We discard them from the final path.
|
|
|
|
if (*path == '/')
|
|
{
|
|
for(cur=path; (*cur == '/') && (cur[1] == '.') && (cur[2] == '.') && ((cur[3] == '/') || (cur[3] == 0)); cur += 3);
|
|
|
|
if (cur != path)
|
|
{
|
|
for(out=path; *cur != 0; *(out++) = *(cur++));
|
|
|
|
*out = 0;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
// This function will take a resolved URI and create a version of it that is relative to
|
|
// another existing URI. The new URI is stored in the "originalURI"
|
|
int daeURI::makeRelativeTo(daeURI* relativeToURI)
|
|
{
|
|
if( getState() == uri_empty || relativeToURI->getState() == uri_empty )
|
|
return(DAE_ERR_INVALID_CALL);
|
|
if( getState() == uri_loaded )
|
|
{
|
|
if (container != NULL)
|
|
validate(container->getDocumentURI());
|
|
else
|
|
validate();
|
|
}
|
|
if( relativeToURI->getState() == uri_loaded )
|
|
{
|
|
if (relativeToURI->getContainer() != NULL)
|
|
relativeToURI->validate(relativeToURI->getContainer()->getDocumentURI());
|
|
else
|
|
relativeToURI->validate();
|
|
}
|
|
|
|
|
|
// Can only do this function if both URIs have the same scheme and authority
|
|
|
|
if((strcmp(getProtocol(), relativeToURI->getProtocol()) != 0) || (strcmp(getAuthority(), relativeToURI->getAuthority()) != 0))
|
|
return(DAE_ERR_INVALID_CALL); // !!!GAC Need to assign a real error code to this
|
|
|
|
// advance till we find a segment that doesn't match
|
|
|
|
const char *this_filepath = getFilepath();
|
|
const char *relativeTo_filepath = relativeToURI->getFilepath();
|
|
const char *this_slash = this_filepath;
|
|
const char *relativeTo_slash = relativeTo_filepath;
|
|
|
|
while(*this_filepath == *relativeTo_filepath)
|
|
{
|
|
if(*this_filepath == '/')
|
|
{
|
|
this_slash = this_filepath;
|
|
relativeTo_slash = relativeTo_filepath;
|
|
}
|
|
this_filepath++;
|
|
relativeTo_filepath++;
|
|
}
|
|
|
|
// Decide how many ../ segments are needed (Filepath should always end in a /)
|
|
int segment_count = 0;
|
|
relativeTo_slash++;
|
|
while(*relativeTo_slash != 0)
|
|
{
|
|
if(*relativeTo_slash == '/')
|
|
segment_count ++;
|
|
relativeTo_slash++;
|
|
}
|
|
this_slash++;
|
|
// Delete old URI string
|
|
safeDelete(originalURIString);
|
|
// Allocate memory for a new "originalURI" and free the old one
|
|
char *newRelativeURI;
|
|
if ( getID() == NULL )
|
|
{
|
|
newRelativeURI = (char*) daeMemorySystem::malloc("uri",strlen(this_slash)+ strlen(file)+(segment_count*3)+1);
|
|
}
|
|
else
|
|
{
|
|
newRelativeURI = (char*) daeMemorySystem::malloc("uri",strlen(this_slash)+ strlen(file)+(segment_count*3)+strlen(id)+2);
|
|
}
|
|
char *temp = newRelativeURI;
|
|
for(int i = 0; i < segment_count; i++)
|
|
{
|
|
strcpy(temp,"../");
|
|
temp += 3;
|
|
}
|
|
strcpy(temp,this_slash);
|
|
strcat(temp,file);
|
|
if(id!=NULL && strlen(getID()) != 0)
|
|
{
|
|
strcat(temp,"#");
|
|
strcat(temp,getID());
|
|
}
|
|
originalURIString = newRelativeURI;
|
|
return(DAE_OK);
|
|
}
|
|
|