From 4058137ef79ce133a4a852593c57f4448be9443b Mon Sep 17 00:00:00 2001 From: minjaesong Date: Sun, 26 Dec 2021 16:19:58 +0900 Subject: [PATCH] fontupd --- assets/FontROM7x14.png | Bin 3346 -> 3212 bytes assets/FontROM7x14_HANGUL.kra | 4 +- assets/disk0/home/hangasm.js | 2 + assets/disk0/tvdos/bin/color.js | 27 +- assets/disk0/tvdos/bin/commandko.js | 567 ++++++++++++++++++++++++++++ 5 files changed, 597 insertions(+), 3 deletions(-) create mode 100644 assets/disk0/tvdos/bin/commandko.js diff --git a/assets/FontROM7x14.png b/assets/FontROM7x14.png index 112394b4bc86ec1cddf5a25d120900cc901b8045..2a16cd9b8595c3650acdf405ab52a5c037989d60 100644 GIT binary patch delta 2806 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 IM6N<$f;-Jg_y7O^ delta 2941 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 40) ? "TSVM Disk Operating System, version " + _TVDOS.VERSION + : "TSVM Disk Operating System " + _TVDOS.VERSION; +const greetLeftPad = (termWidth - welcome_text.length - 6) >> 1; +const greetRightPad = termWidth - greetLeftPad - welcome_text.length - 6; + +function makeHash() { + let e = "YBNDRFG8EJKMCPQXOTLVWIS2A345H769"; + let m = e.length; + return e[Math.floor(Math.random()*m)] + e[Math.floor(Math.random()*m)] + e[Math.floor(Math.random()*m)] + e[Math.floor(Math.random()*m)] + e[Math.floor(Math.random()*m)] +} + +const shellID = makeHash(); + +function print_prompt_text() { + if (goFancy) { + con.color_pair(239,161); + print(" "+CURRENT_DRIVE+":"); + con.color_pair(161,253); + con.addch(16);con.curs_right(); + con.color_pair(0,253); + print(" /"+shell_pwd.join("/").substring(1)+" "); + if (errorlevel != 0) { + con.color_pair(166,253); + print("["+errorlevel+"] "); + } + con.color_pair(253,255); + con.addch(16);con.curs_right(); + con.addch(32);con.curs_right(); + con.color_pair(253,255); + } + else { +// con.color_pair(253,255); + if (errorlevel != 0) + print(CURRENT_DRIVE + ":/" + shell_pwd.join("/") + " [" + errorlevel + "]" + PROMPT_TEXT); + else + print(CURRENT_DRIVE + ":/" + shell_pwd.join("/") + PROMPT_TEXT); + } +} + +function greet() { + if (goFancy) { + con.color_pair(239,255); + con.clear(); + con.color_pair(253,255); + print(' ');con.addch(17);con.curs_right(); + con.color_pair(0,253); + print(" ".repeat(greetLeftPad)+welcome_text+" ".repeat(greetRightPad)); + con.color_pair(253,255); + con.addch(16);con.curs_right();print(' '); + con.move(3,1); + } + else + println(welcome_text); +} + +/*uninterruptible*/ function sendLcdMsg(s) { + // making it uninterruptible + sys.poke(-1025, (s === undefined) ? 0 : s.charCodeAt(0)|0); + sys.poke(-1026, (s === undefined) ? 0 : s.charCodeAt(1)|0); + sys.poke(-1027, (s === undefined) ? 0 : s.charCodeAt(2)|0); + sys.poke(-1028, (s === undefined) ? 0 : s.charCodeAt(3)|0); + sys.poke(-1029, (s === undefined) ? 0 : s.charCodeAt(4)|0); + sys.poke(-1030, (s === undefined) ? 0 : s.charCodeAt(5)|0); + sys.poke(-1031, (s === undefined) ? 0 : s.charCodeAt(6)|0); + sys.poke(-1032, (s === undefined) ? 0 : s.charCodeAt(7)|0); + sys.poke(-1033, (s === undefined) ? 0 : s.charCodeAt(8)|0); + sys.poke(-1034, (s === undefined) ? 0 : s.charCodeAt(9)|0); + sys.poke(-1035, (s === undefined) ? 0 : s.charCodeAt(10)|0); + sys.poke(-1036, (s === undefined) ? 0 : s.charCodeAt(11)|0); + sys.poke(-1037, (s === undefined) ? 0 : s.charCodeAt(12)|0); + sys.poke(-1038, (s === undefined) ? 0 : s.charCodeAt(13)|0); + sys.poke(-1039, (s === undefined) ? 0 : s.charCodeAt(14)|0); + sys.poke(-1040, (s === undefined) ? 0 : s.charCodeAt(15)|0); + sys.poke(-1041, (s === undefined) ? 0 : s.charCodeAt(16)|0); + sys.poke(-1042, (s === undefined) ? 0 : s.charCodeAt(17)|0); + sys.poke(-1043, (s === undefined) ? 0 : s.charCodeAt(18)|0); + sys.poke(-1044, (s === undefined) ? 0 : s.charCodeAt(19)|0); + sys.poke(-1045, (s === undefined) ? 0 : s.charCodeAt(20)|0); + sys.poke(-1046, (s === undefined) ? 0 : s.charCodeAt(21)|0); + sys.poke(-1047, (s === undefined) ? 0 : s.charCodeAt(22)|0); + sys.poke(-1048, (s === undefined) ? 0 : s.charCodeAt(23)|0); +} + +function trimStartRevSlash(s) { + var cnt = 0; + while (cnt < s.length) { + var chr = s[cnt]; + + if (chr != '\\') break; + + cnt += 1; + } + + return s.substring(cnt); +} + +let shell = {}; +shell.replaceVarCall = function(value) { +// syntax: +// line = literal [varcall] [literal] ; +// varcall = "$" ident ; +// ident = ? regex: [A-Za-z_]+ ? ; +// literal = ? you know what it is ? ; + let replaceMap = []; + let varMode = false; + let sb = ''; + for (let i=0; i= 48 && cp <= 57 || cp >= 65 && cp <= 90 || cp == 95 || cp >= 97 && cp <= 122)) { + replaceMap.push({s:sb,r:true}); + sb = ''+char; varMode = false; + } + else sb += char; + }; replaceMap.push({s:sb,r:(varMode)}); + + return replaceMap.map(it => (it.r) ? _TVDOS.variables[it.s] : it.s).join(''); +} +shell.getPwd = function() { return shell_pwd; } +shell.getPwdString = function() { return "\\" + (shell_pwd.concat([""])).join("\\"); } +shell.getCurrentDrive = function() { return CURRENT_DRIVE; } +// example input: echo "the string" > subdir\test.txt +shell.parse = function(input) { + var tokens = []; + var stringBuffer = ""; + var mode = "LITERAL"; // LITERAL, QUOTE, ESCAPE, LIMBO + var i = 0 + while (i < input.length) { + const c = input[i]; +/*digraph g { + LITERAL -> QUOTE [label="\""] + LITERAL -> LIMBO [label="space"] + LITERAL -> LITERAL [label=else] + + QUOTE -> LIMBO [label="\""] + QUOTE -> ESCAPE [label="\\"] + QUOTE -> QUOTE [label=else] + + ESCAPE -> QUOTE + + LIMBO -> LITERAL [label="not space"] + LIMBO -> QUOTE [label="\""] + LIMBO -> LIMBO [label="space"] +}*/ + if ("LITERAL" == mode) { + if (' ' == c) { + tokens.push(stringBuffer); stringBuffer = ""; + mode = "LIMBO"; + } + else if ('"' == c) { + tokens.push(stringBuffer); stringBuffer = ""; + mode = "QUOTE"; + } + else { + stringBuffer += c; + } + } + else if ("LIMBO" == mode) { + if ('"' == c) { + mode = "QUOTE"; + } + else if (c != ' ') { + mode = "LITERAL"; + stringBuffer += c; + } + } + else if ("QUOTE" == mode) { + if ('"' == c) { + tokens.push(stringBuffer); stringBuffer = ""; + mode = "LIMBO"; + } + else if ('^' == c) { + mode = "ESCAPE"; + } + else { + stringBuffer += c; + } + } + else if ("ESCAPE" == mode) { + TODO(); + } + + i += 1; + } + + if (stringBuffer.length > 0) { + tokens.push(stringBuffer); + } + + return tokens; +} +shell.resolvePathInput = function(input) { + // replace revslashes into rslashes + var pathstr = input.replaceAll('\\','/'); + var startsWithSlash = input.startsWith('/'); + var newPwd = []; + + // split them into an array while filtering empty elements except for the root 'head' + var ipwd = (startsWithSlash ? [""] : shell_pwd).concat(pathstr.split("/").filter(function(it) { return (it.length > 0); })); + + serial.println("command.js > resolvePathInput > ipwd = "+ipwd); + serial.println("command.js > resolvePathInput > newPwd = "+newPwd); + + // process dots + ipwd.forEach(function(it) { + serial.println("command.js > resolvePathInput > ipwd.forEach > it = "+it); + if (it === ".." && newPwd[1] !== undefined) { + newPwd.pop(); + } + else if (it !== ".." && it !== ".") { + newPwd.push(it); + } + serial.println("command.js > resolvePathInput > newPwd = "+newPwd); + }); + + // construct new pathstr from pwd arr so it will be sanitised + pathstr = newPwd.join('/').substring(1); + + return { string: pathstr, pwd: newPwd }; +} +shell.coreutils = { +/* Args follow this format: + * <1st arg> <2nd arg> ... + * NOTE: + * even if there's no 1st arg, length of args may not be 1, therefore don't: + * if (args.length < 2) + * but do instead: + * if (args[1] === undefined) + */ + cd: function(args) { + if (args[1] === undefined) { + println(CURRENT_DRIVE+":"+shell_pwd.join("/")); + return + } + var path = shell.resolvePathInput(args[1]) + if (DEBUG_PRINT) serial.println("command.js > cd > pathstr = "+path.string); + + // check if path is valid + var dirOpenedStatus = filesystem.open(CURRENT_DRIVE, path.string, 'R'); + var isDir = filesystem.isDirectory(CURRENT_DRIVE); // open a dir; if path is nonexistent, file won't actually be opened + if (!isDir) { printerrln("CHDIR failed for '"+path.string+"'"); return dirOpenedStatus; } // if file is not opened, IO error code will be returned + + shell_pwd = path.pwd; + }, + mkdir: function(args) { + if (args[1] === undefined) { + printerrln("Syntax error"); + return + } + var path = shell.resolvePathInput(args[1]) + if (DEBUG_PRINT) serial.println("command.js > mkdir > pathstr = "+path.string); + + // check if path is valid + var dirOpenedStatus = filesystem.open(CURRENT_DRIVE, path.string, 'W'); + var mkdird = filesystem.mkDir(CURRENT_DRIVE); + if (!mkdird) { printerrln("MKDIR failed for '"+path.string+"'"); return dirOpenedStatus; } + }, + cls: function(args) { + con.clear(); + graphics.clearPixels(255); + }, + exit: function(args) { + cmdExit = true; + }, + ver: function(args) { + println(welcome_text); + }, + echo: function(args) { + if (args[1] !== undefined) { + args.forEach(function(it,i) { if (i > 0) print(shell.replaceVarCall(it)+" ") }); + } + println(); + }, + rem: function(args) { + return 0; + }, + set: function(args) { + // print all the env vars + if (args[1] === undefined) { + Object.entries(_TVDOS.variables).forEach(function(a) { println(a[0]+"="+a[1]); }) + } + else { + // parse key-value pair with splitter '=' + var key = undefined; var value = undefined; + // if syntax " = " is used? + if ('=' == args[2]) { + key = args[1].toUpperCase(); value = args[3]; + } + else if (args[2] === undefined) { + var pair = args[1].split('='); + key = pair[0].toUpperCase(); value = pair[1]; + } + + if (key == undefined) throw SyntaxError("Input format must be 'key=value'"); + + // if value is undefined, show what envvar[key] has + if (value === undefined) { + if (_TVDOS.variables[key] === undefined) + println("Environment variable '"+key+"' not found"); + else + println(_TVDOS.variables[key]) + } + else { + _TVDOS.variables[key] = shell.replaceVarCall(value); + + // if key is KEYBOARD, reload the keyboard layout + if ("KEYBOARD" == key) + input.changeKeyLayout(_TVDOS.variables.KEYBOARD || "us_qwerty"); + } + } + }, + dir: function(args) { + var pathstr = (args[1] !== undefined) ? args[1] : shell.getPwdString(); + + // check if path is valid + var pathOpenedStatus = filesystem.open(CURRENT_DRIVE, pathstr, 'R'); + if (pathOpenedStatus != 0) { printerrln("File not found"); return pathOpenedStatus; } + + var port = filesystem._toPorts(CURRENT_DRIVE)[0] + com.sendMessage(port, "LIST"); + println(com.pullMessage(port)); + }, + cat: function(args) { + var pathstr = (args[1] !== undefined) ? args[1] : shell.getPwdString(); + + var pathOpenedStatus = filesystem.open(CURRENT_DRIVE, pathstr, 'R'); + if (pathOpenedStatus != 0) { printerrln("File not found"); return pathOpenedStatus; } + let contents = filesystem.readAll(CURRENT_DRIVE); + // TODO just print out what's there + print(contents); + } +}; +shell.coreutils.chdir = shell.coreutils.cd; +Object.freeze(shell.coreutils); +shell.execute = function(line) { + if (0 == line.size) return; + var tokens = shell.parse(line); + var cmd = tokens[0]; + if (cmd === undefined || cmd === '') return 0; + + // handle Ctrl-C + if (con.hitterminate()) return 1; + + if (shell.coreutils[cmd.toLowerCase()] !== undefined) { + var retval = shell.coreutils[cmd.toLowerCase()](tokens); + return retval|0; // return value of undefined will cast into 0 + } + else { + // search through PATH for execution + + var fileExists = false; + var searchDir = (cmd.startsWith("/")) ? [""] : ["/"+shell_pwd.join("/")].concat(_TVDOS.getPath()); + + var pathExt = []; // it seems Nashorn does not like 'let' too much? this line gets ignored sometimes + // fill pathExt using %PATHEXT% but also capitalise them + if (cmd.split(".")[1] === undefined) + _TVDOS.variables.PATHEXT.split(';').forEach(function(it) { pathExt.push(it); pathExt.push(it.toUpperCase()); }); + else + pathExt.push(""); // final empty extension + + searchLoop: + for (var i = 0; i < searchDir.length; i++) { + for (var j = 0; j < pathExt.length; j++) { + var search = searchDir[i]; if (!search.endsWith('\\')) search += '\\'; + var path = trimStartRevSlash(search + cmd + pathExt[j]); + + if (DEBUG_PRINT) { + serial.println("[command.js > shell.execute] file search path: "+path); + } + + if (0 == filesystem.open(CURRENT_DRIVE, path, "R")) { + fileExists = true; + break searchLoop; + } + } + } + + if (!fileExists) { + printerrln('Bad command or filename: "'+cmd+'"'); + return 127; + } + else { + var programCode = filesystem.readAll(CURRENT_DRIVE); + var extension = undefined; + // get proper extension + var dotSepTokens = cmd.split('.'); + if (dotSepTokens.length > 1) extension = dotSepTokens[dotSepTokens.length - 1].toUpperCase(); + + if ("BAT" == extension) { + // parse and run as batch file + var lines = programCode.split('\n').filter(function(it) { return it.length > 0; }); + lines.forEach(function(line) { + shell.execute(line); + }); + } + else { + let gotError = false; + + try { + errorlevel = 0; // reset the number + + if (_G.shellProgramTitles === undefined) _G.shellProgramTitles = []; + _G.shellProgramTitles.push(cmd.toUpperCase()) + sendLcdMsg(_G.shellProgramTitles[_G.shellProgramTitles.length - 1]); + //serial.println(_G.shellProgramTitles); + + errorlevel = execApp(programCode, tokens); // return value of undefined will cast into 0 + } + catch (e) { + gotError = true; + + serial.printerr(`[command.js] program quit with ${e}:\n${e.stack || '(stack trace unavailable)'}`); + + if (`${e}`.startsWith("InterruptedException")) + errorlevel = SIGTERM.name; + else if (e instanceof IllegalAccessException || `${e}`.startsWith("net.torvald.tsvm.ErrorIllegalAccess")) + errorlevel = SIGSEGV.name; + + // exception catched means something went wrong, so if errorlevel is found to be zero, force set to 1. + if (errorlevel === 0 || errorlevel == undefined) + errorlevel = 1; + } + finally { + // sometimes no-error program may return nothing as the errorlevel; force set to 0 then. + if (!gotError && (errorlevel == undefined || (typeof errorlevel.trim == "function" && errorlevel.trim().length == 0) || isNaN(errorlevel))) + errorlevel = 0; + + serial.printerr(`errorlevel: ${errorlevel}`); + + _G.shellProgramTitles.pop(); + sendLcdMsg(_G.shellProgramTitles[_G.shellProgramTitles.length - 1]); + //serial.println(_G.shellProgramTitles); + + return errorlevel; + } + } + } + } +}; +Object.freeze(shell); +_G.shell = shell; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +if (exec_args[1] !== undefined) { + // only meaningful switches would be either /c or /k anyway + var firstSwitch = exec_args[1].toLowerCase(); + + // command /c + // ^[0] ^[1] ^[2] + if ("/c" == firstSwitch) { + if ("" == exec_args[2]) return 0; // no commands were given, just exit successfully + return shell.execute(exec_args[2]); + } + else if ("/k" == firstSwitch) { + if ("" == exec_args[2]) return 0; // no commands were given, just exit successfully + shell.execute(exec_args[2]); + goInteractive = true; + } + else if ("/fancy" == firstSwitch) { + graphics.setBackground(2,3,4); + goFancy = true; + goInteractive = true; + } + else { + printerrln("Invalid switch: "+exec_args[1]); + return 1; + } +} +else { + goInteractive = true; +} + +let cmdExit = false; +if (goInteractive) { + con.curs_set(1); + greet(); + + let cmdHistory = []; // zeroth element is the oldest + let cmdHistoryScroll = 0; // 0 for outside-of-buffer, 1 for most recent + while (!cmdExit) { + con.curs_set(1); + print_prompt_text(); + + var cmdbuf = ""; + + while (true) { + let key = con.getch(); + + // printable chars + if (key >= 32 && key <= 126) { + var s = String.fromCharCode(key); + cmdbuf += s; + print(s); + } + // backspace + else if (key === con.KEY_BACKSPACE && cmdbuf.length > 0) { + cmdbuf = cmdbuf.substring(0, cmdbuf.length - 1); + print(String.fromCharCode(key)); + } + // enter + else if (key === 10 || key === con.KEY_RETURN) { + println(); + + shell.execute(cmdbuf); + + if (cmdbuf.trim().length > 0) + cmdHistory.push(cmdbuf); + + cmdHistoryScroll = 0; + con.curs_set(1); + + break; + } + // up arrow + else if (key === con.KEY_UP && cmdHistory.length > 0 && cmdHistoryScroll < cmdHistory.length) { + cmdHistoryScroll += 1; + + // back the cursor in order to type new cmd + var x = 0; + for (x = 0; x < cmdbuf.length; x++) print(String.fromCharCode(8)); + cmdbuf = cmdHistory[cmdHistory.length - cmdHistoryScroll]; + // re-type the new command + print(cmdbuf); + + } + // down arrow + else if (key === con.KEY_DOWN) { + if (cmdHistoryScroll > 0) { + // back the cursor in order to type new cmd + var x = 0; + for (x = 0; x < cmdbuf.length; x++) print(String.fromCharCode(8)); + cmdbuf = cmdHistory[cmdHistory.length - cmdHistoryScroll]; + // re-type the new command + print(cmdbuf); + + cmdHistoryScroll -= 1; + } + else { + // back the cursor in order to type new cmd + var x = 0; + for (x = 0; x < cmdbuf.length; x++) print(String.fromCharCode(8)); + cmdbuf = ""; + } + } + } + } +} + +return 0; \ No newline at end of file