aboutsummaryrefslogtreecommitdiff
path: root/src/platform/windows/win_file.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/platform/windows/win_file.c')
-rw-r--r--src/platform/windows/win_file.c309
1 files changed, 192 insertions, 117 deletions
diff --git a/src/platform/windows/win_file.c b/src/platform/windows/win_file.c
index e10f2848..2a9504aa 100644
--- a/src/platform/windows/win_file.c
+++ b/src/platform/windows/win_file.c
@@ -1,6 +1,6 @@
//
-// Copyright 2017 Garrett D'Amore <garrett@damore.org>
-// Copyright 2017 Capitar IT Group BV <info@capitar.com>
+// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2018 Capitar IT Group BV <info@capitar.com>
//
// This software is supplied under the terms of the MIT License, a
// copy of which should be located in the distribution where this
@@ -12,11 +12,73 @@
#ifdef NNG_PLATFORM_WINDOWS
+#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
// File support.
+static char *
+nni_plat_find_pathsep(char *path)
+{
+ char *p;
+ // Legal path separators are "\\" and "/" under Windows.
+ // This is sort of a poormans strchr, but with the two specific
+ // separator characters instead.
+ for (p = path; *p != '\0'; p++) {
+ if ((*p == '/') || (*p == '\\')) {
+ return (p);
+ }
+ }
+ return (NULL);
+}
+
+static int
+nni_plat_make_parent_dirs(const char *path)
+{
+ char *dup;
+ char *p;
+
+ // creates everything up until the last component.
+ if ((dup = nni_strdup(path)) == NULL) {
+ return (NNG_ENOMEM);
+ }
+
+ // Skip past C:, C:\, \\ and \ style prefixes, because we cannot
+ // create those things as directories -- they should already exist.
+ p = dup;
+ if (isalpha(p[0]) && (p[1] == ':')) {
+ p += 2;
+ if ((p[0] == '\\') || (p[0] == '/')) {
+ p++;
+ }
+ } else if ((p[0] == '\\') && (p[1] == '\\')) {
+ p += 2;
+ } else if ((p[0] == '\\') || (p[0] == '/')) {
+ p++;
+ }
+
+ while ((p = nni_plat_find_pathsep(p)) != NULL) {
+ *p = '\0';
+
+ if (!CreateDirectory(dup, NULL)) {
+ int rv = GetLastError();
+ if (rv != ERROR_ALREADY_EXISTS) {
+ nni_strfree(dup);
+ return (nni_win_error(rv));
+ }
+ }
+ *p = '\\'; // Windows prefers this though.
+
+ // collapse grouped pathsep characters
+ while ((*p == '/') || (*p == '\\')) {
+ p++;
+ }
+ }
+ nni_strfree(dup);
+ return (0);
+}
+
// nni_plat_file_put writes the named file, with the provided data,
// and the given size. If the file already exists it is overwritten.
// The permissions on the file should be limited to read and write
@@ -28,6 +90,10 @@ nni_plat_file_put(const char *name, const void *data, size_t len)
int rv = 0;
DWORD nwrite;
+ if ((rv = nni_plat_make_parent_dirs(name)) != 0) {
+ return (rv);
+ }
+
h = CreateFile(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE) {
@@ -96,141 +162,104 @@ done:
int
nni_plat_file_delete(const char *name)
{
- if (!DeleteFile(name)) {
- return (nni_win_error(GetLastError()));
+ int rv;
+ if (RemoveDirectory(name)) {
+ return (0);
}
- return (0);
+ if (DeleteFile(name)) {
+ return (0);
+ }
+ if ((rv = nni_win_error(GetLastError())) == NNG_ENOENT) {
+ return (0);
+ }
+ return (rv);
}
-// nni_plat_dir_open attempts to "open a directory" for listing. The
-// handle for further operations is returned in the first argument, and
-// the directory name is supplied in the second.
-struct dirhandle {
+static int
+nni_plat_file_walk_inner(const char *name, nni_plat_file_walker walkfn,
+ void *arg, int flags, bool *stop)
+{
+ char path[MAX_PATH + 1];
+ int rv;
+ int walkrv;
HANDLE dirh;
WIN32_FIND_DATA data;
- int cont; // zero on first read, 1 thereafter.
-};
-
-int
-nni_plat_dir_open(void **dhp, const char *name)
-{
- struct dirhandle *dh;
- char fullpath[MAX_PATH + 1];
-
- if ((dh = NNI_ALLOC_STRUCT(dh)) == NULL) {
- return (NNG_ENOMEM);
- }
- // Append wildcard to directory name
- _snprintf(fullpath, sizeof(fullpath), "%s\\*", name);
-
- if ((dh->dirh = FindFirstFile(fullpath, &dh->data)) ==
- INVALID_HANDLE_VALUE) {
- int rv;
+ _snprintf(path, sizeof(path), "%s\\%s", name, "*");
+ if ((dirh = FindFirstFile(path, &data)) == INVALID_HANDLE_VALUE) {
rv = nni_win_error(GetLastError());
- NNI_FREE_STRUCT(dh);
return (rv);
}
- dh->cont = 0;
- *dhp = dh;
-
- return (0);
-}
-// nni_plat_dir_next gets the next directory entry. Each call returns
-// a new entry (arbitrary order). When no more entries exist, it returns
-// NNG_ENOENT.
-int
-nni_plat_dir_next(void *dir, const char **namep)
-{
- struct dirhandle *dh = dir;
- int rv;
+ for (;;) {
+ // We never return hidden files.
+ if ((data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ||
+ (strcmp(data.cFileName, ".") == 0) ||
+ (strcmp(data.cFileName, "..") == 0)) {
+ goto next_file;
+ }
+ _snprintf(path, sizeof(path), "%s\\%s", name, data.cFileName);
+ walkrv = NNI_PLAT_FILE_WALK_CONTINUE;
+ if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ if ((flags & NNI_PLAT_FILE_WALK_FILES_ONLY) == 0) {
+ walkrv = walkfn(path, arg);
+ }
- if (dh->dirh == INVALID_HANDLE_VALUE) {
- return (NNG_ENOENT);
- }
- if (dh->cont) {
- // We need to read another entry
- if (!FindNextFile(dh->dirh, &dh->data)) {
- rv = GetLastError();
- FindClose(dh->dirh);
- dh->dirh = INVALID_HANDLE_VALUE;
- if (rv == ERROR_NO_MORE_FILES) {
- return (NNG_ENOENT);
+ if (((flags & NNI_PLAT_FILE_WALK_SHALLOW) == 0) &&
+ (walkrv != NNI_PLAT_FILE_WALK_STOP) &&
+ (walkrv != NNI_PLAT_FILE_WALK_PRUNE_CHILD)) {
+ rv = nni_plat_file_walk_inner(
+ path, walkfn, arg, flags, stop);
+ if (rv != 0) {
+ if (rv == NNG_ENOENT) {
+ rv = 0; // File deleted.
+ }
+ FindClose(dirh);
+ return (rv);
+ }
}
- return (nni_win_error(rv));
+ } else if (data.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) {
+ if ((flags & NNI_PLAT_FILE_WALK_FILES_ONLY) == 0) {
+ walkrv = walkfn(path, arg);
+ }
+ } else {
+ walkrv = walkfn(path, arg);
+ }
+
+ if (*stop) {
+ walkrv = NNI_PLAT_FILE_WALK_STOP;
+ }
+
+ switch (walkrv) {
+ case NNI_PLAT_FILE_WALK_STOP:
+ *stop = true;
+ FindClose(dirh);
+ return (0);
+ case NNI_PLAT_FILE_WALK_PRUNE_SIB:
+ FindClose(dirh);
+ return (0);
}
- }
- dh->cont = 1;
- // Skip over directories.
- while (dh->data.dwFileAttributes &
- (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_HIDDEN)) {
- if (!FindNextFile(dh->dirh, &dh->data)) {
+ next_file:
+ if (!FindNextFile(dirh, &data)) {
rv = GetLastError();
- FindClose(dh->dirh);
- dh->dirh = INVALID_HANDLE_VALUE;
+ FindClose(dirh);
if (rv == ERROR_NO_MORE_FILES) {
- return (NNG_ENOENT);
+ break;
}
return (nni_win_error(rv));
}
}
- // Got a good entry.
- *namep = dh->data.cFileName;
- return (0);
-}
-
-// nni_plat_dir_close closes the directory handle, freeing all
-// resources associated with it.
-void
-nni_plat_dir_close(void *dir)
-{
- struct dirhandle *dh = dir;
- if (dh->dirh != INVALID_HANDLE_VALUE) {
- FindClose(dh->dirh);
- }
- NNI_FREE_STRUCT(dh);
-}
-
-int
-nni_plat_dir_create(const char *name)
-{
- char parent[MAX_PATH + 1];
- size_t len;
-
- nni_strlcpy(parent, name, sizeof(parent));
- len = strlen(parent);
- while (len > 0) {
- if ((parent[len - 1] == '/') || (parent[len - 1] == '\\')) {
- parent[len - 1] = '\0';
- break;
- }
- len--;
- }
-
- if (!CreateDirectoryEx(parent, name, NULL)) {
- int rv = GetLastError();
- if (rv == ERROR_ALREADY_EXISTS) {
- return (0);
- }
- return (nni_win_error(rv));
- }
return (0);
}
int
-nni_plat_dir_remove(const char *name)
+nni_plat_file_walk(
+ const char *name, nni_plat_file_walker walkfn, void *arg, int flags)
{
- if (!RemoveDirectory(name)) {
- int rv = GetLastError();
- if (rv == ERROR_PATH_NOT_FOUND) {
- return (0);
- }
- return (nni_win_error(rv));
- }
- return (0);
+ bool stop = false;
+ return (nni_plat_file_walk_inner(name, walkfn, arg, flags, &stop));
}
char *
@@ -249,22 +278,68 @@ nni_plat_home_dir(void)
{
char *homedrv;
char *homedir;
- char stuff[MAX_PATH + 1];
+ char *result;
if (((homedrv = getenv("HOMEDRIVE")) == NULL) ||
((homedir = getenv("HOMEPATH")) == NULL)) {
return (NULL);
}
- _snprintf(stuff, sizeof(stuff), "%s%s", homedrv, homedir);
- return (nni_strdup(stuff));
+ if (nni_asprintf(&result, "%s%s", homedrv, homedir) == 0) {
+ return (result);
+ }
+ return (NULL);
+}
+
+int
+nni_plat_file_type(const char *name, int *typep)
+{
+ HANDLE dirh;
+ WIN32_FIND_DATA data;
+
+ if ((dirh = FindFirstFile(name, &data)) == INVALID_HANDLE_VALUE) {
+ return (nni_win_error(GetLastError()));
+ }
+ (void) FindClose(dirh);
+ if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ *typep = NNI_PLAT_FILE_TYPE_DIR;
+ } else if (data.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) {
+ *typep = NNI_PLAT_FILE_TYPE_OTHER;
+ } else if (data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
+ *typep = NNI_PLAT_FILE_TYPE_OTHER;
+ } else {
+ *typep = NNI_PLAT_FILE_TYPE_FILE;
+ }
+ return (0);
}
char *
nni_plat_join_dir(const char *prefix, const char *suffix)
{
- char stuff[MAX_PATH + 1];
+ char *result;
+
+ if (nni_asprintf(&result, "%s\\%s", prefix, suffix) == 0) {
+ return (result);
+ }
+ return (NULL);
+}
+
+const char *
+nni_plat_file_basename(const char *name)
+{
+ const char *s;
- _snprintf(stuff, sizeof(stuff), "%s\\%s", prefix, suffix);
- return (nni_strdup(stuff));
+ // skip over drive designator if present
+ if (isalpha(name[0]) && (name[1] == ':')) {
+ name += 2;
+ }
+ s = name + strlen(name);
+ while (s > name) {
+ if ((*s == '\\') || (*s == '/')) {
+ return (s + 1);
+ }
+ s--;
+ }
+ return (name);
}
+
#endif // NNG_PLATFORM_WINDOWS \ No newline at end of file