all repos — xyw @ 6f0306810ce94929df59faf93e5ffc50bad19ee7

A minimal virtual machine and assembler for terminals.

Added support for copying and moving files (using | as path separator for src|dst)
h3rald h3rald@h3rald.com
Sun, 26 Jul 2026 16:56:11 +0200
commit

6f0306810ce94929df59faf93e5ffc50bad19ee7

parent

274de990052cccdf5790915dcf875c3368c9c8e0

1 files changed, 121 insertions(+), 0 deletions(-)

jump to
M devices/file.cdevices/file.c

@@ -30,6 +30,8 @@ #define FILE_OP_READ 0x01

#define FILE_OP_WRITE 0x02 #define FILE_OP_APPEND 0x03 #define FILE_OP_DELETE 0x04 +#define FILE_OP_COPY 0x05 +#define FILE_OP_MOVE 0x06 // File types #define XYW_FILE_TYPE_UNKNOWN 0x00

@@ -463,6 +465,119 @@ // Update stats after delete

update_file_stats(data); } +// Parse "source|destination" from path string +static int split_path_pair(xyw_byte *data, char *src, char *dst, size_t bufsize) +{ + xyw_word path_addr = XYW_PEEKW(&data[FILE_PATH]); + char combined[FILE_MAX_PATH * 2]; + + if (path_addr == 0) + return -1; + + get_path_string(path_addr, combined, sizeof(combined)); + + char *sep = strchr(combined, '|'); + if (!sep) + return -1; + + *sep = '\0'; + strncpy(src, combined, bufsize - 1); + src[bufsize - 1] = '\0'; + strncpy(dst, sep + 1, bufsize - 1); + dst[bufsize - 1] = '\0'; + return 0; +} + +// Copy a file +static void file_copy(xyw_byte *data, xyw_byte *error) +{ + char src[FILE_MAX_PATH], dst[FILE_MAX_PATH]; + + XYW_POKEW(&data[FILE_SUCCESS], 0); + + if (split_path_pair(data, src, dst, FILE_MAX_PATH) != 0) + { + *error = FILE_ERROR_INVALID_OP; + return; + } + + // Close if source or dest is currently open + if (file_handle && (strcmp(file_current_path, src) == 0 || strcmp(file_current_path, dst) == 0)) + file_close(); + + XYW_DBG(" Copying: %s -> %s\n", src, dst); + + FILE *in = fopen(src, "rb"); + if (!in) + { + *error = FILE_ERROR_NOT_FOUND; + return; + } + + FILE *out = fopen(dst, "wb"); + if (!out) + { + fclose(in); + *error = FILE_ERROR_ACCESS_DENIED; + return; + } + + char buf[4096]; + size_t total = 0; + size_t n; + while ((n = fread(buf, 1, sizeof(buf), in)) > 0) + { + if (fwrite(buf, 1, n, out) != n) + { + fclose(in); + fclose(out); + *error = FILE_ERROR_IO_ERROR; + return; + } + total += n; + } + + fclose(in); + fclose(out); + + XYW_POKEW(&data[FILE_SUCCESS], (xyw_word)(total > 0xFFFF ? 0xFFFF : total)); +} + +// Move (rename) a file +static void file_move(xyw_byte *data, xyw_byte *error) +{ + char src[FILE_MAX_PATH], dst[FILE_MAX_PATH]; + + XYW_POKEW(&data[FILE_SUCCESS], 0); + + if (split_path_pair(data, src, dst, FILE_MAX_PATH) != 0) + { + *error = FILE_ERROR_INVALID_OP; + return; + } + + // Close if source or dest is currently open + if (file_handle && (strcmp(file_current_path, src) == 0 || strcmp(file_current_path, dst) == 0)) + file_close(); + + XYW_DBG(" Moving: %s -> %s\n", src, dst); + + if (rename(src, dst) != 0) + { + // rename may fail across filesystems; fall back to copy+delete + file_copy(data, error); + if (*error != 0) + return; + if (remove(src) != 0) + { + *error = FILE_ERROR_IO_ERROR; + return; + } + } + + XYW_POKEW(&data[FILE_SUCCESS], 1); +} + void file_setup(xyw_byte *data) { // Close any open file

@@ -506,6 +621,12 @@ file_write_op(data, error, 1);

break; case FILE_OP_DELETE: file_delete(data, error); + break; + case FILE_OP_COPY: + file_copy(data, error); + break; + case FILE_OP_MOVE: + file_move(data, error); break; default: if (op != 0)