mirror of
https://github.com/digarok/gsplus.git
synced 2024-11-24 06:34:02 +00:00
Merge pull request #41 from ksherlock/prodos_mli
Prodos mli (win32 edition)
This commit is contained in:
commit
16b131d1f9
@ -8,7 +8,7 @@ OBJECTS1 = adb.o clock.o config.o debug.o dis.o engine_c.o scc.o iwm.o \
|
||||
ATOBJ = atbridge/aarp.o atbridge/atbridge.o atbridge/elap.o atbridge/llap.o atbridge/port.o
|
||||
PCAPOBJ = atbridge/pcap_delay.o
|
||||
TFEOBJ = tfe/tfe.o tfe/tfearch.o tfe/tfesupp.o
|
||||
FSTOBJ = host_common.o host_fst.o host_mli.o
|
||||
FSTOBJ = unix_host_common.o host_common.o host_fst.o host_mli.o
|
||||
|
||||
include vars
|
||||
|
||||
|
@ -14,47 +14,14 @@
|
||||
#include "gsos.h"
|
||||
#include "fst.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
#include "host_common.h"
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <sys/xattr.h>
|
||||
#include <sys/attr.h>
|
||||
#include <sys/paths.h>
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/xattr.h>
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(_WIN32) || defined(WIN_SDL)
|
||||
#include <io.h>
|
||||
#include <sys/xattr.h>
|
||||
#endif
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
#include <sys/types.h>
|
||||
#include <sys/extattr.h>
|
||||
#endif
|
||||
|
||||
#if defined(_AIX)
|
||||
#include <sys/ea.h>
|
||||
#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;
|
||||
@ -64,42 +31,6 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
@ -245,7 +176,9 @@ word32 host_map_errno(int xerrno) {
|
||||
case 0: return 0;
|
||||
case EBADF:
|
||||
return invalidAccess;
|
||||
#ifdef EDQUOT
|
||||
case EDQUOT:
|
||||
#endif
|
||||
case EFBIG:
|
||||
return volumeFull;
|
||||
case ENOENT:
|
||||
@ -438,7 +371,7 @@ static int hex(byte c) {
|
||||
}
|
||||
|
||||
|
||||
static int finder_info_to_filetype(const byte *buffer, word16 *file_type, word32 *aux_type) {
|
||||
int host_finder_info_to_filetype(const byte *buffer, word16 *file_type, word32 *aux_type) {
|
||||
|
||||
if (!memcmp("pdos", buffer + 4, 4))
|
||||
{
|
||||
@ -552,68 +485,6 @@ int host_file_type_to_finder_info(byte *buffer, word16 file_type, word32 aux_typ
|
||||
}
|
||||
|
||||
|
||||
#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 }
|
||||
@ -646,311 +517,31 @@ static struct ftype_entry prefixes[] = {
|
||||
|
||||
#undef _
|
||||
|
||||
word32 host_get_file_info(const char *path, struct file_info *fi) {
|
||||
struct stat st;
|
||||
memset(fi, 0, sizeof(*fi));
|
||||
void host_synthesize_file_xinfo(const char *path, struct file_info *fi) {
|
||||
|
||||
int ok = stat(path, &st);
|
||||
if (ok < 0) return host_map_errno(errno);
|
||||
/* guess the file type / auxtype based on extension */
|
||||
int n;
|
||||
const char *dot = NULL;
|
||||
const char *slash = NULL;
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -1028,3 +619,10 @@ void host_hexdump_native(void *data, unsigned address, int size) {
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
void host_free_directory(char **data, size_t count) {
|
||||
for (int i = 0; i < count; ++i) free(data[i]);
|
||||
free(data);
|
||||
}
|
||||
|
@ -1,3 +1,41 @@
|
||||
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#pragma pack(push, 2)
|
||||
struct AFP_Info {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
uint32_t file_id;
|
||||
uint32_t backup_date;
|
||||
uint8_t finder_info[32];
|
||||
uint16_t prodos_file_type;
|
||||
uint32_t prodos_aux_type;
|
||||
uint8_t reserved[6];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
void afp_init(struct AFP_Info *info, word16 file_type, word32 aux_type);
|
||||
BOOL afp_verify(struct AFP_Info *info);
|
||||
int afp_to_filetype(struct AFP_Info *info, word16 *file_type, word32 *aux_type);
|
||||
enum { prefer_prodos, prefer_hfs };
|
||||
void afp_synchronize(struct AFP_Info *info, int preference);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
typedef FILETIME host_time_t;
|
||||
typedef struct AFP_Info host_finder_info_t;
|
||||
#else
|
||||
typedef time_t host_time_t;
|
||||
typedef unsigned char host_finder_info_t[32];
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
enum {
|
||||
file_non,
|
||||
file_regular,
|
||||
@ -15,8 +53,8 @@ enum {
|
||||
|
||||
struct file_info {
|
||||
|
||||
time_t create_date;
|
||||
time_t modified_date;
|
||||
host_time_t create_date;
|
||||
host_time_t modified_date;
|
||||
word16 access;
|
||||
word16 storage_type;
|
||||
word16 file_type;
|
||||
@ -25,9 +63,12 @@ struct file_info {
|
||||
word32 blocks;
|
||||
word32 resource_eof;
|
||||
word32 resource_blocks;
|
||||
mode_t st_mode;
|
||||
int has_fi;
|
||||
#ifdef _WIN32
|
||||
struct AFP_Info afp;
|
||||
#else
|
||||
byte finder_info[32];
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@ -53,6 +94,7 @@ enum {
|
||||
N = 0x80
|
||||
};
|
||||
|
||||
extern char *g_cfg_host_path;
|
||||
extern int g_cfg_host_read_only;
|
||||
extern int g_cfg_host_crlf;
|
||||
extern int g_cfg_host_merlin;
|
||||
@ -61,7 +103,11 @@ extern char *host_root;
|
||||
unsigned host_startup(void);
|
||||
void host_shutdown(void);
|
||||
|
||||
#ifdef _WIN32
|
||||
int host_is_root(const BY_HANDLE_FILE_INFORMATION *info);
|
||||
#else
|
||||
int host_is_root(struct stat *);
|
||||
#endif
|
||||
|
||||
/* garbage collected string routines */
|
||||
|
||||
@ -82,24 +128,40 @@ 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 */
|
||||
#ifdef _WIN32
|
||||
word32 host_map_win32_error(DWORD);
|
||||
word32 host_map_win32_error_path(DWORD, const char *path);
|
||||
#endif
|
||||
|
||||
/* file info */
|
||||
int host_finder_info_to_filetype(const byte *buffer, word16 *file_type, word32 *aux_type);
|
||||
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);
|
||||
|
||||
/* guesses filetype/auxtype from extension */
|
||||
void host_synthesize_file_xinfo(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);
|
||||
void host_set_date_time_rec(word32 ptr, host_time_t time);
|
||||
void host_set_date_time(word32 ptr, host_time_t time);
|
||||
|
||||
time_t host_get_date_time(word32 ptr);
|
||||
time_t host_get_date_time_rec(word32 ptr);
|
||||
host_time_t host_get_date_time(word32 ptr);
|
||||
host_time_t host_get_date_time_rec(word32 ptr);
|
||||
|
||||
/* convert to prodos date/time */
|
||||
word32 host_convert_date_time(time_t time);
|
||||
word32 host_convert_date_time(host_time_t time);
|
||||
|
||||
|
||||
/* scan a directory, return array of char * */
|
||||
unsigned host_scan_directory(const char *path, char ***out, size_t *entries, unsigned p8);
|
||||
void host_free_directory(char **data, size_t count);
|
||||
|
||||
|
||||
/* 0x01, 0x0d, 0x0f, 0 on error */
|
||||
unsigned host_storage_type(const char *path, word16 *error_ptr);
|
||||
|
||||
void host_hexdump(word32 address, int size);
|
||||
void host_hexdump_native(void *data, unsigned address, int size);
|
||||
|
||||
|
@ -893,7 +893,7 @@ static word32 fst_open(int class, const char *path) {
|
||||
if (access > 3) return paramRangeErr;
|
||||
|
||||
// special access checks for directories.
|
||||
if (S_ISDIR(fi.st_mode)) {
|
||||
if (fi.storage_type == 0x0d || fi.storage_type == 0x0f) {
|
||||
if (resource_number) return resForkNotFound;
|
||||
switch (request_access) {
|
||||
case readEnableAllowWrite:
|
||||
|
544
src/host_mli.c
544
src/host_mli.c
@ -1,18 +1,23 @@
|
||||
|
||||
#define _BSD_SOURCE
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <libgen.h>
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#else
|
||||
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#endif
|
||||
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#include "defc.h"
|
||||
#include "gsos.h"
|
||||
@ -75,47 +80,18 @@ enum {
|
||||
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;
|
||||
|
||||
#ifdef _WIN32
|
||||
HANDLE h;
|
||||
#else
|
||||
int fd;
|
||||
#endif
|
||||
|
||||
unsigned buffer;
|
||||
unsigned level;
|
||||
unsigned newline_mask;
|
||||
@ -133,6 +109,121 @@ struct file_entry {
|
||||
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);
|
||||
@ -183,45 +274,6 @@ static char *is_host_path(unsigned pathname) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
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;
|
||||
@ -235,10 +287,10 @@ static unsigned lowercase_bits(const char *name) {
|
||||
|
||||
/* name is relative to the host directory. */
|
||||
|
||||
void *create_directory_file(const char *name, const char *path, unsigned *error_ptr, unsigned *block_ptr) {
|
||||
void *create_directory_file(const char *name, const char *path, word16 *error_ptr, unsigned *block_ptr) {
|
||||
|
||||
|
||||
byte *data;
|
||||
byte *data = NULL;
|
||||
int capacity = 0;
|
||||
int count = 0;
|
||||
unsigned offset = 0;
|
||||
@ -249,15 +301,14 @@ void *create_directory_file(const char *name, const char *path, unsigned *error_
|
||||
|
||||
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;
|
||||
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;
|
||||
@ -276,8 +327,7 @@ void *create_directory_file(const char *name, const char *path, unsigned *error_
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* TODO -- this is wrong. */
|
||||
/* trailing /s should be stripped */
|
||||
/* 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";
|
||||
@ -351,7 +401,7 @@ void *create_directory_file(const char *name, const char *path, unsigned *error_
|
||||
blocks = 1;
|
||||
for (int j = 0; j < entry_count; ++j) {
|
||||
|
||||
char *name = entries[j]->d_name;
|
||||
char *name = entries[j];
|
||||
int len = strlen(name);
|
||||
char *tmp = malloc(path_len + 2 + len);
|
||||
if (!tmp) continue;
|
||||
@ -428,12 +478,7 @@ void *create_directory_file(const char *name, const char *path, unsigned *error_
|
||||
*block_ptr = blocks;
|
||||
|
||||
exit:
|
||||
if (entries) {
|
||||
for (i = 0; i < entry_count; ++i) {
|
||||
free(entries[i]);
|
||||
}
|
||||
free(entries);
|
||||
}
|
||||
if (entries) host_free_directory(entries, entry_count);
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -560,10 +605,25 @@ static int mli_create(unsigned dcb, const char *path) {
|
||||
// todo -- remap access.
|
||||
|
||||
if (fi.storage_type == 0x0d) {
|
||||
int ok = mkdir(path, 0777);
|
||||
if (ok < 0)
|
||||
#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);
|
||||
@ -572,49 +632,69 @@ static int mli_create(unsigned dcb, const char *path) {
|
||||
fi.has_fi = 1;
|
||||
host_set_file_info(path, &fi);
|
||||
close(fd);
|
||||
#endif
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
word16 terr = 0;
|
||||
unsigned type;
|
||||
|
||||
if (!path1 || !path2) return badPathSyntax;
|
||||
|
||||
if (stat(path1, &st) < 0) {
|
||||
return host_map_errno_path(errno, path1);
|
||||
}
|
||||
|
||||
if (host_is_root(&st))
|
||||
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 (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);
|
||||
#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;
|
||||
}
|
||||
|
||||
@ -649,9 +729,18 @@ static int mli_write(unsigned dcb, struct file_entry *file) {
|
||||
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;
|
||||
|
||||
@ -700,8 +789,20 @@ static int mli_read(unsigned dcb, struct file_entry *file) {
|
||||
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;
|
||||
|
||||
@ -751,23 +852,22 @@ static int mli_set_buf(unsigned dcb, struct file_entry *file) {
|
||||
|
||||
static int mli_get_eof(unsigned dcb, struct file_entry *file) {
|
||||
|
||||
off_t eof = 0;
|
||||
word16 terr = 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);
|
||||
terr = file_eof(file);
|
||||
if (terr) return terr;
|
||||
break;
|
||||
}
|
||||
if (eof > 0xffffff) return outOfRange;
|
||||
set_memory24_c(dcb + 2, eof, 0);
|
||||
}
|
||||
|
||||
set_memory24_c(dcb + 2, file->eof, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -775,6 +875,9 @@ static int mli_get_eof(unsigned dcb, struct file_entry *file) {
|
||||
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:
|
||||
@ -785,8 +888,16 @@ static int mli_set_eof(unsigned dcb, struct file_entry *file) {
|
||||
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;
|
||||
@ -813,22 +924,22 @@ static int mli_get_mark(unsigned dcb, struct file_entry *file) {
|
||||
|
||||
static int mli_set_mark(unsigned dcb, struct file_entry *file) {
|
||||
|
||||
off_t eof = 0;
|
||||
word16 terr = 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);
|
||||
terr = file_eof(file);
|
||||
if (terr) return terr;
|
||||
break;
|
||||
}
|
||||
|
||||
if (position > eof) return outOfRange;
|
||||
if (position > file->eof) return outOfRange;
|
||||
file->mark = position;
|
||||
|
||||
return 0;
|
||||
@ -842,6 +953,8 @@ static int mli_newline(unsigned dcb, struct file_entry *file) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int mli_close(unsigned dcb, struct file_entry *file) {
|
||||
|
||||
if (!file) {
|
||||
@ -851,18 +964,12 @@ static int mli_close(unsigned dcb, struct file_entry *file) {
|
||||
file = &files[i];
|
||||
if (!file->type) continue;
|
||||
if (file->level < level) continue;
|
||||
mli_close(dcb, file);
|
||||
file_close(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;
|
||||
return file_close(file);
|
||||
}
|
||||
|
||||
|
||||
@ -876,16 +983,11 @@ static int mli_flush(unsigned dcb, struct file_entry *file) {
|
||||
if (!file->type) continue;
|
||||
if (file->level < level) continue;
|
||||
|
||||
if (file->fd >= 0)
|
||||
fsync(file->fd);
|
||||
file_flush(file);
|
||||
}
|
||||
return -1; /* pass to prodos mli */
|
||||
}
|
||||
|
||||
if (!file->type) return invalidRefNum;
|
||||
if (file->fd >= 0) fsync(file->fd);
|
||||
|
||||
return 0;
|
||||
return file_flush(file);
|
||||
}
|
||||
|
||||
|
||||
@ -898,10 +1000,23 @@ static int mli_quit(unsigned dcb) {
|
||||
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 _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));
|
||||
file->fd = -1;
|
||||
|
||||
#if _WIN32
|
||||
file->h = INVALID_HANDLE_VALUE;
|
||||
#else
|
||||
file->fd = -1;
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
/* need a better way to know... */
|
||||
/* host_shutdown(); */
|
||||
@ -909,11 +1024,11 @@ static int mli_quit(unsigned dcb) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static int mli_open(unsigned dcb, const char *name, const char *path) {
|
||||
|
||||
int fd;
|
||||
int refnum = 0;
|
||||
unsigned type;
|
||||
|
||||
struct file_entry *file = NULL;
|
||||
for (unsigned i = 0; i < MAX_FILES; ++i) {
|
||||
@ -929,44 +1044,24 @@ static int mli_open(unsigned dcb, const char *name, const char *path) {
|
||||
//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;
|
||||
}
|
||||
word16 terr = host_get_file_info(path, &fi);
|
||||
if (terr) 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)) {
|
||||
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;
|
||||
|
||||
type = file_regular;
|
||||
file->type = file_regular;
|
||||
|
||||
if (g_cfg_host_crlf) {
|
||||
if (fi.file_type == 0x04 || fi.file_type == 0xb0)
|
||||
@ -978,17 +1073,10 @@ static int mli_open(unsigned dcb, const char *name, const char *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;
|
||||
@ -1017,21 +1105,22 @@ static int mli_set_prefix(unsigned dcb, char *name, char *path) {
|
||||
return saved_prefix ? -2 : -1;
|
||||
}
|
||||
|
||||
int l;
|
||||
struct stat st;
|
||||
unsigned type;
|
||||
word16 terr;
|
||||
|
||||
if (stat(path, &st) < 0)
|
||||
return host_map_errno_path(errno, path);
|
||||
|
||||
if (!S_ISDIR(st.st_mode)) {
|
||||
return badStoreType;
|
||||
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);
|
||||
|
||||
l = strlen(name);
|
||||
int l = strlen(name);
|
||||
/* trim trailing / */
|
||||
while (l > 1 && name[l-1] == '/') --l;
|
||||
name[l] = 0;
|
||||
@ -1157,6 +1246,30 @@ static const char *call_name(unsigned call) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
|
||||
#include <sys/cygwin.h>
|
||||
|
||||
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
|
||||
@ -1170,6 +1283,7 @@ void host_mli_head() {
|
||||
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;
|
||||
@ -1198,27 +1312,30 @@ void host_mli_head() {
|
||||
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:
|
||||
@ -1358,6 +1475,7 @@ void host_mli_head() {
|
||||
|
||||
}
|
||||
fputs("\n", stderr);
|
||||
fflush(stderr);
|
||||
host_gc_free();
|
||||
|
||||
|
||||
|
512
src/unix_host_common.c
Normal file
512
src/unix_host_common.c
Normal file
@ -0,0 +1,512 @@
|
||||
|
||||
#define _BSD_SOURCE
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <sys/xattr.h>
|
||||
#include <sys/attr.h>
|
||||
#include <sys/paths.h>
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/xattr.h>
|
||||
#endif
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
#include <sys/types.h>
|
||||
#include <sys/extattr.h>
|
||||
#endif
|
||||
|
||||
#if defined(_AIX)
|
||||
#include <sys/ea.h>
|
||||
#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
|
||||
|
||||
|
||||
|
||||
|
||||
#include "defc.h"
|
||||
#include "gsos.h"
|
||||
|
||||
#include "host_common.h"
|
||||
|
||||
|
||||
static ino_t root_ino = 0;
|
||||
static dev_t root_dev = 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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#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;
|
||||
|
||||
host_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;
|
||||
host_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;
|
||||
|
||||
host_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
|
||||
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
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) {
|
||||
host_synthesize_file_xinfo(path, fi);
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
|
||||
static int qsort_callback(const void *a, const void *b) {
|
||||
return strcasecmp(*(const char **)a, *(const char **)b);
|
||||
}
|
||||
|
||||
unsigned host_scan_directory(const char *path, char ***out, size_t *entries, unsigned p8) {
|
||||
|
||||
DIR *dp;
|
||||
char **data = NULL;
|
||||
size_t capacity = 0;
|
||||
size_t count = 0;
|
||||
|
||||
dp = opendir(path);
|
||||
if (!dp) return host_map_errno_path(errno, path);
|
||||
|
||||
for(;;) {
|
||||
struct dirent *d = readdir(dp);
|
||||
if (!d) break;
|
||||
|
||||
const char *name = d->d_name;
|
||||
|
||||
if (name[0] == 0) continue;
|
||||
if (name[0] == '.') continue;
|
||||
if (p8) {
|
||||
int ok = 1;
|
||||
int n = strlen(name);
|
||||
if (n > 15) continue;
|
||||
/* check for invalid characters? */
|
||||
for (int i = 0; i < n; ++i) {
|
||||
unsigned char c = name[i];
|
||||
if (isalpha(c) || isdigit(c) || c == '.') continue;
|
||||
ok = 0;
|
||||
break;
|
||||
}
|
||||
if (!ok) continue;
|
||||
}
|
||||
if (count == capacity) {
|
||||
char **tmp;
|
||||
tmp = realloc(data, (capacity + 100) * sizeof(char *));
|
||||
if (!tmp) {
|
||||
closedir(dp);
|
||||
host_free_directory(data, count);
|
||||
return outOfMem;
|
||||
}
|
||||
data = tmp;
|
||||
for (int i = count; i < capacity; ++i) data[i] = 0;
|
||||
capacity += 100;
|
||||
}
|
||||
data[count++] = strdup(name);
|
||||
}
|
||||
closedir(dp);
|
||||
|
||||
qsort(data, count, sizeof(char *), qsort_callback);
|
||||
*entries = count;
|
||||
*out = data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned host_storage_type(const char *path, word16 *error) {
|
||||
struct stat st;
|
||||
if (!path) {
|
||||
*error = badPathSyntax;
|
||||
return 0;
|
||||
}
|
||||
if (stat(path, &st) < 0) {
|
||||
*error = host_map_errno_path(errno, path);
|
||||
return 0;
|
||||
}
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
return host_is_root(&st) ? 0x0f : directoryFile;
|
||||
}
|
||||
if (S_ISDIR(st.st_mode)) return standardFile;
|
||||
*error = badStoreType;
|
||||
return 0;
|
||||
}
|
633
src/win32_host_common.c
Normal file
633
src/win32_host_common.c
Normal file
@ -0,0 +1,633 @@
|
||||
|
||||
#define _WIN32_WINNT 0x0600 // vista+
|
||||
#include <Windows.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "defc.h"
|
||||
#include "gsos.h"
|
||||
|
||||
#include "host_common.h"
|
||||
|
||||
|
||||
|
||||
void afp_init(struct AFP_Info *info, word16 file_type, word32 aux_type) {
|
||||
//static_assert(sizeof(AFP_Info) == 60, "Incorrect AFP_Info size");
|
||||
memset(info, 0, sizeof(*info));
|
||||
info->magic = 0x00504641;
|
||||
info->version = 0x00010000;
|
||||
info->prodos_file_type = file_type;
|
||||
info->prodos_aux_type = aux_type;
|
||||
if (file_type || aux_type)
|
||||
host_file_type_to_finder_info(info->finder_info, file_type, aux_type);
|
||||
}
|
||||
|
||||
BOOL afp_verify(struct AFP_Info *info) {
|
||||
if (!info) return 0;
|
||||
|
||||
if (info->magic != 0x00504641) return 0;
|
||||
if (info->version != 0x00010000) return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int afp_to_filetype(struct AFP_Info *info, word16 *file_type, word32 *aux_type) {
|
||||
// check for prodos ftype/auxtype...
|
||||
if (info->prodos_file_type || info->prodos_aux_type) {
|
||||
*file_type = info->prodos_file_type;
|
||||
*aux_type = info->prodos_aux_type;
|
||||
return 0;
|
||||
}
|
||||
int ok = host_finder_info_to_filetype(info->finder_info, file_type, aux_type);
|
||||
if (ok == 0) {
|
||||
info->prodos_file_type = *file_type;
|
||||
info->prodos_aux_type = *aux_type;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void afp_synchronize(struct AFP_Info *info, int preference) {
|
||||
// if ftype/auxtype is inconsistent between prodos and finder info, use
|
||||
// prodos as source of truth.
|
||||
word16 f;
|
||||
word32 a;
|
||||
if (host_finder_info_to_filetype(info->finder_info, &f, &a) != 0) return;
|
||||
if (f == info->prodos_file_type && a == info->prodos_aux_type) return;
|
||||
if (preference == prefer_prodos)
|
||||
host_file_type_to_finder_info(info->finder_info, info->prodos_file_type, info->prodos_aux_type);
|
||||
else {
|
||||
info->prodos_file_type = f;
|
||||
info->prodos_aux_type = a;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static DWORD root_file_id[3] = {};
|
||||
|
||||
unsigned host_startup(void) {
|
||||
|
||||
|
||||
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);
|
||||
|
||||
|
||||
HANDLE h = CreateFile(host_root, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
fprintf(stderr, "%s does not exist or is not accessible\n", host_root);
|
||||
return invalidFSTop;
|
||||
}
|
||||
FILE_BASIC_INFO fbi;
|
||||
BY_HANDLE_FILE_INFORMATION info;
|
||||
|
||||
GetFileInformationByHandle(h, &info);
|
||||
GetFileInformationByHandleEx(h, FileBasicInfo, &fbi, sizeof(fbi));
|
||||
// can't delete volume root.
|
||||
CloseHandle(h);
|
||||
|
||||
root_file_id[0] = info.dwVolumeSerialNumber;
|
||||
root_file_id[1] = info.nFileIndexHigh;
|
||||
root_file_id[2] = info.nFileIndexLow;
|
||||
|
||||
|
||||
if (!(fbi.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||
fprintf(stderr, "%s is not a directory\n", host_root);
|
||||
CloseHandle(h);
|
||||
return invalidFSTop;
|
||||
}
|
||||
CloseHandle(h);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void host_shutdown(void) {
|
||||
if (host_root) free(host_root);
|
||||
host_root = NULL;
|
||||
memset(root_file_id, 0, sizeof(root_file_id));
|
||||
}
|
||||
|
||||
int host_is_root(const BY_HANDLE_FILE_INFORMATION *info) {
|
||||
DWORD id[3] = { info->dwVolumeSerialNumber, info->nFileIndexHigh, info->nFileIndexLow };
|
||||
|
||||
return memcmp(&id, &root_file_id, sizeof(root_file_id)) == 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Date/time conversion
|
||||
*/
|
||||
|
||||
|
||||
static int dayofweek(int y, int m, int d) {
|
||||
/* 1 <= m <= 12, y > 1752 (in the U.K.) */
|
||||
static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
|
||||
y -= m < 3;
|
||||
return (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7;
|
||||
}
|
||||
|
||||
/*
|
||||
* converts time_t to a gs/os readhextime date/time record.
|
||||
*/
|
||||
|
||||
void host_set_date_time_rec(word32 ptr, FILETIME utc) {
|
||||
|
||||
if (utc.dwLowDateTime == 0 && utc.dwHighDateTime == 0) {
|
||||
for (int i = 0; i < 8; ++i) set_memory_c(ptr++, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
SYSTEMTIME tmLocal;
|
||||
SYSTEMTIME tmUTC;
|
||||
|
||||
FileTimeToSystemTime(&utc, &tmUTC);
|
||||
SystemTimeToTzSpecificLocalTime(NULL, &tmUTC, &tmLocal);
|
||||
|
||||
if (tmLocal.wYear < 1900 || tmLocal.wYear > 1900 + 255) {
|
||||
for (int i = 0; i < 8; ++i) set_memory_c(ptr++, 0, 0);
|
||||
return;
|
||||
}
|
||||
if (tmLocal.wSecond == 60) tmLocal.wSecond = 59; /* leap second */
|
||||
|
||||
int dow = dayofweek(tmLocal.wYear, tmLocal.wMonth, tmLocal.wDay);
|
||||
set_memory_c(ptr++, tmLocal.wSecond, 0);
|
||||
set_memory_c(ptr++, tmLocal.wMinute, 0);
|
||||
set_memory_c(ptr++, tmLocal.wHour, 0);
|
||||
set_memory_c(ptr++, tmLocal.wYear - 1900, 0);
|
||||
set_memory_c(ptr++, tmLocal.wDay - 1, 0); // 1 = sunday
|
||||
set_memory_c(ptr++, tmLocal.wMonth - 1, 0);
|
||||
set_memory_c(ptr++, 0, 0);
|
||||
set_memory_c(ptr++, dow + 1, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* converts time_t to a prodos16 date/time record.
|
||||
*/
|
||||
void host_set_date_time(word32 ptr, FILETIME utc) {
|
||||
|
||||
if (utc.dwLowDateTime == 0 && utc.dwHighDateTime == 0) {
|
||||
for (int i = 0; i < 4; ++i) set_memory_c(ptr++, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
SYSTEMTIME tmLocal;
|
||||
SYSTEMTIME tmUTC;
|
||||
|
||||
FileTimeToSystemTime(&utc, &tmUTC);
|
||||
SystemTimeToTzSpecificLocalTime(NULL, &tmUTC, &tmLocal);
|
||||
|
||||
if (tmLocal.wYear < 1940 || tmLocal.wYear > 2039) {
|
||||
for (int i = 0; i < 4; ++i) set_memory_c(ptr++, 0, 0);
|
||||
return;
|
||||
}
|
||||
if (tmLocal.wSecond == 60) tmLocal.wSecond = 59; /* leap second */
|
||||
|
||||
word16 tmp = 0;
|
||||
tmp |= (tmLocal.wYear % 100) << 9;
|
||||
tmp |= tmLocal.wMonth << 5;
|
||||
tmp |= tmLocal.wDay;
|
||||
|
||||
set_memory16_c(ptr, tmp, 0);
|
||||
ptr += 2;
|
||||
|
||||
tmp = 0;
|
||||
tmp |= tmLocal.wHour << 8;
|
||||
tmp |= tmLocal.wMinute;
|
||||
set_memory16_c(ptr, tmp, 0);
|
||||
}
|
||||
|
||||
|
||||
FILETIME host_get_date_time(word32 ptr) {
|
||||
|
||||
FILETIME utc = {0, 0};
|
||||
|
||||
word16 a = get_memory16_c(ptr + 0, 0);
|
||||
word16 b = get_memory16_c(ptr + 2, 0);
|
||||
if (!a && !b) return utc;
|
||||
|
||||
|
||||
SYSTEMTIME tmLocal;
|
||||
SYSTEMTIME tmUTC;
|
||||
memset(&tmLocal, 0, sizeof(tmLocal));
|
||||
memset(&tmUTC, 0, sizeof(tmUTC));
|
||||
|
||||
tmLocal.wYear = ((a >> 9) & 0x7f) + 1900;
|
||||
tmLocal.wMonth = ((a >> 5) & 0x0f);
|
||||
tmLocal.wDay = (a >> 0) & 0x1f;
|
||||
|
||||
tmLocal.wHour = (b >> 8) & 0x1f;
|
||||
tmLocal.wMinute = (b >> 0) & 0x3f;
|
||||
tmLocal.wSecond = 0;
|
||||
|
||||
|
||||
// 00 - 39 => 2000-2039
|
||||
// 40 - 99 => 1940-1999
|
||||
if (tmLocal.wYear < 40) tmLocal.wYear += 100;
|
||||
|
||||
|
||||
TzSpecificLocalTimeToSystemTime(NULL, &tmLocal, &tmUTC);
|
||||
if (!SystemTimeToFileTime(&tmUTC, &utc)) utc =(FILETIME){0, 0};
|
||||
|
||||
return utc;
|
||||
}
|
||||
|
||||
FILETIME host_get_date_time_rec(word32 ptr) {
|
||||
|
||||
FILETIME utc = {0, 0};
|
||||
|
||||
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 utc;
|
||||
|
||||
SYSTEMTIME tmLocal;
|
||||
SYSTEMTIME tmUTC;
|
||||
memset(&tmLocal, 0, sizeof(tmLocal));
|
||||
memset(&tmUTC, 0, sizeof(tmUTC));
|
||||
|
||||
tmLocal.wSecond = buffer[0];
|
||||
tmLocal.wMinute = buffer[1];
|
||||
tmLocal.wHour = buffer[2];
|
||||
tmLocal.wYear = 1900 + buffer[3];
|
||||
tmLocal.wDay = buffer[4] + 1;
|
||||
tmLocal.wMonth = buffer[5] + 1;
|
||||
|
||||
TzSpecificLocalTimeToSystemTime(NULL, &tmLocal, &tmUTC);
|
||||
if (!SystemTimeToFileTime(&tmUTC, &utc)) utc =(FILETIME){0, 0};
|
||||
|
||||
return utc;
|
||||
}
|
||||
|
||||
|
||||
word32 host_convert_date_time(FILETIME utc) {
|
||||
|
||||
if (utc.dwLowDateTime == 0 && utc.dwHighDateTime == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
SYSTEMTIME tmLocal;
|
||||
SYSTEMTIME tmUTC;
|
||||
|
||||
FileTimeToSystemTime(&utc, &tmUTC);
|
||||
SystemTimeToTzSpecificLocalTime(NULL, &tmUTC, &tmLocal);
|
||||
|
||||
if (tmLocal.wYear < 1940 || tmLocal.wYear > 2039) {
|
||||
return 0;
|
||||
}
|
||||
if (tmLocal.wSecond == 60) tmLocal.wSecond = 59; /* leap second */
|
||||
|
||||
word16 dd = 0;
|
||||
dd |= (tmLocal.wYear % 100) << 9;
|
||||
dd |= tmLocal.wMonth << 5;
|
||||
dd |= tmLocal.wDay;
|
||||
|
||||
|
||||
word16 tt = 0;
|
||||
tt |= tmLocal.wHour << 8;
|
||||
tt |= tmLocal.wMinute;
|
||||
|
||||
return (tt << 16) | dd;
|
||||
}
|
||||
|
||||
|
||||
word32 host_map_win32_error(DWORD e) {
|
||||
switch (e) {
|
||||
case ERROR_NO_MORE_FILES:
|
||||
return endOfDir;
|
||||
case ERROR_FILE_NOT_FOUND:
|
||||
return fileNotFound;
|
||||
case ERROR_PATH_NOT_FOUND:
|
||||
return pathNotFound;
|
||||
case ERROR_INVALID_ACCESS:
|
||||
return invalidAccess;
|
||||
case ERROR_FILE_EXISTS:
|
||||
case ERROR_ALREADY_EXISTS:
|
||||
return dupPathname;
|
||||
case ERROR_DISK_FULL:
|
||||
return volumeFull;
|
||||
case ERROR_INVALID_PARAMETER:
|
||||
return paramRangeErr;
|
||||
case ERROR_DRIVE_LOCKED:
|
||||
return drvrWrtProt;
|
||||
case ERROR_NEGATIVE_SEEK:
|
||||
return outOfRange;
|
||||
case ERROR_SHARING_VIOLATION:
|
||||
return fileBusy; // destroy open file, etc.
|
||||
case ERROR_DIR_NOT_EMPTY:
|
||||
return invalidAccess;
|
||||
|
||||
// ...
|
||||
default:
|
||||
fprintf(stderr, "GetLastError: %08x - %d\n", (int)e, (int)e);
|
||||
return drvrIOError;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void host_get_file_xinfo(const char *path, struct file_info *fi) {
|
||||
|
||||
HANDLE h;
|
||||
|
||||
char *p = host_gc_append_string(path, ":AFP_Resource");
|
||||
LARGE_INTEGER size = { 0 };
|
||||
|
||||
|
||||
h = CreateFile(p, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
if (h != INVALID_HANDLE_VALUE) {
|
||||
GetFileSizeEx(h, &size);
|
||||
CloseHandle(h);
|
||||
}
|
||||
fi->resource_eof = size.LowPart;
|
||||
fi->resource_blocks = (size.LowPart + 511) / 512;
|
||||
|
||||
|
||||
p = host_gc_append_string(path, ":AFP_AfpInfo");
|
||||
|
||||
h = CreateFile(p, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
if (h != INVALID_HANDLE_VALUE) {
|
||||
DWORD read = 0;
|
||||
if (ReadFile(h, &fi->afp, sizeof(struct AFP_Info), &read, NULL) && read == sizeof(struct AFP_Info)) {
|
||||
if (afp_verify(&fi->afp)) fi->has_fi = 1;
|
||||
afp_to_filetype(&fi->afp, &fi->file_type, &fi->aux_type);
|
||||
}
|
||||
CloseHandle(h);
|
||||
}
|
||||
}
|
||||
|
||||
static word16 map_attributes(DWORD dwFileAttributes) {
|
||||
|
||||
// 0x01 = read enable
|
||||
// 0x02 = write enable
|
||||
// 0x04 = invisible
|
||||
// 0x08 = reserved
|
||||
// 0x10 = reserved
|
||||
// 0x20 = backup needed
|
||||
// 0x40 = rename enable
|
||||
// 0x80 = destroy enable
|
||||
|
||||
word16 access = 0;
|
||||
|
||||
if (dwFileAttributes & FILE_ATTRIBUTE_READONLY)
|
||||
access = readEnable;
|
||||
else
|
||||
access = readEnable | writeEnable | renameEnable | destroyEnable;
|
||||
|
||||
if (dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
|
||||
access |= fileInvisible;
|
||||
|
||||
// map FILE_ATTRIBUTE_ARCHIVE to backup needed bit?
|
||||
|
||||
return access;
|
||||
}
|
||||
|
||||
|
||||
word32 host_get_file_info(const char *path, struct file_info *fi) {
|
||||
|
||||
|
||||
HANDLE h = CreateFile(path, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
if (h == INVALID_HANDLE_VALUE) return host_map_win32_error(GetLastError());
|
||||
|
||||
//FILE_BASIC_INFO fbi;
|
||||
//FILE_STANDARD_INFO fsi;
|
||||
//FILE_ID_INFO fii;
|
||||
BY_HANDLE_FILE_INFORMATION info;
|
||||
|
||||
|
||||
memset(fi, 0, sizeof(*fi));
|
||||
//memset(&fbi, 0, sizeof(fbi));
|
||||
//memset(&fsi, 0, sizeof(fsi));
|
||||
|
||||
GetFileInformationByHandle(h, &info);
|
||||
//GetFileInformationByHandleEx(h, FileBasicInfo, &fbi, sizeof(fbi));
|
||||
//GetFileInformationByHandleEx(h, FileStandardInfo, &fsi, sizeof(fsi));
|
||||
//GetFileInformationByHandleEx(h, FileIdInfo, &fii, sizeof(fii));
|
||||
|
||||
word32 size = info.nFileSizeLow;
|
||||
|
||||
fi->eof = size;
|
||||
fi->blocks = (size + 511) / 512;
|
||||
|
||||
fi->create_date = info.ftCreationTime;
|
||||
fi->modified_date = info.ftLastWriteTime;
|
||||
|
||||
if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
fi->storage_type = directoryFile;
|
||||
fi->file_type = 0x0f;
|
||||
|
||||
if (host_is_root(&info)) fi->storage_type = 0x0f;
|
||||
} else {
|
||||
fi->file_type = 0x06;
|
||||
if (size < 0x200) fi->storage_type = seedling;
|
||||
else if (size < 0x20000) fi->storage_type = sapling;
|
||||
else fi->storage_type = tree;
|
||||
|
||||
host_get_file_xinfo(path, fi);
|
||||
if (fi->resource_eof) fi->storage_type = extendedFile;
|
||||
|
||||
if (!fi->has_fi) host_synthesize_file_xinfo(path, fi);
|
||||
}
|
||||
|
||||
// 0x01 = read enable
|
||||
// 0x02 = write enable
|
||||
// 0x04 = invisible
|
||||
// 0x08 = reserved
|
||||
// 0x10 = reserved
|
||||
// 0x20 = backup needed
|
||||
// 0x40 = rename enable
|
||||
// 0x80 = destroy enable
|
||||
|
||||
word16 access = 0;
|
||||
|
||||
if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
|
||||
access = 0x01;
|
||||
else
|
||||
access = 0x01 | 0x02 | 0x40 | 0x80;
|
||||
|
||||
if (info.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
|
||||
access |= 0x04;
|
||||
|
||||
// map FILE_ATTRIBUTE_ARCHIVE to backup needed bit?
|
||||
|
||||
fi->access = map_attributes(info.dwFileAttributes);
|
||||
|
||||
|
||||
CloseHandle(h);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
word32 host_set_file_info(const char *path, struct file_info *fi) {
|
||||
|
||||
if (fi->has_fi && fi->storage_type != 0x0d && fi->storage_type != 0x0f) {
|
||||
char *rpath = host_gc_append_string(path, ":AFP_AfpInfo");
|
||||
|
||||
HANDLE h = CreateFile(rpath, GENERIC_WRITE,
|
||||
FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (h == INVALID_HANDLE_VALUE) return host_map_win32_error(GetLastError());
|
||||
|
||||
WriteFile(h, &fi->afp, sizeof(struct AFP_Info), NULL, NULL);
|
||||
CloseHandle(h);
|
||||
}
|
||||
|
||||
|
||||
if (fi->create_date.dwLowDateTime || fi->create_date.dwHighDateTime
|
||||
|| fi->modified_date.dwLowDateTime || fi->modified_date.dwHighDateTime) {
|
||||
// SetFileInformationByHandle can modify dates.
|
||||
HANDLE h;
|
||||
h = CreateFile(path, FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (h == INVALID_HANDLE_VALUE) return host_map_win32_error(GetLastError());
|
||||
|
||||
FILE_BASIC_INFO fbi;
|
||||
FILE_BASIC_INFO newfbi;
|
||||
memset(&fbi, 0, sizeof(fbi));
|
||||
memset(&newfbi, 0, sizeof(newfbi));
|
||||
|
||||
BOOL ok;
|
||||
ok = GetFileInformationByHandleEx(h, FileBasicInfo, &fbi, sizeof(fbi));
|
||||
|
||||
int delta = 0;
|
||||
|
||||
word16 old_access = map_attributes(fbi.FileAttributes);
|
||||
if (fi->access && fi->access != old_access) {
|
||||
newfbi.FileAttributes = fbi.FileAttributes;
|
||||
|
||||
if (fi->access & fileInvisible) {
|
||||
delta = 1;
|
||||
newfbi.FileAttributes |= FILE_ATTRIBUTE_HIDDEN;
|
||||
}
|
||||
// hfs fst only marks it read enable if all are clear.
|
||||
word16 locked = writeEnable | destroyEnable | renameEnable;
|
||||
if ((fi->access & locked) == 0) {
|
||||
delta = 1;
|
||||
newfbi.FileAttributes |= FILE_ATTRIBUTE_READONLY;
|
||||
}
|
||||
}
|
||||
|
||||
// todo -- compare against nt file time to see if it's actually changed.
|
||||
// to prevent time stamp truncation.
|
||||
|
||||
if (fi->create_date.dwLowDateTime || fi->create_date.dwHighDateTime) {
|
||||
delta = 1;
|
||||
newfbi.CreationTime.LowPart = fi->create_date.dwLowDateTime;
|
||||
newfbi.CreationTime.HighPart = fi->create_date.dwHighDateTime;
|
||||
}
|
||||
if (fi->modified_date.dwLowDateTime || fi->modified_date.dwHighDateTime) {
|
||||
delta = 1;
|
||||
newfbi.LastWriteTime.LowPart = fi->modified_date.dwLowDateTime;
|
||||
newfbi.LastWriteTime.HighPart = fi->modified_date.dwHighDateTime;
|
||||
//newfbi.ChangeTime.LowPart = fi->modified_date.dwLowDateTime; //?
|
||||
//newfbi.ChangeTime.HighPart = fi->modified_date.dwHighDateTime; //?
|
||||
}
|
||||
|
||||
if (delta)
|
||||
ok = SetFileInformationByHandle(h, FileBasicInfo, &newfbi, sizeof(newfbi));
|
||||
CloseHandle(h);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int qsort_callback(const void *a, const void *b) {
|
||||
return strcasecmp(*(const char **)a, *(const char **)b);
|
||||
}
|
||||
|
||||
unsigned host_scan_directory(const char *path, char ***out, size_t *entries, unsigned p8) {
|
||||
|
||||
WIN32_FIND_DATA fdata;
|
||||
HANDLE h;
|
||||
char **data = NULL;
|
||||
size_t capacity = 0;
|
||||
size_t count = 0;
|
||||
DWORD e;
|
||||
|
||||
path = host_gc_append_path(path, "*");
|
||||
|
||||
h = FindFirstFile(path, &fdata);
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
e = GetLastError();
|
||||
if (e == ERROR_FILE_NOT_FOUND) {
|
||||
/* empty directory */
|
||||
*out = NULL;
|
||||
*entries = 0;
|
||||
return 0;
|
||||
}
|
||||
return host_map_win32_error(e);
|
||||
}
|
||||
|
||||
do {
|
||||
char *name = fdata.cFileName;
|
||||
if (name[0] == 0) continue;
|
||||
if (name[0] == '.') continue;
|
||||
if (p8) {
|
||||
int ok = 1;
|
||||
int n = strlen(name);
|
||||
if (n > 15) continue;
|
||||
/* check for invalid characters? */
|
||||
for (int i = 0; i < n; ++i) {
|
||||
unsigned char c = name[i];
|
||||
if (isalpha(c) || isdigit(c) || c == '.') continue;
|
||||
ok = 0;
|
||||
break;
|
||||
}
|
||||
if (!ok) continue;
|
||||
}
|
||||
if (count == capacity) {
|
||||
char **tmp;
|
||||
tmp = realloc(data, (capacity + 100) * sizeof(char *));
|
||||
if (!tmp) {
|
||||
FindClose(h);
|
||||
host_free_directory(data, count);
|
||||
return outOfMem;
|
||||
}
|
||||
data = tmp;
|
||||
for (int i = count; i < capacity; ++i) data[i] = 0;
|
||||
capacity += 100;
|
||||
}
|
||||
data[count++] = strdup(name);
|
||||
|
||||
} while (FindNextFile(h, &fdata) != 0);
|
||||
|
||||
e = GetLastError();
|
||||
FindClose(h);
|
||||
|
||||
if (e && e != ERROR_NO_MORE_FILES) {
|
||||
host_free_directory(data, count);
|
||||
return host_map_win32_error(e);
|
||||
}
|
||||
qsort(data, count, sizeof(char *), qsort_callback);
|
||||
*entries = count;
|
||||
*out = data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
unsigned host_storage_type(const char *path, word16 *error) {
|
||||
if (!path) {
|
||||
*error = badPathSyntax;
|
||||
return 0;
|
||||
}
|
||||
HANDLE h;
|
||||
h = CreateFile(path, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
*error = host_map_win32_error(GetLastError());
|
||||
return 0;
|
||||
}
|
||||
|
||||
BY_HANDLE_FILE_INFORMATION info;
|
||||
GetFileInformationByHandle(h, &info);
|
||||
CloseHandle(h);
|
||||
|
||||
if (host_is_root(&info)) return 0x0f;
|
||||
|
||||
if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
return directoryFile;
|
||||
|
||||
return standardFile;
|
||||
}
|
1239
src/win32_host_fst.c
1239
src/win32_host_fst.c
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user