commit 4ff6a5e
d_m
·
2024-09-17 00:09:25 +0000 UTC
parent 887ceb0
Support creating directories
Previously Varvara did not have a way to create new directories.
This change adds that capability without adding or modifying any
device ports.
The changes are:
(1) When a filename ends in the directory separator, writing
any value to File/write will create a directory. Unlike
with regular files, the value is ignored.
(2) When writing any path (regular files or directories) if
there are missing parent directories Varvara will attempt
to create them first. This behavior is similar to mkdir -p.
The directory separator is / on most platforms, and \ on Windows.
On POSIX platforms the directories will be created with permission
0755 (modified by the user's umask value).
This change has been tested and works in both uxnemu and uxncli.
1 files changed,
+46,
-3
+46,
-3
1@@ -9,17 +9,20 @@
2 #include <unistd.h>
3
4 #ifdef _WIN32
5+#include <direct.h>
6 #include <libiberty/libiberty.h>
7 #define realpath(s, dummy) lrealpath(s)
8 #define DIR_SEP_CHAR '\\'
9 #define DIR_SEP_STR "\\"
10 #define pathcmp(path1, path2, length) strncasecmp(path1, path2, length) /* strncasecmp provided by libiberty */
11 #define notdriveroot(file_name) (file_name[0] != DIR_SEP_CHAR && ((strlen(file_name) > 2 && file_name[1] != ':') || strlen(file_name) <= 2))
12+#define mkdir(file_name) (_mkdir(file_name) == 0)
13 #else
14 #define DIR_SEP_CHAR '/'
15 #define DIR_SEP_STR "/"
16 #define pathcmp(path1, path2, length) strncmp(path1, path2, length)
17 #define notdriveroot(file_name) (file_name[0] != DIR_SEP_CHAR)
18+#define mkdir(file_name) (mkdir(file_name, 0755) == 0)
19 #endif
20
21 #ifndef PATH_MAX
22@@ -48,7 +51,9 @@ typedef struct {
23 enum { IDLE,
24 FILE_READ,
25 FILE_WRITE,
26- DIR_READ } state;
27+ DIR_READ,
28+ DIR_WRITE
29+ } state;
30 int outside_sandbox;
31 } UxnFile;
32
33@@ -207,20 +212,58 @@ file_read(UxnFile *c, void *dest, int len)
34 return 0;
35 }
36
37+static int
38+is_dir_path(char *p)
39+{
40+ char c;
41+ int saw_slash = 0;
42+ while (c = *p++)
43+ saw_slash = c == DIR_SEP_CHAR;
44+ return saw_slash;
45+}
46+
47+int
48+dir_exists(char *p)
49+{
50+ struct stat st;
51+ return stat(p, &st) == 0 && S_ISDIR(st.st_mode);
52+}
53+
54+int
55+ensure_parent_dirs(char *p)
56+{
57+ int ok = 1;
58+ char c, *s = p;
59+ for(; ok && (c = *p); p++) {
60+ if (c == DIR_SEP_CHAR) {
61+ *p = '\0';
62+ ok = dir_exists(s) || mkdir(s);
63+ *p = c;
64+ }
65+ }
66+ return ok;
67+}
68+
69 static Uint16
70 file_write(UxnFile *c, void *src, Uint16 len, Uint8 flags)
71 {
72 Uint16 ret = 0;
73 if(c->outside_sandbox) return 0;
74- if(c->state != FILE_WRITE) {
75+ ensure_parent_dirs(c->current_filename);
76+ if(c->state != FILE_WRITE && c->state != DIR_WRITE) {
77 reset(c);
78- if((c->f = fopen(c->current_filename, (flags & 0x01) ? "ab" : "wb")) != NULL)
79+ if (is_dir_path(c->current_filename))
80+ c->state = DIR_WRITE;
81+ else if((c->f = fopen(c->current_filename, (flags & 0x01) ? "ab" : "wb")) != NULL)
82 c->state = FILE_WRITE;
83 }
84 if(c->state == FILE_WRITE) {
85 if((ret = fwrite(src, 1, len, c->f)) > 0 && fflush(c->f) != 0)
86 ret = 0;
87 }
88+ if (c->state == DIR_WRITE) {
89+ ret = dir_exists(c->current_filename);
90+ }
91 return ret;
92 }
93