diff --git a/src/host_common.c b/src/host_common.c new file mode 100644 index 0000000..666d4bb --- /dev/null +++ b/src/host_common.c @@ -0,0 +1,1028 @@ +#define _BSD_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "defc.h" +#include "gsos.h" +#include "fst.h" + +#include "host_common.h" + +#if defined(__APPLE__) +#include +#include +#include +#endif + +#ifdef __linux__ +#include + +#endif + + +#if defined(_WIN32) || defined(WIN_SDL) +#include +#include +#endif + +#if defined(__FreeBSD__) +#include +#include +#endif + +#if defined(_AIX) +#include +#endif + +#ifndef XATTR_FINDERINFO_NAME +#define XATTR_FINDERINFO_NAME "com.apple.FinderInfo" +#endif + +#ifndef XATTR_RESOURCEFORK_NAME +#define XATTR_RESOURCEFORK_NAME "com.apple.ResourceFork" +#endif + + +ino_t root_ino = 0; +dev_t root_dev = 0; +char *host_root = NULL; +int host_read_only = 0; + +char *g_cfg_host_path = ""; // must not be null. +int g_cfg_host_read_only = 0; +int g_cfg_host_crlf = 1; +int g_cfg_host_merlin = 0; + + + + +unsigned host_startup(void) { + + struct stat st; + + if (!g_cfg_host_path) return invalidFSTop; + if (!*g_cfg_host_path) return invalidFSTop; + if (host_root) free(host_root); + host_root = strdup(g_cfg_host_path); + + host_read_only = g_cfg_host_read_only; + + if (stat(host_root, &st) < 0) { + fprintf(stderr, "%s does not exist\n", host_root); + return invalidFSTop; + } + if (!S_ISDIR(st.st_mode)) { + fprintf(stderr, "%s is not a directory\n", host_root); + return invalidFSTop; + } + + root_ino = st.st_ino; + root_dev = st.st_dev; + + return 0; +} + +void host_shutdown(void) { + if (host_root) free(host_root); + host_root = NULL; + root_ino = 0; + root_dev = 0; +} + +int host_is_root(struct stat *st) { + return st->st_ino == root_ino && st->st_dev == root_dev; +} + + +/* + * simple malloc pool to simplify code. Should never need > 4 allocations. + * + */ +static void *gc[16]; +static void **gc_ptr = &gc[0]; + +void *host_gc_malloc(size_t size) { + if (gc_ptr == &gc[16]) { + errno = ENOMEM; + return NULL; + } + + void *ptr = malloc(size); + if (ptr) { + *gc_ptr++ = ptr; + } + return ptr; +} + + +void host_gc_free(void) { + + while (gc_ptr > gc) free(*--gc_ptr); + +} + +char *host_gc_strdup(const char *src) { + if (!src) return ""; + if (!*src) return ""; + int len = strlen(src) + 1; + char *cp = host_gc_malloc(len); + memcpy(cp, src, len); + return cp; +} + +char *host_gc_append_path(const char *a, const char *b) { + + /* strip all leading /s from b) */ + while (*b == '/') ++b; + + int aa = strlen(a); + int bb = strlen(b); + + char *cp = host_gc_malloc(aa + bb + 2); + if (!cp) return NULL; + memcpy(cp, a, aa); + int len = aa; + + /* strip all trailing /s from b */ + while (len > 2 && cp[len-1] == '/') --len; + cp[len++] = '/'; + memcpy(cp + len, b, bb); + len += bb; + cp[len] = 0; + return cp; +} + + +char *host_gc_append_string(const char *a, const char *b) { + int aa = strlen(a); + int bb = strlen(b); + + char *cp = host_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; +} + + + +/* + * text conversion. + */ + +void host_cr_to_lf(byte *buffer, size_t size) { + size_t i; + for (i = 0; i < size; ++i) { + if (buffer[i] == '\r') buffer[i] = '\n'; + } +} + +void host_lf_to_cr(byte *buffer, size_t size) { + size_t i; + for (i = 0; i < size; ++i) { + if (buffer[i] == '\n') buffer[i] = '\r'; + } +} + +void host_merlin_to_text(byte *buffer, size_t size) { + 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; + } +} + +void host_text_to_merlin(byte *buffer, size_t size) { + 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; + } +} + + +/* + * error remapping. + * NOTE - GS/OS errors are a superset of P8 errors + */ + +static word32 enoent(const char *path) { + /* + 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; +} + +word32 host_map_errno(int xerrno) { + switch(xerrno) { + 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; + } +} + +word32 host_map_errno_path(int xerrno, const char *path) { + if (xerrno == ENOENT) return enoent(path); + return host_map_errno(xerrno); +} + +const char *host_error_name(word16 error) { + 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", + "", + "badBufferAddress", /* P8 MLI only */ + "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 ""; +} + + + +/* + * File info. + */ + + +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; +} + + +static int finder_info_to_filetype(const byte *buffer, word16 *file_type, word32 *aux_type) { + + 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; +} + +int host_file_type_to_finder_info(byte *buffer, word16 file_type, word32 aux_type) { + 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; +} + + +#if defined(__APPLE__) +void host_get_file_xinfo(const char *path, struct file_info *fi) { + + 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; + + tmp = getxattr(path, XATTR_FINDERINFO_NAME, fi->finder_info, 32, 0, 0); + if (tmp == 16 || tmp == 32){ + fi->has_fi = 1; + + finder_info_to_filetype(fi->finder_info, &fi->file_type, &fi->aux_type); + } +} +#elif defined(__sun) +void host_get_file_xinfo(const char *path, struct file_info *fi) { + + 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); + } +} +#elif defined(__linux__) +void host_get_file_xinfo(const char *path, struct file_info *fi) { + + 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; + + tmp = getxattr(path, "user.com.apple.FinderInfo", 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); + } +} +#else +void host_get_file_xinfo(const char *path, struct file_info *fi) { +} +#endif + +#undef _ +#define _(a, b, c) { a, sizeof(a) - 1, b, c } +struct ftype_entry { + char *ext; + unsigned length; + unsigned file_type; + unsigned aux_type; +}; + +static struct ftype_entry suffixes[] = { + _("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} +}; + +static struct ftype_entry prefixes[] = { + _("m16.", 0xb0, 0x0003), + _("e16.", 0xb0, 0x0003), + { 0, 0, 0, 0} +}; + +#undef _ + +word32 host_get_file_info(const char *path, struct file_info *fi) { + struct stat st; + memset(fi, 0, sizeof(*fi)); + + int ok = stat(path, &st); + if (ok < 0) return host_map_errno(errno); + + fi->eof = st.st_size; + fi->blocks = st.st_blocks; + + fi->create_date = st.st_ctime; + fi->modified_date = st.st_mtime; + +#if defined(__APPLE__) + fi->create_date = st.st_birthtime; +#endif + + + fi->st_mode = st.st_mode; + + if (S_ISDIR(st.st_mode)) { + fi->storage_type = directoryFile; + fi->file_type = 0x0f; + if (host_is_root(&st)) + 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 = sapling; + else fi->storage_type = tree; + } 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)) { + host_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 = extendedFile; + + return 0; +} + + + + +#if defined(__APPLE__) +word32 host_set_file_info(const char *path, struct file_info *fi) { + + int ok; + struct attrlist list; + unsigned i = 0; + struct timespec dates[2]; + + if (fi->has_fi && fi->storage_type != 0x0d) { + ok = setxattr(path, XATTR_FINDERINFO_NAME, fi->finder_info, 32, 0, 0); + if (ok < 0) return host_map_errno(errno); + } + + + memset(&list, 0, sizeof(list)); + memset(dates, 0, sizeof(dates)); + + list.bitmapcount = ATTR_BIT_MAP_COUNT; + list.commonattr = 0; + + if (fi->create_date) + { + dates[i++].tv_sec = fi->create_date; + list.commonattr |= ATTR_CMN_CRTIME; + } + + if (fi->modified_date) + { + dates[i++].tv_sec = fi->modified_date; + list.commonattr |= ATTR_CMN_MODTIME; + } + + ok = 0; + if (i) ok = setattrlist(path, &list, dates, i * sizeof(struct timespec), 0); + return 0; +} +#elif defined(__sun) +word32 host_set_file_info(const char *path, struct file_info *fi) { + + if (fi->has_fi && fi->storage_type != 0x0d) { + int fd = attropen(path, XATTR_FINDERINFO_NAME, O_WRONLY | O_CREAT, 0666); + if (fd < 0) return host_map_errno(errno); + write(fd, fi->finder_info, 32); + close(fd); + } + + if (fi->modified_date) { + struct timeval times[2]; + + memset(times, 0, sizeof(times)); + + //times[0] = 0; // access + times[1].tv_sec = fi.modified_date; // modified + int ok = utimes(path, times); + if (ok < 0) return host_map_errno(errno); + } + return 0; +} +#elif defined(__linux__) +word32 host_set_file_info(const char *path, struct file_info *fi) { + + if (fi->has_fi && fi->storage_type != 0x0d) { + int ok = setxattr(path, "user.apple.FinderInfo", fi->finder_info, 32, 0); + if (ok < 0) return host_map_errno(errno); + } + + if (fi->modified_date) { + struct timeval times[2]; + + memset(times, 0, sizeof(times)); + + //times[0] = 0; // access + times[1].tv_sec = fi->modified_date; // modified + int ok = utimes(path, times); + if (ok < 0) return host_map_errno(errno); + } + return 0; +} + +#else + +word32 host_set_file_info(const char *path, struct file_info *fi) { + + if (fi->modified_date) { + + struct timeval times[2]; + + memset(times, 0, sizeof(times)); + + times[0] = 0; // access + times[1].tv_sec = fi->modified_date; // modified + + int ok = utimes(path, times); + if (ok < 0) return host_map_errno(errno); + } + return 0; +} + +#endif + + +/* + * Date/time conversion + */ + +/* + * converts time_t to a gs/os readhextime date/time record. + */ + +void host_set_date_time_rec(word32 ptr, time_t time) { + + if (time == 0) { + for (int i = 0; i < 8; ++i) set_memory_c(ptr++, 0, 0); + return; + } + + struct tm *tm = localtime(&time); + if (tm->tm_sec == 60) tm->tm_sec = 59; /* leap second */ + + 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); +} + +/* + * converts time_t to a prodos16 date/time record. + */ +void host_set_date_time(word32 ptr, time_t time) { + + if (time == 0) { + for (int i = 0; i < 4; ++i) set_memory_c(ptr++, 0, 0); + return; + } + + struct tm *tm = localtime(&time); + + word16 tmp = 0; + tmp |= (tm->tm_year % 100) << 9; + tmp |= tm->tm_mon << 5; + tmp |= tm->tm_mday; + + set_memory16_c(ptr, tmp, 0); + ptr += 2; + + tmp = 0; + tmp |= tm->tm_hour << 8; + tmp |= tm->tm_min; + set_memory16_c(ptr, tmp, 0); +} + +word32 host_convert_date_time(time_t time) { + + if (time == 0) return 0; + + struct tm *tm = localtime(&time); + + word16 dd = 0; + dd |= (tm->tm_year % 100) << 9; + dd |= tm->tm_mon << 5; + dd |= tm->tm_mday; + + word16 tt = 0; + tt |= tm->tm_hour << 8; + tt |= tm->tm_min; + + + return (tt << 16) | dd; +} + + +time_t host_get_date_time(word32 ptr) { + + word16 a = get_memory16_c(ptr + 0, 0); + word16 b = get_memory16_c(ptr + 2, 0); + if (!a && !b) return 0; + + struct tm tm; + memset(&tm, 0, sizeof(tm)); + + tm.tm_year = (a >> 9) & 0x7f; + tm.tm_mon = ((a >> 5) & 0x0f) - 1; + tm.tm_mday = (a >> 0) & 0x1f; + + tm.tm_hour = (b >> 8) & 0x1f; + tm.tm_min = (b >> 0) & 0x3f; + tm.tm_sec = 0; + + tm.tm_isdst = -1; + + // 00 - 39 => 2000-2039 + // 40 - 99 => 1940-1999 + if (tm.tm_year < 40) tm.tm_year += 100; + + + return mktime(&tm); +} + +time_t host_get_date_time_rec(word32 ptr) { + + byte buffer[8]; + for (int i = 0; i < 8; ++i) buffer[i] = get_memory_c(ptr++, 0); + + if (!memcmp(buffer, "\x00\x00\x00\x00\x00\x00\x00\x00", 8)) return 0; + + struct tm tm; + memset(&tm, 0, sizeof(tm)); + + 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; + + return mktime(&tm); +} + + +void host_hexdump(word32 address, int size) { + const char *HexMap = "0123456789abcdef"; + + char buffer1[16 * 3 + 1 + 1]; + char buffer2[16 + 1]; + unsigned i, j; + + printf("\n"); + while (size > 0) { + memset(buffer1, ' ', sizeof(buffer1)); + memset(buffer2, ' ', sizeof(buffer2)); + + int linelen = size; + if (linelen > 16) linelen = 16; + + for (i = 0, j = 0; i < linelen; i++) { + unsigned x = get_memory_c(address + i, 0); + buffer1[j++] = HexMap[x >> 4]; + buffer1[j++] = HexMap[x & 0x0f]; + j++; + if (i == 7) j++; + x &= 0x7f; + // isascii not part of std:: and may be a macro. + buffer2[i] = isascii(x) && isprint(x) ? x : '.'; + } + + buffer1[sizeof(buffer1) - 1] = 0; + buffer2[sizeof(buffer2) - 1] = 0; + + printf("%06x:\t%s\t%s\n", + address, buffer1, buffer2); + address += 16; + size -= 16; + } + printf("\n"); +} + + +void host_hexdump_native(void *data, unsigned address, int size) { + const char *HexMap = "0123456789abcdef"; + + char buffer1[16 * 3 + 1 + 1]; + char buffer2[16 + 1]; + unsigned i, j; + + printf("\n"); + while (size > 0) { + memset(buffer1, ' ', sizeof(buffer1)); + memset(buffer2, ' ', sizeof(buffer2)); + + int linelen = size; + if (linelen > 16) linelen = 16; + + for (i = 0, j = 0; i < linelen; i++) { + unsigned x = ((byte *)data)[address + i]; + buffer1[j++] = HexMap[x >> 4]; + buffer1[j++] = HexMap[x & 0x0f]; + j++; + if (i == 7) j++; + x &= 0x7f; + // isascii not part of std:: and may be a macro. + buffer2[i] = isascii(x) && isprint(x) ? x : '.'; + } + + buffer1[sizeof(buffer1) - 1] = 0; + buffer2[sizeof(buffer2) - 1] = 0; + + printf("%06x:\t%s\t%s\n", + address, buffer1, buffer2); + address += 16; + size -= 16; + } + printf("\n"); +} diff --git a/src/host_common.h b/src/host_common.h new file mode 100644 index 0000000..d595f49 --- /dev/null +++ b/src/host_common.h @@ -0,0 +1,105 @@ +enum { + file_non, + file_regular, + file_resource, + file_directory, +}; + +enum { + translate_none, + translate_crlf, + translate_merlin, +}; + + + +struct file_info { + + 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]; +}; + + +extern Engine_reg engine; + +#define SEC() engine.psr |= 0x01 +#define CLC() engine.psr &= ~0x01 +#define SEV() engine.psr |= 0x40 +#define CLV() engine.psr &= ~0x40 +#define SEZ() engine.psr |= 0x02 +#define CLZ() engine.psr &= ~0x02 +#define SEI() engine.psr |= 0x04 +#define CLI() engine.psr &= ~0x04 + +enum { + C = 0x01, + Z = 0x02, + I = 0x04, + D = 0x08, + X = 0x10, + M = 0x20, + V = 0x40, + N = 0x80 +}; + +extern int g_cfg_host_read_only; +extern int g_cfg_host_crlf; +extern int g_cfg_host_merlin; +extern char *host_root; + +unsigned host_startup(void); +void host_shutdown(void); + +int host_is_root(struct stat *); + +/* garbage collected string routines */ + +void *host_gc_malloc(size_t size); +void host_gc_free(void); +char *host_gc_strdup(const char *src); +char *host_gc_append_path(const char *a, const char *b); +char *host_gc_append_string(const char *a, const char *b); + +/* text conversion */ +void host_cr_to_lf(byte *buffer, size_t size); +void host_lf_to_cr(byte *buffer, size_t size); +void host_merlin_to_text(byte *buffer, size_t size); +void host_text_to_merlin(byte *buffer, size_t size); + +/* errno -> IIgs/mli error */ +word32 host_map_errno(int xerrno); +word32 host_map_errno_path(int xerrno, const char *path); +const char *host_error_name(word16 error); + +/* file info */ + +int host_file_type_to_finder_info(byte *buffer, word16 file_type, word32 aux_type); +void host_get_file_xinfo(const char *path, struct file_info *fi); +word32 host_get_file_info(const char *path, struct file_info *fi); +word32 host_set_file_info(const char *path, struct file_info *fi); + + +void host_set_date_time_rec(word32 ptr, time_t time); +void host_set_date_time(word32 ptr, time_t time); + +time_t host_get_date_time(word32 ptr); +time_t host_get_date_time_rec(word32 ptr); + +/* convert to prodos date/time */ +word32 host_convert_date_time(time_t time); + + +void host_hexdump(word32 address, int size); +void host_hexdump_native(void *data, unsigned address, int size); + diff --git a/src/host_fst.c b/src/host_fst.c index 8a4a8d4..082956e 100644 --- a/src/host_fst.c +++ b/src/host_fst.c @@ -17,9 +17,6 @@ #include #include -#include "defc.h" -#include "gsos.h" -#include "fst.h" #if defined(__APPLE__) #include @@ -27,34 +24,13 @@ #include #endif -#ifdef __linux__ -#include +#include "defc.h" +#include "gsos.h" +#include "fst.h" -#endif +#include "host_common.h" -#if defined(_WIN32) || defined(WIN_SDL) -#include -#include -#endif - -#if defined(__FreeBSD__) -#include -#include -#endif - -#if defined(_AIX) -#include -#endif - -#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; @@ -71,17 +47,6 @@ extern Engine_reg engine; #define global_buffer 0x009a00 -enum { - regular_file, - resource_file, - directory_file, -}; - -enum { - translate_none, - translate_crlf, - translate_merlin, -}; struct directory { @@ -140,124 +105,7 @@ static int free_cookie(int cookie) { 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; -int g_cfg_host_crlf = 1; -int g_cfg_host_merlin = 0; - -/* - * 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) { - if (gc_ptr == &gc[16]) { - errno = ENOMEM; - return NULL; - } - - void *ptr = malloc(size); - if (ptr) { - *gc_ptr++ = ptr; - } - return ptr; -} - - -static void gc_free(void) { - - while (gc_ptr > gc) free(*--gc_ptr); - -} - -static char *append_path(const char *a, const char *b) { - int aa = strlen(a); - int bb = strlen(b); - - 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; -} - - -static char *append_string(const char *a, const char *b) { - int aa = strlen(a); - int bb = strlen(b); - - 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; -} - -static char *gc_strdup(const char *src) { - if (!src) return ""; - if (!*src) return ""; - int len = strlen(src) + 1; - char *cp = gc_malloc(len); - memcpy(cp, src, len); - return cp; -} - -static word32 enoent(const char *path) { - /* - 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; -} - -static word32 map_errno() { - 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; - } -} - -static word32 map_errno_path(const char *path) { - if (errno == ENOENT) return enoent(path); - return map_errno(); -} static struct fd_entry *find_fd(int cookie) { @@ -271,7 +119,7 @@ static struct fd_entry *find_fd(int cookie) { } -static void free_fd(struct fd_entry *e) { +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); @@ -307,41 +155,6 @@ static word32 remove_fd(int cookie) { return rv; } -static void cr_to_lf(byte *buffer, size_t size) { - size_t i; - for (i = 0; i < size; ++i) { - if (buffer[i] == '\r') buffer[i] = '\n'; - } -} - -static void lf_to_cr(byte *buffer, size_t size) { - size_t i; - for (i = 0; i < size; ++i) { - if (buffer[i] == '\n') buffer[i] = '\r'; - } -} - -static void merlin_to_text(byte *buffer, size_t size) { - 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; - } -} - -static void text_to_merlin(byte *buffer, size_t size) { - 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; - } -} static ssize_t safe_read(struct fd_entry *e, byte *buffer, size_t count) { int fd = e->fd; @@ -401,423 +214,6 @@ static ssize_t safe_write(struct fd_entry *e, byte *buffer, size_t count) { } } -struct file_info { - - 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]; -}; - -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; -} - - -static int finder_info_to_filetype(const byte *buffer, word16 *file_type, word32 *aux_type) { - - 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; -} - -static int file_type_to_finder_info(byte *buffer, word16 file_type, word32 aux_type) { - 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; -} - - -#if defined(__APPLE__) -static void get_file_xinfo(const char *path, struct file_info *fi) { - - 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; - - tmp = getxattr(path, XATTR_FINDERINFO_NAME, fi->finder_info, 32, 0, 0); - if (tmp == 16 || tmp == 32) { - fi->has_fi = 1; - - finder_info_to_filetype(fi->finder_info, &fi->file_type, &fi->aux_type); - } -} -#elif defined(__sun) -static void get_file_xinfo(const char *path, struct file_info *fi) { - - 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); - } -} -#elif defined(__linux__) -static void get_file_xinfo(const char *path, struct file_info *fi) { - - 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; - - tmp = getxattr(path, "user.com.apple.FinderInfo", 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); - } -} -#else -static void get_file_xinfo(const char *path, struct file_info *fi) { -} -#endif - -#undef _ -#define _(a, b, c) { a, sizeof(a) - 1, b, c } -struct ftype_entry { - char *ext; - unsigned length; - unsigned file_type; - unsigned aux_type; -}; - -static struct ftype_entry suffixes[] = { - _("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} -}; - -static struct ftype_entry prefixes[] = { - _("m16.", 0xb0, 0x0003), - _("e16.", 0xb0, 0x0003), - { 0, 0, 0, 0} -}; - -#undef _ - -static word32 get_file_info(const char *path, struct file_info *fi) { - struct stat st; - memset(fi, 0, sizeof(*fi)); - - int ok = stat(path, &st); - if (ok < 0) return map_errno(); - - fi->eof = st.st_size; - fi->blocks = st.st_blocks; - - fi->create_date = st.st_ctime; - fi->modified_date = st.st_mtime; - -#if defined(__APPLE__) - fi->create_date = st.st_birthtime; -#endif - - - 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; -} - - - - -#if defined(__APPLE__) -static word32 set_file_info(const char *path, struct file_info *fi) { - - int ok; - struct attrlist list; - unsigned i = 0; - struct timespec dates[2]; - - 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(); - } - - - memset(&list, 0, sizeof(list)); - memset(dates, 0, sizeof(dates)); - - list.bitmapcount = ATTR_BIT_MAP_COUNT; - list.commonattr = 0; - - if (fi->create_date) - { - dates[i++].tv_sec = fi->create_date; - list.commonattr |= ATTR_CMN_CRTIME; - } - - if (fi->modified_date) - { - dates[i++].tv_sec = fi->modified_date; - list.commonattr |= ATTR_CMN_MODTIME; - } - - ok = 0; - if (i) ok = setattrlist(path, &list, dates, i * sizeof(struct timespec), 0); - return 0; -} -#elif defined(__sun) -static word32 set_file_info(const char *path, struct file_info *fi) { - - 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); - } - - if (fi->modified_date) { - struct timeval times[2]; - - memset(times, 0, sizeof(times)); - - //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; -} -#elif defined(__linux__) -static word32 set_file_info(const char *path, struct file_info *fi) { - - 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(); - } - - if (fi->modified_date) { - struct timeval times[2]; - - memset(times, 0, sizeof(times)); - - //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; -} - -#else - -static word32 set_file_info(const char *path, struct file_info *fi) { - - if (fi->modified_date) { - - struct timeval times[2]; - - memset(times, 0, sizeof(times)); - - 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; -} - -#endif - /* * if this is an absolute path, verify and skip past :Host: @@ -860,7 +256,7 @@ static char * get_gsstr(word32 ptr) { if (!ptr) return NULL; int length = get_memory16_c(ptr, 0); ptr += 2; - char *str = gc_malloc(length + 1); + char *str = host_gc_malloc(length + 1); for (int i = 0; i < length; ++i) { char c = get_memory_c(ptr+i, 0); if (c == ':') c = '/'; @@ -872,9 +268,9 @@ static char * get_gsstr(word32 ptr) { static char * get_pstr(word32 ptr) { if (!ptr) return NULL; - int length = get_memory16_c(ptr, 0); - ptr += 2; - char *str = gc_malloc(length + 1); + int length = get_memory_c(ptr, 0); + ptr += 1; + char *str = host_gc_malloc(length + 1); for (int i = 0; i < length; ++i) { char c = get_memory_c(ptr+i, 0); if (c == ':') c = '/'; @@ -983,104 +379,6 @@ static word32 set_option_list(word32 ptr, word16 fstID, const byte *data, int si return 0; } -/* - * converts time_t to a gs/os readhextime date/time record. - */ - -static void set_date_time_rec(word32 ptr, time_t time) { - - if (time == 0) { - for (int i = 0; i < 8; ++i) set_memory_c(ptr++, 0, 0); - return; - } - - struct tm *tm = localtime(&time); - if (tm->tm_sec == 60) tm->tm_sec = 59; /* leap second */ - - 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); -} - -/* - * converts time_t to a prodos16 date/time record. - */ -static void set_date_time(word32 ptr, time_t time) { - - if (time == 0) { - for (int i = 0; i < 4; ++i) set_memory_c(ptr++, 0, 0); - return; - } - - struct tm *tm = localtime(&time); - - word16 tmp = 0; - tmp |= (tm->tm_year % 100) << 9; - tmp |= tm->tm_mon << 5; - tmp |= tm->tm_mday; - - set_memory16_c(ptr, tmp, 0); - ptr += 2; - - tmp = 0; - tmp |= tm->tm_hour << 8; - tmp |= tm->tm_min; - set_memory16_c(ptr, tmp, 0); -} - - -static time_t get_date_time(word32 ptr) { - - word16 a = get_memory16_c(ptr + 0, 0); - word16 b = get_memory16_c(ptr + 2, 0); - if (!a && !b) return 0; - - struct tm tm; - memset(&tm, 0, sizeof(tm)); - - tm.tm_year = (a >> 9) & 0x7f; - tm.tm_mon = ((a >> 5) & 0x0f) - 1; - tm.tm_mday = (a >> 0) & 0x1f; - - tm.tm_hour = (b >> 8) & 0x1f; - tm.tm_min = (b >> 0) & 0x3f; - tm.tm_sec = 0; - - tm.tm_isdst = -1; - - // 00 - 39 => 2000-2039 - // 40 - 99 => 1940-1999 - if (tm.tm_year < 40) tm.tm_year += 100; - - - return mktime(&tm); -} - -static time_t get_date_time_rec(word32 ptr) { - - byte buffer[8]; - for (int i = 0; i < 8; ++i) buffer[i] = get_memory_c(ptr++, 0); - - if (!memcmp(buffer, "\x00\x00\x00\x00\x00\x00\x00\x00", 8)) return 0; - - struct tm tm; - memset(&tm, 0, sizeof(tm)); - - 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; - - return mktime(&tm); -} static char *get_path1(void) { @@ -1100,9 +398,6 @@ static char *get_path2(void) { } -#define SEC() engine.psr |= 1 -#define CLC() engine.psr &= ~1 - static word32 fst_shutdown(void) { @@ -1114,37 +409,18 @@ static word32 fst_shutdown(void) { free_fd(head); head = next; } + host_shutdown(); return 0; } static word32 fst_startup(void) { // if restart, close any previous files. - struct stat st; - - if (!g_cfg_host_path) return invalidFSTop; - if (!*g_cfg_host_path) return invalidFSTop; - if (root) free(root); - root = strdup(g_cfg_host_path); - - read_only = g_cfg_host_read_only; - fst_shutdown(); - 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; - } - root_ino = st.st_ino; - root_dev = st.st_dev; + return host_startup(); - return 0; } @@ -1167,7 +443,7 @@ static word32 fst_create(int class, const char *path) { if (pcount >= 4) { - file_type_to_finder_info(fi.finder_info, fi.file_type, fi.aux_type); + host_file_type_to_finder_info(fi.finder_info, fi.file_type, fi.aux_type); fi.has_fi = 1; } @@ -1178,9 +454,9 @@ static word32 fst_create(int class, const char *path) { 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); + fi.create_date = host_get_date_time(pb + CreateRec_createDate); - file_type_to_finder_info(fi.finder_info, fi.file_type, fi.aux_type); + host_file_type_to_finder_info(fi.finder_info, fi.file_type, fi.aux_type); fi.has_fi = 1; } int ok; @@ -1191,7 +467,7 @@ static word32 fst_create(int class, const char *path) { if (fi.storage_type == 0x0d) { ok = mkdir(path, 0777); if (ok < 0) { - return map_errno_path(path); + return host_map_errno_path(errno, path); } if (class) { @@ -1206,9 +482,9 @@ static word32 fst_create(int class, const char *path) { // 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); + if (ok < 0) return host_map_errno_path(errno, path); // set ftype, auxtype... - set_file_info(path, &fi); + host_set_file_info(path, &fi); close(ok); fi.storage_type = 1; @@ -1226,7 +502,7 @@ static word32 fst_create(int class, const char *path) { 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); + word32 rv = host_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; @@ -1245,18 +521,17 @@ static word32 fst_destroy(int class, const char *path) { if (!path) return badStoreType; if (stat(path, &st) < 0) { - return map_errno_path(path); + return host_map_errno_path(errno, path); } // can't delete volume root. - if (st.st_ino == root_ino && st.st_dev == root_dev) { + if (host_is_root(&st)) return badStoreType; - } int ok = S_ISDIR(st.st_mode) ? rmdir(path) : unlink(path); - if (ok < 0) return map_errno_path(path); + if (ok < 0) return host_map_errno_path(errno, path); return 0; } @@ -1270,7 +545,7 @@ static word32 fst_set_file_info(int class, const char *path) { // load up existing file types / finder info. - get_file_xinfo(path, &fi); + host_get_file_xinfo(path, &fi); word32 option_list = 0; if (class) { @@ -1281,13 +556,13 @@ static word32 fst_set_file_info(int class, const char *path) { 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 >= 6) fi.create_date = host_get_date_time_rec(pb + FileInfoRecGS_createDateTime); + if (pcount >= 7) fi.modified_date = host_get_date_time_rec(pb + FileInfoRecGS_modDateTime); if (pcount >= 8) option_list = get_memory24_c(pb + FileInfoRecGS_optionList, 0); // remainder reserved if (pcount >= 4) { - file_type_to_finder_info(fi.finder_info, fi.file_type, fi.aux_type); + host_file_type_to_finder_info(fi.finder_info, fi.file_type, fi.aux_type); fi.has_fi = 1; } @@ -1297,10 +572,10 @@ static word32 fst_set_file_info(int class, const char *path) { 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); + fi.create_date = host_get_date_time(pb + FileRec_createDate); + fi.modified_date = host_get_date_time(pb + FileRec_modDate); - file_type_to_finder_info(fi.finder_info, fi.file_type, fi.aux_type); + host_file_type_to_finder_info(fi.finder_info, fi.file_type, fi.aux_type); fi.has_fi = 1; } @@ -1319,7 +594,7 @@ static word32 fst_set_file_info(int class, const char *path) { } - return set_file_info(path, &fi); + return host_set_file_info(path, &fi); } static word32 fst_get_file_info(int class, const char *path) { @@ -1329,7 +604,7 @@ static word32 fst_get_file_info(int class, const char *path) { struct file_info fi; int rv = 0; - rv = get_file_info(path, &fi); + rv = host_get_file_info(path, &fi); if (rv) return rv; if (class) { @@ -1341,8 +616,8 @@ static word32 fst_get_file_info(int class, const char *path) { 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); - 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); + if (pcount >= 6) host_set_date_time_rec(pb + FileInfoRecGS_createDateTime, fi.create_date); + if (pcount >= 7) host_set_date_time_rec(pb + FileInfoRecGS_modDateTime, fi.modified_date); if (pcount >= 8) { word16 fst_id = hfsFSID; @@ -1362,8 +637,8 @@ static word32 fst_get_file_info(int class, const char *path) { set_memory32_c(pb + FileRec_auxType, fi.aux_type, 0); set_memory16_c(pb + FileRec_storageType, fi.storage_type, 0); - set_date_time(pb + FileRec_createDate, fi.create_date); - set_date_time(pb + FileRec_modDate, fi.modified_date); + host_set_date_time(pb + FileRec_createDate, fi.create_date); + host_set_date_time(pb + FileRec_modDate, fi.modified_date); set_memory32_c(pb + FileRec_blocksUsed, fi.blocks, 0); } @@ -1473,7 +748,7 @@ static int open_data_fork(const char *path, word16 *access, word16 *error) { break; } if (fd < 0) { - *error = map_errno_path(path); + *error = host_map_errno_path(errno, path); } return fd; @@ -1483,7 +758,7 @@ static int open_resource_fork(const char *path, word16 *access, word16 *error) { // os x / hfs/apfs don't need to specifically create a resource fork. // or do they? - char *rpath = append_path(path, _PATH_RSRCFORKSPEC); + char *rpath = host_gc_append_path(path, _PATH_RSRCFORKSPEC); int fd = -1; for (;;) { @@ -1511,7 +786,7 @@ static int open_resource_fork(const char *path, word16 *access, word16 *error) { break; } if (fd < 0) { - *error = map_errno_path(path); + *error = host_map_errno_path(errno, path); } return fd; @@ -1523,7 +798,7 @@ static int open_resource_fork(const char *path, word16 *access, word16 *error) { static int open_resource_fork(const char *path, word16 *access, word16 *error) { int tmp = open(path, O_RDONLY); if (tmp < 0) { - *error = map_errno_path(path); + *error = host_map_errno_path(errno, path); return -1; } @@ -1554,7 +829,7 @@ static int open_resource_fork(const char *path, word16 *access, word16 *error) { } if (fd < 0) { - *error = map_errno_path(path); + *error = host_map_errno_path(errno, path); close(tmp); return -1; } @@ -1583,12 +858,12 @@ static word32 fst_open(int class, const char *path) { struct file_info fi; word16 rv = 0; - rv = get_file_info(path, &fi); + rv = host_get_file_info(path, &fi); if (rv) return rv; int fd = -1; - int type = regular_file; + int type = file_regular; struct directory *dd = NULL; word16 pcount = 0; @@ -1603,7 +878,7 @@ static word32 fst_open(int class, const char *path) { if (resource_number) { if (resource_number > 1) return paramRangeErr; - type = resource_file; + type = file_resource; } if (access > 3) return paramRangeErr; @@ -1619,11 +894,11 @@ static word32 fst_open(int class, const char *path) { case readWriteEnable: return invalidAccess; } - type = directory_file; + type = file_directory; } - if (read_only) { + if (g_cfg_host_read_only) { switch (request_access) { case readEnableAllowWrite: request_access = readEnable; @@ -1637,13 +912,13 @@ static word32 fst_open(int class, const char *path) { access = request_access; switch(type) { - case regular_file: + case file_regular: fd = open_data_fork(path, &access, &rv); break; - case resource_file: + case file_resource: fd = open_resource_fork(path, &access, &rv); break; - case directory_file: + case file_directory: dd = read_directory(path, &rv); break; } @@ -1655,10 +930,10 @@ static word32 fst_open(int class, const char *path) { 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 >= 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 >= 9) host_set_date_time_rec(pb + OpenRecGS_createDateTime, fi.create_date); + if (pcount >= 10) host_set_date_time_rec(pb + OpenRecGS_modDateTime, fi.modified_date); if (pcount >= 11) { word16 fst_id = hfsFSID; @@ -1693,7 +968,7 @@ static word32 fst_open(int class, const char *path) { return tooManyFilesOpen; } - if (type == regular_file) { + if (type == file_regular){ if (g_cfg_host_crlf) { if (fi.file_type == 0x04 || fi.file_type == 0xb0) @@ -1717,7 +992,7 @@ static word32 fst_open(int class, const char *path) { fd_head = e; engine.xreg = e->cookie; - engine.yreg = access; // actual access, needed in fcr. + engine.yreg = access; // actual access, needed in fcr. return rv; } @@ -1730,7 +1005,7 @@ static word32 fst_read(int class) { if (!e) return invalidRefNum; switch (e->type) { - case directory_file: + case file_directory: return badStoreType; } @@ -1767,7 +1042,7 @@ static word32 fst_read(int class) { 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) return host_map_errno(errno); if (ok == 0) break; transfer_count++; set_memory_c(data_buffer++, b, 0); @@ -1776,12 +1051,12 @@ static word32 fst_read(int class) { if (transfer_count == 0) rv = eofEncountered; } else { - byte *data = gc_malloc(request_count); + byte *data = host_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 = host_map_errno(errno); if (ok == 0) rv = eofEncountered; if (ok > 0) { transfer_count = ok; @@ -1812,7 +1087,7 @@ static word32 fst_write(int class) { return invalidAccess; switch (e->type) { - case directory_file: + case file_directory: return badStoreType; } @@ -1834,7 +1109,7 @@ static word32 fst_write(int class) { } if (request_count == 0) return 0; - byte *data = gc_malloc(request_count); + byte *data = host_gc_malloc(request_count); if (!data) return outOfMem; for (word32 i = 0; i < request_count; ++i) { @@ -1843,7 +1118,7 @@ static word32 fst_write(int class) { word32 rv = 0; ssize_t ok = safe_write(e, data, request_count); - if (ok < 0) rv = map_errno(); + if (ok < 0) rv = host_map_errno(errno); if (ok > 0) { if (class) set_memory32_c(pb + IORecGS_transferCount, ok, 0); @@ -1869,12 +1144,12 @@ static word32 fst_flush(int class) { if (!e) return invalidRefNum; switch (e->type) { - case directory_file: + case file_directory: return badStoreType; } int ok = fsync(e->fd); - if (ok < 0) return map_errno(); + if (ok < 0) return host_map_errno(errno); return 0; } @@ -1912,7 +1187,7 @@ static word32 fst_set_mark(int class) { if (!e) return invalidRefNum; switch (e->type) { - case directory_file: + case file_directory: return badStoreType; } @@ -1932,7 +1207,7 @@ static word32 fst_set_mark(int class) { if (offset < 0) return outOfRange; off_t ok = lseek(e->fd, offset, SEEK_SET); - if (ok < 0) return map_errno(); + if (ok < 0) return host_map_errno(errno); return 0; } @@ -1944,7 +1219,7 @@ static word32 fst_set_eof(int class) { if (!e) return invalidRefNum; switch (e->type) { - case directory_file: + case file_directory: return badStoreType; } @@ -1966,7 +1241,7 @@ static word32 fst_set_eof(int class) { if (offset < 0) return outOfRange; int ok = ftruncate(e->fd, offset); - if (ok < 0) return map_errno(); + if (ok < 0) return host_map_errno(errno); return 0; } @@ -1983,12 +1258,12 @@ static word32 fst_get_mark(int class) { off_t pos = 0; switch (e->type) { - case directory_file: + case file_directory: return badStoreType; } pos = lseek(e->fd, 0, SEEK_CUR); - if (pos < 0) return map_errno(); + if (pos < 0) return host_map_errno(errno); if (class) { set_memory32_c(pb + PositionRecGS_position, pos, 0); @@ -2010,7 +1285,7 @@ static word32 fst_get_eof(int class) { switch (e->type) { - case directory_file: + case file_directory: return badStoreType; } @@ -2020,7 +1295,7 @@ static word32 fst_get_eof(int class) { pos = lseek(e->fd, 0, SEEK_CUR); eof = lseek(e->fd, 0, SEEK_END); - if (eof < 0) return map_errno(); + if (eof < 0) return host_map_errno(errno); lseek(e->fd, pos, SEEK_SET); @@ -2072,7 +1347,7 @@ static struct directory *read_directory(const char *path, word16 *error) { dirp = opendir(path); if (!dirp) { - *error = map_errno_path(path); + *error = host_map_errno_path(errno, path); return NULL; } @@ -2094,7 +1369,7 @@ static struct directory *read_directory(const char *path, word16 *error) { size = sizeof(struct directory) + capacity * sizeof(char *); struct directory * tmp = realloc(dd, size); if (!tmp) { - *error = map_errno(); + *error = host_map_errno(errno); free_directory(dd); closedir(dirp); return NULL; @@ -2122,7 +1397,7 @@ static word32 fst_get_dir_entry(int class) { if (!e) return invalidRefNum; - if (e->type != directory_file) return badFileFormat; + if (e->type != file_directory) return badFileFormat; word32 pb = get_memory24_c(engine.direct + dp_param_blk_ptr, 0); @@ -2179,9 +1454,9 @@ static word32 fst_get_dir_entry(int class) { word32 rv = 0; const char *dname = e->dir->entries[displacement++]; e->dir->displacement = displacement; - char *fullpath = append_path(e->path, dname); + char *fullpath = host_gc_append_path(e->path, dname); struct file_info fi; - rv = get_file_info(fullpath, &fi); + rv = host_get_file_info(fullpath, &fi); if (dname) fprintf(stderr, " - %s", dname); @@ -2197,8 +1472,8 @@ static word32 fst_get_dir_entry(int class) { if (pcount >= 8) set_memory32_c(pb + DirEntryRecGS_eof, fi.eof, 0); if (pcount >= 9) set_memory32_c(pb + DirEntryRecGS_blockCount, fi.blocks, 0); - 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); + if (pcount >= 10) host_set_date_time_rec(pb + DirEntryRecGS_createDateTime, fi.create_date); + if (pcount >= 11) host_set_date_time_rec(pb + DirEntryRecGS_modDateTime, fi.modified_date); 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); @@ -2223,8 +1498,8 @@ static word32 fst_get_dir_entry(int class) { 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); + host_set_date_time_rec(pb + DirEntryRec_createTime, fi.create_date); + host_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); @@ -2239,12 +1514,12 @@ static word32 fst_change_path(int class, const char *path1, const char *path2) { /* 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) + if (stat(path1, &st) < 0) return host_map_errno_path(errno, path1); + if (host_is_root(&st)) return invalidAccess; // rename will delete any previous file. - if (rename(path1, path2) < 0) return map_errno_path(path2); + if (rename(path1, path2) < 0) return host_map_errno_path(errno, path2); return 0; } @@ -2384,159 +1659,6 @@ static const char *call_name(word16 call) { return ""; } -static const char *error_name(word16 error) { - 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 ""; -} void host_fst(void) { @@ -2577,11 +1699,11 @@ void host_fst(void) { } } else { - if (!root) { + if (!host_root) { acc = networkError; engine.acc = acc; SEC(); - fprintf(stderr, " %02x %s\n", acc, error_name(acc)); + fprintf(stderr, " %02x %s\n", acc, host_error_name(acc)); return; } @@ -2593,7 +1715,7 @@ void host_fst(void) { acc = invalidClass; engine.acc = acc; SEC(); - fprintf(stderr, " %02x %s\n", acc, error_name(acc)); + fprintf(stderr, " %02x %s\n", acc, host_error_name(acc)); return; } @@ -2626,14 +1748,14 @@ void host_fst(void) { case 0x01: cp = check_path(path1, &acc); if (acc) break; - path3 = append_path(root, cp); + path3 = host_gc_append_path(host_root, cp); acc = fst_create(class, path3); break; case 0x02: cp = check_path(path1, &acc); if (acc) break; - path3 = append_path(root, cp); + path3 = host_gc_append_path(host_root, cp); acc = fst_destroy(class, path3); break; @@ -2641,25 +1763,25 @@ void host_fst(void) { cp = check_path(path1, &acc); if (acc) break; - path3 = append_path(root, cp); + path3 = host_gc_append_path(host_root, cp); cp = check_path(path2, &acc); if (acc) break; - path4 = append_path(root, cp); + path4 = host_gc_append_path(host_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); + path3 = host_gc_append_path(host_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); + path3 = host_gc_append_path(host_root, cp); acc = fst_get_file_info(class, path3); break; case 0x07: @@ -2671,13 +1793,13 @@ void host_fst(void) { case 0x0b: cp = check_path(path1, &acc); if (acc) break; - path3 = append_path(root, cp); + path3 = host_gc_append_path(host_root, cp); acc = fst_clear_backup(class, path3); break; case 0x10: cp = check_path(path1, &acc); if (acc) break; - path3 = append_path(root, cp); + path3 = host_gc_append_path(host_root, cp); acc = fst_open(class, path3); break; case 0x012: @@ -2721,9 +1843,9 @@ void host_fst(void) { fputs("\n", stderr); } - if (acc) fprintf(stderr, " %02x %s\n", acc, error_name(acc)); + if (acc) fprintf(stderr, " %02x %s\n", acc, host_error_name(acc)); - gc_free(); + host_gc_free(); engine.acc = acc; if (acc) SEC(); diff --git a/src/host_mli.c b/src/host_mli.c new file mode 100644 index 0000000..c4fe8f7 --- /dev/null +++ b/src/host_mli.c @@ -0,0 +1,1430 @@ + +#define _BSD_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "defc.h" +#include "gsos.h" +#include "fst.h" + + +#include "host_common.h" + + +#define LEVEL 0xBFD8 // current file level +#define DEVNUM 0xBF30 // last slot / drive +#define DEVCNT 0xBF31 // count - 1 +#define DEVLST 0xBF32 // active device list +#define PFIXPTR 0xbf9a // active prefix? + +/* bf97 is appletalk prefix? */ + + +#define ENTRY_LENGTH 0x27 +#define ENTRIES_PER_BLOCK 0x0d + + +static unsigned saved_call; +static unsigned saved_dcb; +static unsigned saved_p; +static unsigned saved_patch_address; +static unsigned saved_mli_address; +static unsigned saved_unit; +static char *saved_prefix; + + + +enum { + CREATE = 0xc0, + DESTROY = 0xc1, + RENAME = 0xc2, + SET_FILE_INFO = 0xc3, + GET_FILE_INFO = 0xc4, + ONLINE = 0xc5, + SET_PREFIX = 0xc6, + GET_PREFIX = 0xc7, + OPEN = 0xc8, + NEWLINE = 0xc9, + READ = 0xca, + WRITE = 0xcb, + CLOSE = 0xcc, + FLUSH = 0xcd, + SET_MARK = 0xce, + GET_MARK = 0xcf, + SET_EOF = 0xd0, + GET_EOF = 0xd1, + SET_BUF = 0xd2, + GET_BUF = 0xd3, + ALLOC_INTERRUPT = 0x40, + DEALLOC_INTERRUPT = 0x41, + ATINIT = 0x99, + READ_BLOCK = 0x80, + WRITE_BLOCK = 0x81, + GET_TIME = 0x82, + QUIT = 0x65 +}; + +#if 0 +enum { + noError = 0x0000, + invalidCallNum = 0x01, + invalidPcount = 0x04, + intTableFull = 0x25, + ioError = 0x27, + noDevConnect = 0x28, + writeProtectErr = 0x2B, + diskSwitchErr = 0x2E, + badPathname = 0x40, + fcbFullErr = 0x42, + badFileRefNum = 0x43, + pathNotFound = 0x44, + volumeNotFound = 0x45, + fileNotFound = 0x46, + dupFileName = 0x47, + volumeFullErr = 0x48, + dirFullErr = 0x49, + versionErr = 0x4A, + badStoreType = 0x4B, + eofEncountered = 0x4C, + positionRangeErr = 0x4D, + accessErr = 0x4E, + fileOpenErr = 0x50, + dirDamaged = 0x51, + badVolType = 0x52, + paramRangeErr = 0x53, + vcbFullErr = 0x55, + badBufferAddress = 0x56, + dupVolumeErr = 0x57, + blkNumRangeErr = 0x5A +}; +#endif +#define badBufferAddress 0x56 + + +struct file_entry { + unsigned type; + unsigned translate; + int fd; + unsigned buffer; + unsigned level; + unsigned newline_mask; + unsigned newline_char; + + /* directory stuff */ + unsigned mark; + unsigned eof; + unsigned char *directory_buffer; +}; + +#define MAX_FILES 8 +#define MIN_FILE 0x40 +#define MAX_FILE 0x48 +struct file_entry files[MAX_FILES]; + + +static char *get_pstr(word32 ptr) { + if (!ptr) return NULL; + int length = get_memory_c(ptr, 0); + ptr += 1; + char *str = host_gc_malloc(length + 1); + for (int i = 0; i < length; ++i) { + char c = get_memory_c(ptr+i, 0) & 0x7f; + str[i] = c; + } + str[length] = 0; + return str; +} + +static word32 set_pstr(word32 ptr, const char *str) { + + if (!ptr) return paramRangeErr; + + int l = str ? strlen(str) : 0; + + 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; +} + + + +static char *is_host_path(unsigned pathname) { + /* add + 5 below to skip past the /HOST/ part */ + /* (prefix may be missing trailing / ) */ + char *p = get_pstr(pathname); + if (!p) return NULL; + if (*p == '/') { + if (!strncasecmp(p, "/HOST", 5)) { + if (p[5] == 0) return "/"; + if (p[5] == '/') return p + 5; + } + return NULL; + } + + if (saved_prefix) { + return host_gc_append_path(saved_prefix + 5, p); + } + return NULL; +} + + + +static int scandir_sort(const struct dirent **a, const struct dirent **b) { + return strcasecmp((**a).d_name, (**b).d_name); +} + +static int scandir_filter(const struct dirent *d) { + int i; + const char *name = d->d_name; + + if (name[0] == '.') return 0; + for (i = 0; ; ++i) { + unsigned char c = name[i]; + if (c & 0x80) return 0; + if (c == 0) break; + } + if (i > 15) return 0; + return 1; +} + +static int count_directory_entries(const char *path) { + DIR *dir; + int rv; + + dir = opendir(path); + if (!dir) return 0; + + for(rv = 0;;) { + struct dirent *dp = readdir(dir); + if (!dp) break; + if (!scandir_filter(dp)) continue; + ++rv; + } + + closedir(dir); + return rv; +} + + + +static unsigned lowercase_bits(const char *name) { + unsigned rv = 0x8000; + unsigned bit = 0x4000; + for(unsigned i = 0; i < 15; ++i, bit >>= 1) { + char c = name[i]; + if (c == 0) break; + if (islower(c)) rv |= bit; + } + return rv; +} + +/* name is relative to the host directory. */ + +void *create_directory_file(const char *name, const char *path, unsigned *error_ptr, unsigned *block_ptr) { + + + byte *data; + int capacity = 0; + int count = 0; + unsigned offset = 0; + int i; + struct file_info fi; + unsigned blocks = 1; + unsigned terr; + + word32 w32; + + struct dirent **entries = NULL; + int entry_count = 0; + entry_count = scandir(path, &entries, scandir_filter, scandir_sort); + if (entry_count < 0) { + *error_ptr = host_map_errno_path(errno, path); + goto exit; + } + + + /* also need space for volume/directory header */ + capacity = 1 + (1 + entry_count) / ENTRIES_PER_BLOCK; + capacity *= 512; + + data = malloc(capacity); + if (!data) { + *error_ptr = outOfMem; + goto exit; + } + memset(data, 0, capacity); + + + terr = host_get_file_info(path, &fi); + if (terr) { + *error_ptr = terr; + goto exit; + } + + /* TODO -- this is wrong. */ + /* trailing /s should be stripped */ + const char *base_name = strchr(name, '/'); + base_name = base_name ? base_name + 1 : name; + if (!base_name || !*base_name) base_name = "HOST"; + + int len = strlen(base_name); + if (len > 15) len = 15; + /* previous / next pointers */ + data[0] = 0; + data[1] = 0; + data[2] = 0; + data[3] = 0; + offset = 4; + + int root = fi.storage_type == 0x0f; + if (root) { + data[offset++] = 0xf0 | len; + } else { + data[offset++] = 0xe0 | len; + } + for (i = 0; i < len; ++i) { + data[offset++] = toupper(base_name[i]); + } + for(; i < 15; ++i) { data[offset++] = 0; } + if (root) { + data[offset++] = 0; /* reserved */ + data[offset++] = 0; + + w32 = host_convert_date_time(fi.modified_date); + + data[offset++] = (w32 >> 0) & 0xff; /* last modified... */ + data[offset++] = (w32 >> 8) & 0xff; + data[offset++] = (w32 >> 16) & 0xff; + data[offset++] = (w32 >> 24) & 0xff; + + w32 = lowercase_bits(base_name); + data[offset++] = (w32 >> 0) & 0xff; /* file name case bits */ + data[offset++] = (w32 >> 8) & 0xff; + + + } else { + data[offset++] = 0x75; /* password enabled */ + for (i = 0; i < 7; ++i) data[offset++] = 0; /* password */ + } + + w32 = host_convert_date_time(fi.create_date); + + data[offset++] = (w32 >> 0) & 0xff; /* creation... */ + data[offset++] = (w32 >> 8) & 0xff; + data[offset++] = (w32 >> 16) & 0xff; + data[offset++] = (w32 >> 24) & 0xff; + + data[offset++] = 0; /* version */ + data[offset++] = 0; /* min version */ + data[offset++] = fi.access; /* access */ + data[offset++] = ENTRY_LENGTH; /* entry length */ + data[offset++] = ENTRIES_PER_BLOCK; /* entries per block */ + + data[offset++] = 0; /* file count (placeholder) */ + data[offset++] = 0; + + data[offset++] = 0; /* bitmap ptr, parent ptr, etc */ + data[offset++] = 0; + data[offset++] = 0; + data[offset++] = 0; + + + + int path_len = strlen(path); + + count = 0; + blocks = 1; + for (int j = 0; j < entry_count; ++j) { + + char *name = entries[j]->d_name; + int len = strlen(name); + char *tmp = malloc(path_len + 2 + len); + if (!tmp) continue; + + memcpy(tmp, path, path_len); + tmp[path_len] = '/'; + strcpy(tmp + path_len + 1, name); + + unsigned terr = host_get_file_info(tmp, &fi); + free(tmp); + if (terr) continue; + + /* wrap to next block ? */ + if ((offset + ENTRY_LENGTH) >> 9 == blocks) { + blocks++; + offset = (offset + 511) & ~511; + data[offset++] = 0; /* next/previous block pointers */ + data[offset++] = 0; + data[offset++] = 0; + data[offset++] = 0; + } + + assert(offset + ENTRY_LENGTH < capacity); + + /* create a prodos entry... */ + + + if (fi.storage_type == extendedFile) + fi.storage_type = 1; + int st = fi.storage_type << 4; + + data[offset++] = st | len; + for(i = 0; i < len; ++i) { + data[offset++] = toupper(name[i]); + } + for(; i < 15; ++i) data[offset++] = 0; + data[offset++] = fi.file_type; + data[offset++] = 0; /* key ptr */ + data[offset++] = 0; /* key ptr */ + data[offset++] = fi.blocks & 0xff; + data[offset++] = (fi.blocks >> 8) & 0xff; + data[offset++] = fi.eof & 0xff; + data[offset++] = (fi.eof >> 8) & 0xff; + data[offset++] = (fi.eof >> 16) & 0xff; + + w32 = host_convert_date_time(fi.create_date); + + data[offset++] = (w32 >> 0) & 0xff; /* creation... */ + data[offset++] = (w32 >> 8) & 0xff; + data[offset++] = (w32 >> 16) & 0xff; + data[offset++] = (w32 >> 24) & 0xff; + + w32 = lowercase_bits(name); + data[offset++] = (w32 >> 0) & 0xff; /* file name case bits */ + data[offset++] = (w32 >> 8) & 0xff; + + data[offset++] = fi.access; + data[offset++] = fi.aux_type & 0xff; + data[offset++] = (fi.aux_type >> 8) & 0xff; + + w32 = host_convert_date_time(fi.modified_date); + data[offset++] = (w32 >> 0) & 0xff; /* last mod... */ + data[offset++] = (w32 >> 8) & 0xff; + data[offset++] = (w32 >> 16) & 0xff; + data[offset++] = (w32 >> 24) & 0xff; + + data[offset++] = 0; /* header ptr */ + data[offset++] = 0; + count++; + } + + data[4 + 0x21] = count & 0xff; + data[4 + 0x22] = (count >> 8)& 0xff; + *block_ptr = blocks; + +exit: + if (entries) { + for (i = 0; i < entry_count; ++i) { + free(entries[i]); + } + free(entries); + } + return data; +} + + + +static int mli_atinit(unsigned dcb) { + unsigned pcount = get_memory_c(dcb, 0); + + if (pcount < 3|| pcount > 4) return invalidPcount; + + unsigned version = get_memory_c(dcb + 1, 0); + saved_mli_address = get_memory16_c(dcb + 2, 0); + saved_patch_address = get_memory16_c(dcb + 4, 0); + if (!saved_patch_address) return paramRangeErr; + if (!saved_mli_address) return paramRangeErr; + + saved_unit = 0x80; + if (pcount >= 4) saved_unit = get_memory16_c(dcb + 6, 0); + + if (!saved_unit) return paramRangeErr; + + saved_prefix = 0; + + memset(files, 0, sizeof(files)); /* ??? */ + + return host_startup(); +} + + +enum { + FILE_INFO_pcount = 0, + FILE_INFO_pathname = 1, + FILE_INFO_access = 3, + FILE_INFO_file_type = 4, + FILE_INFO_aux_type = 5, + FILE_INFO_storage_type = 7, + FILE_INFO_blocks = 8, + FILE_INFO_mod_date = 10, + FILE_INFO_mod_time = 12, + FILE_INFO_create_date = 14, + FILE_INFO_create_time = 16 +}; + +static int mli_set_file_info(unsigned dcb, const char *path) { + + + struct file_info fi; + memset(&fi, 0, sizeof(fi)); + + fi.access = get_memory_c(dcb + FILE_INFO_access, 0); + fi.file_type = get_memory_c(dcb + FILE_INFO_file_type, 0); + fi.aux_type = get_memory16_c(dcb + FILE_INFO_aux_type, 0); + fi.modified_date = host_get_date_time(dcb + FILE_INFO_mod_date); + + return host_set_file_info(path, &fi); +} + +static int mli_get_file_info(unsigned dcb, const char *path) { + + struct file_info fi; + int rv = host_get_file_info(path, &fi); + if (rv) return rv; + + /* ignore resource fork */ + if (fi.storage_type == extendedFile) + fi.storage_type = seedling; + + set_memory_c(dcb + FILE_INFO_access, fi.access, 0); + set_memory_c(dcb + FILE_INFO_file_type, fi.file_type, 0); + set_memory16_c(dcb + FILE_INFO_aux_type, fi.aux_type, 0); + set_memory_c(dcb + FILE_INFO_storage_type, fi.storage_type, 0); + set_memory16_c(dcb + FILE_INFO_blocks, fi.blocks, 0); + host_set_date_time(dcb + FILE_INFO_mod_date, fi.modified_date); + host_set_date_time(dcb + FILE_INFO_create_date, fi.create_date); + + return 0; +} + +enum { + CREATE_pcount = 0, + CREATE_pathname = 1, + CREATE_access = 3, + CREATE_file_type = 4, + CREATE_aux_type = 5, + CREATE_storage_type = 7, + CREATE_create_date = 8, + CREATE_create_time = 10 +}; + +static int mli_create(unsigned dcb, const char *path) { + + struct file_info fi; + memset(&fi, 0, sizeof(fi)); + + fi.access = get_memory_c(dcb + CREATE_access, 0); + fi.file_type = get_memory_c(dcb + CREATE_file_type, 0); + fi.aux_type = get_memory16_c(dcb + CREATE_aux_type, 0); + fi.storage_type = get_memory_c(dcb + CREATE_storage_type, 0); + fi.create_date = host_get_date_time(dcb + CREATE_create_date); + + if (g_cfg_host_read_only) return drvrWrtProt; + + /* + * actual prodos needs the correct storage type and file type + * for a usable directory. this just does the right thing. + * TODO - does ProDOS update the storage type in the dcb? + */ + switch(fi.storage_type) { + case 0x0d: + fi.file_type = 0x0f; + break; + case 0x01: + case 0x02: + case 0x03: + fi.storage_type = 0x01; + break; + case 0x00: + if (fi.file_type == 0x0f) fi.storage_type = 0x0d; + else fi.storage_type = 0x01; + break; + default: + return badStoreType; + } + // todo -- remap access. + + if (fi.storage_type == 0x0d) { + int ok = mkdir(path, 0777); + if (ok < 0) + return host_map_errno_path(errno, path); + } else { + int fd = open(path, O_CREAT | O_EXCL | O_RDONLY, 0666); + if (fd < 0) + return host_map_errno_path(errno, path); + + host_file_type_to_finder_info(fi.finder_info, fi.file_type, fi.aux_type); + fi.has_fi = 1; + host_set_file_info(path, &fi); + close(fd); + } + + return 0; +} + +static int mli_destroy(unsigned dcb, const char *path) { + /* can't delete the root directory */ + struct stat st; + if (stat(path, &st) < 0) { + return host_map_errno_path(errno, path); + } + + if (host_is_root(&st)) + return badPathSyntax; + + int ok = S_ISDIR(st.st_mode) ? rmdir(path) : unlink(path); + + if (ok < 0) return host_map_errno(errno); + return 0; +} + +static int mli_rename(unsigned dcb, const char *path1, const char *path2) { + /* can't rename the root directory */ + struct stat st; + + if (!path1 || !path2) return badPathSyntax; + + if (stat(path1, &st) < 0) { + return host_map_errno_path(errno, path1); + } + + if (host_is_root(&st)) + return badPathSyntax; + + + if (stat(path2, &st) == 0) { + return dupPathname; + } + if (host_is_root(&st)) + return badPathSyntax; + + int ok = rename(path1, path2); + if (ok < 0) return host_map_errno(errno); + return 0; +} + + +static int mli_write(unsigned dcb, struct file_entry *file) { + + /* todo -- translate */ + + set_memory16_c(dcb + 6, 0, 0); + + if (!file->type) return invalidRefNum; + if (file->type != file_regular) return invalidAccess; + + unsigned data_buffer = get_memory16_c(dcb + 2, 0); + unsigned request_count = get_memory16_c(dcb + 4, 0); + + if (!data_buffer) return badBufferAddress; + + byte *data = host_gc_malloc(request_count); + if (!data) return drvrIOError; + + for (unsigned i = 0; i < request_count; ++i) { + data[i] = get_memory_c(data_buffer + i, 0); + } + + switch (file->translate) { + case translate_crlf: + host_cr_to_lf(data, request_count); + break; + case translate_merlin: + host_merlin_to_text(data, request_count); + break; + } + + int rv = pwrite(file->fd, data, request_count, file->mark); + if (rv < 0) return host_map_errno(errno); + + set_memory16_c(dcb + 6, rv, 0); + file->mark += rv; + + return 0; +} + + +static int mli_read(unsigned dcb, struct file_entry *file) { + + /* todo -- translate */ + + set_memory16_c(dcb + 6, 0, 0); + + if (!file->type) return invalidRefNum; + + unsigned data_buffer = get_memory16_c(dcb + 2, 0); + unsigned request_count = get_memory16_c(dcb + 4, 0); + + if (!data_buffer) return badBufferAddress; + + unsigned count = 0; + unsigned mask = file->newline_mask; + unsigned nl = file->newline_char; + + if (file->type == file_directory) { + unsigned mark = file->mark; + if (mark >= file->eof) return eofEncountered; + /* support newline mode ... */ + + if (mark + request_count > file->eof) + request_count = file->eof - mark; + + + for (count = 0; count < request_count; ++count) { + byte b = file->directory_buffer[mark++]; + set_memory_c(data_buffer + count, b, 0); + if (mask && (b & mask) == nl) break; + } + file->mark = mark; + set_memory16_c(dcb + 6, count, 0); + return 0; + } + + if (file->type != file_regular) return invalidAccess; + + byte *data = host_gc_malloc(request_count); + if (!data) return drvrIOError; + + int rv = pread(file->fd, data, request_count, file->mark); + if (rv < 0) return host_map_errno(errno); + if (rv == 0) return eofEncountered; + count = rv; + + switch (file->translate) { + case translate_crlf: + host_lf_to_cr(data, count); + break; + case translate_merlin: + host_text_to_merlin(data, count); + break; + } + + + if (mask) { + for (int count = 0; count < rv; ) { + byte b = data[count++]; + if ((b & mask) == nl) break; + } + } + for (unsigned i = 0; i < count; ++i) + set_memory_c(data_buffer + i, data[i], 0); + + file->mark += count; + set_memory16_c(dcb + 6, count, 0); + return 0; +} + + + +static int mli_get_buf(unsigned dcb, struct file_entry *file) { + + if (!file->type) return invalidRefNum; + set_memory16_c(dcb + 2, file->buffer, 0); + return 0; +} + +static int mli_set_buf(unsigned dcb, struct file_entry *file) { + + unsigned buffer = get_memory16_c(dcb + 2, 0); + if (!file->type) return invalidRefNum; + //if (!buffer) return badBufferAddress; + if (buffer & 0xff) return badBufferAddress; + + file->buffer = buffer; + return 0; +} + +static int mli_get_eof(unsigned dcb, struct file_entry *file) { + + off_t eof = 0; + + switch (file->type) { + default: + return invalidRefNum; + + case file_directory: + eof = file->eof; + break; + + case file_regular: + eof = lseek(file->fd, SEEK_END, 0); + if (eof < 0) return host_map_errno(errno); + break; + } + if (eof > 0xffffff) return outOfRange; + set_memory24_c(dcb + 2, eof, 0); + return 0; +} + + +static int mli_set_eof(unsigned dcb, struct file_entry *file) { + + off_t eof = get_memory24_c(dcb + 2, 0); + + switch (file->type) { + default: + return invalidRefNum; + + case file_directory: + return invalidAccess; + break; + + case file_regular: + if (ftruncate(file->fd, eof) < 0) + return host_map_errno(errno); + break; + } + return 0; +} + +static int mli_get_mark(unsigned dcb, struct file_entry *file) { + + off_t position = 0; + + switch (file->type) { + default: + return invalidRefNum; + + case file_directory: + case file_regular: + position = file->mark; + break; + } + if (position > 0xffffff) return outOfRange; + set_memory24_c(dcb + 2, position, 0); + return 0; + +} + +static int mli_set_mark(unsigned dcb, struct file_entry *file) { + + off_t eof = 0; + word32 position = get_memory24_c(dcb + 2, 0); + + switch (file->type) { + default: + return invalidRefNum; + case file_directory: + eof = file->eof; + break; + case file_regular: + eof = lseek(file->fd, SEEK_END, 0); + if (eof < 0) return host_map_errno(errno); + break; + } + + if (position > eof) return outOfRange; + file->mark = position; + + return 0; +} + +static int mli_newline(unsigned dcb, struct file_entry *file) { + if (!file->type) return invalidRefNum; + + file->newline_mask = get_memory_c(dcb + 2, 0); + file->newline_char = get_memory_c(dcb + 3, 0); + return 0; +} + +static int mli_close(unsigned dcb, struct file_entry *file) { + + if (!file) { + unsigned level = get_memory_c(LEVEL, 0); + unsigned i; + for (i = 0; i < MAX_FILES; ++i) { + file = &files[i]; + if (!file->type) continue; + if (file->level < level) continue; + mli_close(dcb, file); + } + return -1; /* pass to prodos mli */ + } + + if (!file->type) return invalidRefNum; + if (file->fd >= 0) close(file->fd); + if (file->directory_buffer) free(file->directory_buffer); + memset(file, 0, sizeof(*file)); + file->fd = -1; + + return 0; +} + + +static int mli_flush(unsigned dcb, struct file_entry *file) { + + if (!file) { + unsigned level = get_memory_c(LEVEL, 0); + unsigned i; + for (i = 0; i < MAX_FILES; ++i) { + file = &files[i]; + if (!file->type) continue; + if (file->level < level) continue; + + if (file->fd >= 0) + fsync(file->fd); + } + return -1; /* pass to prodos mli */ + } + + if (!file->type) return invalidRefNum; + if (file->fd >= 0) fsync(file->fd); + + return 0; +} + + +/* + * quit + * close all open files. + */ +static int mli_quit(unsigned dcb) { + unsigned i; + for (i = 0; i < MAX_FILES; ++i) { + struct file_entry *file = &files[i]; + if (!file->type) continue; + if (file->fd >= 0) close(file->fd); + if (file->directory_buffer) free(file->directory_buffer); + memset(file, 0, sizeof(*file)); + file->fd = -1; + } + /* need a better way to know... */ + /* host_shutdown(); */ + return -1; +} + + +static int mli_open(unsigned dcb, const char *name, const char *path) { + + struct stat st; + + int fd; + int refnum = 0; + unsigned type; + + struct file_entry *file = NULL; + for (unsigned i = 0; i < MAX_FILES; ++i) { + file = &files[i]; + if (!file->type) { + refnum = MIN_FILE + i; + break; + } + } + if (!refnum) return tooManyFilesOpen; + + unsigned buffer = get_memory_c(dcb + 3, 0); + //if (buffer == 0) return badBufferAddress; + if (buffer & 0xff) return badBufferAddress; + + + if (g_cfg_host_read_only) { + fd = open(path, O_RDONLY | O_NONBLOCK); + } else { + fd = open(path, O_RDWR | O_NONBLOCK); + if (fd < 0) { + if (errno == EACCES || errno == EISDIR) + fd = open(path, O_RDONLY | O_NONBLOCK); + } + } + + if (fd < 0) { + return host_map_errno_path(errno, path); + } + + struct file_info fi; + unsigned terr = host_get_file_info(path, &fi); + if (terr) { + close(fd); + return terr; + } + + + type = 0; + if (S_ISDIR(fi.st_mode)) { + unsigned blocks; + unsigned error; + void *tmp; + type = file_directory; + tmp = create_directory_file(name, path, &error, &blocks); + close(fd); + fd = -1; + if (!tmp) return error; + file->directory_buffer = tmp; + file->eof = blocks * 512; + } else if (S_ISREG(fi.st_mode)) { + + type = file_regular; + + if (g_cfg_host_crlf) { + if (fi.file_type == 0x04 || fi.file_type == 0xb0) + file->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] == '.') + file->translate = translate_merlin; + } + + } else { + close(fd); + return badStoreType; + } + + file->type = type; + file->level = get_memory_c(LEVEL, 0); + file->buffer = buffer; + file->fd = fd; + file->type = type; + file->mark = 0; + set_memory_c(dcb + 5, refnum, 0); + return 0; +} + + +static int mli_get_prefix(unsigned dcb) { + if (!saved_prefix) return -1; + unsigned buffer = get_memory16_c(dcb + 1, 0); + if (!buffer) return badBufferAddress; + + set_pstr(buffer, saved_prefix); + return 0; +} + +/* + * name is relative the the /HOST device. + * path is the path on the native fs. + */ +static int mli_set_prefix(unsigned dcb, char *name, char *path) { + /* + * IF path is null, tail patch if we own the prefix. + * otherwise, handle it or forward to mli. + */ + if (!path) { + return saved_prefix ? -2 : -1; + } + + int l; + struct stat st; + + if (stat(path, &st) < 0) + return host_map_errno_path(errno, path); + + if (!S_ISDIR(st.st_mode)) { + return badStoreType; + } + + + /* /HOST/ was previously stripped... add it back. */ + name = host_gc_append_path("/HOST", name); + + l = strlen(name); + /* trim trailing / */ + while (l > 1 && name[l-1] == '/') --l; + name[l] = 0; + + if (l > 63) return badPathSyntax; + + + if (saved_prefix) + free(saved_prefix); + saved_prefix = strdup(name); + set_memory_c(PFIXPTR, 1, 0); + return 0; +} + +static int mli_online(unsigned dcb) { + /* not yet ... */ + unsigned unit = get_memory_c(dcb + 1, 0); + unsigned buffer = get_memory16_c(dcb + 2, 0); + if (unit == saved_unit) { + if (!buffer) return badBufferAddress; + /* slot 2, drive 1 ... why not*/ + set_memory_c(buffer++, saved_unit | 0x04, 0); + set_memory_c(buffer++, 'H', 0); + set_memory_c(buffer++, 'O', 0); + set_memory_c(buffer++, 'S', 0); + set_memory_c(buffer++, 'T', 0); + return 0; + } + if (unit == 0) return -2; + return -1; +} + +static int mli_online_tail(unsigned dcb) { + unsigned buffer = get_memory16_c(dcb + 2, 0); + + host_hexdump(buffer, 256); + + /* if there was an error with the device + there will be an error code instead of a name (length = 0) + */ + + for (unsigned i = 0; i < 16; ++i, buffer += 16) { + unsigned x = get_memory_c(buffer, 0); + if (x == 0 || ((x & 0xf0) == saved_unit)) { + set_memory_c(buffer++, saved_unit | 0x04, 0); + set_memory_c(buffer++, 'H', 0); + set_memory_c(buffer++, 'O', 0); + set_memory_c(buffer++, 'S', 0); + set_memory_c(buffer++, 'T', 0); + break; + } + } + + return 0; +} + +static int mli_rw_block(unsigned dcb) { + unsigned unit = get_memory_c(dcb + 1, 0); + unsigned buffer = get_memory16_c(dcb + 2, 0); + + if (unit == saved_unit) { + if (!buffer) return badBufferAddress; + return notBlockDev; /* network error? */ + } + return -1; +} + + +static unsigned call_pcount(unsigned call) { + switch (call) { + case CREATE: return 7; + case DESTROY: return 1; + case RENAME: return 2; + case SET_FILE_INFO: return 7; + case GET_FILE_INFO: return 10; + case ONLINE: return 2; + case SET_PREFIX: return 1; + case GET_PREFIX: return 1; + case OPEN: return 3; + case NEWLINE: return 3; + case READ: return 4; + case WRITE: return 4; + case CLOSE: return 1; + case FLUSH: return 1; + case SET_MARK: return 2; + case GET_MARK: return 2; + case SET_EOF: return 2; + case GET_EOF: return 2; + case SET_BUF: return 2; + case GET_BUF: return 2; + + default: return 0; + } +} + + +static const char *call_name(unsigned call) { + + switch (call) { + case CREATE: return "CREATE"; + case DESTROY: return "DESTROY"; + case RENAME: return "RENAME"; + case SET_FILE_INFO: return "SET_FILE_INFO"; + case GET_FILE_INFO: return "GET_FILE_INFO"; + case ONLINE: return "ONLINE"; + case SET_PREFIX: return "SET_PREFIX"; + case GET_PREFIX: return "GET_PREFIX"; + case OPEN: return "OPEN"; + case NEWLINE: return "NEWLINE"; + case READ: return "READ"; + case WRITE: return "WRITE"; + case CLOSE: return "CLOSE"; + case FLUSH: return "FLUSH"; + case SET_MARK: return "SET_MARK"; + case GET_MARK: return "GET_MARK"; + case SET_EOF: return "SET_EOF"; + case GET_EOF: return "GET_EOF"; + case SET_BUF: return "SET_BUF"; + case GET_BUF: return "GET_BUF"; + case ATINIT: return "ATINIT"; + + default: return ""; + } +} + +/* + * mli head patch. called before ProDOS mli. + * this call will either + * 1. handle and return + * 2. redirect to the real mli + * 3. call real mli, then call host_mli_tail after + */ +void host_mli_head() { + + word32 rts = get_memory16_c(engine.stack+1, 0); + saved_call = get_memory_c(rts + 1, 0); + saved_dcb = get_memory16_c(rts + 2, 0); + saved_p = engine.psr; + + /* do pcount / path stuff here */ + char *path1 = NULL; + char *path2 = NULL; + char *path3 = NULL; + char *path4 = NULL; + + struct file_entry *file = NULL; + + unsigned pcount = get_memory_c(saved_dcb, 0); + unsigned xpcount = call_pcount(saved_call); + int acc = 0; + int refnum = 0; + + if (xpcount && xpcount != pcount) { + acc = invalidPcount; + goto cleanup; + } + + switch(saved_call) { + case CREATE: + case DESTROY: + case SET_FILE_INFO: + case GET_FILE_INFO: + case OPEN: + path1 = is_host_path(get_memory16_c(saved_dcb + 1, 0)); + if (!path1) goto prodos_mli; + path3 = host_gc_append_path(host_root, path1); + break; + case RENAME: + path1 = is_host_path(get_memory16_c(saved_dcb + 1,0)); + path2 = is_host_path(get_memory16_c(saved_dcb + 3,0)); + if (!path1 && !path2) goto prodos_mli; + if (path1) path3 = host_gc_append_path(host_root, path1); + if (path2) path4 = host_gc_append_path(host_root, path2); + break; + + case SET_PREFIX: + path1 = is_host_path(get_memory16_c(saved_dcb + 1,0)); + if (!path1 && !saved_prefix) goto prodos_mli; + if (path1) path3 = host_gc_append_path(host_root, path1); + break; + + + case GET_PREFIX: + if (!saved_prefix) goto prodos_mli; + break; + + + /* refnum based */ + case NEWLINE: + case READ: + case WRITE: + case SET_MARK: + case GET_MARK: + case SET_EOF: + case GET_EOF: + case SET_BUF: + case GET_BUF: + refnum = get_memory_c(saved_dcb + 1, 0); + + if (refnum >= MIN_FILE && refnum < MAX_FILE) { + file = &files[refnum - MIN_FILE]; + } else { + goto prodos_mli; + } + break; + + case CLOSE: + case FLUSH: + /* special case for refnum == 0 */ + refnum = get_memory_c(saved_dcb + 1, 0); + + if (refnum >= MIN_FILE && refnum < MAX_FILE) { + file = &files[refnum - MIN_FILE]; + } else if (refnum) { + goto prodos_mli; + } + + break; + + } + + fprintf(stderr, "MLI: %02x %s", saved_call, call_name(saved_call)); + if (path1) fprintf(stderr, " - %s", path1); + if (path2) fprintf(stderr, " - %s", path2); + + switch (saved_call) { + default: + engine.kpc = saved_mli_address; + return; + + case ATINIT: + acc = mli_atinit(saved_dcb); + break; + + case CREATE: + acc = mli_create(saved_dcb, path3); + break; + + case DESTROY: + acc = mli_destroy(saved_dcb, path3); + break; + + case SET_FILE_INFO: + acc = mli_set_file_info(saved_dcb, path3); + break; + + case GET_FILE_INFO: + acc = mli_get_file_info(saved_dcb, path3); + break; + + case OPEN: + acc = mli_open(saved_dcb, path1, path3); + break; + + case RENAME: + acc = mli_rename(saved_dcb, path3, path4); + break; + + case SET_PREFIX: + acc = mli_set_prefix(saved_dcb, path1, path3); + break; + + case GET_PREFIX: + acc = mli_get_prefix(saved_dcb); + break; + + case ONLINE: + acc = mli_online(saved_dcb); + break; + + case NEWLINE: + acc = mli_newline(saved_dcb, file); + break; + + case READ: + acc = mli_read(saved_dcb, file); + break; + + case WRITE: + acc = mli_write(saved_dcb, file); + break; + + case SET_MARK: + acc = mli_set_mark(saved_dcb, file); + break; + + case GET_MARK: + acc = mli_get_mark(saved_dcb, file); + break; + + case SET_EOF: + acc = mli_set_eof(saved_dcb, file); + break; + + case GET_EOF: + acc = mli_get_eof(saved_dcb, file); + break; + + case SET_BUF: + acc = mli_set_buf(saved_dcb, file); + break; + + case GET_BUF: + acc = mli_get_buf(saved_dcb, file); + break; + + case CLOSE: + acc = mli_close(saved_dcb, file); + break; + + case FLUSH: + acc = mli_flush(saved_dcb, file); + break; + + case WRITE_BLOCK: + case READ_BLOCK: + acc = mli_rw_block(saved_dcb); + break; + + + case QUIT: + acc = mli_quit(saved_dcb); + break; + + } + fputs("\n", stderr); + host_gc_free(); + + + if (acc == -2) { + /* tail call needed */ + /* + jsr xxxx + dc.b xx + dc.w xxxx + wdm .. + rts + */ + SEI(); + set_memory_c(saved_patch_address + 0, saved_call, 0); + set_memory16_c(saved_patch_address + 1, saved_dcb, 0); + set_memory16_c(engine.stack+1, rts + 3, 0); + return; + } + if (acc < 0) { +prodos_mli: + host_gc_free(); + /* pass to normal dispatcher */ + engine.kpc = saved_mli_address; + return; + } +cleanup: + /* fixup */ + acc &= 0xff; + + if (acc) fprintf(stderr, " %02x %s\n", acc, host_error_name(acc)); + if (acc == 0 && saved_call != ATINIT) { + set_memory_c(DEVNUM, saved_unit, 0); + } + engine.acc &= 0xff00; + engine.acc |= acc; + if (acc) { + SEC(); + CLZ(); + } else { + CLC(); + SEZ(); + } + engine.kpc = rts + 4; + engine.stack += 2; + return; + +} + +void host_mli_tail() { + + if (!(engine.psr & C)) { + + switch(saved_call) { + case SET_PREFIX: + free(saved_prefix); + saved_prefix = NULL; + break; + case ONLINE: + mli_online_tail(saved_dcb); + break; + } + } + + // clean up I bit (set in head) + engine.psr &= ~I; + engine.psr |= (saved_p & I); + host_gc_free(); +} \ No newline at end of file diff --git a/src/moremem.c b/src/moremem.c index 2f0209d..47a7cbc 100644 --- a/src/moremem.c +++ b/src/moremem.c @@ -207,6 +207,10 @@ __attribute__ ((aligned(256))) ; +/* SLOT 7 DEVSEL RAM (used by HOST.MLI) */ +static byte slot7_devsel[15] = { 0 }; + + // OG Added moremem_init() void moremem_init() { g_em_emubyte_cnt = 0; @@ -1637,7 +1641,7 @@ int io_read(word32 loc, double *cyc_ptr) { case 0xf4: case 0xf5: case 0xf6: case 0xf7: case 0xf8: case 0xf9: case 0xfa: case 0xfb: case 0xfc: case 0xfd: case 0xfe: case 0xff: - return 0; + return slot7_devsel[loc & 0x0f] /* 0 */; default: printf("loc: %04x bad\n", loc); @@ -2378,7 +2382,9 @@ void io_write(word32 loc, int val, double *cyc_ptr) { case 0xf4: case 0xf5: case 0xf6: case 0xf7: case 0xf8: case 0xf9: case 0xfa: case 0xfb: case 0xfc: case 0xfd: case 0xfe: case 0xff: - UNIMPL_WRITE; + slot7_devsel[loc & 0x0f] = val; /* UNIMPL_WRITE; */ + return; + default: printf("WRite loc: %x\n",loc); exit(-300); diff --git a/src/sim65816.c b/src/sim65816.c index 2175c97..3abd33e 100644 --- a/src/sim65816.c +++ b/src/sim65816.c @@ -2510,6 +2510,8 @@ static void wdm_hexdump() { } } } +extern void host_mli_head(void); +extern void host_mli_tail(void); void do_wdm(word32 arg) @@ -2532,6 +2534,14 @@ do_wdm(word32 arg) /* 0xFx shouldn't be called unless you know what you're doing */ + case 0xfc: /* mli head patch */ + host_mli_head(); + break; + + case 0xfd: /* mli tail patch */ + host_mli_tail(); + break; + case 0xfe: /* used for ModemWorks branch */ break; diff --git a/src/win32_host_fst.c b/src/win32_host_fst.c index 07f0b49..5e947af 100644 --- a/src/win32_host_fst.c +++ b/src/win32_host_fst.c @@ -846,8 +846,8 @@ static char * get_gsstr(word32 ptr) { static char * get_pstr(word32 ptr) { if (!ptr) return NULL; - int length = get_memory16_c(ptr, 0); - ptr += 2; + int length = get_memory_c(ptr, 0); + ptr += 1; char *str = gc_malloc(length + 1); for (int i = 0; i < length; ++i) { char c = get_memory_c(ptr+i, 0);