mirror of
https://codeberg.org/TicklishHoneyBee/CLIte.git
synced 2026-03-11 09:04:37 +00:00
1012 lines
24 KiB
JavaScript
1012 lines
24 KiB
JavaScript
var clite = {
|
|
state:{
|
|
isinit:false,
|
|
terminal:null,
|
|
input:{
|
|
type:'text',
|
|
form:null,
|
|
field:null
|
|
}
|
|
},
|
|
events:{
|
|
keyup:function(e) {
|
|
try {
|
|
if (e.key == 'Down' || e.key == 'ArrowDown') {
|
|
clite.shell.history.down();
|
|
return false;
|
|
}
|
|
if (e.key == 'Up' || e.key == 'ArrowUp') {
|
|
clite.shell.history.up();
|
|
return false;
|
|
}
|
|
var t = clite.state.input.field.value;
|
|
if (e.key != 'Enter') {
|
|
clite.shell.history.setCurrent(t);
|
|
return true;
|
|
}
|
|
clite.shell.parse(t);
|
|
return false;
|
|
} catch(err) {return false;}
|
|
},
|
|
refocus:function(e) {
|
|
try {
|
|
clite.term.genForm();
|
|
clite.state.input.field.focus();
|
|
} catch(err) {}
|
|
}
|
|
},
|
|
core:{
|
|
execSafe:function(f) {
|
|
try{
|
|
f();
|
|
} catch(e) {
|
|
clite.log.write(e.message);
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
execSafeAsync:function(f) {
|
|
setTimeout(f,10);
|
|
},
|
|
load:{
|
|
script:function(name,callback) {
|
|
var head = document.getElementsByTagName('head')[0];
|
|
var script = document.createElement('script');
|
|
script.type = 'text/javascript';
|
|
script.src = name;
|
|
script.onload = callback;
|
|
head.appendChild(script);
|
|
},
|
|
file:function(name,callback) {
|
|
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.shell.writeLine('open file: '+name);
|
|
return;
|
|
}
|
|
|
|
fetch(name)
|
|
.then(response => response.text())
|
|
.then((data) => {
|
|
callback(data);
|
|
});
|
|
}
|
|
}
|
|
},
|
|
init:function() {
|
|
if (this.state.isinit)
|
|
return;
|
|
this.state.isinit = true;
|
|
|
|
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
|
|
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 = 'cr--r-----';
|
|
n.isdev = true;
|
|
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.io.close(fd);
|
|
// populate /dev (data/filesys.txt is /dev/wfs already)
|
|
clite.log.write('Populating /dev');
|
|
// TODO: are there any other devices we need?
|
|
// populate /bin
|
|
clite.log.write('Populating /bin');
|
|
// create a temporary function to load in commands
|
|
clite.commands = {
|
|
data:null,
|
|
load:function(name,fn) {
|
|
vfsapi.mkFile('/bin/'+name);
|
|
var f = vfsapi.getNode('/bin/'+name);
|
|
if (!f)
|
|
return;
|
|
f.perms = '-r-xr-xr-x';
|
|
f.data.content = fn;
|
|
}
|
|
}
|
|
clite.core.load.script('clite/commands.js',function() {
|
|
clite.core.execSafe(clite.commands.data);
|
|
clite.commands = null;
|
|
// check cookies for login
|
|
clite.log.write('Checking for user session');
|
|
if (clite.user.init(vfsapi)) {// if logged in:
|
|
clite.log.write('found user session?');
|
|
// create user session
|
|
}else{// if new user:
|
|
clite.log.write('Generating guest session');
|
|
// read in /usr/share/introduction and write to shell
|
|
var fd = clite.io.open('/usr/share/introduction',function(fd) {
|
|
clite.user.genGuest();
|
|
if (!fd)
|
|
return;
|
|
var d = clite.io.readAll(fd);
|
|
if (d != null)
|
|
clite.shell.writeLine(d);
|
|
clite.io.close(fd);
|
|
|
|
});
|
|
}
|
|
|
|
}); // end command load callback
|
|
}); // end filesys load callback
|
|
|
|
});
|
|
}
|
|
};
|
|
|
|
clite.user = {
|
|
init:function(vfs) { // returns true if there's a user login or false for guest
|
|
var udata = {
|
|
name:'root',
|
|
uid:0,
|
|
group:0,
|
|
groups:[]
|
|
};
|
|
clite.user.getUID = function() {
|
|
return udata.uid;
|
|
}
|
|
clite.user.getGID = function() {
|
|
return udata.gid;
|
|
}
|
|
clite.user.checkGID = function(gid) {
|
|
if (gid == udata.gid)
|
|
return true;
|
|
if (udata.groups.indexOf(gid) > -1)
|
|
return true;
|
|
return false;
|
|
}
|
|
function setLogin(user,uid,gid,groups) {
|
|
udata.name = user;
|
|
udata.uid = uid;
|
|
udata.group = gid;
|
|
udata.groups = groups;
|
|
clite.shell.env.HOME = '/usr/home/'+user;
|
|
clite.shell.env.PWD = '/usr/home/'+user;
|
|
clite.shell.env.USER = user;
|
|
clite.shell.prompt.generate();
|
|
clite.user.hasLogin = function() {
|
|
return true;
|
|
}
|
|
}
|
|
clite.user.genGuest = function() {
|
|
clite.term.clear();
|
|
vfs.mkDir('/usr/home/guest');
|
|
var n = vfs.getNode('/usr/home/guest');
|
|
if (n) {
|
|
n.perms = '-rwx------';
|
|
n.uid = 1;
|
|
n.gid = 1;
|
|
}
|
|
setLogin('guest',1,1,[]);
|
|
clite.events.refocus();
|
|
}
|
|
return false;
|
|
},
|
|
getUID:function() { // get the user's user id
|
|
return 0;
|
|
},
|
|
getGID:function() { // get the user's group id
|
|
return 0;
|
|
},
|
|
checkGID:function(gid) { // check if the user is in a group
|
|
return true;
|
|
},
|
|
hasLogin:function() { // check if we're in a user session
|
|
return false;
|
|
},
|
|
genGuest:null
|
|
};
|
|
|
|
clite.io = {
|
|
init:function() {
|
|
var vfsapi = clite.vfs.getApi();
|
|
|
|
var perms = {
|
|
// -rwxrwxrwx user1-3,group4-6,other7-9
|
|
checkReadable:function(p,uid,gid) {
|
|
if (p[7] == 'r')
|
|
return true;
|
|
if (clite.user.checkGID(gid) && p[4] == 'r')
|
|
return true;
|
|
if (uid == clite.user.getUID() && p[1] == 'r')
|
|
return true;
|
|
return false;
|
|
},
|
|
checkWritable:function(p,uid,gid) {
|
|
if (p[8] == 'w')
|
|
return true;
|
|
if (clite.user.checkGID(gid) && p[5] == 'w')
|
|
return true;
|
|
if (uid == clite.user.getUID() && p[2] == 'w')
|
|
return true;
|
|
return false;
|
|
},
|
|
checkExecutable:function(p,uid,gid) {
|
|
if (p[9] == 'x')
|
|
return true;
|
|
if (clite.user.checkGID(gid) && p[6] == 'x')
|
|
return true;
|
|
if (uid == clite.user.getUID() && p[3] == 'x')
|
|
return true;
|
|
return false;
|
|
}
|
|
};
|
|
|
|
function getFileDes(path,link,cb) {
|
|
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,
|
|
canread:false,
|
|
canwrite:false,
|
|
canexec:false,
|
|
remote:{
|
|
ispending:p,
|
|
callback:cb
|
|
}
|
|
});
|
|
if (p) {
|
|
clite.core.load.file(n.data.remote,function(d) {
|
|
n.data.content = d;
|
|
fd.remote.ispending = false;
|
|
try{
|
|
fd.remote.callback(fd);
|
|
} catch(err) {}
|
|
});
|
|
}
|
|
fd.canread = perms.checkReadable(fd.node.perms,fd.node.uid,fd.node.gid);
|
|
fd.canwrite = perms.checkWritable(fd.node.perms,fd.node.uid,fd.node.gid);
|
|
fd.canexec = perms.checkExecutable(fd.node.perms,fd.node.uid,fd.node.gid);
|
|
if (!p && cb != null) {
|
|
clite.core.execSafeAsync(function() {cb(fd);});
|
|
}
|
|
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,cb) {
|
|
return getFileDes(path,false,cb);
|
|
}
|
|
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.readAll = function(fd) {
|
|
return fd.node.data.content;
|
|
}
|
|
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,null);
|
|
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,null);
|
|
if (!fd || !fd.canwrite)
|
|
return false;
|
|
return vfsapi.remove(path);
|
|
}
|
|
clite.io.link = function(path,target) {
|
|
if (!getFileDes(target,false,null))
|
|
return false;
|
|
var fd = getFileDes(path,true,null);
|
|
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,
|
|
readAll: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) {
|
|
if (node.perms[7] == 'r')
|
|
return true;
|
|
if (clite.user.checkGID(node.gid) && node.perms[4] == 'r')
|
|
return true;
|
|
if (node.uid == clite.user.getUID() && node.perms[1] == 'r')
|
|
return true;
|
|
return false;
|
|
}
|
|
function mkNode() {
|
|
var fsnode = {
|
|
name: '',
|
|
uid: 0,
|
|
gid: 0,
|
|
perms:'-rw-r--r--',
|
|
data:{
|
|
remote:null,
|
|
isdir:false,
|
|
islink:false,
|
|
isdev: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() {
|
|
if (clite.user.getUID() == 0)
|
|
return vfsdata.api;
|
|
return null;
|
|
}
|
|
|
|
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 = 'drwxr-xr-x';
|
|
n.uid = clite.user.getUID();
|
|
n.gid = clite.user.getGID();
|
|
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 = '';
|
|
n.perms = '-rw-r--r--';
|
|
n.uid = clite.user.getUID();
|
|
n.gid = clite.user.getGID();
|
|
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.data.content = target;
|
|
n.perms = 'lrwxr-xr-x';
|
|
n.uid = clite.user.getUID();
|
|
n.gid = clite.user.getGID();
|
|
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) {
|
|
if (!clite.user.hasLogin()) {
|
|
try {
|
|
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) {
|
|
try {
|
|
clite.term.writeLine(txt);
|
|
} catch(e) {}
|
|
console.log(txt);
|
|
}
|
|
};
|
|
|
|
clite.term = {
|
|
clear:function(form) {
|
|
document.getElementById('terminal').innerHTML = '';
|
|
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)
|
|
f = document.createElement('form');
|
|
f.id = 'form';
|
|
f.innerHTML = '';
|
|
var l = document.createElement('label');
|
|
l.innerHTML = clite.shell.prompt.getHTML();
|
|
f.appendChild(l);
|
|
var i = document.createElement('input');
|
|
i.type = clite.term.getType();
|
|
i.value = clite.shell.history.getCurrent();
|
|
f.appendChild(i);
|
|
clite.state.input.form = f;
|
|
clite.state.input.field = i;
|
|
var t = document.getElementById('terminal');
|
|
if (!t)
|
|
return;
|
|
t.appendChild(f);
|
|
i.onkeyup = clite.events.keyup;
|
|
i.onfocusout = clite.events.refocus;
|
|
i.onblur = clite.events.refocus;
|
|
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() {
|
|
var cb = clite.state.input.type.callback;
|
|
clite.term.setPass(false);
|
|
clite.term.writeLine('loaded');
|
|
clite.term.preventInput();
|
|
cb(reader.result);
|
|
};
|
|
reader.onerror = function() {
|
|
var cb = clite.state.input.type.callback;
|
|
clite.term.setPass(false);
|
|
clite.term.writeLine('load failed');
|
|
clite.term.preventInput();
|
|
cb(null);
|
|
};
|
|
reader.readAsText(e.target.files[0]);
|
|
}
|
|
}
|
|
f.onsubmit = function() {return false;};
|
|
},
|
|
setPass:function(is) {
|
|
clite.state.input.type = (!!is) ? 'password' : 'text';
|
|
},
|
|
setCustom:function(type) {
|
|
clite.state.input.type = type;
|
|
},
|
|
getType:function() {
|
|
if (typeof clite.state.input.type == 'string')
|
|
return clite.state.input.type;
|
|
if (typeof clite.state.input.type == 'object' && typeof clite.state.input.type.type == 'string')
|
|
return clite.state.input.type.type;
|
|
return 'text';
|
|
}
|
|
};
|
|
|
|
clite.shell = {
|
|
exit:function(v) {
|
|
clite.shell.prompt.pop();
|
|
clite.events.refocus();
|
|
},
|
|
readLine:function(cb) {
|
|
},
|
|
writeLine:function(txt) {
|
|
clite.term.writeLine(txt);
|
|
clite.events.refocus();
|
|
},
|
|
resolvePath:function(txt) {
|
|
// TODO: resolve using PATH (check each colon separated path)
|
|
return clite.lib.resolvePath(txt,clite.shell.env.PATH)
|
|
},
|
|
exec:function(args,io) {
|
|
var path = clite.shell.resolvePath(args[0]);
|
|
var fd = clite.io.open(path);
|
|
// TODO: should errors here write to stderr? (io.error)
|
|
if (!fd) {
|
|
clite.term.writeLine('Shell: unknown command: '+args[0]);
|
|
io.exit(-1);
|
|
return;
|
|
}
|
|
|
|
var r = clite.core.execSafe(function() {
|
|
var rr = fd.node.data.content(args,Object.create(clite.shell.env),io);
|
|
if (typeof rr == 'number')
|
|
io.exit(rr);
|
|
});
|
|
if (!r) {
|
|
clite.term.writeLine('Shell: error in command:'+args[0]);
|
|
io.exit(-1);
|
|
}
|
|
clite.io.close(fd);
|
|
},
|
|
parse:function(txt) {
|
|
if (txt.length < 1) {
|
|
clite.shell.writeLine(clite.shell.prompt.getHTML());
|
|
return;
|
|
}
|
|
clite.shell.history.add(txt);
|
|
clite.shell.history.resetCurrent();
|
|
clite.shell.writeLine(clite.shell.prompt.getHTML()+txt);
|
|
clite.shell.prompt.push('');
|
|
clite.term.preventInput();
|
|
// ready stdio (stdout,stdin)
|
|
var stdio = {
|
|
error:clite.term.writeLine,
|
|
write:clite.term.writeLine,
|
|
read:clite.shell.readLine,
|
|
exit:clite.shell.exit,
|
|
istty:true
|
|
};
|
|
// check for a macro
|
|
var args = clite.lib.strToArgs(txt);
|
|
// then either run the macro or expand the program to be executed via PATH
|
|
if (typeof clite.shell.macro[args[0]] != 'undefined') {
|
|
var r = clite.shell.macro[args[0]](args,stdio);
|
|
clite.shell.exit(r);
|
|
}else{
|
|
clite.shell.exec(args,stdio);
|
|
}
|
|
},
|
|
env:{
|
|
USER:'root',
|
|
PWD:'/',
|
|
HOME:'/',
|
|
PATH:'/bin'
|
|
},
|
|
macro:{
|
|
clear:function(args,io) {
|
|
clite.term.clear();
|
|
},
|
|
cd:function(args,io) {
|
|
var dir = clite.shell.env.HOME;
|
|
if (args.length > 1) {
|
|
dir = clite.lib.resolvePath(args[1]);
|
|
}
|
|
var fd = clite.io.open(dir);
|
|
if (!fd) {
|
|
io.error('invalid directory: '+dir);
|
|
return;
|
|
}
|
|
clite.io.close(fd);
|
|
clite.shell.env.PWD = dir;
|
|
clite.shell.prompt.generate();
|
|
},
|
|
pwd:function(args,io) {
|
|
io.write(clite.shell.env.PWD);
|
|
},
|
|
echo:function(args,io) {
|
|
args.shift();
|
|
var txt = args.join(' ');
|
|
io.write(txt);
|
|
},
|
|
which:function(args,io) {
|
|
if (args.length <2)
|
|
return;
|
|
if (typeof clite.shell.macro[args[1]] != 'undefined') {
|
|
return;
|
|
}else{
|
|
var path = clite.shell.resolvePath(args[1]);
|
|
var fd = clite.io.open(path);
|
|
if (!fd)
|
|
return;
|
|
clite.io.close(fd);
|
|
io.write(args[1]+' is '+path);
|
|
}
|
|
},
|
|
type:function(args,io) {
|
|
if (args.length <2)
|
|
return;
|
|
if (typeof clite.shell.macro[args[1]] != 'undefined') {
|
|
io.write(args[1]+' is a shell builtin');
|
|
}else{
|
|
var path = clite.shell.resolvePath(args[1]);
|
|
var fd = clite.io.open(path);
|
|
if (!fd)
|
|
return;
|
|
clite.io.close(fd);
|
|
io.write(args[1]+' is '+path);
|
|
}
|
|
},
|
|
whoami:function(args,io) {
|
|
io.write(clite.shell.env.USER);
|
|
},
|
|
alias:function(args,io) {
|
|
io.write('unimplemented');
|
|
},
|
|
export:function(args,io) {
|
|
if (args.length == 1) {
|
|
Object.keys(clite.shell.env,function(key) {
|
|
io.write(key+'='+clite.shell.env[key]);
|
|
});
|
|
return;
|
|
}
|
|
var parts = args[1].split('=');
|
|
if (parts.length != 2)
|
|
return;
|
|
clite.shell.env[parts[0]] = parts[1];
|
|
}
|
|
},
|
|
prompt:{
|
|
list:[],
|
|
data:'# ',
|
|
set:function(txt) {
|
|
clite.shell.prompt.list = [];
|
|
clite.shell.prompt.data = txt;
|
|
},
|
|
push:function(txt) {
|
|
clite.shell.prompt.list.push(clite.shell.prompt.data);
|
|
clite.shell.prompt.data = txt;
|
|
},
|
|
pop:function() {
|
|
clite.shell.prompt.data = clite.shell.prompt.list.pop();
|
|
},
|
|
generate:function() {
|
|
var p = clite.shell.env.USER+':'+clite.shell.env.PWD;
|
|
if (clite.user.getUID() == 0) {
|
|
p += '# ';
|
|
}else{
|
|
p += '$ ';
|
|
}
|
|
clite.shell.prompt.set(p);
|
|
},
|
|
get:function() {
|
|
return clite.shell.prompt.data;
|
|
},
|
|
getHTML:function() {
|
|
return clite.lib.htmlEncode(clite.shell.prompt.data);
|
|
}
|
|
},
|
|
history:{
|
|
data:[],
|
|
current:'',
|
|
index:0,
|
|
add:function(txt) {
|
|
if (txt.length < 1)
|
|
return;
|
|
clite.shell.history.data.push(txt);
|
|
clite.shell.history.index = clite.shell.history.data.length;
|
|
},
|
|
up:function() {
|
|
if (clite.shell.history.index == 0)
|
|
return;
|
|
clite.shell.history.index--;
|
|
clite.events.refocus();
|
|
},
|
|
down:function() {
|
|
if (clite.shell.history.index == clite.shell.history.data.length)
|
|
return;
|
|
clite.shell.history.index++;
|
|
clite.events.refocus();
|
|
},
|
|
setCurrent:function(txt) {
|
|
clite.shell.history.current = txt;
|
|
},
|
|
getCurrent:function() {
|
|
if (clite.shell.history.index == clite.shell.history.data.length)
|
|
return clite.shell.history.current;
|
|
return clite.shell.history.data[clite.shell.history.index];
|
|
},
|
|
resetCurrent:function() {
|
|
clite.shell.history.current = '';
|
|
clite.shell.history.index = clite.shell.history.data.length;
|
|
}
|
|
}
|
|
};
|
|
|
|
clite.lib = {
|
|
// makes text html safe
|
|
htmlEncode:function(txt) {
|
|
return txt
|
|
.replace(/&/g, '&')
|
|
.replace(/'/g, '&apos')
|
|
.replace(/"/g, '"')
|
|
.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('/');
|
|
},
|
|
// returns an absolute path: ('foo','/etc') -> '/etc/foo': ('foo','bar') -> $PWD/bar/foo
|
|
resolvePath:function(txt,base) {
|
|
if (txt[0] == '/')
|
|
return txt;
|
|
if (txt[0] == '~')
|
|
return clite.shell.env.HOME+txt.substring(1);
|
|
if (typeof base != 'string')
|
|
base = clite.shell.env.PWD;
|
|
if (base[0] != '/')
|
|
base = clite.shell.env.PWD+'/'+base;
|
|
return base+'/'+txt;
|
|
},
|
|
// convert a string into an argument array: 'ls /etc' -> ['ls',/etc]
|
|
strToArgs:function(txt) {
|
|
// TODO: split this properly (escape strings and insert env variables)
|
|
var parts = [];
|
|
var s = '';
|
|
var e = false;
|
|
var q = false;
|
|
for (var i=0; i<txt.length; i++) {
|
|
switch (txt[i]) {
|
|
case '//':
|
|
if (e) {
|
|
s+='//';
|
|
}else{
|
|
e = true;
|
|
}
|
|
break;
|
|
case '"':
|
|
if (!e) {
|
|
if (!q) {
|
|
q = true;
|
|
break;
|
|
}else{
|
|
q = false;
|
|
}
|
|
}else{
|
|
s+='"';
|
|
}
|
|
case ' ':
|
|
if (!q) {
|
|
parts.push(s);
|
|
s = '';
|
|
e = false;
|
|
q = false;
|
|
}
|
|
break;
|
|
default:
|
|
s+=txt[i];
|
|
e = false;
|
|
}
|
|
}
|
|
if (s.length > 0)
|
|
parts.push(s);
|
|
return parts;//txt.split(' ');
|
|
}
|
|
}
|