From b6d539ec7f38da65b4b86a33809214bba746bad0 Mon Sep 17 00:00:00 2001 From: Lisa Milne Date: Wed, 13 Dec 2023 13:42:57 +1000 Subject: [PATCH] shell tabfill and history back in place, ability to set tty into raw mode added --- clite/bios.js | 28 ++++--- clite/commands.js | 8 +- clite/core.js | 196 ++++++++++++++++--------------------------- clite/libterm.js | 6 +- clite/libtime.js | 1 - clite/shell.js | 210 +++++++++++++++++++++++++++++++++------------- 6 files changed, 249 insertions(+), 200 deletions(-) diff --git a/clite/bios.js b/clite/bios.js index 62f2832..de45c1a 100644 --- a/clite/bios.js +++ b/clite/bios.js @@ -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; } diff --git a/clite/commands.js b/clite/commands.js index d194fa5..d2b96e4 100644 --- a/clite/commands.js +++ b/clite/commands.js @@ -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; } diff --git a/clite/core.js b/clite/core.js index 0ba98af..cec9d60 100644 --- a/clite/core.js +++ b/clite/core.js @@ -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) { diff --git a/clite/libterm.js b/clite/libterm.js index cbac6be..23d8cee 100644 --- a/clite/libterm.js +++ b/clite/libterm.js @@ -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); } }); diff --git a/clite/libtime.js b/clite/libtime.js index acb5b97..7b5d624 100644 --- a/clite/libtime.js +++ b/clite/libtime.js @@ -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) { diff --git a/clite/shell.js b/clite/shell.js index f60c4c6..3cf5f24 100644 --- a/clite/shell.js +++ b/clite/shell.js @@ -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