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 active_pid = 0; 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]; } }; var history = { data:[], current:'', index:0, 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 == 0) return; history.index--; term.ttyctrl('iset',history.getCurrent()); }, down:function() { if (history.index == history.data.length) return; history.index++; term.ttyctrl('iset',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; } } function active_exit(pid,v) { if (pid != active_pid) return; inputRead(); } 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 tabfill() { var c = term.ttyctrl('iget'); var parts = stdlib.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(macro).forEach(function(key) { if (add == '' && key.substring(0,a.length) == a) { add = key.substring(a.length); } }); if (add == '') { // test for commands var pparts = env.PATH.split(':'); pparts.forEach(function(pp) { if (add != '') return; var fd = stdio.open('/bin',false); var f; while (add == '' && (f = stdio.read(fd)) != null) { if (f.substring(0,a.length) == a) { add = f.substring(a.length)+' '; } } stdio.close(fd); }); } }else{ // TODO: path fill } if (add == '') return; term.ttyctrl('iset',c+add); } function parseScript(fd) { var l; while ((l = stdio.readLine(fd)) != null) { if (l[0] == '#') continue; run(l,false); } } function run(txt,interactive) { if (txt.length < 1) { if (interactive) { stdio.write(io.stdout,prompt); inputRead(); } return; } history.add(txt); history.resetCurrent(); if (interactive) 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 = { pid:io.pid, stdin:io.stdin, stdout:io.stdout, stderr:io.stderr, exit:active_exit, // 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); if (interactive) inputRead(); return; } var path = resolvePATH(args[0]); if (!path) { stdio.write(io.stderr,'Shell: unknown command: '+args[0]); if (interactive) 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]); io.exit(-1); } if (r == -2) { stdio.write(io.stderr,'Shell: error in command:'+args[0]); io.exit(-1); } } active_pid = stdlib.fork(env,fio,execFunc); if (active_pid == 0) { stdio.write(io.stderr,'Shell: internal error'); if (interactive) inputRead(); } } 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; } active_pid = 0; run(str,true); } 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); } // 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',false); if (fd) { parseScript(fd); stdio.close(fd); } fd = stdio.open(env.HOME+'/.shrc',false); if (fd) { parseScript(fd); stdio.close(fd); } } stdlib.waitall(inputRead); return null; }); }