GME  13
LogicalPath.c
Go to the documentation of this file.
00001 /*
00002  * Licensed to the Apache Software Foundation (ASF) under one or more
00003  * contributor license agreements.  See the NOTICE file distributed with
00004  * this work for additional information regarding copyright ownership.
00005  * The ASF licenses this file to You under the Apache License, Version 2.0
00006  * (the "License"); you may not use this file except in compliance with
00007  * the License.  You may obtain a copy of the License at
00008  * 
00009  *      http://www.apache.org/licenses/LICENSE-2.0
00010  * 
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS,
00013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  */
00017 
00018 /*
00019  * $Id: LogicalPath.c 932887 2010-04-11 13:04:59Z borisk $
00020  */
00021 
00022 #if !defined(XERCESC_INCLUDE_GUARD_WEAVEPATH_CPP)
00023 #define XERCESC_INCLUDE_GUARD_WEAVEPATH_CPP
00024 
00025 /***
00026  *
00027  *  Previously, each <OS>PlatformUtils.cpp has its onw copy of the
00028  *  method weavePaths(), and almost of them implemented the same logic,
00029  *  with few platform specific difference, and unfortunately that 
00030  *  implementation was wrong.
00031  *  
00032  *  The only platform specific issue is slash character.
00033  *  On all platforms other than Windows, chForwardSlash and chBackSlash 
00034  *  are considered slash, while on Windows, two additional characters, 
00035  *  chYenSign and chWonSign are slash as well.
00036  *
00037  *  The idea is to maintain a SINGLE copy of this method rather than
00038  *  each <OS>PlatformUtils.cpp has its own copy, we introduce a new
00039  *  method, XMLPlatformUtils::isAnySlash(), to replace the direct checking
00040  *  code ( if ( c == chForwardSlash || c == chBackSlash).
00041  *
00042  *  With this approach, we might have a performance hit since isAnySlash() 
00043  *  is so frequently used in this implementation, so we intend to make it 
00044  *  inline. Then we face a complier issue.
00045  *
00046  *  There are two compilation units involved, one is PlatformUtils.cpp and 
00047  *  the other <OS>PlatformUtils.cpp. When PlatformUtils.cp get compiled,
00048  *  the weavePath(), remove**Slash() have dependency upon isAnySlash() which
00049  *  is in <OS>PlatformUtils.cpp (and what is worse, it is inlined), so we have
00050  *  undefined/unresolved symbol: isAnySlash() on AIX/xlc_r, Solaris/cc and 
00051  *  Linux/gcc, while MSVC and HP/aCC are fine with this.
00052  *  
00053  *  That means we can not place these new methods in PlatformUtils.cpp with
00054  *  inlined XMLPlatformUtils::isAnySlash() in <OS>PlatformUtils.cpp.
00055  *
00056  *  The solution to this is <os>PlatformUtils.cpp will include this file so that
00057  *  we have only one copy of these methods while get compiled in <os>PlatformUtils
00058  *  inlined isAnySlash().
00059  *
00060  ***/
00061 XMLCh* XMLPlatformUtils::weavePaths(const XMLCh* const    basePath
00062                                   , const XMLCh* const    relativePath
00063                                   , MemoryManager* const  manager)
00064 
00065 {
00066     // Create a buffer as large as both parts and empty it
00067     XMLCh* tmpBuf = (XMLCh*) manager->allocate
00068     (
00069         (XMLString::stringLen(basePath)
00070          + XMLString::stringLen(relativePath) + 2) * sizeof(XMLCh)
00071     );//new XMLCh[XMLString::stringLen(basePath) + XMLString::stringLen(relativePath) + 2];
00072     *tmpBuf = 0;
00073 
00074     //
00075     //  If we have no base path, then just take the relative path as is.
00076     //
00077     if ((!basePath) || (!*basePath))
00078     {
00079         XMLString::copyString(tmpBuf, relativePath);
00080         return tmpBuf;
00081     }
00082 
00083     //
00084     // Remove anything after the last slash
00085     //
00086     const XMLCh* basePtr = basePath + (XMLString::stringLen(basePath) - 1);
00087     while ((basePtr >= basePath)  &&  ((isAnySlash(*basePtr) == false)))
00088     {
00089         basePtr--;
00090     }
00091 
00092     // There is no relevant base path, so just take the relative part
00093     if (basePtr < basePath)
00094     {
00095         XMLString::copyString(tmpBuf, relativePath);
00096         return tmpBuf;
00097     }
00098 
00099     //
00100     // 1. concatenate the base and relative
00101     // 2. remove all occurrences of "/./"
00102     // 3. remove all occurrences of segment/../ where segment is not ../
00103         // 
00104 
00105     XMLString::subString(tmpBuf, basePath, 0, (basePtr - basePath + 1), manager);
00106     tmpBuf[basePtr - basePath + 1] = 0;
00107     XMLString::catString(tmpBuf, relativePath);
00108 
00109     removeDotSlash(tmpBuf, manager);
00110 
00111     removeDotDotSlash(tmpBuf, manager);
00112 
00113     return tmpBuf;
00114 
00115 }
00116 
00117 //
00118 // Remove all occurrences of './' when it is part of '/./'
00119 //
00120 // Since it could be '.\' or other combination on windows ( eg, '.'+chYanSign)
00121 // we can't make use of patterMatch().
00122 //
00123 //
00124 void XMLPlatformUtils::removeDotSlash(XMLCh* const path
00125                                       , MemoryManager* const manager)
00126 {
00127     if ((!path) || (!*path))
00128         return;
00129 
00130     XMLCh* srcPtr = XMLString::replicate(path, manager);
00131     int    srcLen = XMLString::stringLen(srcPtr);
00132     ArrayJanitor<XMLCh>   janName(srcPtr, manager);   
00133     XMLCh* tarPtr = path;
00134 
00135     while (*srcPtr)
00136     {
00137         if ( 3 <= srcLen )
00138         {
00139             if ( (isAnySlash(*srcPtr))     &&
00140                 (chPeriod == *(srcPtr+1)) &&
00141                 (isAnySlash(*(srcPtr+2)))  )
00142             {
00143                 // "\.\x" seen
00144                 // skip the first two, and start from the 3rd,
00145                 // since "\x" could be another "\."
00146                 srcPtr+=2;              
00147                 srcLen-=2;
00148             }
00149             else
00150             {
00151                 *tarPtr++ = *srcPtr++;  // eat the current char
00152                 srcLen--;
00153             }
00154         }
00155         else if ( 1 == srcLen )
00156         {
00157             *tarPtr++ = *srcPtr++;
00158         }
00159         else if ( 2 == srcLen)
00160         {
00161             *tarPtr++ = *srcPtr++;
00162             *tarPtr++ = *srcPtr++;
00163         }
00164 
00165     }
00166 
00167     *tarPtr = 0;
00168 
00169     return;
00170 }
00171 
00172 //
00173 // Remove all occurrences of '/segment/../' when segment is not '..'
00174 //
00175 // Cases with extra /../ is left to the underlying file system.
00176 //
00177 void XMLPlatformUtils::removeDotDotSlash(XMLCh* const path
00178                                          , MemoryManager* const manager)
00179 {
00180     int pathLen = XMLString::stringLen(path);
00181     XMLCh* tmp1 = (XMLCh*) manager->allocate
00182     (
00183         (pathLen+1) * sizeof(XMLCh)
00184     );//new XMLCh [pathLen+1];
00185     ArrayJanitor<XMLCh>   tmp1Name(tmp1, manager);
00186 
00187     XMLCh* tmp2 = (XMLCh*) manager->allocate
00188     (
00189         (pathLen+1) * sizeof(XMLCh)
00190     );//new XMLCh [pathLen+1];
00191     ArrayJanitor<XMLCh>   tmp2Name(tmp2, manager);
00192 
00193     // remove all "<segment>/../" where "<segment>" is a complete
00194     // path segment not equal to ".."
00195     int index = -1;
00196     int segIndex = -1;
00197     int offset = 1;
00198 
00199     while ((index = searchSlashDotDotSlash(&(path[offset]))) != -1)
00200     {
00201         // Undo offset
00202         index += offset;
00203 
00204         // Find start of <segment> within substring ending at found point.
00205         XMLString::subString(tmp1, path, 0, index-1, manager);
00206         segIndex = index - 1;
00207         while ((segIndex >= 0) && (!isAnySlash(tmp1[segIndex])))
00208         {
00209             segIndex--;
00210         }
00211 
00212         // Ensure <segment> exists and != ".."
00213         if (segIndex >= 0                 &&
00214             (path[segIndex+1] != chPeriod ||
00215              path[segIndex+2] != chPeriod ||
00216              segIndex + 3 != index))
00217         {
00218 
00219             XMLString::subString(tmp1, path, 0, segIndex, manager);
00220             XMLString::subString(tmp2, path, index+3, XMLString::stringLen(path), manager);
00221 
00222             path[0] = 0;
00223             XMLString::catString(path, tmp1);
00224             XMLString::catString(path, tmp2);
00225 
00226             offset = (segIndex == 0 ? 1 : segIndex);
00227         }
00228         else
00229         {
00230             offset += 4;
00231         }
00232 
00233     }// while
00234 
00235 }
00236 
00237 int XMLPlatformUtils::searchSlashDotDotSlash(XMLCh* const srcPath)
00238 {
00239     if ((!srcPath) || (!*srcPath))
00240         return -1;
00241 
00242     XMLCh* srcPtr = srcPath;
00243     int    srcLen = XMLString::stringLen(srcPath);
00244     int    retVal = -1;
00245 
00246     while (*srcPtr)
00247     {
00248         if ( 4 <= srcLen )
00249         {
00250             if ( (isAnySlash(*srcPtr))     &&
00251                  (chPeriod == *(srcPtr+1)) &&
00252                  (chPeriod == *(srcPtr+2)) &&
00253                  (isAnySlash(*(srcPtr+3)))  )
00254             {
00255                 retVal = (srcPtr - srcPath);
00256                 break;
00257             }
00258             else
00259             {
00260                 srcPtr++;
00261                 srcLen--;
00262             }
00263         }
00264         else 
00265         {
00266             break;
00267         }
00268 
00269     } // while
00270 
00271     return retVal;
00272 
00273 }
00274 
00275 #endif