diff options
author | Chris Robinson <[email protected]> | 2015-10-04 03:17:52 -0700 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2015-10-04 16:41:54 -0700 |
commit | 074983b19c4a838f1a26cfa5afb879808dfc2fd1 (patch) | |
tree | c62cfc974d818e467614f6997cbf01e74801bac2 /Alc | |
parent | 70fbc2b1ff254295338c6d42b41d9fdb43b02d8f (diff) |
Implement SearchDataFiles for Windows
Diffstat (limited to 'Alc')
-rw-r--r-- | Alc/alstring.h | 2 | ||||
-rw-r--r-- | Alc/helpers.c | 307 |
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 |