summaryrefslogtreecommitdiffstats
path: root/osdep/glob-win.c
diff options
context:
space:
mode:
Diffstat (limited to 'osdep/glob-win.c')
-rw-r--r--osdep/glob-win.c162
1 files changed, 162 insertions, 0 deletions
diff --git a/osdep/glob-win.c b/osdep/glob-win.c
new file mode 100644
index 0000000..08fd90f
--- /dev/null
+++ b/osdep/glob-win.c
@@ -0,0 +1,162 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <windows.h>
+#include <stdbool.h>
+#include <string.h>
+#include "osdep/io.h"
+#include "mpv_talloc.h"
+
+#if HAVE_UWP
+// Missing from MinGW headers.
+WINBASEAPI HANDLE WINAPI FindFirstFileExW(LPCWSTR lpFileName,
+ FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData,
+ FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags);
+#endif
+
+static wchar_t *talloc_wcsdup(void *ctx, const wchar_t *wcs)
+{
+ size_t len = (wcslen(wcs) + 1) * sizeof(wchar_t);
+ return talloc_memdup(ctx, (void*)wcs, len);
+}
+
+static int compare_wcscoll(const void *v1, const void *v2)
+{
+ wchar_t * const* p1 = v1;
+ wchar_t * const* p2 = v2;
+ return wcscoll(*p1, *p2);
+}
+
+static bool exists(const char *filename)
+{
+ wchar_t *wfilename = mp_from_utf8(NULL, filename);
+ bool result = GetFileAttributesW(wfilename) != INVALID_FILE_ATTRIBUTES;
+ talloc_free(wfilename);
+ return result;
+}
+
+int mp_glob(const char *restrict pattern, int flags,
+ int (*errfunc)(const char*, int), mp_glob_t *restrict pglob)
+{
+ // This glob implementation never calls errfunc and doesn't understand any
+ // flags. These features are currently unused in mpv, however if new code
+ // were to use these them, it would probably break on Windows.
+
+ unsigned dirlen = 0;
+ bool wildcards = false;
+
+ // Check for drive relative paths eg. "C:*.flac"
+ if (pattern[0] != '\0' && pattern[1] == ':')
+ dirlen = 2;
+
+ // Split the directory and filename. All files returned by FindFirstFile
+ // will be in this directory. Also check the filename for wildcards.
+ for (unsigned i = 0; pattern[i]; i ++) {
+ if (pattern[i] == '?' || pattern[i] == '*')
+ wildcards = true;
+
+ if (pattern[i] == '\\' || pattern[i] == '/') {
+ dirlen = i + 1;
+ wildcards = false;
+ }
+ }
+
+ // FindFirstFile is unreliable with certain input (it returns weird results
+ // with paths like "." and "..", and presumably others.) If there are no
+ // wildcards in the filename, don't call it, just check if the file exists.
+ // The CRT globbing code does this too.
+ if (!wildcards) {
+ if (!exists(pattern)) {
+ pglob->gl_pathc = 0;
+ return GLOB_NOMATCH;
+ }
+
+ pglob->ctx = talloc_new(NULL);
+ pglob->gl_pathc = 1;
+ pglob->gl_pathv = talloc_array_ptrtype(pglob->ctx, pglob->gl_pathv, 2);
+ pglob->gl_pathv[0] = talloc_strdup(pglob->ctx, pattern);
+ pglob->gl_pathv[1] = NULL;
+ return 0;
+ }
+
+ wchar_t *wpattern = mp_from_utf8(NULL, pattern);
+ WIN32_FIND_DATAW data;
+ HANDLE find = FindFirstFileExW(wpattern, FindExInfoBasic, &data, FindExSearchNameMatch, NULL, 0);
+ talloc_free(wpattern);
+
+ // Assume an error means there were no matches. mpv doesn't check for
+ // glob() errors, so this should be fine for now.
+ if (find == INVALID_HANDLE_VALUE) {
+ pglob->gl_pathc = 0;
+ return GLOB_NOMATCH;
+ }
+
+ size_t pathc = 0;
+ void *tmp = talloc_new(NULL);
+ wchar_t **wnamev = NULL;
+
+ // Read a list of filenames. Unlike glob(), FindFirstFile doesn't return
+ // the full path, since all files are relative to the directory specified
+ // in the pattern.
+ do {
+ if (!wcscmp(data.cFileName, L".") || !wcscmp(data.cFileName, L".."))
+ continue;
+
+ wchar_t *wname = talloc_wcsdup(tmp, data.cFileName);
+ MP_TARRAY_APPEND(tmp, wnamev, pathc, wname);
+ } while (FindNextFileW(find, &data));
+ FindClose(find);
+
+ if (!wnamev) {
+ talloc_free(tmp);
+ pglob->gl_pathc = 0;
+ return GLOB_NOMATCH;
+ }
+
+ // POSIX glob() is supposed to sort paths according to LC_COLLATE.
+ // FindFirstFile just returns paths in the order they are read from the
+ // directory, so sort them manually with wcscoll.
+ qsort(wnamev, pathc, sizeof(wchar_t*), compare_wcscoll);
+
+ pglob->ctx = talloc_new(NULL);
+ pglob->gl_pathc = pathc;
+ pglob->gl_pathv = talloc_array_ptrtype(pglob->ctx, pglob->gl_pathv,
+ pathc + 1);
+
+ // Now convert all filenames to UTF-8 (they had to be in UTF-16 for
+ // sorting) and prepend the directory
+ for (unsigned i = 0; i < pathc; i ++) {
+ int namelen = WideCharToMultiByte(CP_UTF8, 0, wnamev[i], -1, NULL, 0,
+ NULL, NULL);
+ char *path = talloc_array(pglob->ctx, char, namelen + dirlen);
+
+ memcpy(path, pattern, dirlen);
+ WideCharToMultiByte(CP_UTF8, 0, wnamev[i], -1, path + dirlen,
+ namelen, NULL, NULL);
+ pglob->gl_pathv[i] = path;
+ }
+
+ // gl_pathv must be null terminated
+ pglob->gl_pathv[pathc] = NULL;
+ talloc_free(tmp);
+ return 0;
+}
+
+void mp_globfree(mp_glob_t *pglob)
+{
+ talloc_free(pglob->ctx);
+}