#define _BSD_SOURCE #ifdef _WIN32 #include #else #include #include #include #include #include #endif #include #include #include #include #include "defc.h" #include "gsos.h" #include "fst.h" #include "host_common.h" #ifdef _MSC_VER #define strcasecmp stricmp #define strncasecmp strnicmp #endif #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 }; #define badBufferAddress 0x56 struct file_entry { unsigned type; unsigned translate; #ifdef _WIN32 HANDLE h; #else int fd; #endif 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]; #if _WIN32 static word16 file_open(const char *path, struct file_entry *file) { HANDLE h; if (g_cfg_host_read_only) { h = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); } else { h = CreateFile(path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (h == INVALID_HANDLE_VALUE) { h = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); } } if (h == INVALID_HANDLE_VALUE) return host_map_win32_error(GetLastError()); file->h = h; return 0; } static word16 file_close(struct file_entry *file) { if (!file->type) return invalidRefNum; if (file->h != INVALID_HANDLE_VALUE) CloseHandle(file->h); if (file->directory_buffer) free(file->directory_buffer); memset(file, 0, sizeof(*file)); file->h = INVALID_HANDLE_VALUE; return 0; } static word16 file_flush(struct file_entry *file) { if (!file->type) return invalidRefNum; if (file->h != INVALID_HANDLE_VALUE) FlushFileBuffers(file->h); return 0; } static word16 file_eof(struct file_entry *file) { LARGE_INTEGER li; if (!GetFileSizeEx(file->h, &li)) return host_map_win32_error(GetLastError()); if (li.QuadPart > 0xffffff) { file->eof = 0xffffff; return outOfRange; } file->eof = li.QuadPart; return 0; } #else static word16 file_open(const char *path, struct file_entry *file) { int fd; if (g_cfg_host_read_only) { fd = open(path, O_RDONLY | O_NONBLOCK); } else { fd = open(path, O_RDWR | O_NONBLOCK); if (fd < 0) { fd = open(path, O_RDONLY | O_NONBLOCK); } } if (fd < 0) return host_map_errno_path(errno, path); file->fd = fd; return 0; } static word16 file_close(struct file_entry *file) { 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 word16 file_flush(struct file_entry *file) { if (!file->type) return invalidRefNum; if (file->fd >= 0) fsync(file->fd); return 0; } static word16 file_eof(struct file_entry *file) { off_t eof; eof = lseek(file->fd, 0, SEEK_END); if (eof < 0) return host_map_errno(errno); if (eof > 0xffffff) { file->eof = 0xffffff; return outOfRange; } file->eof = eof; return 0; } #endif 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 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, word16 *error_ptr, unsigned *block_ptr) { byte *data = NULL; int capacity = 0; int count = 0; unsigned offset = 0; int i; struct file_info fi; unsigned blocks = 1; unsigned terr; word32 w32; char **entries = NULL; size_t entry_count = 0; terr = host_scan_directory(path, &entries, &entry_count, 1); if (terr) { *error_ptr = terr; return NULL; } /* 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; } /* trailing /s should already 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]; 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) host_free_directory(entries, entry_count); 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_memory_c(dcb + 6, 0) & 0xf0; 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) { #if _WIN32 if (!CreateDirectory(path, NULL)) return host_map_win32_error(GetLastError()); #else if (mkdir(path, 0777) < 0) return host_map_errno_path(errno, path); #endif } else { #if _WIN32 HANDLE h = CreateFile(path, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); if (h == INVALID_HANDLE_VALUE) return host_map_win32_error(GetLastError()); afp_init(&fi.afp, fi.file_type, fi.aux_type); fi.has_fi = 1; // set ftype, auxtype... host_set_file_info(path, &fi); CloseHandle(h); #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); #endif } return 0; } static int mli_destroy(unsigned dcb, const char *path) { word16 terr = 0; unsigned type = host_storage_type(path, &terr); if (type == 0) return terr; switch(type) { case 0: return terr; case 0x0f: return badPathSyntax; /* root directory */ case 0x0d: #if _WIN32 if (!RemoveDirectory(path)) return host_map_win32_error(GetLastError()); #else if (rmdir(path) < 0) return host_map_errno_path(errno, path); #endif break; case 1: case 2: case 3: case 5: #if _WIN32 if (!DeleteFile(path)) return host_map_win32_error(GetLastError()); #else if (unlink(path) < 0) return host_map_errno_path(errno, path); #endif default: return badStoreType; } return 0; } static int mli_rename(unsigned dcb, const char *path1, const char *path2) { /* can't rename the root directory */ word16 terr = 0; unsigned type; if (!path1 || !path2) return badPathSyntax; type = host_storage_type(path1, &terr); if (!type) return terr; if (type == 0x0f) return badPathSyntax; type = host_storage_type(path2, &terr); if (type) return dupPathname; #if _WIN32 if (!MoveFile(path1, path2)) return host_map_win32_error(GetLastError()); #else if (rename(path1, path2) < 0) return host_map_errno(errno); #endif 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; } #if _WIN32 DWORD rv; LARGE_INTEGER li; li.QuadPart = file->mark; if (!SetFilePointerEx(file->h, li, NULL, FILE_BEGIN)) return host_map_win32_error(GetLastError()); if (!WriteFile(file->h, data, request_count, &rv, NULL)) return host_map_win32_error(GetLastError()); #else int rv = pwrite(file->fd, data, request_count, file->mark); if (rv < 0) return host_map_errno(errno); #endif 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; #if _WIN32 LARGE_INTEGER li; DWORD rv; li.QuadPart = file->mark; if (!SetFilePointerEx(file->h, li, NULL, FILE_BEGIN)) return host_map_win32_error(GetLastError()); if (!ReadFile(file->h, data, request_count, &rv, NULL)) return host_map_win32_error(GetLastError()); #else int rv = pread(file->fd, data, request_count, file->mark); if (rv < 0) return host_map_errno(errno); #endif 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) { word16 terr = 0; switch (file->type) { default: return invalidRefNum; case file_directory: break; case file_regular: terr = file_eof(file); if (terr) return terr; break; } set_memory24_c(dcb + 2, file->eof, 0); return 0; } static int mli_set_eof(unsigned dcb, struct file_entry *file) { off_t eof = get_memory24_c(dcb + 2, 0); #if _WIN32 LARGE_INTEGER tmp; #endif switch (file->type) { default: return invalidRefNum; case file_directory: return invalidAccess; break; case file_regular: #if _WIN32 tmp.QuadPart = eof; if (!SetFilePointerEx(file->h, tmp, NULL, FILE_BEGIN)) return host_map_win32_error(GetLastError()); if (!SetEndOfFile(file->h)) return host_map_win32_error(GetLastError()); #else if (ftruncate(file->fd, eof) < 0) return host_map_errno(errno); #endif 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) { word16 terr = 0; word32 position = get_memory24_c(dcb + 2, 0); switch (file->type) { default: return invalidRefNum; case file_directory: break; case file_regular: terr = file_eof(file); if (terr) return terr; break; } if (position > file->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; file_close(file); } return -1; /* pass to prodos mli */ } return file_close(file); } 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; file_flush(file); } return -1; /* pass to prodos mli */ } return file_flush(file); } /* * 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 _WIN32 if (file->h != INVALID_HANDLE_VALUE) CloseHandle(file->h); #else if (file->fd >= 0) close(file->fd); #endif if (file->directory_buffer) free(file->directory_buffer); memset(file, 0, sizeof(*file)); #if _WIN32 file->h = INVALID_HANDLE_VALUE; #else file->fd = -1; #endif } /* need a better way to know... */ /* host_shutdown(); */ return -1; } static int mli_open(unsigned dcb, const char *name, const char *path) { int refnum = 0; 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; struct file_info fi; word16 terr = host_get_file_info(path, &fi); if (terr) return terr; #if _WIN32 file->h = INVALID_HANDLE_VALUE; #else file->fd = -1; #endif if (fi.storage_type == 0x0f || fi.storage_type == 0x0d) { unsigned blocks; void *tmp; tmp = create_directory_file(name, path, &terr, &blocks); if (!tmp) return terr; file->type = file_directory; file->directory_buffer = tmp; file->eof = blocks * 512; } else { terr = file_open(path, file); if (terr) return terr; file->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; } } file->level = get_memory_c(LEVEL, 0); file->buffer = buffer; 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; } unsigned type; word16 terr; type = host_storage_type(path, &terr); switch(type) { case 0x0f: case 0x0d: break; case 0: return terr; default: return badStoreType; } /* /HOST/ was previously stripped... add it back. */ name = host_gc_append_path("/HOST", name); int 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 ""; } } #ifdef __CYGWIN__ #include static char *expand_path(const char *path, word32 *error) { char buffer[PATH_MAX]; if (!path) return path; ssize_t ok = cygwin_conv_path(CCP_POSIX_TO_WIN_A, path, buffer, sizeof(buffer)); if (ok < 0) { if (error) *error = fstError; return NULL; } return host_gc_strdup(buffer); } #else #define expand_path(x, y) (x) #endif /* * 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; word16 terr = 0; /* 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); path3 = expand_path(path3, &terr); 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); path3 = expand_path(path3, &terr); path4 = expand_path(path4, &terr); 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); path3 = expand_path(path3, &terr); 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); fflush(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(); }