2016-10-12 16:18:23 +00:00
|
|
|
/*
|
2018-08-13 17:22:40 +00:00
|
|
|
GSPLUS - Advanced Apple IIGS Emulator Environment
|
|
|
|
Based on the KEGS emulator written by Kent Dickey
|
|
|
|
See COPYRIGHT.txt for Copyright information
|
|
|
|
See LICENSE.txt for license (GPL v2)
|
2016-10-12 16:18:23 +00:00
|
|
|
*/
|
|
|
|
|
2016-11-24 18:04:37 +00:00
|
|
|
#define _BSD_SOURCE
|
2016-10-12 16:18:23 +00:00
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <string.h>
|
2016-11-24 18:04:37 +00:00
|
|
|
#include <time.h>
|
2017-09-19 03:28:39 +00:00
|
|
|
#include <libgen.h>
|
2016-10-12 16:18:23 +00:00
|
|
|
|
|
|
|
#include "defc.h"
|
|
|
|
#include "gsos.h"
|
|
|
|
#include "fst.h"
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
#if defined(__APPLE__)
|
2016-10-12 16:18:23 +00:00
|
|
|
#include <sys/xattr.h>
|
|
|
|
#include <sys/attr.h>
|
|
|
|
#include <sys/paths.h>
|
|
|
|
#endif
|
|
|
|
|
2016-10-30 18:11:15 +00:00
|
|
|
#ifdef __linux__
|
|
|
|
#include <sys/xattr.h>
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
#if defined(_WIN32) || defined(WIN_SDL)
|
2016-10-12 16:18:23 +00:00
|
|
|
#include <io.h>
|
2016-11-24 18:04:37 +00:00
|
|
|
#include <sys/xattr.h>
|
2016-10-31 01:24:06 +00:00
|
|
|
#endif
|
|
|
|
|
2016-11-27 20:09:35 +00:00
|
|
|
#if defined(__FreeBSD__)
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/extattr.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(_AIX)
|
|
|
|
#include <sys/ea.h>
|
|
|
|
#endif
|
2016-10-31 01:24:06 +00:00
|
|
|
|
2016-10-12 16:18:23 +00:00
|
|
|
#ifndef XATTR_FINDERINFO_NAME
|
|
|
|
#define XATTR_FINDERINFO_NAME "com.apple.FinderInfo"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef XATTR_RESOURCEFORK_NAME
|
|
|
|
#define XATTR_RESOURCEFORK_NAME "com.apple.ResourceFork"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
extern Engine_reg engine;
|
|
|
|
|
|
|
|
|
|
|
|
/* direct page offsets */
|
|
|
|
|
|
|
|
#define dp_call_number 0x30
|
|
|
|
#define dp_param_blk_ptr 0x32
|
|
|
|
#define dp_dev1_num 0x36
|
|
|
|
#define dp_dev2_num 0x38
|
|
|
|
#define dp_path1_ptr 0x3a
|
|
|
|
#define dp_path2_ptr 0x3e
|
|
|
|
#define dp_path_flag 0x42
|
|
|
|
|
|
|
|
#define global_buffer 0x009a00
|
|
|
|
|
2016-11-24 18:04:37 +00:00
|
|
|
enum {
|
2018-08-13 17:22:40 +00:00
|
|
|
regular_file,
|
|
|
|
resource_file,
|
|
|
|
directory_file,
|
2016-11-24 18:04:37 +00:00
|
|
|
};
|
|
|
|
|
2018-07-07 04:49:36 +00:00
|
|
|
enum {
|
2018-08-13 17:22:40 +00:00
|
|
|
translate_none,
|
|
|
|
translate_crlf,
|
|
|
|
translate_merlin,
|
2018-07-07 04:49:36 +00:00
|
|
|
};
|
|
|
|
|
2016-11-24 18:04:37 +00:00
|
|
|
|
|
|
|
struct directory {
|
2018-08-13 17:22:40 +00:00
|
|
|
int displacement;
|
|
|
|
int num_entries;
|
|
|
|
char *entries[1];
|
2016-11-24 18:04:37 +00:00
|
|
|
};
|
2016-10-12 16:18:23 +00:00
|
|
|
|
|
|
|
struct fd_entry {
|
2018-08-13 17:22:40 +00:00
|
|
|
struct fd_entry *next;
|
|
|
|
char *path;
|
|
|
|
int cookie;
|
|
|
|
int type;
|
|
|
|
int access;
|
|
|
|
int fd;
|
|
|
|
int translate;
|
|
|
|
struct directory *dir;
|
2016-10-12 16:18:23 +00:00
|
|
|
};
|
|
|
|
|
2016-11-26 02:49:26 +00:00
|
|
|
static void free_directory(struct directory *dd);
|
|
|
|
static struct directory *read_directory(const char *path, word16 *error);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2016-11-26 02:49:26 +00:00
|
|
|
#define COOKIE_BASE 0x8000
|
2016-11-24 18:04:37 +00:00
|
|
|
|
2016-11-26 02:49:26 +00:00
|
|
|
static word32 cookies[32] = {};
|
2016-11-24 18:04:37 +00:00
|
|
|
|
2016-11-26 02:49:26 +00:00
|
|
|
static int alloc_cookie() {
|
2018-08-13 17:22:40 +00:00
|
|
|
for (int i = 0; i < 32; ++i) {
|
|
|
|
word32 x = cookies[i];
|
2016-11-24 18:04:37 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
for (int j = 0; j < 32; ++j, x >>= 1) {
|
|
|
|
if (x & 0x01) continue;
|
2016-11-24 18:04:37 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
cookies[i] |= (1 << j);
|
|
|
|
return COOKIE_BASE + (i * 32 + j);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
2016-11-24 18:04:37 +00:00
|
|
|
}
|
|
|
|
|
2016-11-26 02:49:26 +00:00
|
|
|
static int free_cookie(int cookie) {
|
2018-08-13 17:22:40 +00:00
|
|
|
if (cookie < COOKIE_BASE) return -1;
|
|
|
|
cookie -= COOKIE_BASE;
|
|
|
|
if (cookie >= 32 *32) return -1;
|
2016-11-24 18:04:37 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
int chunk = cookie / 32;
|
|
|
|
int offset = 1 << (cookie % 32);
|
2016-11-24 18:04:37 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
word32 x = cookies[chunk];
|
2016-11-24 18:04:37 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if ((x & offset) == 0) return -1;
|
|
|
|
x &= ~offset;
|
|
|
|
cookies[chunk] = x;
|
|
|
|
return 0;
|
2016-11-24 18:04:37 +00:00
|
|
|
}
|
|
|
|
|
2016-10-12 16:18:23 +00:00
|
|
|
static struct fd_entry *fd_head = NULL;
|
|
|
|
|
|
|
|
static ino_t root_ino = 0;
|
|
|
|
static dev_t root_dev = 0;
|
|
|
|
static char *root = NULL;
|
|
|
|
static int read_only = 0;
|
|
|
|
|
|
|
|
char *g_cfg_host_path = ""; // must not be null.
|
|
|
|
int g_cfg_host_read_only = 0;
|
2018-07-07 04:49:36 +00:00
|
|
|
int g_cfg_host_crlf = 1;
|
|
|
|
int g_cfg_host_merlin = 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* simple malloc pool to simplify code. Should never need > 4 allocations.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static void *gc[16];
|
|
|
|
static void **gc_ptr = &gc[0];
|
|
|
|
static void *gc_malloc(size_t size) {
|
2018-08-13 17:22:40 +00:00
|
|
|
if (gc_ptr == &gc[16]) {
|
|
|
|
errno = ENOMEM;
|
|
|
|
return NULL;
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
void *ptr = malloc(size);
|
|
|
|
if (ptr) {
|
|
|
|
*gc_ptr++ = ptr;
|
|
|
|
}
|
|
|
|
return ptr;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void gc_free(void) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
while (gc_ptr > gc) free(*--gc_ptr);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *append_path(const char *a, const char *b) {
|
2018-08-13 17:22:40 +00:00
|
|
|
int aa = strlen(a);
|
|
|
|
int bb = strlen(b);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
char *cp = gc_malloc(aa + bb + 2);
|
|
|
|
if (!cp) return NULL;
|
|
|
|
memcpy(cp, a, aa);
|
|
|
|
int len = aa;
|
|
|
|
if (len && cp[len-1] != '/' && b[0] != '/') cp[len++] = '/';
|
|
|
|
memcpy(cp + len, b, bb);
|
|
|
|
len += bb;
|
|
|
|
cp[len] = 0;
|
|
|
|
while (len > 2 && cp[len - 1] == '/') cp[--len] = 0;
|
|
|
|
return cp;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static char *append_string(const char *a, const char *b) {
|
2018-08-13 17:22:40 +00:00
|
|
|
int aa = strlen(a);
|
|
|
|
int bb = strlen(b);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
char *cp = gc_malloc(aa + bb + 2);
|
|
|
|
if (!cp) return NULL;
|
|
|
|
memcpy(cp, a, aa);
|
|
|
|
int len = aa;
|
|
|
|
memcpy(cp + len, b, bb);
|
|
|
|
len += bb;
|
|
|
|
cp[len] = 0;
|
|
|
|
return cp;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
2016-11-26 02:49:26 +00:00
|
|
|
static char *gc_strdup(const char *src) {
|
2018-08-13 17:22:40 +00:00
|
|
|
if (!src) return "";
|
|
|
|
if (!*src) return "";
|
|
|
|
int len = strlen(src) + 1;
|
|
|
|
char *cp = gc_malloc(len);
|
|
|
|
memcpy(cp, src, len);
|
|
|
|
return cp;
|
2016-11-26 02:49:26 +00:00
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2017-09-19 03:28:39 +00:00
|
|
|
static word32 enoent(const char *path) {
|
2018-08-13 17:22:40 +00:00
|
|
|
/*
|
|
|
|
some op on path return ENOENT. check if it's
|
|
|
|
fileNotFound or pathNotFound
|
|
|
|
*/
|
|
|
|
char *p = (char *)path;
|
|
|
|
for(;;) {
|
|
|
|
struct stat st;
|
|
|
|
p = dirname(p);
|
|
|
|
if (p == NULL) break;
|
|
|
|
if (p[0] == '.' && p[1] == 0) break;
|
|
|
|
if (p[0] == '/' && p[1] == 0) break;
|
|
|
|
if (stat(p, &st) < 0) return pathNotFound;
|
|
|
|
}
|
|
|
|
return fileNotFound;
|
2017-09-19 03:28:39 +00:00
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
|
|
|
static word32 map_errno() {
|
2018-08-13 17:22:40 +00:00
|
|
|
switch(errno) {
|
|
|
|
case 0: return 0;
|
|
|
|
case EBADF:
|
|
|
|
return invalidAccess;
|
|
|
|
case EDQUOT:
|
|
|
|
case EFBIG:
|
|
|
|
return volumeFull;
|
|
|
|
case ENOENT:
|
|
|
|
return fileNotFound;
|
|
|
|
case ENOTDIR:
|
|
|
|
return pathNotFound;
|
|
|
|
case ENOMEM:
|
|
|
|
return outOfMem;
|
|
|
|
case EEXIST:
|
|
|
|
return dupPathname;
|
|
|
|
default:
|
|
|
|
return drvrIOError;
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
2017-09-19 03:28:39 +00:00
|
|
|
static word32 map_errno_path(const char *path) {
|
2018-08-13 17:22:40 +00:00
|
|
|
if (errno == ENOENT) return enoent(path);
|
|
|
|
return map_errno();
|
2017-09-19 03:28:39 +00:00
|
|
|
}
|
|
|
|
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2016-11-26 02:49:26 +00:00
|
|
|
static struct fd_entry *find_fd(int cookie) {
|
2018-08-13 17:22:40 +00:00
|
|
|
struct fd_entry *head = fd_head;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
while(head) {
|
|
|
|
if (head->cookie == cookie) return head;
|
|
|
|
head = head->next;
|
|
|
|
}
|
|
|
|
return NULL;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
2016-11-24 18:04:37 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
static void free_fd(struct fd_entry *e) {
|
|
|
|
if (!e) return;
|
|
|
|
if (e->cookie) free_cookie(e->cookie);
|
|
|
|
if (e->fd >= 0) close(e->fd);
|
|
|
|
if (e->dir) free_directory(e->dir);
|
|
|
|
free(e->path);
|
|
|
|
free(e);
|
2016-11-26 02:49:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct fd_entry *alloc_fd() {
|
2018-08-13 17:22:40 +00:00
|
|
|
struct fd_entry *e = calloc(sizeof(struct fd_entry), 1);
|
|
|
|
e->fd = -1;
|
|
|
|
return e;
|
2016-11-26 02:49:26 +00:00
|
|
|
}
|
2016-11-24 18:04:37 +00:00
|
|
|
|
|
|
|
|
2016-11-26 02:49:26 +00:00
|
|
|
static word32 remove_fd(int cookie) {
|
2018-08-13 17:22:40 +00:00
|
|
|
word32 rv = invalidRefNum;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
struct fd_entry *prev = NULL;
|
|
|
|
struct fd_entry *head = fd_head;
|
|
|
|
while (head) {
|
|
|
|
if (head->cookie == cookie) {
|
|
|
|
if (prev) prev->next = head->next;
|
|
|
|
else fd_head = head->next;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
free_fd(head);
|
|
|
|
rv = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
prev = head;
|
|
|
|
head = head->next;
|
|
|
|
}
|
|
|
|
return rv;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
2018-07-07 04:49:36 +00:00
|
|
|
static void cr_to_lf(byte *buffer, size_t size) {
|
2018-08-13 17:22:40 +00:00
|
|
|
size_t i;
|
|
|
|
for (i = 0; i < size; ++i) {
|
|
|
|
if (buffer[i] == '\r') buffer[i] = '\n';
|
|
|
|
}
|
2018-07-07 04:49:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void lf_to_cr(byte *buffer, size_t size) {
|
2018-08-13 17:22:40 +00:00
|
|
|
size_t i;
|
|
|
|
for (i = 0; i < size; ++i) {
|
|
|
|
if (buffer[i] == '\n') buffer[i] = '\r';
|
|
|
|
}
|
2018-07-07 04:49:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void merlin_to_text(byte *buffer, size_t size) {
|
2018-08-13 17:22:40 +00:00
|
|
|
size_t i;
|
|
|
|
for (i = 0; i < size; ++i) {
|
|
|
|
byte b = buffer[i];
|
|
|
|
if (b == 0xa0) b = '\t';
|
|
|
|
b &= 0x7f;
|
|
|
|
if (b == '\r') b = '\n';
|
|
|
|
buffer[i] = b;
|
|
|
|
}
|
2018-07-07 04:49:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void text_to_merlin(byte *buffer, size_t size) {
|
2018-08-13 17:22:40 +00:00
|
|
|
size_t i;
|
|
|
|
for (i = 0; i < size; ++i) {
|
|
|
|
byte b = buffer[i];
|
|
|
|
if (b == '\t') b = 0xa0;
|
|
|
|
if (b == '\n') b = '\r';
|
|
|
|
if (b != ' ') b |= 0x80;
|
|
|
|
buffer[i] = b;
|
|
|
|
}
|
2018-07-07 04:49:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t safe_read(struct fd_entry *e, byte *buffer, size_t count) {
|
2018-08-13 17:22:40 +00:00
|
|
|
int fd = e->fd;
|
|
|
|
int tr = e->translate;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
ssize_t ok = read(fd, buffer, count);
|
|
|
|
if (ok >= 0) {
|
|
|
|
size_t i;
|
|
|
|
if (tr == translate_crlf) {
|
|
|
|
for (i = 0; i < ok; ++i) {
|
|
|
|
if (buffer[i] == '\n') buffer[i] = '\r';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (tr == translate_merlin) {
|
|
|
|
for (i = 0; i < ok; ++i) {
|
|
|
|
unsigned char c = buffer[i];
|
|
|
|
if (c == '\t') c = 0xa0;
|
|
|
|
if (c == '\n') c = '\r';
|
|
|
|
if (c != ' ') c |= 0x80;
|
|
|
|
buffer[i] = c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
if (ok < 0 && errno == EINTR) continue;
|
|
|
|
return ok;
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
2018-07-07 04:49:36 +00:00
|
|
|
static ssize_t safe_write(struct fd_entry *e, byte *buffer, size_t count) {
|
2018-08-13 17:22:40 +00:00
|
|
|
int fd = e->fd;
|
|
|
|
int tr = e->translate;
|
|
|
|
|
|
|
|
if (tr == translate_crlf) {
|
|
|
|
size_t i;
|
|
|
|
for (i = 0; i < count; ++i) {
|
|
|
|
if (buffer[i] == '\r') buffer[i] = '\n';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (tr == translate_merlin) {
|
|
|
|
size_t i;
|
|
|
|
for (i = 0; i < count; ++i) {
|
|
|
|
unsigned char c = buffer[i];
|
|
|
|
if (c == 0xa0) c = '\t';
|
|
|
|
c &= 0x7f;
|
|
|
|
if (c == '\r') c = '\n';
|
|
|
|
buffer[i] = c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
ssize_t ok = write(fd, buffer, count);
|
|
|
|
if (ok >= 0) return ok;
|
|
|
|
if (ok < 0 && errno == EINTR) continue;
|
|
|
|
return ok;
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct file_info {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
time_t create_date;
|
|
|
|
time_t modified_date;
|
|
|
|
word16 access;
|
|
|
|
word16 storage_type;
|
|
|
|
word16 file_type;
|
|
|
|
word32 aux_type;
|
|
|
|
word32 eof;
|
|
|
|
word32 blocks;
|
|
|
|
word32 resource_eof;
|
|
|
|
word32 resource_blocks;
|
|
|
|
mode_t st_mode;
|
|
|
|
int has_fi;
|
|
|
|
byte finder_info[32];
|
2016-10-12 16:18:23 +00:00
|
|
|
};
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
static int hex(byte c) {
|
|
|
|
if (c >= '0' && c <= '9') return c - '0';
|
|
|
|
if (c >= 'a' && c <= 'f') return c + 10 - 'a';
|
|
|
|
if (c >= 'A' && c <= 'F') return c + 10 - 'A';
|
|
|
|
return 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int finder_info_to_filetype(const byte *buffer, word16 *file_type, word32 *aux_type) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (!memcmp("pdos", buffer + 4, 4))
|
|
|
|
{
|
|
|
|
if (buffer[0] == 'p') {
|
|
|
|
*file_type = buffer[1];
|
|
|
|
*aux_type = (buffer[2] << 8) | buffer[3];
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!memcmp("PSYS", buffer, 4)) {
|
|
|
|
*file_type = 0xff;
|
|
|
|
*aux_type = 0x0000;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!memcmp("PS16", buffer, 4)) {
|
|
|
|
*file_type = 0xb3;
|
|
|
|
*aux_type = 0x0000;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// old mpw method for encoding.
|
|
|
|
if (!isxdigit(buffer[0]) && isxdigit(buffer[1]) && buffer[2] == ' ' && buffer[3] == ' ')
|
|
|
|
{
|
|
|
|
*file_type = (hex(buffer[0]) << 8) | hex(buffer[1]);
|
|
|
|
*aux_type = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!memcmp("TEXT", buffer, 4)) {
|
|
|
|
*file_type = 0x04;
|
|
|
|
*aux_type = 0x0000;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!memcmp("BINA", buffer, 4)) {
|
|
|
|
*file_type = 0x00;
|
|
|
|
*aux_type = 0x0000;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!memcmp("dImgdCpy", buffer, 8)) {
|
|
|
|
*file_type = 0xe0;
|
|
|
|
*aux_type = 0x0005;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!memcmp("MIDI", buffer, 4)) {
|
|
|
|
*file_type = 0xd7;
|
|
|
|
*aux_type = 0x0000;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!memcmp("AIFF", buffer, 4)) {
|
|
|
|
*file_type = 0xd8;
|
|
|
|
*aux_type = 0x0000;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!memcmp("AIFC", buffer, 4)) {
|
|
|
|
*file_type = 0xd8;
|
|
|
|
*aux_type = 0x0001;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int file_type_to_finder_info(byte *buffer, word16 file_type, word32 aux_type) {
|
2018-08-13 17:22:40 +00:00
|
|
|
if (file_type > 0xff || aux_type > 0xffff) return -1;
|
|
|
|
|
|
|
|
if (!file_type && aux_type == 0x0000) {
|
|
|
|
memcpy(buffer, "BINApdos", 8);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (file_type == 0x04 && aux_type == 0x0000) {
|
|
|
|
memcpy(buffer, "TEXTpdos", 8);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (file_type == 0xff && aux_type == 0x0000) {
|
|
|
|
memcpy(buffer, "PSYSpdos", 8);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (file_type == 0xb3 && aux_type == 0x0000) {
|
|
|
|
memcpy(buffer, "PS16pdos", 8);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (file_type == 0xd7 && aux_type == 0x0000) {
|
|
|
|
memcpy(buffer, "MIDIpdos", 8);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (file_type == 0xd8 && aux_type == 0x0000) {
|
|
|
|
memcpy(buffer, "AIFFpdos", 8);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (file_type == 0xd8 && aux_type == 0x0001) {
|
|
|
|
memcpy(buffer, "AIFCpdos", 8);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (file_type == 0xe0 && aux_type == 0x0005) {
|
|
|
|
memcpy(buffer, "dImgdCpy", 8);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
memcpy(buffer, "p pdos", 8);
|
|
|
|
buffer[1] = (file_type) & 0xff;
|
|
|
|
buffer[2] = (aux_type >> 8) & 0xff;
|
|
|
|
buffer[3] = (aux_type) & 0xff;
|
|
|
|
return 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-11-24 18:04:37 +00:00
|
|
|
#if defined(__APPLE__)
|
2016-10-12 16:18:23 +00:00
|
|
|
static void get_file_xinfo(const char *path, struct file_info *fi) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
ssize_t tmp;
|
|
|
|
tmp = getxattr(path, XATTR_RESOURCEFORK_NAME, NULL, 0, 0, 0);
|
|
|
|
if (tmp < 0) tmp = 0;
|
|
|
|
fi->resource_eof = tmp;
|
|
|
|
fi->resource_blocks = (tmp + 511) / 512;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
tmp = getxattr(path, XATTR_FINDERINFO_NAME, fi->finder_info, 32, 0, 0);
|
|
|
|
if (tmp == 16 || tmp == 32) {
|
|
|
|
fi->has_fi = 1;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
finder_info_to_filetype(fi->finder_info, &fi->file_type, &fi->aux_type);
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
2016-10-30 18:21:07 +00:00
|
|
|
#elif defined(__sun)
|
2016-10-12 16:18:23 +00:00
|
|
|
static void get_file_xinfo(const char *path, struct file_info *fi) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
// can't stat an xattr directly?
|
|
|
|
int fd;
|
|
|
|
fd = attropen(path, XATTR_RESOURCEFORK_NAME, O_RDONLY);
|
|
|
|
if (fd >= 0) {
|
|
|
|
if (fstat(fd, &st) == 0) {
|
|
|
|
fi->resource_eof = st.st_size;
|
|
|
|
fi->resource_blocks = st.st_blocks;
|
|
|
|
}
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
fd = attropen(path, XATTR_FINDERINFO_NAME, O_RDONLY);
|
|
|
|
if (fd >= 0) {
|
|
|
|
int tmp = read(fd, fi->finder_info, 32);
|
|
|
|
if (tmp == 16 || tmp == 32) {
|
|
|
|
fi->has_fi = 1;
|
|
|
|
finder_info_to_filetype(fi->finder_info, &fi->file_type, &fi->aux_type);
|
|
|
|
}
|
|
|
|
close(fd);
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
2016-11-24 18:04:37 +00:00
|
|
|
#elif defined(__linux__)
|
2016-10-12 16:18:23 +00:00
|
|
|
static void get_file_xinfo(const char *path, struct file_info *fi) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
ssize_t tmp;
|
|
|
|
tmp = getxattr(path, "user.com.apple.ResourceFork", NULL, 0);
|
|
|
|
if (tmp < 0) tmp = 0;
|
|
|
|
fi->resource_eof = tmp;
|
|
|
|
fi->resource_blocks = (tmp + 511) / 512;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
tmp = getxattr(path, "user.com.apple.FinderInfo", fi->finder_info, 32);
|
|
|
|
if (tmp == 16 || tmp == 32) {
|
|
|
|
fi->has_fi = 1;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
finder_info_to_filetype(fi->finder_info, &fi->file_type, &fi->aux_type);
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
#else
|
|
|
|
static void get_file_xinfo(const char *path, struct file_info *fi) {
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-07-07 04:49:36 +00:00
|
|
|
#undef _
|
|
|
|
#define _(a, b, c) { a, sizeof(a) - 1, b, c }
|
|
|
|
struct ftype_entry {
|
2018-08-13 17:22:40 +00:00
|
|
|
char *ext;
|
|
|
|
unsigned length;
|
|
|
|
unsigned file_type;
|
|
|
|
unsigned aux_type;
|
2018-07-07 04:49:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct ftype_entry suffixes[] = {
|
2018-08-13 17:22:40 +00:00
|
|
|
_("c", 0xb0, 0x0008),
|
|
|
|
_("cc", 0xb0, 0x0008),
|
|
|
|
_("h", 0xb0, 0x0008),
|
|
|
|
_("rez", 0xb0, 0x0015),
|
|
|
|
_("asm", 0xb0, 0x0003),
|
|
|
|
_("mac", 0xb0, 0x0003),
|
|
|
|
_("pas", 0xb0, 0x0005),
|
|
|
|
_("txt", 0x04, 0x0000),
|
|
|
|
_("text", 0x04, 0x0000),
|
|
|
|
_("s", 0x04, 0x0000),
|
|
|
|
{ 0, 0, 0, 0}
|
2018-07-07 04:49:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct ftype_entry prefixes[] = {
|
2018-08-13 17:22:40 +00:00
|
|
|
_("m16.", 0xb0, 0x0003),
|
|
|
|
_("e16.", 0xb0, 0x0003),
|
|
|
|
{ 0, 0, 0, 0}
|
2018-07-07 04:49:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#undef _
|
|
|
|
|
2016-10-12 16:18:23 +00:00
|
|
|
static word32 get_file_info(const char *path, struct file_info *fi) {
|
2018-08-13 17:22:40 +00:00
|
|
|
struct stat st;
|
|
|
|
memset(fi, 0, sizeof(*fi));
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
int ok = stat(path, &st);
|
|
|
|
if (ok < 0) return map_errno();
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
fi->eof = st.st_size;
|
|
|
|
fi->blocks = st.st_blocks;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
fi->create_date = st.st_ctime;
|
|
|
|
fi->modified_date = st.st_mtime;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
#if defined(__APPLE__)
|
|
|
|
fi->create_date = st.st_birthtime;
|
2016-10-12 16:18:23 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
fi->st_mode = st.st_mode;
|
|
|
|
|
|
|
|
if (S_ISDIR(st.st_mode)) {
|
|
|
|
fi->storage_type = 0x0d;
|
|
|
|
fi->file_type = 0x0f;
|
|
|
|
if (st.st_ino == root_ino && st.st_dev == root_dev)
|
|
|
|
fi->storage_type = 0x0f;
|
|
|
|
} else if (S_ISREG(st.st_mode)) {
|
|
|
|
fi->file_type = 0x06;
|
|
|
|
if (st.st_size < 0x200) fi->storage_type = seedling;
|
|
|
|
else if (st.st_size < 0x20000) fi->storage_type = 0x0002;
|
|
|
|
else fi->storage_type = 0x0003;
|
|
|
|
} else {
|
|
|
|
fi->storage_type = st.st_mode & S_IFMT;
|
|
|
|
fi->file_type = 0;
|
|
|
|
}
|
|
|
|
// 0x01 = read enable
|
|
|
|
// 0x02 = write enable
|
|
|
|
// 0x04 = invisible
|
|
|
|
// 0x08 = reserved
|
|
|
|
// 0x10 = reserved
|
|
|
|
// 0x20 = backup needed
|
|
|
|
// 0x40 = rename enable
|
|
|
|
// 0x80 = destroy enable
|
|
|
|
|
|
|
|
fi->access = 0xc3; // placeholder...
|
|
|
|
|
|
|
|
if (S_ISREG(st.st_mode)) {
|
|
|
|
get_file_xinfo(path, fi);
|
|
|
|
|
|
|
|
if (!fi->has_fi) {
|
|
|
|
/* guess the file type / auxtype based on extension */
|
|
|
|
int n;
|
|
|
|
const char *dot = NULL;
|
|
|
|
const char *slash = NULL;
|
|
|
|
|
|
|
|
for(n = 0;; ++n) {
|
|
|
|
char c = path[n];
|
|
|
|
if (c == 0) break;
|
|
|
|
else if (c == '/') { slash = path + n + 1; dot = NULL; }
|
|
|
|
else if (c == '.') dot = path + n + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dot && *dot) {
|
|
|
|
for (n = 0; n < sizeof(suffixes) / sizeof(suffixes[0]); ++n) {
|
|
|
|
if (!suffixes[n].ext) break;
|
|
|
|
if (!strcasecmp(dot, suffixes[n].ext)) {
|
|
|
|
fi->file_type = suffixes[n].file_type;
|
|
|
|
fi->aux_type = suffixes[n].aux_type;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// get file type/aux type
|
|
|
|
|
|
|
|
if (fi->resource_eof) fi->storage_type = 0x0005;
|
|
|
|
|
|
|
|
return 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-10-31 01:24:06 +00:00
|
|
|
#if defined(__APPLE__)
|
2016-10-12 16:18:23 +00:00
|
|
|
static word32 set_file_info(const char *path, struct file_info *fi) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
int ok;
|
|
|
|
struct attrlist list;
|
|
|
|
unsigned i = 0;
|
|
|
|
struct timespec dates[2];
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (fi->has_fi && fi->storage_type != 0x0d) {
|
|
|
|
ok = setxattr(path, XATTR_FINDERINFO_NAME, fi->finder_info, 32, 0, 0);
|
|
|
|
if (ok < 0) return map_errno();
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
memset(&list, 0, sizeof(list));
|
|
|
|
memset(dates, 0, sizeof(dates));
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
list.bitmapcount = ATTR_BIT_MAP_COUNT;
|
|
|
|
list.commonattr = 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (fi->create_date)
|
|
|
|
{
|
|
|
|
dates[i++].tv_sec = fi->create_date;
|
|
|
|
list.commonattr |= ATTR_CMN_CRTIME;
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (fi->modified_date)
|
|
|
|
{
|
|
|
|
dates[i++].tv_sec = fi->modified_date;
|
|
|
|
list.commonattr |= ATTR_CMN_MODTIME;
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
ok = 0;
|
|
|
|
if (i) ok = setattrlist(path, &list, dates, i * sizeof(struct timespec), 0);
|
|
|
|
return 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
2018-08-13 17:22:40 +00:00
|
|
|
#elif defined(__sun)
|
2016-10-12 16:18:23 +00:00
|
|
|
static word32 set_file_info(const char *path, struct file_info *fi) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (fi->has_fi && fi->storage_type != 0x0d) {
|
|
|
|
int fd = attropen(path, XATTR_FINDERINFO_NAME, O_WRONLY | O_CREAT, 0666);
|
|
|
|
if (fd < 0) return map_errno();
|
|
|
|
write(fd, fi->finder_info, 32);
|
|
|
|
close(fd);
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (fi->modified_date) {
|
|
|
|
struct timeval times[2];
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
memset(times, 0, sizeof(times));
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
//times[0] = 0; // access
|
|
|
|
times[1].tv_sec = fi.modified_date; // modified
|
|
|
|
int ok = utimes(path, times);
|
|
|
|
if (ok < 0) return map_errno();
|
|
|
|
}
|
|
|
|
return 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
2016-11-24 18:04:37 +00:00
|
|
|
#elif defined(__linux__)
|
2016-10-12 16:18:23 +00:00
|
|
|
static word32 set_file_info(const char *path, struct file_info *fi) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (fi->has_fi && fi->storage_type != 0x0d) {
|
|
|
|
int ok = setxattr(path, "user.apple.FinderInfo", fi->finder_info, 32, 0);
|
|
|
|
if (ok < 0) return map_errno();
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (fi->modified_date) {
|
|
|
|
struct timeval times[2];
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
memset(times, 0, sizeof(times));
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
//times[0] = 0; // access
|
|
|
|
times[1].tv_sec = fi->modified_date; // modified
|
|
|
|
int ok = utimes(path, times);
|
|
|
|
if (ok < 0) return map_errno();
|
|
|
|
}
|
|
|
|
return 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
static word32 set_file_info(const char *path, struct file_info *fi) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (fi->modified_date) {
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
struct timeval times[2];
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
memset(times, 0, sizeof(times));
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
times[0] = 0; // access
|
|
|
|
times[1].tv_sec = fi->modified_date; // modified
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
int ok = utimes(path, times);
|
|
|
|
if (ok < 0) return map_errno();
|
|
|
|
}
|
|
|
|
return 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if this is an absolute path, verify and skip past :Host:
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static const char *check_path(const char *in, word32 *error) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
word32 tmp;
|
|
|
|
if (!error) error = &tmp;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
*error = 0;
|
|
|
|
if (!in) return "";
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
/* check for .. */
|
|
|
|
const char *cp = in;
|
|
|
|
do {
|
|
|
|
while (*cp == '/') ++cp;
|
|
|
|
if (cp[0] == '.' && cp[1] == '.' && (cp[2] == '/' || cp[2] == 0))
|
|
|
|
{
|
|
|
|
*error = badPathSyntax;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
cp = strchr(cp, '/');
|
|
|
|
} while(cp);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (in[0] != '/') return in;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (strncasecmp(in, "/Host", 5) == 0 && (in[5] == '/' || in[5] == 0)) {
|
|
|
|
in += 5;
|
|
|
|
while (*in == '/') ++in;
|
|
|
|
return in;
|
|
|
|
}
|
|
|
|
*error = volNotFound;
|
|
|
|
return NULL;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static char * get_gsstr(word32 ptr) {
|
2018-08-13 17:22:40 +00:00
|
|
|
if (!ptr) return NULL;
|
|
|
|
int length = get_memory16_c(ptr, 0);
|
|
|
|
ptr += 2;
|
|
|
|
char *str = gc_malloc(length + 1);
|
|
|
|
for (int i = 0; i < length; ++i) {
|
|
|
|
char c = get_memory_c(ptr+i, 0);
|
|
|
|
if (c == ':') c = '/';
|
|
|
|
str[i] = c;
|
|
|
|
}
|
|
|
|
str[length] = 0;
|
|
|
|
return str;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static char * get_pstr(word32 ptr) {
|
2018-08-13 17:22:40 +00:00
|
|
|
if (!ptr) return NULL;
|
|
|
|
int length = get_memory16_c(ptr, 0);
|
|
|
|
ptr += 2;
|
|
|
|
char *str = gc_malloc(length + 1);
|
|
|
|
for (int i = 0; i < length; ++i) {
|
|
|
|
char c = get_memory_c(ptr+i, 0);
|
|
|
|
if (c == ':') c = '/';
|
|
|
|
str[i] = c;
|
|
|
|
}
|
|
|
|
str[length] = 0;
|
|
|
|
return str;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static word32 set_gsstr(word32 ptr, const char *str) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (!ptr) return paramRangeErr;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
int l = str ? strlen(str) : 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
word32 cap = get_memory16_c(ptr, 0);
|
|
|
|
ptr += 2;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (cap < 4) return paramRangeErr;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
set_memory16_c(ptr, l, 0);
|
|
|
|
ptr += 2;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (cap < l + 4) return buffTooSmall;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
for (int i = 0; i < l; ++i) {
|
|
|
|
char c = *str++;
|
|
|
|
if (c == '/') c = ':';
|
|
|
|
set_memory_c(ptr++, c, 0);
|
|
|
|
}
|
|
|
|
return 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static word32 set_gsstr_truncate(word32 ptr, const char *str) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (!ptr) return paramRangeErr;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
int l = str ? strlen(str) : 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
word32 cap = get_memory16_c(ptr, 0);
|
|
|
|
ptr += 2;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (cap < 4) return paramRangeErr;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
set_memory16_c(ptr, l, 0);
|
|
|
|
ptr += 2;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
// get dir entry copies data even
|
|
|
|
// if buffTooSmall...
|
|
|
|
int rv = 0;
|
|
|
|
if (cap < l + 4) {
|
|
|
|
l = cap - 4;
|
|
|
|
rv = buffTooSmall;
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
for (int i = 0; i < l; ++i) {
|
|
|
|
char c = *str++;
|
|
|
|
if (c == '/') c = ':';
|
|
|
|
set_memory_c(ptr++, c, 0);
|
|
|
|
}
|
|
|
|
return rv;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static word32 set_pstr(word32 ptr, const char *str) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (!ptr) return paramRangeErr;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
int l = str ? strlen(str) : 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (l > 255) return buffTooSmall;
|
|
|
|
// / is the pascal separator.
|
|
|
|
set_memory_c(ptr++, l, 0);
|
|
|
|
for (int i = 0; i < l; ++i) {
|
|
|
|
set_memory_c(ptr++, *str++, 0);
|
|
|
|
}
|
|
|
|
return 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static word32 set_option_list(word32 ptr, word16 fstID, const byte *data, int size) {
|
2018-08-13 17:22:40 +00:00
|
|
|
if (!ptr) return 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
// totalSize
|
|
|
|
word32 cap = get_memory16_c(ptr, 0);
|
|
|
|
ptr += 2;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (cap < 4) return paramRangeErr;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
// reqSize
|
|
|
|
set_memory16_c(ptr, size + 2, 0);
|
|
|
|
ptr += 2;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (cap < size + 6) return buffTooSmall;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
// fileSysID.
|
|
|
|
set_memory16_c(ptr, fstID, 0);
|
|
|
|
ptr += 2;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
for (int i = 0; i < size; ++i)
|
|
|
|
set_memory_c(ptr++, *data++, 0);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
return 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* converts time_t to a gs/os readhextime date/time record.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void set_date_time_rec(word32 ptr, time_t time) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (time == 0) {
|
|
|
|
for (int i = 0; i < 8; ++i) set_memory_c(ptr++, 0, 0);
|
|
|
|
return;
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
struct tm *tm = localtime(&time);
|
|
|
|
if (tm->tm_sec == 60) tm->tm_sec = 59; /* leap second */
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
set_memory_c(ptr++, tm->tm_sec, 0);
|
|
|
|
set_memory_c(ptr++, tm->tm_min, 0);
|
|
|
|
set_memory_c(ptr++, tm->tm_hour, 0);
|
|
|
|
set_memory_c(ptr++, tm->tm_year, 0);
|
|
|
|
set_memory_c(ptr++, tm->tm_mday - 1, 0);
|
|
|
|
set_memory_c(ptr++, tm->tm_mon, 0);
|
|
|
|
set_memory_c(ptr++, 0, 0);
|
|
|
|
set_memory_c(ptr++, tm->tm_wday + 1, 0);
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* converts time_t to a prodos16 date/time record.
|
|
|
|
*/
|
|
|
|
static void set_date_time(word32 ptr, time_t time) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (time == 0) {
|
|
|
|
for (int i = 0; i < 4; ++i) set_memory_c(ptr++, 0, 0);
|
|
|
|
return;
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
struct tm *tm = localtime(&time);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
word16 tmp = 0;
|
|
|
|
tmp |= (tm->tm_year % 100) << 9;
|
|
|
|
tmp |= tm->tm_mon << 5;
|
|
|
|
tmp |= tm->tm_mday;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
set_memory16_c(ptr, tmp, 0);
|
|
|
|
ptr += 2;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
tmp = 0;
|
|
|
|
tmp |= tm->tm_hour << 8;
|
|
|
|
tmp |= tm->tm_min;
|
|
|
|
set_memory16_c(ptr, tmp, 0);
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static time_t get_date_time(word32 ptr) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
word16 a = get_memory16_c(ptr + 0, 0);
|
|
|
|
word16 b = get_memory16_c(ptr + 2, 0);
|
|
|
|
if (!a && !b) return 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
struct tm tm;
|
|
|
|
memset(&tm, 0, sizeof(tm));
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
tm.tm_year = (a >> 9) & 0x7f;
|
|
|
|
tm.tm_mon = ((a >> 5) & 0x0f) - 1;
|
|
|
|
tm.tm_mday = (a >> 0) & 0x1f;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
tm.tm_hour = (b >> 8) & 0x1f;
|
|
|
|
tm.tm_min = (b >> 0) & 0x3f;
|
|
|
|
tm.tm_sec = 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
tm.tm_isdst = -1;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
// 00 - 39 => 2000-2039
|
|
|
|
// 40 - 99 => 1940-1999
|
|
|
|
if (tm.tm_year < 40) tm.tm_year += 100;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
return mktime(&tm);
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static time_t get_date_time_rec(word32 ptr) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
byte buffer[8];
|
|
|
|
for (int i = 0; i < 8; ++i) buffer[i] = get_memory_c(ptr++, 0);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (!memcmp(buffer, "\x00\x00\x00\x00\x00\x00\x00\x00", 8)) return 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
struct tm tm;
|
|
|
|
memset(&tm, 0, sizeof(tm));
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
tm.tm_sec = buffer[0];
|
|
|
|
tm.tm_min = buffer[1];
|
|
|
|
tm.tm_hour = buffer[2];
|
|
|
|
tm.tm_year = buffer[3];
|
|
|
|
tm.tm_mday = buffer[4] + 1;
|
|
|
|
tm.tm_mon = buffer[5];
|
|
|
|
tm.tm_isdst = -1;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
return mktime(&tm);
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static char *get_path1(void) {
|
2018-08-13 17:22:40 +00:00
|
|
|
word32 direct = engine.direct;
|
|
|
|
word16 flags = get_memory16_c(direct + dp_path_flag, 0);
|
|
|
|
if (flags & (1 << 14))
|
|
|
|
return get_gsstr( get_memory24_c(direct + dp_path1_ptr, 0));
|
|
|
|
return NULL;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static char *get_path2(void) {
|
2018-08-13 17:22:40 +00:00
|
|
|
word32 direct = engine.direct;
|
|
|
|
word16 flags = get_memory16_c(direct + dp_path_flag, 0);
|
|
|
|
if (flags & (1 << 6))
|
|
|
|
return get_gsstr( get_memory24_c(direct + dp_path2_ptr, 0));
|
|
|
|
return NULL;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define SEC() engine.psr |= 1
|
|
|
|
#define CLC() engine.psr &= ~1
|
|
|
|
|
|
|
|
|
|
|
|
static word32 fst_shutdown(void) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
// close any remaining files.
|
|
|
|
struct fd_entry *head = fd_head;
|
|
|
|
while (head) {
|
|
|
|
struct fd_entry *next = head->next;
|
2016-11-26 02:49:26 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
free_fd(head);
|
|
|
|
head = next;
|
|
|
|
}
|
|
|
|
return 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static word32 fst_startup(void) {
|
2018-08-13 17:22:40 +00:00
|
|
|
// if restart, close any previous files.
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
struct stat st;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (!g_cfg_host_path) return invalidFSTop;
|
|
|
|
if (!*g_cfg_host_path) return invalidFSTop;
|
|
|
|
if (root) free(root);
|
|
|
|
root = strdup(g_cfg_host_path);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
read_only = g_cfg_host_read_only;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
fst_shutdown();
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
memset(&cookies, 0, sizeof(cookies));
|
|
|
|
if (stat(root, &st) < 0) {
|
|
|
|
fprintf(stderr, "%s does not exist\n", root);
|
|
|
|
return invalidFSTop;
|
|
|
|
}
|
|
|
|
if (!S_ISDIR(st.st_mode)) {
|
|
|
|
fprintf(stderr, "%s is not a directory\n", root);
|
|
|
|
return invalidFSTop;
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
root_ino = st.st_ino;
|
|
|
|
root_dev = st.st_dev;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
return 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static word32 fst_create(int class, const char *path) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
word32 pb = get_memory24_c(engine.direct + dp_param_blk_ptr, 0);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
struct file_info fi;
|
|
|
|
memset(&fi, 0, sizeof(fi));
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
word16 pcount = 0;
|
|
|
|
if (class) {
|
|
|
|
pcount = get_memory16_c(pb, 0);
|
|
|
|
if (pcount >= 2) fi.access = get_memory16_c(pb + CreateRecGS_access, 0);
|
|
|
|
if (pcount >= 3) fi.file_type = get_memory16_c(pb + CreateRecGS_fileType, 0);
|
|
|
|
if (pcount >= 4) fi.aux_type = get_memory32_c(pb + CreateRecGS_auxType, 0);
|
|
|
|
if (pcount >= 5) fi.storage_type = get_memory16_c(pb + CreateRecGS_storageType, 0);
|
|
|
|
if (pcount >= 6) fi.eof = get_memory32_c(pb + CreateRecGS_eof, 0);
|
|
|
|
if (pcount >= 7) fi.resource_eof = get_memory32_c(pb + CreateRecGS_resourceEOF, 0);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (pcount >= 4) {
|
|
|
|
file_type_to_finder_info(fi.finder_info, fi.file_type, fi.aux_type);
|
|
|
|
fi.has_fi = 1;
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
} else {
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
fi.access = get_memory16_c(pb + CreateRec_fAccess, 0);
|
|
|
|
fi.file_type = get_memory16_c(pb + CreateRec_fileType, 0);
|
|
|
|
fi.aux_type = get_memory32_c(pb + CreateRec_auxType, 0);
|
|
|
|
fi.storage_type = get_memory16_c(pb + CreateRec_storageType, 0);
|
|
|
|
fi.create_date = get_date_time(pb + CreateRec_createDate);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
file_type_to_finder_info(fi.finder_info, fi.file_type, fi.aux_type);
|
|
|
|
fi.has_fi = 1;
|
|
|
|
}
|
|
|
|
int ok;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (fi.storage_type == 0 && fi.file_type == 0x0f)
|
|
|
|
fi.storage_type = 0x0d;
|
2017-09-19 03:28:39 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (fi.storage_type == 0x0d) {
|
|
|
|
ok = mkdir(path, 0777);
|
|
|
|
if (ok < 0) {
|
|
|
|
return map_errno_path(path);
|
|
|
|
}
|
2017-09-19 03:28:39 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (class) {
|
|
|
|
if (pcount >= 5) set_memory16_c(pb + CreateRecGS_storageType, fi.storage_type, 0);
|
|
|
|
} else {
|
|
|
|
set_memory16_c(pb + CreateRec_storageType, fi.storage_type, 0);
|
|
|
|
}
|
2017-09-19 03:28:39 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (fi.storage_type <= 3 || fi.storage_type == 0x05) {
|
|
|
|
// normal file.
|
|
|
|
// 0x05 is an extended/resource file but we don't do anything special.
|
|
|
|
ok = open(path, O_WRONLY | O_CREAT | O_EXCL, 0666);
|
|
|
|
if (ok < 0) return map_errno_path(path);
|
|
|
|
// set ftype, auxtype...
|
|
|
|
set_file_info(path, &fi);
|
|
|
|
close(ok);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
fi.storage_type = 1;
|
2017-09-19 03:28:39 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (class) {
|
|
|
|
if (pcount >= 5) set_memory16_c(pb + CreateRecGS_storageType, fi.storage_type, 0);
|
|
|
|
} else {
|
|
|
|
set_memory16_c(pb + CreateRec_storageType, fi.storage_type, 0);
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (fi.storage_type == 0x8005) {
|
|
|
|
// convert an existing file to an extended file.
|
|
|
|
// this checks that the file exists and has a 0-sized resource.
|
|
|
|
word32 rv = get_file_info(path, &fi);
|
|
|
|
if (rv) return rv;
|
|
|
|
if (fi.storage_type == extendedFile) return resExistsErr;
|
|
|
|
if (fi.storage_type < seedling || fi.storage_type > tree) return resAddErr;
|
|
|
|
return 0;
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
// other storage types?
|
|
|
|
return badStoreType;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static word32 fst_destroy(int class, const char *path) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
struct stat st;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (!path) return badStoreType;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (stat(path, &st) < 0) {
|
|
|
|
return map_errno_path(path);
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
// can't delete volume root.
|
|
|
|
if (st.st_ino == root_ino && st.st_dev == root_dev) {
|
|
|
|
return badStoreType;
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
int ok = S_ISDIR(st.st_mode) ? rmdir(path) : unlink(path);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (ok < 0) return map_errno_path(path);
|
|
|
|
return 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static word32 fst_set_file_info(int class, const char *path) {
|
|
|
|
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
word32 pb = get_memory24_c(engine.direct + dp_param_blk_ptr, 0);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
struct file_info fi;
|
|
|
|
memset(&fi, 0, sizeof(fi));
|
2016-10-12 16:18:23 +00:00
|
|
|
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
// load up existing file types / finder info.
|
|
|
|
get_file_xinfo(path, &fi);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
word32 option_list = 0;
|
|
|
|
if (class) {
|
|
|
|
word16 pcount = get_memory16_c(pb, 0);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (pcount >= 2) fi.access = get_memory16_c(pb + FileInfoRecGS_access, 0);
|
|
|
|
if (pcount >= 3) fi.file_type = get_memory16_c(pb + FileInfoRecGS_fileType, 0);
|
|
|
|
if (pcount >= 4) fi.aux_type = get_memory32_c(pb + FileInfoRecGS_auxType, 0);
|
|
|
|
// reserved.
|
|
|
|
//if (pcount >= 5) fi.storage_type = get_memory16_c(pb + FileInfoRecGS_storageType, 0);
|
|
|
|
if (pcount >= 6) fi.create_date = get_date_time_rec(pb + FileInfoRecGS_createDateTime);
|
|
|
|
if (pcount >= 7) fi.modified_date = get_date_time_rec(pb + FileInfoRecGS_modDateTime);
|
|
|
|
if (pcount >= 8) option_list = get_memory24_c(pb + FileInfoRecGS_optionList, 0);
|
|
|
|
// remainder reserved
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (pcount >= 4) {
|
|
|
|
file_type_to_finder_info(fi.finder_info, fi.file_type, fi.aux_type);
|
|
|
|
fi.has_fi = 1;
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
} else {
|
|
|
|
fi.access = get_memory16_c(pb + FileRec_fAccess, 0);
|
|
|
|
fi.file_type = get_memory16_c(pb + FileRec_fileType, 0);
|
|
|
|
fi.aux_type = get_memory32_c(pb + FileRec_auxType, 0);
|
|
|
|
// reserved.
|
|
|
|
//fi.storage_type = get_memory32_c(pb + FileRec_storageType, 0);
|
|
|
|
fi.create_date = get_date_time(pb + FileRec_createDate);
|
|
|
|
fi.modified_date = get_date_time(pb + FileRec_modDate);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
file_type_to_finder_info(fi.finder_info, fi.file_type, fi.aux_type);
|
|
|
|
fi.has_fi = 1;
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (option_list) {
|
|
|
|
// total size, req size, fst id, data...
|
|
|
|
int total_size = get_memory16_c(option_list + 0, 0);
|
|
|
|
int req_size = get_memory16_c(option_list + 2, 0);
|
|
|
|
int fst_id = get_memory16_c(option_list + 4, 0);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
int size = req_size - 6;
|
|
|
|
if ((fst_id == proDOSFSID || fst_id == hfsFSID || fst_id == appleShareFSID) && size >= 32) {
|
|
|
|
fi.has_fi = 1;
|
|
|
|
for (int i = 0; i <32; ++i)
|
|
|
|
fi.finder_info[i] = get_memory_c(option_list + 6 + i, 0);
|
|
|
|
}
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
return set_file_info(path, &fi);
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static word32 fst_get_file_info(int class, const char *path) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
word32 pb = get_memory24_c(engine.direct + dp_param_blk_ptr, 0);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
struct file_info fi;
|
|
|
|
int rv = 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
rv = get_file_info(path, &fi);
|
|
|
|
if (rv) return rv;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (class) {
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
word16 pcount = get_memory16_c(pb, 0);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (pcount >= 2) set_memory16_c(pb + FileInfoRecGS_access, fi.access, 0);
|
|
|
|
if (pcount >= 3) set_memory16_c(pb + FileInfoRecGS_fileType, fi.file_type, 0);
|
|
|
|
if (pcount >= 4) set_memory32_c(pb + FileInfoRecGS_auxType, fi.aux_type, 0);
|
|
|
|
if (pcount >= 5) set_memory16_c(pb + FileInfoRecGS_storageType, fi.storage_type, 0);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (pcount >= 6) set_date_time_rec(pb + FileInfoRecGS_createDateTime, fi.create_date);
|
|
|
|
if (pcount >= 7) set_date_time_rec(pb + FileInfoRecGS_modDateTime, fi.modified_date);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (pcount >= 8) {
|
|
|
|
word16 fst_id = hfsFSID;
|
|
|
|
//if (fi.storage_type == 0x0f) fst_id = mfsFSID;
|
|
|
|
word32 option_list = get_memory24_c(pb + FileInfoRecGS_optionList, 0);
|
|
|
|
rv = set_option_list(option_list, fst_id, fi.finder_info, fi.has_fi ? 32 : 0);
|
|
|
|
}
|
|
|
|
if (pcount >= 9) set_memory32_c(pb + FileInfoRecGS_eof, fi.eof, 0);
|
|
|
|
if (pcount >= 10) set_memory32_c(pb + FileInfoRecGS_blocksUsed, fi.blocks, 0);
|
|
|
|
if (pcount >= 11) set_memory32_c(pb + FileInfoRecGS_resourceEOF, fi.resource_eof, 0);
|
|
|
|
if (pcount >= 12) set_memory32_c(pb + FileInfoRecGS_resourceBlocks, fi.resource_blocks, 0);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
} else {
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
set_memory16_c(pb + FileRec_fAccess, fi.access, 0);
|
|
|
|
set_memory16_c(pb + FileRec_fileType, fi.file_type, 0);
|
|
|
|
set_memory32_c(pb + FileRec_auxType, fi.aux_type, 0);
|
|
|
|
set_memory16_c(pb + FileRec_storageType, fi.storage_type, 0);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
set_date_time(pb + FileRec_createDate, fi.create_date);
|
|
|
|
set_date_time(pb + FileRec_modDate, fi.modified_date);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
set_memory32_c(pb + FileRec_blocksUsed, fi.blocks, 0);
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
return rv;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static word32 fst_judge_name(int class, char *path) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (class == 0) return invalidClass;
|
|
|
|
|
|
|
|
|
|
|
|
word32 pb = get_memory24_c(engine.direct + dp_param_blk_ptr, 0);
|
|
|
|
word16 pcount = get_memory16_c(pb, 0);
|
|
|
|
word16 name_type = get_memory16_c(pb + JudgeNameRecGS_nameType, 0);
|
|
|
|
word32 name = pcount >= 5 ? get_memory24_c(pb + JudgeNameRecGS_name, 0) : 0;
|
|
|
|
|
|
|
|
// 255 max length.
|
|
|
|
if (pcount >= 4) set_memory16_c(pb + JudgeNameRecGS_maxLen, 255, 0);
|
|
|
|
if (pcount >= 6) set_memory16_c(pb + JudgeNameRecGS_nameFlags, 0, 0);
|
|
|
|
|
|
|
|
word16 nameFlags = 0;
|
|
|
|
word16 rv = 0;
|
|
|
|
|
|
|
|
if (name) {
|
|
|
|
word16 cap = get_memory16_c(name, 0);
|
|
|
|
if (cap < 4) return buffTooSmall;
|
|
|
|
word16 length = get_memory16_c(name + 2, 0);
|
|
|
|
if (length == 0) {
|
|
|
|
nameFlags |= 1 << 13;
|
|
|
|
rv = set_gsstr(name, "A");
|
|
|
|
} else {
|
|
|
|
// if volume name, only allow "Host" ?
|
|
|
|
if (length > 255) nameFlags |= 1 << 14;
|
|
|
|
for (int i = 0; i < length; ++i) {
|
|
|
|
char c = get_memory_c(name + 4 + i, 0);
|
|
|
|
if (c == 0 || c == ':' || c == '/' || c == '\\') {
|
|
|
|
nameFlags |= 1 << 15;
|
|
|
|
set_memory_c(name + 4 + i, '.', 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pcount >= 6) set_memory16_c(pb + JudgeNameRecGS_nameFlags, nameFlags, 0);
|
|
|
|
return rv;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static word32 fst_volume(int class) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
word32 pb = get_memory24_c(engine.direct + dp_param_blk_ptr, 0);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
word32 rv = 0;
|
|
|
|
if (class) {
|
|
|
|
word16 pcount = get_memory16_c(pb, 0);
|
|
|
|
if (pcount >= 2) rv = set_gsstr(get_memory24_c(pb + VolumeRecGS_volName, 0), ":Host");
|
|
|
|
// finder bug -- if used blocks is 0, doesn't display header.
|
|
|
|
if (pcount >= 3) set_memory32_c(pb + VolumeRecGS_totalBlocks, 0x007fffff, 0);
|
|
|
|
if (pcount >= 4) set_memory32_c(pb + VolumeRecGS_freeBlocks, 0x007fffff-1, 0);
|
|
|
|
if (pcount >= 5) set_memory16_c(pb + VolumeRecGS_fileSysID, mfsFSID, 0);
|
|
|
|
if (pcount >= 6) set_memory16_c(pb + VolumeRecGS_blockSize, 512, 0);
|
|
|
|
// handled via gs/os
|
|
|
|
//if (pcount >= 7) set_memory16_c(pb + VolumeRecGS_characteristics, 0, 0);
|
|
|
|
//if (pcount >= 8) set_memory16_c(pb + VolumeRecGS_deviceID, 0, 0);
|
|
|
|
} else {
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
// prodos 16 uses / sep.
|
|
|
|
rv = set_pstr(get_memory24_c(pb + VolumeRec_volName, 0), "/Host");
|
|
|
|
set_memory32_c(pb + VolumeRec_totalBlocks, 0x007fffff, 0);
|
|
|
|
set_memory32_c(pb + VolumeRec_freeBlocks, 0x007fffff-1, 0);
|
|
|
|
set_memory16_c(pb + VolumeRec_fileSysID, mfsFSID, 0);
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
return rv;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static word32 fst_clear_backup(int class, const char *path) {
|
2018-08-13 17:22:40 +00:00
|
|
|
return invalidFSTop;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
2016-11-24 18:04:37 +00:00
|
|
|
|
2016-10-12 16:18:23 +00:00
|
|
|
static int open_data_fork(const char *path, word16 *access, word16 *error) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
int fd = -1;
|
|
|
|
for (;;) {
|
|
|
|
|
|
|
|
switch(*access) {
|
|
|
|
case readEnableAllowWrite:
|
|
|
|
case readWriteEnable:
|
|
|
|
fd = open(path, O_RDWR);
|
|
|
|
break;
|
|
|
|
case readEnable:
|
|
|
|
fd = open(path, O_RDONLY);
|
|
|
|
break;
|
|
|
|
case writeEnable:
|
|
|
|
fd = open(path, O_WRONLY);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*access == readEnableAllowWrite) {
|
|
|
|
if (fd < 0) {
|
|
|
|
*access = readEnable;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
*access = readWriteEnable;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (fd < 0) {
|
|
|
|
*error = map_errno_path(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
#if defined(__APPLE__)
|
2016-10-12 16:18:23 +00:00
|
|
|
static int open_resource_fork(const char *path, word16 *access, word16 *error) {
|
2018-08-13 17:22:40 +00:00
|
|
|
// os x / hfs/apfs don't need to specifically create a resource fork.
|
|
|
|
// or do they?
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
char *rpath = append_path(path, _PATH_RSRCFORKSPEC);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
int fd = -1;
|
|
|
|
for (;;) {
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
switch(*access) {
|
|
|
|
case readEnableAllowWrite:
|
|
|
|
case readWriteEnable:
|
|
|
|
fd = open(rpath, O_RDWR | O_CREAT, 0666);
|
|
|
|
break;
|
|
|
|
case readEnable:
|
|
|
|
fd = open(rpath, O_RDONLY);
|
|
|
|
break;
|
|
|
|
case writeEnable:
|
|
|
|
fd = open(rpath, O_WRONLY | O_CREAT, 0666);
|
|
|
|
break;
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (*access == readEnableAllowWrite) {
|
|
|
|
if (fd < 0) {
|
|
|
|
*access = readEnable;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
*access = readWriteEnable;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (fd < 0) {
|
|
|
|
*error = map_errno_path(path);
|
|
|
|
}
|
2016-11-24 18:04:37 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
return fd;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
#elif defined __sun
|
|
|
|
static int open_resource_fork(const char *path, word16 *access, word16 *error) {
|
2018-08-13 17:22:40 +00:00
|
|
|
int tmp = open(path, O_RDONLY);
|
|
|
|
if (tmp < 0) {
|
|
|
|
*error = map_errno_path(path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int fd = -1;
|
|
|
|
for(;;) {
|
|
|
|
|
|
|
|
switch(*access) {
|
|
|
|
case readEnableAllowWrite:
|
|
|
|
case readWriteEnable:
|
|
|
|
fd = openat(tmp, XATTR_RESOURCEFORK_NAME, O_RDWR | O_CREAT | O_XATTR, 0666);
|
|
|
|
break;
|
|
|
|
case readEnable:
|
|
|
|
fd = openat(tmp, XATTR_RESOURCEFORK_NAME, O_RDONLY | O_XATTR);
|
|
|
|
break;
|
|
|
|
case writeEnable:
|
|
|
|
fd = openat(tmp, XATTR_RESOURCEFORK_NAME, O_WRONLY | O_CREAT | O_XATTR, 0666);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*access == readEnableAllowWrite) {
|
|
|
|
if (fd < 0) {
|
|
|
|
*access = readEnable;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
*access = readWriteEnable;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fd < 0) {
|
|
|
|
*error = map_errno_path(path);
|
|
|
|
close(tmp);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
close(tmp);
|
|
|
|
|
|
|
|
return fd;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
#elif defined __linux__
|
|
|
|
static int open_resource_fork(const char *path, word16 *access, word16 *error) {
|
2018-08-13 17:22:40 +00:00
|
|
|
*error = resForkNotFound;
|
|
|
|
return -1;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
#else
|
|
|
|
static int open_resource_fork(const char *path, word16 *access, word16 *error) {
|
2018-08-13 17:22:40 +00:00
|
|
|
*error = resForkNotFound;
|
|
|
|
return -1;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
static word32 fst_open(int class, const char *path) {
|
|
|
|
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
word32 pb = get_memory24_c(engine.direct + dp_param_blk_ptr, 0);
|
|
|
|
|
|
|
|
struct file_info fi;
|
|
|
|
word16 rv = 0;
|
|
|
|
|
|
|
|
rv = get_file_info(path, &fi);
|
|
|
|
if (rv) return rv;
|
|
|
|
|
|
|
|
|
|
|
|
int fd = -1;
|
|
|
|
int type = regular_file;
|
|
|
|
struct directory *dd = NULL;
|
|
|
|
|
|
|
|
word16 pcount = 0;
|
|
|
|
word16 request_access = readEnableAllowWrite;
|
|
|
|
word16 access = 0;
|
|
|
|
word16 resource_number = 0;
|
|
|
|
if (class) {
|
|
|
|
pcount = get_memory16_c(pb, 0);
|
|
|
|
if (pcount >= 3) request_access = get_memory16_c(pb + OpenRecGS_requestAccess, 0);
|
|
|
|
if (pcount >= 4) resource_number = get_memory16_c(pb + OpenRecGS_resourceNumber, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (resource_number) {
|
|
|
|
if (resource_number > 1) return paramRangeErr;
|
|
|
|
type = resource_file;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (access > 3) return paramRangeErr;
|
|
|
|
|
|
|
|
// special access checks for directories.
|
|
|
|
if (S_ISDIR(fi.st_mode)) {
|
|
|
|
if (resource_number) return resForkNotFound;
|
|
|
|
switch (request_access) {
|
|
|
|
case readEnableAllowWrite:
|
|
|
|
request_access = readEnable;
|
|
|
|
break;
|
|
|
|
case writeEnable:
|
|
|
|
case readWriteEnable:
|
|
|
|
return invalidAccess;
|
|
|
|
}
|
|
|
|
type = directory_file;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (read_only) {
|
|
|
|
switch (request_access) {
|
|
|
|
case readEnableAllowWrite:
|
|
|
|
request_access = readEnable;
|
|
|
|
break;
|
|
|
|
case readWriteEnable:
|
|
|
|
case writeEnable:
|
|
|
|
return invalidAccess;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
access = request_access;
|
|
|
|
switch(type) {
|
|
|
|
case regular_file:
|
|
|
|
fd = open_data_fork(path, &access, &rv);
|
|
|
|
break;
|
|
|
|
case resource_file:
|
|
|
|
fd = open_resource_fork(path, &access, &rv);
|
|
|
|
break;
|
|
|
|
case directory_file:
|
|
|
|
dd = read_directory(path, &rv);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rv) return rv;
|
|
|
|
|
|
|
|
|
|
|
|
if (class) {
|
|
|
|
if (pcount >= 5) set_memory16_c(pb + OpenRecGS_access, access, 0);
|
|
|
|
if (pcount >= 6) set_memory16_c(pb + OpenRecGS_fileType, fi.file_type, 0);
|
|
|
|
if (pcount >= 7) set_memory32_c(pb + OpenRecGS_auxType, fi.aux_type, 0);
|
|
|
|
if (pcount >= 8) set_memory16_c(pb + OpenRecGS_storageType, fi.storage_type, 0);
|
|
|
|
|
|
|
|
if (pcount >= 9) set_date_time_rec(pb + OpenRecGS_createDateTime, fi.create_date);
|
|
|
|
if (pcount >= 10) set_date_time_rec(pb + OpenRecGS_modDateTime, fi.modified_date);
|
|
|
|
|
|
|
|
if (pcount >= 11) {
|
|
|
|
word16 fst_id = hfsFSID;
|
|
|
|
//if (fi.storage_type == 0x0f) fst_id = mfsFSID;
|
|
|
|
|
|
|
|
word32 option_list = get_memory24_c(pb + OpenRecGS_optionList, 0);
|
|
|
|
word32 tmp = set_option_list(option_list, fst_id, fi.finder_info, fi.has_fi ? 32 : 0);
|
|
|
|
if (!rv) rv = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcount >= 12) set_memory32_c(pb + OpenRecGS_eof, fi.eof, 0);
|
|
|
|
if (pcount >= 13) set_memory32_c(pb + OpenRecGS_blocksUsed, fi.blocks, 0);
|
|
|
|
if (pcount >= 14) set_memory32_c(pb + OpenRecGS_resourceEOF, fi.resource_eof, 0);
|
|
|
|
if (pcount >= 15) set_memory32_c(pb + OpenRecGS_resourceBlocks, fi.resource_blocks, 0);
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
// prodos 16 doesn't return anything in the parameter block.
|
|
|
|
|
|
|
|
struct fd_entry *e = alloc_fd();
|
|
|
|
if (!e) {
|
|
|
|
if (fd >=0) close(fd);
|
|
|
|
if (dd) free_directory(dd);
|
|
|
|
return outOfMem;
|
|
|
|
}
|
|
|
|
e->fd = fd;
|
|
|
|
e->dir = dd;
|
|
|
|
|
|
|
|
e->cookie = alloc_cookie();
|
|
|
|
if (!e->cookie) {
|
|
|
|
free_fd(e);
|
|
|
|
return tooManyFilesOpen;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == regular_file) {
|
|
|
|
|
|
|
|
if (g_cfg_host_crlf) {
|
|
|
|
if (fi.file_type == 0x04 || fi.file_type == 0xb0)
|
|
|
|
e->translate = translate_crlf;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_cfg_host_merlin && fi.file_type == 0x04) {
|
|
|
|
int n = strlen(path);
|
|
|
|
if (n >= 3 && path[n-1] == 'S' && path[n-2] == '.')
|
|
|
|
e->translate = translate_merlin;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
e->access = access;
|
|
|
|
e->path = strdup(path);
|
|
|
|
e->type = type;
|
|
|
|
|
|
|
|
|
|
|
|
// insert it in the linked list.
|
|
|
|
e->next = fd_head;
|
|
|
|
fd_head = e;
|
|
|
|
|
|
|
|
engine.xreg = e->cookie;
|
|
|
|
engine.yreg = access; // actual access, needed in fcr.
|
|
|
|
|
|
|
|
return rv;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static word32 fst_read(int class) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
int cookie = engine.yreg;
|
|
|
|
struct fd_entry *e = find_fd(cookie);
|
|
|
|
|
|
|
|
if (!e) return invalidRefNum;
|
|
|
|
|
|
|
|
switch (e->type) {
|
|
|
|
case directory_file:
|
|
|
|
return badStoreType;
|
|
|
|
}
|
|
|
|
|
|
|
|
word32 pb = get_memory24_c(engine.direct + dp_param_blk_ptr, 0);
|
|
|
|
|
|
|
|
word32 data_buffer = 0;
|
|
|
|
word32 request_count = 0;
|
|
|
|
word32 transfer_count = 0;
|
|
|
|
|
|
|
|
if (class) {
|
|
|
|
data_buffer = get_memory24_c(pb + IORecGS_dataBuffer, 0);
|
|
|
|
request_count = get_memory24_c(pb + IORecGS_requestCount, 0);
|
|
|
|
// pre-zero transfer count
|
|
|
|
set_memory32_c(pb + IORecGS_transferCount, 0, 0);
|
|
|
|
} else {
|
|
|
|
data_buffer = get_memory24_c(pb + FileIORec_dataBuffer, 0);
|
|
|
|
request_count = get_memory24_c(pb + FileIORec_requestCount, 0);
|
|
|
|
set_memory32_c(pb + FileIORec_transferCount, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (request_count == 0) return 0;
|
|
|
|
|
|
|
|
|
|
|
|
word16 newline_mask;
|
|
|
|
word32 rv = 0;
|
|
|
|
ssize_t ok;
|
|
|
|
|
|
|
|
newline_mask = get_memory16_c(global_buffer, 0);
|
|
|
|
if (newline_mask) {
|
|
|
|
byte newline_table[256];
|
|
|
|
for (int i = 0; i < 256; ++i)
|
|
|
|
newline_table[i] = get_memory_c(global_buffer + 2 + i, 0);
|
|
|
|
|
|
|
|
for (word32 i = 0; i < request_count; ++i) {
|
|
|
|
byte b;
|
|
|
|
ok = safe_read(e, &b, 1);
|
|
|
|
if (ok < 0) return map_errno();
|
|
|
|
if (ok == 0) break;
|
|
|
|
transfer_count++;
|
|
|
|
set_memory_c(data_buffer++, b, 0);
|
|
|
|
if (newline_table[b & newline_mask]) break;
|
|
|
|
}
|
|
|
|
if (transfer_count == 0) rv = eofEncountered;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
byte *data = gc_malloc(request_count);
|
|
|
|
if (!data) return outOfMem;
|
|
|
|
|
|
|
|
|
|
|
|
ok = safe_read(e, data, request_count);
|
|
|
|
if (ok < 0) rv = map_errno();
|
|
|
|
if (ok == 0) rv = eofEncountered;
|
|
|
|
if (ok > 0) {
|
|
|
|
transfer_count = ok;
|
|
|
|
for (size_t i = 0; i < ok; ++i) {
|
|
|
|
set_memory_c(data_buffer + i, data[i], 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (transfer_count) {
|
|
|
|
if (class)
|
|
|
|
set_memory32_c(pb + IORecGS_transferCount, transfer_count, 0);
|
|
|
|
else
|
|
|
|
set_memory32_c(pb + FileIORec_transferCount, transfer_count, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static word32 fst_write(int class) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
int cookie = engine.yreg;
|
|
|
|
struct fd_entry *e = find_fd(cookie);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (!e) return invalidRefNum;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (!(e->access & writeEnable))
|
|
|
|
return invalidAccess;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
switch (e->type) {
|
|
|
|
case directory_file:
|
|
|
|
return badStoreType;
|
|
|
|
}
|
2016-11-24 18:04:37 +00:00
|
|
|
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
word32 pb = get_memory24_c(engine.direct + dp_param_blk_ptr, 0);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
word32 data_buffer = 0;
|
|
|
|
word32 request_count = 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (class) {
|
|
|
|
data_buffer = get_memory24_c(pb + IORecGS_dataBuffer, 0);
|
|
|
|
request_count = get_memory24_c(pb + IORecGS_requestCount, 0);
|
|
|
|
// pre-zero transfer count
|
|
|
|
set_memory32_c(pb + IORecGS_transferCount, 0, 0);
|
|
|
|
} else {
|
|
|
|
data_buffer = get_memory24_c(pb + FileIORec_dataBuffer, 0);
|
|
|
|
request_count = get_memory24_c(pb + FileIORec_requestCount, 0);
|
|
|
|
set_memory32_c(pb + FileIORec_transferCount, 0, 0);
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (request_count == 0) return 0;
|
|
|
|
byte *data = gc_malloc(request_count);
|
|
|
|
if (!data) return outOfMem;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
for (word32 i = 0; i < request_count; ++i) {
|
|
|
|
data[i] = get_memory_c(data_buffer + i,0);
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
word32 rv = 0;
|
|
|
|
ssize_t ok = safe_write(e, data, request_count);
|
|
|
|
if (ok < 0) rv = map_errno();
|
|
|
|
if (ok > 0) {
|
|
|
|
if (class)
|
|
|
|
set_memory32_c(pb + IORecGS_transferCount, ok, 0);
|
|
|
|
else
|
|
|
|
set_memory32_c(pb + FileIORec_transferCount, ok, 0);
|
|
|
|
}
|
|
|
|
return rv;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static word32 fst_close(int class) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
int cookie = engine.yreg;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
return remove_fd(cookie);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static word32 fst_flush(int class) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
int cookie = engine.yreg;
|
|
|
|
struct fd_entry *e = find_fd(cookie);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (!e) return invalidRefNum;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
switch (e->type) {
|
|
|
|
case directory_file:
|
|
|
|
return badStoreType;
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
int ok = fsync(e->fd);
|
|
|
|
if (ok < 0) return map_errno();
|
|
|
|
return 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static off_t get_offset(int fd, word16 base, word32 displacement) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
off_t pos = lseek(fd, 0, SEEK_CUR);
|
|
|
|
off_t eof = lseek(fd, 0, SEEK_END);
|
|
|
|
lseek(fd, pos, SEEK_SET);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
switch (base) {
|
|
|
|
case startPlus:
|
|
|
|
return displacement;
|
|
|
|
break;
|
|
|
|
case eofMinus:
|
|
|
|
return eof - displacement;
|
|
|
|
break;
|
|
|
|
case markPlus:
|
|
|
|
return pos + displacement;
|
|
|
|
break;
|
|
|
|
case markMinus:
|
|
|
|
return pos - displacement;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static word32 fst_set_mark(int class) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
int cookie = engine.yreg;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
struct fd_entry *e = find_fd(cookie);
|
|
|
|
if (!e) return invalidRefNum;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
switch (e->type) {
|
|
|
|
case directory_file:
|
|
|
|
return badStoreType;
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
word32 pb = get_memory24_c(engine.direct + dp_param_blk_ptr, 0);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
word16 base = 0;
|
|
|
|
word32 displacement = 0;
|
|
|
|
if (class) {
|
|
|
|
base = get_memory16_c(pb + SetPositionRecGS_base, 0);
|
|
|
|
displacement = get_memory32_c(pb + SetPositionRecGS_displacement, 0);
|
|
|
|
} else {
|
|
|
|
displacement = get_memory32_c(pb + MarkRec_position, 0);
|
|
|
|
}
|
|
|
|
if (base > markMinus) return paramRangeErr;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
off_t offset = get_offset(e->fd, base, displacement);
|
|
|
|
if (offset < 0) return outOfRange;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
off_t ok = lseek(e->fd, offset, SEEK_SET);
|
|
|
|
if (ok < 0) return map_errno();
|
|
|
|
return 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static word32 fst_set_eof(int class) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
int cookie = engine.yreg;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
struct fd_entry *e = find_fd(cookie);
|
|
|
|
if (!e) return invalidRefNum;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
switch (e->type) {
|
|
|
|
case directory_file:
|
|
|
|
return badStoreType;
|
|
|
|
}
|
2016-11-24 18:04:37 +00:00
|
|
|
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
word32 pb = get_memory24_c(engine.direct + dp_param_blk_ptr, 0);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
word16 base = 0;
|
|
|
|
word32 displacement = 0;
|
|
|
|
if (class) {
|
|
|
|
base = get_memory16_c(pb + SetPositionRecGS_base, 0);
|
|
|
|
displacement = get_memory32_c(pb + SetPositionRecGS_displacement, 0);
|
|
|
|
} else {
|
|
|
|
displacement = get_memory32_c(pb + EOFRec_eofPosition, 0);
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (base > markMinus) return paramRangeErr;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
off_t offset = get_offset(e->fd, base, displacement);
|
|
|
|
if (offset < 0) return outOfRange;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
int ok = ftruncate(e->fd, offset);
|
|
|
|
if (ok < 0) return map_errno();
|
|
|
|
return 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static word32 fst_get_mark(int class) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
int cookie = engine.yreg;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
struct fd_entry *e = find_fd(cookie);
|
|
|
|
if (!e) return invalidRefNum;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
word32 pb = get_memory24_c(engine.direct + dp_param_blk_ptr, 0);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
off_t pos = 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
switch (e->type) {
|
|
|
|
case directory_file:
|
|
|
|
return badStoreType;
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
pos = lseek(e->fd, 0, SEEK_CUR);
|
|
|
|
if (pos < 0) return map_errno();
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (class) {
|
|
|
|
set_memory32_c(pb + PositionRecGS_position, pos, 0);
|
|
|
|
} else {
|
|
|
|
set_memory32_c(pb + MarkRec_position, pos, 0);
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
return 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static word32 fst_get_eof(int class) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
int cookie = engine.yreg;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
struct fd_entry *e = find_fd(cookie);
|
|
|
|
if (!e) return invalidRefNum;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
word32 pb = get_memory24_c(engine.direct + dp_param_blk_ptr, 0);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2016-11-24 18:04:37 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
switch (e->type) {
|
|
|
|
case directory_file:
|
|
|
|
return badStoreType;
|
|
|
|
}
|
2016-11-24 18:04:37 +00:00
|
|
|
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
off_t eof = 0;
|
|
|
|
off_t pos = 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
pos = lseek(e->fd, 0, SEEK_CUR);
|
|
|
|
eof = lseek(e->fd, 0, SEEK_END);
|
|
|
|
if (eof < 0) return map_errno();
|
|
|
|
lseek(e->fd, pos, SEEK_SET);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (class) {
|
|
|
|
set_memory32_c(pb + PositionRecGS_position, eof, 0);
|
|
|
|
} else {
|
|
|
|
set_memory32_c(pb + MarkRec_position, eof, 0);
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
return 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
2016-11-26 02:49:26 +00:00
|
|
|
static void free_directory(struct directory *dd) {
|
2018-08-13 17:22:40 +00:00
|
|
|
if (!dd) return;
|
|
|
|
for (int i = 0; i < dd->num_entries; ++i) {
|
|
|
|
free(dd->entries[i]);
|
|
|
|
}
|
|
|
|
free(dd);
|
2016-11-26 02:49:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int qsort_callback(const void *a, const void *b) {
|
2018-08-13 17:22:40 +00:00
|
|
|
return strcmp(*(const char **)a, *(const char **)b);
|
2016-11-26 02:49:26 +00:00
|
|
|
}
|
|
|
|
|
2016-10-12 16:18:23 +00:00
|
|
|
/*
|
|
|
|
* exclude files from get_dir_entries.
|
|
|
|
*
|
|
|
|
*/
|
2016-11-26 02:49:26 +00:00
|
|
|
static int filter_directory_entry(const char *name) {
|
2018-08-13 17:22:40 +00:00
|
|
|
if (!name[0]) return 1;
|
|
|
|
if (name[0] == '.') {
|
|
|
|
return 1;
|
|
|
|
/*
|
|
|
|
if (!strcmp(name, ".")) return 1;
|
|
|
|
if (!strcmp(name, "..")) return 1;
|
|
|
|
if (!strncmp(name, "._", 2)) return 1; // ._ resource fork
|
|
|
|
if (!strcmp(name, ".fseventsd")) return 1;
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
return 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
2016-11-24 18:04:37 +00:00
|
|
|
|
2016-11-26 02:49:26 +00:00
|
|
|
static struct directory *read_directory(const char *path, word16 *error) {
|
2018-08-13 17:22:40 +00:00
|
|
|
DIR *dirp;
|
|
|
|
struct directory *dd;
|
|
|
|
int capacity = 100;
|
|
|
|
int size = sizeof(struct directory) + capacity * sizeof(char *);
|
|
|
|
|
|
|
|
dirp = opendir(path);
|
|
|
|
if (!dirp) {
|
|
|
|
*error = map_errno_path(path);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
dd = (struct directory *)malloc(size);
|
|
|
|
if (!dd) {
|
|
|
|
*error = outOfMem;
|
|
|
|
closedir(dirp);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
memset(dd, 0, size);
|
|
|
|
|
|
|
|
for(;;) {
|
|
|
|
struct dirent *d = readdir(dirp);
|
|
|
|
if (!d) break;
|
|
|
|
if (filter_directory_entry(d->d_name)) continue;
|
|
|
|
|
|
|
|
if (dd->num_entries >= capacity) {
|
|
|
|
capacity += capacity;
|
|
|
|
size = sizeof(struct directory) + capacity * sizeof(char *);
|
|
|
|
struct directory * tmp = realloc(dd, size);
|
|
|
|
if (!tmp) {
|
|
|
|
*error = map_errno();
|
|
|
|
free_directory(dd);
|
|
|
|
closedir(dirp);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
dd = tmp;
|
|
|
|
}
|
|
|
|
dd->entries[dd->num_entries++] = strdup(d->d_name);
|
|
|
|
};
|
|
|
|
|
|
|
|
closedir(dirp);
|
|
|
|
|
|
|
|
// sort them....
|
|
|
|
qsort(dd->entries, dd->num_entries, sizeof(char *), qsort_callback);
|
|
|
|
|
|
|
|
return dd;
|
2016-11-24 18:04:37 +00:00
|
|
|
}
|
|
|
|
|
2016-11-26 02:49:26 +00:00
|
|
|
|
2016-10-12 16:18:23 +00:00
|
|
|
static word32 fst_get_dir_entry(int class) {
|
|
|
|
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
int cookie = engine.yreg;
|
|
|
|
|
|
|
|
struct fd_entry *e = find_fd(cookie);
|
|
|
|
if (!e) return invalidRefNum;
|
|
|
|
|
|
|
|
|
|
|
|
if (e->type != directory_file) return badFileFormat;
|
|
|
|
|
|
|
|
word32 pb = get_memory24_c(engine.direct + dp_param_blk_ptr, 0);
|
|
|
|
|
|
|
|
word16 base = 0;
|
|
|
|
word16 pcount = 0;
|
|
|
|
word32 displacement = 0;
|
|
|
|
word32 name = 0;
|
|
|
|
|
|
|
|
if (class) {
|
|
|
|
pcount = get_memory16_c(pb, 0);
|
|
|
|
base = get_memory16_c(pb + DirEntryRecGS_base, 0);
|
|
|
|
displacement = get_memory16_c(pb + DirEntryRecGS_displacement, 0);
|
|
|
|
name = get_memory24_c(pb + DirEntryRecGS_name, 0);
|
|
|
|
} else {
|
|
|
|
base = get_memory16_c(pb + DirEntryRec_base, 0);
|
|
|
|
displacement = get_memory16_c(pb + DirEntryRec_displacement, 0);
|
|
|
|
name = get_memory24_c(pb + DirEntryRec_nameBuffer, 0);
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (base == 0 && displacement == 0) {
|
|
|
|
// count them up.
|
|
|
|
int count = e->dir->num_entries;
|
|
|
|
e->dir->displacement = 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (class) {
|
|
|
|
if (pcount >= 6) set_memory16_c(pb + DirEntryRecGS_entryNum, count, 0);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
set_memory16_c(pb + DirEntryRec_entryNum, count, 0);
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
int dir_displacement = e->dir->displacement;
|
|
|
|
switch (base) {
|
|
|
|
case 0: // displacement is absolute entry number.
|
|
|
|
break;
|
|
|
|
case 1: // displacement is added to the current displacement.
|
|
|
|
displacement = dir_displacement + displacement;
|
|
|
|
break;
|
|
|
|
case 2: // displacement is substracted from current displacement.
|
|
|
|
displacement = dir_displacement - displacement;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return paramRangeErr;
|
|
|
|
}
|
|
|
|
//if (displacement) --displacement;
|
|
|
|
--displacement;
|
|
|
|
if (displacement < 0) return endOfDir;
|
|
|
|
if (displacement >= e->dir->num_entries) return endOfDir;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
word32 rv = 0;
|
|
|
|
const char *dname = e->dir->entries[displacement++];
|
|
|
|
e->dir->displacement = displacement;
|
|
|
|
char *fullpath = append_path(e->path, dname);
|
|
|
|
struct file_info fi;
|
|
|
|
rv = get_file_info(fullpath, &fi);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (dname) fprintf(stderr, " - %s", dname);
|
2016-11-26 02:49:26 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
// p16 and gs/os both use truncating c1 output string.
|
|
|
|
rv = set_gsstr_truncate(name, dname);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (class) {
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (pcount > 2) set_memory16_c(pb + DirEntryRecGS_flags, fi.storage_type == 0x05 ? 0x8000 : 0, 0);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (pcount >= 6) set_memory16_c(pb + DirEntryRecGS_entryNum, displacement, 0);
|
|
|
|
if (pcount >= 7) set_memory16_c(pb + DirEntryRecGS_fileType, fi.file_type, 0);
|
|
|
|
if (pcount >= 8) set_memory32_c(pb + DirEntryRecGS_eof, fi.eof, 0);
|
|
|
|
if (pcount >= 9) set_memory32_c(pb + DirEntryRecGS_blockCount, fi.blocks, 0);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (pcount >= 10) set_date_time_rec(pb + DirEntryRecGS_createDateTime, fi.create_date);
|
|
|
|
if (pcount >= 11) set_date_time_rec(pb + DirEntryRecGS_modDateTime, fi.modified_date);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (pcount >= 12) set_memory16_c(pb + DirEntryRecGS_access, fi.access, 0);
|
|
|
|
if (pcount >= 13) set_memory32_c(pb + DirEntryRecGS_auxType, fi.aux_type, 0);
|
|
|
|
if (pcount >= 14) set_memory16_c(pb + DirEntryRecGS_fileSysID, mfsFSID, 0);
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (pcount >= 15) {
|
|
|
|
word16 fst_id = hfsFSID;
|
|
|
|
//if (fi.storage_type == 0x0f) fst_id = mfsFSID;
|
|
|
|
word32 option_list = get_memory24_c(pb + DirEntryRecGS_optionList, 0);
|
|
|
|
word32 tmp = set_option_list(option_list, fst_id, fi.finder_info, fi.has_fi ? 32 : 0);
|
|
|
|
if (!rv) rv = tmp;
|
|
|
|
}
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
if (pcount >= 16) set_memory32_c(pb + DirEntryRecGS_resourceEOF, fi.resource_eof, 0);
|
|
|
|
if (pcount >= 17) set_memory32_c(pb + DirEntryRecGS_resourceBlocks, fi.resource_blocks, 0);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
set_memory16_c(pb + DirEntryRec_flags, fi.storage_type == 0x05 ? 0x8000 : 0, 0);
|
|
|
|
|
|
|
|
set_memory16_c(pb + DirEntryRec_entryNum, displacement, 0);
|
|
|
|
set_memory16_c(pb + DirEntryRec_fileType, fi.file_type, 0);
|
|
|
|
set_memory32_c(pb + DirEntryRec_endOfFile, fi.eof, 0);
|
|
|
|
set_memory32_c(pb + DirEntryRec_blockCount, fi.blocks, 0);
|
|
|
|
|
|
|
|
set_date_time_rec(pb + DirEntryRec_createTime, fi.create_date);
|
|
|
|
set_date_time_rec(pb + DirEntryRec_modTime, fi.modified_date);
|
|
|
|
|
|
|
|
set_memory16_c(pb + DirEntryRec_access, fi.access, 0);
|
|
|
|
set_memory32_c(pb + DirEntryRec_auxType, fi.aux_type, 0);
|
|
|
|
set_memory16_c(pb + DirEntryRec_fileSysID, mfsFSID, 0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static word32 fst_change_path(int class, const char *path1, const char *path2) {
|
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
/* make sure they're not trying to rename the volume... */
|
|
|
|
struct stat st;
|
|
|
|
if (stat(path1, &st) < 0) return map_errno_path(path1);
|
|
|
|
if (st.st_dev == root_dev && st.st_ino == root_ino)
|
|
|
|
return invalidAccess;
|
2016-10-12 16:18:23 +00:00
|
|
|
|
2018-08-13 17:22:40 +00:00
|
|
|
// rename will delete any previous file.
|
|
|
|
if (rename(path1, path2) < 0) return map_errno_path(path2);
|
|
|
|
return 0;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static word32 fst_format(int class) {
|
2018-08-13 17:22:40 +00:00
|
|
|
return notBlockDev;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static word32 fst_erase(int class) {
|
2018-08-13 17:22:40 +00:00
|
|
|
return notBlockDev;
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static const char *call_name(word16 call) {
|
2018-08-13 17:22:40 +00:00
|
|
|
static char* class1[] = {
|
|
|
|
// 0x00
|
|
|
|
"",
|
|
|
|
"CreateGS",
|
|
|
|
"DestroyGS",
|
|
|
|
"",
|
|
|
|
"ChangePathGS",
|
|
|
|
"SetFileInfoGS",
|
|
|
|
"GetFileInfoGS",
|
|
|
|
"JudgeNameGS",
|
|
|
|
"VolumeGS",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"ClearBackupGS",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
|
|
|
|
// 0x10
|
|
|
|
"OpenGS",
|
|
|
|
"",
|
|
|
|
"ReadGS",
|
|
|
|
"WriteGS",
|
|
|
|
"CloseGS",
|
|
|
|
"FlushGS",
|
|
|
|
"SetMarkGS",
|
|
|
|
"GetMarkGS",
|
|
|
|
"SetEOFGS",
|
|
|
|
"GetEOFGS",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"GetDirEntryGS",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
|
|
|
|
// 0x20
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"FormatGS",
|
|
|
|
"EraseDiskGS",
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static char* class0[] = {
|
|
|
|
// 0x00
|
|
|
|
"",
|
|
|
|
"CREATE",
|
|
|
|
"DESTROY",
|
|
|
|
"",
|
|
|
|
"CHANGE_PATH",
|
|
|
|
"SET_FILE_INFO",
|
|
|
|
"GET_FILE_INFO",
|
|
|
|
"",
|
|
|
|
"VOLUME",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"CLEAR_BACKUP_BIT",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
|
|
|
|
// 0X10
|
|
|
|
"OPEN",
|
|
|
|
"",
|
|
|
|
"READ",
|
|
|
|
"WRITE",
|
|
|
|
"CLOSE",
|
|
|
|
"FLUSH",
|
|
|
|
"SET_MARK",
|
|
|
|
"GET_MARK",
|
|
|
|
"SET_EOF",
|
|
|
|
"GET_EOF",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"GET_DIR_ENTRY",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
|
|
|
|
// 0X20
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"FORMAT",
|
|
|
|
"ERASE_DISK",
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (call & 0x8000) {
|
|
|
|
static char *sys[] = {
|
|
|
|
"",
|
|
|
|
"fst_startup",
|
|
|
|
"fst_shutdown",
|
|
|
|
"fst_remove_vcr",
|
|
|
|
"fst_deferred_flush"
|
|
|
|
};
|
|
|
|
call &= ~0x8000;
|
|
|
|
|
|
|
|
if (call < sizeof(sys) / sizeof(sys[0]))
|
|
|
|
return sys[call];
|
|
|
|
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
int class = call >> 13;
|
|
|
|
call &= 0x1fff;
|
|
|
|
switch(class) {
|
|
|
|
case 0:
|
|
|
|
if (call < sizeof(class0) / sizeof(class0[0]))
|
|
|
|
return class0[call];
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
if (call < sizeof(class1) / sizeof(class1[0]))
|
|
|
|
return class1[call];
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
return "";
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static const char *error_name(word16 error) {
|
2018-08-13 17:22:40 +00:00
|
|
|
static char *errors[] = {
|
|
|
|
"",
|
|
|
|
"badSystemCall",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"invalidPcount",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"gsosActive",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
// 0x10
|
|
|
|
"devNotFound",
|
|
|
|
"invalidDevNum",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
// 0x20
|
|
|
|
"drvrBadReq",
|
|
|
|
"drvrBadCode",
|
|
|
|
"drvrBadParm",
|
|
|
|
"drvrNotOpen",
|
|
|
|
"drvrPriorOpen",
|
|
|
|
"irqTableFull",
|
|
|
|
"drvrNoResrc",
|
|
|
|
"drvrIOError",
|
|
|
|
"drvrNoDevice",
|
|
|
|
"drvrBusy",
|
|
|
|
"",
|
|
|
|
"drvrWrtProt",
|
|
|
|
"drvrBadCount",
|
|
|
|
"drvrBadBlock",
|
|
|
|
"drvrDiskSwitch",
|
|
|
|
"drvrOffLine",
|
|
|
|
// 0x30
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
// 0x40
|
|
|
|
"badPathSyntax",
|
|
|
|
"",
|
|
|
|
"tooManyFilesOpen",
|
|
|
|
"invalidRefNum",
|
|
|
|
"pathNotFound",
|
|
|
|
"volNotFound",
|
|
|
|
"fileNotFound",
|
|
|
|
"dupPathname",
|
|
|
|
"volumeFull",
|
|
|
|
"volDirFull",
|
|
|
|
"badFileFormat",
|
|
|
|
"badStoreType",
|
|
|
|
"eofEncountered",
|
|
|
|
"outOfRange",
|
|
|
|
"invalidAccess",
|
|
|
|
"buffTooSmall",
|
|
|
|
// 0x50
|
|
|
|
"fileBusy",
|
|
|
|
"dirError",
|
|
|
|
"unknownVol",
|
|
|
|
"paramRangeErr",
|
|
|
|
"outOfMem",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"dupVolume",
|
|
|
|
"notBlockDev",
|
|
|
|
"invalidLevel",
|
|
|
|
"damagedBitMap",
|
|
|
|
"badPathNames",
|
|
|
|
"notSystemFile",
|
|
|
|
"osUnsupported",
|
|
|
|
"",
|
|
|
|
"stackOverflow",
|
|
|
|
// 0x60
|
|
|
|
"dataUnavail",
|
|
|
|
"endOfDir",
|
|
|
|
"invalidClass",
|
|
|
|
"resForkNotFound",
|
|
|
|
"invalidFSTID",
|
|
|
|
"invalidFSTop",
|
|
|
|
"fstCaution",
|
|
|
|
"devNameErr",
|
|
|
|
"defListFull",
|
|
|
|
"supListFull",
|
|
|
|
"fstError",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
//0x70
|
|
|
|
"resExistsErr",
|
|
|
|
"resAddErr",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
//0x80
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"",
|
|
|
|
"networkError"
|
|
|
|
};
|
|
|
|
|
|
|
|
if (error < sizeof(errors) / sizeof(errors[0]))
|
|
|
|
return errors[error];
|
|
|
|
return "";
|
2016-10-12 16:18:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void host_fst(void) {
|
2018-08-13 17:22:40 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* input:
|
|
|
|
* c = set
|
|
|
|
* a = default error code
|
|
|
|
* x = gs/os callnum
|
|
|
|
* y = [varies]
|
|
|
|
*
|
|
|
|
* output:
|
|
|
|
* c = set/clear
|
|
|
|
* a = error code/0
|
|
|
|
* x = varies
|
|
|
|
* y = varies
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
word32 acc = 0;
|
|
|
|
word16 call = engine.xreg;
|
|
|
|
|
|
|
|
fprintf(stderr, "Host FST: %04x %s", call, call_name(call));
|
|
|
|
|
|
|
|
|
|
|
|
if (call & 0x8000) {
|
|
|
|
fputs("\n", stderr);
|
|
|
|
// system level.
|
|
|
|
switch(call) {
|
|
|
|
case 0x8001:
|
|
|
|
acc = fst_startup();
|
|
|
|
break;
|
|
|
|
case 0x8002:
|
|
|
|
acc = fst_shutdown();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
acc = badSystemCall;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
|
|
|
|
if (!root) {
|
|
|
|
acc = networkError;
|
|
|
|
engine.acc = acc;
|
|
|
|
SEC();
|
|
|
|
fprintf(stderr, " %02x %s\n", acc, error_name(acc));
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int class = call >> 13;
|
|
|
|
call &= 0x1fff;
|
|
|
|
|
|
|
|
if (class > 1) {
|
|
|
|
acc = invalidClass;
|
|
|
|
engine.acc = acc;
|
|
|
|
SEC();
|
|
|
|
fprintf(stderr, " %02x %s\n", acc, error_name(acc));
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *path1 = NULL;
|
|
|
|
char *path2 = NULL;
|
|
|
|
char *path3 = NULL;
|
|
|
|
char *path4 = NULL;
|
|
|
|
const char *cp;
|
|
|
|
|
|
|
|
switch(call & 0xff) {
|
|
|
|
case 0x01:
|
|
|
|
case 0x02:
|
|
|
|
case 0x05:
|
|
|
|
case 0x06:
|
|
|
|
case 0x0b:
|
|
|
|
case 0x10:
|
|
|
|
path1 = get_path1();
|
|
|
|
break;
|
|
|
|
case 0x04:
|
|
|
|
path1 = get_path1();
|
|
|
|
path2 = get_path2();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (path1) fprintf(stderr, " - %s", path1);
|
|
|
|
if (path2) fprintf(stderr, " - %s", path2);
|
|
|
|
|
|
|
|
switch(call & 0xff) {
|
|
|
|
case 0x01:
|
|
|
|
cp = check_path(path1, &acc);
|
|
|
|
if (acc) break;
|
|
|
|
path3 = append_path(root, cp);
|
|
|
|
|
|
|
|
acc = fst_create(class, path3);
|
|
|
|
break;
|
|
|
|
case 0x02:
|
|
|
|
cp = check_path(path1, &acc);
|
|
|
|
if (acc) break;
|
|
|
|
path3 = append_path(root, cp);
|
|
|
|
|
|
|
|
acc = fst_destroy(class, path3);
|
|
|
|
break;
|
|
|
|
case 0x04:
|
|
|
|
|
|
|
|
cp = check_path(path1, &acc);
|
|
|
|
if (acc) break;
|
|
|
|
path3 = append_path(root, cp);
|
|
|
|
|
|
|
|
cp = check_path(path2, &acc);
|
|
|
|
if (acc) break;
|
|
|
|
path4 = append_path(root, cp);
|
|
|
|
|
|
|
|
acc = fst_change_path(class, path3, path4);
|
|
|
|
break;
|
|
|
|
case 0x05:
|
|
|
|
cp = check_path(path1, &acc);
|
|
|
|
if (acc) break;
|
|
|
|
path3 = append_path(root, cp);
|
|
|
|
|
|
|
|
acc = fst_set_file_info(class, path3);
|
|
|
|
break;
|
|
|
|
case 0x06:
|
|
|
|
cp = check_path(path1, &acc);
|
|
|
|
if (acc) break;
|
|
|
|
path3 = append_path(root, cp);
|
|
|
|
acc = fst_get_file_info(class, path3);
|
|
|
|
break;
|
|
|
|
case 0x07:
|
|
|
|
acc = fst_judge_name(class, path1);
|
|
|
|
break;
|
|
|
|
case 0x08:
|
|
|
|
acc = fst_volume(class);
|
|
|
|
break;
|
|
|
|
case 0x0b:
|
|
|
|
cp = check_path(path1, &acc);
|
|
|
|
if (acc) break;
|
|
|
|
path3 = append_path(root, cp);
|
|
|
|
acc = fst_clear_backup(class, path3);
|
|
|
|
break;
|
|
|
|
case 0x10:
|
|
|
|
cp = check_path(path1, &acc);
|
|
|
|
if (acc) break;
|
|
|
|
path3 = append_path(root, cp);
|
|
|
|
acc = fst_open(class, path3);
|
|
|
|
break;
|
|
|
|
case 0x012:
|
|
|
|
acc = fst_read(class);
|
|
|
|
break;
|
|
|
|
case 0x013:
|
|
|
|
acc = fst_write(class);
|
|
|
|
break;
|
|
|
|
case 0x14:
|
|
|
|
acc = fst_close(class);
|
|
|
|
break;
|
|
|
|
case 0x15:
|
|
|
|
acc = fst_flush(class);
|
|
|
|
break;
|
|
|
|
case 0x16:
|
|
|
|
acc = fst_set_mark(class);
|
|
|
|
break;
|
|
|
|
case 0x17:
|
|
|
|
acc = fst_get_mark(class);
|
|
|
|
break;
|
|
|
|
case 0x18:
|
|
|
|
acc = fst_set_eof(class);
|
|
|
|
break;
|
|
|
|
case 0x19:
|
|
|
|
acc = fst_get_eof(class);
|
|
|
|
break;
|
|
|
|
case 0x1c:
|
|
|
|
acc = fst_get_dir_entry(class);
|
|
|
|
break;
|
|
|
|
case 0x24:
|
|
|
|
acc = fst_format(class);
|
|
|
|
break;
|
|
|
|
case 0x25:
|
|
|
|
acc = fst_erase(class);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
acc = invalidFSTop;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
fputs("\n", stderr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (acc) fprintf(stderr, " %02x %s\n", acc, error_name(acc));
|
|
|
|
|
|
|
|
gc_free();
|
|
|
|
|
|
|
|
engine.acc = acc;
|
|
|
|
if (acc) SEC();
|
|
|
|
else CLC();
|
|
|
|
}
|