diff --git a/clite/core.css b/clite/core.css index bd38451..fdcf415 100644 --- a/clite/core.css +++ b/clite/core.css @@ -7,7 +7,7 @@ header h1 {line-height:40px; font-size:30px;} section {} section.content {} section.content div#terminal {border: 1px solid #FFFFFF; width:100%; min-width: 500px; max-width:1000px; margin:10px auto; overflow:hidden; min-height:100px; padding:10px;} -section.content article {} +section.content article {unicode-bidi: embed; white-space: pre;} section.content form {} section.content form label, section.content form input, section.content form input:focus {display:block; float:left; border:none; margin:0; padding:0; font-family:monospace; font-size:14px; line-height:20px; background-color:#000000; color:#FFFFFF; outline:none;} diff --git a/clite/core.js b/clite/core.js index f789102..be94180 100644 --- a/clite/core.js +++ b/clite/core.js @@ -62,10 +62,9 @@ var clite = { head.appendChild(script); }, file:function(name,callback) { - if (window.location.protocol == 'file:') { - clite.shell.writeLine('open file: '+name); + 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.events.refocus(); + clite.shell.writeLine('open file: '+name); return; } @@ -84,26 +83,412 @@ var clite = { this.core.execSafeAsync(function() { clite.term.clear(false); + + // setup vfs + clite.log.write('Setting up VFS'); + clite.vfs.init(); + var vfsapi = clite.vfs.getApi(); + clite.log.init(vfsapi); + clite.log.write('Mounting wfs on /'); // mount core (root) filesystem using data/filesys.txt - // populate /dev + clite.core.load.file('data/filesys.txt',function(data) { + if (data == null) { + clite.log.write('No Filesystem Found'); + return; + } + vfsapi.mkFile('/dev/wfs'); + var n = vfsapi.getNode('/dev/wfs'); + if (!n) { + clite.log.write('wfs device failure'); + return; + } + n.data.content = data; + n.perms = '-r--r-----'; + var fd = clite.io.open('/dev/wfs'); + var l; + while ((l = clite.io.readLine(fd)) != null) { + if (l.length <1 || l[0] == '#') + continue; + var parts = l.split(':'); + if (parts.length != 5) + continue; + var url = parts[0]; + var path = parts[1]; + var uid = parseInt(parts[2]); + var gid = parseInt(parts[3]); + var perms = parts[4]; + + var fn = vfsapi.getNode(path); + // TODO: make the full path if needed + if (!fn) { + if (perms[0] == 'd') { + vfsapi.mkDir(path); + }else{ + vfsapi.mkFile(path); + } + fn = vfsapi.getNode(path); + } + if (!fn) + continue; + fn.perms = perms; + fn.uid = uid; + fn.gid = gid; + fn.name = clite.lib.basename(path); + fn.data.remote = url; + fn.data.content = null; + clite.log.write('added "'+path+'" ("'+fn.name+'")'); + } + clite.io.close(fd); + // populate /dev (data/filesys.txt is /dev/wfs + clite.log.write('Populating /dev'); // check cookies for login + clite.log.write('Checking for user session'); // if new user: // read in /usr/share/introduction and write to shell // if logged in: // create user session + clite.log.write('Creating Shell'); clite.events.refocus(); + }); - clite.core.load.file('data/filesys.txt',clite.shell.writeLine); }); } }; +clite.io = { + init:function() { + var vfsapi = clite.vfs.getApi(); + + function getFileDes(path,link) { + var n = vfsapi.getNode(path); + if (!n) + return null; + if (n.data.islink && !link) // if link is false, follow links + n = vfsapi.getNode(n.data.content); + var p = (n.data.remote && n.data.content == null); + var fd = Object.create({ + node:n, + pos:0, + canwrite:false, + remote:{ + ispending:p, + callback:null + } + }); + if (p) + clite.core.load.file(n.data.remote,function(d) { + n.data.content = d; + try{ + fd.remote.callback(fd); + } catch(err) {} + }); + // TODO check permissions to see if this is writable + return fd; + } + clite.io.creat = function(path,type) { + switch (type) { + case 'd': + return vfsapi.mkDir(path); + break; + case 'l': + return vfsapi.mkLink(path); + break; + default: + return vfsapi.mkFile(path); + } + } + clite.io.open = function(path) { + return getFileDes(path,false); + } + clite.io.close = function(fd) { + try{ + fd.node = null; + delete fd; + } catch(err) {} + } + clite.io.read = function(fd) { + if (fd.node.data.content == null) + return null; + if (fd.pos >= fd.node.data.content.length) + return null; + return fd.node.data.content[fd.pos++]; + } + clite.io.readLine = function(fd) { + if (fd.node.data.content == null) + return null; + if (fd.pos >= fd.node.data.content.length) + return null; + if (typeof fd.node.data.content != 'string') + return null; + var e = fd.node.data.content.indexOf('\n',fd.pos); + var l = ''; + if (e < 0) { + l = fd.node.data.content.substring(fd.pos); + }else{ + l = fd.node.data.content.substring(fd.pos,e); + fd.pos++; + } + fd.pos += l.length; + return l; + } + clite.io.write = function(fd,data) { + if (!fd.canwrite) + return false; + if (typeof fd.data.content != 'string') + return false; + if (typeof data != 'string') + return false; + if (fd.pos + data.length >= fd.data.content.length) { + fd.data.content = fd.data.content.substring(0,fd.pos)+data; + fd.pos = fd.data.content.length; + }else{ + var b = fd.data.content.substring(0,fd.pos); + var e = fd.data.content.substring(fd.pos+data.length); + fd.data.content = b+data+e; + } + return true; + } + clite.io.ftruncate = function(fd,len) { + if (!fd.canwrite) + return false; + if (typeof fd.data.content != 'string') + return false; + fd.data.content = fd.data.content.substring(0,len); + if (fd.pos > len) + fd.pos = len; + return true; + } + clite.io.truncate = function(path,len) { + var fd = getFileDes(path,false); + if (!fd) + return false; + var r = clite.io.ftruncate(fd,len); + close(fd); + return r; + } + clite.io.seek = function(fd,pos) { + if (fd.node.data.content == null) + return 0; + if (fd.node.data.content.length < 1) + return 0; + if (pos<0) + return fd.pos; + fd.pos = pos; + if (fd.pos >= fd.node.data.content.length) + fd.pos = fd.node.data.content.length-1; + return fd.pos; + } + clite.io.remove = function(path) { + var fd = getFileDes(path,true); + if (!fd || !fd.canwrite) + return false; + return vfsapi.remove(path); + } + clite.io.link = function(path,target) { + if (!getFileDes(target,false)) + return false; + var fd = getFileDes(path,true); + if (fd) { + if (!fd.canwrite) + return false; + fd.node.data.content = target; + return true; + } + return vfsapi.mkLink(path,target); + } + }, + creat:null, + open:null, + close:null, + read:null, + readLine:null, + write:null, + ftruncate:null, + truncate:null, + seek:null, + remove:null, + link:null +}; + +clite.vfs = { + isinit:false, + init:function() { + var vfsdata = { + fs:{}, + api:{} + }; + + function findNodeChild(node,name) { + for (var i=0; i 0) { + if (!n.data.isdir || !checkCanOpen(n)) + return null; + var p = parts.shift(); + if (!p || p == '') + continue; + var nn = findNodeChild(n,p); + if (!nn) + return null; + n = nn; + } + if (!checkCanOpen(n)) + return null; + return n; + } + vfsdata.api.getFile = function(path) { + var n = vfsdata.api.getNode(path); + if (!n) + return null; + if (n.data.islink) // get the actual file data, not the link data + return vfsdata.api.getFile(n.data.content); + return n.data.content; + } + vfsdata.api.mkDir = function(path) { + if (vfsdata.api.getNode(path) != null) + return false; + var parts = path.split('/'); + var name = parts.pop(); + var dir = parts.join('/'); + if (dir.length < 1) + dir = '/'; + var parent = vfsdata.api.getNode(dir); + if (!parent || !parent.data.isdir) + return false; + var n = mkNode(); + n.name = name; + n.data.parent = parent; + n.data.content = []; + n.data.isdir = true; + n.perms[0] = 'd'; + // TODO: set uid/gid/permissions + parent.data.content.push(n); + return true; + } + vfsdata.api.mkFile = function(path) { + if (vfsdata.api.getNode(path) != null) + return false; + var parts = path.split('/'); + var name = parts.pop(); + var dir = parts.join('/'); + if (dir.length < 1) + dir = '/'; + var parent = vfsdata.api.getNode(dir); + if (!parent || !parent.data.isdir) + return false; + var n = mkNode(); + n.name = name; + n.data.parent = parent; + n.data.content = ''; + // TODO: set uid/gid/permissions + parent.data.content.push(n); + return true; + } + vfsdata.api.mkLink = function(path,target) { + if (vfsdata.api.getNode(path) != null) + return false; + var parts = path.split('/'); + var name = parts.pop(); + var dir = parts.join('/'); + if (dir.length < 1) + dir = '/'; + var parent = vfsdata.api.getNode(dir); + if (!parent || !parent.data.isdir) + return false; + var n = mkNode(); + n.name = name; + n.data.parent = parent; + n.data.islink = true; + n.perms[0] = 'l'; + n.data.content = target; + // TODO: set uid/gid/permissions + parent.data.content.push(n); + return true; + } + vfsdata.api.remove = function(path) { + return false; + } + + // make some directories + vfsdata.api.mkDir('/bin'); + vfsdata.api.mkDir('/dev'); + vfsdata.api.mkDir('/etc'); + vfsdata.api.mkDir('/usr'); + vfsdata.api.mkDir('/usr/clite'); + vfsdata.api.mkDir('/usr/clite/web'); + vfsdata.api.mkDir('/usr/home'); + vfsdata.api.mkDir('/usr/share'); + vfsdata.api.mkDir('/var'); + vfsdata.api.mkFile('/var/logs'); + + clite.io.init(); + vfsdata.api.isinit = true; + + clite.core.execSafeAsync(function() {clite.vfs.init = null;}); + }, + getApi:null +}; + clite.log = { + init:function(vfs) { + clite.log.write = function(txt) { + // TODO: after login don't write to term or shell + try { + if (clite.term.hasInput()) { + clite.shell.writeLine(txt); + }else{ + clite.term.writeLine(txt); + } + } catch(e) {} + var n = vfs.getNode('/var/logs'); + if (n) + n.data.content += txt+'\n'; + console.log(txt); + } + clite.core.execSafeAsync(function(){clite.log.init = null;}); + }, write:function(txt) { - // TODO: after login log to /var/log/messages instead of to shell try { - clite.shell.writeLine(txt); + clite.term.writeLine(txt); } catch(e) {} console.log(txt); } @@ -115,6 +500,26 @@ clite.term = { if (form) clite.term.genForm(); }, + preventInput:function() { + var f = document.getElementById('form'); + if (!f) + return; + f.parentNode.removeChild(f); + }, + hasInput:function() { + var f = document.getElementById('form'); + if (!f) + return false; + return true; + }, + writeLine:function(txt) { + var a = document.createElement('article'); + a.innerHTML = txt; + var t = document.getElementById('terminal'); + if (!t) + return; + t.appendChild(a); + }, genForm:function() { var f = document.getElementById('form'); if (!f) @@ -137,18 +542,22 @@ clite.term = { i.onkeyup = clite.events.keyup; i.onfocusout = clite.events.refocus; i.onblur = clite.events.refocus; - if (i.type == 'file') { + if (i.type == 'file') { // this is only needed due to dirty hacks to read in a file: path i.onchange = function(e) { var reader = new FileReader(); reader.onload = function() { - clite.state.input.type.callback(reader.result); + var cb = clite.state.input.type.callback; clite.term.setPass(false); - clite.shell.writeLine('loaded'); + clite.term.writeLine('loaded'); + clite.term.preventInput(); + cb(reader.result); }; reader.onerror = function() { - clite.state.input.type.callback(null); + var cb = clite.state.input.type.callback; clite.term.setPass(false); - clite.shell.writeLine('load failed'); + clite.term.writeLine('load failed'); + clite.term.preventInput(); + cb(null); }; reader.readAsText(e.target.files[0]); } @@ -174,12 +583,7 @@ clite.shell = { readLine:function(cb) { }, writeLine:function(txt) { - var a = document.createElement('article'); - a.innerHTML = txt; - var t = document.getElementById('terminal'); - if (!t) - return; - t.appendChild(a); + clite.term.writeLine(txt); clite.events.refocus(); }, exec:function(txt) { @@ -193,6 +597,8 @@ clite.shell = { clite.shell.history.add(txt); clite.shell.history.resetCurrent(); clite.shell.writeLine(clite.shell.prompt.getHTML()+txt); + // check for a macro + // then either run the macro or expand the program to be executed via PATH clite.shell.exec(txt); }, prompt:{ @@ -254,6 +660,7 @@ clite.shell = { }; clite.lib = { + // makes text html safe htmlEncode:function(txt) { return txt .replace(/&/g, '&') @@ -262,5 +669,16 @@ clite.lib = { .replace(/>/g, '>') .replace(/