Skip to content

Commit

Permalink
/vsizip / /vsitar: support alternate syntax /vsitar/{/path/to/the/arc…
Browse files Browse the repository at this point in the history
…hive}/path/inside/the/tar/file so as not to be dependant on file extension and enable chaining

git-svn-id: https://svn.osgeo.org/gdal/trunk@35126 f0d54148-0727-0410-94bb-9a71ac55c965
  • Loading branch information
rouault committed Aug 16, 2016
1 parent f5ff12d commit b6e4c32
Show file tree
Hide file tree
Showing 4 changed files with 201 additions and 23 deletions.
80 changes: 79 additions & 1 deletion autotest/gcore/vsizip.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,90 @@ def vsizip_1():
data = gdal.VSIFReadL(1, 4, f)
gdal.VSIFCloseL(f)

gdal.Unlink("/vsimem/test.zip")
if data.decode('ASCII') != 'abcd':
gdaltest.post_reason('fail')
print(data)
return 'fail'

# Test alternate uri syntax
gdal.Rename("/vsimem/test.zip", "/vsimem/test.xxx")
f= gdal.VSIFOpenL("/vsizip/{/vsimem/test.xxx}/subdir3/abcd", "rb")
if f is None:
gdaltest.post_reason('fail')
return 'fail'
data = gdal.VSIFReadL(1, 4, f)
gdal.VSIFCloseL(f)

if data.decode('ASCII') != 'abcd':
gdaltest.post_reason('fail')
print(data)
return 'fail'

# With a trailing slash
f= gdal.VSIFOpenL("/vsizip/{/vsimem/test.xxx}/subdir3/abcd/", "rb")
if f is None:
gdaltest.post_reason('fail')
return 'fail'
gdal.VSIFCloseL(f)

# Test ReadDir()
if len(gdal.ReadDir("/vsizip/{/vsimem/test.xxx}")) != 3:
gdaltest.post_reason('fail')
print(gdal.ReadDir("/vsizip/{/vsimem/test.xxx}"))
return 'fail'

# Unbalanced curls
f = gdal.VSIFOpenL("/vsizip/{/vsimem/test.xxx", "rb")
if f is not None:
gdaltest.post_reason('fail')
return 'fail'

# Non existing mainfile
f = gdal.VSIFOpenL("/vsizip/{/vsimem/test.xxx}/bla", "rb")
if f is not None:
gdaltest.post_reason('fail')
return 'fail'

# Non existing subfile
f= gdal.VSIFOpenL("/vsizip/{/vsimem/test.zzz}/bla", "rb")
if f is not None:
gdaltest.post_reason('fail')
return 'fail'

# Wrong syntax
f= gdal.VSIFOpenL("/vsizip/{/vsimem/test.xxx}.aux.xml", "rb")
if f is not None:
gdaltest.post_reason('fail')
return 'fail'

# Test nested { { } }
hZIP = gdal.VSIFOpenL("/vsizip/{/vsimem/zipinzip.yyy}", "wb")
if hZIP is None:
gdaltest.post_reason('fail 1')
return 'fail'
f = gdal.VSIFOpenL("/vsizip/{/vsimem/zipinzip.yyy}/test.xxx", "wb")
f_src = gdal.VSIFOpenL("/vsimem/test.xxx", "rb")
data = gdal.VSIFReadL(1, 10000, f_src)
gdal.VSIFCloseL(f_src)
gdal.VSIFWriteL(data, 1, len(data), f)
gdal.VSIFCloseL(f)
gdal.VSIFCloseL(hZIP)

f= gdal.VSIFOpenL("/vsizip/{/vsizip/{/vsimem/zipinzip.yyy}/test.xxx}/subdir3/abcd/", "rb")
if f is None:
gdaltest.post_reason('fail')
return 'fail'
data = gdal.VSIFReadL(1, 4, f)
gdal.VSIFCloseL(f)

if data.decode('ASCII') != 'abcd':
gdaltest.post_reason('fail')
print(data)
return 'fail'

gdal.Unlink("/vsimem/test.xxx")
gdal.Unlink("/vsimem/zipinzip.yyy")

return 'success'

###############################################################################
Expand Down
132 changes: 111 additions & 21 deletions gdal/port/cpl_vsil_abstract_archive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,36 @@ int VSIArchiveFilesystemHandler::FindFileInArchive(const char* archiveFilename,
return FALSE;
}

/************************************************************************/
/* CompactFilename() */
/************************************************************************/

static CPLString CompactFilename(const char* pszArchiveInFileNameIn)
{
char* pszArchiveInFileName = CPLStrdup(pszArchiveInFileNameIn);

/* Replace a/../b by b and foo/a/../b by foo/b */
while(true)
{
char* pszPrevDir = strstr(pszArchiveInFileName, "/../");
if (pszPrevDir == NULL || pszPrevDir == pszArchiveInFileName)
break;

char* pszPrevSlash = pszPrevDir - 1;
while(pszPrevSlash != pszArchiveInFileName &&
*pszPrevSlash != '/')
pszPrevSlash --;
if (pszPrevSlash == pszArchiveInFileName)
memmove(pszArchiveInFileName, pszPrevDir + 4, strlen(pszPrevDir + 4) + 1);
else
memmove(pszPrevSlash + 1, pszPrevDir + 4, strlen(pszPrevDir + 4) + 1);
}

CPLString osFileInArchive = pszArchiveInFileName;
CPLFree(pszArchiveInFileName);
return osFileInArchive;
}

/************************************************************************/
/* SplitFilename() */
/************************************************************************/
Expand All @@ -277,6 +307,86 @@ char* VSIArchiveFilesystemHandler::SplitFilename(const char *pszFilename,
if (strcmp(pszFilename, GetPrefix()) == 0)
return NULL;

// Detect extended syntax: /vsiXXX/{archive_filename}/file_in_archive
if( pszFilename[strlen(GetPrefix())+ 1] == '{' )
{
pszFilename += strlen(GetPrefix()) + 1;
int nCountCurles = 0;
while(pszFilename[i])
{
if( pszFilename[i] == '{' )
nCountCurles ++;
else if( pszFilename[i] == '}' )
{
nCountCurles --;
if( nCountCurles == 0 )
break;
}
i ++;
}
if( nCountCurles > 0 )
return NULL;
char* archiveFilename = CPLStrdup(pszFilename + 1);
archiveFilename[i - 1] = 0;

bool bArchiveFileExists = false;
if (!bCheckMainFileExists)
{
bArchiveFileExists = true;
}
else
{
CPLMutexHolder oHolder( &hMutex );

if (oFileList.find(archiveFilename) != oFileList.end() )
{
bArchiveFileExists = true;
}
}

if (!bArchiveFileExists)
{
VSIStatBufL statBuf;
VSIFilesystemHandler *poFSHandler =
VSIFileManager::GetHandler( archiveFilename );
if (poFSHandler->Stat(archiveFilename, &statBuf,
VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG) == 0 &&
!VSI_ISDIR(statBuf.st_mode))
{
bArchiveFileExists = true;
}
}

if (bArchiveFileExists)
{
if (pszFilename[i + 1] == '/' ||
pszFilename[i + 1] == '\\')
{
osFileInArchive = CompactFilename(pszFilename + i + 2);
}
else if( pszFilename[i+1] == '\0' )
osFileInArchive = "";
else
{
CPLFree(archiveFilename);
return NULL;
}

/* Remove trailing slash */
if (osFileInArchive.size())
{
char lastC = osFileInArchive[strlen(osFileInArchive) - 1];
if (lastC == '\\' || lastC == '/')
osFileInArchive.resize(strlen(osFileInArchive) - 1);
}

return archiveFilename;
}

CPLFree(archiveFilename);
return NULL;
}

/* Allow natural chaining of VSI drivers without requiring double slash */

CPLString osDoubleVsi(GetPrefix());
Expand Down Expand Up @@ -354,27 +464,7 @@ char* VSIArchiveFilesystemHandler::SplitFilename(const char *pszFilename,
if (pszFilename[i + nToSkip] == '/' ||
pszFilename[i + nToSkip] == '\\')
{
char* pszArchiveInFileName = CPLStrdup(pszFilename + i + nToSkip + 1);

/* Replace a/../b by b and foo/a/../b by foo/b */
while(true)
{
char* pszPrevDir = strstr(pszArchiveInFileName, "/../");
if (pszPrevDir == NULL || pszPrevDir == pszArchiveInFileName)
break;

char* pszPrevSlash = pszPrevDir - 1;
while(pszPrevSlash != pszArchiveInFileName &&
*pszPrevSlash != '/')
pszPrevSlash --;
if (pszPrevSlash == pszArchiveInFileName)
memmove(pszArchiveInFileName, pszPrevDir + nToSkip, strlen(pszPrevDir + nToSkip) + 1);
else
memmove(pszPrevSlash + 1, pszPrevDir + nToSkip, strlen(pszPrevDir + nToSkip) + 1);
}

osFileInArchive = pszArchiveInFileName;
CPLFree(pszArchiveInFileName);
osFileInArchive = CompactFilename(pszFilename + i + nToSkip + 1);
}
else
osFileInArchive = "";
Expand Down
5 changes: 5 additions & 0 deletions gdal/port/cpl_vsil_gzip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2539,6 +2539,11 @@ void VSIZipWriteHandle::StartNewFile(VSIZipWriteHandle* poSubFile)
* were path/to/the/file.zip is relative or absolute and path/inside/the/zip/file
* is the relative path to the file inside the archive.
*
* Starting with GDAL 2.2, an alternate syntax is available so as to enable
* chaining and not being dependant on .zip extension :
* /vsitar/{/path/to/the/archive}/path/inside/the/zip/file. Note that /path/to/the/archive
* may also itself this alternate syntax.
*
* If the path is absolute, it should begin with a / on a Unix-like OS (or C:\ on Windows),
* so the line looks like /vsizip//home/gdal/...
* For example gdalinfo /vsizip/myarchive.zip/subdir1/file1.tif
Expand Down
7 changes: 6 additions & 1 deletion gdal/port/cpl_vsil_tar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -476,10 +476,15 @@ VSIVirtualHandle* VSITarFilesystemHandler::Open( const char *pszFilename,
* All portions of the file system underneath the base path "/vsitar/" will be
* handled by this driver.
*
* The syntax to open a file inside a zip file is /vsitar/path/to/the/file.tar/path/inside/the/tar/file
* The syntax to open a file inside a tar file is /vsitar/path/to/the/file.tar/path/inside/the/tar/file
* were path/to/the/file.tar is relative or absolute and path/inside/the/tar/file
* is the relative path to the file inside the archive.
*
* Starting with GDAL 2.2, an alternate syntax is available so as to enable
* chaining and not being dependant on .tar extension :
* /vsitar/{/path/to/the/archive}/path/inside/the/tar/file. Note that /path/to/the/archive
* may also itself this alternate syntax.
*
* If the path is absolute, it should begin with a / on a Unix-like OS (or C:\ on Windows),
* so the line looks like /vsitar//home/gdal/...
* For example gdalinfo /vsitar/myarchive.tar/subdir1/file1.tif
Expand Down

0 comments on commit b6e4c32

Please sign in to comment.