Skip to content

Commit

Permalink
Accept forward slash as path separator on Windows.
Browse files Browse the repository at this point in the history
On Windows, all routines in DCMTK that handle path names now properly
handle paths that use the forward slash or a mix of forward slash and
backslash characters as path separators.
  • Loading branch information
Marco Eichelberg committed May 23, 2021
1 parent d3e7353 commit 52a6b76
Show file tree
Hide file tree
Showing 4 changed files with 233 additions and 15 deletions.
27 changes: 27 additions & 0 deletions dcmdata/libsrc/dcddirif.cc
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,12 @@ static int componentCount(const OFString &filename,
int count = (length > 0) ? 1 : 0;
for (size_t i = 0; i < length; i++)
{
#ifdef _WIN32
// Windows accepts both backslash and forward slash as path separators.
if ((filename.at(i) == separator) || (filename.at(i) == '/'))
#else
if (filename.at(i) == separator)
#endif
count++;
}
return count;
Expand All @@ -98,6 +103,11 @@ static OFBool isComponentTooLarge(const OFString &filename,
{
size_t pos1 = 0;
size_t pos2 = filename.find(separator);
#ifdef _WIN32
// Windows accepts both backslash and forward slash as path separators.
size_t pos3 = filename.find('/');
if ((pos2 == OFString_npos) || ((pos3 != OFString_npos) && (pos3 < pos2))) pos2 = pos3;
#endif
while (pos2 != OFString_npos)
{
/* check whether component length is within limit */
Expand All @@ -108,6 +118,10 @@ static OFBool isComponentTooLarge(const OFString &filename,
}
pos1 = pos2 + 1;
pos2 = filename.find(separator, pos1);
#ifdef _WIN32
pos3 = filename.find('/', pos1);
if ((pos2 == OFString_npos) || ((pos3 != OFString_npos) && (pos3 < pos2))) pos2 = pos3;
#endif
}
if (!result)
{
Expand Down Expand Up @@ -139,6 +153,10 @@ static OFBool locateInvalidFilenameChars(const OFString &filename,
{
c = filename.at(i);
if ((c == '_') || isdigit(c) || (c == separator) ||
#ifdef _WIN32
/* Windows accepts both backslash and forward slash as path separators. */
(c == '/') ||
#endif
(isalpha(c) && (isupper(c) || (islower(c) && mapFilenames))))
{
/* all ok */
Expand Down Expand Up @@ -166,7 +184,12 @@ static OFString &hostToDicomFilename(const OFString &hostFilename,
for (size_t i = 0; i < length; i++)
{
const unsigned char c = hostFilename.at(i);
#ifdef _WIN32
// Windows accepts both backslash and forward slash as path separators.
if ((c == PATH_SEPARATOR) || (c == '/'))
#else
if (c == PATH_SEPARATOR)
#endif
{
/* the PATH_SEPARATOR depends on the OS (see <osconfig.h>) */
dicomFilename += '\\';
Expand Down Expand Up @@ -1381,6 +1404,10 @@ OFBool DicomDirInterface::isFilenameValid(const OFFilename &filename,
size_t invalidChar = 0;
/* check whether the file name path is ok and in local format */
if ((fname[0] == PATH_SEPARATOR) /* absolute path? */ ||
#ifdef _WIN32
/* Windows accepts both backslash and forward slash as path separators. */
(fname[0] == '/') ||
#endif
locateInvalidFilenameChars(fname, invalidChar, MapFilenamesMode))
{
DCMDATA_ERROR("invalid character(s) in filename: " << fname << OFendl
Expand Down
13 changes: 10 additions & 3 deletions dcmpstat/libsrc/dviface.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1998,9 +1998,16 @@ int DVInterface::deleteImageFile(const char *filename)
{
if ((filename != NULL) && (pHandle != NULL))
{
const char *pos;
if (((pos = strrchr(filename, OFstatic_cast(int, PATH_SEPARATOR))) == NULL) || // check whether image file resides in index.dat directory
(strncmp(filename, pHandle->getStorageArea(), pos - filename) == 0))
const char *pos = strrchr(filename, OFstatic_cast(int, PATH_SEPARATOR));
#ifdef _WIN32
// Windows accepts both backslash and forward slash as path separators.
const char *pos2 = strrchr(filename, OFstatic_cast(int, '/'));

// if pos2 points to a character closer to the end of the string, use this instead of strPos
if ((pos == NULL) || ((pos2 != NULL) && (pos2 > pos))) pos = pos2;
#endif
// check whether image file resides in index.dat directory
if ((pos == NULL) || (strncmp(filename, pHandle->getStorageArea(), pos - filename) == 0))
{
// DB_deleteImageFile((/*const */char *)filename);
if (unlink(filename) == 0)
Expand Down
105 changes: 94 additions & 11 deletions ofstd/libsrc/ofstd.cc
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,13 @@ OFFilename &OFStandard::getDirNameFromPath(OFFilename &result,
{
const wchar_t *strValue = pathName.getWideCharPointer();
const wchar_t *strPos = wcsrchr(strValue, L'\\' /* WIDE_PATH_SEPARATOR */);

// Windows accepts both backslash and forward slash as path separators.
const wchar_t *strPos2 = wcsrchr(strValue, L'/');

// if strPos2 points to a character closer to the end of the string, use this instead of strPos
if ((strPos == NULL) || ((strPos2 != NULL) && (strPos2 > strPos))) strPos = strPos2;

/* path separator found? */
if (strPos == NULL)
{
Expand All @@ -618,11 +625,13 @@ OFFilename &OFStandard::getDirNameFromPath(OFFilename &result,
const char *strValue = pathName.getCharPointer();
const char *strPos = strrchr(strValue, PATH_SEPARATOR);

/* silently accept forward slash as alternative path separator. Windows meanwhile supports this. */
if (strPos == NULL && (PATH_SEPARATOR != '/'))
{
strPos = strrchr(strValue, '/');
}
#ifdef _WIN32
// Windows accepts both backslash and forward slash as path separators.
const char *strPos2 = strrchr(strValue, '/');

// if strPos2 points to a character closer to the end of the string, use this instead of strPos
if ((strPos == NULL) || ((strPos2 != NULL) && (strPos2 > strPos))) strPos = strPos2;
#endif

/* path separator found? */
if (strPos == NULL)
Expand Down Expand Up @@ -661,6 +670,13 @@ OFFilename &OFStandard::getFilenameFromPath(OFFilename &result,
{
const wchar_t *strValue = pathName.getWideCharPointer();
const wchar_t *strPos = wcsrchr(strValue, L'\\' /* WIDE_PATH_SEPARATOR */);

// Windows accepts both backslash and forward slash as path separators.
const wchar_t *strPos2 = wcsrchr(strValue, L'/');

// if strPos2 points to a character closer to the end of the string, use this instead of strPos
if ((strPos == NULL) || ((strPos2 != NULL) && (strPos2 > strPos))) strPos = strPos2;

/* path separator found? */
if (strPos == NULL)
{
Expand All @@ -680,6 +696,15 @@ OFFilename &OFStandard::getFilenameFromPath(OFFilename &result,
{
const char *strValue = pathName.getCharPointer();
const char *strPos = strrchr(strValue, PATH_SEPARATOR);

#ifdef _WIN32
// Windows accepts both backslash and forward slash as path separators.
const char *strPos2 = strrchr(strValue, '/');

// if strPos2 points to a character closer to the end of the string, use this instead of strPos
if ((strPos == NULL) || ((strPos2 != NULL) && (strPos2 > strPos))) strPos = strPos2;
#endif

/* path separator found? */
if (strPos == NULL)
{
Expand Down Expand Up @@ -719,7 +744,9 @@ OFFilename &OFStandard::normalizeDirName(OFFilename &result,
{
const wchar_t *strValue = dirName.getWideCharPointer();
size_t strLength = (strValue == NULL) ? 0 : wcslen(strValue);
while ((strLength > 1) && (strValue[strLength - 1] == L'\\' /* WIDE_PATH_SEPARATOR */))
// Windows accepts both backslash and forward slash as path separators.
while ((strLength > 1) && ((strValue[strLength - 1] == L'\\' /* WIDE_PATH_SEPARATOR */) ||
(strValue[strLength - 1] == L'/' )))
--strLength;
/* avoid "." as a directory name, use empty string instead */
if (allowEmptyDirName && ((strLength == 0) || ((strLength == 1) && (strValue[0] == L'.'))))
Expand All @@ -741,8 +768,15 @@ OFFilename &OFStandard::normalizeDirName(OFFilename &result,
{
const char *strValue = dirName.getCharPointer();
size_t strLength = (strValue == NULL) ? 0 : strlen(strValue);
#ifdef _WIN32
// Windows accepts both backslash and forward slash as path separators.
while ((strLength > 1) && ((strValue[strLength - 1] == PATH_SEPARATOR) ||
(strValue[strLength - 1] == '/' )))
--strLength;
#else
while ((strLength > 1) && (strValue[strLength - 1] == PATH_SEPARATOR))
--strLength;
#endif
/* avoid "." as a directory name, use empty string instead */
if (allowEmptyDirName && ((strLength == 0) || ((strLength == 1) && (strValue[0] == '.'))))
result.clear();
Expand Down Expand Up @@ -786,7 +820,8 @@ OFFilename &OFStandard::combineDirAndFilename(OFFilename &result,
size_t strLength = (strValue == NULL) ? 0 : wcslen(strValue);
/* check whether 'fileName' contains absolute path */
/* (this check also covers UNC syntax, e.g. "\\server\...") */
if ((strLength > 0) && (strValue[0] == L'\\' /* WIDE_PATH_SEPARATOR */))
// Windows accepts both backslash and forward slash as path separators.
if ((strLength > 0) && ((strValue[0] == L'\\' /* WIDE_PATH_SEPARATOR */) || (strValue[0] == L'/')))
{
result.set(strValue, OFTrue /*convert*/);
return result;
Expand All @@ -798,7 +833,8 @@ OFFilename &OFStandard::combineDirAndFilename(OFFilename &result,
const wchar_t c = strValue[0];
if (((c >= L'A') && (c <= L'Z')) || ((c >= L'a') && (c <= L'z')))
{
if ((strValue[1] == L':') && (strValue[2] == L'\\' /* WIDE_PATH_SEPARATOR */))
// Windows accepts both backslash and forward slash as path separators.
if ((strValue[1] == L':') && ((strValue[2] == L'\\' /* WIDE_PATH_SEPARATOR */))||(strValue[2] == L'/'))
{
result.set(strValue, OFTrue /*convert*/);
return result;
Expand Down Expand Up @@ -827,7 +863,8 @@ OFFilename &OFStandard::combineDirAndFilename(OFFilename &result,
wchar_t *tmpString = new wchar_t[strLength + resLength + 1 + 1];
wcscpy(tmpString, resValue);
/* add path separator (if required) ... */
if (resValue[resLength - 1] != L'\\' /* WIDE_PATH_SEPARATOR */)
// Windows accepts both backslash and forward slash as path separators.
if ((resValue[resLength - 1] != L'\\' /* WIDE_PATH_SEPARATOR */) && (resValue[resLength - 1] != L'/'))
{
tmpString[resLength] = L'\\' /* WIDE_PATH_SEPARATOR */;
tmpString[resLength + 1] = L'\0';
Expand All @@ -846,7 +883,12 @@ OFFilename &OFStandard::combineDirAndFilename(OFFilename &result,
size_t strLength = (strValue == NULL) ? 0 : strlen(strValue);
/* check whether 'fileName' contains absolute path */
/* (this check also covers UNC syntax, e.g. "\\server\...") */
#ifdef _WIN32
// Windows accepts both backslash and forward slash as path separators.
if ((strLength > 0) && ((strValue[0] == PATH_SEPARATOR) || (strValue[0] == '/')))
#else
if ((strLength > 0) && (strValue[0] == PATH_SEPARATOR))
#endif
{
result.set(strValue);
return result;
Expand All @@ -858,7 +900,7 @@ OFFilename &OFStandard::combineDirAndFilename(OFFilename &result,
const char c = strValue[0];
if (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')))
{
if ((strValue[1] == ':') && (strValue[2] == '\\'))
if ((strValue[1] == ':') && ((strValue[2] == '\\') || (strValue[2] == '/')))
{
result.set(strValue);
return result;
Expand All @@ -881,7 +923,12 @@ OFFilename &OFStandard::combineDirAndFilename(OFFilename &result,
char *tmpString = new char[buflen];
OFStandard::strlcpy(tmpString, resValue, buflen);
/* add path separator (if required) ... */
#ifdef _WIN32
// Windows accepts both backslash and forward slash as path separators.
if ((resValue[resLength - 1] != PATH_SEPARATOR) && (resValue[resLength - 1] != '/'))
#else
if (resValue[resLength - 1] != PATH_SEPARATOR)
#endif
{
tmpString[resLength] = PATH_SEPARATOR;
tmpString[resLength + 1] = '\0';
Expand Down Expand Up @@ -934,7 +981,7 @@ OFCondition OFStandard::removeRootDirFromPathname(OFFilename &result,
/* remove root dir prefix from path name */
wcscpy(tmpString, pathValue + rootLength);
/* remove leading path separator (if present) */
if (!allowLeadingPathSeparator && (tmpString[0] == PATH_SEPARATOR))
if (!allowLeadingPathSeparator && ((tmpString[0] == PATH_SEPARATOR) || (tmpString[0] == '/')))
result.set(tmpString + 1, OFTrue /*convert*/);
else
result.set(tmpString, OFTrue /*convert*/);
Expand Down Expand Up @@ -974,7 +1021,12 @@ OFCondition OFStandard::removeRootDirFromPathname(OFFilename &result,
/* remove root dir prefix from path name */
OFStandard::strlcpy(tmpString, pathValue + rootLength, buflen);
/* remove leading path separator (if present) */
#ifdef _WIN32
// Windows accepts both backslash and forward slash as path separators.
if (!allowLeadingPathSeparator && ((tmpString[0] == PATH_SEPARATOR) || (tmpString[0] == '/')))
#else
if (!allowLeadingPathSeparator && (tmpString[0] == PATH_SEPARATOR))
#endif
result.set(tmpString + 1);
else
result.set(tmpString);
Expand Down Expand Up @@ -1242,12 +1294,22 @@ OFCondition OFStandard::createDirectory(const OFFilename &dirName,
size_t rootLength = (rootValue == NULL) ? 0 : wcslen(rootValue);
/* check for absolute path containing Windows drive name, e. g. "c:\",
* is not required since the root directory should always exist */
#ifdef _WIN32
// Windows accepts both backslash and forward slash as path separators.
if ((dirLength > 1) && ((dirValue[dirLength - 1] == L'\\' /* WIDE_PATH_SEPARATOR */) || (dirValue[dirLength - 1] == L'/')))
#else
if ((dirLength > 1) && (dirValue[dirLength - 1] == L'\\' /* WIDE_PATH_SEPARATOR */))
#endif
{
/* ignore trailing path separator */
--dirLength;
}
#ifdef _WIN32
// Windows accepts both backslash and forward slash as path separators.
if ((rootLength > 1) && ((rootValue[rootLength - 1] == L'\\' /* WIDE_PATH_SEPARATOR */) || (rootValue[rootLength - 1] == L'/')))
#else
if ((rootLength > 1) && (rootValue[rootLength - 1] == L'\\' /* WIDE_PATH_SEPARATOR */))
#endif
{
/* ignore trailing path separator */
--rootLength;
Expand All @@ -1271,7 +1333,13 @@ OFCondition OFStandard::createDirectory(const OFFilename &dirName,
/* search for next path separator */
do {
++pos;
#ifdef _WIN32
// Windows accepts both backslash and forward slash as path separators.
} while ((dirValue[pos] != L'\\' /* WIDE_PATH_SEPARATOR */) && (dirValue[pos] != L'/') && (dirValue[pos] != '\0'));
#else
} while ((dirValue[pos] != L'\\' /* WIDE_PATH_SEPARATOR */) && (dirValue[pos] != L'\0'));
#endif

/* get name of current directory component */
wchar_t *subDir = new wchar_t[pos + 1];
wcsncpy(subDir, dirValue, pos /*num*/);
Expand Down Expand Up @@ -1303,12 +1371,22 @@ OFCondition OFStandard::createDirectory(const OFFilename &dirName,
size_t rootLength = (rootValue == NULL) ? 0 : strlen(rootValue);
/* check for absolute path containing Windows drive name, e. g. "c:\",
* is not required since the root directory should always exist */
#ifdef _WIN32
// Windows accepts both backslash and forward slash as path separators.
if ((dirLength > 1) && ((dirValue[dirLength - 1] == PATH_SEPARATOR) || (dirValue[dirLength - 1] == '/')))
#else
if ((dirLength > 1) && (dirValue[dirLength - 1] == PATH_SEPARATOR))
#endif
{
/* ignore trailing path separator */
--dirLength;
}
#ifdef _WIN32
// Windows accepts both backslash and forward slash as path separators.
if ((rootLength > 1) && ((rootValue[rootLength - 1] == PATH_SEPARATOR) || (rootValue[rootLength - 1] == '/')))
#else
if ((rootLength > 1) && (rootValue[rootLength - 1] == PATH_SEPARATOR))
#endif
{
/* ignore trailing path separator */
--rootLength;
Expand All @@ -1332,7 +1410,12 @@ OFCondition OFStandard::createDirectory(const OFFilename &dirName,
/* search for next path separator */
do {
++pos;
#ifdef _WIN32
// Windows accepts both backslash and forward slash as path separators.
} while ((dirValue[pos] != PATH_SEPARATOR) && (dirValue[pos] != '/') && (dirValue[pos] != '\0'));
#else
} while ((dirValue[pos] != PATH_SEPARATOR) && (dirValue[pos] != '\0'));
#endif
/* get name of current directory component */
char *subDir = new char[pos + 1];
strlcpy(subDir, dirValue, pos + 1 /*size*/);
Expand Down
Loading

0 comments on commit 52a6b76

Please sign in to comment.