blob: d15d7c14d9830f908b3a706283ad1801e9491c8a [file] [log] [blame]
#if !defined(WEAVEPATH_CPP)
#define WEAVEPATH_CPP
/***
*
* Previously, each <OS>PlatformUtils.cpp has its onw copy of the
* method weavePaths(), and almost of them implemented the same logic,
* with few platform specific difference, and unfortunately that
* implementation was wrong.
*
* The only platform specific issue is slash character.
* On all platforms other than Windows, chForwardSlash and chBackSlash
* are considered slash, while on Windows, two additional characters,
* chYenSign and chWonSign are slash as well.
*
* The idea is to maintain a SINGLE copy of this method rather than
* each <OS>PlatformUtils.cpp has its own copy, we introduce a new
* method, XMLPlatformUtils::isAnySlash(), to replace the direct checking
* code ( if ( c == chForwardSlash || c == chBackSlash).
*
* With this approach, we might have a performance hit since isAnySlash()
* is so frequently used in this implementation, so we intend to make it
* inline. Then we face a complier issue.
*
* There are two compilation units involved, one is PlatformUtils.cpp and
* the other <OS>PlatformUtils.cpp. When PlatformUtils.cp get compiled,
* the weavePath(), remove**Slash() have dependency upon isAnySlash() which
* is in <OS>PlatformUtils.cpp (and what is worse, it is inlined), so we have
* undefined/unresolved symbol: isAnySlash() on AIX/xlc_r, Solaris/cc and
* Linux/gcc, while MSVC and HP/aCC are fine with this.
*
* That means we can not place these new methods in PlatformUtils.cpp with
* inlined XMLPlatformUtils::isAnySlash() in <OS>PlatformUtils.cpp.
*
* The solution to this is <os>PlatformUtils.cpp will include this file so that
* we have only one copy of these methods while get compiled in <os>PlatformUtils
* inlined isAnySlash().
*
***/
XMLCh* XMLPlatformUtils::weavePaths(const XMLCh* const basePath
, const XMLCh* const relativePath
, MemoryManager* const manager)
{
// Create a buffer as large as both parts and empty it
XMLCh* tmpBuf = (XMLCh*) manager->allocate
(
(XMLString::stringLen(basePath)
+ XMLString::stringLen(relativePath) + 2) * sizeof(XMLCh)
);//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) || (!*basePath))
{
XMLString::copyString(tmpBuf, relativePath);
return tmpBuf;
}
//
// Remove anything after the last slash
//
const XMLCh* basePtr = basePath + (XMLString::stringLen(basePath) - 1);
while ((basePtr >= basePath) && ((isAnySlash(*basePtr) == false)))
{
basePtr--;
}
// There is no relevant base path, so just take the relative part
if (basePtr < basePath)
{
XMLString::copyString(tmpBuf, relativePath);
return tmpBuf;
}
//
// 1. concatenate the base and relative
// 2. remove all occurences of "/./"
// 3. remove all occurences of segment/../ where segment is not ../
//
XMLString::subString(tmpBuf, basePath, 0, (basePtr - basePath + 1), manager);
tmpBuf[basePtr - basePath + 1] = 0;
XMLString::catString(tmpBuf, relativePath);
removeDotSlash(tmpBuf, manager);
removeDotDotSlash(tmpBuf, manager);
return tmpBuf;
}
//
// Remove all occurences of './' when it is part of '/./'
//
// Since it could be '.\' or other combination on windows ( eg, '.'+chYanSign)
// we can't make use of patterMatch().
//
//
void XMLPlatformUtils::removeDotSlash(XMLCh* const path
, MemoryManager* const manager)
{
if ((!path) || (!*path))
return;
XMLCh* srcPtr = XMLString::replicate(path, manager);
int srcLen = XMLString::stringLen(srcPtr);
ArrayJanitor<XMLCh> janName(srcPtr, manager);
XMLCh* tarPtr = path;
while (*srcPtr)
{
if ( 3 <= srcLen )
{
if ( (isAnySlash(*srcPtr)) &&
(chPeriod == *(srcPtr+1)) &&
(isAnySlash(*(srcPtr+2))) )
{
// "\.\x" seen
// skip the first two, and start from the 3rd,
// since "\x" could be another "\."
srcPtr+=2;
srcLen-=2;
}
else
{
*tarPtr++ = *srcPtr++; // eat the current char
srcLen--;
}
}
else if ( 1 == srcLen )
{
*tarPtr++ = *srcPtr++;
}
else if ( 2 == srcLen)
{
*tarPtr++ = *srcPtr++;
*tarPtr++ = *srcPtr++;
}
}
*tarPtr = 0;
return;
}
//
// Remove all occurences of '/segment/../' when segment is not '..'
//
// Cases with extra /../ is left to the underlying file system.
//
void XMLPlatformUtils::removeDotDotSlash(XMLCh* const path
, MemoryManager* const manager)
{
int pathLen = XMLString::stringLen(path);
XMLCh* tmp1 = (XMLCh*) manager->allocate
(
(pathLen+1) * sizeof(XMLCh)
);//new XMLCh [pathLen+1];
ArrayJanitor<XMLCh> tmp1Name(tmp1, manager);
XMLCh* tmp2 = (XMLCh*) manager->allocate
(
(pathLen+1) * sizeof(XMLCh)
);//new XMLCh [pathLen+1];
ArrayJanitor<XMLCh> tmp2Name(tmp2, manager);
// remove all "<segment>/../" where "<segment>" is a complete
// path segment not equal to ".."
int index = -1;
int segIndex = -1;
int offset = 1;
while ((index = searchSlashDotDotSlash(&(path[offset]))) != -1)
{
// Undo offset
index += offset;
// Find start of <segment> within substring ending at found point.
XMLString::subString(tmp1, path, 0, index-1, manager);
segIndex = index - 1;
while ((segIndex >= 0) && (!isAnySlash(tmp1[segIndex])))
{
segIndex--;
}
// Ensure <segment> exists and != ".."
if (segIndex >= 0 &&
(path[segIndex+1] != chPeriod ||
path[segIndex+2] != chPeriod ||
segIndex + 3 != index))
{
XMLString::subString(tmp1, path, 0, segIndex, manager);
XMLString::subString(tmp2, path, index+3, XMLString::stringLen(path), manager);
path[0] = 0;
XMLString::catString(path, tmp1);
XMLString::catString(path, tmp2);
offset = (segIndex == 0 ? 1 : segIndex);
}
else
{
offset += 4;
}
}// while
}
int XMLPlatformUtils::searchSlashDotDotSlash(XMLCh* const srcPath)
{
if ((!srcPath) || (!*srcPath))
return -1;
XMLCh* srcPtr = srcPath;
int srcLen = XMLString::stringLen(srcPath);
int retVal = -1;
while (*srcPtr)
{
if ( 4 <= srcLen )
{
if ( (isAnySlash(*srcPtr)) &&
(chPeriod == *(srcPtr+1)) &&
(chPeriod == *(srcPtr+2)) &&
(isAnySlash(*(srcPtr+3))) )
{
retVal = (srcPtr - srcPath);
break;
}
else
{
srcPtr++;
srcLen--;
}
}
else
{
break;
}
} // while
return retVal;
}
#endif