diff --git a/clite/shell.js b/clite/shell.js index 5903087..4c576fe 100644 --- a/clite/shell.js +++ b/clite/shell.js @@ -8,824 +8,811 @@ clite.commands.load('sh',function(args,env,io) { var clite = io.include('clite'); var has_exited = false; + let builtins = ['clear','cd','pwd','echo','which','type','whoami','alias','export','true','false','exit']; + let builtins_func = [builtin_clear,builtin_cd,builtin_pwd,builtin_echo,builtin_which,builtin_type,builtin_whoami,builtin_alias,builtin_export,builtin_true,builtin_false,builtin_exit]; + let history_data = []; + let history_current = ''; + let history_index = 0; + let parser_files = []; + let parser_callback = null; + let parser_lines = []; + let parser_script = null; + let exec_callback = null; + let exec_cmds = []; + let exec_active = null; + let exec_bufferout = null; + let exec_bufferin = null; + let exec_skipto = null; + let exec_cfg = {closout:false,closin:false,closerr:false}; + let exec_last = 0; + let exec_io = {}; + let exec_conditionals = []; + let exec_loops = []; - var macro = { - clear:function(args,io) { - term.clear(); - return 0; - }, - cd:function(args,io) { - var dir = env.HOME; - if (args.length > 1) - dir = clite.resolvePath(args[1]); - var fd = stdio.open(dir,stdio.flags.O_SEARCH|stdio.flags.O_DIRECTORY); - if (!fd) { - stdio.fprintf(io.stderr,'invalid directory: %s\n',dir); - return 1; - } - stdio.close(fd); - env.PWD = dir; - return 0; - }, - pwd:function(args,io) { - stdio.fprintf(io.stdout,'%s\n',env.PWD); - return 0; - }, - echo:function(args,io) { - args.shift(); - var txt = args.join(' ')+'\n'; - stdio.write(io.stdout,txt); - return 0; - }, - which:function(args,io) { - if (args.length <2) - return 1; - if (typeof macro[args[1]] != 'undefined') { - return 1; - }else{ - var path = resolvePATH(args[1]); - if (!path) - return 1; - stdio.fprintf(io.stdout,'%s is %s\n',args[1],path); - } - return 0; - }, - type:function(args,io) { - if (args.length <2) - return 1; - if (typeof macro[args[1]] != 'undefined') { - stdio.fprintf(io.stdout,'%s is a shell builtin\n',args[1]); - }else{ - var path = resolvePATH(args[1]); - if (!path) - return 1; - stdio.fprintf(io.stdout,'%s is %s\n',args[1],path); - } - return 0; - }, - whoami:function(args,io) { - stdio.fprintf(io.stdout,'%s\n',env.USER); - return 0; - }, - alias:function(args,io) { - stdio.fprintf(io.stdout,'unimplemented\n'); - return 0; - }, - export:function(args,io) { - if (args.length == 1) { - Object.keys(env).forEach(function(key) { - stdio.fprintf(io.stdout,'%s=%s\n',key,env[key]); - }); - return 0; - } - var parts = args[1].split('='); - if (parts.length != 2) - return 0; - env[parts[0]] = parts[1]; - return 0; - }, - true:function(args,io) { - return 0; - }, - false:function(args,io) { + function builtin_clear(args,io) { + term.clear(); + return 0; + } + function builtin_cd(args,io) { + var dir = env.HOME; + if (args.length > 1) + dir = clite.resolvePath(args[1]); + var fd = stdio.open(dir,stdio.flags.O_SEARCH|stdio.flags.O_DIRECTORY); + if (!fd) { + stdio.fprintf(io.stderr,'invalid directory: %s\n',dir); return 1; - }, - exit:function(args,io) { - var ev = 0; - if (args.length > 1) - ev = parseInt(args[1]); - doShellExit(ev); + } + stdio.close(fd); + env.PWD = dir; + return 0; + } + function builtin_pwd(args,io) { + stdio.fprintf(io.stdout,'%s\n',env.PWD); + return 0; + } + function builtin_echo(args,io) { + args.shift(); + var txt = args.join(' ')+'\n'; + stdio.write(io.stdout,txt); + return 0; + } + function builtin_which(args,io) { + if (args.length <2) + return 1; + if (builtins.indexOf(args[1]) > -1) { + return 1; + }else{ + var path = resolvePATH(args[1]); + if (!path) + return 1; + stdio.fprintf(io.stdout,'%s is %s\n',args[1],path); + } + return 0; + } + function builtin_type(args,io) { + if (args.length <2) + return 1; + if (builtins.indexOf(args[1]) > -1) { + stdio.fprintf(io.stdout,'%s is a shell builtin\n',args[1]); + }else{ + var path = resolvePATH(args[1]); + if (!path) + return 1; + stdio.fprintf(io.stdout,'%s is %s\n',args[1],path); + } + return 0; + } + function builtin_whoami(args,io) { + stdio.fprintf(io.stdout,'%s\n',env.USER); + return 0; + } + function builtin_alias(args,io) { + stdio.fprintf(io.stdout,'unimplemented\n'); + return 0; + } + function builtin_export(args,io) { + if (args.length == 1) { + Object.keys(env).forEach(function(key) { + stdio.fprintf(io.stdout,'%s=%s\n',key,env[key]); + }); return 0; } - }; + var parts = args[1].split('='); + if (parts.length != 2) + return 0; + env[parts[0]] = parts[1]; + return 0; + } + function builtin_true(args,io) { + return 0; + } + function builtin_false(args,io) { + return 1; + } + function builtin_exit(args,io) { + var ev = 0; + if (args.length > 1) + ev = parseInt(args[1]); + doShellExit(ev); + return 0; + } - var history = { - data:[], - current:'', - index:0, - load:function() { - let path = clite.resolvePath('~/.shhistory'); - let fd = stdio.open(path,stdio.flags.O_RDONLY|stdio.flags.O_SYNC); - if (!fd) - return; - var e; - while ((e = stdio.readLine(fd)) != null) { - history.add(e); - } - stdio.close(fd); - }, - save:function() { - let path = clite.resolvePath('~/.shhistory'); - let fd = stdio.open(path,stdio.flags.O_WRONLY|stdio.flags.O_TRUNC|stdio.flags.O_CREAT|stdio.flags.O_SYNC); - if (!fd) - return; - history.data.forEach(function(l) { - stdio.write(fd,l+'\n'); - }); - stdio.close(fd); - }, - add:function(txt) { - if (txt.length < 1) - return; - if (history.data.length > 0 && history.data[history.data.length-1] == txt) - return; - history.data.push(txt); - history.index = history.data.length; - }, - up:function() { - if (history.index < 1) - return null; - history.index--; - return history.getCurrent(); - }, - down:function() { - if (history.index >= history.data.length) - return null; - history.index++; - return history.getCurrent(); - }, - setCurrent:function(txt) { - history.current = txt; - }, - getCurrent:function() { - if (history.index >= history.data.length) - return history.current; - return history.data[history.index]; - }, - resetCurrent:function() { - history.current = ''; - history.index = history.data.length; - }, - clear:function() { - history.data = []; - history.current = ''; - history.index = 0; + function history_load() { + let path = clite.resolvePath('~/.shhistory'); + let fd = stdio.open(path,stdio.flags.O_RDONLY|stdio.flags.O_SYNC); + if (!fd) + return; + var e; + while ((e = stdio.readLine(fd)) != null) { + history_add(e); } - }; + stdio.close(fd); + } + function history_save() { + let path = clite.resolvePath('~/.shhistory'); + let fd = stdio.open(path,stdio.flags.O_WRONLY|stdio.flags.O_TRUNC|stdio.flags.O_CREAT|stdio.flags.O_SYNC); + if (!fd) + return; + history_data.forEach(function(l) { + stdio.write(fd,l+'\n'); + }); + stdio.close(fd); + } + function history_add(txt) { + if (txt.length < 1) + return; + if (history_data.length > 0 && history_data[history_data.length-1] == txt) + return; + history_data.push(txt); + history_index = history_data.length; + } + function history_up() { + if (history_index < 1) + return null; + history_index--; + return history_getCurrent(); + } + function history_down() { + if (history_index >= history_data.length) + return null; + history_index++; + return history_getCurrent(); + } + function history_setCurrent(txt) { + history_current = txt; + } + function history_getCurrent() { + if (history_index >= history_data.length) + return history_current; + return history_data[history_index]; + } + function history_resetCurrent() { + history_current = ''; + history_index = history_data.length; + } + function history_clear() { + history_data = []; + history_current = ''; + history_index = 0; + } - var parser = { - files:[], - callback:null, - queue:function(file) { - parser.files.push(file); - }, - internal:{ - lines:[], - scrpt:null, - parseFile:function(f) { - var fn = clite.resolvePath(f); - if (fn == null) { - parser.run(); - return; - } - var st = stdio.stat(fn); - if (st == null || st.type != stdio.types.FT_SCRIPT) { - parser.run(); - return; - } - var fd = stdio.open(fn,stdio.flags.O_RDONLY,parser.internal.prepFile); - if (!fd) - parser.run(); - }, - prepFile:function(fd) { - var data = stdio.readAll(fd); - if (fd != io.stdin) - stdio.close(fd); - if (!data) { - parser.run(); - return; - } - parser.internal.script = ''; - parser.internal.lines = data.split('\n'); - if (parser.internal.lines.length < 1) { - parser.run(); - return; - } - parser.internal.parseLine(); - }, - parseLine:function() { - var l = parser.internal.lines.shift(); - if (l == null) { - parser.internal.exec(); - return; - } - if (l[0] != '#') - parser.internal.script += l+'; '; - parser.internal.parseLine(); - }, - exec:function() { - exec.setCommand(parser.internal.script); - exec.run(parser.run); - } - }, - exit:function() { - if (typeof parser.callback !== 'function') - return; - try{ - parser.callback(); - } catch(err) { - stdio.write(io.stderr,'Shell: internal error in parser\n'); - io.exit(1); - return; - } - }, - run:function(cb) { - if (typeof cb === 'function') - parser.callback = cb; - var f = parser.files.shift(); - if (!f) { - parser.exit(); - return; - } - parser.internal.parseFile(f); - }, - stdin:function() { - parser.callback = function() { - io.exit(0); - } - parser.internal.prepFile(io.stdin); + function parser_queue(file) { + parser_files.push(file); + } + function parser_parseFile(f) { + var fn = clite.resolvePath(f); + if (fn == null) { + parser_run(); + return; } - }; + var st = stdio.stat(fn); + if (st == null || st.type != stdio.types.FT_SCRIPT) { + parser_run(); + return; + } + var fd = stdio.open(fn,stdio.flags.O_RDONLY,parser_prepFile); + if (!fd) + parser_run(); + } + function parser_prepFile(fd) { + var data = stdio.readAll(fd); + if (fd != io.stdin) + stdio.close(fd); + if (!data) { + parser_run(); + return; + } + parser_script = ''; + parser_lines = data.split('\n'); + if (parser_lines.length < 1) { + parser_run(); + return; + } + parser_parseLine(); + } + function parser_parseLine() { + var l = parser_lines.shift(); + if (l == null) { + parser_exec(); + return; + } + if (l[0] != '#') + parser_script += l+'; '; + parser_parseLine(); + } + function parser_exec() { + exec_setCommand(parser_script); + exec_run(parser_run); + } + function parser_exit() { + if (typeof parser_callback !== 'function') + return; + try{ + parser_callback(); + } catch(err) { + stdio.write(io.stderr,'Shell: internal error in parser\n'); + io.exit(1); + return; + } + } + function parser_run(cb) { + if (typeof cb === 'function') + parser_callback = cb; + var f = parser_files.shift(); + if (!f) { + parser_exit(); + return; + } + parser_parseFile(f); + } + function parser_stdin() { + parser_callback = function() { + io.exit(0); + } + parser_prepFile(io.stdin); + } - var exec = { - callback:null, - cmds:[], - active:null, - bufferout:null, - bufferin:null, - skipto:null, - cfg:{closout:false,closin:false,closerr:false}, - last:0, - io:{}, - internal:{ - conditionals:[], - loops:[], - doCommand:function(cmd) { - exec.active = cmd; - if ((cmd.onlastsuccess && exec.last != 0) || (cmd.onlastfail && exec.last == 0)) { - exec.run(); - return; - } - switch (cmd.args[0]) { - case 'if': - exec.internal.conditionals.push(cmd); - exec.run(); - return; - break; - case 'then': - if (exec.last == 0) { - exec.internal.conditionals[exec.internal.conditionals.length-1].conditional = true; - }else{ - exec.internal.conditionals[exec.internal.conditionals.length-1].conditional = false; - exec.skipto = 'else'; - } - exec.run(); - return; - break; - case 'else': - if (exec.internal.conditionals[exec.internal.conditionals.length-1].conditional != false) { - exec.skipto = 'fi'; - } - exec.run(); - return; - break; - case 'fi': - exec.internal.conditionals.pop(); - exec.run(); - return; - break; - case 'while': - case 'do': - case 'done': - exec.run(); - return; - break; - default:; - } - var usebi = false; - var usebo = false; - if (cmd.stdin != '&0') { - switch (cmd.stdin) { - case '|': - exec.stdin.setOut(); - usebi = true; - break; - case null: - exec.stdin.setNull(); - break; - default: - exec.stdin.setFile(cmd.stdin); - break; - } - }else{ - if (exec.cfg.closin) { - stdio.close(exec.io.stdin); - exec.cfg.closin = false; - } - exec.io.stdin = io.stdin; - } - if (cmd.stdout != '&1') { - switch (cmd.stdout) { - case '|': - exec.stdout.setBuffered(); - usebo = true; - break; - case null: - exec.stdout.setNull(); - break; - default: - exec.stdout.setFile(cmd.stdout); - break; - } - }else{ - if (exec.cfg.closout) { - stdio.close(exec.io.stdout); - exec.cfg.closout = false; - } - exec.io.stdout = io.stdout; - } - if (cmd.stderr != '&2') { - switch (cmd.stderr) { - case '&1': - exec.stderr.setOut(); - break; - case null: - exec.stderr.setNull(); - break; - default: - exec.stderr.setFile(cmd.stderr); - break; - } - }else{ - if (exec.cfg.closerr) { - stdio.close(exec.io.stderr); - exec.cfg.closerr = false; - } - exec.io.stderr = io.stderr; - } - if (exec.io.stdin == null) { - stdio.write(io.stderr,'could not open standard input for reading\n'); - exec.setLast(1); - exec.run(); - return; - } - if (exec.io.stdout == null) { - stdio.write(io.stderr,'could not open standard output for writing\n'); - exec.setLast(1); - exec.run(); - return; - } - if (exec.io.stderr == null) { - stdio.write(io.stderr,'could not open standard error for writing\n'); - exec.setLast(1); - exec.run(); - return; - } - if (typeof macro[cmd.args[0]] != 'undefined') { - exec.io.exit = io.exit; - var r = macro[cmd.args[0]](cmd.args,exec.io); - exec.setLast(r); - exec.run(); - return; - } - - if (!cmd.path) { - stdio.fprintf(io.stderr,'Shell: unknown command: %s\n',cmd.args[0]); - exec.setLast(1); - exec.run(); - return; - } - var pid = stdlib.fork(env,exec.io,exec.internal.doExec); - if (pid == 0) { - stdio.write(io.stderr,'Shell: internal error\n'); - exec.setLast(1); - exec.run(); - return; - } - - stdlib.waitpid(pid,function(pid,ev) { - exec.setLast(ev); - exec.run(); - }); - }, - doExec:function(env,io) { - var r = stdlib.exec(exec.active.path,exec.active.args,env,io); - if (r > 0) - return; - - if (r == -1) { - stdlib.fprintf(io.stderr,'Shell: unknown command: %s\n',exec.active.args[0]); - io.exit(-1); - return; - } - - if (r == -2) { - stdio.fprintf(io.stderr,'Shell: error in command: %s\n',exec.active.args[0]); - io.exit(-1); - return; - } - - if (r == -3 || r == -4) { - stdio.fprintf(io.stderr,'Shell: not an executable file: %s\n',exec.active.args[0]); - io.exit(-3); - return; - } - - if (r == -5) { - stdio.fprintf(io.stderr,'Shell: invalid parser for file: %s\n',exec.active.args[0]); - io.exit(-3); - return; - } - - if (r == -6) { - stdio.fprintf(io.stderr,'Shell: no valid parser for file: %s\n',exec.active.args[0]); - io.exit(-3); - return; - } - - if (r == -7 || r == -8) { - stdio.fprintf(io.stderr,'Shell: failed to set uid/gid for: %s\n',exec.active.args[0]); - io.exit(-3); - return; - } - - if (r<0) { - stdio.fprintf(io.stderr,'Shell: could not exec file: %s\n',exec.active.args[0]); - io.exit(-3); - } + function exec_doCommand(cmd) { + exec_active = cmd; + if ((cmd.onlastsuccess && exec_last != 0) || (cmd.onlastfail && exec_last == 0)) { + exec_run(); + return; + } + switch (cmd.args[0]) { + case 'if': + exec_conditionals.push(cmd); + exec_run(); + return; + break; + case 'then': + if (exec_last == 0) { + exec_conditionals[exec_conditionals.length-1].conditional = true; + }else{ + exec_conditionals[exec_conditionals.length-1].conditional = false; + exec_skipto = 'else'; } - }, - setLast:function(v) { - exec.last = parseInt(v); - env['?'] = exec.last.toString(); - }, - setCommand:function(txt) { - var args = clite.strToArgs(txt); - if (args.length < 1) - return; - args.forEach(function(val,i) { - args[i] = preprocess(val); - }); - var cmd = { - args:[], - path:null, - stdin:'&0', - stdout:'&1', - stderr:'&2', - onlastsuccess:false, - onlastfail:false, - conditional:false - }; - var c = structuredClone(cmd); - exec.cmds = []; - var fault = false; - var conds = []; - var loops = []; - for (var i=0; i 0) { - exec.cmds.push(c); - c = structuredClone(cmd); - } - continue; - } - if (args[i] == 'if') { - if (c.args.length > 0) { - exec.cmds.push(c); - c = structuredClone(cmd); - } - c.args.push(args[i]); - exec.cmds.push(c); - conds.push(c); + exec_run(); + return; + break; + case 'else': + if (exec_conditionals[exec_conditionals.length-1].conditional != false) { + exec_skipto = 'fi'; + } + exec_run(); + return; + break; + case 'fi': + exec_conditionals.pop(); + exec_run(); + return; + break; + case 'while': + case 'do': + case 'done': + exec_run(); + return; + break; + default:; + } + var usebi = false; + var usebo = false; + if (cmd.stdin != '&0') { + switch (cmd.stdin) { + case '|': + exec_stdin_setOut(); + usebi = true; + break; + case null: + exec_stdin_setNull(); + break; + default: + exec_stdin_setFile(cmd.stdin); + break; + } + }else{ + if (exec_cfg.closin) { + stdio.close(exec_io.stdin); + exec_cfg.closin = false; + } + exec_io.stdin = io.stdin; + } + if (cmd.stdout != '&1') { + switch (cmd.stdout) { + case '|': + exec_stdout_setBuffered(); + usebo = true; + break; + case null: + exec_stdout_setNull(); + break; + default: + exec_stdout_setFile(cmd.stdout); + break; + } + }else{ + if (exec_cfg.closout) { + stdio.close(exec_io.stdout); + exec_cfg.closout = false; + } + exec_io.stdout = io.stdout; + } + if (cmd.stderr != '&2') { + switch (cmd.stderr) { + case '&1': + exec_stderr_setOut(); + break; + case null: + exec_stderr_setNull(); + break; + default: + exec_stderr_setFile(cmd.stderr); + break; + } + }else{ + if (exec_cfg.closerr) { + stdio.close(exec_io.stderr); + exec_cfg.closerr = false; + } + exec_io.stderr = io.stderr; + } + if (exec_io.stdin == null) { + stdio.write(io.stderr,'could not open standard input for reading\n'); + exec_setLast(1); + exec_run(); + return; + } + if (exec_io.stdout == null) { + stdio.write(io.stderr,'could not open standard output for writing\n'); + exec_setLast(1); + exec_run(); + return; + } + if (exec_io.stderr == null) { + stdio.write(io.stderr,'could not open standard error for writing\n'); + exec_setLast(1); + exec_run(); + return; + } + if (builtins.indexOf(cmd.args[0]) > -1) { + exec_io.exit = io.exit; + let fn = builtins.indexOf(cmd.args[0]); + + let r = builtins_func[fn](cmd.args,exec_io); + + exec_setLast(r); + exec_run(); + return; + } + + if (!cmd.path) { + stdio.fprintf(io.stderr,'Shell: unknown command: %s\n',cmd.args[0]); + exec_setLast(1); + exec_run(); + return; + } + var pid = stdlib.fork(env,exec_io,exec_doExec); + if (pid == 0) { + stdio.write(io.stderr,'Shell: internal error\n'); + exec_setLast(1); + exec_run(); + return; + } + + stdlib.waitpid(pid,function(pid,ev) { + exec_setLast(ev); + exec_run(); + }); + } + function exec_doExec(env,io) { + var r = stdlib.exec(exec_active.path,exec_active.args,env,io); + if (r > 0) + return; + + if (r == -1) { + stdlib.fprintf(io.stderr,'Shell: unknown command: %s\n',exec_active.args[0]); + io.exit(-1); + return; + } + + if (r == -2) { + stdio.fprintf(io.stderr,'Shell: error in command: %s\n',exec_active.args[0]); + io.exit(-1); + return; + } + + if (r == -3 || r == -4) { + stdio.fprintf(io.stderr,'Shell: not an executable file: %s\n',exec_active.args[0]); + io.exit(-3); + return; + } + + if (r == -5) { + stdio.fprintf(io.stderr,'Shell: invalid parser for file: %s\n',exec_active.args[0]); + io.exit(-3); + return; + } + + if (r == -6) { + stdio.fprintf(io.stderr,'Shell: no valid parser for file: %s\n',exec_active.args[0]); + io.exit(-3); + return; + } + + if (r == -7 || r == -8) { + stdio.fprintf(io.stderr,'Shell: failed to set uid/gid for: %s\n',exec_active.args[0]); + io.exit(-3); + return; + } + + if (r<0) { + stdio.fprintf(io.stderr,'Shell: could not exec file: %s\n',exec_active.args[0]); + io.exit(-3); + } + } + function exec_setLast(v) { + exec_last = parseInt(v); + env['?'] = exec_last.toString(); + } + function exec_setCommand(txt) { + var args = clite.strToArgs(txt); + if (args.length < 1) + return; + args.forEach(function(val,i) { + args[i] = preprocess(val); + }); + var cmd = { + args:[], + path:null, + stdin:'&0', + stdout:'&1', + stderr:'&2', + onlastsuccess:false, + onlastfail:false, + conditional:false + }; + var c = structuredClone(cmd); + exec_cmds = []; + var fault = false; + var conds = []; + var loops = []; + for (var i=0; i 0) { + exec_cmds.push(c); c = structuredClone(cmd); - continue; } - if (args[i] == 'then') { - if (c.args.length > 0) { - exec.cmds.push(c); - c = structuredClone(cmd); - } - c.args.push(args[i]); - exec.cmds.push(c); - if (conds.length < 1) { - fault = true; - }else{ - conds[conds.length-1].args.push(c); - } + continue; + } + if (args[i] == 'if') { + if (c.args.length > 0) { + exec_cmds.push(c); c = structuredClone(cmd); - continue; - } - if (args[i] == 'else') { - if (c.args.length > 0) { - exec.cmds.push(c); - c = structuredClone(cmd); - } - c.args.push(args[i]); - exec.cmds.push(c); - if (conds.length < 1) { - fault = true; - }else{ - conds[conds.length-1].args.push(c); - } - c = structuredClone(cmd); - continue; - } - if (args[i] == 'fi') { - if (c.args.length > 0) { - exec.cmds.push(c); - c = structuredClone(cmd); - } - c.args.push(args[i]); - exec.cmds.push(c); - if (conds.length < 1) { - fault = true; - }else{ - conds[conds.length-1].args.push(c); - conds.pop(); - } - c = structuredClone(cmd); - continue; - } - if (args[i] == 'while') { - if (c.args.length > 0) { - exec.cmds.push(c); - c = structuredClone(cmd); - } - c.args.push(args[i]); - exec.cmds.push(c); - loops.push(c); - c = structuredClone(cmd); - continue; - } - if (args[i] == 'do') { - if (c.args.length > 0) { - exec.cmds.push(c); - c = structuredClone(cmd); - } - c.args.push(args[i]); - exec.cmds.push(c); - if (loops.length < 1) { - fault = true; - }else{ - loops[loops.length-1].args.push(c); - } - c = structuredClone(cmd); - continue; - } - if (args[i] == 'done') { - if (c.args.length > 0) { - exec.cmds.push(c); - c = structuredClone(cmd); - } - c.args.push(args[i]); - exec.cmds.push(c); - if (loops.length < 1) { - fault = true; - }else{ - loops[loops.length-1].args.push(c); - loops.pop(); - } - c = structuredClone(cmd); - continue; - } - if (args[i] == '|') { - c.stdout = '|'; - exec.cmds.push(c); - c = structuredClone(cmd); - c.stdin = '|'; - continue; - } - if (args[i] == '||') { - exec.cmds.push(c); - c = structuredClone(cmd); - c.onlastfail = true; - continue; - } - if (args[i] == '&&') { - exec.cmds.push(c); - c = structuredClone(cmd); - c.onlastsuccess = true; - continue; - } - if (args[i] == '>') { - i++; - c.stdout = clite.resolvePath(args[i]); - if (c.stdout == '/dev/null') - c.stdout = null; - continue; - } - if (args[i] == '2>&1') { - c.stderr = '&1'; - continue; - } - if (args[i] == '>>') { - i++; - c.stdout = clite.resolvePath(args[i]); - if (c.stdout == '/dev/null') { - c.stdout = null; - }else{ - c.stdout = '+'+c.stdout; - } - continue; - } - if (args[i] == '<') { - i++; - c.stdin = clite.resolvePath(args[i]); - if (c.stdin == '/dev/null') - c.stdin = null; - continue; } c.args.push(args[i]); + exec_cmds.push(c); + conds.push(c); + c = structuredClone(cmd); + continue; } - if (conds.length > 0) - fault = true; - if (loops.length > 0) - fault = true; - if (c.args.length > 0) - exec.cmds.push(c); - exec.cmds.forEach(function(c,i) { - if (c.args.length < 1) { + if (args[i] == 'then') { + if (c.args.length > 0) { + exec_cmds.push(c); + c = structuredClone(cmd); + } + c.args.push(args[i]); + exec_cmds.push(c); + if (conds.length < 1) { fault = true; - return; - } - c.args[0] = c.args[0].trim(); - c.path = resolvePATH(c.args[0]); - }); - if (fault) { - stdio.write(io.stderr,'Shell: unable to parse command\n'); - exec.exit(); - return; - } - exec.io.pid = io.pid; - exec.io.stdin = io.stdin; - exec.io.stdout = io.stdout; - exec.io.stderr = io.stderr; - exec.io.exit = null; // filled in by fork() - exec.io.include = null; // filled in by fork() - exec.last = 0; - }, - stdin:{ - setFile:function(path) { - if (exec.cfg.closin) { - stdio.close(exec.io.stdin); - exec.cfg.closin = false; - } - exec.io.stdin = stdio.open(path,stdio.flags.O_RDONLY); - exec.cfg.closin = true; - }, - setNull:function() { - if (exec.cfg.closin) { - stdio.close(exec.io.stdin); - exec.cfg.closin = false; - } - exec.io.stdin = stdio.open('/dev/null',stdio.flags.O_RDONLY); - exec.cfg.closin = true; - }, - setOut:function() { - if (exec.cfg.closin) { - stdio.close(exec.io.stdin); - exec.cfg.closin = false; - } - if (exec.bufferout) { - exec.io.stdout = null; - exec.cfg.closout = false; - exec.bufferin = exec.bufferout; - exec.bufferout = null; - exec.io.stdin = exec.bufferin; - stdio.seek(exec.io.stdin,0); - exec.cfg.closin = true; }else{ - exec.stdin.setNull(); + conds[conds.length-1].args.push(c); } + c = structuredClone(cmd); + continue; } - }, - stdout:{ - setBuffered:function() { - if (exec.cfg.closout) { - stdio.close(exec.io.stdout); - exec.cfg.closout = false; - if (exec.bufferout) - exec.bufferout = null; + if (args[i] == 'else') { + if (c.args.length > 0) { + exec_cmds.push(c); + c = structuredClone(cmd); } - exec.bufferout = stdio.mkstemp('/tmp/shell-XXXXXX'); - exec.io.stdout = exec.bufferout; - exec.cfg.closout = false; - }, - setFile:function(path) { - if (exec.cfg.closout) { - stdio.close(exec.io.stdout); - exec.cfg.closout = false; - } - var flags = stdio.flags.O_WRONLY|stdio.flags.O_CREAT|stdio.flags.O_SYNC; - var p = path; - if (path[0] == '+') { - flags |= stdio.flags.O_APPEND; - p = path.substring(1); + c.args.push(args[i]); + exec_cmds.push(c); + if (conds.length < 1) { + fault = true; }else{ - flags |= stdio.flags.O_TRUNC; + conds[conds.length-1].args.push(c); } - exec.io.stdout = stdio.open(p,flags); - exec.cfg.closout = true; - }, - setNull:function() { - if (exec.cfg.closout) { - stdio.close(exec.io.stdout); - exec.cfg.closout = false; - } - exec.io.stdout = stdio.open('/dev/null',stdio.flags.O_WRONLY); - exec.cfg.closout = true; + c = structuredClone(cmd); + continue; } - }, - stderr:{ - setFile:function(path) { - if (exec.cfg.closerr) { - stdio.close(exec.io.stderr); - exec.cfg.closerr = false; + if (args[i] == 'fi') { + if (c.args.length > 0) { + exec_cmds.push(c); + c = structuredClone(cmd); } - var flags = stdio.flags.O_WRONLY|stdio.flags.O_CREAT|stdio.flags.O_SYNC; - var p = path; - if (path[0] == '+') { - flags |= stdio.flags.O_APPEND; - p = path.substring(1); + c.args.push(args[i]); + exec_cmds.push(c); + if (conds.length < 1) { + fault = true; }else{ - flags |= stdio.flags.O_TRUNC; + conds[conds.length-1].args.push(c); + conds.pop(); } - exec.io.stderr = stdio.open(p,flags); - exec.cfg.closerr = true; - }, - setNull:function() { - if (exec.cfg.closerr) { - stdio.close(exec.io.stderr); - exec.cfg.closerr = false; + c = structuredClone(cmd); + continue; + } + if (args[i] == 'while') { + if (c.args.length > 0) { + exec_cmds.push(c); + c = structuredClone(cmd); } - exec.io.stderr = stdio.open('/dev/null',stdio.flags.O_WRONLY); - exec.cfg.closerr = true; - }, - setOut:function() { - if (exec.cfg.closerr) { - stdio.close(exec.io.stderr); - exec.cfg.closerr = false; + c.args.push(args[i]); + exec_cmds.push(c); + loops.push(c); + c = structuredClone(cmd); + continue; + } + if (args[i] == 'do') { + if (c.args.length > 0) { + exec_cmds.push(c); + c = structuredClone(cmd); } - exec.io.stderr = exec.io.stdout; - exec.cfg.closerr = exec.cfg.closout; - } - }, - exit:function() { - if (exec.cfg.closin) { - stdio.close(exec.io.stdin); - exec.cfg.closin = false; - } - var out = ''; - if (exec.bufferout) - out = stdio.readAll(exec.bufferout); - if (exec.cfg.closout) { - stdio.close(exec.io.stdout); - exec.cfg.closout = false; - } - if (exec.cfg.closerr) { - stdio.close(exec.io.stderr); - exec.cfg.closerr = false; - } - exec.active = null; - exec.cmds = []; - if (typeof exec.callback !== 'function') - return; - try{ - exec.callback(out); - } catch(err) { - console.log(err.fileName+':'+err.lineNumber+':'+err.columnNumber+':'+err.message); - stdio.write(io.stderr,'Shell: internal error in command execution\n'); - io.exit(1); - return; - } - }, - run:function(cb) { - if (typeof cb === 'function') - exec.callback = cb; - var c; - if (exec.skipto !== null) { - if (exec.skipto == 'fi') { - while ((c = exec.cmds.shift()) != null) { - if (c.args[0] == 'fi') - break; - } - }else if (exec.skipto == 'else') { - while ((c = exec.cmds.shift()) != null) { - if (c.args[0] == 'else' || c.args[0] == 'fi') - break; - } + c.args.push(args[i]); + exec_cmds.push(c); + if (loops.length < 1) { + fault = true; + }else{ + loops[loops.length-1].args.push(c); } - exec.skipto = null; - }else{ - c = exec.cmds.shift(); + c = structuredClone(cmd); + continue; } - if (!c) { - exec.exit(); - return; + if (args[i] == 'done') { + if (c.args.length > 0) { + exec_cmds.push(c); + c = structuredClone(cmd); + } + c.args.push(args[i]); + exec_cmds.push(c); + if (loops.length < 1) { + fault = true; + }else{ + loops[loops.length-1].args.push(c); + loops.pop(); + } + c = structuredClone(cmd); + continue; } - exec.internal.doCommand(c); + if (args[i] == '|') { + c.stdout = '|'; + exec_cmds.push(c); + c = structuredClone(cmd); + c.stdin = '|'; + continue; + } + if (args[i] == '||') { + exec_cmds.push(c); + c = structuredClone(cmd); + c.onlastfail = true; + continue; + } + if (args[i] == '&&') { + exec_cmds.push(c); + c = structuredClone(cmd); + c.onlastsuccess = true; + continue; + } + if (args[i] == '>') { + i++; + c.stdout = clite.resolvePath(args[i]); + if (c.stdout == '/dev/null') + c.stdout = null; + continue; + } + if (args[i] == '2>&1') { + c.stderr = '&1'; + continue; + } + if (args[i] == '>>') { + i++; + c.stdout = clite.resolvePath(args[i]); + if (c.stdout == '/dev/null') { + c.stdout = null; + }else{ + c.stdout = '+'+c.stdout; + } + continue; + } + if (args[i] == '<') { + i++; + c.stdin = clite.resolvePath(args[i]); + if (c.stdin == '/dev/null') + c.stdin = null; + continue; + } + c.args.push(args[i]); } - }; + if (conds.length > 0) + fault = true; + if (loops.length > 0) + fault = true; + if (c.args.length > 0) + exec_cmds.push(c); + exec_cmds.forEach(function(c,i) { + if (c.args.length < 1) { + fault = true; + return; + } + c.args[0] = c.args[0].trim(); + c.path = resolvePATH(c.args[0]); + }); + if (fault) { + stdio.write(io.stderr,'Shell: unable to parse command\n'); + exec_exit(); + return; + } + exec_io.pid = io.pid; + exec_io.stdin = io.stdin; + exec_io.stdout = io.stdout; + exec_io.stderr = io.stderr; + exec_io.exit = null; // filled in by fork() + exec_io.include = null; // filled in by fork() + exec_last = 0; + } + function exec_stdin_setFile(path) { + if (exec_cfg.closin) { + stdio.close(exec_io.stdin); + exec_cfg.closin = false; + } + exec_io.stdin = stdio.open(path,stdio.flags.O_RDONLY); + exec_cfg.closin = true; + } + function exec_stdin_setNull() { + if (exec_cfg.closin) { + stdio.close(exec_io.stdin); + exec_cfg.closin = false; + } + exec_io.stdin = stdio.open('/dev/null',stdio.flags.O_RDONLY); + exec_cfg.closin = true; + } + function exec_stdin_setOut() { + if (exec_cfg.closin) { + stdio.close(exec_io.stdin); + exec_cfg.closin = false; + } + if (exec_bufferout) { + exec_io.stdout = null; + exec_cfg.closout = false; + exec_bufferin = exec_bufferout; + exec_bufferout = null; + exec_io.stdin = exec_bufferin; + stdio.seek(exec_io.stdin,0); + exec_cfg.closin = true; + }else{ + exec_stdin_setNull(); + } + } + function exec_stdout_setBuffered() { + if (exec_cfg.closout) { + stdio.close(exec_io.stdout); + exec_cfg.closout = false; + if (exec_bufferout) + exec_bufferout = null; + } + exec_bufferout = stdio.mkstemp('/tmp/shell-XXXXXX'); + exec_io.stdout = exec_bufferout; + exec_cfg.closout = false; + } + function exec_stdout_setFile(path) { + if (exec_cfg.closout) { + stdio.close(exec_io.stdout); + exec_cfg.closout = false; + } + var flags = stdio.flags.O_WRONLY|stdio.flags.O_CREAT|stdio.flags.O_SYNC; + var p = path; + if (path[0] == '+') { + flags |= stdio.flags.O_APPEND; + p = path.substring(1); + }else{ + flags |= stdio.flags.O_TRUNC; + } + exec_io.stdout = stdio.open(p,flags); + exec_cfg.closout = true; + } + function exec_stdout_setNull() { + if (exec_cfg.closout) { + stdio.close(exec_io.stdout); + exec_cfg.closout = false; + } + exec_io.stdout = stdio.open('/dev/null',stdio.flags.O_WRONLY); + exec_cfg.closout = true; + } + function exec_stderr_setFile(path) { + if (exec_cfg.closerr) { + stdio.close(exec_io.stderr); + exec_cfg.closerr = false; + } + var flags = stdio.flags.O_WRONLY|stdio.flags.O_CREAT|stdio.flags.O_SYNC; + var p = path; + if (path[0] == '+') { + flags |= stdio.flags.O_APPEND; + p = path.substring(1); + }else{ + flags |= stdio.flags.O_TRUNC; + } + exec_io.stderr = stdio.open(p,flags); + exec_cfg.closerr = true; + } + function exec_stderr_setNull() { + if (exec_cfg.closerr) { + stdio.close(exec_io.stderr); + exec_cfg.closerr = false; + } + exec_io.stderr = stdio.open('/dev/null',stdio.flags.O_WRONLY); + exec_cfg.closerr = true; + } + function exec_stderr_setOut() { + if (exec_cfg.closerr) { + stdio.close(exec_io.stderr); + exec_cfg.closerr = false; + } + exec_io.stderr = exec_io.stdout; + exec_cfg.closerr = exec_cfg.closout; + } + function exec_exit() { + if (exec_cfg.closin) { + stdio.close(exec_io.stdin); + exec_cfg.closin = false; + } + var out = ''; + if (exec_bufferout) + out = stdio.readAll(exec_bufferout); + if (exec_cfg.closout) { + stdio.close(exec_io.stdout); + exec_cfg.closout = false; + } + if (exec_cfg.closerr) { + stdio.close(exec_io.stderr); + exec_cfg.closerr = false; + } + exec_active = null; + exec_cmds = []; + if (typeof exec_callback !== 'function') + return; + try{ + exec_callback(out); + } catch(err) { + console.log(err.fileName+':'+err.lineNumber+':'+err.columnNumber+':'+err.message); + stdio.write(io.stderr,'Shell: internal error in command execution\n'); + io.exit(1); + return; + } + } + function exec_run(cb) { + if (typeof cb === 'function') + exec_callback = cb; + var c; + if (exec_skipto !== null) { + if (exec_skipto == 'fi') { + while ((c = exec_cmds.shift()) != null) { + if (c.args[0] == 'fi') + break; + } + }else if (exec_skipto == 'else') { + while ((c = exec_cmds.shift()) != null) { + if (c.args[0] == 'else' || c.args[0] == 'fi') + break; + } + } + exec_skipto = null; + }else{ + c = exec_cmds.shift(); + } + if (!c) { + exec_exit(); + return; + } + exec_doCommand(c); + } function help() { stdio.write(io.stdout,` @@ -839,7 +826,7 @@ Options: } function doShellExit(v) { - history.save(); + history_save(); stdio.write(io.stdout,'\n'); has_exited = true; io.exit(v); @@ -927,7 +914,7 @@ Options: var add = ''; if (ai == 0) { // test for shell macros/builtins - Object.keys(macro).forEach(function(key) { + builtins.forEach(function(key) { if (add == '' && key.substring(0,a.length) == a) { add = key.substring(a.length); } @@ -993,80 +980,70 @@ Options: stdio.write(io.stdout,prompt); } + function inputProcess(str) { + let c = history_getCurrent(); + switch (str) { + case String.fromCharCode(4): + case 4: + term.ttyctrl('raw',false); + doShellExit(0); + break; + case '\t': // tab + case 9: + var ct = tabfill(c); + if (ct != null) { + c = ct; + }else{ + stdio.write(io.stdout,'\b \b'); + } + break; + case '\n': // enter + term.ttyctrl('raw',false); + history_add(c); + history_resetCurrent(); + exec_setCommand(c); + exec_run(inputRead); + return; + break; + case '\b': + case 8: + if (c.length > 0) { + c = c.substring(0,c.length-1); + stdio.write(io.stdout,'\b \b'); + } + break; + case -3: // down arrow + var ct = history_down(); + if (ct != null) { + for (var i=0; i 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