diff --git a/src/internal/ReaderMgr.cpp b/src/internal/ReaderMgr.cpp
index 50ba1ae97585e4b8dd06cea88f054ca11fb99096..ea5caf8c926e7e6bb047c413b4d5f0b2b44f357f 100644
--- a/src/internal/ReaderMgr.cpp
+++ b/src/internal/ReaderMgr.cpp
@@ -56,6 +56,10 @@
 
 /**
  * $Log$
+ * Revision 1.3  2000/01/12 00:15:03  roddey
+ * Changes to deal with multiply nested, relative pathed, entities and to deal
+ * with the new URL class changes.
+ *
  * Revision 1.2  1999/12/18 00:20:00  roddey
  * More changes to support the new, completely orthagonal, support for
  * intrinsic encodings.
@@ -85,14 +89,14 @@
 #include <util/URL.hpp>
 #include <util/XMLUni.hpp>
 #include <sax/InputSource.hpp>
+#include <framework/LocalFileInputSource.hpp>
+#include <framework/URLInputSource.hpp>
 #include <framework/XMLBuffer.hpp>
 #include <framework/XMLDocumentHandler.hpp>
 #include <framework/XMLEntityDecl.hpp>
 #include <framework/XMLEntityHandler.hpp>
 #include <internal/EndOfEntityException.hpp>
 #include <internal/ReaderMgr.hpp>
-#include <internal/URLInputSource.hpp>
-
 
 
 // ---------------------------------------------------------------------------
@@ -100,8 +104,7 @@
 // ---------------------------------------------------------------------------
 ReaderMgr::ReaderMgr() :
 
-    fBasePath(0)
-    , fCurEntity(0)
+    fCurEntity(0)
     , fCurReader(0)
     , fEntityHandler(0)
     , fEntityStack(0)
@@ -119,7 +122,6 @@ ReaderMgr::~ReaderMgr()
     //  does not own its elements either, so deleting it will not delete the
     //  entities it still references!)
     //
-    delete [] fBasePath;
     delete fCurReader;
     delete fReaderStack;
     delete fEntityStack;
@@ -520,8 +522,8 @@ XMLReader* ReaderMgr::createReader( const   XMLCh* const        sysId
     XMLBuffer expSysId;
 
     //
-    //  Allow the entity handler to expand the system id. If we don't
-    //  have one, or they don't do anything, then just use it as is.
+    //  Allow the entity handler to expand the system id if they choose
+    //  to do so.
     //
     if (fEntityHandler)
     {
@@ -530,37 +532,7 @@ XMLReader* ReaderMgr::createReader( const   XMLCh* const        sysId
     }
      else
     {
-        //
-        //  Try to parse it as a URL. If it succeeds, it must be a
-        //  fully qualified path. Otherwise, its just a file name and
-        //  could be partial so check and append it to the base path
-        //  if so.
-        //
-        URL tmpURL;
-        try
-        {
-            tmpURL.setURL(sysId);
-            expSysId.set(tmpURL.getPath());
-        }
-
-        catch(const MalformedURLException&)
-        {
-            // Its just a file path
-            if (XMLPlatformUtils::isRelative(sysId))
-            {
-                // Its relative so first store the base directory, if any
-                if (fBasePath)
-                    expSysId.set(fBasePath);
-
-                // And then append the relative path
-                expSysId.append(sysId);
-            }
-             else
-            {
-                // The path is not relative, so just set it directly
-                expSysId.set(sysId);
-            }
-        }
+        expSysId.set(sysId);
     }
 
     // Call the entity resolver interface to get an input source
@@ -574,9 +546,31 @@ XMLReader* ReaderMgr::createReader( const   XMLCh* const        sysId
         );
     }
 
-    // If they didn't give us anything, then make up a URL input source
+    //
+    //  If they didn't create a source via the entity resolver, then we
+    //  have to create one on our own.
+    //
     if (!srcToFill)
-        srcToFill = new URLInputSource(expSysId.getRawBuffer());
+    {
+        LastExtEntityInfo lastInfo;
+        getLastExtEntityInfo(lastInfo);
+
+        try
+        {
+            URL urlTmp(lastInfo.systemId, expSysId.getRawBuffer());
+            srcToFill = new URLInputSource(urlTmp);
+        }
+
+        catch(const MalformedURLException&)
+        {
+            // Its not a URL, so lets assume its a local file name.
+            srcToFill = new LocalFileInputSource
+            (
+                lastInfo.systemId
+                , expSysId.getRawBuffer()
+            );
+        }
+    }
 
     // Put a janitor on the input source
     Janitor<InputSource> janSrc(srcToFill);
@@ -601,10 +595,7 @@ XMLReader* ReaderMgr::createReader( const   XMLCh* const        sysId
     if (!retVal)
         return 0;
 
-    //
-    //  Give this reader the next available reader number. And let the input
-    //  source live to be returned to the caller.
-    //
+    // Give this reader the next available reader number and return it
     retVal->setReaderNum(fNextReaderNum++);
     return retVal;
 }
@@ -794,10 +785,6 @@ void ReaderMgr::reset()
     // Reset all of the flags
     fThrowEOE = false;
 
-    // Delete the base path, which has to be reset each time
-    delete [] fBasePath;
-    fBasePath = 0;
-
     // Delete the current reader and flush the reader stack
     delete fCurReader;
     fCurReader = 0;
diff --git a/src/internal/ReaderMgr.hpp b/src/internal/ReaderMgr.hpp
index 098b90839bd6478570bf180fed1e350b3313a0e6..eab81e6eddbc317ccc5b9a5eb8c2d3324b01dbb7 100644
--- a/src/internal/ReaderMgr.hpp
+++ b/src/internal/ReaderMgr.hpp
@@ -56,6 +56,10 @@
 
 /**
  * $Log$
+ * Revision 1.3  2000/01/12 00:15:04  roddey
+ * Changes to deal with multiply nested, relative pathed, entities and to deal
+ * with the new URL class changes.
+ *
  * Revision 1.2  1999/12/15 19:48:03  roddey
  * Changed to use new split of transcoder interfaces into XML transcoders and
  * LCP transcoders, and implementation of intrinsic transcoders as pluggable
@@ -212,7 +216,6 @@ public :
     // -----------------------------------------------------------------------
     //  Setter methods
     // -----------------------------------------------------------------------
-    void setBasePath(const XMLCh* const path);
     void setEntityHandler(XMLEntityHandler* const newHandler);
     void setThrowEOE(const bool newValue);
 
@@ -228,10 +231,6 @@ private :
     // -----------------------------------------------------------------------
     //  Private data members
     //
-    //  fBasePath
-    //      This is the base path from which all other relative references
-    //      are based. Its passed on to us from the scanner.
-    //
     //  fCurEntity
     //      This is the current top of stack entity. We pull it off the stack
     //      and store it here for efficiency.
@@ -266,7 +265,6 @@ private :
     //      of entities in the int/ext subsets, so it will turn this flag off
     //      until it gets into the content usually.
     // -----------------------------------------------------------------------
-    XMLCh*                      fBasePath;
     XMLEntityDecl*              fCurEntity;
     XMLReader*                  fCurReader;
     XMLEntityHandler*           fEntityHandler;
@@ -357,12 +355,6 @@ inline void ReaderMgr::skipPastChar(const XMLCh toSkipPast)
     }
 }
 
-inline void ReaderMgr::setBasePath(const XMLCh* const path)
-{
-    delete [] fBasePath;
-    fBasePath = XMLString::replicate(path);
-}
-
 inline void ReaderMgr::setEntityHandler(XMLEntityHandler* const newHandler)
 {
     fEntityHandler = newHandler;
diff --git a/src/internal/XMLScanner.cpp b/src/internal/XMLScanner.cpp
index 764bad5f81ce2d229d7ccca00a97ab0a72c689be..127ff259967a2df6a4cf273d3030a81daa660c9a 100644
--- a/src/internal/XMLScanner.cpp
+++ b/src/internal/XMLScanner.cpp
@@ -56,6 +56,10 @@
 
 /**
  * $Log$
+ * Revision 1.5  2000/01/12 00:15:04  roddey
+ * Changes to deal with multiply nested, relative pathed, entities and to deal
+ * with the new URL class changes.
+ *
  * Revision 1.4  1999/12/08 00:15:06  roddey
  * Some small last minute fixes to get into the 3.0.1 build that is going to be
  * going out anyway for platform fixes.
@@ -87,6 +91,7 @@
 // ---------------------------------------------------------------------------
 //  Includes
 // ---------------------------------------------------------------------------
+#include <util/Janitor.hpp>
 #include <util/Mutexes.hpp>
 #include <util/PlatformUtils.hpp>
 #include <util/RefVectorOf.hpp>
@@ -97,6 +102,8 @@
 #include <util/XMLUni.hpp>
 #include <sax/InputSource.hpp>
 #include <sax/SAXException.hpp>
+#include <framework/LocalFileInputSource.hpp>
+#include <framework/URLInputSource.hpp>
 #include <framework/XMLDocumentHandler.hpp>
 #include <framework/XMLElementDecl.hpp>
 #include <framework/XMLErrorReporter.hpp>
@@ -204,6 +211,52 @@ XMLScanner::~XMLScanner()
 // ---------------------------------------------------------------------------
 //  XMLScanner: Main entry point to scan a document
 // ---------------------------------------------------------------------------
+void XMLScanner::scanDocument(  const   XMLCh* const    systemId
+                                , const bool            reuseValidator)
+{
+    //
+    //  First we try to parse it as a URL. If that fails, we assume its
+    //  a file and try it that way.
+    //
+    InputSource* srcToUse = 0;
+    try
+    {
+        //
+        //  Create a temporary URL. Since this is the primary document,
+        //  it has to be fully qualified. If not, then assume we are just
+        //  mistaking a file for a URL.
+        //
+        URL tmpURL(systemId);
+        if (tmpURL.isRelative())
+            ThrowXML(MalformedURLException, XML4CExcepts::URL_NoProtocolPresent);
+        srcToUse = new URLInputSource(tmpURL);
+    }
+
+    catch(const MalformedURLException&)
+    {
+        srcToUse = new LocalFileInputSource(systemId);
+    }
+
+    catch(...)
+    {
+        // Just rethrow this, since its not our problem
+        throw;
+    }
+
+    Janitor<InputSource> janSrc(srcToUse);
+    scanDocument(*srcToUse, reuseValidator);
+}
+
+void XMLScanner::scanDocument(  const   char* const systemId
+                                , const bool        reuseValidator)
+{
+    // We just delegate this to the XMLCh version after transcoding
+    XMLCh* tmpBuf = XMLString::transcode(systemId);
+    ArrayJanitor<XMLCh> janBuf(tmpBuf);
+    scanDocument(tmpBuf, reuseValidator);
+}
+
+
 void XMLScanner::scanDocument(const InputSource& src, const bool reuseValidator)
 {
     //
@@ -359,6 +412,45 @@ void XMLScanner::scanDocument(const InputSource& src, const bool reuseValidator)
 //  returns false, then the scan of the prolog failed and the token is not
 //  going to work on subsequent scanNext() calls.
 //
+bool XMLScanner::scanFirst( const   XMLCh* const    systemId
+                            ,       XMLPScanToken&  toFill
+                            , const bool            reuseValidator)
+{
+    //
+    //  First we try to parse it as a URL. If that fails, we assume its
+    //  a file and try it that way.
+    //
+    InputSource* srcToUse = 0;
+    try
+    {
+        srcToUse = new URLInputSource(XMLUni::fgZeroLenString, systemId);
+    }
+
+    catch(const MalformedURLException&)
+    {
+        srcToUse = new LocalFileInputSource(systemId);
+    }
+
+    catch(...)
+    {
+        // Just rethrow this, since its not our problem
+        throw;
+    }
+
+    Janitor<InputSource> janSrc(srcToUse);
+    return scanFirst(*srcToUse, toFill, reuseValidator);
+}
+
+bool XMLScanner::scanFirst( const   char* const     systemId
+                            ,       XMLPScanToken&  toFill
+                            , const bool            reuseValidator)
+{
+    // We just delegate this to the XMLCh version after transcoding
+    XMLCh* tmpBuf = XMLString::transcode(systemId);
+    ArrayJanitor<XMLCh> janBuf(tmpBuf);
+    return scanFirst(tmpBuf, toFill, reuseValidator);
+}
+
 bool XMLScanner::scanFirst( const   InputSource&    src
                             ,       XMLPScanToken&  toFill
                             , const bool            reuseValidator)
diff --git a/src/internal/XMLScanner.hpp b/src/internal/XMLScanner.hpp
index eb829e68f223758d316c56ecfe11cf196261e6f8..bb314aff03d7f317c9e224b6b4965f6f06c28a14 100644
--- a/src/internal/XMLScanner.hpp
+++ b/src/internal/XMLScanner.hpp
@@ -56,8 +56,12 @@
 
 /**
  * $Log$
- * Revision 1.1  1999/11/09 01:08:23  twl
- * Initial revision
+ * Revision 1.2  2000/01/12 00:15:04  roddey
+ * Changes to deal with multiply nested, relative pathed, entities and to deal
+ * with the new URL class changes.
+ *
+ * Revision 1.1.1.1  1999/11/09 01:08:23  twl
+ * Initial checkin
  *
  * Revision 1.4  1999/11/08 20:44:52  rahul
  * Swat for adding in Product name and CVS comment log variable.
@@ -232,12 +236,36 @@ public :
         const   InputSource&    src
         , const bool            reuseValidator = false
     );
+    void scanDocument
+    (
+        const   XMLCh* const    systemId
+        , const bool            reuseValidator = false
+    );
+    void scanDocument
+    (
+        const   char* const     systemId
+        , const bool            reuseValidator = false
+    );
+
     bool scanFirst
     (
         const   InputSource&    src
         ,       XMLPScanToken&  toFill
         , const bool            reuseValidator = false
     );
+    bool scanFirst
+    (
+        const   XMLCh* const    systemId
+        ,       XMLPScanToken&  toFill
+        , const bool            reuseValidator = false
+    );
+    bool scanFirst
+    (
+        const   char* const     systemId
+        ,       XMLPScanToken&  toFill
+        , const bool            reuseValidator = false
+    );
+
     bool scanNext(XMLPScanToken& toFill);
 
 
diff --git a/src/internal/XMLScanner2.cpp b/src/internal/XMLScanner2.cpp
index a92136a4beeb74660a6e75eb295bf839e9346507..b2926d8a43e11c7d97901ff08ab9b6fd07a54acf 100644
--- a/src/internal/XMLScanner2.cpp
+++ b/src/internal/XMLScanner2.cpp
@@ -56,6 +56,10 @@
 
 /**
  * $Log$
+ * Revision 1.4  2000/01/12 00:15:04  roddey
+ * Changes to deal with multiply nested, relative pathed, entities and to deal
+ * with the new URL class changes.
+ *
  * Revision 1.3  1999/12/18 00:20:24  roddey
  * Fixed a small reported memory leak
  *
@@ -104,7 +108,7 @@
 #include <framework/XMLValidator.hpp>
 #include <internal/XMLScanner.hpp>
 #include <internal/EndOfEntityException.hpp>
-#include <internal/URLInputSource.hpp>
+
 
 
 // ---------------------------------------------------------------------------
@@ -752,41 +756,6 @@ void XMLScanner::scanReset(const InputSource& src)
 
     // Push this read onto the reader manager
     fReaderMgr.pushReader(newReader, 0);
-
-    //
-    //  We know that the file is legal now, so lets get the base directory
-    //  off of it and store it. If there is no directory component on the
-    //  path, then this returns a null.
-    //
-    //  We have to assume it could be a URL so we create a temporary URL
-    //  and ask it for the path part of itself. That will insure that if its
-    //  relative we really see it as a relative path.
-    //
-    URL tmpURL;
-
-    try
-    {
-        tmpURL.setURL(src.getSystemId());
-
-        //
-        //  Its a valid URL so its assumed to be fully qualified. Get the
-        //  base part of the path part of the URL.
-        //
-        XMLCh* basePath = XMLPlatformUtils::getBasePath(tmpURL.getPath());
-        ArrayJanitor<XMLCh> pathJan(basePath);
-        fReaderMgr.setBasePath(basePath);
-    }
-
-    catch(const MalformedURLException&)
-    {
-        //
-        //  Its not a URL, so assume its just a plain file path and could
-        //  be partial, so get the complete path.
-        //
-        XMLCh* basePath = XMLPlatformUtils::getBasePath(src.getSystemId());
-        ArrayJanitor<XMLCh> pathJan(basePath);
-        fReaderMgr.setBasePath(basePath);
-    }
 }
 
 
diff --git a/src/parsers/DOMParser.cpp b/src/parsers/DOMParser.cpp
index 669ec83c3f16859028fb32cb7bf0aebc1869e626..758005573a8ffdfca1b6dbe81c18f959c657c9d3 100644
--- a/src/parsers/DOMParser.cpp
+++ b/src/parsers/DOMParser.cpp
@@ -61,6 +61,10 @@
  *  are created and added to the DOM tree.
  *
  * $Log$
+ * Revision 1.3  2000/01/12 00:15:22  roddey
+ * Changes to deal with multiply nested, relative pathed, entities and to deal
+ * with the new URL class changes.
+ *
  * Revision 1.2  2000/01/05 01:16:11  andyh
  * DOM Level 2 core, namespace support added.
  *
@@ -82,7 +86,6 @@
 #include <sax/SAXParseException.hpp>
 #include <framework/XMLNotationDecl.hpp>
 #include <util/IOException.hpp>
-#include <internal/URLInputSource.hpp>
 #include <internal/XMLScanner.hpp>
 #include <validators/DTD/DTDValidator.hpp>
 #include <parsers/DOMParser.hpp>
@@ -210,29 +213,51 @@ void DOMParser::parse(const InputSource& source, const bool reuseValidator)
         fParseInProgress = false;
     }
 
-    catch(const SAXException&)
+    catch(...)
     {
         fParseInProgress = false;
         throw;
     }
+}
+
+void DOMParser::parse(const XMLCh* const systemId, const bool reuseValidator)
+{
+    // Avoid multiple entrance
+    if (fParseInProgress)
+        ThrowXML(IOException, XML4CExcepts::Gen_ParseInProgress);
 
-    catch(const XMLException&)
+    try
+    { 
+        fParseInProgress = true;
+        fScanner->scanDocument(systemId, reuseValidator);
+        fParseInProgress = false;
+    }
+
+    catch(...)
     {
         fParseInProgress = false;
         throw;
     }
 }
 
-void DOMParser::parse(const XMLCh* const systemId, const bool reuseValidator)
-{
-    // Just call the URL input source version
-    parse(URLInputSource(systemId), reuseValidator);
-}
-
 void DOMParser::parse(const char* const systemId, const bool reuseValidator)
 {
-    // Just call the URL input source version
-    parse(URLInputSource(systemId), reuseValidator);
+    // Avoid multiple entrance
+    if (fParseInProgress)
+        ThrowXML(IOException, XML4CExcepts::Gen_ParseInProgress);
+
+    try
+    { 
+        fParseInProgress = true;
+        fScanner->scanDocument(systemId, reuseValidator);
+        fParseInProgress = false;
+    }
+
+    catch(...)
+    {
+        fParseInProgress = false;
+        throw;
+    }
 }
 
 
@@ -244,16 +269,28 @@ bool DOMParser::parseFirst( const   XMLCh* const    systemId
                             ,       XMLPScanToken&  toFill
                             , const bool            reuseValidator)
 {
-    // Call the other version with a URL input source
-    return parseFirst(URLInputSource(systemId), toFill, reuseValidator);
+    //
+    //  Avoid multiple entrance. We cannot enter here while a regular parse
+    //  is in progress.
+    //
+    if (fParseInProgress)
+        ThrowXML(IOException, XML4CExcepts::Gen_ParseInProgress);
+
+    return fScanner->scanFirst(systemId, toFill, reuseValidator);
 }
 
 bool DOMParser::parseFirst( const   char* const         systemId
                             ,       XMLPScanToken&      toFill
                             , const bool                reuseValidator)
 {
-    // Call the other version with a URL input source
-    return parseFirst(URLInputSource(systemId), toFill, reuseValidator);
+    //
+    //  Avoid multiple entrance. We cannot enter here while a regular parse
+    //  is in progress.
+    //
+    if (fParseInProgress)
+        ThrowXML(IOException, XML4CExcepts::Gen_ParseInProgress);
+
+    return fScanner->scanFirst(systemId, toFill, reuseValidator);
 }
 
 bool DOMParser::parseFirst( const   InputSource&    source
diff --git a/src/parsers/SAXParser.cpp b/src/parsers/SAXParser.cpp
index c413d384276fb5756c559bdb411065fb9bad01d2..4b8720310e928c8a37b3876e43cc16b68617dc35 100644
--- a/src/parsers/SAXParser.cpp
+++ b/src/parsers/SAXParser.cpp
@@ -56,6 +56,10 @@
 
 /**
  * $Log$
+ * Revision 1.3  2000/01/12 00:15:22  roddey
+ * Changes to deal with multiply nested, relative pathed, entities and to deal
+ * with the new URL class changes.
+ *
  * Revision 1.2  1999/12/15 19:57:48  roddey
  * Got rid of redundant 'const' on boolean return value. Some compilers choke
  * on this and its useless.
@@ -78,7 +82,6 @@
 #include <sax/ErrorHandler.hpp>
 #include <sax/EntityResolver.hpp>
 #include <sax/SAXParseException.hpp>
-#include <internal/URLInputSource.hpp>
 #include <internal/XMLScanner.hpp>
 #include <parsers/SAXParser.hpp>
 #include <validators/DTD/DTDValidator.hpp>
@@ -258,19 +261,13 @@ void SAXParser::parse(const InputSource& source, const bool reuseValidator)
         ThrowXML(IOException, XML4CExcepts::Gen_ParseInProgress);
 
     try
-    { 
+    {
         fParseInProgress = true;
         fScanner->scanDocument(source, reuseValidator);
         fParseInProgress = false;
     }
 
-    catch (const SAXException&)
-    {
-        fParseInProgress = false;
-        throw;
-    }
-
-    catch (const XMLException&)
+    catch (...)
     {
         fParseInProgress = false;
         throw;
@@ -279,18 +276,42 @@ void SAXParser::parse(const InputSource& source, const bool reuseValidator)
 
 void SAXParser::parse(const XMLCh* const systemId, const bool reuseValidator)
 {
+    // Avoid multiple entrance
     if (fParseInProgress)
         ThrowXML(IOException, XML4CExcepts::Gen_ParseInProgress);
 
-    parse(URLInputSource(systemId), reuseValidator);
+    try
+    {
+        fParseInProgress = true;
+        fScanner->scanDocument(systemId, reuseValidator);
+        fParseInProgress = false;
+    }
+
+    catch (...)
+    {
+        fParseInProgress = false;
+        throw;
+    }
 }
 
 void SAXParser::parse(const char* const systemId, const bool reuseValidator)
 {
+    // Avoid multiple entrance
     if (fParseInProgress)
         ThrowXML(IOException, XML4CExcepts::Gen_ParseInProgress);
 
-    parse(URLInputSource(systemId), reuseValidator);
+    try
+    {
+        fParseInProgress = true;
+        fScanner->scanDocument(systemId, reuseValidator);
+        fParseInProgress = false;
+    }
+
+    catch (...)
+    {
+        fParseInProgress = false;
+        throw;
+    }
 }
 
 void SAXParser::setDocumentHandler(DocumentHandler* const handler)
@@ -365,16 +386,28 @@ bool SAXParser::parseFirst( const   XMLCh* const    systemId
                             ,       XMLPScanToken&  toFill
                             , const bool            reuseValidator)
 {
-    // Call the other version with a URL input source
-    return parseFirst(URLInputSource(systemId), toFill, reuseValidator);
+    //
+    //  Avoid multiple entrance. We cannot enter here while a regular parse
+    //  is in progress.
+    //
+    if (fParseInProgress)
+        ThrowXML(IOException, XML4CExcepts::Gen_ParseInProgress);
+
+    return fScanner->scanFirst(systemId, toFill, reuseValidator);
 }
 
 bool SAXParser::parseFirst( const   char* const     systemId
                             ,       XMLPScanToken&  toFill
                             , const bool            reuseValidator)
 {
-    // Call the other version with a URL input source
-    return parseFirst(URLInputSource(systemId), toFill, reuseValidator);
+    //
+    //  Avoid multiple entrance. We cannot enter here while a regular parse
+    //  is in progress.
+    //
+    if (fParseInProgress)
+        ThrowXML(IOException, XML4CExcepts::Gen_ParseInProgress);
+
+    return fScanner->scanFirst(systemId, toFill, reuseValidator);
 }
 
 bool SAXParser::parseFirst( const   InputSource&    source
diff --git a/src/sax/InputSource.cpp b/src/sax/InputSource.cpp
index 7821f74fe8b1014d518d5f892d48e7f5896afb02..f9db9f8e2dc8420d5ca0872ac56a191677cbe57d 100644
--- a/src/sax/InputSource.cpp
+++ b/src/sax/InputSource.cpp
@@ -56,8 +56,12 @@
 
 /**
  * $Log$
- * Revision 1.1  1999/11/09 01:07:45  twl
- * Initial revision
+ * Revision 1.2  2000/01/12 00:15:39  roddey
+ * Changes to deal with multiply nested, relative pathed, entities and to deal
+ * with the new URL class changes.
+ *
+ * Revision 1.1.1.1  1999/11/09 01:07:45  twl
+ * Initial checkin
  *
  * Revision 1.2  1999/11/08 20:45:01  rahul
  * Swat for adding in Product name and CVS comment log variable.
@@ -74,8 +78,52 @@
 
 
 // ---------------------------------------------------------------------------
-//  InputSource: Constructors and Destructor
+//  InputSource: Destructor
+// ---------------------------------------------------------------------------
+InputSource::~InputSource()
+{
+    delete [] fEncoding;
+    delete [] fPublicId;
+    delete [] fSystemId;
+}
+
+
 // ---------------------------------------------------------------------------
+//  InputSource: Setter methods
+// ---------------------------------------------------------------------------
+void InputSource::setEncoding(const XMLCh* const encodingStr)
+{
+    delete [] fEncoding;
+    fEncoding = XMLString::replicate(encodingStr);
+}
+
+
+void InputSource::setPublicId(const XMLCh* const publicId)
+{
+    delete [] fPublicId;
+    fPublicId = XMLString::replicate(publicId);
+}
+
+
+void InputSource::setSystemId(const XMLCh* const systemId)
+{
+    delete [] fSystemId;
+    fSystemId = XMLString::replicate(systemId);
+}
+
+
+
+// ---------------------------------------------------------------------------
+//  InputSource: Hidden Constructors
+// ---------------------------------------------------------------------------
+InputSource::InputSource() :
+
+    fEncoding(0)
+    , fPublicId(0)
+    , fSystemId(0)
+{
+}
+
 InputSource::InputSource(const XMLCh* const systemId) :
 
     fEncoding(0)
@@ -109,34 +157,3 @@ InputSource::InputSource(const  char* const systemId
     , fSystemId(XMLString::transcode(systemId))
 {
 }
-
-InputSource::~InputSource()
-{
-    delete [] fEncoding;
-    delete [] fPublicId;
-    delete [] fSystemId;
-}
-
-
-// ---------------------------------------------------------------------------
-//  InputSource: Setter methods
-// ---------------------------------------------------------------------------
-void InputSource::setEncoding(const XMLCh* const encodingStr)
-{
-    delete [] fEncoding;
-    fEncoding = XMLString::replicate(encodingStr);
-}
-
-
-void InputSource::setPublicId(const XMLCh* const publicId)
-{
-    delete [] fPublicId;
-    fPublicId = XMLString::replicate(publicId);
-}
-
-
-void InputSource::setSystemId(const XMLCh* const systemId)
-{
-    delete [] fSystemId;
-    fSystemId = XMLString::replicate(systemId);
-}
diff --git a/src/sax/InputSource.hpp b/src/sax/InputSource.hpp
index 1f504490b0a0d3beb3c4d9e7527fcea2dba0c5a4..4f3cc779872638cf8768db543c61dc370db5fb5d 100644
--- a/src/sax/InputSource.hpp
+++ b/src/sax/InputSource.hpp
@@ -56,8 +56,12 @@
 
 /**
  * $Log$
- * Revision 1.1  1999/11/09 01:07:46  twl
- * Initial revision
+ * Revision 1.2  2000/01/12 00:15:39  roddey
+ * Changes to deal with multiply nested, relative pathed, entities and to deal
+ * with the new URL class changes.
+ *
+ * Revision 1.1.1.1  1999/11/09 01:07:46  twl
+ * Initial checkin
  *
  * Revision 1.2  1999/11/08 20:45:01  rahul
  * Swat for adding in Product name and CVS comment log variable.
@@ -101,8 +105,12 @@ class BinInputStream;
   * around beyond the call.</p>
   *
   * $Log$
-  * Revision 1.1  1999/11/09 01:07:46  twl
-  * Initial revision
+  * Revision 1.2  2000/01/12 00:15:39  roddey
+  * Changes to deal with multiply nested, relative pathed, entities and to deal
+  * with the new URL class changes.
+  *
+  * Revision 1.1.1.1  1999/11/09 01:07:46  twl
+  * Initial checkin
   *
   * Revision 1.2  1999/11/08 20:45:01  rahul
   * Swat for adding in Product name and CVS comment log variable.
@@ -230,6 +238,7 @@ protected :
     // -----------------------------------------------------------------------
     //  Hidden constructors
     // -----------------------------------------------------------------------
+    InputSource();
     InputSource(const XMLCh* const systemId);
     InputSource
     (
diff --git a/src/util/PlatformUtils.hpp b/src/util/PlatformUtils.hpp
index 99977a5c5b6ad9c7ebca7d256b1a26fc1ba24428..46be8c764237b675c5a66282d98c7da2ce689923 100644
--- a/src/util/PlatformUtils.hpp
+++ b/src/util/PlatformUtils.hpp
@@ -56,8 +56,12 @@
 
 /**
  * $Log$
- * Revision 1.1  1999/11/09 01:04:55  twl
- * Initial revision
+ * Revision 1.2  2000/01/12 00:16:22  roddey
+ * Changes to deal with multiply nested, relative pathed, entities and to deal
+ * with the new URL class changes.
+ *
+ * Revision 1.1.1.1  1999/11/09 01:04:55  twl
+ * Initial checkin
  *
  * Revision 1.3  1999/11/08 20:45:11  rahul
  * Swat for adding in Product name and CVS comment log variable.
@@ -185,8 +189,13 @@ public :
     // -----------------------------------------------------------------------
     //  File system methods
     // -----------------------------------------------------------------------
-    static XMLCh* getBasePath(const XMLCh* const srcPath);
+    static XMLCh* getFullPath(const XMLCh* const srcPath);
     static bool isRelative(const XMLCh* const toCheck);
+    static XMLCh* weavePaths
+    (
+        const   XMLCh* const    basePath
+        , const XMLCh* const    relativePath
+    );
 
 
     // -----------------------------------------------------------------------
diff --git a/src/util/Platforms/Win32/Win32PlatformUtils.cpp b/src/util/Platforms/Win32/Win32PlatformUtils.cpp
index b4afa1fcf3f00af81bac090e8ad798af02a28063..0dcdbefd40fdef472ac75668ca2d324728adbf8f 100644
--- a/src/util/Platforms/Win32/Win32PlatformUtils.cpp
+++ b/src/util/Platforms/Win32/Win32PlatformUtils.cpp
@@ -56,6 +56,10 @@
 
 /**
  * $Log$
+ * Revision 1.3  2000/01/12 00:16:47  roddey
+ * Changes to deal with multiply nested, relative pathed, entities and to deal
+ * with the new URL class changes.
+ *
  * Revision 1.2  1999/11/22 20:41:26  abagchi
  * Changed 'intlFiles/Locales' to 'icu/data'
  *
@@ -450,7 +454,7 @@ void XMLPlatformUtils::writeToStdOut(const char* const toWrite)
 // ---------------------------------------------------------------------------
 //  XMLPlatformUtils: File system methods
 // ---------------------------------------------------------------------------
-XMLCh* XMLPlatformUtils::getBasePath(const XMLCh* const srcPath)
+XMLCh* XMLPlatformUtils::getFullPath(const XMLCh* const srcPath)
 {
     //
     //  NOTE: THe path provided has always already been opened successfully,
@@ -470,10 +474,6 @@ XMLCh* XMLPlatformUtils::getBasePath(const XMLCh* const srcPath)
         if (!::GetFullPathNameW(srcPath, bufSize, tmpPath, &namePart))
             return 0;
 
-        // Cap it off at the name part, leaving just the full path
-        if (namePart)
-            *namePart = 0;
-
         // Return a copy of the path
         return XMLString::replicate(tmpPath);
     }
@@ -491,10 +491,6 @@ XMLCh* XMLPlatformUtils::getBasePath(const XMLCh* const srcPath)
         if (!::GetFullPathNameA(tmpSrcPath, bufSize, tmpPath, &namePart))
             return 0;
 
-        // Cap it off at the name part, leaving just the full path
-        if (namePart)
-            *namePart = 0;
-
         // Return a transcoded copy of the path
         return XMLString::transcode(tmpPath);
     }
@@ -533,6 +529,114 @@ bool XMLPlatformUtils::isRelative(const XMLCh* const toCheck)
 }
 
 
+XMLCh* XMLPlatformUtils::weavePaths(const   XMLCh* const    basePath
+                                    , const XMLCh* const    relativePath)
+
+{
+    // Create a buffer as large as both parts and empty it
+    XMLCh* tmpBuf = new XMLCh[XMLString::stringLen(basePath)
+                              + XMLString::stringLen(relativePath)
+                              + 2];
+    *tmpBuf = 0;
+
+    //
+    //  If we have no base path, then just take the relative path as
+    //  is.
+    //
+    if (!basePath)
+    {
+        XMLString::copyString(tmpBuf, relativePath);
+        return tmpBuf;
+    }
+
+    if (!*basePath)
+    {
+        XMLString::copyString(tmpBuf, relativePath);
+        return tmpBuf;
+    }
+
+    const XMLCh* basePtr = basePath + (XMLString::stringLen(basePath) - 1);
+    if ((*basePtr != chForwardSlash)
+    &&  (*basePtr != chBackSlash))
+    {
+        while ((basePtr >= basePath)
+        &&     ((*basePtr != chForwardSlash) && (*basePtr != chBackSlash)))
+        {
+            basePtr--;
+        }
+    }
+
+    // There is no relevant base path, so just take the relative part
+    if (basePtr < basePath)
+    {
+        XMLString::copyString(tmpBuf, relativePath);
+        return tmpBuf;
+    }
+
+    // After this, make sure the buffer gets handled if we exit early
+    ArrayJanitor<XMLCh> janBuf(tmpBuf);
+
+    //
+    //  We have some path part, so we need to check to see if we ahve to
+    //  weave any of the parts together.
+    //
+    const XMLCh* pathPtr = relativePath;
+    while (true)
+    {
+        // If it does not start with some period, then we are done
+        if (*pathPtr != chPeriod)
+            break;
+
+        unsigned int periodCount = 1;
+        pathPtr++;
+        if (*pathPtr == chPeriod)
+        {
+            pathPtr++;
+            periodCount++;
+        }
+
+        // Has to be followed by a \ or / or the null to mean anything
+        if ((*pathPtr != chForwardSlash) && (*pathPtr != chBackSlash)
+        &&  *pathPtr)
+        {
+            break;
+        }
+        if (*pathPtr)
+            pathPtr++;
+
+        // If its one period, just eat it, else move backwards in the base
+        if (periodCount == 2)
+        {
+            basePtr--;
+            while ((basePtr >= basePath)
+            &&     ((*basePtr != chForwardSlash) && (*basePtr != chBackSlash)))
+            {
+                basePtr--;
+            }
+
+            if (basePtr < basePath)
+            {
+                // The base cannot provide enough levels, so its in error
+                // <TBD>
+            }
+        }
+    }
+
+    // Copy the base part up to the base pointer
+    XMLCh* bufPtr = tmpBuf;
+    const XMLCh* tmpPtr = basePath;
+    while (tmpPtr <= basePtr)
+        *bufPtr++ = *tmpPtr++;
+
+    // And then copy on the rest of our path
+    XMLString::copyString(bufPtr, pathPtr);
+
+    // Orphan the buffer and return it
+    janBuf.orphan();
+    return tmpBuf;
+}
+
+
 
 // ---------------------------------------------------------------------------
 //  XMLPlatformUtils: Timing Methods
diff --git a/src/util/URL.cpp b/src/util/URL.cpp
index 90a2bbd17c3170abc3e8c0f9d7432c47ad065fc7..00dcfdfaba79bb1c2adaa3982c02b0ee8074bd4f 100644
--- a/src/util/URL.cpp
+++ b/src/util/URL.cpp
@@ -56,8 +56,12 @@
 
 /**
  * $Log$
- * Revision 1.1  1999/11/09 01:04:20  twl
- * Initial revision
+ * Revision 1.2  2000/01/12 00:16:22  roddey
+ * Changes to deal with multiply nested, relative pathed, entities and to deal
+ * with the new URL class changes.
+ *
+ * Revision 1.1.1.1  1999/11/09 01:04:20  twl
+ * Initial checkin
  *
  * Revision 1.3  1999/11/08 20:45:16  rahul
  * Swat for adding in Product name and CVS comment log variable.
@@ -72,7 +76,9 @@
 #include <util/Janitor.hpp>
 #include <util/PlatformUtils.hpp>
 #include <util/RuntimeException.hpp>
+#include <util/TransService.hpp>
 #include <util/URL.hpp>
+#include <util/XMLNetAccessor.hpp>
 #include <util/XMLString.hpp>
 #include <util/XMLUni.hpp>
 
@@ -89,7 +95,6 @@ struct TypeEntry
 {
     URL::Protocols  protocol;
     const XMLCh*    prefix;
-    bool            supported;
 };
 
 
@@ -112,26 +117,17 @@ struct TypeEntry
 // ---------------------------------------------------------------------------
 static const XMLCh  gFileString[] =
 {
-        chLatin_f, chLatin_i, chLatin_l, chLatin_e, chColon
-    ,   chForwardSlash, chForwardSlash, chNull
+        chLatin_f, chLatin_i, chLatin_l, chLatin_e, chNull
 };
 
 static const XMLCh gFTPString[]  =
 {
-        chLatin_f, chLatin_t, chLatin_p, chColon, chForwardSlash
-    ,   chForwardSlash, chNull
-};
-
-static const XMLCh gGopherString[]  =
-{
-        chLatin_g, chLatin_o, chLatin_p, chLatin_h, chLatin_e
-    ,   chLatin_r, chColon, chForwardSlash, chForwardSlash, chNull
+        chLatin_f, chLatin_t, chLatin_p, chNull
 };
 
 static const XMLCh gHTTPString[] =
 {
-        chLatin_h, chLatin_t, chLatin_t, chLatin_p, chColon
-    ,   chForwardSlash, chForwardSlash, chNull
+        chLatin_h, chLatin_t, chLatin_t, chLatin_p, chNull
 };
 
 static const XMLCh gLocalHostString[] =
@@ -140,60 +136,17 @@ static const XMLCh gLocalHostString[] =
     ,   chLatin_h, chLatin_o, chLatin_s, chLatin_t, chNull
 };
 
-static const XMLCh gMailToString[]  =
-{
-        chLatin_m, chLatin_a, chLatin_i, chLatin_l, chLatin_t
-    ,   chLatin_o, chColon, chForwardSlash, chForwardSlash, chNull
-};
-
-static const XMLCh gNewsString[] =
-{
-        chLatin_n, chLatin_e, chLatin_w, chLatin_s, chColon
-    ,   chForwardSlash, chForwardSlash, chNull
-};
-
-static const XMLCh gNNTPString[] =
-{
-        chLatin_n, chLatin_n, chLatin_t, chLatin_p, chColon
-    ,   chForwardSlash, chForwardSlash, chNull
-};
-
-static const XMLCh gTelnetString[]  =
-{
-        chLatin_t, chLatin_e, chLatin_l, chLatin_n, chLatin_e
-    ,   chLatin_t, chColon, chForwardSlash, chForwardSlash, chNull
-};
-
-static const XMLCh gWaisString[]  =
-{
-        chLatin_w, chLatin_a, chLatin_i, chLatin_s, chColon
-    ,   chForwardSlash, chForwardSlash, chNull
-};
-
-static const XMLCh gProsperoString[]  =
-{
-        chLatin_p, chLatin_r, chLatin_o, chLatin_s, chLatin_p
-    ,   chLatin_e, chLatin_r, chLatin_o, chColon, chForwardSlash
-    ,   chForwardSlash, chNull
-};
-
 static TypeEntry gTypeList[URL::Protocols_Count] = 
 {
-        { URL::File     , gFileString       , true }
-    ,   { URL::HTTP     , gHTTPString       , false }
-    ,   { URL::FTP      , gFTPString        , false }
-    ,   { URL::Gopher   , gGopherString     , false }
-    ,   { URL::MailTo   , gMailToString     , false }
-    ,   { URL::News     , gNewsString       , false }
-    ,   { URL::NNTP     , gNNTPString       , false }
-    ,   { URL::Telnet   , gTelnetString     , false }
-    ,   { URL::Wais     , gWaisString       , false }
-    ,   { URL::Prospero , gProsperoString   , false }
+        { URL::File     , gFileString }
+    ,   { URL::HTTP     , gHTTPString }
+    ,   { URL::FTP      , gFTPString  }
 };
+static const unsigned int gTypeListSize = sizeof(gTypeList) / sizeof(gTypeList[0]);
 
 // !!! Keep these up to date with list above!
-static const unsigned int gMaxProtoLen = 11;
-static const unsigned int gMaxColonPos = gMaxProtoLen - 3;
+static const unsigned int gMaxProtoLen = 4;
+
 
 
 // ---------------------------------------------------------------------------
@@ -223,24 +176,141 @@ static unsigned int xlatHexDigit(const XMLCh toXlat)
 
 
 
+// ---------------------------------------------------------------------------
+//  URL: Public, static methods
+// ---------------------------------------------------------------------------
+URL::Protocols URL::lookupByName(const XMLCh* const protoName)
+{
+    for (unsigned int index = 0; index < gTypeListSize; index++)
+    {
+        if (!XMLString::compareIString(gTypeList[index].prefix, protoName))
+            return gTypeList[index].protocol;
+    }
+    return URL::Unknown;
+}
+
+
+
 // ---------------------------------------------------------------------------
 //  URL: Constructors and Destructor
 // ---------------------------------------------------------------------------
 URL::URL() :
 
-    fFullURL(0)
+    fFragment(0)
     , fHost(0)
+    , fPassword(0)
     , fPath(0)
-    , fProtocol(URL::File)
+    , fProtocol(URL::Unknown)
+    , fQuery(0)
+    , fUser(0)
+    , fURLText(0)
 {
 }
 
+URL::URL(const  XMLCh* const    baseURL
+        , const XMLCh* const    relativeURL) :
+
+    fFragment(0)
+    , fHost(0)
+    , fPassword(0)
+    , fPath(0)
+    , fProtocol(URL::Unknown)
+    , fQuery(0)
+    , fUser(0)
+    , fURLText(0)
+{
+    setURL(baseURL, relativeURL);
+}
+
+URL::URL(const  XMLCh* const    baseURL
+        , const char* const     relativeURL) :
+
+    fFragment(0)
+    , fHost(0)
+    , fPassword(0)
+    , fPath(0)
+    , fProtocol(URL::Unknown)
+    , fQuery(0)
+    , fUser(0)
+    , fURLText(0)
+{
+    XMLCh* tmpRel = XMLString::transcode(relativeURL);
+    ArrayJanitor<XMLCh> janRel(tmpRel);
+    setURL(baseURL, tmpRel);
+}
+
+URL::URL(const  URL&            baseURL
+        , const XMLCh* const    relativeURL) :
+
+    fFragment(0)
+    , fHost(0)
+    , fPassword(0)
+    , fPath(0)
+    , fProtocol(URL::Unknown)
+    , fQuery(0)
+    , fUser(0)
+    , fURLText(0)
+{
+    setURL(baseURL, relativeURL);
+}
+
+URL::URL(const  URL&        baseURL
+        , const char* const relativeURL) :
+
+    fFragment(0)
+    , fHost(0)
+    , fPassword(0)
+    , fPath(0)
+    , fProtocol(URL::Unknown)
+    , fQuery(0)
+    , fUser(0)
+    , fURLText(0)
+{
+    XMLCh* tmpRel = XMLString::transcode(relativeURL);
+    ArrayJanitor<XMLCh> janRel(tmpRel);
+    setURL(baseURL, tmpRel);
+}
+
+URL::URL(const XMLCh* const urlText) :
+
+    fFragment(0)
+    , fHost(0)
+    , fPassword(0)
+    , fPath(0)
+    , fProtocol(URL::Unknown)
+    , fQuery(0)
+    , fUser(0)
+    , fURLText(0)
+{
+    setURL(urlText);
+}
+
+URL::URL(const char* const urlText) :
+
+    fFragment(0)
+    , fHost(0)
+    , fPassword(0)
+    , fPath(0)
+    , fProtocol(URL::Unknown)
+    , fQuery(0)
+    , fUser(0)
+    , fURLText(0)
+{
+    XMLCh* tmpText = XMLString::transcode(urlText);
+    ArrayJanitor<XMLCh> janRel(tmpText);
+    setURL(tmpText);
+}
+
 URL::URL(const URL& toCopy) :
 
-    fFullURL(XMLString::replicate(toCopy.fFullURL))
+    fFragment(XMLString::replicate(toCopy.fFragment))
     , fHost(XMLString::replicate(toCopy.fHost))
+    , fPassword(XMLString::replicate(toCopy.fPassword))
     , fPath(XMLString::replicate(toCopy.fPath))
     , fProtocol(toCopy.fProtocol)
+    , fQuery(XMLString::replicate(toCopy.fQuery))
+    , fUser(XMLString::replicate(toCopy.fUser))
+    , fURLText(XMLString::replicate(toCopy.fURLText))
 {
 }
 
@@ -262,29 +332,26 @@ URL& URL::operator=(const URL& toAssign)
     cleanup();
 
     // And copy his stuff
-    fFullURL = XMLString::replicate(toAssign.fFullURL);
+    fFragment = XMLString::replicate(toAssign.fFragment);
     fHost = XMLString::replicate(toAssign.fHost);
+    fPassword = XMLString::replicate(toAssign.fPassword);
     fPath = XMLString::replicate(toAssign.fPath);
     fProtocol = toAssign.fProtocol;
+    fQuery = XMLString::replicate(toAssign.fQuery);
+    fUser = XMLString::replicate(toAssign.fUser);
+    fURLText = XMLString::replicate(toAssign.fURLText);
 
     return *this;
 }
 
 bool URL::operator==(const URL& toCompare) const
 {
-    // Test the obvious one first
-    if (fProtocol != toCompare.fProtocol)
-        return false;
-
     //
-    //  Oh well, we have to test the components. Don't test the original
-    //  URLs, because normalization might have occured that would have made
-    //  them equal even though actual text of the full URLs is not.
+    //  Compare the two complete URLs (which have been processed the same
+    //  way so they should now be the same even if they came in via different
+    //  relative parts.
     //
-    if (XMLString::compareString(fPath, toCompare.fPath))
-        return false;
-
-    if (XMLString::compareString(fHost, toCompare.fHost))
+    if (XMLString::compareString(getURLText(), toCompare.getURLText()))
         return false;
 
     return true;
@@ -295,8 +362,12 @@ bool URL::operator==(const URL& toCompare) const
 // ---------------------------------------------------------------------------
 //  URL: Getter methods
 // ---------------------------------------------------------------------------
-const XMLCh* URL::getProtocol() const
+const XMLCh* URL::getProtocolName() const
 {
+    // Check to see if its ever been set
+    if (fProtocol == Unknown)
+        ThrowXML(MalformedURLException, XML4CExcepts::URL_NoProtocolPresent);
+
     return gTypeList[fProtocol].prefix;
 }
 
@@ -306,10 +377,14 @@ const XMLCh* URL::getProtocol() const
 // ---------------------------------------------------------------------------
 void URL::setURL(const XMLCh* const urlText)
 {
-    fFullURL = XMLString::replicate(urlText);
+    //
+    //  Try to parse the URL. If this fails, we just give up, cleanup and
+    //  rethrow out of here.
+    //
+    cleanup();
     try
     {
-        parse();
+        parse(urlText);
     }
 
     catch(...)
@@ -319,13 +394,27 @@ void URL::setURL(const XMLCh* const urlText)
     }
 }
 
-void URL::setURL(const char* const urlText)
+void URL::setURL(const  XMLCh* const    baseURL
+                , const XMLCh* const    relativeURL)
 {
-    // Transcode the passed string to Unicode
-    fFullURL = XMLString::transcode(urlText);
+    cleanup();
     try
     {
-        parse();
+        // Parse our URL string
+        parse(relativeURL);
+
+        //
+        //  If its relative and the base is non-null and non-empty, then
+        //  parse the base URL string and conglomerate them.
+        //
+        if (isRelative() && baseURL)
+        {
+            if (*baseURL)
+            {
+                URL basePart(baseURL);
+                conglomerateWithBase(basePart);
+            }
+        }
     }
 
     catch(...)
@@ -335,293 +424,655 @@ void URL::setURL(const char* const urlText)
     }
 }
 
+void URL::setURL(const  URL&            baseURL
+                , const XMLCh* const    relativeURL)
+{
+    cleanup();
+    try
+    {
+        // Parse our URL string
+        parse(relativeURL);
+
+        // If its relative, then conglomerate with the base URL
+        if (isRelative())
+            conglomerateWithBase(baseURL);
+    }
+
+    catch(...)
+    {
+        cleanup();
+        throw;
+    }
+}
 
 
 // ---------------------------------------------------------------------------
 //  URL: Miscellaneous methods
 // ---------------------------------------------------------------------------
+bool URL::isRelative() const
+{
+    // If no protocol then relative
+    if (fProtocol == Unknown)
+        return true;
+
+    // If no path, or the path is not absolute, then relative
+    if (!fPath)
+        return true;
+
+    if (*fPath != chForwardSlash)
+        return true;
+
+    return false;
+}
+
+
 BinInputStream* URL::makeNewStream() const
 {
-    switch(fProtocol)
+    //
+    //  If its a local host, then we short circuit it and use our own file
+    //  stream support. Otherwise, we just let it fall through and let the
+    //  installed network access object provide a stream.
+    //
+    if (fProtocol == URL::File)
     {
-        case URL::File  :
+        if (!fHost || !XMLString::compareIString(fHost, L"localhost"))
         {
-            BinFileInputStream* retStrm = new BinFileInputStream(getPath());
+            //
+            //  We have to play a little trick here. If its really a Windows
+            //  style fully qualified path, we have to toss the leading /
+            //  character.
+            //
+            const XMLCh* realPath = fPath;
+            if (*fPath == chForwardSlash)
+            {
+                if (XMLString::stringLen(fPath) > 3)
+                {
+                    if (*(fPath + 2) == chColon)
+                    {
+                        const XMLCh chDrive = *(fPath + 1);
+                        if (((chDrive >= chLatin_A) && (chDrive <= chLatin_Z))
+                        ||  ((chDrive >= chLatin_a) && (chDrive <= chLatin_z)))
+                        {
+                            realPath = fPath + 3;
+                        }
+                    }
+                }
+            }
+
+            BinFileInputStream* retStrm = new BinFileInputStream(realPath);
             if (!retStrm->getIsOpen())
             {
                 delete retStrm;
                 return 0;
             }
             return retStrm;
-            break;
         }
-
-        default :
-            ThrowXML(MalformedURLException, XML4CExcepts::URL_UnsupportedProto);
-            break;
     }
-    return 0;
+
+    //
+    //  If we don't have have an installed net accessor object, then we
+    //  have to just throw here.
+    //
+    if (!XMLPlatformUtils::fgNetAccessor)
+        ThrowXML(MalformedURLException, XML4CExcepts::URL_UnsupportedProto);
+
+    // Else ask the net accessor to create the stream
+    return XMLPlatformUtils::fgNetAccessor->makeNew(*this);
+}
+
+void URL::makeRelativeTo(const XMLCh* const baseURLText)
+{
+    // If this one is not relative, don't bother
+    if (!isRelative())
+        return;
+
+    URL baseURL(baseURLText);
+    conglomerateWithBase(baseURL);
 }
 
+void URL::makeRelativeTo(const URL& baseURL)
+{
+    // If this one is not relative, don't bother
+    if (!isRelative())
+        return;
+    conglomerateWithBase(baseURL);
+}
+
+
+
 
 // ---------------------------------------------------------------------------
 //  URL: Private helper methods
 // ---------------------------------------------------------------------------
 
+//
+//  This method will take the broken out parts of the URL and build up the
+//  full text. We don't do this unless someone asks us to, since its often
+//  never required.
+//
+void URL::buildFullText()
+{
+    // Calculate the worst case size of the buffer required
+    unsigned int bufSize = gMaxProtoLen + 1
+                           + XMLString::stringLen(fFragment) + 1
+                           + XMLString::stringLen(fHost) + 2
+                           + XMLString::stringLen(fPassword) + 1
+                           + XMLString::stringLen(fPath)
+                           + XMLString::stringLen(fQuery) + 1
+                           + XMLString::stringLen(fUser) + 1
+                           + 16;
+
+    // Clean up the existing buffer and allocate another
+    delete [] fURLText;
+    fURLText = new XMLCh[bufSize];
+    *fURLText = 0;
+
+    XMLCh* outPtr = fURLText;
+    if (fProtocol != Unknown)
+    {
+        XMLString::catString(fURLText, getProtocolName());
+        outPtr += XMLString::stringLen(fURLText);
+        *outPtr++ = chColon;
+        *outPtr++ = chForwardSlash;
+        *outPtr++ = chForwardSlash;
+    }
+
+    if (fHost)
+    {
+        XMLString::copyString(outPtr, fHost);
+        outPtr += XMLString::stringLen(fHost);
+
+        if (fUser)
+        {
+            *outPtr++ = chAt;
+            XMLString::copyString(outPtr, fUser);
+            outPtr += XMLString::stringLen(fUser);
+
+            if (fPassword)
+            {
+                *outPtr++ = chColon;
+                XMLString::copyString(outPtr, fPassword);
+                outPtr += XMLString::stringLen(fPassword);
+            }
+        }
+    }
+
+    if (fPath)
+    {
+        XMLString::copyString(outPtr, fPath);
+        outPtr += XMLString::stringLen(fPath);
+    }
+
+    if (fQuery)
+    {
+        *outPtr++ = chQuestion;
+        XMLString::copyString(outPtr, fQuery);
+        outPtr += XMLString::stringLen(fQuery);
+    }
+
+    if (fFragment)
+    {
+        *outPtr++ = chPound;
+        XMLString::copyString(outPtr, fFragment);
+        outPtr += XMLString::stringLen(fFragment);
+    }
+}
+
+
 //
 //  Just a central place to handle cleanup, since its done from a number
 //  of different spots.
 //
 void URL::cleanup()
 {
-    delete [] fFullURL;
-    fFullURL = 0;
+    delete [] fFragment;
     delete [] fHost;
-    fHost = 0;
+    delete [] fPassword;
     delete [] fPath;
+    delete [] fQuery;
+    delete [] fUser;
+    delete [] fURLText;
+
+    fFragment = 0;
+    fHost = 0;
+    fPassword = 0;
     fPath = 0;
+    fQuery = 0;
+    fUser = 0;
+    fURLText = 0;
+
+    fProtocol = Unknown;
 }
 
 
-//
-//  This method searches our list of protocols and sees if the passed text
-//  starts with one of them. The prefix is the whole thing up to the second
-//  forward slash. The length of the text is passed so that obvious failures
-//  can be found quickly.
-//
-URL::Protocols URL::findType(unsigned int& curPos) const
+void URL::conglomerateWithBase(const URL& baseURL)
 {
-    XMLCh tmpStr[gMaxProtoLen+1];
+    // The base URL cannot be relative
+    if (baseURL.isRelative())
+    {
+        // <TBD> Add an error for this
+        // ThrowXML(MalformedURLException, XML4CExcepts::URL_BaseWasRelative);
+    }
 
     //
-    //  Remember the current position so we can do exploratory reads from
-    //  the URL. Then look forward for a colon.
+    //  Check a special case. If all we have is a fragment, then we want
+    //  to just take the base host and path, plus our fragment.
     //
-    const unsigned int orgPos = curPos;
-    unsigned int tmpPos = curPos;
-    unsigned int tmpIndex = 0;
-    while (true)
+    if ((fProtocol == Unknown)
+    &&  !fHost
+    &&  !fPath
+    &&  fFragment)
     {
-        // Get another char from the source URL. Indicate end of text is ok
-        const XMLCh nextCh = getNextCh(tmpPos, true);
-
-        // If we hit the end, then no good
-        if (!nextCh)
-            ThrowXML(MalformedURLException, XML4CExcepts::URL_MalformedURL);
-
-        // Store this new character
-        tmpStr[tmpIndex++] = nextCh;
-
-        // If we hit the colon, break out
-        if (nextCh == chColon)
-            break;
-
-        // If we exceed the max colon pos without finding a colon, then no good
-        if (tmpIndex > gMaxColonPos)
-            ThrowXML(MalformedURLException, XML4CExcepts::URL_MalformedURL);
+        fProtocol = baseURL.fProtocol;
+        fHost = XMLString::replicate(baseURL.fHost);
+        fUser = XMLString::replicate(baseURL.fUser);
+        fPassword = XMLString::replicate(baseURL.fPassword);
+        fPath = XMLString::replicate(baseURL.fPath);
+        return;
     }
 
     //
-    //  See if the next two chars are forward slashes, If not, then undo
-    //  our read and return local. Else store them and compare against
-    //  the list. Indicate that end of input is ok here.
+    //  All we have to do is run up through our fields and, for each one
+    //  that we don't have, use the based URL's. Once we hit one field
+    //  that we have, we stop.
     //
-    const bool gotSlashes = (getNextCh(tmpPos, true) == chForwardSlash)
-                            && (getNextCh(tmpPos, true) == chForwardSlash);
-    if (!gotSlashes)
-        ThrowXML(MalformedURLException, XML4CExcepts::URL_MalformedURL);
+    if (fProtocol != Unknown)
+        return;
+    fProtocol = baseURL.fProtocol;
+
+    if (fHost || !baseURL.fHost)
+        return;
+    fHost = XMLString::replicate(baseURL.fHost);
+    if (baseURL.fUser)
+        fUser = XMLString::replicate(baseURL.fUser);
+    if (baseURL.fPassword)
+        fPassword = XMLString::replicate(baseURL.fPassword);
+
+    // If we have a path and its absolute, then we are done
+    const bool hadPath = (fPath != 0);
+    if (hadPath)
+    {
+        if (*fPath == chForwardSlash)
+            return;
+    }
 
-    // Store the slashes in our temp string too
-    tmpStr[tmpIndex++] = chForwardSlash;
-    tmpStr[tmpIndex++] = chForwardSlash;
+    // Its a relative path, so weave them together.
+    if (baseURL.fPath)
+        weavePaths(baseURL.fPath);
 
-    // Update the caller's position and cap off our string
-    curPos = tmpPos;
-    tmpStr[tmpIndex] = chNull;
+    // If we had any original path, then we are done
+    if (hadPath)
+        return;
 
-    //
-    //  Ok, lets see if tmpStr matches any of the prefixes in our list of
-    //  protocols.
-    //
-    for (unsigned int index = 0; index < Protocols_Count; index++)
-    {
-        if (!XMLString::compareString(tmpStr, gTypeList[index].prefix))
-            return gTypeList[index].protocol;
-    }
+    if (fQuery || !baseURL.fQuery)
+        return;
+    fQuery = XMLString::replicate(baseURL.fQuery);
 
-    // Cannot be a supported URL protocol
-    ThrowXML(MalformedURLException, XML4CExcepts::URL_UnsupportedProto);
+    if (fFragment || !baseURL.fFragment)
+        return;
+    fFragment = XMLString::replicate(baseURL.fFragment);
 }
 
 
-//
-//  This method is used during the parse. It gets the next character out of
-//  the source URL (in fFullURL, which is a copy of the original text) and
-//  returns it. It updates the passed position with the new position.
-//
-//  The primary job of this method is to handle escaped characters, by reading
-//  them in and converting them to a Unicode char.
-//
-XMLCh URL::getNextCh(unsigned int& pos, const bool endOk) const
+void URL::parse(const XMLCh* const urlText)
 {
+    // Simplify things by checking for the psycho scenarios first
+    if (!*urlText)
+        ThrowXML(MalformedURLException, XML4CExcepts::URL_NoProtocolPresent);
+
     //
-    //  If we are at the end of the URL, then either return a zero or
-    //  throw if end of URL is not legal here.
+    //  The first thing we will do is to check for a file name, so that
+    //  we don't waste time thinking its a URL. If its in the form x:\
+    //  or x:/ and x is an ASCII letter, then assume that's the deal.
     //
-    if (!fFullURL[pos])
+    if (((*urlText >= chLatin_A) && (*urlText <= chLatin_Z))
+    ||  ((*urlText >= chLatin_a) && (*urlText <= chLatin_z)))
     {
-        if (!endOk)
-            ThrowXML(MalformedURLException, XML4CExcepts::URL_MalformedURL);
-        return chNull;
+        if (*(urlText + 1) == chColon)
+        {
+            if (((*urlText + 2) == chForwardSlash)
+            ||  ((*urlText + 2) == chBackSlash))
+            {
+                ThrowXML(MalformedURLException, XML4CExcepts::URL_NoProtocolPresent);
+            }
+        }
     }
 
+    // Get a copy of the URL that we can modify
+    XMLCh* srcCpy = XMLString::replicate(urlText);
+    ArrayJanitor<XMLCh> janSrcCopy(srcCpy);
+
     //
-    //  See if the current character is a '%'. If so, then we need to
-    //  deal with an escaped character.
+    //  Get a pointer now that we can run up thrown the source as we parse
+    //  bits and pieces out of it.
     //
-    if (fFullURL[pos] == chPercent)
-    {
-        XMLCh escapedChar = 0;
+    XMLCh* srcPtr = srcCpy;
 
-        // There must be at least two more characters
-        if (!fFullURL[pos+1] || !fFullURL[pos+2])
-            ThrowXML(MalformedURLException, XML4CExcepts::URL_MalformedURL);
-
-        // Get them out and test them
-        const XMLCh test1 = fFullURL[pos+1];
-        const XMLCh test2 = fFullURL[pos+2];
+    // Run up past any spaces
+    while (*srcPtr)
+    {
+        if (!XMLPlatformUtils::fgTransService->isSpace(*srcPtr))
+            break;
+    }
 
-        if (!isHexDigit(test1) || !isHexDigit(test2))
-            ThrowXML(MalformedURLException, XML4CExcepts::URL_MalformedURL);
+    // Make sure it wasn't all space
+    if (!*srcPtr)
+        ThrowXML(MalformedURLException, XML4CExcepts::URL_NoProtocolPresent);
 
-        // Convert these to a character
-        escapedChar = XMLCh((xlatHexDigit(test1) << 4) + xlatHexDigit(test2));
+    //
+    //  Ok, the next thing we have to do is to find either a / or : character.
+    //  If the : is first, we assume we have a protocol. If the / is first,
+    //  then we skip to the host processing.
+    //
+    static const XMLCh listOne[]    = { chColon, chForwardSlash, chNull };
+    static const XMLCh listTwo[]    = { chAt, chForwardSlash, chNull };
+    static const XMLCh listThree[]  = { chColon, chForwardSlash, chNull };
+    static const XMLCh listFour[]   = { chForwardSlash, chNull };
+    static const XMLCh listFive[]   = { chPound, chQuestion, chNull };
+    static const XMLCh listSix[]    = { chPound, chNull };
+    XMLCh* ptr1 = XMLString::findAny(srcPtr, listOne);
+    XMLCh* ptr2;
+
+    // If we found a protocol, then deal with it
+    if (ptr1)
+    {
+        if (*ptr1 == chColon)
+        {
+            // Cap the string at the colon
+            *ptr1 = 0;
 
-        // Bump the position up
-        pos += 3;
+            // And try to find it in our list of protocols
+            fProtocol = lookupByName(srcPtr);
 
-        return escapedChar;
+            if (fProtocol == Unknown)
+            {
+                ThrowXML1
+                (
+                    MalformedURLException
+                    , XML4CExcepts::URL_UnsupportedProto1
+                    , srcPtr
+                );
+            }
+        
+            // And move our source pointer up past what we've processed
+            srcPtr = (ptr1 + 1);
+        }
     }
 
-    // Else just return the current char and bump the position
-    return fFullURL[pos++];
-}
-
-
-//
-//  This method is called to parse the text into its components and validate
-//  that the URL is legal. It uses getNextCh() to pull characters out of the
-//  URL.
-//
-void URL::parse()
-{
-    // This is the current position that we track during the parse
-    unsigned int curPos = 0;
-    XMLCh nextCh;
-    const unsigned int bufSize = 2047;
-    XMLCh tmpBuf[bufSize + 1];
-    unsigned int bufIndex;
+    //
+    //  Ok, next we need to see if we have any host part. If the next
+    //  two characters are //, then we need to check, else move on.
+    //
+    if ((*srcPtr == chForwardSlash) && (*(srcPtr + 1) == chForwardSlash))
+    {
+        // Move up past the slashes
+        srcPtr += 2;
+
+        //
+        //  If we aren't at the end of the string, then there has to be a
+        //  host name at this point.
+        //
+        if (*srcPtr)
+        {
+            // Search from here for either a @ or / character
+            ptr1 = XMLString::findAny(srcPtr, listTwo);
+
+            //
+            //  If we found something, then the host is between where
+            //  we are and what we found. Else the host is the rest of
+            //  the content and we are done. If its empty, leave it null.
+            //
+            if (ptr1)
+            {
+                if (ptr1 != srcPtr)
+                {
+                    fHost = new XMLCh[(ptr1 - srcPtr) + 1];
+                    ptr2 = fHost;
+                    while (srcPtr < ptr1)
+                        *ptr2++ = *srcPtr++;
+                    *ptr2 = 0;
+                }
+
+                //
+                //  If we found a @, then we have to parse out a user name
+                //  and optional password.
+                //
+                if (*srcPtr == chAt)
+                {
+                    // Move up past the @ and look for a / or : character
+                    srcPtr++;
+                    ptr1 = XMLString::findAny(srcPtr, listThree);
+
+                    //
+                    //  If we found something, then the user name is between
+                    //  where we are and what we found. Else the user name
+                    //  is the rest of the string and we are done.
+                    //
+                    if (ptr1)
+                    {
+                        fUser = new XMLCh[(ptr1 - srcPtr) + 1];
+                        ptr2 = fUser;
+                        while (srcPtr < ptr1)
+                            *ptr2++ = *srcPtr++;
+                        *ptr2 = 0;
+
+                        //
+                        //  If we found a : character, then everything from
+                        //  after that to the end or a / character is the
+                        //  password.
+                        //
+                        if (*srcPtr == chColon)
+                        {
+                            srcPtr++;
+                            ptr1 = XMLString::findAny(srcPtr, listFour);
+
+                            //
+                            //  If we found one, then the password is everything
+                            //  from where we are to there. Else, the password
+                            //  is the rest of the string.
+                            //
+                            if (ptr1)
+                            {
+                                fPassword = new XMLCh[(ptr1 - srcPtr) + 1];
+                                ptr2 = fPassword;
+                                while (srcPtr < ptr1)
+                                    *ptr2++ = *srcPtr++;
+                                *ptr2 = 0;
+                            }
+                             else
+                            {
+                                fPassword = XMLString::replicate(srcPtr);
+                                return;
+                            }
+                        }
+                    }
+                     else
+                    {
+                        fUser = XMLString::replicate(srcPtr);
+                        return;
+                    }
+                }
+            }
+             else
+            {
+                fHost = XMLString::replicate(srcPtr);
+                return;
+            }
+        }
+    }
 
     //
-    //  Search the text for a prefix. We will get back the type of the prefix
-    //  and the current position will be updated.
+    //  Next is the path part. It can be absolute, i.e. starting with a
+    //  forward slash character, or relative. Its basically everything up
+    //  to the end of the string or to any trailing query or fragment.
     //
-    fProtocol = findType(curPos);
+    ptr1 = XMLString::findAny(srcPtr, listFive);
+    if (!ptr1)
+    {
+        fPath = XMLString::replicate(srcPtr);
+        return;
+    }
+
+    // Everything from where we are to what we found is the path
+    if (ptr1 > srcPtr)
+    {
+        fPath = new XMLCh[(ptr1 - srcPtr) + 1];
+        ptr2 = fPath;
+        while (srcPtr < ptr1)
+            *ptr2++ = *srcPtr++;
+        *ptr2 = 0;
+    }
 
-    //  In order to distinguish between a malformed URL and an unsupported
-    //  URL, we watch here for the types that we support. If its not supported
-    //  we throw a runtime exception.
     //
-    if (!gTypeList[fProtocol].supported)
-        ThrowXML(MalformedURLException, XML4CExcepts::URL_UnsupportedProto);
+    //  If we found a fragment, then it is the rest of the string and we
+    //  are done.
+    //
+    if (*srcPtr == chPound)
+    {
+        srcPtr++;
+        fFragment = XMLString::replicate(srcPtr);
+        return;
+    }
 
     //
-    //  Check the next char. It must be either a forward slash or it will
-    //  be the first char of the host name. We don't allow end of input
-    //  here so it will cause an exception if we hit it.
+    //  The query is either the rest of the string, or up to the fragment
+    //  separator.
     //
-    nextCh = getNextCh(curPos);
-    if (nextCh == chForwardSlash)
+    srcPtr++;
+    ptr1 = XMLString::findAny(srcPtr, listSix);
+    if (!ptr1)
     {
-        // There is no host so make an empty one
-        fHost = new XMLCh[1];
-        fHost[0] = chNull;
+        fQuery = XMLString::replicate(srcPtr);
+        return;
     }
      else
     {
-        // Put in the lookahead char we did above before we enter the loop
-        bufIndex = 0;
-        tmpBuf[bufIndex++] = nextCh;
-
-        // And now read up to the slash separator
-        while (true)
-        {
-            // Get the next char, end of input is not valid here
-            const XMLCh nextCh = getNextCh(curPos);
-
-            // Break out on the forward slash
-            if (nextCh == chForwardSlash)
-                break;
+        fQuery = new XMLCh[(ptr1 - srcPtr) + 1];
+        ptr2 = fQuery;
+        while (srcPtr < ptr1)
+            *ptr2++ = *srcPtr++;
+        *ptr2 = 0;
+    }
 
-            // Otherwise, save it
-            tmpBuf[bufIndex++] = nextCh;
+    // If we are not at the end now, then everything else is the fragment
+    if (*srcPtr == chPound)
+    {
+        srcPtr++;
+        fFragment = XMLString::replicate(srcPtr);
+    }
+}
 
-            // If we max out on the temp buffer, definitely bad
-            if (bufIndex >= bufSize)
-                ThrowXML(MalformedURLException, XML4CExcepts::URL_MalformedURL);
-        }
 
-        // Cap the temp buffer and replicate to our host member
-        tmpBuf[bufIndex] = chNull;
-        fHost = XMLString::replicate(tmpBuf);
-    }
+void URL::weavePaths(const XMLCh* const basePart)
+{
+    // Watch for stupid stuff
+    if (!basePart)
+        return;
+    if (!*basePart)
+        return;
 
     //
-    //  Now we need to get the path part of the URL. This should be the
-    //  rest of the content. So we just go until we get a null char back
-    //  from the character spooler. This gets rid of all escaped chars
-    //  in the URL.
+    //  Ok, lets start at the end of the base path and work backwards and
+    //  our path part and work forwards. For each leading . we see, we just
+    //  eat it. For each leading .. we see, we eat it and throw away one
+    //  level in the source URL.
     //
-    bufIndex = 0;
-    while (true)
-    {
-        // Tell it that end of input is ok
-        const XMLCh nextCh = getNextCh(curPos, true);
-        tmpBuf[bufIndex++] = nextCh;
+    //  If the last character in the base part is a forward slash, back
+    //  up one first before we look for the last slash.
+    //
+    const XMLCh* basePtr = basePart + (XMLString::stringLen(basePart) - 1);
+    if (*basePtr == chForwardSlash)
+        basePtr--;
 
-        // Break out at the end
-        if (!nextCh)
-            break;
+    while ((basePtr >= basePart)
+    &&     ((*basePtr != chForwardSlash) && (*basePtr != chBackSlash)))
+    {
+        basePtr--;
     }
 
+    if (basePtr < basePart)
+        return;
+
+    // Create a buffer as large as both parts
+    XMLCh* tmpBuf = new XMLCh[XMLString::stringLen(fPath)
+                              + XMLString::stringLen(basePart)
+                              + 2];
     //
-    //  And get our own copy of the temp buffer as the path. If the path
-    //  does not start with 'x:', then assume that its a Unix style path
-    //  and put in a leading '/'.
+    //  If we have no path part, then copy the base part up to the
+    //  base pointer
     //
-    tmpBuf[bufIndex] = chNull;
-    if ((((tmpBuf[0] >= chLatin_A) && (tmpBuf[0] <= chLatin_Z))
-    ||   ((tmpBuf[0] >= chLatin_a) && (tmpBuf[0] <= chLatin_z)))
-    &&  (tmpBuf[1] == chColon))
-    {
-        fPath = XMLString::replicate(tmpBuf);
-    }
-     else
+    if (!fPath)
     {
-        fPath = new XMLCh[XMLString::stringLen(tmpBuf) + 2];
-        fPath[0] = chForwardSlash;
-        XMLString::copyString(&fPath[1], tmpBuf);
+        XMLCh* bufPtr = tmpBuf;
+        const XMLCh* tmpPtr = basePart;
+        while (tmpPtr <= basePtr)
+            *bufPtr++ = *tmpPtr++;
+        *bufPtr = 0;
+
+        fPath = tmpBuf;
+        return;
     }
 
+    // After this, make sure the buffer gets handled if we exit early
+    ArrayJanitor<XMLCh> janBuf(tmpBuf);
+
     //
-    //  <TBD> When we have more complete support, get rid of this. But for
-    //  now we only support file:// (which is checked above) and for files
-    //  we only support an empty host or "localhost" which means the same
-    //  thing.
+    //  We have some path part, so we need to check to see if we ahve to
+    //  weave any of the parts together.
     //
-    if (fHost[0])
+    XMLCh* pathPtr = fPath;
+    while (true)
     {
-        if (XMLString::compareString(fHost, gLocalHostString))
-            ThrowXML(MalformedURLException, XML4CExcepts::URL_OnlyLocalHost);
+        // If it does not start with some period, then we are done
+        if (*pathPtr != chPeriod)
+            break;
+
+        unsigned int periodCount = 1;
+        pathPtr++;
+        if (*pathPtr == chPeriod)
+        {
+            pathPtr++;
+            periodCount++;
+        }
+
+        // Has to be followed by a / or \ or the null to mean anything
+        if ((*pathPtr != chForwardSlash) && (*pathPtr != chBackSlash)
+        &&  *pathPtr)
+        {
+            break;
+        }
+        if (*pathPtr)
+            pathPtr++;
+
+        // If its one period, just eat it, else move backwards in the base
+        if (periodCount == 2)
+        {
+            basePtr--;
+            while ((basePtr >= basePart)
+            &&     ((*basePtr != chForwardSlash) && (*basePtr != chBackSlash)))
+            {
+                basePtr--;
+            }
+
+            if (basePtr < basePart)
+            {
+                // The base cannot provide enough levels, so its in error
+                // <TBD>
+            }
+        }
     }
+
+    // Copy the base part up to the base pointer
+    XMLCh* bufPtr = tmpBuf;
+    const XMLCh* tmpPtr = basePart;
+    while (tmpPtr <= basePtr)
+        *bufPtr++ = *tmpPtr++;
+
+    // And then copy on the rest of our path
+    XMLString::copyString(bufPtr, pathPtr);
+
+    // Now delete our path and make the new buffer our path
+    delete [] fPath;
+    janBuf.orphan();
+    fPath = tmpBuf;
 }
diff --git a/src/util/URL.hpp b/src/util/URL.hpp
index 1c5b5822182f5cc745a180d92d8dcc998e037f7b..ee8559b037153b163503aa8b3ddd343fc6b17bc7 100644
--- a/src/util/URL.hpp
+++ b/src/util/URL.hpp
@@ -56,6 +56,10 @@
 
 /**
  * $Log$
+ * Revision 1.3  2000/01/12 00:16:22  roddey
+ * Changes to deal with multiply nested, relative pathed, entities and to deal
+ * with the new URL class changes.
+ *
  * Revision 1.2  1999/11/23 01:49:56  rahulj
  * Cannot use class qualifier in class defn. CC under HPUX is happy.
  *
@@ -90,8 +94,8 @@ MakeXML4CException(MalformedURLException, XMLUTIL_EXPORT)
 
 
 //
-//  This class provides a minimal URL implementation to be able to deal
-//  with file:// protocol URLs. It will be fleshed out in the future.
+//  This class supports file, http, and ftp style URLs. All others are
+//  rejected
 //
 class XMLUTIL_EXPORT URL
 {
@@ -99,30 +103,58 @@ public:
     // -----------------------------------------------------------------------
     //  Class types
     //
-    //  Local MUST be the last one in the list! And they must remain in this
-    //  order because they are indexes into an array internally!
+    //  And they must remain in this order because they are indexes into an
+    //  array internally!
     // -----------------------------------------------------------------------
     enum Protocols
     {
         File
         , HTTP
         , FTP
-        , Gopher
-        , MailTo
-        , News
-        , NNTP
-        , Telnet
-        , Wais
-        , Prospero
 
         , Protocols_Count
+        , Unknown
     };
 
 
+    // -----------------------------------------------------------------------
+    //  Public static methods
+    // -----------------------------------------------------------------------
+    Protocols lookupByName(const XMLCh* const protoName);
+
+
     // -----------------------------------------------------------------------
     //  Constructors and Destructor
     // -----------------------------------------------------------------------
     URL();
+    URL
+    (
+        const   XMLCh* const    baseURL
+        , const XMLCh* const    relativeURL
+    );
+    URL
+    (
+        const   XMLCh* const    baseURL
+        , const char* const     relativeURL
+    );
+    URL
+    (
+        const   URL&            baseURL
+        , const XMLCh* const    relativeURL
+    );
+    URL
+    (
+        const   URL&            baseURL
+        , const char* const     relativeURL
+    );
+    URL
+    (
+        const   XMLCh* const    urlText
+    );
+    URL
+    (
+        const   char* const     urlText
+    );
     URL(const URL& toCopy);
     virtual ~URL();
 
@@ -138,58 +170,96 @@ public:
     // -----------------------------------------------------------------------
     //  Getter methods
     // -----------------------------------------------------------------------
+    const XMLCh* getFragment() const;
     const XMLCh* getHost() const;
-    const XMLCh* getProtocol() const;
+    const XMLCh* getPassword() const;
     const XMLCh* getPath() const;
-    Protocols getType() const;
-    const XMLCh* getURL() const;
+    Protocols getProtocol() const;
+    const XMLCh* getProtocolName() const;
+    const XMLCh* getQuery() const;
+    const XMLCh* getURLText() const;
+    const XMLCh* getUser() const;
 
 
     // -----------------------------------------------------------------------
-    //  Getter methods
+    //  Setter methods
     // -----------------------------------------------------------------------
     void setURL(const XMLCh* const urlText);
-    void setURL(const char* const urlText);
+    void setURL
+    (
+        const   XMLCh* const    baseURL
+        , const XMLCh* const    relativeURL
+    );
+    void setURL
+    (
+        const   URL&            baseURL
+        , const XMLCh* const    relativeURL
+    );
 
 
     // -----------------------------------------------------------------------
     //  Miscellaneous methods
     // -----------------------------------------------------------------------
+    bool isRelative() const;
     BinInputStream* makeNewStream() const;
+    void makeRelativeTo(const XMLCh* const baseURLText);
+    void makeRelativeTo(const URL& baseURL);
 
 
 private:
     // -----------------------------------------------------------------------
     //  Private helper methods
     // -----------------------------------------------------------------------
+    void buildFullText();
     void cleanup();
-    Protocols findType(unsigned int& curPos) const;
-    XMLCh getNextCh(unsigned int& pos, const bool endOk = false) const;
-    XMLCh peekNextCh(unsigned int pos) const;
-    void parse();
+    void conglomerateWithBase(const URL& baseURL);
+    void parse
+    (
+        const   XMLCh* const    urlText
+    );
+    void weavePaths(const XMLCh* const basePart);
 
 
     // -----------------------------------------------------------------------
     //  Data members
     //
-    //  fFullURL
-    //      This is a copy of the original URL.
+    //  fFragment
+    //      The fragment part of the URL, if any. If none, its a null.
     //
     //  fHost
     //      The host part of the URL that was parsed out. This one will often
-    //      be empty (or "localhost", which also means the current machine.)
+    //      be null (or "localhost", which also means the current machine.)
+    //
+    //  fPassword
+    //      The password found, if any. If none then its a null.
     //
     //  fPath
-    //      The path part of the URL that was parsed out.
+    //      The path part of the URL that was parsed out, if any. If none,
+    //      then its a null.
     //
     //  fProtocol
     //      Indicates the type of the URL's source. The text of the prefix
     //      can be gotten from this.
+    //
+    //  fQuery
+    //      The query part of the URL, if any. If none, then its a null.
+    //
+    //  fUser
+    //      The username found, if any. If none, then its a null.
+    //
+    //  fURLText
+    //      This is a copy of the URL text, after it has been taken apart,
+    //      made relative if needed, canonicalized, and then put back
+    //      together. Its only created upon demand.
     // -----------------------------------------------------------------------
-    XMLCh*      fFullURL;
+    XMLCh*      fFragment;
     XMLCh*      fHost;
+    XMLCh*      fPassword;
     XMLCh*      fPath;
     Protocols   fProtocol;
+    XMLCh*      fQuery;
+    XMLCh*      fUser;
+    XMLCh*      fURLText;
 };
 
 
@@ -205,25 +275,52 @@ inline bool URL::operator!=(const URL& toCompare) const
 // ---------------------------------------------------------------------------
 //  URL: Getter methods
 // ---------------------------------------------------------------------------
+inline const XMLCh* URL::getFragment() const
+{
+    return fFragment;
+}
+
 inline const XMLCh* URL::getHost() const
 {
     return fHost;
 }
 
+inline const XMLCh* URL::getPassword() const
+{
+    return fPassword;
+}
+
 inline const XMLCh* URL::getPath() const
 {
     return fPath;
 }
 
-inline URL::Protocols URL::getType() const
+inline URL::Protocols URL::getProtocol() const
 {
     return fProtocol;
 }
 
-inline const XMLCh* URL::getURL() const
+inline const XMLCh* URL::getQuery() const
+{
+    return fQuery;
+}
+
+inline const XMLCh* URL::getUser() const
 {
-    return fFullURL;
+    return fUser;
 }
 
+inline const XMLCh* URL::getURLText() const
+{
+    //
+    //  Fault it in if not already. Since this is a const method and we
+    //  can't use mutable members due the compilers we have to support,
+    //  we have to cast off the constness.
+    //
+    if (!fURLText)
+        ((URL*)this)->buildFullText();
+
+    return fURLText;
+}
 
 #endif
diff --git a/src/util/XMLNetAccessor.hpp b/src/util/XMLNetAccessor.hpp
index 8159e22cacaf66ac3ffd7ae74044a9e585d44d37..0799786c181410f6dca6383166f7fb0fd0864753 100644
--- a/src/util/XMLNetAccessor.hpp
+++ b/src/util/XMLNetAccessor.hpp
@@ -56,8 +56,12 @@
 
 /**
  * $Log$
- * Revision 1.1  1999/11/09 01:05:50  twl
- * Initial revision
+ * Revision 1.2  2000/01/12 00:16:23  roddey
+ * Changes to deal with multiply nested, relative pathed, entities and to deal
+ * with the new URL class changes.
+ *
+ * Revision 1.1.1.1  1999/11/09 01:05:50  twl
+ * Initial checkin
  *
  * Revision 1.2  1999/11/08 20:45:20  rahul
  * Swat for adding in Product name and CVS comment log variable.
@@ -101,8 +105,7 @@ public :
     // -----------------------------------------------------------------------
     virtual BinInputStream* makeNew
     (
-        const   XMLCh* const            urlSource
-        ,       XML4CExcepts::Codes&    failReason
+        const   URL&                    urlSrc
     ) = 0;
 
 
diff --git a/src/util/XMLString.cpp b/src/util/XMLString.cpp
index f19cc38495f4269f6cf7a5f6955b56264b2f5373..3296c7a02d89a43d9222017ee28739046b555ed9 100644
--- a/src/util/XMLString.cpp
+++ b/src/util/XMLString.cpp
@@ -56,6 +56,10 @@
 
 /**
  * $Log$
+ * Revision 1.4  2000/01/12 00:16:23  roddey
+ * Changes to deal with multiply nested, relative pathed, entities and to deal
+ * with the new URL class changes.
+ *
  * Revision 1.3  1999/12/18 00:18:10  roddey
  * More changes to support the new, completely orthagonal support for
  * intrinsic encodings.
@@ -846,7 +850,46 @@ unsigned int XMLString::hash(   const   XMLCh* const    tohash
 }
 
 
-unsigned int XMLString::hashN(   const   XMLCh* const    tohash
+const XMLCh* XMLString::findAny(const   XMLCh* const    toSearch
+                                , const XMLCh* const    searchList)
+{
+    const XMLCh* srcPtr = toSearch;
+    while (*srcPtr)
+    {
+        const XMLCh* listPtr = searchList;
+        const XMLCh  curCh = *srcPtr;
+
+        while (*listPtr)
+        {
+            if (curCh == *listPtr++)
+                return srcPtr;
+        }
+        srcPtr++;
+    }
+    return 0;
+}
+
+XMLCh* XMLString::findAny(          XMLCh* const    toSearch
+                            , const XMLCh* const    searchList)
+{
+    XMLCh* srcPtr = toSearch;
+    while (*srcPtr)
+    {
+        const XMLCh* listPtr = searchList;
+        const XMLCh  curCh = *srcPtr;
+
+        while (*listPtr)
+        {
+            if (curCh == *listPtr++)
+                return srcPtr;
+        }
+        srcPtr++;
+    }
+    return 0;
+}
+
+
+unsigned int XMLString::hashN(  const   XMLCh* const    tohash
                                 , const unsigned int    n
                                 , const unsigned int    hashModulus)
 {
diff --git a/src/util/XMLString.hpp b/src/util/XMLString.hpp
index 336c7d7dc45ba28dd2d563ce7088b31bf8999b4b..76059bce8a863cf1b55ac0bdae7f4baeb47184d2 100644
--- a/src/util/XMLString.hpp
+++ b/src/util/XMLString.hpp
@@ -56,6 +56,10 @@
 
 /**
  * $Log$
+ * Revision 1.4  2000/01/12 00:16:23  roddey
+ * Changes to deal with multiply nested, relative pathed, entities and to deal
+ * with the new URL class changes.
+ *
  * Revision 1.3  1999/12/18 00:18:10  roddey
  * More changes to support the new, completely orthagonal support for
  * intrinsic encodings.
@@ -295,6 +299,18 @@ public:
         , const unsigned int    maxChars
     );
 
+    static const XMLCh* findAny
+    (
+        const   XMLCh* const    toSearch
+        , const XMLCh* const    searchList
+    );
+
+    static XMLCh* findAny
+    (
+                XMLCh* const    toSearch
+        , const XMLCh* const    searchList
+    );
+
     static unsigned int hash
     (
         const   XMLCh* const    toHash
diff --git a/src/validators/DTD/DTDValidator2.cpp b/src/validators/DTD/DTDValidator2.cpp
index 17ef241c6526ddbae5fc162e4880e3a2a7aa06f3..5018b7fec61f854828819996a3f7f4fcf01e42f1 100644
--- a/src/validators/DTD/DTDValidator2.cpp
+++ b/src/validators/DTD/DTDValidator2.cpp
@@ -56,6 +56,10 @@
 
 /**
  * $Log$
+ * Revision 1.4  2000/01/12 00:17:07  roddey
+ * Changes to deal with multiply nested, relative pathed, entities and to deal
+ * with the new URL class changes.
+ *
  * Revision 1.3  1999/12/04 01:13:16  roddey
  * Fixed the logic for checking for PIs that start with 'xml'. It was doing doing "if (stringICompare()) "
  * instead of "if (!stringICompare()).
@@ -83,12 +87,12 @@
 #include <util/UnexpectedEOFException.hpp>
 #include <util/URL.hpp>
 #include <util/XMLUni.hpp>
+#include <sax/InputSource.hpp>
 #include <framework/XMLBufferMgr.hpp>
 #include <framework/XMLDocumentHandler.hpp>
 #include <framework/XMLElementDecl.hpp>
 #include <framework/XMLEntityHandler.hpp>
 #include <framework/XMLNotationDecl.hpp>
-#include <internal/URLInputSource.hpp>
 #include <internal/EndOfEntityException.hpp>
 #include <internal/ReaderMgr.hpp>
 #include <internal/XMLScanner.hpp>