Compare commits
7 Commits
v0.8.4-bet
...
v0.8.7-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
285960bdd6
|
|||
|
cf99845f6b
|
|||
|
48bd1e2015
|
|||
|
304665904f
|
|||
|
56ae3a85a6
|
|||
|
0178cb2194
|
|||
|
a2cb5cd473
|
4
.gitignore
vendored
4
.gitignore
vendored
@@ -14,9 +14,11 @@
|
||||
|
||||
!/.gitignore
|
||||
!/.gitattributes
|
||||
!/build.js
|
||||
!/LICENSE
|
||||
!/README.md
|
||||
!/settings.gradle
|
||||
!/build.gradle
|
||||
!/gradle.properties
|
||||
!/gradle
|
||||
!/gradle/wrapper
|
||||
!/gradle/wrapper/gradle-wrapper.properties
|
||||
29
build.gradle
29
build.gradle
@@ -1,37 +1,32 @@
|
||||
plugins {
|
||||
id 'application'
|
||||
id "application"
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
toolchain.languageVersion = JavaLanguageVersion.of(11)
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
jar {
|
||||
manifest {
|
||||
attributes (
|
||||
'Main-Class': project.main_class
|
||||
)
|
||||
}
|
||||
manifest.attributes["Main-class"] = project.main_class
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs = [ 'src/java' ]
|
||||
main.resources.srcDirs = [ 'src/assets' ]
|
||||
main.java.srcDirs = [ "src/java" ]
|
||||
main.resources.srcDirs = [ "src/assets" ]
|
||||
}
|
||||
|
||||
processResources {
|
||||
filesMatching('metadata.json') {
|
||||
expand (
|
||||
'version': project.project_version,
|
||||
'name': project.project_name
|
||||
filesMatching "metadata.json", {
|
||||
expand(
|
||||
version: project.project_version,
|
||||
name: project.project_name
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
application {
|
||||
mainClass = project.main_class
|
||||
}
|
||||
|
||||
archivesBaseName = project.project_name
|
||||
base.archivesName = project.project_name
|
||||
version = project.project_version
|
||||
group = project.project_group
|
||||
@@ -1,3 +1,4 @@
|
||||
project_group = me.topchetoeu
|
||||
project_name = jscript
|
||||
project_version = 0.8.4-beta
|
||||
project_version = 0.8.6-beta
|
||||
main_class = me.topchetoeu.jscript.utils.JScriptRepl
|
||||
|
||||
7
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
7
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
@@ -0,0 +1,5 @@
|
||||
plugins {
|
||||
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0'
|
||||
}
|
||||
|
||||
rootProject.name = properties.project_name
|
||||
|
||||
@@ -7,22 +7,17 @@ public class Filename {
|
||||
public final String protocol;
|
||||
public final String path;
|
||||
|
||||
public String toString() {
|
||||
@Override public String toString() {
|
||||
return protocol + "://" + path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
@Override public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + protocol.hashCode();
|
||||
result = prime * result + path.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
@Override public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null) return false;
|
||||
if (getClass() != obj.getClass()) return false;
|
||||
@@ -41,9 +36,6 @@ public class Filename {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public Filename(String protocol, String path) {
|
||||
path = path.trim();
|
||||
protocol = protocol.trim();
|
||||
|
||||
@@ -711,14 +711,14 @@ public class Values {
|
||||
res.append("{\n");
|
||||
|
||||
for (var el : obj.values.entrySet()) {
|
||||
for (int i = 0; i < tab + 1; i++) System.out.print(" ");
|
||||
for (int i = 0; i < tab + 1; i++) res.append(" ");
|
||||
res.append(toReadable(ctx, el.getKey(), passed, tab + 1));
|
||||
res.append(": ");
|
||||
res.append(toReadable(ctx, el.getValue(), passed, tab + 1));
|
||||
res.append(",\n");
|
||||
}
|
||||
for (var el : obj.properties.entrySet()) {
|
||||
for (int i = 0; i < tab + 1; i++) System.out.print(" ");
|
||||
for (int i = 0; i < tab + 1; i++) res.append(" ");
|
||||
res.append(toReadable(ctx, el.getKey(), passed, tab + 1));
|
||||
res.append(": [prop],\n");
|
||||
}
|
||||
|
||||
38
src/java/me/topchetoeu/jscript/lib/ConsoleLib.java
Normal file
38
src/java/me/topchetoeu/jscript/lib/ConsoleLib.java
Normal file
@@ -0,0 +1,38 @@
|
||||
package me.topchetoeu.jscript.lib;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import me.topchetoeu.jscript.core.engine.values.Values;
|
||||
import me.topchetoeu.jscript.utils.filesystem.File;
|
||||
import me.topchetoeu.jscript.utils.interop.Arguments;
|
||||
import me.topchetoeu.jscript.utils.interop.Expose;
|
||||
import me.topchetoeu.jscript.utils.interop.WrapperName;
|
||||
|
||||
@WrapperName("Console")
|
||||
public class ConsoleLib {
|
||||
public static interface Writer {
|
||||
void writeLine(String val) throws IOException;
|
||||
}
|
||||
|
||||
private File file;
|
||||
|
||||
@Expose
|
||||
public void __log(Arguments args) {
|
||||
var res = new StringBuilder();
|
||||
var first = true;
|
||||
|
||||
for (var el : args.args) {
|
||||
if (!first) res.append(" ");
|
||||
first = false;
|
||||
res.append(Values.toReadable(args.ctx, el).getBytes());
|
||||
}
|
||||
|
||||
for (var line : res.toString().split("\n", -1)) {
|
||||
file.write(line.getBytes());
|
||||
}
|
||||
}
|
||||
|
||||
public ConsoleLib(File file) {
|
||||
this.file = file;
|
||||
}
|
||||
}
|
||||
@@ -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,7 +38,7 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package me.topchetoeu.jscript.lib;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
|
||||
import me.topchetoeu.jscript.common.Reading;
|
||||
import me.topchetoeu.jscript.core.engine.Context;
|
||||
import me.topchetoeu.jscript.core.engine.Environment;
|
||||
import me.topchetoeu.jscript.core.engine.scope.GlobalScope;
|
||||
@@ -34,27 +32,6 @@ public class Internals {
|
||||
else throw EngineException.ofError("Modules are not supported.");
|
||||
}
|
||||
|
||||
@Expose(target = ExposeTarget.STATIC)
|
||||
public static Object __log(Arguments args) {
|
||||
for (var arg : args.args) {
|
||||
Values.printValue(args.ctx, arg);
|
||||
System.out.print(" ");
|
||||
}
|
||||
System.out.println();
|
||||
|
||||
return args.get(0);
|
||||
}
|
||||
@Expose(target = ExposeTarget.STATIC)
|
||||
public static String __readline() {
|
||||
try {
|
||||
return Reading.readline();
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Expose(target = ExposeTarget.STATIC)
|
||||
public static Thread __setTimeout(Arguments args) {
|
||||
var func = args.convert(0, FunctionValue.class);
|
||||
@@ -175,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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package me.topchetoeu.jscript.utils.filesystem;
|
||||
|
||||
public abstract class BaseFile<T> 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,44 @@
|
||||
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;
|
||||
|
||||
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 byte[] readAll() {
|
||||
var parts = new LinkedList<byte[]>();
|
||||
var buff = new byte[1024];
|
||||
var size = 0;
|
||||
|
||||
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();
|
||||
@@ -34,4 +56,106 @@ public interface File {
|
||||
}
|
||||
return new String(res.data());
|
||||
}
|
||||
|
||||
public static File ofStream(InputStream str) {
|
||||
return new File() {
|
||||
@Override public int read(byte[] buff) {
|
||||
try {
|
||||
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 (FilesystemException e) { throw e.setAction(ActionType.READ); }
|
||||
}
|
||||
};
|
||||
}
|
||||
public static File ofStream(OutputStream str) {
|
||||
return new File() {
|
||||
@Override public void write(byte[] buff) {
|
||||
try {
|
||||
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 (FilesystemException e) { throw e.setAction(ActionType.WRITE); }
|
||||
}
|
||||
};
|
||||
}
|
||||
public static File ofLineWriter(LineWriter writer) {
|
||||
var buff = new Buffer();
|
||||
return new File() {
|
||||
@Override public void write(byte[] val) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
catch (FilesystemException e) { throw e.setAction(ActionType.READ); }
|
||||
}
|
||||
};
|
||||
}
|
||||
public static File ofIterator(Iterator<String> it) {
|
||||
return ofLineReader(LineReader.ofIterator(it));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<String>(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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package me.topchetoeu.jscript.utils.filesystem;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class HandleManager {
|
||||
private Set<File> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<String> it) {
|
||||
return () -> {
|
||||
if (it.hasNext()) return it.next();
|
||||
else return null;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package me.topchetoeu.jscript.utils.filesystem;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface LineWriter {
|
||||
void writeLine(String value) throws IOException;
|
||||
}
|
||||
@@ -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<String> 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<String> stream) throws IOException {
|
||||
this.it = stream.iterator();
|
||||
this.filename = filename;
|
||||
}
|
||||
}
|
||||
@@ -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<Buffer> {
|
||||
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 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);
|
||||
public MemoryFile(Buffer buff, Mode mode) {
|
||||
super(buff, mode);
|
||||
}
|
||||
}
|
||||
@@ -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<Path, Buffer> files = new HashMap<>();
|
||||
private HashSet<Path> 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 void create(String _path, EntryType type) {
|
||||
var path = realPath(_path);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public File open(String _path, Mode perms) {
|
||||
@Override public File open(String _path, Mode perms) {
|
||||
try {
|
||||
var path = realPath(_path);
|
||||
var pcount = path.getNameCount();
|
||||
|
||||
if (files.containsKey(path)) return new MemoryFile(path.toString(), files.get(path), perms);
|
||||
if (files.containsKey(path)) return handles.put(new MemoryFile(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');
|
||||
}
|
||||
|
||||
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(path.toString(), FSCode.DOESNT_EXIST);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileStat stat(String _path) {
|
||||
return handles.put(new MemoryFile(new Buffer(res.toString().getBytes()), perms.intersect(Mode.READ)));
|
||||
}
|
||||
else throw new FilesystemException(ErrorReason.DOESNT_EXIST);
|
||||
}
|
||||
catch (FilesystemException e) { throw e.setPath(_path).setAction(ActionType.OPEN); }
|
||||
}
|
||||
@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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<RandomAccessFile> {
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +1,48 @@
|
||||
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);
|
||||
|
||||
@Override public File open(String _path, Mode perms) {
|
||||
try {
|
||||
var path = realPath(normalize(_path));
|
||||
checkMode(path, 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);
|
||||
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(path.toString(), FSCode.DOESNT_EXIST); }
|
||||
catch (IOException e) { throw new FilesystemException(ErrorReason.DOESNT_EXIST); }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create(String _path, EntryType type) {
|
||||
catch (FilesystemException e) { throw e.setAction(ActionType.OPEN).setPath(_path); }
|
||||
}
|
||||
@Override public boolean create(String _path, EntryType type) {
|
||||
try {
|
||||
var path = realPath(_path);
|
||||
|
||||
if (type == EntryType.NONE != Files.exists(path)) throw new FilesystemException(path.toString(), FSCode.ALREADY_EXISTS);
|
||||
|
||||
try {
|
||||
switch (type) {
|
||||
case FILE:
|
||||
@@ -58,11 +56,14 @@ public class PhysicalFilesystem implements Filesystem {
|
||||
Files.delete(path);
|
||||
}
|
||||
}
|
||||
catch (IOException e) { throw new FilesystemException(path.toString(), FSCode.NO_PERMISSIONS_RW); }
|
||||
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); }
|
||||
|
||||
@Override
|
||||
public FileStat stat(String _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());
|
||||
|
||||
@@ -4,7 +4,7 @@ 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;
|
||||
|
||||
public class RootFilesystem implements Filesystem {
|
||||
@@ -12,15 +12,29 @@ public class RootFilesystem implements Filesystem {
|
||||
public final PermissionsProvider perms;
|
||||
|
||||
private boolean canRead(String _path) {
|
||||
return perms.hasPermission("jscript.file.read:" + _path, '/');
|
||||
return perms.hasPermission("jscript.file.read:" + _path, Matcher.fileWildcard());
|
||||
}
|
||||
private boolean canWrite(String _path) {
|
||||
return perms.hasPermission("jscript.file.write:" + _path, '/');
|
||||
return perms.hasPermission("jscript.file.write:" + _path, Matcher.fileWildcard());
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -36,30 +50,43 @@ public class RootFilesystem implements Filesystem {
|
||||
}
|
||||
}
|
||||
@Override public File open(String path, Mode perms) throws FilesystemException {
|
||||
try {
|
||||
var filename = Filename.parse(path);
|
||||
var protocol = protocols.get(filename.protocol);
|
||||
if (protocol == null) throw new FilesystemException(filename.toString(), FSCode.DOESNT_EXIST);
|
||||
var protocol = getProtocol(filename);
|
||||
|
||||
modeAllowed(filename.toString(), perms);
|
||||
|
||||
try { return protocol.open(filename.path, perms); }
|
||||
catch (FilesystemException e) { throw new FilesystemException(filename.toString(), e.code); }
|
||||
return protocol.open(filename.path, perms);
|
||||
}
|
||||
@Override public void create(String path, EntryType type) throws FilesystemException {
|
||||
catch (FilesystemException e) { throw e.setPath(path).setAction(ActionType.OPEN); }
|
||||
}
|
||||
@Override public boolean create(String path, EntryType type) throws FilesystemException {
|
||||
try {
|
||||
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);
|
||||
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 {
|
||||
try {
|
||||
var filename = Filename.parse(path);
|
||||
var protocol = protocols.get(filename.protocol);
|
||||
if (protocol == null) throw new FilesystemException(filename.toString(), FSCode.DOESNT_EXIST);
|
||||
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) {
|
||||
|
||||
@@ -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<String, File> 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));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package me.topchetoeu.jscript.utils.permissions;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
public interface Matcher {
|
||||
static class State {
|
||||
public final int predI, trgI, wildcardI;
|
||||
public final boolean wildcard;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("State [pr=%s;trg=%s;wildN=%s;wild=%s]", predI, trgI, wildcardI, wildcard);
|
||||
}
|
||||
|
||||
public State(int predicateI, int targetI, int wildcardI, boolean wildcard) {
|
||||
this.predI = predicateI;
|
||||
this.trgI = targetI;
|
||||
this.wildcardI = wildcardI;
|
||||
this.wildcard = wildcard;
|
||||
}
|
||||
}
|
||||
|
||||
boolean match(String predicate, String value);
|
||||
|
||||
public static Matcher fileWildcard() {
|
||||
return (predicate, value) -> execWildcard(predicate, value, '/');
|
||||
}
|
||||
public static Matcher namespaceWildcard() {
|
||||
return (predicate, value) -> execWildcard(predicate, value, '.');
|
||||
}
|
||||
public static Matcher wildcard() {
|
||||
return (predicate, value) -> execWildcard(predicate, value, '\0');
|
||||
}
|
||||
|
||||
public static boolean execWildcard(String predicate, String target, char delim) {
|
||||
if (predicate.equals("")) return target.equals("");
|
||||
|
||||
var queue = new LinkedList<State>();
|
||||
queue.push(new State(0, 0, 0, false));
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
var state = queue.poll();
|
||||
var predEnd = state.predI >= predicate.length();
|
||||
|
||||
if (state.trgI >= target.length()) return predEnd;
|
||||
var predC = predEnd ? 0 : predicate.charAt(state.predI);
|
||||
var trgC = target.charAt(state.trgI);
|
||||
|
||||
if (state.wildcard) {
|
||||
if (state.wildcardI == 2 || trgC != delim) {
|
||||
queue.add(new State(state.predI, state.trgI + 1, state.wildcardI, true));
|
||||
}
|
||||
queue.add(new State(state.predI, state.trgI, 0, false));
|
||||
}
|
||||
else if (predC == '*') {
|
||||
queue.add(new State(state.predI + 1, state.trgI, state.wildcardI + 1, false));
|
||||
}
|
||||
else if (state.wildcardI > 0) {
|
||||
if (state.wildcardI > 2) throw new IllegalArgumentException("Too many sequential stars.");
|
||||
queue.add(new State(state.predI, state.trgI, state.wildcardI, true));
|
||||
}
|
||||
else if (!predEnd && (predC == '?' || predC == trgC)) {
|
||||
queue.add(new State(state.predI + 1, state.trgI + 1, 0, false));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,124 +1,19 @@
|
||||
package me.topchetoeu.jscript.utils.permissions;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
public class Permission {
|
||||
private static class State {
|
||||
public final int predI, trgI, wildcardI;
|
||||
public final boolean wildcard;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("State [pr=%s;trg=%s;wildN=%s;wild=%s]", predI, trgI, wildcardI, wildcard);
|
||||
}
|
||||
|
||||
public State(int predicateI, int targetI, int wildcardI, boolean wildcard) {
|
||||
this.predI = predicateI;
|
||||
this.trgI = targetI;
|
||||
this.wildcardI = wildcardI;
|
||||
this.wildcard = wildcard;
|
||||
}
|
||||
}
|
||||
|
||||
public final String namespace;
|
||||
public final String value;
|
||||
public final Matcher matcher;
|
||||
|
||||
public boolean match(Permission perm) {
|
||||
if (!Permission.match(namespace, perm.namespace, '.')) return false;
|
||||
if (value == null || perm.value == null) return true;
|
||||
return Permission.match(value, perm.value);
|
||||
}
|
||||
public boolean match(Permission perm, char delim) {
|
||||
if (!Permission.match(namespace, perm.namespace, '.')) return false;
|
||||
if (value == null || perm.value == null) return true;
|
||||
return Permission.match(value, perm.value, delim);
|
||||
@Override public String toString() {
|
||||
return namespace;
|
||||
}
|
||||
|
||||
public boolean match(String perm) {
|
||||
return match(new Permission(perm));
|
||||
public Permission(String namespace, Matcher matcher) {
|
||||
this.namespace = namespace;
|
||||
this.matcher = matcher;
|
||||
}
|
||||
public boolean match(String perm, char delim) {
|
||||
return match(new Permission(perm), delim);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (value != null) return namespace + ":" + value;
|
||||
else return namespace;
|
||||
}
|
||||
|
||||
public Permission(String raw) {
|
||||
var i = raw.indexOf(':');
|
||||
|
||||
if (i > 0) {
|
||||
value = raw.substring(i + 1);
|
||||
namespace = raw.substring(0, i);
|
||||
}
|
||||
else {
|
||||
value = null;
|
||||
namespace = raw;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean match(String predicate, String target, char delim) {
|
||||
if (predicate.equals("")) return target.equals("");
|
||||
|
||||
var queue = new LinkedList<State>();
|
||||
queue.push(new State(0, 0, 0, false));
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
var state = queue.poll();
|
||||
var predEnd = state.predI >= predicate.length();
|
||||
|
||||
if (state.trgI >= target.length()) return predEnd;
|
||||
var predC = predEnd ? 0 : predicate.charAt(state.predI);
|
||||
var trgC = target.charAt(state.trgI);
|
||||
|
||||
if (state.wildcard) {
|
||||
if (state.wildcardI == 2 || trgC != delim) {
|
||||
queue.add(new State(state.predI, state.trgI + 1, state.wildcardI, true));
|
||||
}
|
||||
queue.add(new State(state.predI, state.trgI, 0, false));
|
||||
}
|
||||
else if (predC == '*') {
|
||||
queue.add(new State(state.predI + 1, state.trgI, state.wildcardI + 1, false));
|
||||
}
|
||||
else if (state.wildcardI > 0) {
|
||||
if (state.wildcardI > 2) throw new IllegalArgumentException("Too many sequential stars.");
|
||||
queue.add(new State(state.predI, state.trgI, state.wildcardI, true));
|
||||
}
|
||||
else if (!predEnd && (predC == '?' || predC == trgC)) {
|
||||
queue.add(new State(state.predI + 1, state.trgI + 1, 0, false));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
public static boolean match(String predicate, String target) {
|
||||
if (predicate.equals("")) return target.equals("");
|
||||
|
||||
var queue = new LinkedList<State>();
|
||||
queue.push(new State(0, 0, 0, false));
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
var state = queue.poll();
|
||||
|
||||
if (state.predI >= predicate.length() || state.trgI >= target.length()) {
|
||||
return state.predI >= predicate.length() && state.trgI >= target.length();
|
||||
}
|
||||
|
||||
var predC = predicate.charAt(state.predI);
|
||||
var trgC = target.charAt(state.trgI);
|
||||
|
||||
if (predC == '*') {
|
||||
queue.add(new State(state.predI, state.trgI + 1, state.wildcardI, true));
|
||||
queue.add(new State(state.predI + 1, state.trgI, 0, false));
|
||||
}
|
||||
else if (predC == '?' || predC == trgC) {
|
||||
queue.add(new State(state.predI + 1, state.trgI + 1, 0, false));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
this(raw, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package me.topchetoeu.jscript.utils.permissions;
|
||||
|
||||
public class PermissionPredicate {
|
||||
public final String namespace;
|
||||
public final String value;
|
||||
public final boolean denies;
|
||||
|
||||
public boolean match(Permission permission, String value) {
|
||||
if (!match(permission)) return false;
|
||||
if (this.value == null || value == null) return true;
|
||||
if (permission.matcher == null) return true;
|
||||
else return permission.matcher.match(this.value, value);
|
||||
}
|
||||
public boolean match(Permission permission) {
|
||||
return Matcher.namespaceWildcard().match(namespace, permission.namespace);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (value != null) return namespace + ":" + value;
|
||||
else return namespace;
|
||||
}
|
||||
|
||||
public PermissionPredicate(String raw) {
|
||||
raw = raw.trim();
|
||||
|
||||
if (raw.startsWith("!")) {
|
||||
denies = true;
|
||||
raw = raw.substring(1).trim();
|
||||
}
|
||||
else denies = false;
|
||||
|
||||
var i = raw.indexOf(':');
|
||||
|
||||
if (i > 0) {
|
||||
value = raw.substring(i + 1);
|
||||
namespace = raw.substring(0, i);
|
||||
}
|
||||
else {
|
||||
value = null;
|
||||
namespace = raw;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,34 +1,59 @@
|
||||
package me.topchetoeu.jscript.utils.permissions;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class PermissionsManager implements PermissionsProvider {
|
||||
public static final PermissionsProvider ALL_PERMS = new PermissionsManager().add(new Permission("**"));
|
||||
public final ArrayList<PermissionPredicate> predicates = new ArrayList<>();
|
||||
|
||||
public final ArrayList<Permission> allowed = new ArrayList<>();
|
||||
public final ArrayList<Permission> denied = new ArrayList<>();
|
||||
|
||||
public PermissionsProvider add(Permission perm) {
|
||||
allowed.add(perm);
|
||||
public PermissionsProvider add(PermissionPredicate perm) {
|
||||
predicates.add(perm);
|
||||
return this;
|
||||
}
|
||||
public PermissionsProvider add(String perm) {
|
||||
allowed.add(new Permission(perm));
|
||||
predicates.add(new PermissionPredicate(perm));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Permission perm, char delim) {
|
||||
for (var el : denied) if (el.match(perm, delim)) return false;
|
||||
for (var el : allowed) if (el.match(perm, delim)) return true;
|
||||
@Override public boolean hasPermission(Permission perm, String value) {
|
||||
for (var el : predicates) {
|
||||
if (el.match(perm, value)) {
|
||||
if (el.denies) return false;
|
||||
else return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean hasPermission(Permission perm) {
|
||||
for (var el : denied) if (el.match(perm)) return false;
|
||||
for (var el : allowed) if (el.match(perm)) return true;
|
||||
@Override public boolean hasPermission(Permission perm) {
|
||||
for (var el : predicates) {
|
||||
if (el.match(perm)) {
|
||||
if (el.denies) return false;
|
||||
else return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public PermissionsProvider addFromStream(InputStream stream) throws IOException {
|
||||
var reader = new BufferedReader(new InputStreamReader(stream));
|
||||
String line;
|
||||
|
||||
while ((line = reader.readLine()) != null) {
|
||||
var i = line.indexOf('#');
|
||||
if (i >= 0) line = line.substring(0, i);
|
||||
|
||||
line = line.trim();
|
||||
|
||||
if (line.isEmpty()) continue;
|
||||
|
||||
add(line);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,27 +5,25 @@ import me.topchetoeu.jscript.core.engine.values.Symbol;
|
||||
|
||||
public interface PermissionsProvider {
|
||||
public static final Symbol ENV_KEY = new Symbol("Environment.perms");
|
||||
public static final PermissionsProvider ALL_PERMS = (perm, value) -> true;
|
||||
|
||||
boolean hasPermission(Permission perm, char delim);
|
||||
boolean hasPermission(Permission perm);
|
||||
boolean hasPermission(Permission perm, String value);
|
||||
|
||||
default boolean hasPermission(String perm, char delim) {
|
||||
return hasPermission(new Permission(perm), delim);
|
||||
default boolean hasPermission(Permission perm) {
|
||||
return hasPermission(perm, null);
|
||||
}
|
||||
default boolean hasPermission(String perm) {
|
||||
return hasPermission(new Permission(perm));
|
||||
|
||||
default boolean hasPermission(String perm, String value, Matcher matcher) {
|
||||
return hasPermission(new Permission(perm, matcher), value);
|
||||
}
|
||||
default boolean hasPermission(String perm, Matcher matcher) {
|
||||
return hasPermission(new Permission(perm, matcher));
|
||||
}
|
||||
|
||||
public static PermissionsProvider get(Extensions exts) {
|
||||
return new PermissionsProvider() {
|
||||
@Override public boolean hasPermission(Permission perm) {
|
||||
return (perm, value) -> {
|
||||
if (exts.hasNotNull(ENV_KEY)) return ((PermissionsProvider)exts.get(ENV_KEY)).hasPermission(perm);
|
||||
else return true;
|
||||
}
|
||||
@Override public boolean hasPermission(Permission perm, char delim) {
|
||||
if (exts.hasNotNull(ENV_KEY)) return ((PermissionsProvider)exts.get(ENV_KEY)).hasPermission(perm, delim);
|
||||
else return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user