Skip to content

Commit

Permalink
CPLGetPath()/CPLGetDirname(): make them work with /vsicurl? and URL e…
Browse files Browse the repository at this point in the history
…ncoded

Fixes #11467
  • Loading branch information
rouault committed Dec 10, 2024
1 parent 5c222aa commit 3d51c0e
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 6 deletions.
12 changes: 12 additions & 0 deletions autotest/cpp/test_cpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1059,6 +1059,12 @@ TEST_F(test_cpl, CPLGetPath)
EXPECT_STREQ(CPLGetPath("/foo/bar"), "/foo");
EXPECT_STREQ(CPLGetPath("/vsicurl/http://example.com/foo/bar?suffix"),
"/vsicurl/http://example.com/foo?suffix");
EXPECT_STREQ(
CPLGetPath(
"/vsicurl?foo=bar&url=https%3A%2F%2Fraw.githubusercontent.com%"
"2FOSGeo%2Fgdal%2Fmaster%2Fautotest%2Fogr%2Fdata%2Fpoly.shp"),
"/vsicurl?foo=bar&url=https%3A%2F%2Fraw.githubusercontent.com%2FOSGeo%"
"2Fgdal%2Fmaster%2Fautotest%2Fogr%2Fdata");
}

TEST_F(test_cpl, CPLGetDirname)
Expand All @@ -1067,6 +1073,12 @@ TEST_F(test_cpl, CPLGetDirname)
EXPECT_STREQ(CPLGetDirname("/foo/bar"), "/foo");
EXPECT_STREQ(CPLGetDirname("/vsicurl/http://example.com/foo/bar?suffix"),
"/vsicurl/http://example.com/foo?suffix");
EXPECT_STREQ(
CPLGetDirname(
"/vsicurl?foo=bar&url=https%3A%2F%2Fraw.githubusercontent.com%"
"2FOSGeo%2Fgdal%2Fmaster%2Fautotest%2Fogr%2Fdata%2Fpoly.shp"),
"/vsicurl?foo=bar&url=https%3A%2F%2Fraw.githubusercontent.com%2FOSGeo%"
"2Fgdal%2Fmaster%2Fautotest%2Fogr%2Fdata");
}

TEST_F(test_cpl, VSIGetDiskFreeSpace)
Expand Down
29 changes: 29 additions & 0 deletions autotest/ogr/ogr_shape.py
Original file line number Diff line number Diff line change
Expand Up @@ -1745,6 +1745,35 @@ def test_ogr_shape_44():
assert f is not None, "did not get expected feature"


###############################################################################
# Test /vsicurl?url=


@pytest.mark.require_curl()
def test_ogr_shape_vsicurl_url():

conn = gdaltest.gdalurlopen(
"https://raw.githubusercontent.com/OSGeo/gdal/release/3.10/autotest/ogr/data/poly.shp"
)
if conn is None:
pytest.skip("cannot open URL")
conn.close()

ds = ogr.Open(
"/vsicurl?url=https%3A%2F%2Fraw.githubusercontent.com%2FOSGeo%2Fgdal%2Frelease%2F3.10%2Fautotest%2Fogr%2Fdata%2Fpoly.shp"
)
assert ds is not None

lyr = ds.GetLayer(0)

srs = lyr.GetSpatialRef()
wkt = srs.ExportToWkt()
assert wkt.find("OSGB") != -1, "did not get expected SRS"

f = lyr.GetNextFeature()
assert f is not None, "did not get expected feature"


###############################################################################
# Test ignored fields works ok on a shapefile.

Expand Down
81 changes: 75 additions & 6 deletions port/cpl_path.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,18 +128,52 @@ static int CPLFindFilenameStart(const char *pszFilename, size_t nStart = 0)
const char *CPLGetPath(const char *pszFilename)

{
char *pszStaticResult = CPLGetStaticResult();
if (pszStaticResult == nullptr)
return CPLStaticBufferTooSmall(pszStaticResult);

size_t nSuffixPos = 0;
if (STARTS_WITH(pszFilename, "/vsicurl/http"))
{
const char *pszQuestionMark = strchr(pszFilename, '?');
if (pszQuestionMark)
nSuffixPos = static_cast<size_t>(pszQuestionMark - pszFilename);
}
else if (STARTS_WITH(pszFilename, "/vsicurl?") &&
strstr(pszFilename, "url="))
{
std::string osRet;
const CPLStringList aosTokens(
CSLTokenizeString2(pszFilename + strlen("/vsicurl?"), "&", 0));
for (int i = 0; i < aosTokens.size(); i++)
{
if (osRet.empty())
osRet = "/vsicurl?";
else
osRet += '&';
if (STARTS_WITH(aosTokens[i], "url=") &&
!STARTS_WITH(aosTokens[i], "url=/vsicurl"))
{
char *pszUnescaped =
CPLUnescapeString(aosTokens[i], nullptr, CPLES_URL);
char *pszPath = CPLEscapeString(
CPLGetPath(pszUnescaped + strlen("url=")), -1, CPLES_URL);
osRet += "url=";
osRet += pszPath;
CPLFree(pszPath);
CPLFree(pszUnescaped);
}
else
{
osRet += aosTokens[i];
}
}
CPLStrlcpy(pszStaticResult, osRet.c_str(), CPL_PATH_BUF_SIZE);
return pszStaticResult;
}

const int iFileStart = CPLFindFilenameStart(pszFilename, nSuffixPos);
char *pszStaticResult = CPLGetStaticResult();

if (pszStaticResult == nullptr || iFileStart >= CPL_PATH_BUF_SIZE)
if (iFileStart >= CPL_PATH_BUF_SIZE)
return CPLStaticBufferTooSmall(pszStaticResult);

CPLAssert(!(pszFilename >= pszStaticResult &&
Expand Down Expand Up @@ -197,18 +231,53 @@ const char *CPLGetPath(const char *pszFilename)
const char *CPLGetDirname(const char *pszFilename)

{
char *pszStaticResult = CPLGetStaticResult();
if (pszStaticResult == nullptr)
return CPLStaticBufferTooSmall(pszStaticResult);

size_t nSuffixPos = 0;
if (STARTS_WITH(pszFilename, "/vsicurl/http"))
{
const char *pszQuestionMark = strchr(pszFilename, '?');
if (pszQuestionMark)
nSuffixPos = static_cast<size_t>(pszQuestionMark - pszFilename);
}
else if (STARTS_WITH(pszFilename, "/vsicurl?") &&
strstr(pszFilename, "url="))
{
std::string osRet;
const CPLStringList aosTokens(
CSLTokenizeString2(pszFilename + strlen("/vsicurl?"), "&", 0));
for (int i = 0; i < aosTokens.size(); i++)
{
if (osRet.empty())
osRet = "/vsicurl?";
else
osRet += '&';
if (STARTS_WITH(aosTokens[i], "url=") &&
!STARTS_WITH(aosTokens[i], "url=/vsicurl"))
{
char *pszUnescaped =
CPLUnescapeString(aosTokens[i], nullptr, CPLES_URL);
char *pszPath = CPLEscapeString(
CPLGetDirname(pszUnescaped + strlen("url=")), -1,
CPLES_URL);
osRet += "url=";
osRet += pszPath;
CPLFree(pszPath);
CPLFree(pszUnescaped);
}
else
{
osRet += aosTokens[i];
}
}
CPLStrlcpy(pszStaticResult, osRet.c_str(), CPL_PATH_BUF_SIZE);
return pszStaticResult;
}

const int iFileStart = CPLFindFilenameStart(pszFilename, nSuffixPos);
char *pszStaticResult = CPLGetStaticResult();

if (pszStaticResult == nullptr || iFileStart >= CPL_PATH_BUF_SIZE)
if (iFileStart >= CPL_PATH_BUF_SIZE)
return CPLStaticBufferTooSmall(pszStaticResult);

CPLAssert(!(pszFilename >= pszStaticResult &&
Expand Down

0 comments on commit 3d51c0e

Please sign in to comment.