move shell to /bin/sh (and shell.js)

This commit is contained in:
Lisa Milne 2023-11-25 20:43:51 +10:00
parent 279d1afbc2
commit b34d9590cb
3 changed files with 440 additions and 461 deletions

View file

@ -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
View 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;
});
}

View file

@ -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.