Merge pull request #3426 from bparker06/hash_filename

correctly handle content paths containing a hash (#), fixes issue #3273
This commit is contained in:
Twinaphex 2016-08-26 17:20:43 +02:00 committed by GitHub
commit 7edf762ac6
5 changed files with 167 additions and 42 deletions

View File

@ -37,6 +37,7 @@
#include <retro_assert.h>
#include <retro_stat.h>
#include <retro_miscellaneous.h>
#include <string/stdstring.h>
/**
* path_mkdir:
@ -84,6 +85,43 @@ end:
return ret;
}
/**
* path_get_archive_delim:
* @path : path
*
* Find delimiter of an archive file. Only the first '#'
* after a compression extension is considered.
*
* Returns: pointer to the delimiter in the path if it contains
* a compressed file, otherwise NULL.
*/
const char *path_get_archive_delim(const char *path)
{
#ifdef HAVE_COMPRESSION
const char *last = find_last_slash(path);
char *delim = NULL;
#ifdef HAVE_ZLIB
if (last)
delim = strcasestr(last, ".zip#");
if (delim)
return delim + 4;
#endif
#ifdef HAVE_7ZIP
if (last)
delim = strcasestr(last, ".7z#");
if (delim)
return delim + 3;
#endif
#endif
return NULL;
}
/**
* path_get_extension:
* @path : path
@ -127,8 +165,9 @@ char *path_remove_extension(char *path)
*
* Checks if path contains a compressed file.
*
* Currently we only check for hash symbol (#) inside the pathname.
* If path is ever expanded to a general URI, we should check for that here.
* Currently we only check for a hash symbol (#) inside the pathname
* that is preceded by an archive extension. If path is ever expanded
* to a general URI, we should check for that here.
*
* Example: Somewhere in the path there might be a compressed file
* E.g.: /path/to/file.7z#mygame.img
@ -137,7 +176,7 @@ char *path_remove_extension(char *path)
**/
bool path_contains_compressed_file(const char *path)
{
return (strchr(path, '#') != NULL);
return path_get_archive_delim(path) != NULL;
}
/**
@ -150,20 +189,21 @@ bool path_contains_compressed_file(const char *path)
**/
bool path_is_compressed_file(const char* path)
{
#if defined(HAVE_COMPRESSION) && (defined(HAVE_ZLIB) || defined(HAVE_7ZIP))
const char* file_ext = path_get_extension(path);
#ifdef HAVE_COMPRESSION
const char *ext = path_get_extension(path);
#ifdef HAVE_ZLIB
if (!strcmp(file_ext, "zip"))
if (strcasestr(ext, "zip"))
return true;
#endif
#ifdef HAVE_7ZIP
if (!strcmp(file_ext, "7z"))
if (strcasestr(ext, "zip"))
return true;
#endif
#endif
return false;
}
@ -317,31 +357,11 @@ void fill_pathname_dir(char *in_dir, const char *in_basename,
**/
void fill_pathname_base(char *out, const char *in_path, size_t size)
{
const char *ptr_bak = NULL;
const char *ptr = find_last_slash(in_path);
const char *ptr = path_basename(in_path);
(void)ptr_bak;
if (ptr)
ptr++;
else
if (!ptr)
ptr = in_path;
#ifdef HAVE_COMPRESSION
/* In case of compression, we also have to consider paths like
* /path/to/archive.7z#mygame.img
* and
* /path/to/archive.7z#folder/mygame.img
* basename would be mygame.img in both cases
*/
ptr_bak = ptr;
ptr = strchr(ptr_bak,'#');
if (ptr)
ptr++;
else
ptr = ptr_bak;
#endif
retro_assert(strlcpy(out, ptr, size) < size);
}
@ -462,7 +482,7 @@ void path_basedir(char *path)
#ifdef HAVE_COMPRESSION
/* We want to find the directory with the zipfile in basedir. */
last = strchr(path,'#');
last = path + (path_get_archive_delim(path) - path);
if (last)
*last = '\0';
#endif
@ -500,19 +520,19 @@ void path_parent_dir(char *path)
**/
const char *path_basename(const char *path)
{
/* We cut either at the first compression-related hash or the last slash; whichever comes last */
const char *last = find_last_slash(path);
#ifdef HAVE_COMPRESSION
const char *last_hash = NULL;
const char *delim = path_get_archive_delim(path);
/* We cut either at the last hash or the last slash; whichever comes last */
last_hash = strchr(path,'#');
if (last_hash > last)
return last_hash + 1;
if (delim)
return delim + 1;
#endif
if (last)
return last + 1;
return path;
}
@ -703,15 +723,15 @@ void fill_short_pathname_representation(char* out_rep,
{
char path_short[PATH_MAX_LENGTH] = {0};
#ifdef HAVE_COMPRESSION
char *last_hash = NULL;
char *last_slash = NULL;
#endif
fill_pathname(path_short, path_basename(in_path), "",
sizeof(path_short));
#ifdef HAVE_COMPRESSION
last_hash = (char*)strchr(path_short,'#');
if(last_hash != NULL)
last_slash = find_last_slash(path_short);
if(last_slash != NULL)
{
/* We handle paths like:
* /path/to/file.7z#mygame.img
@ -720,8 +740,8 @@ void fill_short_pathname_representation(char* out_rep,
* We check whether something is actually
* after the hash to avoid going over the buffer.
*/
retro_assert(strlen(last_hash) > 1);
strlcpy(out_rep, last_hash + 1, size);
retro_assert(strlen(last_slash) > 1);
strlcpy(out_rep, last_slash + 1, size);
}
else
#endif

View File

@ -84,6 +84,18 @@ bool path_contains_compressed_file(const char *path);
*/
bool path_file_exists(const char *path);
/**
* path_get_archive_delim:
* @path : path
*
* Gets delimiter of an archive file. Only the first '#'
* after a compression extension is considered.
*
* Returns: pointer to the delimiter in the path if it contains
* a compressed file, otherwise NULL.
*/
const char *path_get_archive_delim(const char *path);
/**
* path_get_extension:
* @path : path

View File

@ -109,6 +109,20 @@ struct string_list *string_list_new(void);
bool string_list_append(struct string_list *list, const char *elem,
union string_list_elem_attr attr);
/**
* string_list_append_n:
* @list : pointer to string list
* @elem : element to add to the string list
* @length : read at most this many bytes from elem
* @attr : attributes of new element.
*
* Appends a new element to the string list.
*
* Returns: true (1) if successful, otherwise false (0).
**/
bool string_list_append_n(struct string_list *list, const char *elem,
unsigned length, union string_list_elem_attr attr);
/**
* string_list_free
* @list : pointer to string list object

View File

@ -130,6 +130,40 @@ bool string_list_append(struct string_list *list, const char *elem,
return true;
}
/**
* string_list_append_n:
* @list : pointer to string list
* @elem : element to add to the string list
* @length : read at most this many bytes from elem
* @attr : attributes of new element.
*
* Appends a new element to the string list.
*
* Returns: true (1) if successful, otherwise false (0).
**/
bool string_list_append_n(struct string_list *list, const char *elem,
unsigned length, union string_list_elem_attr attr)
{
char *data_dup = NULL;
if (list->size >= list->cap &&
!string_list_capacity(list, list->cap * 2))
return false;
data_dup = (char*)malloc(length + 1);
if (!data_dup)
return false;
strlcpy(data_dup, elem, length + 1);
list->elems[list->size].data = data_dup;
list->elems[list->size].attr = attr;
list->size++;
return true;
}
/**
* string_list_set:
* @list : pointer to string list

View File

@ -312,6 +312,51 @@ static int content_7zip_file_read(
return outsize;
}
/**
* filename_split_archive:
* @str : filename to turn into a string list
*
* Creates a new string list based on filename @path, delimited by a hash (#).
*
* Returns: new string list if successful, otherwise NULL.
*/
struct string_list *filename_split_archive(const char *path)
{
struct string_list *list = string_list_new();
union string_list_elem_attr attr;
memset(&attr, 0, sizeof(attr));
#ifdef HAVE_COMPRESSION
const char *delim = path_get_archive_delim(path);
if (delim)
{
// add archive path to list first
if (!string_list_append_n(list, path, delim - path, attr))
goto error;
// now add the path within the archive
delim++;
if (*delim)
{
if (!string_list_append(list, delim, attr))
goto error;
}
}
else
#endif
if (!string_list_append(list, path, attr))
goto error;
return list;
error:
string_list_free(list);
return NULL;
}
static struct string_list *compressed_7zip_file_list_new(
const char *path, const char* ext)
{
@ -621,7 +666,7 @@ static int content_file_compressed_read(
{
int ret = 0;
const char* file_ext = NULL;
struct string_list *str_list = string_split(path, "#");
struct string_list *str_list = filename_split_archive(path);
/* Safety check.
* If optional_filename and optional_filename