You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdegraphics/kviewshell/plugins/djvu/libdjvu/GURL.cpp

1966 lines
47 KiB

//C- -*- C++ -*-
//C- -------------------------------------------------------------------
//C- DjVuLibre-3.5
//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun.
//C- Copyright (c) 2001 AT&T
//C-
//C- This software is subject to, and may be distributed under, the
//C- GNU General Public License, Version 2. The license should have
//C- accompanied the software or you may obtain a copy of the license
//C- from the Free Software Foundation at http://www.fsf.org .
//C-
//C- This program is distributed in the hope that it will be useful,
//C- but WITHOUT ANY WARRANTY; without even the implied warranty of
//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//C- GNU General Public License for more details.
//C-
//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library
//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech
//C- Software authorized us to replace the original DjVu(r) Reference
//C- Library notice by the following text (see doc/lizard2002.djvu):
//C-
//C- ------------------------------------------------------------------
//C- | DjVu (r) Reference Library (v. 3.5)
//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved.
//C- | The DjVu Reference Library is protected by U.S. Pat. No.
//C- | 6,058,214 and patents pending.
//C- |
//C- | This software is subject to, and may be distributed under, the
//C- | GNU General Public License, Version 2. The license should have
//C- | accompanied the software or you may obtain a copy of the license
//C- | from the Free Software Foundation at http://www.fsf.org .
//C- |
//C- | The computer code originally released by LizardTech under this
//C- | license and unmodified by other parties is deemed "the LIZARDTECH
//C- | ORIGINAL CODE." Subject to any third party intellectual property
//C- | claims, LizardTech grants recipient a worldwide, royalty-free,
//C- | non-exclusive license to make, use, sell, or otherwise dispose of
//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the
//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU
//C- | General Public License. This grant only confers the right to
//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to
//C- | the extent such infringement is reasonably necessary to enable
//C- | recipient to make, have made, practice, sell, or otherwise dispose
//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to
//C- | any greater extent that may be necessary to utilize further
//C- | modifications or combinations.
//C- |
//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
//C- +------------------------------------------------------------------
//
// $Id: GURL.cpp,v 1.19 2005/04/27 16:34:13 leonb Exp $
// $Name: release_3_5_15 $
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#if NEED_GNUG_PRAGMAS
# pragma implementation
#endif
// From: Leon Bottou, 1/31/2002
// This has been heavily changed by Lizardtech.
// They decided to use URLs for everyting, including
// the most basic file access. The URL class now is a unholy
// mixture of code for syntactically parsing the urls (which it was)
// and file status code (only for local file: urls).
#include "GException.h"
#include "GOS.h"
#include "GURL.h"
#include "debug.h"
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <string.h>
#ifdef WIN32
# include <atlbase.h>
# include <windows.h>
# include <direct.h>
#endif /* WIN32 */
// -- MAXPATHLEN
#ifndef MAXPATHLEN
# ifdef _MAX_PATH
# define MAXPATHLEN _MAX_PATH
# else
# define MAXPATHLEN 1024
# endif
#else
# if ( MAXPATHLEN < 1024 )
# undef MAXPATHLEN
# define MAXPATHLEN 1024
# endif
#endif
#if defined(UNIX) || defined(OS2)
# include <unistd.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <errno.h>
# include <fcntl.h>
# include <pwd.h>
# include <stdio.h>
# ifdef AUTOCONF
# ifdef TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
# else
# ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
# endif
# ifdef HAVE_DIRENT_H
# include <dirent.h>
# define NAMLEN(dirent) strlen((dirent)->d_name)
# else
# define dirent direct
# define NAMLEN(dirent) (dirent)->d_namlen
# ifdef HAVE_SYS_NDIR_H
# include <sys/ndir.h>
# endif
# ifdef HAVE_SYS_DIR_H
# include <sys/dir.h>
# endif
# ifdef HAVE_NDIR_H
# include <ndir.h>
# endif
# endif
# else /* !AUTOCONF */
# include <sys/time.h>
# if defined(XENIX)
# define USE_DIRECT
# include <sys/ndir.h>
# elif defined(OLDBSD)
# define USE_DIRECT
# include <sys/dir.h>
# endif
# ifdef USE_DIRECT
# define dirent direct
# define NAMLEN(dirent) (dirent)->d_namlen
# else
# include <dirent.h>
# define NAMLEN(dirent) strlen((dirent)->d_name)
# endif
# endif /* !AUTOCONF */
#endif /* UNIX */
#ifdef macintosh
#include <unix.h>
#include <errno.h>
#include <unistd.h>
#endif
#ifdef HAVE_NAMESPACES
namespace DJVU {
# ifdef NOT_DEFINED // Just to fool emacs c++ mode
}
#endif
#endif
static const char djvuopts[]="DJVUOPTS";
static const char localhost[]="file://localhost/";
static const char backslash='\\';
static const char colon=':';
static const char dot='.';
static const char filespecslashes[] = "file://";
static const char filespec[] = "file:";
static const char slash='/';
static const char percent='%';
static const char localhostspec1[] = "//localhost/";
static const char localhostspec2[] = "///";
static const char nillchar=0;
#if defined(UNIX)
static const char tilde='~';
static const char root[] = "/";
#elif defined(WIN32) || defined(OS2)
static const char root[] = "\\";
#elif defined(macintosh)
static char const * const root = &nillchar;
#else
#error "Define something here for your operating system"
#endif
static const int
pathname_start(const GUTF8String &url, const int protolength);
// hexval --
// -- Returns the hexvalue of a character.
// Returns -1 if illegal;
static int
hexval(char c)
{
return ((c>='0' && c<='9')
?(c-'0')
:((c>='A' && c<='F')
?(c-'A'+10)
:((c>='a' && c<='f')
?(c-'a'+10):(-1))));
}
static bool
is_argument(const char * start)
// Returns TRUE if 'start' points to the beginning of an argument
// (either hash or CGI)
{
// return (*start=='#' || *start=='?' || *start=='&' || *start==';');
return (*start=='#' || *start=='?' );
}
static bool
is_argument_sep(const char * start)
// Returns TRUE if 'start' points to the beginning of an argument
// (either hash or CGI)
{
return (*start=='&')||(*start == ';');
}
void
GURL::convert_slashes(void)
{
GUTF8String xurl(get_string());
#if defined(WIN32)
const int protocol_length=protocol(xurl).length();
for(char *ptr=(xurl.getbuf()+protocol_length);*ptr;ptr++)
if(*ptr == backslash)
*ptr=slash;
url=xurl;
#endif
}
static void
collapse(char * ptr, const int chars)
// Will remove the first 'chars' chars from the string and
// move the rest toward the beginning. Will take into account
// string length
{
const int length=strlen(ptr);
const char *srcptr=ptr+((chars>length)?length:chars);
while((*(ptr++) = *(srcptr++)))
EMPTY_LOOP;
}
GUTF8String
GURL::beautify_path(GUTF8String xurl)
{
const int protocol_length=GURL::protocol(xurl).length();
// Eats parts like ./ or ../ or ///
char * buffer;
GPBuffer<char> gbuffer(buffer,xurl.length()+1);
strcpy(buffer, (const char *)xurl);
// Find start point
char * start=buffer+pathname_start(xurl,protocol_length);
// Find end of the url (don't touch arguments)
char * ptr;
GUTF8String args;
for(ptr=start;*ptr;ptr++)
{
if (is_argument(ptr))
{
args=ptr;
*ptr=0;
break;
}
}
// Eat multiple slashes
for(;(ptr=strstr(start, "////"));collapse(ptr, 3))
EMPTY_LOOP;
for(;(ptr=strstr(start, "//"));collapse(ptr, 1))
EMPTY_LOOP;
// Convert /./ stuff into plain /
for(;(ptr=strstr(start, "/./"));collapse(ptr, 2))
EMPTY_LOOP;
#if defined(WIN32) || defined(OS2)
if(!xurl.cmp(filespec,sizeof(filespec)-1))
{
int offset=1;
if(start&&(start[0] == '/')&&
!xurl.cmp("file:////",sizeof("file:////")-1))
{
collapse(start, 1);
offset=0;
}
for(ptr=start+offset;(ptr=strchr(ptr, '/'));)
{
if(isalpha((++ptr)[0]))
{
if((ptr[1] == ':')&&(ptr[2]=='/'))
{
char *buffer2;
GPBuffer<char> gbuffer2(buffer2,strlen(ptr)+1);
strcpy(buffer2,ptr);
gbuffer.resize(strlen(ptr)+sizeof(localhost));
strcpy(buffer,localhost);
strcat(buffer,buffer2);
ptr=(start=buffer+sizeof(localhost))+1;
}
}
}
}
#endif
// Process /../
while((ptr=strstr(start, "/../")))
{
for(char * ptr1=ptr-1;(ptr1>=start);ptr1--)
{
if (*ptr1==slash)
{
collapse(ptr1, ptr-ptr1+3);
break;
}
}
}
// Remove trailing /.
ptr=start+strlen(start)-2;
if((ptr>=start)&& (ptr == GUTF8String("/.")))
{
ptr[1]=0;
}
// Eat trailing /..
ptr=start+strlen(start)-3;
if((ptr >= start) && (ptr == GUTF8String("/..")))
{
for(char * ptr1=ptr-1;(ptr1>=start);ptr1--)
{
if (*ptr1==slash)
{
ptr1[1]=0;
break;
}
}
}
// Done. Copy the buffer back into the URL and add arguments.
xurl=buffer;
return (xurl+args);
}
void
GURL::beautify_path(void)
{
url=beautify_path(get_string());
}
void
GURL::init(const bool nothrow)
{
GCriticalSectionLock lock(&class_lock);
validurl=true;
if (url.length())
{
GUTF8String proto=protocol();
if (proto.length()<2)
{
validurl=false;
if(!nothrow)
G_THROW( ERR_MSG("GURL.no_protocol") "\t"+url);
return;
}
// Below we have to make this complex test to detect URLs really
// referring to *local* files. Surprisingly, file://hostname/dir/file
// is also valid, but shouldn't be treated thru local FS.
if (proto=="file" && url[5]==slash &&
(url[6]!=slash || !url.cmp(localhost, sizeof(localhost))))
{
// Separate the arguments
GUTF8String arg;
{
const char * const url_ptr=url;
const char * ptr;
for(ptr=url_ptr;*ptr&&!is_argument(ptr);ptr++)
EMPTY_LOOP;
arg=ptr;
url=url.substr(0,(size_t)ptr-(size_t)url_ptr);
}
// Do double conversion
GUTF8String tmp=UTF8Filename();
if (!tmp.length())
{
validurl=false;
if(!nothrow)
G_THROW( ERR_MSG("GURL.fail_to_file") );
return;
}
url=GURL::Filename::UTF8(tmp).get_string();
if (!url.length())
{
validurl=false;
if(!nothrow)
G_THROW( ERR_MSG("GURL.fail_to_URL") );
return;
}
// Return the argument back
url+=arg;
}
convert_slashes();
beautify_path();
parse_cgi_args();
}
}
GURL::GURL(void)
: validurl(false)
{
}
GURL::GURL(const char * url_in)
: url(url_in ? url_in : ""), validurl(false)
{
}
GURL::GURL(const GUTF8String & url_in)
: url(url_in), validurl(false)
{
}
GURL::GURL(const GNativeString & url_in)
: url(url_in.getNative2UTF8()), validurl(false)
{
#if defined(WIN32) || defined(OS2)
if(is_valid() && is_local_file_url())
{
GURL::Filename::UTF8 xurl(UTF8Filename());
url=xurl.get_string(true);
validurl=false;
}
#endif
}
GURL::GURL(const GURL & url_in)
: validurl(false)
{
if(url_in.is_valid())
{
url=url_in.get_string();
init();
}else
{
url=url_in.url;
}
}
GURL &
GURL::operator=(const GURL & url_in)
{
GCriticalSectionLock lock(&class_lock);
if(url_in.is_valid())
{
url=url_in.get_string();
init(true);
}else
{
url=url_in.url;
validurl=false;
}
return *this;
}
GUTF8String
GURL::protocol(const GUTF8String& url)
{
const char * const url_ptr=url;
const char * ptr=url_ptr;
for(char c=*ptr;
c && (isalnum(c) || c == '+' || c == '-' || c == '.');
c=*(++ptr)) EMPTY_LOOP;
return(*ptr==colon)?GUTF8String(url_ptr, ptr-url_ptr):GUTF8String();
}
GUTF8String
GURL::hash_argument(void) const
// Returns the HASH argument (anything after '#' and before '?')
{
const GUTF8String xurl(get_string());
bool found=false;
GUTF8String arg;
// Break if CGI argument is found
for(const char * start=xurl;*start&&(*start!='?');start++)
{
if (found)
{
arg+=*start;
}else
{
found=(*start=='#');
}
}
return decode_reserved(arg);
}
void
GURL::set_hash_argument(const GUTF8String &arg)
{
const GUTF8String xurl(get_string());
GUTF8String new_url;
bool found=false;
const char * ptr;
for(ptr=xurl;*ptr;ptr++)
{
if (is_argument(ptr))
{
if (*ptr!='#')
{
break;
}
found=true;
} else if (!found)
{
new_url+=*ptr;
}
}
url=new_url+"#"+GURL::encode_reserved(arg)+ptr;
}
void
GURL::parse_cgi_args(void)
// Will read CGI arguments from the URL into
// cgi_name_arr and cgi_value_arr
{
if(!validurl)
init();
GCriticalSectionLock lock1(&class_lock);
cgi_name_arr.empty();
cgi_value_arr.empty();
// Search for the beginning of CGI arguments
const char * start=url;
while(*start)
{
if(*(start++)=='?')
{
break;
}
}
// Now loop until we see all of them
while(*start)
{
GUTF8String arg; // Storage for another argument
while(*start) // Seek for the end of it
{
if (is_argument_sep(start))
{
start++;
break;
} else
{
arg+=*start++;
}
}
if (arg.length())
{
// Got argument in 'arg'. Split it into 'name' and 'value'
const char * ptr;
const char * const arg_ptr=arg;
for(ptr=arg_ptr;*ptr&&(*ptr != '=');ptr++)
EMPTY_LOOP;
GUTF8String name, value;
if (*ptr)
{
name=GUTF8String(arg_ptr, (int)((ptr++)-arg_ptr));
value=GUTF8String(ptr, arg.length()-name.length()-1);
} else
{
name=arg;
}
int args=cgi_name_arr.size();
cgi_name_arr.resize(args);
cgi_value_arr.resize(args);
cgi_name_arr[args]=decode_reserved(name);
cgi_value_arr[args]=decode_reserved(value);
}
}
}
void
GURL::store_cgi_args(void)
// Will store CGI arguments from the cgi_name_arr and cgi_value_arr
// back into the URL
{
if(!validurl)
init();
GCriticalSectionLock lock1(&class_lock);
const char * const url_ptr=url;
const char * ptr;
for(ptr=url_ptr;*ptr&&(*ptr!='?');ptr++)
EMPTY_LOOP;
GUTF8String new_url(url_ptr, ptr-url_ptr);
for(int i=0;i<cgi_name_arr.size();i++)
{
GUTF8String name=GURL::encode_reserved(cgi_name_arr[i]);
GUTF8String value=GURL::encode_reserved(cgi_value_arr[i]);
new_url+=(i?"&":"?")+name;
if (value.length())
new_url+="="+value;
}
url=new_url;
}
int
GURL::cgi_arguments(void) const
{
if(!validurl)
const_cast<GURL *>(this)->init();
return cgi_name_arr.size();
}
int
GURL::djvu_cgi_arguments(void) const
{
if(!validurl)
const_cast<GURL *>(this)->init();
GCriticalSectionLock lock((GCriticalSection *) &class_lock);
int args=0;
for(int i=0;i<cgi_name_arr.size();i++)
{
if (cgi_name_arr[i].upcase()==djvuopts)
{
args=cgi_name_arr.size()-(i+1);
break;
}
}
return args;
}
GUTF8String
GURL::cgi_name(int num) const
{
if(!validurl) const_cast<GURL *>(this)->init();
GCriticalSectionLock lock((GCriticalSection *) &class_lock);
return (num<cgi_name_arr.size())?cgi_name_arr[num]:GUTF8String();
}
GUTF8String
GURL::djvu_cgi_name(int num) const
{
if(!validurl) const_cast<GURL *>(this)->init();
GCriticalSectionLock lock((GCriticalSection *) &class_lock);
GUTF8String arg;
for(int i=0;i<cgi_name_arr.size();i++)
if (cgi_name_arr[i].upcase()==djvuopts)
{
for(i++;i<cgi_name_arr.size();i++)
if (! num--)
{
arg=cgi_name_arr[i];
break;
}
break;
}
return arg;
}
GUTF8String
GURL::cgi_value(int num) const
{
if(!validurl) const_cast<GURL *>(this)->init();
GCriticalSectionLock lock((GCriticalSection *) &class_lock);
return (num<cgi_value_arr.size())?cgi_value_arr[num]:GUTF8String();
}
GUTF8String
GURL::djvu_cgi_value(int num) const
{
if(!validurl) const_cast<GURL *>(this)->init();
GCriticalSectionLock lock((GCriticalSection *) &class_lock);
GUTF8String arg;
for(int i=0;i<cgi_name_arr.size();i++)
{
if (cgi_name_arr[i].upcase()==djvuopts)
{
for(i++;i<cgi_name_arr.size();i++)
{
if (! num--)
{
arg=cgi_value_arr[i];
break;
}
}
break;
}
}
return arg;
}
DArray<GUTF8String>
GURL::cgi_names(void) const
{
if(!validurl) const_cast<GURL *>(this)->init();
GCriticalSectionLock lock((GCriticalSection *) &class_lock);
return cgi_name_arr;
}
DArray<GUTF8String>
GURL::cgi_values(void) const
{
if(!validurl) const_cast<GURL *>(this)->init();
GCriticalSectionLock lock((GCriticalSection *) &class_lock);
return cgi_value_arr;
}
DArray<GUTF8String>
GURL::djvu_cgi_names(void) const
{
if(!validurl) const_cast<GURL *>(this)->init();
GCriticalSectionLock lock((GCriticalSection *) &class_lock);
int i;
DArray<GUTF8String> arr;
for(i=0;(i<cgi_name_arr.size())&&
(cgi_name_arr[i].upcase()!=djvuopts)
;i++)
EMPTY_LOOP;
int size=cgi_name_arr.size()-(i+1);
if (size>0)
{
arr.resize(size-1);
for(i=0;i<arr.size();i++)
arr[i]=cgi_name_arr[cgi_name_arr.size()-arr.size()+i];
}
return arr;
}
DArray<GUTF8String>
GURL::djvu_cgi_values(void) const
{
if(!validurl) const_cast<GURL *>(this)->init();
GCriticalSectionLock lock((GCriticalSection *) &class_lock);
int i;
DArray<GUTF8String> arr;
for(i=0;i<cgi_name_arr.size()&&(cgi_name_arr[i].upcase()!=djvuopts);i++)
EMPTY_LOOP;
int size=cgi_name_arr.size()-(i+1);
if (size>0)
{
arr.resize(size-1);
for(i=0;i<arr.size();i++)
arr[i]=cgi_value_arr[cgi_value_arr.size()-arr.size()+i];
}
return arr;
}
void
GURL::clear_all_arguments(void)
{
clear_hash_argument();
clear_cgi_arguments();
}
void
GURL::clear_hash_argument(void)
// Clear anything after first '#' and before the following '?'
{
if(!validurl) init();
GCriticalSectionLock lock(&class_lock);
bool found=false;
GUTF8String new_url;
for(const char * start=url;*start;start++)
{
// Break on first CGI arg.
if (*start=='?')
{
new_url+=start;
break;
}
if (!found)
{
if (*start=='#')
found=true;
else
new_url+=*start;
}
}
url=new_url;
}
void
GURL::clear_cgi_arguments(void)
{
if(!validurl)
init();
GCriticalSectionLock lock1(&class_lock);
// Clear the arrays
cgi_name_arr.empty();
cgi_value_arr.empty();
// And clear everything past the '?' sign in the URL
for(const char * ptr=url;*ptr;ptr++)
if (*ptr=='?')
{
url.setat(ptr-url, 0);
break;
}
}
void
GURL::clear_djvu_cgi_arguments(void)
{
if(!validurl) init();
// First - modify the arrays
GCriticalSectionLock lock(&class_lock);
for(int i=0;i<cgi_name_arr.size();i++)
{
if (cgi_name_arr[i].upcase()==djvuopts)
{
cgi_name_arr.resize(i-1);
cgi_value_arr.resize(i-1);
break;
}
}
// And store them back into the URL
store_cgi_args();
}
void
GURL::add_djvu_cgi_argument(const GUTF8String &name, const char * value)
{
if(!validurl)
init();
GCriticalSectionLock lock1(&class_lock);
// Check if we already have the "DJVUOPTS" argument
bool have_djvuopts=false;
for(int i=0;i<cgi_name_arr.size();i++)
{
if (cgi_name_arr[i].upcase()==djvuopts)
{
have_djvuopts=true;
break;
}
}
// If there is no DJVUOPTS, insert it
if (!have_djvuopts)
{
int pos=cgi_name_arr.size();
cgi_name_arr.resize(pos);
cgi_value_arr.resize(pos);
cgi_name_arr[pos]=djvuopts;
}
// Add new argument to the array
int pos=cgi_name_arr.size();
cgi_name_arr.resize(pos);
cgi_value_arr.resize(pos);
cgi_name_arr[pos]=name;
cgi_value_arr[pos]=value;
// And update the URL
store_cgi_args();
}
bool
GURL::is_local_file_url(void) const
{
if(!validurl) const_cast<GURL *>(this)->init();
GCriticalSectionLock lock((GCriticalSection *) &class_lock);
return (protocol()=="file" && url[5]==slash);
}
static const int
pathname_start(const GUTF8String &url, const int protolength)
{
const int length=url.length();
int retval=0;
if(protolength+1<length)
{
retval=url.search(slash,((url[protolength+1] == '/')
?((url[protolength+2] == '/')?(protolength+3):(protolength+2))
:(protolength+1)));
}
return (retval>0)?retval:length;
}
GUTF8String
GURL::pathname(void) const
{
return (is_local_file_url())
?GURL::encode_reserved(UTF8Filename())
:url.substr(pathname_start(url,protocol().length()),(unsigned int)(-1));
}
GURL
GURL::base(void) const
{
const GUTF8String xurl(get_string());
const int protocol_length=protocol(xurl).length();
const int xurl_length=xurl.length();
const char * const url_ptr=xurl;
const char * ptr, * xslash;
ptr=xslash=url_ptr+protocol_length+1;
if(xslash[0] == '/')
{
xslash++;
if(xslash[0] == '/')
xslash++;
for(ptr=xslash;ptr[0] && !is_argument(ptr);ptr++)
{
if ((ptr[0]==slash)&&ptr[1]&&!is_argument(ptr+1))
xslash=ptr;
}
if(xslash[0] != '/')
{
xslash=url_ptr+xurl_length;
}
}
return GURL::UTF8(
// ifdef WIN32
// (*(xslash-1) == colon)?
// (GUTF8String(xurl,(int)(xslash-url_ptr))+"/" ):
// endif
(GUTF8String(xurl,(int)(xslash-url_ptr))+"/"));
}
bool
GURL::operator==(const GURL & gurl2) const
{
bool retval=false;
const GUTF8String g1(get_string());
const int g1_length=g1.length();
const GUTF8String g2(gurl2.get_string());
const int g2_length=g2.length();
if(g1_length == g2_length) // exactly equal
{
retval=(g1==g2);
}else if(g1_length+1 == g2_length) // g1 is g2 with a slash at the end
{
retval=(g2[g1_length] == '/')&&!g1.cmp(g2,g1_length);
}else if(g2_length+1 == g1_length) // g2 is g1 with a slash at the end
{
retval=(g1[g2_length] == '/')&&!g1.cmp(g2,g2_length);
}
return retval;
}
GUTF8String
GURL::name(void) const
{
if(!validurl)
const_cast<GURL *>(this)->init();
GUTF8String retval;
if(!is_empty())
{
const GUTF8String xurl(url);
const int protocol_length=protocol(xurl).length();
const char * ptr, * xslash=(const char *)xurl+protocol_length-1;
for(ptr=(const char *)xurl+protocol_length;
*ptr && !is_argument(ptr);ptr++)
{
if (*ptr==slash)
xslash=ptr;
}
retval=GUTF8String(xslash+1, ptr-xslash-1);
}
return retval;
}
GUTF8String
GURL::fname(void) const
{
if(!validurl)
const_cast<GURL *>(this)->init();
return decode_reserved(name());
}
GUTF8String
GURL::extension(void) const
{
if(!validurl)
const_cast<GURL *>(this)->init();
GUTF8String xfilename=name();
GUTF8String retval;
for(int i=xfilename.length()-1;i>=0;i--)
{
if (xfilename[i]=='.')
{
retval=(const char*)xfilename+i+1;
break;
}
}
return retval;
}
GUTF8String
GURL::decode_reserved(const GUTF8String &gurl)
{
const char *url=gurl;
char *res;
GPBuffer<char> gres(res,gurl.length()+1);
char *r=res;
for(const char * ptr=url;*ptr;++ptr,++r)
{
if (*ptr!=percent)
{
r[0]=*ptr;
}else
{
int c1,c2;
if ( ((c1=hexval(ptr[1]))>=0)
&& ((c2=hexval(ptr[2]))>=0) )
{
r[0]=(c1<<4)|c2;
ptr+=2;
} else
{
r[0]=*ptr;
}
}
}
r[0]=0;
GUTF8String retval(res);
if(!retval.is_valid())
{
retval=GNativeString(res);
}
return retval;
}
GUTF8String
GURL::encode_reserved(const GUTF8String &gs)
{
const char *s=(const char *)gs;
// Potentially unsafe characters (cf. RFC1738 and RFC1808)
static const char hex[] = "0123456789ABCDEF";
unsigned char *retval;
GPBuffer<unsigned char> gd(retval,strlen(s)*3+1);
unsigned char *d=retval;
for (; *s; s++,d++)
{
// Convert directory separator to slashes
#if defined(WIN32) || defined(OS2)
if (*s == backslash || *s== slash)
#else
#ifdef macintosh
if (*s == colon )
#else
#ifdef UNIX
if (*s == slash )
#else
#error "Define something here for your operating system"
#endif
#endif
#endif
{
*d = slash;
continue;
}
unsigned char const ss=(unsigned char const)(*s);
// WARNING: Whenever you modify this conversion code,
// make sure, that the following functions are in sync:
// encode_reserved()
// decode_reserved()
// url_to_filename()
// filename_to_url()
// unreserved characters
if ( (ss>='a' && ss<='z') ||
(ss>='A' && ss<='Z') ||
(ss>='0' && ss<='9') ||
(strchr("$-_.+!*'(),:~=", ss)) )
{
*d = ss;
continue;
}
// escape sequence
d[0] = percent;
d[1] = hex[ (ss >> 4) & 0xf ];
d[2] = hex[ (ss) & 0xf ];
d+=2;
}
*d = 0;
return retval;
}
// -------------------------------------------
// Functions for converting filenames and urls
// -------------------------------------------
static GUTF8String
url_from_UTF8filename(const GUTF8String &gfilename)
{
if(GURL::UTF8(gfilename).is_valid())
{
DEBUG_MSG("Debug: URL as Filename: " << gfilename << "\n");
}
const char *filename=gfilename;
if(filename && (unsigned char)filename[0] == (unsigned char)0xEF
&& (unsigned char)filename[1] == (unsigned char)0xBB
&& (unsigned char)filename[2] == (unsigned char)0xBF)
{
filename+=3;
}
// Special case for blank pages
if(!filename || !filename[0])
{
return GUTF8String();
}
// Normalize file name to url slash-and-escape syntax
GUTF8String oname=GURL::expand_name(filename);
GUTF8String nname=GURL::encode_reserved(oname);
// Preprend "file://" to file name. If file is on the local
// machine, include "localhost".
GUTF8String url=filespecslashes;
const char *cnname=nname;
if (cnname[0] == slash)
{
if (cnname[1] == slash)
{
url += cnname+2;
}else
{
url = localhost + nname;
}
}else
{
url += (localhostspec1+2) + nname;
}
return url;
}
GUTF8String
GURL::get_string(const bool nothrow) const
{
if(!validurl)
const_cast<GURL *>(this)->init(nothrow);
return url;
}
// -- Returns a url for accessing a given file.
// If useragent is not provided, standard url will be created,
// but will not be understood by some versions if IE.
GUTF8String
GURL::get_string(const GUTF8String &useragent) const
{
if(!validurl)
const_cast<GURL *>(this)->init();
GUTF8String retval(url);
if(is_local_file_url()&&useragent.length())
{
if(useragent.search("MSIE") >= 0 || useragent.search("Microsoft")>=0)
{
retval=filespecslashes + expand_name(UTF8Filename());
}
}
return retval;
}
GURL::UTF8::UTF8(const GUTF8String &xurl)
: GURL(xurl) {}
GURL::UTF8::UTF8(const GUTF8String &xurl,const GURL &codebase)
: GURL(xurl,codebase) {}
GURL::GURL(const GUTF8String &xurl,const GURL &codebase)
: validurl(false)
{
if(GURL::UTF8(xurl).is_valid())
{
url=xurl;
}else
{
const char *c=xurl;
if(c[0] == slash)
{
GURL base(codebase);
for(GURL newbase=base.base();newbase!=base;newbase=base.base())
{
base=newbase;
}
url=base.get_string(true)+GURL::encode_reserved(xurl);
}else
{
url=beautify_path(codebase.get_string(true)+GUTF8String(slash)+GURL::encode_reserved(xurl));
}
}
}
GURL::Native::Native(const GNativeString &xurl)
: GURL(xurl) {}
GURL::Native::Native(const GNativeString &xurl,const GURL &codebase)
: GURL(xurl,codebase) {}
GURL::GURL(const GNativeString &xurl,const GURL &codebase)
: validurl(false)
{
GURL retval(xurl.getNative2UTF8(),codebase);
if(retval.is_valid())
{
#if defined(WIN32)
// Hack for IE to change \\ to /
if(retval.is_local_file_url())
{
GURL::Filename::UTF8 retval2(retval.UTF8Filename());
url=retval2.get_string(true);
validurl=false;
}else
#endif // WIN32
{
url=retval.get_string(true);
validurl=false;
}
}
}
GURL::Filename::Filename(const GNativeString &gfilename)
{
url=url_from_UTF8filename(gfilename.getNative2UTF8());
}
GURL::Filename::Native::Native(const GNativeString &gfilename)
: GURL::Filename(gfilename) {}
GURL::Filename::Filename(const GUTF8String &gfilename)
{
url=url_from_UTF8filename(gfilename);
}
GURL::Filename::UTF8::UTF8(const GUTF8String &gfilename)
: GURL::Filename(gfilename) {}
// filename --
// -- Applies heuristic rules to convert a url into a valid file name.
// Returns a simple basename in case of failure.
GUTF8String
GURL::UTF8Filename(void) const
{
GUTF8String retval;
if(! is_empty())
{
const char *url_ptr=url;
// WARNING: Whenever you modify this conversion code,
// make sure, that the following functions are in sync:
// encode_reserved()
// decode_reserved()
// url_to_filename()
// filename_to_url()
GUTF8String urlcopy=decode_reserved(url);
url_ptr = urlcopy;
// All file urls are expected to start with filespec which is "file:"
if (GStringRep::cmp(filespec, url_ptr, sizeof(filespec)-1)) //if not
return GOS::basename(url_ptr);
url_ptr += sizeof(filespec)-1;
#if defined(macintosh)
//remove all leading slashes
for(;*url_ptr==slash;url_ptr++)
EMPTY_LOOP;
// Remove possible localhost spec
if ( !GStringRep::cmp(localhost, url_ptr, sizeof(localhost)-1) )
url_ptr += sizeof(localhost)-1;
//remove all leading slashes
while(*url_ptr==slash)
url_ptr++;
#else
// Remove possible localhost spec
if ( !GStringRep::cmp(localhostspec1, url_ptr, sizeof(localhostspec1)-1) )
// RFC 1738 local host form
url_ptr += sizeof(localhostspec1)-1;
else if ( !GStringRep::cmp(localhostspec2, url_ptr, sizeof(localhostspec2)-1 ) )
// RFC 1738 local host form
url_ptr += sizeof(localhostspec2)-1;
else if ( (strlen(url_ptr) > 4) // "file://<letter>:/<path>"
&& (url_ptr[0] == slash) // "file://<letter>|/<path>"
&& (url_ptr[1] == slash)
&& isalpha(url_ptr[2])
&& ( url_ptr[3] == colon || url_ptr[3] == '|' )
&& (url_ptr[4] == slash) )
url_ptr += 2;
else if ( (strlen(url_ptr)) > 2 // "file:/<path>"
&& (url_ptr[0] == slash)
&& (url_ptr[1] != slash) )
url_ptr++;
#endif
// Check if we are finished
#if defined(macintosh)
{
char *l_url;
GPBuffer<char> gl_url(l_url,strlen(url_ptr)+1);
const char *s;
char *r;
for ( s=url_ptr,r=l_url; *s; s++,r++)
{
*r=(*s == slash)?colon:*s;
}
*r=0;
retval = expand_name(l_url,root);
}
#else
retval = expand_name(url_ptr,root);
#endif
#if defined(WIN32) || defined(OS2)
if (url_ptr[0] && url_ptr[1]=='|' && url_ptr[2]== slash)
{
if ((url_ptr[0]>='a' && url_ptr[0]<='z')
|| (url_ptr[0]>='A' && url_ptr[0]<='Z'))
{
GUTF8String drive;
drive.format("%c%c%c", url_ptr[0],colon,backslash);
retval = expand_name(url_ptr+3, drive);
}
}
#endif
}
// Return what we have
return retval;
}
GNativeString
GURL::NativeFilename(void) const
{
return UTF8Filename().getUTF82Native();
}
#if defined(UNIX) || defined(macintosh) || defined(OS2)
static int
urlstat(const GURL &url,struct stat &buf)
{
return ::stat(url.NativeFilename(),&buf);
}
#endif
// is_file(url) --
// -- returns true if filename denotes a regular file.
bool
GURL::is_file(void) const
{
bool retval=false;
if(is_local_file_url())
{
#if defined(UNIX) || defined(macintosh) || defined(OS2)
struct stat buf;
if (!urlstat(*this,buf))
{
retval=!(buf.st_mode & S_IFDIR);
}
#elif defined(WIN32)
GUTF8String filename(UTF8Filename());
if(filename.length() >= MAX_PATH)
{
if(!filename.cmp("\\\\",2))
filename="\\\\?\\UNC"+filename.substr(1,-1);
else
filename="\\\\?\\"+filename;
}
wchar_t *wfilename;
const size_t wfilename_size=filename.length()+1;
GPBuffer<wchar_t> gwfilename(wfilename,wfilename_size);
filename.ncopy(wfilename,wfilename_size);
DWORD dwAttrib;
dwAttrib = GetFileAttributesW(wfilename);
if((dwAttrib|1) == 0xFFFFFFFF)
{
USES_CONVERSION ;
dwAttrib = GetFileAttributes(A2CT(NativeFilename())) ;//MBCS cvt
}
retval=!( dwAttrib & FILE_ATTRIBUTE_DIRECTORY );
#else
# error "Define something here for your operating system"
#endif
}
return retval;
}
bool
GURL::is_local_path(void) const
{
bool retval=false;
if(is_local_file_url())
{
#if defined(UNIX) || defined(macintosh) || defined(OS2)
struct stat buf;
retval=!urlstat(*this,buf);
#else
GUTF8String filename(UTF8Filename());
if(filename.length() >= MAX_PATH)
{
if(!filename.cmp("\\\\",2))
filename="\\\\?\\UNC"+filename.substr(1,-1);
else
filename="\\\\?\\"+filename;
}
wchar_t *wfilename;
const size_t wfilename_size=filename.length()+1;
GPBuffer<wchar_t> gwfilename(wfilename,wfilename_size);
filename.ncopy(wfilename,wfilename_size);
DWORD dwAttrib;
dwAttrib = GetFileAttributesW(wfilename);
if((dwAttrib|1) == 0xFFFFFFFF)
{
USES_CONVERSION ;
dwAttrib = GetFileAttributes(A2CT(NativeFilename())) ;//MBCS cvt
}
retval=( (dwAttrib|1) != 0xFFFFFFFF);
#endif
}
return retval;
}
// is_dir(url) --
// -- returns true if url denotes a directory.
bool
GURL::is_dir(void) const
{
bool retval=false;
if(is_local_file_url())
{
// UNIX implementation
#if defined(UNIX) || defined(macintosh) || defined(OS2)
struct stat buf;
if (!urlstat(*this,buf))
{
retval=(buf.st_mode & S_IFDIR);
}
#elif defined(WIN32) // (either Windows or WCE)
GUTF8String filename(UTF8Filename());
if(filename.length() >= MAX_PATH)
{
if(!filename.cmp("\\\\",2))
filename="\\\\?\\UNC"+filename.substr(1,-1);
else
filename="\\\\?\\"+filename;
}
wchar_t *wfilename;
const size_t wfilename_size=filename.length()+1;
GPBuffer<wchar_t> gwfilename(wfilename,wfilename_size);
filename.ncopy(wfilename,wfilename_size);
DWORD dwAttrib;
dwAttrib = GetFileAttributesW(wfilename);
if((dwAttrib|1) == 0xFFFFFFFF)
{
USES_CONVERSION ;
dwAttrib = GetFileAttributes(A2CT(NativeFilename())) ;//MBCS cvt
}
retval=((dwAttrib != 0xFFFFFFFF)&&( dwAttrib & FILE_ATTRIBUTE_DIRECTORY ));
#else
# error "Define something here for your operating system"
#endif
}
return retval;
}
// Follows symbolic links.
GURL
GURL::follow_symlinks(void) const
{
GURL ret = *this;
#if defined(S_IFLNK)
#if defined(UNIX) || defined(macintosh)
int lnklen;
char lnkbuf[MAXPATHLEN+1];
struct stat buf;
while ( (urlstat(ret, buf) >= 0) &&
(buf.st_mode & S_IFLNK) &&
((lnklen = readlink(ret.NativeFilename(),lnkbuf,sizeof(lnkbuf))) > 0) )
{
lnkbuf[lnklen] = 0;
GNativeString lnk(lnkbuf);
ret = GURL(lnk, ret.base());
}
#endif
#endif
return ret;
}
int
GURL::mkdir() const
{
if(! is_local_file_url())
return -1;
int retval=0;
const GURL baseURL=base();
if (baseURL.get_string() != url && !baseURL.is_dir())
retval = baseURL.mkdir();
if(!retval)
{
#if defined(UNIX)
if (is_dir())
retval = 0;
else
retval = ::mkdir(NativeFilename(), 0755);
#elif defined(WIN32)
USES_CONVERSION;
if (is_dir())
retval = 0;
else
retval = CreateDirectory(A2CT(NativeFilename()), NULL);
#else
# error "Define something here for your operating system"
#endif
}
return retval;
}
// deletefile
// -- deletes a file or directory
int
GURL::deletefile(void) const
{
int retval = -1;
if(is_local_file_url())
{
#if defined(UNIX)
if (is_dir())
retval = ::rmdir(NativeFilename());
else
retval = ::unlink(NativeFilename());
#elif defined(WIN32)
USES_CONVERSION;
if (is_dir())
retval = ::RemoveDirectory(A2CT(NativeFilename()));
else
retval = ::DeleteFile(A2CT(NativeFilename()));
#else
# error "Define something here for your operating system"
#endif
}
return retval;
}
GList<GURL>
GURL::listdir(void) const
{
GList<GURL> retval;
if(is_dir())
{
#if defined(UNIX) || defined(OS2)
DIR * dir=opendir(NativeFilename());//MBCS cvt
for(dirent *de=readdir(dir);de;de=readdir(dir))
{
const int len = NAMLEN(de);
if (de->d_name[0]== dot && len==1)
continue;
if (de->d_name[0]== dot && de->d_name[1]== dot && len==2)
continue;
retval.append(GURL::Native(de->d_name,*this));
}
closedir(dir);
#elif defined (WIN32)
GURL::UTF8 wildcard("*.*",*this);
WIN32_FIND_DATA finddata;
HANDLE handle = FindFirstFile(wildcard.NativeFilename(), &finddata);//MBCS cvt
const GUTF8String gpathname=pathname();
const GUTF8String gbase=base().pathname();
if( handle != INVALID_HANDLE_VALUE)
{
do
{
GURL::UTF8 Entry(finddata.cFileName,*this);
const GUTF8String gentry=Entry.pathname();
if((gentry != gpathname) && (gentry != gbase))
retval.append(Entry);
} while( FindNextFile(handle, &finddata) );
FindClose(handle);
}
#else
# error "Define something here for your operating system"
#endif
}
return retval;
}
int
GURL::cleardir(const int timeout) const
{
int retval=(-1);
if(is_dir())
{
GList<GURL> dirlist=listdir();
retval=0;
for(GPosition pos=dirlist;pos&&!retval;++pos)
{
const GURL &Entry=dirlist[pos];
if(Entry.is_dir())
{
if((retval=Entry.cleardir(timeout)) < 0)
{
break;
}
}
if(((retval=Entry.deletefile())<0) && (timeout>0))
{
GOS::sleep(timeout);
retval=Entry.deletefile();
}
}
}
return retval;
}
int
GURL::renameto(const GURL &newurl) const
{
if (is_local_file_url() && newurl.is_local_file_url())
return rename(NativeFilename(),newurl.NativeFilename());
return -1;
}
// expand_name(filename[, fromdirname])
// -- returns the full path name of filename interpreted
// relative to fromdirname. Use current working dir when
// fromdirname is null.
GUTF8String
GURL::expand_name(const GUTF8String &xfname, const char *from)
{
const char *fname=xfname;
GUTF8String retval;
const size_t maxlen=xfname.length()*9+MAXPATHLEN+10;
char * const string_buffer = retval.getbuf(maxlen);
// UNIX implementation
#if defined(UNIX)
// Perform tilde expansion
GUTF8String senv;
if (fname && fname[0]==tilde)
{
int n;
for(n=1;fname[n] && fname[n]!= slash;n++)
EMPTY_LOOP;
struct passwd *pw=0;
if (n!=1)
{
GUTF8String user(fname+1, n-1);
pw=getpwnam(user);
}else if ((senv=GOS::getenv("HOME")).length())
{
from=(const char *)senv;
fname = fname + n;
}else if ((senv=GOS::getenv("LOGNAME")).length())
{
pw = getpwnam((const char *)senv.getUTF82Native());
}else
{
pw=getpwuid(getuid());
}
if (pw)
{
senv=GNativeString(pw->pw_dir).getNative2UTF8();
from = (const char *)senv;
fname = fname + n;
}
for(;fname[0] == slash; fname++)
EMPTY_LOOP;
}
// Process absolute vs. relative path
if (fname && fname[0]== slash)
{
string_buffer[0]=slash;
string_buffer[1]=0;
}else if (from)
{
strcpy(string_buffer, expand_name(from));
}else
{
strcpy(string_buffer, GOS::cwd());
}
char *s = string_buffer + strlen(string_buffer);
if(fname)
{
for(;fname[0]== slash;fname++)
EMPTY_LOOP;
// Process path components
while(fname[0])
{
if (fname[0] == dot )
{
if (!fname[1] || fname[1]== slash)
{
fname++;
continue;
}else if (fname[1]== dot && (fname[2]== slash || !fname[2]))
{
fname +=2;
for(;s>string_buffer+1 && *(s-1)== slash; s--)
EMPTY_LOOP;
for(;s>string_buffer+1 && *(s-1)!= slash; s--)
EMPTY_LOOP;
continue;
}
}
if ((s==string_buffer)||(*(s-1)!= slash))
{
*s = slash;
s++;
}
while (*fname &&(*fname!= slash))
{
*s = *fname++;
if ((size_t)((++s)-string_buffer) > maxlen)
{
G_THROW( ERR_MSG("GURL.big_name") );
}
}
*s = 0;
for(;fname[0]== slash;fname++)
EMPTY_LOOP;
}
}
if (!fname || !fname[0])
{
for(;s>string_buffer+1 && *(s-1) == slash; s--)
EMPTY_LOOP;
*s = 0;
}
#elif defined (WIN32) // WIN32 implementation
// Handle base
strcpy(string_buffer, (char const *)(from ? expand_name(from) : GOS::cwd()));
// GNativeString native;
if (fname)
{
char *s = string_buffer;
char drv[4];
// Handle absolute part of fname
// Put absolute part of the file name in string_buffer, and
// the relative part pointed to by fname.
if (fname[0]== slash || fname[0]== backslash)
{
if (fname[1]== slash || fname[1]== backslash)
{ // Case "//abcd"
s[0]=s[1]= backslash; s[2]=0;
}
else
{ // Case "/abcd" or "/"
// File is at the root of the current drive. Delete the
// slash at the beginning of the filename and leave
// an explicit identification of the root of the drive in
// string_buffer.
fname++;
s[3] = '\0';
}
}
else if (fname[0] && fname[1]==colon)
{
if (fname[2]!= slash && fname[2]!= backslash)
{ // Case "x:abcd"
if ( toupper((unsigned char)s[0]) != toupper((unsigned char)fname[0])
|| s[1]!=colon)
{
drv[0]=fname[0];
drv[1]=colon;
drv[2]= dot ;
drv[3]=0;
GetFullPathName(drv, maxlen, string_buffer, &s);
strcpy(string_buffer,(const char *)GUTF8String(string_buffer).getNative2UTF8());
s = string_buffer;
}
fname += 2;
}
else if (fname[3]!= slash && fname[3]!= backslash)
{ // Case "x:/abcd"
s[0]=toupper((unsigned char)fname[0]);
s[1]=colon;
s[2]=backslash;
s[3]=0;
fname += 3;
}
else
{ // Case "x://abcd"
s[0]=s[1]=backslash;
s[2]=0;
fname += 4;
}
}
// Process path components
for(;*fname== slash || *fname==backslash;fname++)
EMPTY_LOOP;
while(*fname)
{
if (fname[0]== dot )
{
if (fname[1]== slash || fname[1]==backslash || !fname[1])
{
fname++;
continue;
}else if ((fname[1] == dot)
&& (fname[2]== slash || fname[2]==backslash || !fname[2]))
{
fname += 2;
char *back=_tcsrchr(string_buffer,backslash);
char *forward=_tcsrchr(string_buffer,slash);
if(back>forward)
{
*back=0;
}else if(forward)
{
*forward=0;
}
s = string_buffer;
continue;
}
char* s2=s;//MBCS DBCS
for(;*s;s++)
EMPTY_LOOP;
char* back = _tcsrchr(s2,backslash);//MBCS DBCS
if ((s>string_buffer)&&(*(s-1)!= slash)&&
(back == NULL || (back!=NULL && s-1 != back) ))//MBCS DBCS
{
*s = backslash;
s++;
}
while (*fname && *fname!= slash && *fname!=backslash)
{
*s = *fname++;
if ((size_t)((++s)-string_buffer) > maxlen)
G_THROW( ERR_MSG("GURL.big_name") );
}
*s = 0;
}
char* s2=s;//MBCS DBCS
for(;*s;s++)
EMPTY_LOOP;
char* back = _tcsrchr(s2,backslash);//MBCS DBCS
if ((s>string_buffer)&&(*(s-1)!= slash)
&&(back == NULL || (back!=NULL && s-1 != back) ))//MBCS DBCS
{
*s = backslash;
s++;
}
while (*fname && (*fname!= slash) && (*fname!=backslash))
{
*s = *fname++;
if ((size_t)((++s)-string_buffer) > maxlen)
G_THROW( ERR_MSG("GURL.big_name") );
}
*s = 0;
for(;(*fname== slash)||(*fname==backslash);fname++)
EMPTY_LOOP;
}
}
#elif defined(macintosh) // MACINTOSH implementation
strcpy(string_buffer, (const char *)(from?from:GOS::cwd()));
if (!GStringRep::cmp(fname, string_buffer,strlen(string_buffer)) || is_file(fname))
{
strcpy(string_buffer, "");//please don't expand, the logic of filename is chaos.
}
// Process path components
char *s = string_buffer + strlen(string_buffer);
if(fname)
{
for(;fname[0]==colon;fname++)
EMPTY_LOOP;
while(fname[0])
{
if (fname[0]== dot )
{
if (fname[1]==colon || !fname[1])
{
fname++;
continue;
}
if ((fname[1]== dot )
&&(fname[2]==colon || fname[2]==0))
{
fname +=2;
for(;(s>string_buffer+1)&&(*(s-1)==colon);s--)
EMPTY_LOOP;
for(;(s>string_buffer+1)&&(*(s-1)!=colon);s--)
EMPTY_LOOP;
continue;
}
}
if ((s==string_buffer)||(*(s-1)!=colon))
{
*s = colon;
s++;
}
while (*fname!=0 && *fname!=colon)
{
*s = *fname++;
if ((++s)-string_buffer > maxlen)
G_THROW( ERR_MSG("GURL.big_name") );
}
*s = 0;
for(;fname[0]==colon;fname++)
EMPTY_LOOP;
}
}
for(;(s>string_buffer+1) && (*(s-1)==colon);s--)
EMPTY_LOOP;
*s = 0;
return ((string_buffer[0]==colon)?(string_buffer+1):string_buffer);
#else
# error "Define something here for your operating system"
#endif
return retval;
}
unsigned int
hash(const GURL & gurl)
{
unsigned int retval;
const GUTF8String s(gurl.get_string());
const int len=s.length();
if(len && (s[len-1] == '/')) // Don't include the trailing slash as part of the hash.
{
retval=hash(s.substr(0,len-1));
}else
{
retval=hash(s);
}
return retval;
}
#ifdef HAVE_NAMESPACES
}
# ifndef NOT_USING_DJVU_NAMESPACE
using namespace DJVU;
# endif
#endif