diff --git a/src/java/me/topchetoeu/jscript/lib/FileLib.java b/src/java/me/topchetoeu/jscript/lib/FileLib.java index 02b31f5..4bcf4ea 100644 --- a/src/java/me/topchetoeu/jscript/lib/FileLib.java +++ b/src/java/me/topchetoeu/jscript/lib/FileLib.java @@ -10,12 +10,12 @@ import me.topchetoeu.jscript.utils.interop.WrapperName; @WrapperName("File") public class FileLib { - public final File file; + public final File fd; @Expose public PromiseLib __pointer(Arguments args) { return PromiseLib.await(args.ctx, () -> { try { - return file.seek(0, 1); + return fd.seek(0, 1); } catch (FilesystemException e) { throw e.toEngineException(); } }); @@ -23,9 +23,9 @@ public class FileLib { @Expose public PromiseLib __length(Arguments args) { return PromiseLib.await(args.ctx, () -> { try { - long curr = file.seek(0, 1); - long res = file.seek(0, 2); - file.seek(curr, 0); + long curr = fd.seek(0, 1); + long res = fd.seek(0, 2); + fd.seek(curr, 0); return res; } catch (FilesystemException e) { throw e.toEngineException(); } @@ -38,8 +38,8 @@ public class FileLib { try { var buff = new byte[n]; var res = new ArrayValue(); - int resI = file.read(buff); - + int resI = fd.read(buff); + for (var i = resI - 1; i >= 0; i--) res.set(args.ctx, i, (int)buff[i]); return res; } @@ -53,7 +53,7 @@ public class FileLib { var res = new byte[val.size()]; for (var i = 0; i < val.size(); i++) res[i] = (byte)Values.toNumber(args.ctx, val.get(i)); - file.write(res); + fd.write(res); return null; } @@ -62,7 +62,7 @@ public class FileLib { } @Expose public PromiseLib __close(Arguments args) { return PromiseLib.await(args.ctx, () -> { - file.close(); + fd.close(); return null; }); } @@ -72,13 +72,13 @@ public class FileLib { var whence = args.getInt(1); try { - return file.seek(ptr, whence); + return fd.seek(ptr, whence); } catch (FilesystemException e) { throw e.toEngineException(); } }); } - public FileLib(File file) { - this.file = file; + public FileLib(File fd) { + this.fd = fd; } } diff --git a/src/java/me/topchetoeu/jscript/lib/FilesystemLib.java b/src/java/me/topchetoeu/jscript/lib/FilesystemLib.java index e3eddb4..c5162fd 100644 --- a/src/java/me/topchetoeu/jscript/lib/FilesystemLib.java +++ b/src/java/me/topchetoeu/jscript/lib/FilesystemLib.java @@ -8,13 +8,14 @@ import me.topchetoeu.jscript.core.engine.Context; import me.topchetoeu.jscript.core.engine.values.ObjectValue; import me.topchetoeu.jscript.core.engine.values.Values; import me.topchetoeu.jscript.core.exceptions.EngineException; +import me.topchetoeu.jscript.utils.filesystem.ActionType; import me.topchetoeu.jscript.utils.filesystem.EntryType; +import me.topchetoeu.jscript.utils.filesystem.ErrorReason; import me.topchetoeu.jscript.utils.filesystem.File; import me.topchetoeu.jscript.utils.filesystem.FileStat; import me.topchetoeu.jscript.utils.filesystem.Filesystem; import me.topchetoeu.jscript.utils.filesystem.FilesystemException; import me.topchetoeu.jscript.utils.filesystem.Mode; -import me.topchetoeu.jscript.utils.filesystem.FilesystemException.FSCode; import me.topchetoeu.jscript.utils.interop.Arguments; import me.topchetoeu.jscript.utils.interop.Expose; import me.topchetoeu.jscript.utils.interop.ExposeField; @@ -50,11 +51,10 @@ public class FilesystemLib { try { if (fs.stat(path).type != EntryType.FILE) { - throw new FilesystemException(path, FSCode.NOT_FILE); + throw new FilesystemException(ErrorReason.DOESNT_EXIST, "Not a file").setAction(ActionType.OPEN); } - var file = fs.open(path, _mode); - return new FileLib(file); + return new FileLib(fs.open(path, _mode)); } catch (FilesystemException e) { throw e.toEngineException(); } }); @@ -75,7 +75,7 @@ public class FilesystemLib { var path = fs.normalize(args.getString(0)); if (fs.stat(path).type != EntryType.FOLDER) { - throw new FilesystemException(path, FSCode.NOT_FOLDER); + throw new FilesystemException(ErrorReason.DOESNT_EXIST, "Not a directory").setAction(ActionType.OPEN); } file = fs.open(path, Mode.READ); diff --git a/src/java/me/topchetoeu/jscript/lib/Internals.java b/src/java/me/topchetoeu/jscript/lib/Internals.java index 127fa9a..1f08cc5 100644 --- a/src/java/me/topchetoeu/jscript/lib/Internals.java +++ b/src/java/me/topchetoeu/jscript/lib/Internals.java @@ -152,6 +152,8 @@ public class Internals { glob.define(null, "Encoding", false, wp.getNamespace(EncodingLib.class)); glob.define(null, "Filesystem", false, wp.getNamespace(FilesystemLib.class)); + glob.define(false, wp.getConstr(FileLib.class)); + glob.define(false, wp.getConstr(DateLib.class)); glob.define(false, wp.getConstr(ObjectLib.class)); glob.define(false, wp.getConstr(FunctionLib.class)); diff --git a/src/java/me/topchetoeu/jscript/utils/JScriptRepl.java b/src/java/me/topchetoeu/jscript/utils/JScriptRepl.java index ae31fd8..4b1cba6 100644 --- a/src/java/me/topchetoeu/jscript/utils/JScriptRepl.java +++ b/src/java/me/topchetoeu/jscript/utils/JScriptRepl.java @@ -24,6 +24,7 @@ import me.topchetoeu.jscript.utils.filesystem.MemoryFilesystem; import me.topchetoeu.jscript.utils.filesystem.Mode; import me.topchetoeu.jscript.utils.filesystem.PhysicalFilesystem; import me.topchetoeu.jscript.utils.filesystem.RootFilesystem; +import me.topchetoeu.jscript.utils.filesystem.STDFilesystem; import me.topchetoeu.jscript.utils.modules.ModuleRepo; import me.topchetoeu.jscript.utils.permissions.PermissionsManager; import me.topchetoeu.jscript.utils.permissions.PermissionsProvider; @@ -108,6 +109,7 @@ public class JScriptRepl { var fs = new RootFilesystem(PermissionsProvider.get(environment)); fs.protocols.put("temp", new MemoryFilesystem(Mode.READ_WRITE)); fs.protocols.put("file", new PhysicalFilesystem(".")); + fs.protocols.put("std", STDFilesystem.ofStd(System.in, System.out, System.err)); environment.add(PermissionsProvider.ENV_KEY, PermissionsManager.ALL_PERMS); environment.add(Filesystem.ENV_KEY, fs); diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/ActionType.java b/src/java/me/topchetoeu/jscript/utils/filesystem/ActionType.java new file mode 100644 index 0000000..323fadb --- /dev/null +++ b/src/java/me/topchetoeu/jscript/utils/filesystem/ActionType.java @@ -0,0 +1,28 @@ +package me.topchetoeu.jscript.utils.filesystem; + +public enum ActionType { + UNKNOWN(0, "An operation performed upon", "An operation was performed upon"), + READ(1, "Reading from", "Read from"), + WRITE(2, "Writting to", "Wrote to"), + SEEK(3, "Seeking in", "Sought in"), + CLOSE(4, "Closing", "Closed"), + STAT(5, "Stat of", "Statted"), + OPEN(6, "Opening", "Opened"), + CREATE(7, "Creating", "Created"), + DELETE(8, "Deleting", "Deleted"), + CLOSE_FS(9, "Closing filesystem", "Closed filesystem"); + + public final int code; + public final String continuous, past; + + public String readable(boolean usePast) { + if (usePast) return past; + else return continuous; + } + + private ActionType(int code, String continuous, String past) { + this.code = code; + this.continuous = continuous; + this.past = past; + } +} diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/BaseFile.java b/src/java/me/topchetoeu/jscript/utils/filesystem/BaseFile.java new file mode 100644 index 0000000..5fc9c34 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/utils/filesystem/BaseFile.java @@ -0,0 +1,59 @@ +package me.topchetoeu.jscript.utils.filesystem; + +public abstract class BaseFile implements File { + private T handle; + private Mode mode; + + protected final T handle() { + return handle; + } + + protected abstract int onRead(byte[] buff); + protected abstract void onWrite(byte[] buff); + protected abstract long onSeek(long offset, int pos); + protected abstract boolean onClose(); + + @Override public int read(byte[] buff) { + try { + if (handle == null) throw new FilesystemException(ErrorReason.CLOSED); + if (!mode.readable) throw new FilesystemException(ErrorReason.NO_PERMISSION, "File not open for reading."); + return onRead(buff); + } + catch (FilesystemException e) { throw e.setAction(ActionType.READ); } + } + @Override public void write(byte[] buff) { + try { + if (handle == null) throw new FilesystemException(ErrorReason.CLOSED); + if (!mode.writable) throw new FilesystemException(ErrorReason.NO_PERMISSION, "File not open for writting."); + onWrite(buff); + } + catch (FilesystemException e) { throw e.setAction(ActionType.WRITE); } + } + @Override public long seek(long offset, int pos) { + try { + if (handle == null) throw new FilesystemException(ErrorReason.CLOSED); + if (!mode.writable) throw new FilesystemException(ErrorReason.NO_PERMISSION, "File not open for seeking."); + return onSeek(offset, pos); + } + catch (FilesystemException e) { throw e.setAction(ActionType.SEEK); } + } + @Override public boolean close() { + if (handle != null) { + try { + var res = onClose(); + handle = null; + mode = Mode.NONE; + return res; + } + catch (FilesystemException e) { throw e.setAction(ActionType.CLOSE); } + } + else return false; + } + + public BaseFile(T handle, Mode mode) { + this.mode = mode; + this.handle = handle; + + if (mode == Mode.NONE) this.handle = null; + } +} diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/ErrorReason.java b/src/java/me/topchetoeu/jscript/utils/filesystem/ErrorReason.java new file mode 100644 index 0000000..91cdc2b --- /dev/null +++ b/src/java/me/topchetoeu/jscript/utils/filesystem/ErrorReason.java @@ -0,0 +1,23 @@ +package me.topchetoeu.jscript.utils.filesystem; + +public enum ErrorReason { + UNKNOWN(0, "failed", false), + NO_PERMISSION(1, "is not allowed", false), + CLOSED(1, "that was closed", true), + UNSUPPORTED(2, "is not supported", false), + ILLEGAL_ARGS(3, "with illegal arguments", true), + DOESNT_EXIST(4, "that doesn't exist", true), + ALREADY_EXISTS(5, "that already exists", true), + ILLEGAL_PATH(6, "with illegal path", true), + NO_PARENT(7, "with a missing parent folder", true); + + public final int code; + public final boolean usePast; + public final String readable; + + private ErrorReason(int code, String readable, boolean usePast) { + this.code = code; + this.readable = readable; + this.usePast = usePast; + } +} diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/File.java b/src/java/me/topchetoeu/jscript/utils/filesystem/File.java index 561c93c..3017fc0 100644 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/File.java +++ b/src/java/me/topchetoeu/jscript/utils/filesystem/File.java @@ -3,51 +3,42 @@ package me.topchetoeu.jscript.utils.filesystem; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.Iterator; +import java.util.LinkedList; import me.topchetoeu.jscript.common.Buffer; -import me.topchetoeu.jscript.utils.LineWriter; -import me.topchetoeu.jscript.utils.filesystem.FilesystemException.FSCode; -import me.topchetoeu.jscript.utils.permissions.Permission; -import me.topchetoeu.jscript.utils.permissions.PermissionsProvider; - public interface File { - int read(byte[] buff); - void write(byte[] buff); - long seek(long offset, int pos); - void close(); + default int read(byte[] buff) { throw new FilesystemException(ErrorReason.UNSUPPORTED).setAction(ActionType.READ); } + default void write(byte[] buff) { throw new FilesystemException(ErrorReason.UNSUPPORTED).setAction(ActionType.WRITE); } + default long seek(long offset, int pos) { throw new FilesystemException(ErrorReason.UNSUPPORTED).setAction(ActionType.SEEK); } + default boolean close() { return false; } - default File wrap(String name, PermissionsProvider perms, Permission read, Permission write, Permission seek, Permission close) { - var self = this; + default byte[] readAll() { + var parts = new LinkedList(); + var buff = new byte[1024]; + var size = 0; - return new File() { - @Override public int read(byte[] buff) { - if (read != null && perms.hasPermission(read, name)) return self.read(buff); - else throw new FilesystemException(name, FSCode.NO_PERMISSIONS_R); - } - @Override public void write(byte[] buff) { - if (write != null && perms.hasPermission(write, name)) self.write(buff); - else throw new FilesystemException(name, FSCode.NO_PERMISSIONS_RW); - } - @Override public long seek(long offset, int pos) { - if (seek != null && perms.hasPermission(seek, name)) return self.seek(offset, pos); - else throw new FilesystemException(name, FSCode.NO_PERMISSIONS_R); - } - @Override public void close() { - if (close != null && perms.hasPermission(close, name)) self.close(); - else throw new FilesystemException(name, FSCode.NO_PERMISSIONS_R); - } - }; + while (true) { + var n = read(buff); + if (n == 0) break; + + parts.add(buff); + size += n; + } + + buff = new byte[size]; + + var i = 0; + + for (var part : parts) { + System.arraycopy(part, 0, buff, i, part.length); + i += part.length; + } + + return buff; } - default String readToString() { - long len = seek(0, 2); - if (len < 0) return null; - seek(0, 0); - - byte[] res = new byte[(int)len]; - len = read(res); - - return new String(res); + return new String(readAll()); } default String readLine() { var res = new Buffer(); @@ -66,74 +57,105 @@ public interface File { return new String(res.data()); } - public static File ofStream(String name, InputStream str) { + public static File ofStream(InputStream str) { return new File() { @Override public int read(byte[] buff) { try { - return str.read(buff); + try { return str.read(buff); } + catch (NullPointerException e) { throw new FilesystemException(ErrorReason.ILLEGAL_ARGS, e.getMessage()); } + catch (IOException e) { throw new FilesystemException(ErrorReason.UNKNOWN, e.getMessage()); } } - catch (IOException e) { - throw new FilesystemException(name, FSCode.NO_PERMISSIONS_R); - } - } - @Override public void write(byte[] buff) { - throw new FilesystemException(name, FSCode.NO_PERMISSIONS_RW); - } - @Override public long seek(long offset, int pos) { - throw new FilesystemException(name, FSCode.UNSUPPORTED_OPERATION); - } - @Override public void close() { - throw new FilesystemException(name, FSCode.UNSUPPORTED_OPERATION); + catch (FilesystemException e) { throw e.setAction(ActionType.READ); } } }; } - public static File ofStream(String name, OutputStream str) { + public static File ofStream(OutputStream str) { return new File() { - @Override public int read(byte[] buff) { - throw new FilesystemException(name, FSCode.NO_PERMISSIONS_R); - } @Override public void write(byte[] buff) { try { - str.write(buff); + try { str.write(buff); } + catch (NullPointerException e) {throw new FilesystemException(ErrorReason.ILLEGAL_ARGS, e.getMessage()); } + catch (IOException e) { throw new FilesystemException(ErrorReason.UNKNOWN, e.getMessage()); } } - catch (IOException e) { - throw new FilesystemException(name, FSCode.NO_PERMISSIONS_RW); - } - } - @Override public long seek(long offset, int pos) { - throw new FilesystemException(name, FSCode.UNSUPPORTED_OPERATION); - } - @Override public void close() { - throw new FilesystemException(name, FSCode.UNSUPPORTED_OPERATION); + catch (FilesystemException e) { throw e.setAction(ActionType.WRITE); } } }; } - public static File ofLineWriter(String name, LineWriter writer) { + public static File ofLineWriter(LineWriter writer) { var buff = new Buffer(); - return new File() { - @Override public int read(byte[] buff) { - throw new FilesystemException(name, FSCode.NO_PERMISSIONS_R); - } @Override public void write(byte[] val) { - for (var b : val) { - if (b == '\n') { - try { - writer.writeLine(new String(buff.data())); + try { + if (val == null) throw new FilesystemException(ErrorReason.ILLEGAL_ARGS, "Given buffer is null."); + + for (var b : val) { + if (b == '\n') { + try { + writer.writeLine(new String(buff.data())); + } + catch (IOException e) { + throw new FilesystemException(ErrorReason.UNKNOWN, e.getMessage()); + } } - catch (IOException e) { - throw new FilesystemException(name, FSCode.NO_PERMISSIONS_RW); + else buff.append(b); + } + } + catch (FilesystemException e) { throw e.setAction(ActionType.WRITE); } + } + }; + } + public static File ofLineReader(LineReader reader) { + return new File() { + private int offset = 0; + private byte[] prev = new byte[0]; + + @Override + public int read(byte[] buff) { + try { + if (buff == null) throw new FilesystemException(ErrorReason.ILLEGAL_ARGS, "Given buffer is null."); + var ptr = 0; + + while (true) { + if (prev == null) break; + if (offset >= prev.length) { + try { + var line = reader.readLine(); + + if (line == null) { + prev = null; + break; + } + else prev = (line + "\n").getBytes(); + + offset = 0; + } + catch (IOException e) { + throw new FilesystemException(ErrorReason.UNKNOWN, e.getMessage()); + } + } + + if (ptr + prev.length - offset > buff.length) { + var n = buff.length - ptr; + System.arraycopy(prev, offset, buff, ptr, buff.length - ptr); + offset += n; + ptr += n; + break; + } + else { + var n = prev.length - offset; + System.arraycopy(prev, offset, buff, ptr, n); + offset += n; + ptr += n; } } - else buff.append(b); + + return ptr; } - } - @Override public long seek(long offset, int pos) { - throw new FilesystemException(name, FSCode.UNSUPPORTED_OPERATION); - } - @Override public void close() { - throw new FilesystemException(name, FSCode.UNSUPPORTED_OPERATION); + catch (FilesystemException e) { throw e.setAction(ActionType.READ); } } }; } + public static File ofIterator(Iterator it) { + return ofLineReader(LineReader.ofIterator(it)); + } } \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/FileStat.java b/src/java/me/topchetoeu/jscript/utils/filesystem/FileStat.java index cd25646..903bde1 100644 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/FileStat.java +++ b/src/java/me/topchetoeu/jscript/utils/filesystem/FileStat.java @@ -5,6 +5,9 @@ public class FileStat { public final EntryType type; public FileStat(Mode mode, EntryType type) { + if (mode == Mode.NONE) type = EntryType.NONE; + if (type == EntryType.NONE) mode = Mode.NONE; + this.mode = mode; this.type = type; } diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/Filesystem.java b/src/java/me/topchetoeu/jscript/utils/filesystem/Filesystem.java index 134d6ff..8cac973 100644 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/Filesystem.java +++ b/src/java/me/topchetoeu/jscript/utils/filesystem/Filesystem.java @@ -6,10 +6,11 @@ import me.topchetoeu.jscript.core.engine.values.Symbol; public interface Filesystem { public static final Symbol ENV_KEY = Symbol.get("Environment.fs"); - String normalize(String... path); - File open(String path, Mode mode) throws FilesystemException; - void create(String path, EntryType type) throws FilesystemException; - FileStat stat(String path) throws FilesystemException; + default String normalize(String... path) { return Paths.normalize(path); } + default boolean create(String path, EntryType type) { throw new FilesystemException(ErrorReason.UNSUPPORTED).setAction(ActionType.CREATE); } + File open(String path, Mode mode); + FileStat stat(String path); + void close(); public static Filesystem get(Extensions exts) { return exts.get(ENV_KEY); diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/FilesystemException.java b/src/java/me/topchetoeu/jscript/utils/filesystem/FilesystemException.java index c4cc4d3..688280b 100644 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/FilesystemException.java +++ b/src/java/me/topchetoeu/jscript/utils/filesystem/FilesystemException.java @@ -1,57 +1,90 @@ package me.topchetoeu.jscript.utils.filesystem; +import java.util.ArrayList; + import me.topchetoeu.jscript.core.engine.values.Values; import me.topchetoeu.jscript.core.exceptions.EngineException; public class FilesystemException extends RuntimeException { - public static enum FSCode { - DOESNT_EXIST(0x1), - NOT_FILE(0x2), - NOT_FOLDER(0x3), - NO_PERMISSIONS_R(0x4), - NO_PERMISSIONS_RW(0x5), - FOLDER_NOT_EMPTY(0x6), - ALREADY_EXISTS(0x7), - FOLDER_EXISTS(0x8), - UNSUPPORTED_OPERATION(0x9); + public final ErrorReason reason; + public final String details; + private ActionType action; + private EntryType entry = EntryType.FILE; + private String path; - public final int code; + public FilesystemException setPath(String path) { + this.path = path; + return this; + } + public FilesystemException setAction(ActionType action) { + if (action == null) action = ActionType.UNKNOWN; - private FSCode(int code) { this.code = code; } + this.action = action; + return this; + } + public FilesystemException setEntry(EntryType entry) { + if (entry == null) entry = EntryType.NONE; + + this.entry = entry; + return this; } - public static final String[] MESSAGES = { - "How did we get here?", - "The file or folder '%s' doesn't exist or is inaccessible.", - "'%s' is not a file", - "'%s' is not a folder", - "No permissions to read '%s'", - "No permissions to write '%s'", - "Can't delete '%s', since it is a full folder.", - "'%s' already exists.", - "An unsupported operation was performed on the file '%s'." - }; - - public final String message, filename; - public final FSCode code; - - public FilesystemException(String message, String filename, FSCode code) { - super(code + ": " + String.format(message, filename)); - this.message = message; - this.code = code; - this.filename = filename; + public ActionType action() { + return action; } - public FilesystemException(String filename, FSCode code) { - super(code + ": " + String.format(MESSAGES[code.code], filename)); - this.message = MESSAGES[code.code]; - this.code = code; - this.filename = filename; + public String path() { + return path; + } + public EntryType entry() { + return entry; } public EngineException toEngineException() { var res = EngineException.ofError("IOError", getMessage()); - Values.setMember(null, res.value, "code", code); - Values.setMember(null, res.value, "filename", filename.toString()); + + Values.setMember(null, res.value, "action", action.code); + Values.setMember(null, res.value, "reason", reason.code); + Values.setMember(null, res.value, "path", path); + Values.setMember(null, res.value, "entry", entry.name); + if (details != null) Values.setMember(null, res.value, "details", details); + return res; } + + @Override public String getMessage() { + var parts = new ArrayList(10); + + path = String.join(" ", parts).trim(); + if (path.isEmpty()) path = null; + parts.clear(); + + parts.add(action == null ? "An action performed upon " : action.readable(reason.usePast)); + + if (entry == EntryType.FILE) parts.add("file"); + if (entry == EntryType.FOLDER) parts.add("folder"); + + if (path != null) parts.add(path); + + parts.add(reason.readable); + + var msg = String.join(" ", parts); + if (details != null) msg += ": " + details; + + return msg; + } + + public FilesystemException(ErrorReason type, String details) { + super(); + if (type == null) type = ErrorReason.UNKNOWN; + + this.details = details; + this.reason = type; + } + public FilesystemException(ErrorReason type) { + this(type, null); + } + public FilesystemException() { + this(null); + } + } diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/HandleManager.java b/src/java/me/topchetoeu/jscript/utils/filesystem/HandleManager.java new file mode 100644 index 0000000..c6dbacb --- /dev/null +++ b/src/java/me/topchetoeu/jscript/utils/filesystem/HandleManager.java @@ -0,0 +1,32 @@ +package me.topchetoeu.jscript.utils.filesystem; + +import java.util.HashSet; +import java.util.Set; + +public class HandleManager { + private Set files = new HashSet<>(); + + public File put(File val) { + var handle = new File() { + @Override public int read(byte[] buff) { + return val.read(buff); + } + @Override public void write(byte[] buff) { + val.write(buff); + } + @Override public long seek(long offset, int pos) { + return val.seek(offset, pos); + } + @Override public boolean close() { + return files.remove(this) && val.close(); + } + }; + files.add(handle); + return handle; + } + public void close() { + while (!files.isEmpty()) { + files.stream().findFirst().get().close(); + } + } +} diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/LineReader.java b/src/java/me/topchetoeu/jscript/utils/filesystem/LineReader.java new file mode 100644 index 0000000..6d84450 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/utils/filesystem/LineReader.java @@ -0,0 +1,16 @@ +package me.topchetoeu.jscript.utils.filesystem; + +import java.io.IOException; +import java.util.Iterator; + + +public interface LineReader { + String readLine() throws IOException; + + public static LineReader ofIterator(Iterator it) { + return () -> { + if (it.hasNext()) return it.next(); + else return null; + }; + } +} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/utils/LineWriter.java b/src/java/me/topchetoeu/jscript/utils/filesystem/LineWriter.java similarity index 70% rename from src/java/me/topchetoeu/jscript/utils/LineWriter.java rename to src/java/me/topchetoeu/jscript/utils/filesystem/LineWriter.java index 5ad2836..501631a 100644 --- a/src/java/me/topchetoeu/jscript/utils/LineWriter.java +++ b/src/java/me/topchetoeu/jscript/utils/filesystem/LineWriter.java @@ -1,4 +1,4 @@ -package me.topchetoeu.jscript.utils; +package me.topchetoeu.jscript.utils.filesystem; import java.io.IOException; diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/ListFile.java b/src/java/me/topchetoeu/jscript/utils/filesystem/ListFile.java deleted file mode 100644 index 02d4427..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/ListFile.java +++ /dev/null @@ -1,73 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -import java.io.IOException; -import java.util.Iterator; -import java.util.stream.Stream; - -import me.topchetoeu.jscript.utils.filesystem.FilesystemException.FSCode; - -public class ListFile implements File { - private Iterator it; - private String filename; - private byte[] currFile; - private long ptr = 0, start = 0, end = 0; - - private void next() { - if (it != null && it.hasNext()) { - start = end; - currFile = (it.next() + "\n").getBytes(); - end = start + currFile.length; - } - else { - it = null; - currFile = null; - end = -1; - } - } - - @Override - public void close() { - it = null; - currFile = null; - } - - @Override - public int read(byte[] buff) { - if (ptr < start) return 0; - if (it == null) return 0; - - var i = 0; - - while (i < buff.length) { - while (i + ptr >= end) { - next(); - if (it == null) return 0; - } - - int cpyN = Math.min(currFile.length, buff.length - i); - System.arraycopy(currFile, (int)(ptr + i - start), buff, i, cpyN); - - i += cpyN; - } - - ptr += i; - return i; - } - - @Override - public long seek(long offset, int pos) { - if (pos == 2) throw new FilesystemException(filename, FSCode.UNSUPPORTED_OPERATION); - if (pos == 1) offset += ptr; - return ptr = offset; - } - - @Override - public void write(byte[] buff) { - throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_RW); - } - - public ListFile(String filename, Stream stream) throws IOException { - this.it = stream.iterator(); - this.filename = filename; - } -} diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/MemoryFile.java b/src/java/me/topchetoeu/jscript/utils/filesystem/MemoryFile.java index 167d6f7..5dd88be 100644 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/MemoryFile.java +++ b/src/java/me/topchetoeu/jscript/utils/filesystem/MemoryFile.java @@ -1,62 +1,35 @@ package me.topchetoeu.jscript.utils.filesystem; import me.topchetoeu.jscript.common.Buffer; -import me.topchetoeu.jscript.utils.filesystem.FilesystemException.FSCode; -public class MemoryFile implements File { +class MemoryFile extends BaseFile { private int ptr; - private Mode mode; - private Buffer data; - private String filename; - public Buffer data() { return data; } - - @Override - public int read(byte[] buff) { - if (data == null || !mode.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R); - var res = data.read(ptr, buff); + @Override protected int onRead(byte[] buff) { + var res = handle().read(ptr, buff); ptr += res; return res; } - @Override - public void write(byte[] buff) { - if (data == null || !mode.writable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_RW); - - data.write(ptr, buff); + @Override protected void onWrite(byte[] buff) { + handle().write(ptr, buff); ptr += buff.length; } - - @Override - public long seek(long offset, int pos) { - if (data == null || !mode.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R); - + @Override protected long onSeek(long offset, int pos) { if (pos == 0) ptr = (int)offset; else if (pos == 1) ptr += (int)offset; - else if (pos == 2) ptr = data.length() - (int)offset; + else if (pos == 2) ptr = handle().length() - (int)offset; if (ptr < 0) ptr = 0; - if (ptr > data.length()) ptr = data.length(); + if (ptr > handle().length()) ptr = handle().length(); return pos; } - - @Override - public void close() { - mode = Mode.NONE; + @Override protected boolean onClose() { ptr = 0; + return true; } - public MemoryFile(String filename, Buffer buff, Mode mode) { - this.filename = filename; - this.data = buff; - this.mode = mode; + public MemoryFile(Buffer buff, Mode mode) { + super(buff, mode); } - - public static MemoryFile fromFileList(String filename, java.io.File[] list) { - var res = new StringBuilder(); - - for (var el : list) res.append(el.getName()).append('\n'); - - return new MemoryFile(filename, new Buffer(res.toString().getBytes()), Mode.READ); - } -} +} \ No newline at end of file diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/MemoryFilesystem.java b/src/java/me/topchetoeu/jscript/utils/filesystem/MemoryFilesystem.java index f83f5f1..8967294 100644 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/MemoryFilesystem.java +++ b/src/java/me/topchetoeu/jscript/utils/filesystem/MemoryFilesystem.java @@ -6,75 +6,79 @@ import java.util.HashSet; import me.topchetoeu.jscript.common.Buffer; import me.topchetoeu.jscript.common.Filename; -import me.topchetoeu.jscript.utils.filesystem.FilesystemException.FSCode; public class MemoryFilesystem implements Filesystem { public final Mode mode; private HashMap files = new HashMap<>(); private HashSet folders = new HashSet<>(); + private HandleManager handles = new HandleManager(); private Path realPath(String path) { return Filename.normalize(path); } - @Override - public String normalize(String... path) { + @Override public String normalize(String... path) { return Paths.normalize(path); } + @Override public File open(String _path, Mode perms) { + try { + var path = realPath(_path); + var pcount = path.getNameCount(); - @Override - public void create(String _path, EntryType type) { - var path = realPath(_path); + if (files.containsKey(path)) return handles.put(new MemoryFile(files.get(path), perms)); + else if (folders.contains(path)) { + var res = new StringBuilder(); - switch (type) { - case FILE: - if (!folders.contains(path.getParent())) throw new FilesystemException(path.toString(), FSCode.DOESNT_EXIST); - if (folders.contains(path) || files.containsKey(path)) throw new FilesystemException(path.toString(), FSCode.ALREADY_EXISTS); - if (folders.contains(path)) throw new FilesystemException(path.toString(), FSCode.ALREADY_EXISTS); - files.put(path, new Buffer()); - break; - case FOLDER: - if (!folders.contains(path.getParent())) throw new FilesystemException(_path, FSCode.DOESNT_EXIST); - if (folders.contains(path) || files.containsKey(path)) throw new FilesystemException(path.toString(), FSCode.ALREADY_EXISTS); - folders.add(path); - break; - default: - case NONE: - if (!folders.remove(path) && files.remove(path) == null) throw new FilesystemException(path.toString(), FSCode.DOESNT_EXIST); - } - } + for (var folder : folders) { + if (pcount + 1 != folder.getNameCount()) continue; + if (!folder.startsWith(path)) continue; + res.append(folder.toFile().getName()).append('\n'); + } - @Override - public File open(String _path, Mode perms) { - var path = realPath(_path); - var pcount = path.getNameCount(); + for (var file : files.keySet()) { + if (pcount + 1 != file.getNameCount()) continue; + if (!file.startsWith(path)) continue; + res.append(file.toFile().getName()).append('\n'); + } - if (files.containsKey(path)) return new MemoryFile(path.toString(), files.get(path), perms); - else if (folders.contains(path)) { - var res = new StringBuilder(); - for (var folder : folders) { - if (pcount + 1 != folder.getNameCount()) continue; - if (!folder.startsWith(path)) continue; - res.append(folder.toFile().getName()).append('\n'); + return handles.put(new MemoryFile(new Buffer(res.toString().getBytes()), perms.intersect(Mode.READ))); } - for (var file : files.keySet()) { - if (pcount + 1 != file.getNameCount()) continue; - if (!file.startsWith(path)) continue; - res.append(file.toFile().getName()).append('\n'); - } - return new MemoryFile(path.toString(), new Buffer(res.toString().getBytes()), perms.intersect(Mode.READ)); + else throw new FilesystemException(ErrorReason.DOESNT_EXIST); } - else throw new FilesystemException(path.toString(), FSCode.DOESNT_EXIST); + catch (FilesystemException e) { throw e.setPath(_path).setAction(ActionType.OPEN); } } - - @Override - public FileStat stat(String _path) { + @Override public boolean create(String _path, EntryType type) { + try { + var path = realPath(_path); + + switch (type) { + case FILE: + if (!folders.contains(path.getParent())) throw new FilesystemException(ErrorReason.NO_PARENT); + if (folders.contains(path) || files.containsKey(path)) return false; + files.put(path, new Buffer()); + return true; + case FOLDER: + if (!folders.contains(path.getParent())) throw new FilesystemException(ErrorReason.NO_PARENT); + if (folders.contains(path) || files.containsKey(path)) return false; + folders.add(path); + return true; + default: + case NONE: + return folders.remove(path) || files.remove(path) != null; + } + } + catch (FilesystemException e) { throw e.setPath(_path).setAction(ActionType.CREATE); } + } + @Override public FileStat stat(String _path) { var path = realPath(_path); if (files.containsKey(path)) return new FileStat(mode, EntryType.FILE); else if (folders.contains(path)) return new FileStat(mode, EntryType.FOLDER); else return new FileStat(Mode.NONE, EntryType.NONE); } + @Override public void close() throws FilesystemException { + handles.close(); + } public MemoryFilesystem put(String path, byte[] data) { var _path = realPath(path); diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/Mode.java b/src/java/me/topchetoeu/jscript/utils/filesystem/Mode.java index 51092dc..b64d288 100644 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/Mode.java +++ b/src/java/me/topchetoeu/jscript/utils/filesystem/Mode.java @@ -3,6 +3,7 @@ package me.topchetoeu.jscript.utils.filesystem; public enum Mode { NONE("", false, false), READ("r", true, false), + WRITE("rw", false, true), READ_WRITE("rw", true, true); public final String name; @@ -10,9 +11,7 @@ public enum Mode { public final boolean writable; public Mode intersect(Mode other) { - if (this == NONE || other == NONE) return NONE; - if (this == READ_WRITE && other == READ_WRITE) return READ_WRITE; - return READ; + return of(readable && other.readable, writable && other.writable); } private Mode(String mode, boolean r, boolean w) { @@ -21,9 +20,20 @@ public enum Mode { this.writable = w; } + public static Mode of(boolean read, boolean write) { + if (read && write) return READ_WRITE; + if (read) return READ; + if (write) return WRITE; + return NONE; + } + public static Mode parse(String mode) { - switch (mode) { + switch (mode.toLowerCase()) { case "r": return READ; + case "w": return WRITE; + case "r+": + case "w+": + case "wr": case "rw": return READ_WRITE; default: return NONE; } diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/PhysicalFile.java b/src/java/me/topchetoeu/jscript/utils/filesystem/PhysicalFile.java index 42d3859..67efb80 100644 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/PhysicalFile.java +++ b/src/java/me/topchetoeu/jscript/utils/filesystem/PhysicalFile.java @@ -3,55 +3,33 @@ package me.topchetoeu.jscript.utils.filesystem; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; +import java.nio.file.Path; -import me.topchetoeu.jscript.utils.filesystem.FilesystemException.FSCode; - -public class PhysicalFile implements File { - private String filename; - private RandomAccessFile file; - private Mode mode; - - @Override - public int read(byte[] buff) { - if (file == null || !mode.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R); - else try { return file.read(buff); } - catch (IOException e) { throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R); } +public class PhysicalFile extends BaseFile { + @Override protected int onRead(byte[] buff) { + try { return handle().read(buff); } + catch (IOException e) { throw new FilesystemException(ErrorReason.NO_PERMISSION).setAction(ActionType.READ); } } - @Override - public void write(byte[] buff) { - if (file == null || !mode.writable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_RW); - else try { file.write(buff); } - catch (IOException e) { throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_RW); } + @Override protected void onWrite(byte[] buff) { + try { handle().write(buff); } + catch (IOException e) { throw new FilesystemException(ErrorReason.NO_PERMISSION).setAction(ActionType.WRITE); } } - - @Override - public long seek(long offset, int pos) { - if (file == null || !mode.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R); - + @Override protected long onSeek(long offset, int pos) { try { - if (pos == 1) offset += file.getFilePointer(); - else if (pos == 2) offset += file.length(); - file.seek(offset); + if (pos == 1) offset += handle().getFilePointer(); + else if (pos == 2) offset += handle().length(); + handle().seek(offset); return offset; } - catch (IOException e) { throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R); } + catch (IOException e) { throw new FilesystemException(ErrorReason.NO_PERMISSION).setAction(ActionType.SEEK); } } - - @Override - public void close() { - if (file == null) return; - try { file.close(); } + @Override protected boolean onClose() { + try { handle().close(); } catch (IOException e) {} // SHUT - file = null; - mode = Mode.NONE; + return true; } - public PhysicalFile(String name, String path, Mode mode) throws FileNotFoundException { - this.filename = name; - this.mode = mode; - - if (mode == Mode.NONE) file = null; - else try { file = new RandomAccessFile(path, mode.name); } - catch (FileNotFoundException e) { throw new FilesystemException(filename, FSCode.DOESNT_EXIST); } + public PhysicalFile(Path path, Mode mode) throws FileNotFoundException { + super(new RandomAccessFile(path.toFile(), mode.name), mode); } } diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/PhysicalFilesystem.java b/src/java/me/topchetoeu/jscript/utils/filesystem/PhysicalFilesystem.java index 4820c17..6793f1d 100644 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/PhysicalFilesystem.java +++ b/src/java/me/topchetoeu/jscript/utils/filesystem/PhysicalFilesystem.java @@ -1,68 +1,69 @@ package me.topchetoeu.jscript.utils.filesystem; import java.io.IOException; +import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; +import java.nio.file.NoSuchFileException; import java.nio.file.Path; -import me.topchetoeu.jscript.utils.filesystem.FilesystemException.FSCode; - public class PhysicalFilesystem implements Filesystem { public final String root; + private HandleManager handles = new HandleManager(); private void checkMode(Path path, Mode mode) { - if (!path.startsWith(root)) throw new FilesystemException(path.toString(), FSCode.NO_PERMISSIONS_R); + if (!path.startsWith(root)) throw new FilesystemException(ErrorReason.NO_PERMISSION, "Tried to jailbreak the sandbox."); - if (mode.readable && !Files.isReadable(path)) throw new FilesystemException(path.toString(), FSCode.NO_PERMISSIONS_R); - if (mode.writable && !Files.isWritable(path)) throw new FilesystemException(path.toString(), FSCode.NO_PERMISSIONS_RW); + if (mode.readable && !Files.isReadable(path)) throw new FilesystemException(ErrorReason.NO_PERMISSION, "No read permissions"); + if (mode.writable && !Files.isWritable(path)) throw new FilesystemException(ErrorReason.NO_PERMISSION, "No write permissions"); } private Path realPath(String path) { return Path.of(Paths.chroot(root, path)); } - @Override - public String normalize(String... paths) { + @Override public String normalize(String... paths) { return Paths.normalize(paths); } - - @Override - public File open(String _path, Mode perms) { - _path = normalize(_path); - var path = realPath(_path); - - checkMode(path, perms); - + @Override public File open(String _path, Mode perms) { try { - if (Files.isDirectory(path)) return new ListFile(_path, Files.list(path).map((v -> v.getFileName().toString()))); - else return new PhysicalFile(_path, path.toString(), perms); + var path = realPath(normalize(_path)); + checkMode(path, perms); + + try { + if (Files.isDirectory(path)) return handles.put(File.ofIterator( + Files.list(path).map(v -> v.getFileName().toString()).iterator() + )); + else return handles.put(new PhysicalFile(path, perms)); + } + catch (IOException e) { throw new FilesystemException(ErrorReason.DOESNT_EXIST); } } - catch (IOException e) { throw new FilesystemException(path.toString(), FSCode.DOESNT_EXIST); } + catch (FilesystemException e) { throw e.setAction(ActionType.OPEN).setPath(_path); } } - - @Override - public void create(String _path, EntryType type) { - var path = realPath(_path); - - if (type == EntryType.NONE != Files.exists(path)) throw new FilesystemException(path.toString(), FSCode.ALREADY_EXISTS); - + @Override public boolean create(String _path, EntryType type) { try { - switch (type) { - case FILE: - Files.createFile(path); - break; - case FOLDER: - Files.createDirectories(path); - break; - case NONE: - default: - Files.delete(path); - } - } - catch (IOException e) { throw new FilesystemException(path.toString(), FSCode.NO_PERMISSIONS_RW); } - } + var path = realPath(_path); - @Override - public FileStat stat(String _path) { + try { + switch (type) { + case FILE: + Files.createFile(path); + break; + case FOLDER: + Files.createDirectories(path); + break; + case NONE: + default: + Files.delete(path); + } + } + catch (FileAlreadyExistsException | NoSuchFileException e) { return false; } + catch (IOException e) { throw new FilesystemException(ErrorReason.NO_PARENT); } + } + catch (FilesystemException e) { throw e.setAction(ActionType.CREATE).setPath(_path); } + + return true; + } + @Override public FileStat stat(String _path) { var path = realPath(_path); if (!Files.exists(path)) return new FileStat(Mode.NONE, EntryType.NONE); @@ -81,6 +82,12 @@ public class PhysicalFilesystem implements Filesystem { Files.isDirectory(path) ? EntryType.FOLDER : EntryType.FILE ); } + @Override public void close() throws FilesystemException { + try { + handles.close(); + } + catch (FilesystemException e) { throw e.setAction(ActionType.CLOSE_FS); } + } public PhysicalFilesystem(String root) { this.root = Paths.normalize(Path.of(root).toAbsolutePath().toString()); diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/RootFilesystem.java b/src/java/me/topchetoeu/jscript/utils/filesystem/RootFilesystem.java index 38386a9..755896f 100644 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/RootFilesystem.java +++ b/src/java/me/topchetoeu/jscript/utils/filesystem/RootFilesystem.java @@ -4,7 +4,6 @@ import java.util.HashMap; import java.util.Map; import me.topchetoeu.jscript.common.Filename; -import me.topchetoeu.jscript.utils.filesystem.FilesystemException.FSCode; import me.topchetoeu.jscript.utils.permissions.Matcher; import me.topchetoeu.jscript.utils.permissions.PermissionsProvider; @@ -20,8 +19,22 @@ public class RootFilesystem implements Filesystem { } private void modeAllowed(String _path, Mode mode) throws FilesystemException { - if (mode.readable && perms != null && !canRead(_path)) throw new FilesystemException(_path, FSCode.NO_PERMISSIONS_R); - if (mode.writable && perms != null && !canWrite(_path)) throw new FilesystemException(_path, FSCode.NO_PERMISSIONS_RW); + if (mode.readable && perms != null && !canRead(_path)) { + throw new FilesystemException(ErrorReason.NO_PERMISSION, "No read permissions").setPath(_path); + } + if (mode.writable && perms != null && !canWrite(_path)) { + throw new FilesystemException(ErrorReason.NO_PERMISSION, "No wtrite permissions").setPath(_path); + } + } + + private Filesystem getProtocol(Filename filename) { + var protocol = protocols.get(filename.protocol); + + if (protocol == null) { + throw new FilesystemException(ErrorReason.DOESNT_EXIST, "The protocol '" + filename.protocol + "' doesn't exist."); + } + + return protocol; } @Override public String normalize(String... paths) { @@ -37,30 +50,43 @@ public class RootFilesystem implements Filesystem { } } @Override public File open(String path, Mode perms) throws FilesystemException { - var filename = Filename.parse(path); - var protocol = protocols.get(filename.protocol); - if (protocol == null) throw new FilesystemException(filename.toString(), FSCode.DOESNT_EXIST); - modeAllowed(filename.toString(), perms); + try { + var filename = Filename.parse(path); + var protocol = getProtocol(filename); - try { return protocol.open(filename.path, perms); } - catch (FilesystemException e) { throw new FilesystemException(filename.toString(), e.code); } + modeAllowed(filename.toString(), perms); + return protocol.open(filename.path, perms); + } + catch (FilesystemException e) { throw e.setPath(path).setAction(ActionType.OPEN); } } - @Override public void create(String path, EntryType type) throws FilesystemException { - var filename = Filename.parse(path); - var protocol = protocols.get(filename.protocol); - if (protocol == null) throw new FilesystemException(filename.toString(), FSCode.DOESNT_EXIST); - modeAllowed(filename.toString(), Mode.READ_WRITE); + @Override public boolean create(String path, EntryType type) throws FilesystemException { + try { + var filename = Filename.parse(path); + var protocol = getProtocol(filename); - try { protocol.create(filename.path, type); } - catch (FilesystemException e) { throw new FilesystemException(filename.toString(), e.code); } + modeAllowed(filename.toString(), Mode.WRITE); + return protocol.create(filename.path, type); + } + catch (FilesystemException e) { throw e.setPath(path).setAction(ActionType.CREATE); } } @Override public FileStat stat(String path) throws FilesystemException { - var filename = Filename.parse(path); - var protocol = protocols.get(filename.protocol); - if (protocol == null) throw new FilesystemException(filename.toString(), FSCode.DOESNT_EXIST); + try { + var filename = Filename.parse(path); + var protocol = getProtocol(filename); - try { return protocol.stat(filename.path); } - catch (FilesystemException e) { throw new FilesystemException(filename.toString(), e.code); } + return protocol.stat(filename.path); + } + catch (FilesystemException e) { throw e.setPath(path).setAction(ActionType.STAT); } + } + @Override public void close() throws FilesystemException { + try { + for (var protocol : protocols.values()) { + protocol.close(); + } + + protocols.clear(); + } + catch (FilesystemException e) { throw e.setAction(ActionType.CLOSE_FS); } } public RootFilesystem(PermissionsProvider perms) { diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/STDFilesystem.java b/src/java/me/topchetoeu/jscript/utils/filesystem/STDFilesystem.java new file mode 100644 index 0000000..393f3e5 --- /dev/null +++ b/src/java/me/topchetoeu/jscript/utils/filesystem/STDFilesystem.java @@ -0,0 +1,42 @@ +package me.topchetoeu.jscript.utils.filesystem; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashMap; + +public class STDFilesystem implements Filesystem { + private final HashMap handles = new HashMap<>(); + + @Override + public String normalize(String... path) { + var res = Paths.normalize(path); + while (res.startsWith("/")) res = res.substring(1); + return res; + } + + @Override public File open(String path, Mode mode) { + path = normalize(path); + if (handles.containsKey(path)) return handles.get(path); + else throw new FilesystemException(ErrorReason.DOESNT_EXIST).setAction(ActionType.OPEN).setPath(path); + } + @Override public FileStat stat(String path) { + path = normalize(path); + if (handles.containsKey(path)) return new FileStat(Mode.READ_WRITE, EntryType.FILE); + else return new FileStat(Mode.NONE, EntryType.NONE); + } + @Override public void close() { + handles.clear(); + } + + public STDFilesystem add(String name, File handle) { + this.handles.put(name, handle); + return this; + } + + public static STDFilesystem ofStd(InputStream in, OutputStream out, OutputStream err) { + return new STDFilesystem() + .add("in", File.ofStream(in)) + .add("out", File.ofStream(out)) + .add("err", File.ofStream(err)); + } +} diff --git a/src/java/me/topchetoeu/jscript/utils/filesystem/Stdio.java b/src/java/me/topchetoeu/jscript/utils/filesystem/Stdio.java deleted file mode 100644 index 1407c31..0000000 --- a/src/java/me/topchetoeu/jscript/utils/filesystem/Stdio.java +++ /dev/null @@ -1,24 +0,0 @@ -package me.topchetoeu.jscript.utils.filesystem; - -import me.topchetoeu.jscript.core.engine.Extensions; -import me.topchetoeu.jscript.core.engine.values.Symbol; -import me.topchetoeu.jscript.core.exceptions.EngineException; - -public class Stdio { - public static final Symbol STDIN = Symbol.get("IO.stdin"); - public static final Symbol STDOUT = Symbol.get("IO.stdout"); - public static final Symbol STDERR = Symbol.get("IO.stderr"); - - public static File stdout(Extensions exts) { - if (exts.hasNotNull(STDOUT)) return exts.get(STDOUT); - else throw EngineException.ofError("stdout is not supported."); - } - public static File stdin(Extensions exts) { - if (exts.hasNotNull(STDIN)) return exts.get(STDIN); - else throw EngineException.ofError("stdin is not supported."); - } - public static File stderr(Extensions exts) { - if (exts.hasNotNull(STDERR)) return exts.get(STDERR); - else throw EngineException.ofError("stderr is not supported."); - } -}