mirror of
https://codeberg.org/TicklishHoneyBee/CLIte.git
synced 2026-03-11 09:04:37 +00:00
move shell to /bin/sh (and shell.js)
This commit is contained in:
parent
279d1afbc2
commit
b34d9590cb
3 changed files with 440 additions and 461 deletions
629
clite/core.js
629
clite/core.js
|
|
@ -34,7 +34,7 @@ var clite = {
|
|||
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);
|
||||
//clite.shell.writeLine('open file: '+name);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -79,7 +79,7 @@ var clite = {
|
|||
this.state.isinit = true;
|
||||
|
||||
this.core.execSafeAsync(function() {
|
||||
clite.term.clear(false);
|
||||
clite.term.clear();
|
||||
var vfsapi = null;
|
||||
|
||||
function defaultDevices() {
|
||||
|
|
@ -196,8 +196,9 @@ var clite = {
|
|||
buffer:[],
|
||||
input:function(str) {
|
||||
if (n.data.content.receive.callback != null) {
|
||||
var fn = n.data.content.receive.callback;
|
||||
clite.core.execSafeAsync(function() {
|
||||
n.data.content.receive.callback(str);
|
||||
fn(str);
|
||||
});
|
||||
n.data.content.receive.callback = null;
|
||||
return;
|
||||
|
|
@ -212,6 +213,8 @@ var clite = {
|
|||
return true;
|
||||
}
|
||||
n.data.content.receive.callback = cb;
|
||||
clite.term.genForm();
|
||||
clite.term.data.field.focus();
|
||||
return true;
|
||||
},
|
||||
write:clite.term.writeLine
|
||||
|
|
@ -350,6 +353,10 @@ readme.txt:/usr/clite/readme:0:0:-rw-r--r--`;
|
|||
clite.core.load.script('clite/commands.js',init3);
|
||||
}
|
||||
function init3(data) {
|
||||
clite.core.execSafe(clite.commands.data);
|
||||
clite.core.load.script('clite/shell.js',init4);
|
||||
}
|
||||
function init4(data) {
|
||||
clite.core.execSafe(clite.commands.data);
|
||||
clite.commands = null;
|
||||
clite.log.write('Bringing up the process manager');
|
||||
|
|
@ -380,22 +387,56 @@ readme.txt:/usr/clite/readme:0:0:-rw-r--r--`;
|
|||
// read in /usr/share/introduction and write to shell
|
||||
if (window.location.protocol == 'file:') {
|
||||
clite.user.genGuest();
|
||||
clite.shell.writeLine(clite_text);
|
||||
//clite.shell.writeLine(clite_text);
|
||||
}else{
|
||||
var fd = clite.io.open('/usr/share/introduction',function(fd) {
|
||||
clite.user.genGuest();
|
||||
clite.shell.writeLine(clite_text);
|
||||
if (!fd)
|
||||
return;
|
||||
var d = clite.io.readAll(fd);
|
||||
if (d != null)
|
||||
clite.shell.writeLine(d);
|
||||
clite.io.close(fd);
|
||||
//var fd = clite.io.open('/usr/share/introduction',function(fd) {
|
||||
//clite.user.genGuest();
|
||||
//clite.shell.writeLine(clite_text);
|
||||
//if (!fd)
|
||||
//return;
|
||||
//var d = clite.io.readAll(fd);
|
||||
//if (d != null)
|
||||
//clite.shell.writeLine(d);
|
||||
//clite.io.close(fd);
|
||||
|
||||
});
|
||||
//});
|
||||
}
|
||||
// create the login shell
|
||||
// should really have init/getty exec the shell with the env data etc
|
||||
var env = clite.user.getEnv();
|
||||
var io = {
|
||||
stdin:clite.io.open('/dev/tty',false),
|
||||
stdout:clite.io.open('/dev/tty',false),
|
||||
stderr:clite.io.open('/dev/tty',false),
|
||||
exit:null,
|
||||
include:function(name) {
|
||||
switch (name) {
|
||||
case 'stdlib':
|
||||
return clite.lib.api;
|
||||
break;
|
||||
case 'stdio':
|
||||
return clite.io;
|
||||
break;
|
||||
case 'term':
|
||||
return clite.term.api;
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
istty:{
|
||||
stdin:true, // false if stdin (read) is not clite.shell.readLine
|
||||
stdout:true // false if stdout (write) is not clite.term.writeLine
|
||||
}
|
||||
};
|
||||
clite.lib.fork(env,io,init5);
|
||||
}
|
||||
}
|
||||
function init5(env,io) {
|
||||
clite.lib.exec('/bin/sh',['sh'],env,io);
|
||||
}
|
||||
|
||||
init1();
|
||||
|
||||
|
|
@ -577,9 +618,6 @@ clite.user = {
|
|||
vfs.mkLink('/usr/home/guest/web','/usr/share/site');
|
||||
|
||||
setLogin(1);
|
||||
|
||||
// should really have init/getty exec the shell with the env data etc
|
||||
clite.shell.init();
|
||||
}
|
||||
clite.user.getEnv = function() {
|
||||
return structuredClone(udata.env);
|
||||
|
|
@ -900,6 +938,13 @@ clite.io = {
|
|||
var fd = getFileDes(path,true,false);
|
||||
return clite.io.fchmod(fd,mode);
|
||||
}
|
||||
clite.io.isatty = function(fd) {
|
||||
return fd.node.data.istty;
|
||||
}
|
||||
clite.io.fprintf = function(fd,fmt) {
|
||||
// TODO: actually support formatting
|
||||
return clite.io.write(fd,fmt);
|
||||
}
|
||||
},
|
||||
// 0: unknown
|
||||
// 1: text file
|
||||
|
|
@ -936,7 +981,9 @@ clite.io = {
|
|||
stat:null,
|
||||
fstat:null,
|
||||
chmod:null,
|
||||
fchmod:null
|
||||
fchmod:null,
|
||||
isatty:null,
|
||||
fprintf:null
|
||||
};
|
||||
|
||||
clite.vfs = {
|
||||
|
|
@ -1174,54 +1221,35 @@ clite.term = {
|
|||
type:'text',
|
||||
form:null,
|
||||
field:null,
|
||||
handler:null
|
||||
handler:null,
|
||||
prompt:''
|
||||
},
|
||||
events:{
|
||||
keydown:function(e) {
|
||||
return clite.term.events.keypress(e);
|
||||
},
|
||||
keypress:function(e) {
|
||||
if (e.key == 'Tab') {
|
||||
clite.shell.tabfill();
|
||||
clite.term.events.refocus();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
keyup:function(e) {
|
||||
//if (e.key.length > 1 && e.key != 'Spacebar' && clite.term.data.handler != null) {
|
||||
//if (e.key == 'Enter') {
|
||||
//clite.term.data.handler(e.target.value);
|
||||
//}else{
|
||||
//clite.term.data.handler(e.key);
|
||||
//}
|
||||
//}
|
||||
try {
|
||||
if (e.key == 'Down' || e.key == 'ArrowDown') {
|
||||
clite.shell.history.down();
|
||||
return false;
|
||||
if (e.key.length > 1 && e.key != 'Spacebar' && clite.term.data.handler != null) {
|
||||
if (e.key == 'Enter') {
|
||||
clite.term.data.handler(e.target.value);
|
||||
}else{
|
||||
clite.term.data.handler('\1'+e.key);
|
||||
}
|
||||
if (e.key == 'Up' || e.key == 'ArrowUp') {
|
||||
clite.shell.history.up();
|
||||
return false;
|
||||
}
|
||||
if (e.key == 'Tab') {
|
||||
return false;
|
||||
}
|
||||
var t = clite.term.data.field.value;
|
||||
if (e.key != 'Enter') {
|
||||
clite.shell.history.setCurrent(t);
|
||||
return true;
|
||||
}
|
||||
clite.shell.run(t);
|
||||
return false;
|
||||
} catch(err) {return false;}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
refocus:function(e) {
|
||||
try {
|
||||
clite.term.genForm();
|
||||
clite.term.data.field.focus();
|
||||
} catch(err) {}
|
||||
clite.core.execSafeAsync(function() {
|
||||
try{
|
||||
clite.term.data.field.focus()
|
||||
} catch(err) {
|
||||
clite.term.genForm();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
tty:{
|
||||
|
|
@ -1242,10 +1270,8 @@ clite.term = {
|
|||
return clite.term.tty.data.data.onkeypress(e);
|
||||
}
|
||||
},
|
||||
clear:function(form) {
|
||||
clear:function() {
|
||||
document.getElementById('terminal').innerHTML = '';
|
||||
if (form)
|
||||
clite.term.genForm();
|
||||
},
|
||||
preventInput:function() {
|
||||
return;
|
||||
|
|
@ -1276,11 +1302,11 @@ clite.term = {
|
|||
f.id = 'form';
|
||||
f.innerHTML = '';
|
||||
var l = document.createElement('label');
|
||||
l.innerHTML = clite.shell.prompt.getHTML();
|
||||
l.innerHTML = clite.lib.htmlEncode(clite.term.data.prompt);
|
||||
f.appendChild(l);
|
||||
var i = document.createElement('input');
|
||||
i.type = clite.term.getType();
|
||||
i.value = clite.shell.history.getCurrent();
|
||||
//i.value = clite.shell.history.getCurrent();
|
||||
f.appendChild(i);
|
||||
clite.term.data.form = f;
|
||||
clite.term.data.field = i;
|
||||
|
|
@ -1288,19 +1314,8 @@ clite.term = {
|
|||
if (!t)
|
||||
return;
|
||||
t.appendChild(f);
|
||||
if (clite.term.tty.data != null) {
|
||||
i.onkeydown = clite.term.tty.keydown;
|
||||
i.onkeypress = clite.term.tty.keypress;
|
||||
i.onkeyup = clite.term.tty.keyup;
|
||||
}else if (clite.shell.reading.is) {
|
||||
i.onkeydown = clite.shell.reading.keydown;
|
||||
i.onkeypress = clite.shell.reading.keypress;
|
||||
i.onkeyup = clite.shell.reading.keyup;
|
||||
}else{
|
||||
i.onkeydown = clite.term.events.keydown;
|
||||
i.onkeypress = clite.term.events.keypress;
|
||||
i.onkeyup = clite.term.events.keyup;
|
||||
}
|
||||
i.onkeydown = clite.term.events.keydown;
|
||||
i.onkeyup = clite.term.events.keyup;
|
||||
i.onfocusout = clite.term.events.refocus;
|
||||
i.onblur = clite.term.events.refocus;
|
||||
if (i.type == 'file') { // this is only needed due to dirty hacks to read in a file: path
|
||||
|
|
@ -1339,10 +1354,41 @@ clite.term = {
|
|||
return 'text';
|
||||
},
|
||||
api:{
|
||||
clear:function() { // clears the terminal
|
||||
clite.term.clear(false);
|
||||
// clears the terminal
|
||||
clear:function() {
|
||||
clite.term.clear();
|
||||
},
|
||||
opentty:function() { // gets an empty element with key events (allows view/less to display content in a clean element)
|
||||
ttyctrl:function(fn,v) {
|
||||
switch (fn) {
|
||||
case 'iget': // returns the current input string - bypassing tty read
|
||||
try{
|
||||
return clite.term.data.field.value;
|
||||
} catch(err) {
|
||||
return '';
|
||||
}
|
||||
break;
|
||||
case 'iset': // sets the current input string
|
||||
try{
|
||||
if (typeof v !== 'string')
|
||||
v = '';
|
||||
clite.term.data.field.value = v;
|
||||
return true;
|
||||
} catch(err) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'prompt': // sets the prompt for input
|
||||
if (typeof v !== 'string')
|
||||
v = '';
|
||||
clite.term.data.prompt = v;
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
},
|
||||
// gets an empty element with key events (allows view/less to display content in a clean element)
|
||||
opentty:function() {
|
||||
var tty = {
|
||||
handleKeys:function(cb) {
|
||||
this.data.cb = cb;
|
||||
|
|
@ -1411,384 +1457,6 @@ clite.term = {
|
|||
}
|
||||
};
|
||||
|
||||
clite.shell = {
|
||||
reading:{
|
||||
is:false,
|
||||
buff:'',
|
||||
callback:null,
|
||||
keydown:function(e) {
|
||||
if (e.key == 'Tab')
|
||||
return false;
|
||||
},
|
||||
keypress:function(e) {
|
||||
if (e.key == 'Tab') {
|
||||
clite.term.events.refocus();
|
||||
return false;
|
||||
}
|
||||
},
|
||||
keyup:function(e) {
|
||||
if (e.key.length > 1 && e.key != 'Spacebar') {
|
||||
clite.shell.reading.is = false;
|
||||
clite.term.preventInput();
|
||||
if (e.key == 'Enter') {
|
||||
clite.shell.reading.callback(clite.shell.reading.buff);
|
||||
}else{
|
||||
clite.shell.reading.callback(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
clite.shell.reading.buff = e.target.value;
|
||||
}
|
||||
},
|
||||
init:function() {
|
||||
clite.shell.env = clite.user.getEnv();
|
||||
clite.shell.prompt.generate();
|
||||
clite.term.events.refocus();
|
||||
},
|
||||
exit:function(pid,v) {
|
||||
if (pid > -1)
|
||||
clite.proc.exit(pid);
|
||||
if (clite.term.tty.data != null)
|
||||
clite.term.api.closetty(clite.term.tty.data);
|
||||
clite.shell.prompt.pop();
|
||||
clite.term.events.refocus();
|
||||
},
|
||||
readLine:function(cb) {
|
||||
clite.shell.reading.is = true;
|
||||
clite.shell.reading.buff = '';
|
||||
clite.shell.reading.callback = cb;
|
||||
clite.term.events.refocus();
|
||||
},
|
||||
writeLine:function(txt) {
|
||||
clite.term.writeLine(txt);
|
||||
clite.term.events.refocus();
|
||||
},
|
||||
resolvePath:function(txt) {
|
||||
var paths = clite.shell.env.PATH.split(':');
|
||||
var path = null;
|
||||
paths.forEach(function(p) {
|
||||
if (path)
|
||||
return;
|
||||
var rp = clite.lib.resolvePath(txt,p);
|
||||
var fd = clite.io.open(rp,false);
|
||||
if (fd) {
|
||||
clite.io.close(fd);
|
||||
path = rp;
|
||||
}
|
||||
});
|
||||
return path;
|
||||
},
|
||||
include:function(name) {
|
||||
switch (name) {
|
||||
case 'stdlib':
|
||||
return clite.lib.api;
|
||||
break;
|
||||
case 'stdio':
|
||||
return clite.io;
|
||||
break;
|
||||
case 'term':
|
||||
return clite.term.api;
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
preprocess:function(txt) {
|
||||
var parts = txt.split('$');
|
||||
var pptxt = '';
|
||||
if (parts.length == 1)
|
||||
return txt;
|
||||
parts.forEach(function(p,i) {
|
||||
if (i > 0) {
|
||||
var b = p;
|
||||
var e = '';
|
||||
var pi = b.indexOf(' ');
|
||||
if (pi > 0) {
|
||||
e = b.substring(pi);
|
||||
b = b.substring(0,pi);
|
||||
}
|
||||
if (b[0] == '{') {
|
||||
var pi = b.indexOf('}');
|
||||
b = b.substring(1,pi);
|
||||
}
|
||||
if (typeof clite.shell.env[b] === 'string') {
|
||||
b = clite.shell.env[b];
|
||||
}else{
|
||||
b = '';
|
||||
}
|
||||
pptxt = b+e;
|
||||
return;
|
||||
}
|
||||
pptxt += p;
|
||||
});
|
||||
txt = pptxt;
|
||||
parts = txt.split('`');
|
||||
pptxt = '';
|
||||
if (parts.length == 1)
|
||||
return txt;
|
||||
parts.forEach(function(p,i) {
|
||||
if (i > 0) {
|
||||
var b = p;
|
||||
var e = '';
|
||||
var pi = b.indexOf('`');
|
||||
if (pi > 0) {
|
||||
e = b.substring(pi);
|
||||
b = b.substring(0,pi);
|
||||
}
|
||||
// TODO: replace b with the output of the command in b (command substitution)
|
||||
pptxt = b+e;
|
||||
return;
|
||||
}
|
||||
pptxt += p;
|
||||
});
|
||||
return pptxt;
|
||||
},
|
||||
run:function(txt) {
|
||||
if (txt.length < 1) {
|
||||
clite.shell.writeLine(clite.shell.prompt.get());
|
||||
return;
|
||||
}
|
||||
clite.shell.history.add(txt);
|
||||
clite.shell.history.resetCurrent();
|
||||
clite.shell.writeLine(clite.shell.prompt.get()+txt);
|
||||
clite.shell.prompt.push('');
|
||||
clite.term.preventInput();
|
||||
// split command line into arguments
|
||||
var args = clite.lib.strToArgs(txt);
|
||||
args.forEach(function(val,i) {
|
||||
args[i] = clite.shell.preprocess(val);
|
||||
});
|
||||
// prepare io (stdin,stdout,stderr)
|
||||
// TODO: check for io redirects and piping, then setup stdio accordingly
|
||||
var io = {
|
||||
read:clite.shell.readLine, // stdin
|
||||
write:clite.term.writeLine, // stdout
|
||||
error:clite.term.writeLine, // stderr
|
||||
exit:clite.shell.exit, // equivalent to C's exit(), needed by asynchronous programs
|
||||
include:clite.shell.include, // include libraries, allows them to be used in a program
|
||||
istty:{
|
||||
stdin:true, // false if stdin (read) is not clite.shell.readLine
|
||||
stdout:true // false if stdout (write) is not clite.term.writeLine
|
||||
}
|
||||
};
|
||||
// check for a macro
|
||||
// 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,io);
|
||||
clite.shell.exit(r);
|
||||
return;
|
||||
}
|
||||
|
||||
var path = clite.shell.resolvePath(args[0]);
|
||||
if (!path) {
|
||||
clite.term.writeLine('Shell: unknown command: '+args[0]);
|
||||
clite.shell.exit(-1,-1);
|
||||
return;
|
||||
}
|
||||
|
||||
function execFunc(env,io) {
|
||||
var r = clite.lib.exec(path,args,env,io);
|
||||
if (r == 0)
|
||||
return;
|
||||
|
||||
if (r == -1) {
|
||||
clite.term.writeLine('Shell: unknown command: '+args[0]);
|
||||
clite.shell.exit(-1,-2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (r == -2) {
|
||||
clite.term.writeLine('Shell: error in command:'+args[0]);
|
||||
clite.shell.exit(-1,-3);
|
||||
}
|
||||
}
|
||||
if (!clite.lib.fork(clite.shell.env,io,execFunc)) {
|
||||
clite.term.writeLine('Shell: internal error');
|
||||
clite.shell.exit(-1,-1);
|
||||
}
|
||||
},
|
||||
tabfill:function() {
|
||||
var c = clite.shell.history.getCurrent();
|
||||
var parts = clite.lib.strToArgs(c);
|
||||
var ai = parts.length-1;
|
||||
if (ai < 0)
|
||||
return;
|
||||
var a = parts[ai];
|
||||
var add = '';
|
||||
if (ai == 0) {
|
||||
// test for shell macros/builtins
|
||||
Object.keys(clite.shell.macro).forEach(function(key) {
|
||||
if (add == '' && key.substring(0,a.length) == a) {
|
||||
add = key.substring(a.length);
|
||||
}
|
||||
});
|
||||
if (add == '') {
|
||||
// test for commands
|
||||
var pparts = clite.shell.env.PATH.split(':');
|
||||
pparts.forEach(function(pp) {
|
||||
if (add != '')
|
||||
return;
|
||||
var fd = clite.io.open('/bin');
|
||||
var f;
|
||||
while (add == '' && (f = clite.io.read(fd)) != null) {
|
||||
if (f.name.substring(0,a.length) == a) {
|
||||
add = f.name.substring(a.length)+' ';
|
||||
}
|
||||
}
|
||||
clite.io.close(fd);
|
||||
});
|
||||
}
|
||||
}else{
|
||||
// TODO: path fill
|
||||
}
|
||||
if (add == '')
|
||||
return;
|
||||
clite.shell.history.setCurrent(c+add);
|
||||
},
|
||||
env:{},
|
||||
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,false);
|
||||
if (!fd) {
|
||||
io.error('invalid directory: '+dir);
|
||||
return;
|
||||
}
|
||||
if (!fd.node.data.isdir) {
|
||||
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]);
|
||||
if (!path)
|
||||
return;
|
||||
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]);
|
||||
if (!path)
|
||||
return;
|
||||
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).forEach(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() {
|
||||
if (clite.shell.prompt.list.length > 0)
|
||||
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.term.events.refocus();
|
||||
},
|
||||
down:function() {
|
||||
if (clite.shell.history.index == clite.shell.history.data.length)
|
||||
return;
|
||||
clite.shell.history.index++;
|
||||
clite.term.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) {
|
||||
|
|
@ -1812,14 +1480,16 @@ clite.lib = {
|
|||
resolvePath:function(txt,base) {
|
||||
if (txt == '/')
|
||||
return txt;
|
||||
if (txt[0] == '~')
|
||||
txt = clite.shell.env.HOME+txt.substring(1);
|
||||
var env = clite.user.getEnv();
|
||||
if (txt[0] == '~') {
|
||||
txt = env.HOME+txt.substring(1);
|
||||
}
|
||||
var path = txt;
|
||||
if (txt[0] != '/') {
|
||||
if (typeof base != 'string')
|
||||
base = clite.shell.env.PWD;
|
||||
base = env.PWD;
|
||||
if (base[0] != '/')
|
||||
base = clite.shell.env.PWD+'/'+base;
|
||||
base = env.PWD+'/'+base;
|
||||
path = base+'/'+txt;
|
||||
}
|
||||
var parts = path.split('/');
|
||||
|
|
@ -1974,16 +1644,39 @@ clite.lib = {
|
|||
var pid = clite.proc.add(call);
|
||||
if (!pid)
|
||||
return 0;
|
||||
io.exit = function(v) {
|
||||
clite.shell.exit(pid,v);
|
||||
var nio = Object.create(io);
|
||||
nio.exit = function(v) {
|
||||
clite.lib.exit(pid,v);
|
||||
}
|
||||
nio.include = function(name) {
|
||||
switch (name) {
|
||||
case 'stdlib':
|
||||
return Object.create(clite.lib.api);
|
||||
break;
|
||||
case 'stdio':
|
||||
var stdio = Object.create(clite.io);
|
||||
stdio.printf = function(fmt) {
|
||||
// TODO: pass arguments through
|
||||
return stdio.fprintf(nio.stdout,fmt);
|
||||
}
|
||||
return stdio;
|
||||
break;
|
||||
case 'term':
|
||||
return Object.create(clite.term.api);
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
clite.core.execSafeAsync(function() {
|
||||
call(structuredClone(env),io);
|
||||
call(structuredClone(env),nio);
|
||||
});
|
||||
return pid;
|
||||
},
|
||||
exec:function(path,args,env,io) {
|
||||
var fd = clite.io.open(path);
|
||||
var fd = clite.io.open(path,false);
|
||||
if (!fd)
|
||||
return -1;
|
||||
|
||||
|
|
@ -2000,6 +1693,18 @@ clite.lib = {
|
|||
return -2;
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
exit:function(pid,v) {
|
||||
if (pid > -1)
|
||||
clite.proc.exit(pid);
|
||||
if (clite.term.tty.data != null)
|
||||
clite.term.api.closetty(clite.term.tty.data);
|
||||
},
|
||||
getuid:function() {
|
||||
return clite.user.getUID();
|
||||
},
|
||||
getgid:function() {
|
||||
return clite.user.getGID();
|
||||
}
|
||||
}
|
||||
clite.lib.api = { // this is passed to programs via include('stdlib')
|
||||
|
|
@ -2009,5 +1714,7 @@ clite.lib.api = { // this is passed to programs via include('stdlib')
|
|||
strToArgs:clite.lib.strToArgs,
|
||||
uname:clite.lib.uname,
|
||||
fork:clite.lib.fork,
|
||||
exec:clite.lib.exec
|
||||
exec:clite.lib.exec,
|
||||
getuid:clite.lib.getuid,
|
||||
getgid:clite.lib.getgid
|
||||
};
|
||||
|
|
|
|||
269
clite/shell.js
Normal file
269
clite/shell.js
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
clite.commands.data = function() {
|
||||
// insert commands below this line
|
||||
|
||||
clite.commands.load('sh',function(args,env,io) {
|
||||
var stdio = io.include('stdio');
|
||||
var stdlib = io.include('stdlib');
|
||||
var term = io.include('term');
|
||||
var prompt = '$ ';
|
||||
var macro = {
|
||||
clear:function(args,io) {
|
||||
term.clear();
|
||||
},
|
||||
cd:function(args,io) {
|
||||
var dir = env.HOME;
|
||||
if (args.length > 1) {
|
||||
dir = stdlib.resolvePath(args[1]);
|
||||
}
|
||||
var fd = stdio.open(dir,false);
|
||||
if (!fd) {
|
||||
stdio.write(io.stderr,'invalid directory: '+dir);
|
||||
return;
|
||||
}
|
||||
if (!fd.node.data.isdir) {
|
||||
stdio.write(io.stderr,'invalid directory: '+dir);
|
||||
return;
|
||||
}
|
||||
stdio.close(fd);
|
||||
env.PWD = dir;
|
||||
},
|
||||
pwd:function(args,io) {
|
||||
stdio.write(io.stdout,env.PWD);
|
||||
},
|
||||
echo:function(args,io) {
|
||||
args.shift();
|
||||
var txt = args.join(' ');
|
||||
stdio.write(io.stdout,txt);
|
||||
},
|
||||
which:function(args,io) {
|
||||
if (args.length <2)
|
||||
return;
|
||||
if (typeof macro[args[1]] != 'undefined') {
|
||||
return;
|
||||
}else{
|
||||
var path = resolvePATH(args[1]);
|
||||
if (!path)
|
||||
return;
|
||||
stdio.write(io.stdout,args[1]+' is '+path);
|
||||
}
|
||||
},
|
||||
type:function(args,io) {
|
||||
if (args.length <2)
|
||||
return;
|
||||
if (typeof macro[args[1]] != 'undefined') {
|
||||
stdio.write(io.stdout,args[1]+' is a shell builtin');
|
||||
}else{
|
||||
var path = resolvePATH(args[1]);
|
||||
if (!path)
|
||||
return;
|
||||
stdio.write(io.stdout,args[1]+' is '+path);
|
||||
}
|
||||
},
|
||||
whoami:function(args,io) {
|
||||
stdio.write(io.stdout,env.USER);
|
||||
},
|
||||
alias:function(args,io) {
|
||||
stdio.write(io.stdout,'unimplemented');
|
||||
},
|
||||
export:function(args,io) {
|
||||
if (args.length == 1) {
|
||||
Object.keys(env).forEach(function(key) {
|
||||
stdio.write(io.stdout,key+'='+env[key]);
|
||||
});
|
||||
return;
|
||||
}
|
||||
var parts = args[1].split('=');
|
||||
if (parts.length != 2)
|
||||
return;
|
||||
env[parts[0]] = parts[1];
|
||||
}
|
||||
};
|
||||
|
||||
function resolvePATH(txt) {
|
||||
if (typeof env.PATH === 'undefined')
|
||||
env.PATH = '/bin';
|
||||
var paths = env.PATH.split(':');
|
||||
var path = null;
|
||||
paths.forEach(function(p) {
|
||||
if (path)
|
||||
return;
|
||||
var rp = stdlib.resolvePath(txt,p);
|
||||
var fd = stdio.open(rp,false);
|
||||
if (fd) {
|
||||
stdio.close(fd);
|
||||
path = rp;
|
||||
}
|
||||
});
|
||||
return path;
|
||||
}
|
||||
|
||||
function preprocess(txt) {
|
||||
var parts = txt.split('$');
|
||||
var pptxt = '';
|
||||
if (parts.length == 1)
|
||||
return txt;
|
||||
parts.forEach(function(p,i) {
|
||||
if (i > 0) {
|
||||
var b = p;
|
||||
var e = '';
|
||||
if (b[0] == '{') {
|
||||
var pi = b.indexOf('}');
|
||||
e = b.substring(pi+1);
|
||||
b = b.substring(1,pi);
|
||||
}else{
|
||||
var pi = b.indexOf(' ');
|
||||
if (pi > 0) {
|
||||
e = b.substring(pi);
|
||||
b = b.substring(0,pi);
|
||||
}
|
||||
}
|
||||
if (typeof env[b] === 'string') {
|
||||
b = env[b];
|
||||
}else{
|
||||
b = '';
|
||||
}
|
||||
pptxt += b+e;
|
||||
return;
|
||||
}
|
||||
pptxt += p;
|
||||
});
|
||||
txt = pptxt;
|
||||
parts = txt.split('`');
|
||||
pptxt = '';
|
||||
if (parts.length == 1)
|
||||
return txt;
|
||||
parts.forEach(function(p,i) {
|
||||
if (i > 0) {
|
||||
var b = p;
|
||||
var e = '';
|
||||
var pi = b.indexOf('`');
|
||||
if (pi > 0) {
|
||||
e = b.substring(pi);
|
||||
b = b.substring(0,pi);
|
||||
}
|
||||
// TODO: replace b with the output of the command in b (command substitution)
|
||||
pptxt = b+e;
|
||||
return;
|
||||
}
|
||||
pptxt += p;
|
||||
});
|
||||
return pptxt;
|
||||
}
|
||||
|
||||
function run(txt) {
|
||||
if (txt.length < 1) {
|
||||
stdio.write(io.stdout,prompt);
|
||||
inputRead();
|
||||
return;
|
||||
}
|
||||
//clite.shell.history.add(txt);
|
||||
//clite.shell.history.resetCurrent();
|
||||
stdio.write(io.stdout,prompt+txt);
|
||||
// clear the tty prompt
|
||||
term.ttyctrl('prompt','');
|
||||
// split command line into arguments
|
||||
var args = stdlib.strToArgs(txt);
|
||||
args.forEach(function(val,i) {
|
||||
args[i] = preprocess(val);
|
||||
});
|
||||
// prepare io (stdin,stdout,stderr)
|
||||
// TODO: check for io redirects and piping, then setup stdio accordingly
|
||||
var fio = {
|
||||
stdin:io.stdin,
|
||||
stdout:io.stdout,
|
||||
stderr:io.stderr,
|
||||
exit:null, // filled in by fork()
|
||||
include:null, // filled in by fork()
|
||||
};
|
||||
// check for a macro
|
||||
// then either run the macro or expand the program to be executed via PATH
|
||||
if (typeof macro[args[0]] != 'undefined') {
|
||||
var r = macro[args[0]](args,fio);
|
||||
inputRead();
|
||||
return;
|
||||
}
|
||||
|
||||
var path = resolvePATH(args[0]);
|
||||
if (!path) {
|
||||
stdio.write(io.stderr,'Shell: unknown command: '+args[0]);
|
||||
inputRead();
|
||||
return;
|
||||
}
|
||||
|
||||
function execFunc(env,io) {
|
||||
var r = stdlib.exec(path,args,env,io);
|
||||
if (r > 0)
|
||||
return;
|
||||
|
||||
if (r == -1)
|
||||
stdlib.write(io.stderr,'Shell: unknown command: '+args[0]);
|
||||
|
||||
if (r == -2)
|
||||
stdio.write(io.stderr,'Shell: error in command:'+args[0]);
|
||||
inputRead();
|
||||
}
|
||||
if (!stdlib.fork(env,fio,execFunc)) {
|
||||
stdio.write(io.stderr,'Shell: internal error');
|
||||
inputRead();
|
||||
}
|
||||
}
|
||||
|
||||
function inputControl(key) {
|
||||
//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;
|
||||
//}
|
||||
//if (e.key == 'Tab') {
|
||||
//return false;
|
||||
//}
|
||||
//var t = clite.term.data.field.value;
|
||||
//if (e.key != 'Enter') {
|
||||
//clite.shell.history.setCurrent(t);
|
||||
//return true;
|
||||
//}
|
||||
//clite.shell.run(t);
|
||||
//return false;
|
||||
//} catch(err) {return false;}
|
||||
}
|
||||
function input(str) {
|
||||
if (str[0] == '\1') {
|
||||
inputControl(str.substring(1));
|
||||
inputRead();
|
||||
return;
|
||||
}
|
||||
|
||||
run(str);
|
||||
}
|
||||
function inputRead() {
|
||||
// generate the prompt
|
||||
var p = env.PS1;
|
||||
if (typeof p === 'undefined')
|
||||
p = '${USER}:${PWD}'
|
||||
|
||||
prompt = preprocess(p);
|
||||
if (stdlib.getuid() == 0) {
|
||||
prompt += '# ';
|
||||
}else{
|
||||
prompt += '$ ';
|
||||
}
|
||||
// set the prompt
|
||||
term.ttyctrl('prompt',prompt);
|
||||
// then read from stdin
|
||||
stdio.read(io.stdin,input);
|
||||
}
|
||||
|
||||
// TODO: this check will exec future shell scripts
|
||||
if (!stdio.isatty(io.stdin))
|
||||
return 0;
|
||||
|
||||
inputRead();
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
}
|
||||
|
|
@ -356,6 +356,9 @@ stdio: io.include('stdio')
|
|||
The user has all permissions, the group has read and execute
|
||||
permissions, others have only read permissions.
|
||||
|
||||
isattty(fd)
|
||||
returns true if fd refers to a tty.
|
||||
|
||||
term: io.include('term')
|
||||
Provides access to raw terminal and tty functions.
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue