WIP fs impl

This commit is contained in:
2026-02-10 17:48:36 +01:00
parent c0d965be26
commit 9c5e4a3d5b
6 changed files with 842 additions and 83 deletions

View File

@@ -12,6 +12,9 @@
// we need the header.
#include "neonucleus.h"
// to use the numerical accuracy better
#define NN_COMPONENT_CALLBUDGET 10000
#ifdef NN_ATOMIC_NONE
typedef size_t nn_refc_t;
@@ -41,25 +44,6 @@ bool nn_decRef(nn_refc_t *refc) {
}
#endif
// Based off https://stackoverflow.com/questions/5919996/how-to-detect-reliably-mac-os-x-ios-linux-windows-in-c-preprocessor
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
//define something for Windows (32-bit and 64-bit, this part is common)
#define NN_WINDOWS
#elif __APPLE__
#define NN_MACOS
#elif __linux__
#define NN_LINUX
#endif
#if __unix__ // all unices not caught above
// Unix
#define NN_UNIX
#define NN_POSIX
#elif defined(_POSIX_VERSION)
// POSIX
#define NN_POSIX
#endif
typedef struct nn_Lock nn_Lock;
// the special includes
@@ -328,6 +312,43 @@ void nn_crc32ChecksumBytes(unsigned int checksum, char out[8]) {
}
}
bool nn_simplifyPath(const char original[NN_MAX_PATH], char simplified[NN_MAX_PATH]) {
// pass 1: check for valid characters, and \ becomes /
for(size_t i = 0; true; i++) {
if(original[i] == '\\') simplified[i] = '/';
else simplified[i] = original[i];
if(original[i] == '\0') break;
}
// get rid of //, starting / and ending /
{
while(simplified[0] == '/') {
for(size_t i = 1; true; i++) {
simplified[i-1] = simplified[i];
if(simplified[i] == '\0') break;
}
}
size_t j = 0;
for(size_t i = 0; simplified[i] != '\0'; i++) {
if(simplified[i] == '/' && simplified[i+1] == '/') {
// simply discard it
continue;
} else {
simplified[j] = simplified[i];
j++;
}
}
simplified[j] = '\0';
while(simplified[j-1] == '/') {
j--;
simplified[j] = '\0';
}
}
// TODO: handle ..
// valid
return true;
}
int nn_memcmp(const char *a, const char *b, size_t len) {
for(size_t i = 0; i < len; i++) {
char c = a[i];
@@ -549,6 +570,7 @@ typedef struct nn_Component {
char *address;
nn_ComponentType *ctype;
int slot;
float budgetUsed;
void *userdata;
void *state;
} nn_Component;
@@ -1024,6 +1046,7 @@ nn_Exit nn_tick(nn_Computer *computer) {
return NN_EBADSTATE;
}
nn_resetCallBudget(computer);
nn_resetComponentBudgets(computer);
computer->state = NN_RUNNING;
nn_ArchitectureRequest req;
req.computer = computer;
@@ -1031,8 +1054,6 @@ nn_Exit nn_tick(nn_Computer *computer) {
req.localState = computer->archState;
req.action = NN_ARCH_TICK;
err = computer->arch.handler(&req);
// dont crash on EBUSY because it just means go again
if(err == NN_EBUSY) return err;
if(err) {
computer->state = NN_CRASHED;
nn_setErrorFromExit(computer, err);
@@ -1284,7 +1305,7 @@ nn_Exit nn_call(nn_Computer *computer, const char *address, const char *method)
if(nn_strcmp(c.address, address) != 0) continue;
// minimum cost of a component call
nn_callCost(computer, 1);
if(computer->callBudget > 0) computer->callBudget--;
nn_ComponentRequest req;
req.typeUserdata = c.ctype->userdata;
@@ -1340,10 +1361,32 @@ void nn_resetCallBudget(nn_Computer *computer) {
}
bool nn_componentsOverused(nn_Computer *computer) {
for(size_t i = 0; i < computer->componentLen; i++) {
if(computer->components[i].budgetUsed >= NN_COMPONENT_CALLBUDGET) return true;
}
if(computer->totalCallBudget == 0) return false;
return computer->callBudget == 0;
}
void nn_resetComponentBudgets(nn_Computer *computer) {
for(size_t i = 0; i < computer->componentLen; i++) {
computer->components[i].budgetUsed = 0;
}
}
bool nn_costComponent(nn_Computer *computer, const char *address, double perTick) {
return nn_costComponentN(computer, address, 1, perTick);
}
bool nn_costComponentN(nn_Computer *computer, const char *address, double amount, double perTick) {
for(size_t i = 0; i < computer->componentLen; i++) {
nn_Component *c = &computer->components[i];
if(nn_strcmp(c->address, address) != 0) continue;
c->budgetUsed += (NN_COMPONENT_CALLBUDGET * amount) / perTick;
return c->budgetUsed >= NN_COMPONENT_CALLBUDGET;
}
return false;
}
bool nn_checkstack(nn_Computer *computer, size_t amount) {
return computer->stackSize + amount <= NN_MAX_STACK;
}
@@ -1647,6 +1690,7 @@ typedef struct nn_EEPROM_state {
nn_Universe *universe;
nn_EEPROM eeprom;
void *userdata;
nn_EEPROMHandler *handler;
} nn_EEPROM_state;
typedef struct nn_VEEPROM_state {
@@ -1685,7 +1729,7 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
return NN_OK;
case NN_COMP_DEINIT:
ereq.action = NN_EEPROM_DROP;
return state->eeprom.handler(&ereq);
return state->handler(&ereq);
case NN_COMP_ENABLED:
req->methodEnabled = true;
return NN_OK;
@@ -1698,19 +1742,20 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
}
if(nn_strcmp(method, "isReadOnly") == 0) {
ereq.action = NN_EEPROM_ISREADONLY;
nn_Exit e = state->eeprom.handler(&ereq);
nn_Exit e = state->handler(&ereq);
if(e) return e;
req->returnCount = 1;
return nn_pushbool(computer, ereq.buflen != 0);
}
if(nn_strcmp(method, "getChecksum") == 0) {
nn_costComponent(computer, req->compAddress, 1);
// yup, on-stack.
// Perhaps in the future we'll make it heap-allocated.
char buf[state->eeprom.size];
ereq.action = NN_EEPROM_GET;
ereq.buf = buf;
ereq.buflen = state->eeprom.size;
nn_Exit e = state->eeprom.handler(&ereq);
nn_Exit e = state->handler(&ereq);
if(e) return e;
unsigned int chksum = nn_computeCRC32(buf, ereq.buflen);
char encoded[8];
@@ -1719,6 +1764,7 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
return nn_pushlstring(computer, encoded, 8);
}
if(nn_strcmp(method, "makeReadonly") == 0) {
nn_costComponent(computer, req->compAddress, 1);
// 1st argument is a string, which is the checksum we're meant to have
if(nn_getstacksize(computer) < 1) {
nn_setError(computer, "bad argument #1 (string expected)");
@@ -1740,7 +1786,7 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
ereq.action = NN_EEPROM_GET;
ereq.buf = buf;
ereq.buflen = state->eeprom.size;
nn_Exit e = state->eeprom.handler(&ereq);
nn_Exit e = state->handler(&ereq);
if(e) return e;
unsigned int chksum = nn_computeCRC32(buf, ereq.buflen);
char encoded[8];
@@ -1751,23 +1797,26 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
}
ereq.action = NN_EEPROM_MAKEREADONLY;
e = state->eeprom.handler(&ereq);
e = state->handler(&ereq);
if(e) return e;
req->returnCount = 1;
return nn_pushbool(computer, true);
}
if(nn_strcmp(method, "getLabel") == 0) {
nn_costComponent(computer, req->compAddress, 1);
char buf[NN_MAX_LABEL];
ereq.action = NN_EEPROM_GETLABEL;
ereq.buf = buf;
ereq.buflen = NN_MAX_LABEL;
nn_Exit e = state->eeprom.handler(&ereq);
nn_Exit e = state->handler(&ereq);
if(e) return e;
req->returnCount = 1;
if(ereq.buf == NULL) return nn_pushnull(computer);
return nn_pushlstring(computer, buf, ereq.buflen);
}
if(nn_strcmp(method, "setLabel") == 0) {
nn_costComponent(computer, req->compAddress, 1);
if(nn_getstacksize(computer) < 1) {
nn_setError(computer, "bad argument #1 (string expected)");
return NN_EBADCALL;
@@ -1784,46 +1833,50 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
ereq.action = NN_EEPROM_SETLABEL;
ereq.buf = buf;
ereq.buflen = len;
nn_Exit e = state->eeprom.handler(&ereq);
nn_Exit e = state->handler(&ereq);
if(e) return e;
req->returnCount = 1;
return nn_pushlstring(computer, buf, ereq.buflen);
}
if(nn_strcmp(method, "get") == 0) {
nn_costComponent(computer, req->compAddress, 1);
// yup, on-stack.
// Perhaps in the future we'll make it heap-allocated.
char buf[state->eeprom.size];
ereq.action = NN_EEPROM_GET;
ereq.buf = buf;
ereq.buflen = state->eeprom.size;
nn_Exit e = state->eeprom.handler(&ereq);
nn_Exit e = state->handler(&ereq);
if(e) return e;
req->returnCount = 1;
return nn_pushlstring(computer, buf, ereq.buflen);
}
if(nn_strcmp(method, "getData") == 0) {
nn_costComponent(computer, req->compAddress, 1);
// yup, on-stack.
// Perhaps in the future we'll make it heap-allocated.
char buf[state->eeprom.dataSize];
ereq.action = NN_EEPROM_GETDATA;
ereq.buf = buf;
ereq.buflen = state->eeprom.dataSize;
nn_Exit e = state->eeprom.handler(&ereq);
nn_Exit e = state->handler(&ereq);
if(e) return e;
req->returnCount = 1;
return nn_pushlstring(computer, buf, ereq.buflen);
}
if(nn_strcmp(method, "getArchitecture") == 0) {
nn_costComponent(computer, req->compAddress, 1);
char buf[NN_MAX_ARCHNAME];
ereq.action = NN_EEPROM_GETARCH;
ereq.buf = buf;
ereq.buflen = NN_MAX_ARCHNAME;
nn_Exit e = state->eeprom.handler(&ereq);
nn_Exit e = state->handler(&ereq);
if(e) return e;
req->returnCount = 1;
return nn_pushlstring(computer, buf, ereq.buflen);
}
if(nn_strcmp(method, "set") == 0) {
nn_costComponent(computer, req->compAddress, 1);
if(nn_getstacksize(computer) < 1) {
nn_setError(computer, "bad argument #1 (string expected)");
return NN_EBADCALL;
@@ -1842,9 +1895,10 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
// DO NOT MODIFY IT!!!!
ereq.buf = (char*)s;
ereq.buflen = len;
return state->eeprom.handler(&ereq);
return state->handler(&ereq);
}
if(nn_strcmp(method, "setData") == 0) {
nn_costComponent(computer, req->compAddress, 1);
if(nn_getstacksize(computer) < 1) {
nn_setError(computer, "bad argument #1 (string expected)");
return NN_EBADCALL;
@@ -1863,9 +1917,10 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
// DO NOT MODIFY IT!!!!
ereq.buf = (char*)s;
ereq.buflen = len;
return state->eeprom.handler(&ereq);
return state->handler(&ereq);
}
if(nn_strcmp(method, "setArchitecture") == 0) {
nn_costComponent(computer, req->compAddress, 1);
if(nn_getstacksize(computer) < 1) {
nn_setError(computer, "bad argument #1 (string expected)");
return NN_EBADCALL;
@@ -1884,20 +1939,30 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
// DO NOT MODIFY IT!!!!
ereq.buf = (char*)s;
ereq.buflen = len;
return state->eeprom.handler(&ereq);
return state->handler(&ereq);
}
return NN_OK;
}
return NN_OK;
}
nn_ComponentType *nn_createEEPROM(nn_Universe *universe, const nn_EEPROM *eeprom, void *userdata) {
nn_EEPROM nn_defaultEEPROM = (nn_EEPROM) {
.size = 4 * NN_KiB,
.dataSize = 256,
.readEnergyCost = 10,
.writeEnergyCost = 100,
.readDataEnergyCost = 10,
.writeDataEnergyCost = 50,
};
nn_ComponentType *nn_createEEPROM(nn_Universe *universe, const nn_EEPROM *eeprom, nn_EEPROMHandler *handler, void *userdata) {
nn_Context ctx = universe->ctx;
nn_EEPROM_state *state = nn_alloc(&ctx, sizeof(*state));
if(state == NULL) return NULL;
state->universe = universe;
state->eeprom = *eeprom;
state->userdata = userdata;
state->handler = handler;
const nn_Method methods[] = {
{"getSize", "function(): number - Get the storage capacity of the EEPROM.", NN_DIRECT},
{"getDataSize", "function(): number - Get the storage capacity of the EEPROM data.", NN_DIRECT},
@@ -2006,9 +2071,7 @@ nn_ComponentType *nn_createVEEPROM(nn_Universe *universe, const nn_EEPROM *eepro
state->archlen = archlen;
nn_memcpy(state->arch, vmem->arch, sizeof(char) * archlen);
nn_EEPROM neeprom = *eeprom;
neeprom.handler = nn_veeprom_handler;
nn_ComponentType *ty = nn_createEEPROM(universe, &neeprom, state);
nn_ComponentType *ty = nn_createEEPROM(universe, eeprom, nn_veeprom_handler, state);
if(ty == NULL) goto fail;
return ty;
fail:;
@@ -2018,3 +2081,395 @@ fail:;
nn_free(&ctx, state, sizeof(*state));
return NULL;
}
typedef struct nn_Filesystem_state {
nn_Universe *universe;
void *userdata;
nn_FilesystemHandler *handler;
nn_Filesystem fs;
} nn_Filesystem_state;
nn_Filesystem nn_defaultFilesystems[4] = {
(nn_Filesystem) {
.spaceTotal = 1 * NN_MiB,
.readsPerTick = 4,
.writesPerTick = 2,
.dataEnergyCost = 256.0 / NN_MiB,
},
(nn_Filesystem) {
.spaceTotal = 2 * NN_MiB,
.readsPerTick = 4,
.writesPerTick = 2,
.dataEnergyCost = 512.0 / NN_MiB,
},
(nn_Filesystem) {
.spaceTotal = 4 * NN_MiB,
.readsPerTick = 7,
.writesPerTick = 3,
.dataEnergyCost = 1024.0 / NN_MiB,
},
(nn_Filesystem) {
.spaceTotal = 8 * NN_MiB,
.readsPerTick = 13,
.writesPerTick = 5,
.dataEnergyCost = 2048.0 / NN_MiB,
},
};
nn_Filesystem nn_defaultFloppy = (nn_Filesystem) {
.spaceTotal = 512 * NN_KiB,
.readsPerTick = 1,
.writesPerTick = 1,
.dataEnergyCost = 8.0 / NN_MiB,
};
nn_Exit nn_filesystem_handler(nn_ComponentRequest *req) {
nn_Filesystem_state *state = req->typeUserdata;
void *instance = req->compUserdata;
// NULL for FREETYPE
nn_Computer *computer = req->computer;
nn_Context ctx = state->universe->ctx;
nn_FilesystemRequest fsreq;
fsreq.userdata = state->userdata;
fsreq.instance = instance;
fsreq.computer = computer;
fsreq.fsConf = &state->fs;
const char *method = req->methodCalled;
nn_Exit err;
switch(req->action) {
case NN_COMP_FREETYPE:
nn_free(&ctx, state, sizeof(*state));
break;
case NN_COMP_INIT:
return NN_OK;
case NN_COMP_DEINIT:
fsreq.action = NN_FS_DROP;
return state->handler(&fsreq);
case NN_COMP_ENABLED:
req->methodEnabled = true;
return NN_OK;
case NN_COMP_CALL:
if(nn_strcmp(method, "spaceTotal") == 0) {
req->returnCount = 1;
return nn_pushnumber(computer, state->fs.spaceTotal);
}
if(nn_strcmp(method, "spaceUsed") == 0) {
fsreq.action = NN_FS_SPACEUSED;
err = state->handler(&fsreq);
if(err) return err;
req->returnCount = 1;
return nn_pushnumber(computer, fsreq.size);
}
if(nn_strcmp(method, "isReadOnly") == 0) {
fsreq.action = NN_FS_ISREADONLY;
err = state->handler(&fsreq);
if(err) return err;
req->returnCount = 1;
return nn_pushbool(computer, fsreq.size != 0);
}
if(nn_strcmp(method, "getLabel") == 0) {
char buf[NN_MAX_LABEL];
fsreq.action = NN_FS_GETLABEL;
fsreq.strarg1 = buf;
fsreq.strarg1len = NN_MAX_LABEL;
err = state->handler(&fsreq);
if(err) return err;
req->returnCount = 1;
if(fsreq.strarg1 == NULL) return nn_pushnull(computer);
return nn_pushlstring(computer, buf, fsreq.strarg1len);
}
if(nn_strcmp(method, "setLabel") == 0) {
if(nn_getstacksize(computer) < 1) {
nn_setError(computer, "bad argument #1 (string expected)");
return NN_EBADCALL;
}
if(!nn_isstring(computer, 0)) {
nn_setError(computer, "bad argument #1 (string expected)");
return NN_EBADCALL;
}
fsreq.action = NN_FS_SETLABEL;
// DO NOT MODIFY THE BUFFER!!!
fsreq.strarg1 = (char *)nn_tolstring(computer, 0, &fsreq.strarg1len);
err = state->handler(&fsreq);
if(err) return err;
req->returnCount = 1;
if(fsreq.strarg1 == NULL) return nn_pushnull(computer);
return nn_pushlstring(computer, fsreq.strarg1, fsreq.strarg1len);
}
if(nn_strcmp(method, "open") == 0) {
if(nn_getstacksize(computer) < 1) {
nn_setError(computer, "bad argument #1 (string expected)");
return NN_EBADCALL;
}
if(!nn_isstring(computer, 0)) {
nn_setError(computer, "bad argument #1 (string expected)");
return NN_EBADCALL;
}
if(nn_getstacksize(computer) < 2) {
err = nn_pushstring(computer, "r");
if(err) return err;
}
if(!nn_isstring(computer, 1)) {
nn_setError(computer, "bad argument #2 (string expected)");
return NN_EBADCALL;
}
size_t pathlen;
const char *path = nn_tolstring(computer, 0, &pathlen);
if(pathlen >= NN_MAX_PATH) {
nn_setError(computer, "path too long");
return NN_EBADCALL;
}
char truepath[NN_MAX_PATH];
nn_simplifyPath(path, truepath);
size_t modelen;
const char *mode = nn_tolstring(computer, 1, &modelen);
fsreq.action = NN_FS_OPEN;
fsreq.strarg1 = truepath;
fsreq.strarg1len = nn_strlen(truepath);
fsreq.strarg2 = (char *)mode;
fsreq.strarg2len = modelen;
err = state->handler(&fsreq);
if(err) return err;
req->returnCount = 1;
return nn_pushnumber(computer, fsreq.fd);
}
if(nn_strcmp(method, "close") == 0) {
if(nn_getstacksize(computer) < 1) {
nn_setError(computer, "bad argument #1 (integer expected)");
return NN_EBADCALL;
}
if(!nn_isnumber(computer, 0)) {
nn_setError(computer, "bad argument #1 (integer expected)");
return NN_EBADCALL;
}
fsreq.fd = nn_tonumber(computer, 0);
fsreq.action = NN_FS_CLOSE;
return state->handler(&fsreq);
}
if(nn_strcmp(method, "read") == 0) {
if(nn_getstacksize(computer) < 1) {
nn_setError(computer, "bad argument #1 (integer expected)");
return NN_EBADCALL;
}
if(!nn_isnumber(computer, 0)) {
nn_setError(computer, "bad argument #1 (integer expected)");
return NN_EBADCALL;
}
if(nn_getstacksize(computer) < 2) {
err = nn_pushnumber(computer, NN_MAX_READ);
if(err) return err;
}
if(!nn_isnumber(computer, 1)) {
nn_setError(computer, "bad argument #2 (integer expected)");
return NN_EBADCALL;
}
fsreq.action = NN_FS_READ;
fsreq.fd = nn_tonumber(computer, 0);
size_t requested = nn_tonumber(computer, 1);
if(requested > NN_MAX_READ) requested = NN_MAX_READ;
char buf[requested];
fsreq.strarg1 = buf;
fsreq.strarg1len = requested;
err = state->handler(&fsreq);
if(err) return err;
req->returnCount = 1;
if(fsreq.strarg1 == NULL) return nn_pushnull(computer);
return nn_pushlstring(computer, fsreq.strarg1, fsreq.strarg1len);
}
if(nn_strcmp(method, "write") == 0) {
if(nn_getstacksize(computer) < 1) {
nn_setError(computer, "bad argument #1 (integer expected)");
return NN_EBADCALL;
}
if(!nn_isnumber(computer, 0)) {
nn_setError(computer, "bad argument #1 (integer expected)");
return NN_EBADCALL;
}
if(nn_getstacksize(computer) < 2) {
nn_setError(computer, "bad argument #2 (string expected)");
return NN_EBADCALL;
}
if(!nn_isnumber(computer, 1)) {
nn_setError(computer, "bad argument #2 (string expected)");
return NN_EBADCALL;
}
fsreq.action = NN_FS_WRITE;
fsreq.fd = nn_tonumber(computer, 0);
fsreq.strarg1 = (char *)nn_tolstring(computer, 1, &fsreq.strarg1len);
err = state->handler(&fsreq);
if(err) return err;
req->returnCount = 1;
return nn_pushbool(computer, true);
}
if(nn_strcmp(method, "seek") == 0) {
if(nn_getstacksize(computer) < 1) {
nn_setError(computer, "bad argument #1 (integer expected)");
return NN_EBADCALL;
}
if(!nn_isnumber(computer, 0)) {
nn_setError(computer, "bad argument #1 (integer expected)");
return NN_EBADCALL;
}
if(nn_getstacksize(computer) < 2) {
err = nn_pushstring(computer, "cur");
if(err) return err;
}
if(!nn_isstring(computer, 1)) {
nn_setError(computer, "bad argument #2 (string expected)");
return NN_EBADCALL;
}
if(nn_getstacksize(computer) < 2) {
err = nn_pushnumber(computer, 0);
if(err) return err;
}
if(!nn_isnumber(computer, 2)) {
nn_setError(computer, "bad argument #2 (string expected)");
return NN_EBADCALL;
}
fsreq.action = NN_FS_SEEK;
fsreq.fd = nn_tonumber(computer, 0);
const char *whence = nn_tostring(computer, 1);
fsreq.off = nn_tonumber(computer, 2);
if(nn_strcmp(whence, "set") == 0) {
fsreq.whence = NN_SEEK_SET;
} else if(nn_strcmp(whence, "cur") == 0) {
fsreq.whence = NN_SEEK_CUR;
} else if(nn_strcmp(whence, "end") == 0) {
fsreq.whence = NN_SEEK_END;
} else {
nn_setError(computer, "bad seek whence");
return NN_EBADCALL;
}
err = state->handler(&fsreq);
return nn_pushnumber(computer, fsreq.off);
}
if(nn_strcmp(method, "list") == 0) {
if(nn_getstacksize(computer) < 1) {
nn_setError(computer, "bad argument #1 (string expected)");
return NN_EBADCALL;
}
if(!nn_isstring(computer, 0)) {
nn_setError(computer, "bad argument #1 (string expected)");
return NN_EBADCALL;
}
char truepath[NN_MAX_PATH];
size_t pathlen;
const char *path = nn_tolstring(computer, 0, &pathlen);
if(pathlen >= NN_MAX_PATH) {
nn_setError(computer, "path too long");
return NN_EBADCALL;
}
nn_simplifyPath(path, truepath);
int dirfd;
fsreq.action = NN_FS_OPENDIR;
fsreq.strarg1 = truepath;
fsreq.strarg1len = nn_strlen(truepath);
err = state->handler(&fsreq);
if(err) return err;
dirfd = fsreq.fd;
// this sucks hard
size_t entryCount = 0;
while(1) {
char entry[NN_MAX_PATH];
fsreq.action = NN_FS_READDIR;
fsreq.fd = dirfd;
fsreq.strarg1 = entry;
fsreq.strarg1len = NN_MAX_PATH;
err = state->handler(&fsreq);
if(err) goto list_fail;
if(fsreq.strarg1 == NULL) break;
if(fsreq.strarg1len == 1 && entry[0] == '.') continue;
if(fsreq.strarg1len == 2 && entry[0] == '.' && entry[1] == '.') continue;
err = nn_pushlstring(computer, entry, fsreq.strarg1len);
if(err) goto list_fail;
entryCount++;
}
err = nn_pusharraytable(computer, entryCount);
if(err) goto list_fail;
req->returnCount = 1;
fsreq.action = NN_FS_CLOSEDIR;
fsreq.fd = dirfd;
state->handler(&fsreq);
return NN_OK;
list_fail:
fsreq.action = NN_FS_CLOSEDIR;
fsreq.fd = dirfd;
state->handler(&fsreq);
return err;
}
if(nn_strcmp(method, "exists") == 0) {
if(nn_getstacksize(computer) < 1) {
nn_setError(computer, "bad argument #1 (string expected)");
return NN_EBADCALL;
}
if(!nn_isstring(computer, 0)) {
nn_setError(computer, "bad argument #1 (string expected)");
return NN_EBADCALL;
}
char truepath[NN_MAX_PATH];
size_t pathlen;
const char *path = nn_tolstring(computer, 0, &pathlen);
if(pathlen >= NN_MAX_PATH) {
nn_setError(computer, "path too long");
return NN_EBADCALL;
}
nn_simplifyPath(path, truepath);
fsreq.action = NN_FS_EXISTS;
fsreq.strarg1 = truepath;
fsreq.strarg1len = nn_strlen(truepath);
err = state->handler(&fsreq);
if(err) return err;
req->returnCount = 1;
return nn_pushbool(computer, fsreq.size != 0);
}
}
return NN_OK;
}
nn_ComponentType *nn_createFilesystem(nn_Universe *universe, const nn_Filesystem *filesystem, nn_FilesystemHandler *handler, void *userdata) {
nn_Context ctx = universe->ctx;
nn_Filesystem_state *state = nn_alloc(&ctx, sizeof(*state));
if(state == NULL) return NULL;
state->universe = universe;
state->userdata = userdata;
state->handler = handler;
state->fs = *filesystem;
const nn_Method methods[] = {
{"spaceTotal", "function(): number - Get the storage capacity of the filesystem.", NN_DIRECT},
{"spaceUsed", "function(): number - Get the space used by the files on the drive.", NN_DIRECT},
{"isReadOnly", "function(): boolean - Returns whether the drive is read-only.", NN_DIRECT},
{"getLabel", "function(): string - Get the filesystem label.", NN_INDIRECT},
{"setLabel", "function(label: string): string - Set the filesystem label and return what was actually set, which may be truncated.", NN_INDIRECT},
{"open", "function(path: string, mode?: string = 'r'): number - Open a file. Valid modes are 'r' (read-only), 'w' (write-only) and 'a' (append-only).", NN_INDIRECT},
{"close", "function(fd: number) - Closes a file.", NN_INDIRECT},
{"read", "function(fd: number, count?: number): string? - Reads part of a file. If there is no more data, nothing is returned.", NN_INDIRECT},
{"write", "function(fd: number, data: string): boolean - Writes the data to a file. Returns true on success.", NN_INDIRECT},
{"seek", "function(fd: number, whence: string, offset: number): number - Seeks a file. Valid whences are 'set' (relative to start), 'cur' (relative to current), 'end' (relative to EoF, backwards). Returns the new position.", NN_INDIRECT},
{"list", "function(path: string): string[] - Returns the names of the entries inside of the directory. Directories get a / appended to their names.", NN_INDIRECT},
{"exists", "function(path: string): boolean - Checks if there exists an entry at the specified path.", NN_INDIRECT},
{"size", "function(path: string) - Gets the size of the entry at the specified path.", NN_INDIRECT},
{"remove", "function(path: string): boolean - Removes the entry at the specified path.", NN_INDIRECT},
{"rename", "function(from: string, to: string): boolean - Renames or moves an entry to a new location.", NN_INDIRECT},
{"isDirectory", "function(path: string): boolean - Checks if the entry at the specified path is a directory.", NN_INDIRECT},
{"lastModified", "function(path: string): number - Returns the UNIX timestamp of the time the entry was last modified. This is stored in milliseconds, but is always a multiple of 1000.", NN_INDIRECT},
{"makeDirectory", "function(path: string): boolean - Creates a directory. Creates parent directories if necessary.", NN_INDIRECT},
{NULL, NULL, NN_INDIRECT},
};
nn_ComponentType *t = nn_createComponentType(universe, "filesystem", state, methods, nn_filesystem_handler);
if(t == NULL) {
nn_free(&ctx, state, sizeof(*state));
return NULL;
}
return t;
}