1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
|
// max # of characters we support using the "\\?\" syntax
// (0x7FFF + 1 for NULL terminator)
#define PATHCCH_MAX_CCH 0x8000
#define LONG_PATH_ID L"\\\\?\\"
#define UNC_PREFIX "\\\\"
#define UNC_LONG_ID L"\\\\?\\UNC\\"
#define CUR_DIR_REL_PATH_ID ".\\"
#define WILDCAR_CHAR_ASTER '*'
#define WILDCAR_CHAR_QUEMARK '?'
#define DIR_DOWN ".."
#define DIR_UP "."
bool ValidatePath(_In_ const TCHAR *sPath, _Out_ bool & bDir)
{
//If the path contains a wildcards search test only the parent directory
const TCHAR * sLastElement = _tcsrchr(sPath, _T('\\')); //The last component in the path
bool bFoundWildCard = false;
if(sLastElement)
{
++sLastElement;
if(_tcsrchr(sLastElement, _T(WILDCAR_CHAR_ASTER)) || _tcsrchr(sLastElement, _T(WILDCAR_CHAR_QUEMARK))) //If wilcard characters are contained in the last path component
{
bFoundWildCard = true;
--sLastElement;
const_cast<TCHAR *>(sLastElement)[0] = _T('\0');
}
}
DWORD dwPathAttr = GetFileAttributes(sPath);
if(dwPathAttr == INVALID_FILE_ATTRIBUTES)
{
_com_error sErrorMsg(GetLastError());
//CProgramError.Set(sErrorMsg.ErrorMessage());
return false;
}
bDir = dwPathAttr & FILE_ATTRIBUTE_DIRECTORY;
if(bFoundWildCard)
{
const_cast<TCHAR *>(sLastElement)[0] = _T('\\');
}
return true;
}
void RespondPathComponent(_In_ const TCHAR *pComponent, _Out_ std::vector<CString> & vPathComponents)
{
const TCHAR *pComponentLimiterR = _tcschr(pComponent, _T('\\'));
const TCHAR *pComponentLimiterL = _tcschr(pComponent, _T('/'));
const TCHAR *pComponentLimiter = NULL;
if(pComponentLimiterR && pComponentLimiterL)
pComponentLimiter = (pComponentLimiterR > pComponentLimiterL ? pComponentLimiterL : pComponentLimiterR);
else
pComponentLimiter = (pComponentLimiterR ? pComponentLimiterR : pComponentLimiterL);
if(pComponentLimiter)
{
size_t szComponent = pComponentLimiter - pComponent;
if(szComponent)
{
CString sTemp;
sTemp.SetString(pComponent, szComponent);
vPathComponents.push_back(sTemp);
}
++pComponentLimiter;
RespondPathComponent(pComponentLimiter, vPathComponents);
}
else
{
size_t szLastComp = _tcslen(pComponent);
if(szLastComp)
{
CString sTemp;
sTemp.SetString(pComponent, szLastComp);
vPathComponents.push_back(sTemp);
}
}
}
size_t FixUpPathComponents(_Out_ std::vector<CString> & vPathComponents, _In_ const TCHAR *pPathComponents)
{
RespondPathComponent(pPathComponents, vPathComponents);
size_t szNumComponents = vPathComponents.size();
for(size_t i(0); i < szNumComponents; ++i)
{
if(vPathComponents[i] == _T(DIR_DOWN))
{
vPathComponents.erase(vPathComponents.begin() + i); //Remove the current component
--szNumComponents;
if(i > 0)
{
vPathComponents.erase(vPathComponents.begin() + i - 1);
--szNumComponents;
}
}
else if(vPathComponents[i] == _T(DIR_UP))
{
if( (i + 1) < szNumComponents )
{
vPathComponents.erase(vPathComponents.begin() + i + 1);
--szNumComponents;
}
vPathComponents.erase(vPathComponents.begin() + i); //Remove the current component
--szNumComponents;
}
}
return szNumComponents;
}
//Note that sCurrentDir is appended to all relative paths (nomatter the drive letter) - it needs to be a full path, not ending with '\\'
bool ExpandAndFixUpPath(_Inout_ CString & sPath, _In_opt_ const CString sCurrentDir)
{
const size_t InPathStrSz = sPath.GetLength();
if(!InPathStrSz) //Invalid path
return false;
//sPath.LockBuffer(); //Lock character buffer
const TCHAR *PathBuffer = sPath.GetString(); //Retrieve the buffer
if(InPathStrSz > 1) //To suppose the path is full it needs to have at lease 2 characters
{
if(_tcsstr(PathBuffer, _T(UNC_PREFIX)) == PathBuffer) //If the path begin with UNC_PREFIX
{
std::vector<CString> vPathComponents;
size_t nComponents;
if((nComponents = FixUpPathComponents(vPathComponents, &PathBuffer[2])) < 2) //A UNC path needs at leas two elements
return false;
sPath = _T('\\');
for(size_t i(0); i < nComponents; ++i)
{
sPath += _T('\\');
sPath += vPathComponents[i];
}
return true;
}
else if(PathBuffer[1] == _T(':')) //If the path begin with a disk designator
{
std::vector<CString> vPathComponents;
if(FixUpPathComponents(vPathComponents, &PathBuffer[2]))
{
if(PathBuffer[2] == _T('\\') || PathBuffer[2] == _T('/'))
{
sPath.SetString(PathBuffer, 2);
for(size_t i(0); i < vPathComponents.size(); ++i)
{
sPath += _T('\\');
sPath += vPathComponents[i];
}
}
else
{
sPath = sCurrentDir;
for(size_t i(0); i < vPathComponents.size(); ++i)
{
sPath += _T('\\');
sPath += vPathComponents[i];
}
}
}
else
{
sPath.SetString(PathBuffer, 2);
sPath += _T('\\');
}
return true;
}
}
std::vector<CString> vPathComponents;
const TCHAR *pComponentsBegin = (_tcsstr(PathBuffer, _T(CUR_DIR_REL_PATH_ID)) == PathBuffer ? &PathBuffer[((sizeof(_T(CUR_DIR_REL_PATH_ID)) / sizeof(TCHAR)) - 1)] : PathBuffer);
FixUpPathComponents(vPathComponents, pComponentsBegin);
sPath = sCurrentDir;
for(size_t i(0); i < vPathComponents.size(); ++i)
{
sPath += _T('\\');
sPath += vPathComponents[i];
}
return true;
}
bool NormalizePath(_Inout_ CString & sPath, _In_ const CString sCurrentDir, _Out_opt_ bool *bDir = NULL, _In_ bool bValidate = false)
{
if(!ExpandAndFixUpPath(sPath, sCurrentDir)) //Extend the path to it's full form
return false;
size_t LongPathLen = sPath.GetLength();
const TCHAR *pPathBuf = sPath.GetString();
#ifdef _UNICODE
if(LongPathLen <= (MAX_PATH - 1)) //If the path is longer in the range of MAX_PATH return it directly
{
if(bValidate)
if(!ValidatePath(pPathBuf, *bDir)) //Validate path before returning it
return false;
return true;
}
bool bIsUNCPath = _tcsstr(pPathBuf, _T(UNC_PREFIX)) == pPathBuf;
if(!bIsUNCPath)
{
if(LongPathLen > (PATHCCH_MAX_CCH - 1 - ((sizeof(LONG_PATH_ID) / sizeof(WCHAR)) - 1))) //If have no space to store the prefix fail
{
//CProgramError.Set(_T("Path too long!"));
return false;
}
CString sPathTmp = LONG_PATH_ID;
sPathTmp += pPathBuf;
if(bValidate)
if(!ValidatePath(sPathTmp.GetString(), *bDir)) //Validate path before returning it
return false;
sPath = sPathTmp;
return true;
}
else
{
if( LongPathLen > ( PATHCCH_MAX_CCH - 1 - ((sizeof(UNC_LONG_ID) / sizeof(WCHAR)) - 1) + ((sizeof(_T(UNC_PREFIX)) / sizeof(WCHAR)) - 1) ) ) //If have no space to store the prefix fail
{
//CProgramError.Set(_T("Path too long!"));
return false;
}
CString sPathTmp = UNC_LONG_ID;
sPathTmp += &pPathBuf[2];
if(bValidate)
if(!ValidatePath(sPathTmp.GetString(), *bDir)) //Validate path before returning it
return false;
sPath = sPathTmp;
return true;
}
#else
if(bValidate)
if(!ValidatePath(pPathBuf, *bDir)) //Validate path before returning it
return false;
return true;
#endif
}
|