Compare commits

...

3 Commits

Author SHA1 Message Date
~d6 df51651789 Update file_stat to match the existing documentation.
Previously, writing to the stat port would produce a line of text
similar to the listing used when reading a directory. This did not
match the documentation around the stat port.

After the change, writing an address to the stat port will fill that
address with bytes equal to the length parameter. These will either be
a hexadecimal file size, or else the same character repeated N times
according to the following logic:

 - path is a directory
 ? file size of path does not fit in N hexadecimal characters
 ! path is not readable

Note that unlike when reading a directory, the string will not include
the requested path, a newline, or a null terminator.
2024-09-19 14:06:03 -07:00
Devine Lu Linvega 0da4c3335d Merge branch 'main' of git.sr.ht:~rabbits/uxn 2024-09-17 18:10:02 -07:00
~d6 e772d56936 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.
2024-09-17 18:09:14 -07:00
1 changed files with 75 additions and 9 deletions

View File

@ -9,17 +9,20 @@
#include <unistd.h>
#ifdef _WIN32
#include <direct.h>
#include <libiberty/libiberty.h>
#define realpath(s, dummy) lrealpath(s)
#define DIR_SEP_CHAR '\\'
#define DIR_SEP_STR "\\"
#define pathcmp(path1, path2, length) strncasecmp(path1, path2, length) /* strncasecmp provided by libiberty */
#define notdriveroot(file_name) (file_name[0] != DIR_SEP_CHAR && ((strlen(file_name) > 2 && file_name[1] != ':') || strlen(file_name) <= 2))
#define mkdir(file_name) (_mkdir(file_name) == 0)
#else
#define DIR_SEP_CHAR '/'
#define DIR_SEP_STR "/"
#define pathcmp(path1, path2, length) strncmp(path1, path2, length)
#define notdriveroot(file_name) (file_name[0] != DIR_SEP_CHAR)
#define mkdir(file_name) (mkdir(file_name, 0755) == 0)
#endif
#ifndef PATH_MAX
@ -48,7 +51,9 @@ typedef struct {
enum { IDLE,
FILE_READ,
FILE_WRITE,
DIR_READ } state;
DIR_READ,
DIR_WRITE
} state;
int outside_sandbox;
} UxnFile;
@ -207,33 +212,94 @@ file_read(UxnFile *c, void *dest, int len)
return 0;
}
static int
is_dir_path(char *p)
{
char c;
int saw_slash = 0;
while (c = *p++)
saw_slash = c == DIR_SEP_CHAR;
return saw_slash;
}
int
dir_exists(char *p)
{
struct stat st;
return stat(p, &st) == 0 && S_ISDIR(st.st_mode);
}
int
ensure_parent_dirs(char *p)
{
int ok = 1;
char c, *s = p;
for(; ok && (c = *p); p++) {
if (c == DIR_SEP_CHAR) {
*p = '\0';
ok = dir_exists(s) || mkdir(s);
*p = c;
}
}
return ok;
}
static Uint16
file_write(UxnFile *c, void *src, Uint16 len, Uint8 flags)
{
Uint16 ret = 0;
if(c->outside_sandbox) return 0;
if(c->state != FILE_WRITE) {
ensure_parent_dirs(c->current_filename);
if(c->state != FILE_WRITE && c->state != DIR_WRITE) {
reset(c);
if((c->f = fopen(c->current_filename, (flags & 0x01) ? "ab" : "wb")) != NULL)
if (is_dir_path(c->current_filename))
c->state = DIR_WRITE;
else if((c->f = fopen(c->current_filename, (flags & 0x01) ? "ab" : "wb")) != NULL)
c->state = FILE_WRITE;
}
if(c->state == FILE_WRITE) {
if((ret = fwrite(src, 1, len, c->f)) > 0 && fflush(c->f) != 0)
ret = 0;
}
if (c->state == DIR_WRITE) {
ret = dir_exists(c->current_filename);
}
return ret;
}
static Uint16
stat_fill(Uint8 *dest, Uint16 len, char c)
{
Uint16 i;
for (i = 0; i < len; i++)
*(dest++) = c;
return len;
}
static Uint16
stat_size(Uint8 *dest, Uint16 len, off_t size)
{
Uint16 i;
dest += len - 1;
for (i = 0; i < len; i++) {
*(dest--) = '0' + (Uint8)(size & 0xf);
size = size >> 4;
}
return size == 0 ? len : stat_fill(dest, len, '?');
}
static Uint16
file_stat(UxnFile *c, void *dest, Uint16 len)
{
char *basename = strrchr(c->current_filename, DIR_SEP_CHAR);
if(c->outside_sandbox) return 0;
if(basename != NULL)
basename++;
struct stat st;
if(c->outside_sandbox)
return 0;
else if(stat(c->current_filename, &st))
return stat_fill(dest, len, '!');
else if(S_ISDIR(st.st_mode))
return stat_fill(dest, len, '-');
else
basename = c->current_filename;
return get_entry(dest, len, c->current_filename, basename, 0);
return stat_size(dest, len, st.st_size);
}
static Uint16