diff --git a/src/me/topchetoeu/jscript/filesystem/EntryType.java b/src/me/topchetoeu/jscript/filesystem/EntryType.java index 26e255a..c22a8df 100644 --- a/src/me/topchetoeu/jscript/filesystem/EntryType.java +++ b/src/me/topchetoeu/jscript/filesystem/EntryType.java @@ -1,7 +1,13 @@ package me.topchetoeu.jscript.filesystem; public enum EntryType { - NONE, - FILE, - FOLDER, + NONE("none"), + FILE("file"), + FOLDER("folder"); + + public final String name; + + private EntryType(String name) { + this.name = name; + } } \ No newline at end of file diff --git a/src/me/topchetoeu/jscript/filesystem/File.java b/src/me/topchetoeu/jscript/filesystem/File.java index b7a14a8..487b5dc 100644 --- a/src/me/topchetoeu/jscript/filesystem/File.java +++ b/src/me/topchetoeu/jscript/filesystem/File.java @@ -1,27 +1,39 @@ package me.topchetoeu.jscript.filesystem; -import java.io.IOException; - public interface File { - int read() throws IOException, InterruptedException; - boolean write(byte val) throws IOException, InterruptedException; - long tell() throws IOException, InterruptedException; - void seek(long offset, int pos) throws IOException, InterruptedException; - void close() throws IOException, InterruptedException; - Permissions perms(); + int read(byte[] buff); + void write(byte[] buff); + long getPtr(); + void setPtr(long offset, int pos); + void close(); + Mode mode(); - default String readToString() throws IOException, InterruptedException { - seek(0, 2); - long len = tell(); + default String readToString() { + setPtr(0, 2); + long len = getPtr(); if (len < 0) return null; - seek(0, 0); - byte[] res = new byte[(int)len]; + setPtr(0, 0); - for (var i = 0; i < len; i++) { - res[i] = (byte)read(); - } + byte[] res = new byte[(int)len]; + read(res); return new String(res); } + default String readLine() { + var res = new Buffer(new byte[0]); + var buff = new byte[1]; + + while (true) { + if (read(buff) == 0) { + if (res.length() == 0) return null; + else break; + } + + if (buff[0] == '\n') break; + + res.write(res.length(), buff); + } + return new String(res.data()); + } } \ No newline at end of file diff --git a/src/me/topchetoeu/jscript/filesystem/FileStat.java b/src/me/topchetoeu/jscript/filesystem/FileStat.java new file mode 100644 index 0000000..8140d4a --- /dev/null +++ b/src/me/topchetoeu/jscript/filesystem/FileStat.java @@ -0,0 +1,11 @@ +package me.topchetoeu.jscript.filesystem; + +public class FileStat { + public final Mode mode; + public final EntryType type; + + public FileStat(Mode mode, EntryType type) { + this.mode = mode; + this.type = type; + } +} \ No newline at end of file diff --git a/src/me/topchetoeu/jscript/filesystem/Filesystem.java b/src/me/topchetoeu/jscript/filesystem/Filesystem.java index 02ae9d5..0cfea46 100644 --- a/src/me/topchetoeu/jscript/filesystem/Filesystem.java +++ b/src/me/topchetoeu/jscript/filesystem/Filesystem.java @@ -1,10 +1,7 @@ package me.topchetoeu.jscript.filesystem; -import java.io.IOException; - public interface Filesystem { - File open(String path) throws IOException, InterruptedException; - boolean mkdir(String path) throws IOException, InterruptedException; - EntryType type(String path) throws IOException, InterruptedException; - boolean rm(String path) throws IOException, InterruptedException; -} + File open(String path, Mode mode) throws FilesystemException; + void create(String path, EntryType type) throws FilesystemException; + FileStat stat(String path) throws FilesystemException; +} \ No newline at end of file diff --git a/src/me/topchetoeu/jscript/filesystem/FilesystemException.java b/src/me/topchetoeu/jscript/filesystem/FilesystemException.java new file mode 100644 index 0000000..63745e3 --- /dev/null +++ b/src/me/topchetoeu/jscript/filesystem/FilesystemException.java @@ -0,0 +1,55 @@ +package me.topchetoeu.jscript.filesystem; + +import me.topchetoeu.jscript.engine.values.Values; +import me.topchetoeu.jscript.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); + + public final int code; + + private FSCode(int code) { this.code = code; } + } + + 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." + }; + + public final String message, filename; + public final FSCode code; + + public FilesystemException(String message, String filename, FSCode code) { + super(code + ": " + message.formatted(filename)); + this.message = message; + this.code = code; + this.filename = filename; + } + public FilesystemException(String filename, FSCode code) { + super(code + ": " + MESSAGES[code.code].formatted(filename)); + this.message = MESSAGES[code.code]; + this.code = code; + this.filename = filename; + } + + public EngineException toEngineException() { + var res = EngineException.ofError("IOError", getMessage()); + Values.setMember(null, res.value, "code", code); + Values.setMember(null, res.value, "filename", filename.toString()); + return res; + } +} diff --git a/src/me/topchetoeu/jscript/filesystem/InaccessibleFile.java b/src/me/topchetoeu/jscript/filesystem/InaccessibleFile.java deleted file mode 100644 index 2cc59b4..0000000 --- a/src/me/topchetoeu/jscript/filesystem/InaccessibleFile.java +++ /dev/null @@ -1,35 +0,0 @@ -package me.topchetoeu.jscript.filesystem; - -import java.io.IOException; - -public class InaccessibleFile implements File { - public static final InaccessibleFile INSTANCE = new InaccessibleFile(); - - @Override - public int read() throws IOException, InterruptedException { - return -1; - } - - @Override - public boolean write(byte val) throws IOException, InterruptedException { - return false; - } - - @Override - public long tell() throws IOException, InterruptedException { - return 0; - } - - @Override - public void seek(long offset, int pos) throws IOException, InterruptedException { } - - @Override - public void close() throws IOException, InterruptedException { } - - @Override - public Permissions perms() { - return Permissions.NONE; - } - - private InaccessibleFile() { } -} diff --git a/src/me/topchetoeu/jscript/filesystem/Mode.java b/src/me/topchetoeu/jscript/filesystem/Mode.java new file mode 100644 index 0000000..617e77b --- /dev/null +++ b/src/me/topchetoeu/jscript/filesystem/Mode.java @@ -0,0 +1,31 @@ +package me.topchetoeu.jscript.filesystem; + +public enum Mode { + NONE("", false, false), + READ("r", true, false), + READ_WRITE("rw", true, true); + + public final String name; + public final boolean readable; + 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; + } + + private Mode(String mode, boolean r, boolean w) { + this.name = mode; + this.readable = r; + this.writable = w; + } + + public static Mode parse(String mode) { + switch (mode) { + case "r": return READ; + case "rw": return READ_WRITE; + default: return NONE; + } + } +} diff --git a/src/me/topchetoeu/jscript/filesystem/Permissions.java b/src/me/topchetoeu/jscript/filesystem/Permissions.java deleted file mode 100644 index 5a5ff34..0000000 --- a/src/me/topchetoeu/jscript/filesystem/Permissions.java +++ /dev/null @@ -1,17 +0,0 @@ -package me.topchetoeu.jscript.filesystem; - -public enum Permissions { - NONE("", false, false), - READ("r", true, false), - READ_WRITE("rw", true, true); - - public final String readMode; - public final boolean readable; - public final boolean writable; - - private Permissions(String mode, boolean r, boolean w) { - this.readMode = mode; - this.readable = r; - this.writable = w; - } -} diff --git a/src/me/topchetoeu/jscript/filesystem/PhysicalFile.java b/src/me/topchetoeu/jscript/filesystem/PhysicalFile.java index cb5d067..5fba05e 100644 --- a/src/me/topchetoeu/jscript/filesystem/PhysicalFile.java +++ b/src/me/topchetoeu/jscript/filesystem/PhysicalFile.java @@ -1,50 +1,62 @@ package me.topchetoeu.jscript.filesystem; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; +import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode; + public class PhysicalFile implements File { + private String filename; private RandomAccessFile file; - private Permissions perms; + private Mode perms; @Override - public int read() throws IOException, InterruptedException { - if (file == null || !perms.readable) return -1; - else return file.read(); + public int read(byte[] buff) { + if (file == null || !perms.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); } + } + @Override + public void write(byte[] buff) { + if (file == null || !perms.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 - public boolean write(byte val) throws IOException, InterruptedException { - if (file == null || !perms.writable) return false; - file.write(val); - return true; + public long getPtr() { + if (file == null || !perms.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R); + else try { return file.getFilePointer(); } + catch (IOException e) { throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R); } + } + @Override + public void setPtr(long offset, int pos) { + if (file == null || !perms.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R); + + try { + if (pos == 1) pos += file.getFilePointer(); + else if (pos == 2) pos += file.length(); + file.seek(pos); + } + catch (IOException e) { throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R); } } @Override - public long tell() throws IOException, InterruptedException { - if (file == null) return 0; - return file.getFilePointer(); - } - @Override - public void seek(long offset, int pos) throws IOException, InterruptedException { + public void close() { if (file == null) return; - if (pos == 0) file.seek(pos); - else if (pos == 1) file.seek(file.getFilePointer() + pos); - else if (pos == 2) file.seek(file.length() + pos); + try { file.close(); } + catch (IOException e) {} // SHUT + file = null; + perms = Mode.NONE; } - @Override - public void close() throws IOException, InterruptedException { - if (file == null) return; - file.close(); - } + public Mode mode() { return perms; } - @Override - public Permissions perms() { return perms; } - - public PhysicalFile(String path, Permissions mode) throws IOException { - if (mode == Permissions.NONE) file = null; - else file = new RandomAccessFile(path, mode.readMode); + public PhysicalFile(String path, Mode mode) throws FileNotFoundException { + if (mode == Mode.NONE) file = null; + else try { file = new RandomAccessFile(path, mode.name); } + catch (FileNotFoundException e) { throw new FilesystemException(filename, FSCode.DOESNT_EXIST); } perms = mode; } diff --git a/src/me/topchetoeu/jscript/filesystem/PhysicalFilesystem.java b/src/me/topchetoeu/jscript/filesystem/PhysicalFilesystem.java index 0241676..a694520 100644 --- a/src/me/topchetoeu/jscript/filesystem/PhysicalFilesystem.java +++ b/src/me/topchetoeu/jscript/filesystem/PhysicalFilesystem.java @@ -1,74 +1,75 @@ package me.topchetoeu.jscript.filesystem; +import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.Path; +import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode; + public class PhysicalFilesystem implements Filesystem { public final Path root; - private Permissions getPerms(Path path) { - var file = path.toFile(); - if (!path.startsWith(root)) return Permissions.NONE; - if (file.canRead() && file.canWrite()) return Permissions.READ_WRITE; - if (file.canRead()) return Permissions.READ; - - return Permissions.NONE; - } private Path getPath(String name) { - return root.resolve(name); + return root.resolve(name.replace("\\", "/")).normalize(); + } + + private void checkMode(Path path, Mode mode) { + if (!path.startsWith(root)) throw new FilesystemException(path.toString(), FSCode.NO_PERMISSIONS_R); + if (mode.readable && !path.toFile().canRead()) throw new FilesystemException(path.toString(), FSCode.NO_PERMISSIONS_R); + if (mode.writable && !path.toFile().canWrite()) throw new FilesystemException(path.toString(), FSCode.NO_PERMISSIONS_RW); } @Override - public File open(String path) throws IOException, InterruptedException { - var _path = root.resolve(path); - - var perms = getPerms(_path); - if (perms == Permissions.NONE) return InaccessibleFile.INSTANCE; - + public File open(String path, Mode perms) { + var _path = getPath(path); var f = _path.toFile(); - if (f.isDirectory()) { - var res = new StringBuilder(); + checkMode(_path, perms); - for (var child : f.listFiles()) res.append(child.toString()).append('\n'); - return new MemoryFile(res.toString().getBytes(), Permissions.READ); + if (f.isDirectory()) return MemoryFile.fromFileList(path, f.listFiles()); + else try { return new PhysicalFile(path, perms); } + catch (FileNotFoundException e) { throw new FilesystemException(_path.toString(), FSCode.DOESNT_EXIST); } + } + + @Override + public void create(String path, EntryType type) { + var _path = getPath(path); + var f = _path.toFile(); + + checkMode(_path, Mode.READ_WRITE); + switch (type) { + case FILE: + try { + if (!f.createNewFile()) throw new FilesystemException(_path.toString(), FSCode.ALREADY_EXISTS); + else break; + } + catch (IOException e) { throw new FilesystemException(_path.toString(), FSCode.NO_PERMISSIONS_RW); } + case FOLDER: + if (!f.mkdir()) throw new FilesystemException(_path.toString(), FSCode.ALREADY_EXISTS); + else break; + case NONE: + default: + if (!f.delete()) throw new FilesystemException(_path.toString(), FSCode.DOESNT_EXIST); + else break; } - else return new PhysicalFile(path, perms); } @Override - public boolean mkdir(String path) throws IOException, InterruptedException { + public FileStat stat(String path) { var _path = getPath(path); - var perms = getPerms(_path); var f = _path.toFile(); - if (!perms.writable) return false; - else return f.mkdir(); - } + if (f.exists()) throw new FilesystemException(_path.toString(), FSCode.DOESNT_EXIST); + checkMode(_path, Mode.READ); - @Override - public EntryType type(String path) throws IOException, InterruptedException { - var _path = getPath(path); - var perms = getPerms(_path); - var f = _path.toFile(); - - if (perms == Permissions.NONE) return EntryType.NONE; - else if (f.isFile()) return EntryType.FILE; - else return EntryType.FOLDER; - } - - @Override - public boolean rm(String path) throws IOException, InterruptedException { - var _path = getPath(path); - var perms = getPerms(_path); - var f = _path.toFile(); - - if (!perms.writable) return false; - else return f.delete(); + return new FileStat( + f.canWrite() ? Mode.READ_WRITE : Mode.READ, + f.isFile() ? EntryType.FILE : EntryType.FOLDER + ); } public PhysicalFilesystem(Path root) { - this.root = root; + this.root = root.toAbsolutePath().normalize(); } }