mirror of
https://codeberg.org/TicklishHoneyBee/CLIte.git
synced 2026-03-11 09:04:37 +00:00
get a working vfs and unix io
This commit is contained in:
parent
1381e55db6
commit
8df6253e2b
2 changed files with 437 additions and 19 deletions
|
|
@ -7,7 +7,7 @@ header h1 {line-height:40px; font-size:30px;}
|
|||
section {}
|
||||
section.content {}
|
||||
section.content div#terminal {border: 1px solid #FFFFFF; width:100%; min-width: 500px; max-width:1000px; margin:10px auto; overflow:hidden; min-height:100px; padding:10px;}
|
||||
section.content article {}
|
||||
section.content article {unicode-bidi: embed; white-space: pre;}
|
||||
section.content form {}
|
||||
section.content form label, section.content form input, section.content form input:focus {display:block; float:left; border:none; margin:0; padding:0; font-family:monospace; font-size:14px; line-height:20px; background-color:#000000; color:#FFFFFF; outline:none;}
|
||||
|
||||
|
|
|
|||
454
clite/core.js
454
clite/core.js
|
|
@ -62,10 +62,9 @@ var clite = {
|
|||
head.appendChild(script);
|
||||
},
|
||||
file:function(name,callback) {
|
||||
if (window.location.protocol == 'file:') {
|
||||
clite.shell.writeLine('open file: '+name);
|
||||
if (window.location.protocol == 'file:') { // this is a dirty hack and I hate it, just let me open a file: path!
|
||||
clite.term.setCustom({type:'file',callback:callback});
|
||||
clite.events.refocus();
|
||||
clite.shell.writeLine('open file: '+name);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -84,26 +83,412 @@ var clite = {
|
|||
|
||||
this.core.execSafeAsync(function() {
|
||||
clite.term.clear(false);
|
||||
|
||||
// setup vfs
|
||||
clite.log.write('Setting up VFS');
|
||||
clite.vfs.init();
|
||||
var vfsapi = clite.vfs.getApi();
|
||||
clite.log.init(vfsapi);
|
||||
clite.log.write('Mounting wfs on /');
|
||||
// mount core (root) filesystem using data/filesys.txt
|
||||
// populate /dev
|
||||
clite.core.load.file('data/filesys.txt',function(data) {
|
||||
if (data == null) {
|
||||
clite.log.write('No Filesystem Found');
|
||||
return;
|
||||
}
|
||||
vfsapi.mkFile('/dev/wfs');
|
||||
var n = vfsapi.getNode('/dev/wfs');
|
||||
if (!n) {
|
||||
clite.log.write('wfs device failure');
|
||||
return;
|
||||
}
|
||||
n.data.content = data;
|
||||
n.perms = '-r--r-----';
|
||||
var fd = clite.io.open('/dev/wfs');
|
||||
var l;
|
||||
while ((l = clite.io.readLine(fd)) != null) {
|
||||
if (l.length <1 || l[0] == '#')
|
||||
continue;
|
||||
var parts = l.split(':');
|
||||
if (parts.length != 5)
|
||||
continue;
|
||||
var url = parts[0];
|
||||
var path = parts[1];
|
||||
var uid = parseInt(parts[2]);
|
||||
var gid = parseInt(parts[3]);
|
||||
var perms = parts[4];
|
||||
|
||||
var fn = vfsapi.getNode(path);
|
||||
// TODO: make the full path if needed
|
||||
if (!fn) {
|
||||
if (perms[0] == 'd') {
|
||||
vfsapi.mkDir(path);
|
||||
}else{
|
||||
vfsapi.mkFile(path);
|
||||
}
|
||||
fn = vfsapi.getNode(path);
|
||||
}
|
||||
if (!fn)
|
||||
continue;
|
||||
fn.perms = perms;
|
||||
fn.uid = uid;
|
||||
fn.gid = gid;
|
||||
fn.name = clite.lib.basename(path);
|
||||
fn.data.remote = url;
|
||||
fn.data.content = null;
|
||||
clite.log.write('added "'+path+'" ("'+fn.name+'")');
|
||||
}
|
||||
clite.io.close(fd);
|
||||
// populate /dev (data/filesys.txt is /dev/wfs
|
||||
clite.log.write('Populating /dev');
|
||||
// check cookies for login
|
||||
clite.log.write('Checking for user session');
|
||||
// if new user:
|
||||
// read in /usr/share/introduction and write to shell
|
||||
// if logged in:
|
||||
// create user session
|
||||
|
||||
clite.log.write('Creating Shell');
|
||||
clite.events.refocus();
|
||||
});
|
||||
|
||||
clite.core.load.file('data/filesys.txt',clite.shell.writeLine);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
clite.io = {
|
||||
init:function() {
|
||||
var vfsapi = clite.vfs.getApi();
|
||||
|
||||
function getFileDes(path,link) {
|
||||
var n = vfsapi.getNode(path);
|
||||
if (!n)
|
||||
return null;
|
||||
if (n.data.islink && !link) // if link is false, follow links
|
||||
n = vfsapi.getNode(n.data.content);
|
||||
var p = (n.data.remote && n.data.content == null);
|
||||
var fd = Object.create({
|
||||
node:n,
|
||||
pos:0,
|
||||
canwrite:false,
|
||||
remote:{
|
||||
ispending:p,
|
||||
callback:null
|
||||
}
|
||||
});
|
||||
if (p)
|
||||
clite.core.load.file(n.data.remote,function(d) {
|
||||
n.data.content = d;
|
||||
try{
|
||||
fd.remote.callback(fd);
|
||||
} catch(err) {}
|
||||
});
|
||||
// TODO check permissions to see if this is writable
|
||||
return fd;
|
||||
}
|
||||
clite.io.creat = function(path,type) {
|
||||
switch (type) {
|
||||
case 'd':
|
||||
return vfsapi.mkDir(path);
|
||||
break;
|
||||
case 'l':
|
||||
return vfsapi.mkLink(path);
|
||||
break;
|
||||
default:
|
||||
return vfsapi.mkFile(path);
|
||||
}
|
||||
}
|
||||
clite.io.open = function(path) {
|
||||
return getFileDes(path,false);
|
||||
}
|
||||
clite.io.close = function(fd) {
|
||||
try{
|
||||
fd.node = null;
|
||||
delete fd;
|
||||
} catch(err) {}
|
||||
}
|
||||
clite.io.read = function(fd) {
|
||||
if (fd.node.data.content == null)
|
||||
return null;
|
||||
if (fd.pos >= fd.node.data.content.length)
|
||||
return null;
|
||||
return fd.node.data.content[fd.pos++];
|
||||
}
|
||||
clite.io.readLine = function(fd) {
|
||||
if (fd.node.data.content == null)
|
||||
return null;
|
||||
if (fd.pos >= fd.node.data.content.length)
|
||||
return null;
|
||||
if (typeof fd.node.data.content != 'string')
|
||||
return null;
|
||||
var e = fd.node.data.content.indexOf('\n',fd.pos);
|
||||
var l = '';
|
||||
if (e < 0) {
|
||||
l = fd.node.data.content.substring(fd.pos);
|
||||
}else{
|
||||
l = fd.node.data.content.substring(fd.pos,e);
|
||||
fd.pos++;
|
||||
}
|
||||
fd.pos += l.length;
|
||||
return l;
|
||||
}
|
||||
clite.io.write = function(fd,data) {
|
||||
if (!fd.canwrite)
|
||||
return false;
|
||||
if (typeof fd.data.content != 'string')
|
||||
return false;
|
||||
if (typeof data != 'string')
|
||||
return false;
|
||||
if (fd.pos + data.length >= fd.data.content.length) {
|
||||
fd.data.content = fd.data.content.substring(0,fd.pos)+data;
|
||||
fd.pos = fd.data.content.length;
|
||||
}else{
|
||||
var b = fd.data.content.substring(0,fd.pos);
|
||||
var e = fd.data.content.substring(fd.pos+data.length);
|
||||
fd.data.content = b+data+e;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
clite.io.ftruncate = function(fd,len) {
|
||||
if (!fd.canwrite)
|
||||
return false;
|
||||
if (typeof fd.data.content != 'string')
|
||||
return false;
|
||||
fd.data.content = fd.data.content.substring(0,len);
|
||||
if (fd.pos > len)
|
||||
fd.pos = len;
|
||||
return true;
|
||||
}
|
||||
clite.io.truncate = function(path,len) {
|
||||
var fd = getFileDes(path,false);
|
||||
if (!fd)
|
||||
return false;
|
||||
var r = clite.io.ftruncate(fd,len);
|
||||
close(fd);
|
||||
return r;
|
||||
}
|
||||
clite.io.seek = function(fd,pos) {
|
||||
if (fd.node.data.content == null)
|
||||
return 0;
|
||||
if (fd.node.data.content.length < 1)
|
||||
return 0;
|
||||
if (pos<0)
|
||||
return fd.pos;
|
||||
fd.pos = pos;
|
||||
if (fd.pos >= fd.node.data.content.length)
|
||||
fd.pos = fd.node.data.content.length-1;
|
||||
return fd.pos;
|
||||
}
|
||||
clite.io.remove = function(path) {
|
||||
var fd = getFileDes(path,true);
|
||||
if (!fd || !fd.canwrite)
|
||||
return false;
|
||||
return vfsapi.remove(path);
|
||||
}
|
||||
clite.io.link = function(path,target) {
|
||||
if (!getFileDes(target,false))
|
||||
return false;
|
||||
var fd = getFileDes(path,true);
|
||||
if (fd) {
|
||||
if (!fd.canwrite)
|
||||
return false;
|
||||
fd.node.data.content = target;
|
||||
return true;
|
||||
}
|
||||
return vfsapi.mkLink(path,target);
|
||||
}
|
||||
},
|
||||
creat:null,
|
||||
open:null,
|
||||
close:null,
|
||||
read:null,
|
||||
readLine:null,
|
||||
write:null,
|
||||
ftruncate:null,
|
||||
truncate:null,
|
||||
seek:null,
|
||||
remove:null,
|
||||
link:null
|
||||
};
|
||||
|
||||
clite.vfs = {
|
||||
isinit:false,
|
||||
init:function() {
|
||||
var vfsdata = {
|
||||
fs:{},
|
||||
api:{}
|
||||
};
|
||||
|
||||
function findNodeChild(node,name) {
|
||||
for (var i=0; i<node.data.content.length; i++) {
|
||||
var nn = node.data.content[i];
|
||||
if (nn.name == name)
|
||||
return nn;
|
||||
}
|
||||
}
|
||||
function checkCanOpen(node) {
|
||||
return true;
|
||||
}
|
||||
function mkNode() {
|
||||
var fsnode = {
|
||||
name: '',
|
||||
uid: 0,
|
||||
gid: 0,
|
||||
perms:'-rw-r-----',
|
||||
data:{
|
||||
remote:null,
|
||||
isdir:false,
|
||||
islink:false,
|
||||
parent:null,
|
||||
content:null
|
||||
}
|
||||
};
|
||||
return Object.create(fsnode);
|
||||
}
|
||||
|
||||
// set up the root node
|
||||
vfsdata.fs = mkNode();
|
||||
vfsdata.fs.data.content = [];
|
||||
vfsdata.fs.data.isdir = true;
|
||||
vfsdata.fs.perms[0] = 'd';
|
||||
|
||||
clite.vfs.getApi = function() {
|
||||
// TODO: only allow for the root user
|
||||
return vfsdata.api;
|
||||
}
|
||||
|
||||
vfsdata.api.getNode = function(path) {
|
||||
var n = vfsdata.fs;
|
||||
var parts = path.split('/');
|
||||
while (parts.length > 0) {
|
||||
if (!n.data.isdir || !checkCanOpen(n))
|
||||
return null;
|
||||
var p = parts.shift();
|
||||
if (!p || p == '')
|
||||
continue;
|
||||
var nn = findNodeChild(n,p);
|
||||
if (!nn)
|
||||
return null;
|
||||
n = nn;
|
||||
}
|
||||
if (!checkCanOpen(n))
|
||||
return null;
|
||||
return n;
|
||||
}
|
||||
vfsdata.api.getFile = function(path) {
|
||||
var n = vfsdata.api.getNode(path);
|
||||
if (!n)
|
||||
return null;
|
||||
if (n.data.islink) // get the actual file data, not the link data
|
||||
return vfsdata.api.getFile(n.data.content);
|
||||
return n.data.content;
|
||||
}
|
||||
vfsdata.api.mkDir = function(path) {
|
||||
if (vfsdata.api.getNode(path) != null)
|
||||
return false;
|
||||
var parts = path.split('/');
|
||||
var name = parts.pop();
|
||||
var dir = parts.join('/');
|
||||
if (dir.length < 1)
|
||||
dir = '/';
|
||||
var parent = vfsdata.api.getNode(dir);
|
||||
if (!parent || !parent.data.isdir)
|
||||
return false;
|
||||
var n = mkNode();
|
||||
n.name = name;
|
||||
n.data.parent = parent;
|
||||
n.data.content = [];
|
||||
n.data.isdir = true;
|
||||
n.perms[0] = 'd';
|
||||
// TODO: set uid/gid/permissions
|
||||
parent.data.content.push(n);
|
||||
return true;
|
||||
}
|
||||
vfsdata.api.mkFile = function(path) {
|
||||
if (vfsdata.api.getNode(path) != null)
|
||||
return false;
|
||||
var parts = path.split('/');
|
||||
var name = parts.pop();
|
||||
var dir = parts.join('/');
|
||||
if (dir.length < 1)
|
||||
dir = '/';
|
||||
var parent = vfsdata.api.getNode(dir);
|
||||
if (!parent || !parent.data.isdir)
|
||||
return false;
|
||||
var n = mkNode();
|
||||
n.name = name;
|
||||
n.data.parent = parent;
|
||||
n.data.content = '';
|
||||
// TODO: set uid/gid/permissions
|
||||
parent.data.content.push(n);
|
||||
return true;
|
||||
}
|
||||
vfsdata.api.mkLink = function(path,target) {
|
||||
if (vfsdata.api.getNode(path) != null)
|
||||
return false;
|
||||
var parts = path.split('/');
|
||||
var name = parts.pop();
|
||||
var dir = parts.join('/');
|
||||
if (dir.length < 1)
|
||||
dir = '/';
|
||||
var parent = vfsdata.api.getNode(dir);
|
||||
if (!parent || !parent.data.isdir)
|
||||
return false;
|
||||
var n = mkNode();
|
||||
n.name = name;
|
||||
n.data.parent = parent;
|
||||
n.data.islink = true;
|
||||
n.perms[0] = 'l';
|
||||
n.data.content = target;
|
||||
// TODO: set uid/gid/permissions
|
||||
parent.data.content.push(n);
|
||||
return true;
|
||||
}
|
||||
vfsdata.api.remove = function(path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// make some directories
|
||||
vfsdata.api.mkDir('/bin');
|
||||
vfsdata.api.mkDir('/dev');
|
||||
vfsdata.api.mkDir('/etc');
|
||||
vfsdata.api.mkDir('/usr');
|
||||
vfsdata.api.mkDir('/usr/clite');
|
||||
vfsdata.api.mkDir('/usr/clite/web');
|
||||
vfsdata.api.mkDir('/usr/home');
|
||||
vfsdata.api.mkDir('/usr/share');
|
||||
vfsdata.api.mkDir('/var');
|
||||
vfsdata.api.mkFile('/var/logs');
|
||||
|
||||
clite.io.init();
|
||||
vfsdata.api.isinit = true;
|
||||
|
||||
clite.core.execSafeAsync(function() {clite.vfs.init = null;});
|
||||
},
|
||||
getApi:null
|
||||
};
|
||||
|
||||
clite.log = {
|
||||
init:function(vfs) {
|
||||
clite.log.write = function(txt) {
|
||||
// TODO: after login don't write to term or shell
|
||||
try {
|
||||
if (clite.term.hasInput()) {
|
||||
clite.shell.writeLine(txt);
|
||||
}else{
|
||||
clite.term.writeLine(txt);
|
||||
}
|
||||
} catch(e) {}
|
||||
var n = vfs.getNode('/var/logs');
|
||||
if (n)
|
||||
n.data.content += txt+'\n';
|
||||
console.log(txt);
|
||||
}
|
||||
clite.core.execSafeAsync(function(){clite.log.init = null;});
|
||||
},
|
||||
write:function(txt) {
|
||||
// TODO: after login log to /var/log/messages instead of to shell
|
||||
try {
|
||||
clite.shell.writeLine(txt);
|
||||
clite.term.writeLine(txt);
|
||||
} catch(e) {}
|
||||
console.log(txt);
|
||||
}
|
||||
|
|
@ -115,6 +500,26 @@ clite.term = {
|
|||
if (form)
|
||||
clite.term.genForm();
|
||||
},
|
||||
preventInput:function() {
|
||||
var f = document.getElementById('form');
|
||||
if (!f)
|
||||
return;
|
||||
f.parentNode.removeChild(f);
|
||||
},
|
||||
hasInput:function() {
|
||||
var f = document.getElementById('form');
|
||||
if (!f)
|
||||
return false;
|
||||
return true;
|
||||
},
|
||||
writeLine:function(txt) {
|
||||
var a = document.createElement('article');
|
||||
a.innerHTML = txt;
|
||||
var t = document.getElementById('terminal');
|
||||
if (!t)
|
||||
return;
|
||||
t.appendChild(a);
|
||||
},
|
||||
genForm:function() {
|
||||
var f = document.getElementById('form');
|
||||
if (!f)
|
||||
|
|
@ -137,18 +542,22 @@ clite.term = {
|
|||
i.onkeyup = clite.events.keyup;
|
||||
i.onfocusout = clite.events.refocus;
|
||||
i.onblur = clite.events.refocus;
|
||||
if (i.type == 'file') {
|
||||
if (i.type == 'file') { // this is only needed due to dirty hacks to read in a file: path
|
||||
i.onchange = function(e) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = function() {
|
||||
clite.state.input.type.callback(reader.result);
|
||||
var cb = clite.state.input.type.callback;
|
||||
clite.term.setPass(false);
|
||||
clite.shell.writeLine('loaded');
|
||||
clite.term.writeLine('loaded');
|
||||
clite.term.preventInput();
|
||||
cb(reader.result);
|
||||
};
|
||||
reader.onerror = function() {
|
||||
clite.state.input.type.callback(null);
|
||||
var cb = clite.state.input.type.callback;
|
||||
clite.term.setPass(false);
|
||||
clite.shell.writeLine('load failed');
|
||||
clite.term.writeLine('load failed');
|
||||
clite.term.preventInput();
|
||||
cb(null);
|
||||
};
|
||||
reader.readAsText(e.target.files[0]);
|
||||
}
|
||||
|
|
@ -174,12 +583,7 @@ clite.shell = {
|
|||
readLine:function(cb) {
|
||||
},
|
||||
writeLine:function(txt) {
|
||||
var a = document.createElement('article');
|
||||
a.innerHTML = txt;
|
||||
var t = document.getElementById('terminal');
|
||||
if (!t)
|
||||
return;
|
||||
t.appendChild(a);
|
||||
clite.term.writeLine(txt);
|
||||
clite.events.refocus();
|
||||
},
|
||||
exec:function(txt) {
|
||||
|
|
@ -193,6 +597,8 @@ clite.shell = {
|
|||
clite.shell.history.add(txt);
|
||||
clite.shell.history.resetCurrent();
|
||||
clite.shell.writeLine(clite.shell.prompt.getHTML()+txt);
|
||||
// check for a macro
|
||||
// then either run the macro or expand the program to be executed via PATH
|
||||
clite.shell.exec(txt);
|
||||
},
|
||||
prompt:{
|
||||
|
|
@ -254,6 +660,7 @@ clite.shell = {
|
|||
};
|
||||
|
||||
clite.lib = {
|
||||
// makes text html safe
|
||||
htmlEncode:function(txt) {
|
||||
return txt
|
||||
.replace(/&/g, '&')
|
||||
|
|
@ -262,5 +669,16 @@ clite.lib = {
|
|||
.replace(/>/g, '>')
|
||||
.replace(/</g, '<')
|
||||
.replace(/ /g, ' ');
|
||||
},
|
||||
// returns the file name of a path /tmp/test/foo = foo
|
||||
basename:function(txt) {
|
||||
var parts = txt.split('/');
|
||||
return parts[parts.length-1];
|
||||
},
|
||||
// returns the directory name of a path /tmp/test/foo = /tmp/test
|
||||
dirname:function(txt) {
|
||||
var parts = txt.split('/');
|
||||
parts.pop();
|
||||
return parts.join('/');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue