shell tabfill and history back in place, ability to set tty into raw mode added

This commit is contained in:
Lisa Milne 2023-12-13 13:42:57 +10:00
parent 015af5b3ba
commit b6d539ec7f
6 changed files with 249 additions and 200 deletions

View file

@ -385,6 +385,8 @@ bios.input.keyboard = {
}
if (k.code == 0)
return false;
e.preventDefault();
bios.input.keyboard.internal.resetFocus();
bios.input.keybuffer.input(k);
return false;
},
@ -407,6 +409,7 @@ bios.input.keyboard = {
if (k.code == 0)
return false;
e.target.value = '';
bios.input.keyboard.internal.resetFocus();
bios.input.keybuffer.input(k);
return false;
},
@ -424,27 +427,27 @@ bios.input.keyboard = {
switch (key) {
case "Down":
case "ArrowDown":
k.char = String.fromCharCode(17);
k.char = -3;
k.lchr = k.char;
k.code = 17;
k.code = -3;
break;
case "Up":
case "ArrowUp":
k.char = String.fromCharCode(18);
k.char = -4;
k.lchr = k.char;
k.code = 18;
k.code = -4;
break;
case "Left":
case "ArrowLeft":
k.char = String.fromCharCode(19);
k.char = -5;
k.lchr = k.char;
k.code = 19;
k.code = -5;
break;
case "Right":
case "ArrowRight":
k.char = String.fromCharCode(20);
k.char = -6;
k.lchr = k.char;
k.code = 20;
k.code = -6;
break;
case "Enter":
k.char = String.fromCharCode(10);
@ -458,9 +461,9 @@ bios.input.keyboard = {
k.code = 27;
break;
case "Shift":
k.char = String.fromCharCode(15);
k.char = -2;
k.lchr = k.char;
k.code = 15;
k.code = -2;
break;
case "Tab":
k.char = String.fromCharCode(9);
@ -476,6 +479,11 @@ bios.input.keyboard = {
k.lchr = k.char;
k.code = 8;
break;
case 'Control':
k.char = -1;
k.lchar = k.char;
k.code = -1;
break;
default:
k.code = key;
}

View file

@ -320,24 +320,24 @@ Options:
return 0;
break;
default:
stdio.write(io.stderr,'unknown argument: -'+args[i][j]);
stdio.fprintf(io.stderr,'unknown argument: -%c\n',args[i][j]);
}
}
}else if (file == null) {
file = clite.resolvePath(args[i],false);
}else{
stdio.write(io.stderr,'unknown argument: '+args[i]);
stdio.fprintf(io.stderr,'unknown argument: %s\n',args[i]);
}
}
if (file == null) {
stdio.write(io.stderr,'no file specified');
stdio.write(io.stderr,'no file specified\n');
return 1;
}
function loadRemote(fd) {
if (!fd) {
stdio.write(io.stderr,'unable to create file: '+file);
stdio.fprintf(io.stderr,'unable to create file: %s\n',file);
io.exit(1);
return;
}

View file

@ -2145,7 +2145,8 @@ clite.tty = {
data:{
active:null,
activeID:-1,
ttys:[]
ttys:[],
control:false
},
internal:{
genTTY:function() {return null;},
@ -2220,8 +2221,8 @@ clite.tty = {
}
// special case, returns any non-character key found
if (t.buff[i].code <32 || t.buff[i].code > 126) {
k = t.buff.splice(i,1);
return t.code;
var k = t.buff.splice(i,1);
return k.code;
}
l += t.buff[i].char;
}
@ -2230,6 +2231,23 @@ clite.tty = {
t.buff = t.buff.slice(off);
return l;
},
getControl:function(ch) {
var key = {
down:false,
char:0,
lchar:0,
code:0
};
var ind = '-abcdefghijklmnopqrstuvwxyz[\\]^_?';
var i = ind.indexOf(ch);
if (i>0) {
key.char = String.fromCharCode(i);
key.lchar = key.char;
key.code = i;
return key;
}
return null;
}
},
init:function(vfsapi) {
@ -2283,21 +2301,44 @@ clite.tty = {
return true;
},
input:function(key) {
// TODO: check for control for special conversions
if (key.down)
if (key.down) {
if (key.code == -1)
clite.tty.data.control = true;
return;
}
if (key.code < 0) {
if (key.code == -1) {
clite.tty.data.control = false;
return;
}else{
if (clite.tty.data.active && typeof clite.tty.data.active.rcb === 'function') {
var f = clite.tty.data.active.rcb;
clite.tty.data.active.rcb = null;
f(key.code);
return;
}
}
}
if (clite.tty.data.control) {
var k = clite.tty.internal.getControl(key.char);
if (k)
key = k;
}
if (!clite.tty.data.active)
return;
var t = clite.tty.data.active;
// TODO: this hides control characters from echoing... do we want that?
if (t.echo && ((key.code > 31 && key.code < 127) || key.code == 10))
if (t.echo && ((key.code > 31 && key.code < 127) || key.code == 10 || key.code == 9))
clite.tty.write(t.id,key.char);
if (t.raw) {
if (typeof t.rcb === 'function') {
if ((key.code > 31 && key.code < 127) || key.code == 10) {
t.rcb(key.char);
var f = t.rcb;
t.rcb = null;
if ((key.code > 31 && key.code < 127) || key.code == 10 || key.code == 9) {
f(key.char);
}else{
t.rcb(key.code);
f(key.code);
}
}else{
t.buff.push(key);
@ -2406,6 +2447,29 @@ clite.tty = {
clite.console.drawBuff(t.data);
clite.console.setCursor(0,0);
},
ttyctrl:function(id,fn,v) {
if (id<0 || id>=clite.tty.data.ttys.length)
return;
var t = clite.tty.data.ttys[id];
switch (fn) {
case 'raw':
if (typeof v === 'boolean')
t.raw = v;
return t.raw;
break;
case 'atton':
break;
case 'attoff':
break;
case 'echo':
if (typeof v === 'boolean')
t.echo = v;
return t.echo;
break;
default:
return false;
}
}
};
@ -2516,120 +2580,6 @@ clite.console = {
}
}
clite.term = {
//events:{
//keyup:function(e) {
//if (clite.term.data.isalt && clite.term.data.alt.israwIn) {
//if (clite.term.data.handler != null) {
//if (e.key == 'Tab') {
//clite.term.data.handler('\t');
//}else{
//clite.term.data.handler(e.key);
//}
//}
//if (!clite.term.data.alt.echo) {
//e.target.value = '';
//clite.term.data.buff = '';
//return false;
//}
//}else if (e.key.length > 1 && e.key != 'Spacebar' && clite.term.data.handler != null) {
//if (e.key == 'Enter') {
//clite.term.data.buff = '';
//clite.term.data.handler(e.target.value);
//return false;
//}else{
//clite.term.data.handler('\1'+e.key);
//}
//}
//clite.term.data.buff = e.target.value;
//return false;
//},
//},
ttyctrl:function(fn,v) {
switch (fn) {
//case 'iget': // returns the current input string - bypassing tty read
//try{
//return clite.term.data.buff;
//} catch(err) {
//return '';
//}
//break;
//case 'iset': // sets the current input string
//try{
//if (typeof v !== 'string')
//v = '';
//clite.term.data.field.value = v;
//clite.term.data.buff = 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;
//case 'hide': // turns off input
//clite.term.data.show = false;
//var f = document.getElementById('form');
//if (!f)
//return;
//f.parentNode.removeChild(f);
//break;
//case 'show': // turns on input
//clite.term.data.show = true;
////clite.term.genForm();
//break;
//case 'alt': // switch to the alternate frame buffer
//if (typeof v === 'boolean') {
//clite.term.data.isalt = v;
//}
//if (!clite.term.data.isalt) {
//var t = document.getElementById('termalt');
//if (t)
//t.parentNode.removeChild(t);
//}else{
////clite.term.genAlt();
//}
////clite.term.genForm();
//return clite.term.data.isalt;
//break;
//case 'rawout':
//if (!clite.term.data.isalt)
//return false;
//if (typeof v === 'boolean') {
//clite.term.data.alt.israwOut = v;
//}
//return clite.term.data.alt.israwOut;
//break;
//case 'rawin':
//if (!clite.term.data.isalt)
//return false;
//if (typeof v === 'boolean') {
//clite.term.data.alt.israwIn = v;
//}
//return clite.term.data.alt.israwIn;
//break;
//case 'atton':
//break;
//case 'attoff':
//break;
//case 'echo':
//if (!clite.term.data.isalt)
//return false;
//if (typeof v === 'boolean') {
//clite.term.data.alt.echo = v;
//}
//return clite.term.data.alt.echo;
//break;
default:
return false;
}
}
};
clite.lib = {
// makes text html safe
htmlEncode:function(txt) {

View file

@ -2,6 +2,8 @@ clite.libs.data = function() {
clite.libs.load('libterm','term',function(io,env) {
// TODO: get the controlling tty id
return Object.create({
// clears the terminal
@ -11,8 +13,8 @@ return Object.create({
},
ttyctrl:function(fn,v) {
return false;
//return clite.term.ttyctrl(fn,v);
var ctty = 0;
return clite.tty.ttyctrl(ctty,fn,v);
}
});

View file

@ -30,7 +30,6 @@ clite.libs.load('libtime','time',function(io,env) {
t = t.toString();
}
clite.io.close(io.pid,fd);
console.log(t);
return parseInt(t,10);
}
function genTime(time,tz) {

View file

@ -6,7 +6,6 @@ clite.commands.load('sh',function(args,env,io) {
var stdlib = io.include('stdlib');
var term = io.include('term');
var clite = io.include('clite');
var prompt = '$ ';
var has_exited = false;
var macro = {
clear:function(args,io) {
@ -96,16 +95,16 @@ clite.commands.load('sh',function(args,env,io) {
history.index = history.data.length;
},
up:function() {
if (history.index == 0)
return;
if (history.index < 1)
return null;
history.index--;
term.ttyctrl('iset',history.getCurrent());
return history.getCurrent();
},
down:function() {
if (history.index >= history.data.length)
return;
return null;
history.index++;
term.ttyctrl('iset',history.getCurrent());
return history.getCurrent();
},
setCurrent:function(txt) {
history.current = txt;
@ -126,6 +125,17 @@ clite.commands.load('sh',function(args,env,io) {
}
}
function help() {
stdio.write(io.stdout,`
sh - command interpreter (shell)
Usage: sh [OPTION] [FILE]
Options:
-? Print this help information
`);
}
function resolvePATH(txt) {
if (typeof env.PATH === 'undefined')
env.PATH = '/bin';
@ -196,12 +206,11 @@ clite.commands.load('sh',function(args,env,io) {
return pptxt;
}
function tabfill() {
var c = '';//term.ttyctrl('iget');
function tabfill(c) {
var parts = clite.strToArgs(c);
var ai = parts.length-1;
if (ai < 0)
return;
return null;
var a = parts[ai];
var add = '';
if (ai == 0) {
@ -231,8 +240,9 @@ clite.commands.load('sh',function(args,env,io) {
// TODO: path fill
}
if (add == '')
return;
term.ttyctrl('iset',c+add);
return null;
stdio.write(io.stdout,'\b'+add);
return c+add;
}
function parseScript(fd) {
@ -323,7 +333,7 @@ clite.commands.load('sh',function(args,env,io) {
if (typeof p === 'undefined')
p = '${USER}:${PWD}'
prompt = preprocess(p);
var prompt = preprocess(p);
if (stdlib.getuid() == 0) {
prompt += '# ';
}else{
@ -333,66 +343,146 @@ clite.commands.load('sh',function(args,env,io) {
stdio.write(io.stdout,prompt);
}
function inputControl(key) {
switch(key) {
case 'Tab':
tabfill();
break;
case 'Down':
case 'ArrowDown':
history.down();
break;
case 'Up':
case 'ArrowUp':
history.up();
break;
default:;
}
}
function input(str) {
//if (str[0] == '\1') {
//inputControl(str.substring(1));
//inputRead();
//return;
//}
run(str,true);
}
function inputRead() {
if (has_exited)
return;
var c = '';
var cc = 0;
function inputProcess(str) {
switch (str) {
case '\t': // tab
case 9:
var ct = tabfill(c);
if (ct != null) {
c = ct;
cc = c.length;
}
break;
case '\n': // enter
term.ttyctrl('raw',false);
run(c,true);
c = '';
cc = 0;
return;
break;
case '\b':
case 8:
if (c.length > 0) {
cc--;
c = c.substring(0,cc);
stdio.write(io.stdout,'\b \b');
}
break;
case -3: // down arrow
var ct = history.down();
if (ct != null) {
for (var i=0; i<c.length; i++) {
stdio.write(io.stdout,'\b \b');
}
c = ct;
cc = c.length;
stdio.write(io.stdout,c);
}
break;
case -4: // up arrow
var ct = history.up();
if (ct != null) {
for (var i=0; i<c.length; i++) {
stdio.write(io.stdout,'\b \b');
}
c = ct;
cc = c.length;
stdio.write(io.stdout,c);
}
break;
default:
if (typeof str === 'string') {
c += str;
cc++;
history.setCurrent(c);
}
}
stdio.read(io.stdin,inputProcess);
}
// write the prompt
writePrompt();
// set tty to raw mode
term.ttyctrl('raw',true);
// then read from stdin
stdio.read(io.stdin,input);
stdio.read(io.stdin,inputProcess);
}
// run a shell script
if (!stdio.isatty(io.stdin)) {
parseScript(io.stdin);
return 0;
// otherwise we have an interactive shell
// so run /etc/shrc and ~/.shrc
}else{
var fd = stdio.open('/etc/shrc',stdio.flags.O_RDONLY|stdio.flags.O_SYNC);
if (fd) {
parseScript(fd);
stdio.close(fd);
}
fd = stdio.open(env.HOME+'/.shrc',stdio.flags.O_RDONLY|stdio.flags.O_SYNC);
if (fd) {
function main(args) {
var f = null;
var s = false;
for (var i=1; i<args.length; i++) {
if (args[i][0] == '-') {
for (var j=1; j<args[i].length; j++) {
switch (args[i][j]) {
case '?':
help();
return 0;
break;
case 's':
s = true;
break;
default:
stdio.fprintf(io.stderr,'unknown argument -%c\n',args[i][j]);
}
}
}else if (f == null) {
f = clite.resolvePath(args[i]);
}else{
stdio.fprintf(io.stderr,'unknown argument - %s\n',args[i]);
}
}
// run a shell script if one is specified
if (f != null) {
var fd = stdio.open(f,stdio.flags.O_RDONLY);
if (!fd)
return 1;
parseScript(fd);
stdio.close(fd);
stdlib.waitall(function() {
io.exit(0);
});
// run a shell script from stdin
}else if (!stdio.isatty(io.stdin)) {
if (!s)
return 0;
parseScript(io.stdin);
stdlib.waitall(function() {
io.exit(0);
});
// otherwise we have an interactive shell
// so run /etc/shrc and ~/.shrc
}else{
var fd = stdio.open('/etc/shrc',stdio.flags.O_RDONLY|stdio.flags.O_SYNC);
if (fd) {
parseScript(fd);
stdio.close(fd);
}
fd = stdio.open(env.HOME+'/.shrc',stdio.flags.O_RDONLY|stdio.flags.O_SYNC);
if (fd) {
parseScript(fd);
stdio.close(fd);
}
stdlib.waitall(function() {
history.clear();
inputRead();
});
}
return null;
}
//alert('shell');
stdlib.waitall(function() {
history.clear();
inputRead();
});
return null;
return main(args);
});
}