aboutsummaryrefslogtreecommitdiffstats
path: root/Alc
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2015-10-04 03:17:52 -0700
committerChris Robinson <[email protected]>2015-10-04 16:41:54 -0700
commit074983b19c4a838f1a26cfa5afb879808dfc2fd1 (patch)
treec62cfc974d818e467614f6997cbf01e74801bac2 /Alc
parent70fbc2b1ff254295338c6d42b41d9fdb43b02d8f (diff)
Implement SearchDataFiles for Windows
Diffstat (limited to 'Alc')
-rw-r--r--Alc/alstring.h2
-rw-r--r--Alc/helpers.c307
2 files changed, 298 insertions, 11 deletions
diff --git a/Alc/alstring.h b/Alc/alstring.h
index 5cbb6ac5..f53d2c57 100644
--- a/Alc/alstring.h
+++ b/Alc/alstring.h
@@ -41,6 +41,8 @@ void al_string_append_range(al_string *str, const al_string_char_type *from, con
#include <wchar.h>
/* Windows-only methods to deal with WideChar strings. */
void al_string_copy_wcstr(al_string *str, const wchar_t *from);
+void al_string_append_wcstr(al_string *str, const wchar_t *from);
+void al_string_append_wrange(al_string *str, const wchar_t *from, const wchar_t *to);
#endif
#endif /* ALSTRING_H */
diff --git a/Alc/helpers.c b/Alc/helpers.c
index 6355dd1e..3c0ee8c7 100644
--- a/Alc/helpers.c
+++ b/Alc/helpers.c
@@ -533,17 +533,265 @@ FILE *OpenDataFile(const char *fname, const char *subdir)
}
+static const WCHAR *strchrW(const WCHAR *str, WCHAR ch)
+{
+ for(;*str != 0;++str)
+ {
+ if(*str == ch)
+ return str;
+ }
+ return NULL;
+}
+
+static const WCHAR *strrchrW(const WCHAR *str, WCHAR ch)
+{
+ const WCHAR *ret = NULL;
+ for(;*str != 0;++str)
+ {
+ if(*str == ch)
+ ret = str;
+ }
+ return ret;
+}
+
+/* Compares the filename in the find data with the match string. The match
+ * string may contain the "%r" marker to signifiy a sample rate (really any
+ * positive integer), or "%%" to signify a single '%'.
+ */
+static int MatchFilter(const WCHAR *match, const WIN32_FIND_DATAW *fdata)
+{
+ const WCHAR *name = fdata->cFileName;
+ int ret = 1;
+
+ do {
+ const WCHAR *p = strchrW(match, '%');
+ if(!p)
+ ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE,
+ match, -1, name, -1) == CSTR_EQUAL;
+ else
+ {
+ size_t len = p-match;
+ ret = (size_t)lstrlenW(name) >= len;
+ if(ret)
+ ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE,
+ match, len, name, len) == CSTR_EQUAL;
+ if(ret)
+ {
+ match += len;
+ name += len;
+
+ ++p;
+ if(*p == 'r')
+ {
+ unsigned long l = 0;
+ while(*name >= '0' && *name <= '9')
+ {
+ l = l*10 + (*name-'0');
+ ++name;
+ }
+ ret = l > 0;
+ ++p;
+ }
+ }
+ }
+
+ match = p;
+ } while(ret && match && *match);
+
+ return ret;
+}
+
+static void RecurseDirectorySearch(const char *path, const WCHAR *match, vector_al_string *results)
+{
+ WIN32_FIND_DATAW fdata;
+ const WCHAR *sep, *p;
+ HANDLE hdl;
+
+ if(!match[0])
+ return;
+
+ /* Find the last directory separator and the next '%' marker in the match
+ * string. */
+ sep = strrchrW(match, '\\');
+ p = strchrW(match, '%');
+
+ /* If there's no separator, test the files in the specified path against
+ * the match string, and add the results. */
+ if(!sep)
+ {
+ al_string pathstr = AL_STRING_INIT_STATIC();
+ WCHAR *wpath;
+
+ TRACE("Searching %s for %ls\n", path, match);
+
+ al_string_append_cstr(&pathstr, path);
+ al_string_append_cstr(&pathstr, "\\*.*");
+ wpath = FromUTF8(al_string_get_cstr(pathstr));
+
+ hdl = FindFirstFileW(wpath, &fdata);
+ if(hdl != INVALID_HANDLE_VALUE)
+ {
+ do {
+ if(MatchFilter(match, &fdata))
+ {
+ al_string str = AL_STRING_INIT_STATIC();
+ al_string_copy_cstr(&str, path);
+ al_string_append_char(&str, '\\');
+ al_string_append_wcstr(&str, fdata.cFileName);
+ TRACE("Got result %s\n", al_string_get_cstr(str));
+ VECTOR_PUSH_BACK(*results, str);
+ }
+ } while(FindNextFileW(hdl, &fdata));
+ FindClose(hdl);
+ }
+
+ free(wpath);
+ al_string_deinit(&pathstr);
+
+ return;
+ }
+
+ /* If there's no '%' marker, or it's after the final separator, append the
+ * remaining directories to the path and recurse into it with the remaining
+ * filename portion. */
+ if(!p || p-sep >= 0)
+ {
+ al_string npath = AL_STRING_INIT_STATIC();
+ al_string_append_cstr(&npath, path);
+ al_string_append_char(&npath, '\\');
+ al_string_append_wrange(&npath, match, sep);
+
+ TRACE("Recursing into %s with %ls\n", al_string_get_cstr(npath), sep+1);
+ RecurseDirectorySearch(al_string_get_cstr(npath), sep+1, results);
+
+ al_string_deinit(&npath);
+ return;
+ }
+
+ /* Look for the last separator before the '%' marker, and the first
+ * separator after it. */
+ sep = strchrW(match, '\\');
+ if(sep-p >= 0) sep = NULL;
+ for(;;)
+ {
+ const WCHAR *next = strchrW(sep?sep+1:match, '\\');
+ if(next-p < 0)
+ {
+ al_string npath = AL_STRING_INIT_STATIC();
+ WCHAR *nwpath, *nwmatch;
+
+ /* Append up to the last directory before the one with a '%'. */
+ al_string_copy_cstr(&npath, path);
+ if(sep)
+ {
+ al_string_append_char(&npath, '\\');
+ al_string_append_wrange(&npath, match, sep);
+ }
+ al_string_append_cstr(&npath, "\\*.*");
+ nwpath = FromUTF8(al_string_get_cstr(npath));
+
+ /* Take the directory name containing a '%' as a new string to
+ * match against. */
+ if(!sep)
+ {
+ nwmatch = calloc(2, next-match+1);
+ memcpy(nwmatch, match, (next-match)*2);
+ }
+ else
+ {
+ nwmatch = calloc(2, next-(sep+1)+1);
+ memcpy(nwmatch, sep+1, (next-(sep+1))*2);
+ }
+
+ /* For each matching directory name, recurse into it with the
+ * remaining string. */
+ TRACE("Searching %s for %ls\n", al_string_get_cstr(npath), nwmatch);
+ hdl = FindFirstFileW(nwpath, &fdata);
+ if(hdl != INVALID_HANDLE_VALUE)
+ {
+ do {
+ if(MatchFilter(nwmatch, &fdata))
+ {
+ al_string ndir = AL_STRING_INIT_STATIC();
+ al_string_copy(&ndir, npath);
+ al_string_append_char(&ndir, '\\');
+ al_string_append_wcstr(&ndir, fdata.cFileName);
+ TRACE("Recursing %s with %ls\n", al_string_get_cstr(ndir), next+1);
+ RecurseDirectorySearch(al_string_get_cstr(ndir), next+1, results);
+ al_string_deinit(&ndir);
+ }
+ } while(FindNextFileW(hdl, &fdata));
+ FindClose(hdl);
+ }
+
+ free(nwmatch);
+ free(nwpath);
+ al_string_deinit(&npath);
+ break;
+ }
+ sep = next;
+ }
+}
+
vector_al_string SearchDataFiles(const char *match, const char *subdir)
{
+ static const int ids[2] = { CSIDL_APPDATA, CSIDL_COMMON_APPDATA };
static RefCount search_lock;
vector_al_string results = VECTOR_INIT_STATIC();
+ WCHAR *wmatch;
+ size_t i;
while(ATOMIC_EXCHANGE(uint, &search_lock, 1) == 1)
althrd_yield();
- ERR("FIXME\n");
+ wmatch = FromUTF8(match);
+ if(!wmatch)
+ {
+ ERR("Failed to convert UTF-8 filename: \"%s\"\n", match);
+ return results;
+ }
+ for(i = 0;wmatch[i];++i)
+ {
+ if(wmatch[i] == '/')
+ wmatch[i] = '\\';
+ }
+
+ /* If the path is absolute, use it directly. */
+ if(wmatch[0] != '\0' && wmatch[1] == ':' && is_slash(wmatch[2]))
+ {
+ CHAR drv[3] = { wmatch[0], ':', 0 };
+ RecurseDirectorySearch(drv, wmatch+3, &results);
+ }
+ else
+ {
+ /* Search the CWD. */
+ RecurseDirectorySearch(".", wmatch, &results);
- ATOMIC_LOAD(&search_lock, 0);
+ /* Search the local and global data dirs. */
+ for(i = 0;i < COUNTOF(ids);i++)
+ {
+ WCHAR buffer[PATH_MAX];
+ if(SHGetSpecialFolderPathW(NULL, buffer, ids[i], FALSE) != FALSE)
+ {
+ al_string path = AL_STRING_INIT_STATIC();
+ al_string_copy_wcstr(&path, buffer);
+ if(!is_slash(VECTOR_BACK(path)))
+ al_string_append_char(&path, '\\');
+ al_string_append_cstr(&path, subdir);
+#define FIX_SLASH(i) do { \
+ if(*(i) == '/') *(i) = '\\'; \
+} while(0)
+ VECTOR_FOR_EACH(char, path, FIX_SLASH);
+#undef FIX_SLASH
+
+ RecurseDirectorySearch(al_string_get_cstr(path), wmatch, &results);
+ al_string_deinit(&path);
+ }
+ }
+ }
+
+ free(wmatch);
+ ATOMIC_STORE(&search_lock, 0);
return results;
}
@@ -674,7 +922,7 @@ static int MatchFilter(const struct dirent *dir)
int ret = 1;
do {
- char *p = strchr(match, '%');
+ const char *p = strchr(match, '%');
if(!p)
ret = strcmp(match, name) == 0;
else
@@ -753,21 +1001,30 @@ static void RecurseDirectorySearch(const char *path, const char *match, vector_a
}
sep = strchr(match, '/');
- while(sep)
+ if(sep-p >= 0) sep = NULL;
+ for(;;)
{
- char *next = strchr(sep+1, '/');
+ char *next = strchr(sep?sep+1:match, '/');
if(next-p < 0)
{
al_string npath = AL_STRING_INIT_STATIC();
al_string nmatch = AL_STRING_INIT_STATIC();
- if(path) al_string_append_cstr(&npath, path);
- al_string_append_char(&npath, '/');
- al_string_append_range(&npath, match, sep);
+ if(!sep)
+ {
+ al_string_append_cstr(&npath, path?path:"/.");
+ MatchString = match;
+ }
+ else
+ {
+ if(path) al_string_append_cstr(&npath, path);
+ al_string_append_char(&npath, '/');
+ al_string_append_range(&npath, match, sep);
- al_string_append_range(&nmatch, sep+1, next);
+ al_string_append_range(&nmatch, sep+1, next);
+ MatchString = al_string_get_cstr(nmatch);
+ }
- MatchString = al_string_get_cstr(nmatch);
TRACE("Searching %s for %s\n", al_string_get_cstr(npath), MatchString);
n = scandir(al_string_get_cstr(npath), &namelist, MatchFilter, alphasort);
if(n >= 0)
@@ -790,6 +1047,8 @@ static void RecurseDirectorySearch(const char *path, const char *match, vector_a
al_string_deinit(&npath);
break;
}
+
+ sep = next;
}
}
@@ -852,7 +1111,7 @@ vector_al_string SearchDataFiles(const char *match, const char *subdir)
al_string_deinit(&path);
}
- ATOMIC_LOAD(&search_lock, 0);
+ ATOMIC_STORE(&search_lock, 0);
return results;
}
@@ -1051,4 +1310,30 @@ void al_string_copy_wcstr(al_string *str, const wchar_t *from)
*VECTOR_ITER_END(*str) = 0;
}
}
+
+void al_string_append_wcstr(al_string *str, const wchar_t *from)
+{
+ int len;
+ if((len=WideCharToMultiByte(CP_UTF8, 0, from, -1, NULL, 0, NULL, NULL)) > 0)
+ {
+ size_t strlen = al_string_length(*str);
+ VECTOR_RESERVE(*str, strlen+len);
+ VECTOR_RESIZE(*str, strlen+len-1);
+ WideCharToMultiByte(CP_UTF8, 0, from, -1, &VECTOR_FRONT(*str) + strlen, len, NULL, NULL);
+ *VECTOR_ITER_END(*str) = 0;
+ }
+}
+
+void al_string_append_wrange(al_string *str, const wchar_t *from, const wchar_t *to)
+{
+ int len;
+ if((len=WideCharToMultiByte(CP_UTF8, 0, from, to-from, NULL, 0, NULL, NULL)) > 0)
+ {
+ size_t strlen = al_string_length(*str);
+ VECTOR_RESERVE(*str, strlen+len+1);
+ VECTOR_RESIZE(*str, strlen+len);
+ WideCharToMultiByte(CP_UTF8, 0, from, to-from, &VECTOR_FRONT(*str) + strlen, len+1, NULL, NULL);
+ *VECTOR_ITER_END(*str) = 0;
+ }
+}
#endif