diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/edit.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/edit.lua index 3fae5aeef..55860887e 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/bin/edit.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/bin/edit.lua @@ -684,4 +684,4 @@ while running do end term.clear() -term.setCursorBlink(false) +term.setCursorBlink(true) diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/sh.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/sh.lua index 9bb706eee..f5fde9c62 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/bin/sh.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/bin/sh.lua @@ -12,7 +12,8 @@ end shell.prime() local update_gpu = io.output().tty -local interactive = io.input().tty +local needs_profile = io.input().tty +local input_handler = {hint = sh.hintHandler} if #args == 0 then while true do @@ -20,15 +21,13 @@ if #args == 0 then while not tty.isAvailable() do event.pull("term_available") end - if interactive == true then -- first time run AND interactive - interactive = 0 - tty.setReadHandler({hint = sh.hintHandler}) + if needs_profile then -- first time run AND interactive + needs_profile = nil dofile("/etc/profile.lua") end io.write(sh.expand(os.getenv("PS1") or "$ ")) - tty.setCursorBlink(true) end - local command = io.read() + local command = tty:read(input_handler) if command then command = text.trim(command) if command == "exit" then diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/tree.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/tree.lua new file mode 100644 index 000000000..e831cee04 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/openos/bin/tree.lua @@ -0,0 +1,321 @@ +local shell = require("shell") +local fs = require("filesystem") +local tx = require("transforms") +local text = require("text") + +local args, opts = shell.parse(...) + +local function die(...) + io.stderr:write(...) + os.exit(1) +end + +do -- handle cli + if opts.help then + print([[Usage: tree [OPTION]... [FILE]... + -a, --all do not ignore entries starting with . + --full-time with -l, print time in full iso format + -h, --human-readable with -l, print human readable sizes + --si likewise, but use powers of 1000 not 1024 + --level=LEVEL descend only LEVEL directories deep + --color=WHEN WHEN can be + auto - colorize output only if writing to a tty, + always - always colorize output, + never - never colorize output; (default: auto) + -l use a long listing format + -f print the full path prefix for each file + -i do not print indentation lines + -p append "/" indicator to directories + -Q, --quote quote filenames with double quotes + -r, --reverse reverse order while sorting + -S sort by file size + -t sort by modification type, newest first + -X sort alphabetically by entry extension + -C do not count files and directories + -R count root directories like other files + --help print this help and exit]]) + return 0 + end + + if #args == 0 then + table.insert(args, ".") + end + + opts.level = tonumber(opts.level) or math.huge + if opts.level < 1 then + die("Invalid level, must be greater than 0") + end + + opts.color = opts.color or "auto" + if opts.color == "auto" then + opts.color = io.stdout.tty and "always" or "never" + end + + if opts.color ~= "always" and opts.color ~= "never" then + die("Invalid value for --color=WHEN option; WHEN should be auto, always or never") + end +end + +local function peekable(iterator, state, var1) + local nextItem = {iterator(state, var1)} + + return setmetatable({ + peek = function() + return table.unpack(nextItem) + end + }, { + __call = coroutine.wrap(function() + while true do + local item = nextItem + nextItem = {iterator(state, nextItem[1])} + coroutine.yield(table.unpack(item)) + if nextItem[1] == nil then break end + end + end) + }) +end + +local function filter(entry) + return opts.a or entry:sub(1, 1) ~= "." +end + +local function stat(path) + local st = {} + st.path = path + st.name = fs.name(path) or "/" + st.sortName = st.name:gsub("^%.","") + st.time = fs.lastModified(path) + st.isLink = fs.isLink(path) + st.isDirectory = fs.isDirectory(path) + st.size = st.isLink and 0 or fs.size(path) + st.extension = st.name:match("(%.[^.]+)$") or "" + st.fs = fs.get(path) + return st +end + +local colorize +if opts.color == "always" then + -- from /lib/core/full_ls.lua + local colors = tx.foreach(text.split(os.getenv("LS_COLORS") or "", {":"}, true), function(e) + local parts = text.split(e, {"="}, true) + return parts[2], parts[1] + end) + + function colorize(stat) + return stat.isLink and colors.ln or + stat.isDirectory and colors.di or + colors["*" .. stat.extension] or + colors.fi + end +end + +local function list(path) + return coroutine.wrap(function() + local l = {} + for entry in fs.list(path) do + if filter(entry) then + table.insert(l, stat(fs.concat(path, entry))) + end + end + + if opts.S then + table.sort(l, function(a, b) + return a.size < b.size + end) + elseif opts.t then + table.sort(l, function(a, b) + return a.time < b.time + end) + elseif opts.X then + table.sort(l, function(a, b) + return a.extension < b.extension + end) + else + table.sort(l, function(a, b) + return a.sortName < b.sortName + end) + end + + for i = opts.r and #l or 1, opts.r and 1 or #l, opts.r and -1 or 1 do + coroutine.yield(l[i]) + end + end) +end + +local function digRoot(rootPath) + coroutine.yield(stat(rootPath), {}) + + if not fs.isDirectory(rootPath) then + return + end + local iterStack = {peekable(list(rootPath))} + local pathStack = {rootPath} + local levelStack = {not not iterStack[#iterStack]:peek()} + + + repeat + local entry = iterStack[#iterStack]() + + if entry then + levelStack[#levelStack] = not not iterStack[#iterStack]:peek() + + local path = fs.concat(fs.concat(table.unpack(pathStack)), entry.name) + + coroutine.yield(entry, levelStack) + + if entry.isDirectory and opts.level > #levelStack then + table.insert(iterStack, peekable(list(path))) + table.insert(pathStack, entry.name) + table.insert(levelStack, not not iterStack[#iterStack]:peek()) + end + else + table.remove(iterStack) + table.remove(pathStack) + table.remove(levelStack) + end + until #iterStack == 0 +end + +local function dig(roots) + return coroutine.wrap(function() + for _, root in ipairs(roots) do + digRoot(root) + end + end) +end + +local function nod(n) -- from /lib/core/full_ls.lua + return n and (tostring(n):gsub("(%.[0-9]+)0+$","%1")) or "0" +end + +local function formatFSize(size) -- from /lib/core/full_ls.lua + if not opts.h and not opts["human-readable"] and not opts.si then + return tostring(size) + end + + local sizes = {"", "K", "M", "G"} + local unit = 1 + local power = opts.si and 1000 or 1024 + + while size > power and unit < #sizes do + unit = unit + 1 + size = size / power + end + + return nod(math.floor(size*10)/10)..sizes[unit] +end + +local function pad(txt) -- from /lib/core/full_ls.lua + txt = tostring(txt) + return #txt >= 2 and txt or "0" .. txt +end + +local function formatTime(epochms) -- from /lib/core/full_ls.lua + local month_names = {"January","February","March","April","May","June", + "July","August","September","October","November","December"} + + if epochms == 0 then return "" end + + local d = os.date("*t", epochms) + local day, hour, min, sec = nod(d.day), pad(nod(d.hour)), pad(nod(d.min)), pad(nod(d.sec)) + + if opts["full-time"] then + return string.format("%s-%s-%s %s:%s:%s ", d.year, pad(nod(d.month)), pad(day), hour, min, sec) + else + return string.format("%s %+2s %+2s:%+2s ", month_names[d.month]:sub(1,3), day, hour, pad(min)) + end +end + +local function writeEntry(entry, levelStack) + for i, hasNext in ipairs(levelStack) do + if opts.i then break end + + if i == #levelStack then + if hasNext then + io.write("├── ") + else + io.write("└── ") + end + else + if hasNext then + io.write("│   ") + else + io.write(" ") + end + end + end + + if opts.l then + io.write("[") + + io.write(entry.isDirectory and "d" or entry.isLink and "l" or "f", "-") + io.write("r", entry.fs.isReadOnly() and "-" or "w", " ") + + io.write(formatFSize(entry.size), " ") + + io.write(formatTime(entry.time)) + io.write("] ") + end + + if opts.Q then io.write('"') end + + if opts.color == "always" then + io.write("\27[" .. colorize(entry) .. "m") + end + + if opts.f then + io.write(entry.path) + else + io.write(entry.name) + end + + if opts.color == "always" then + io.write("\27[0m") + end + + if opts.p and entry.isDirectory then + io.write("/") + end + + if opts.Q then io.write('"') end + io.write("\n") +end + +local function writeCount(dirs, files) + io.write("\n") + io.write(dirs, " director", dirs == 1 and "y" or "ies") + io.write(", ") + io.write(files, " file", files == 1 and "" or "s") + io.write("\n") +end + +local dirs, files = 0, 0 + +local roots = {} +for _, arg in ipairs(args) do + local path = shell.resolve(arg) + local real, reason = fs.realPath(path) + if not real then + die("cannot access ", path, ": ", reason or "unknown error") + elseif not fs.exists(path) then + die("cannot access ", path, ":", "No such file or directory") + else + table.insert(roots, real) + end +end + +for entry, levelStack in dig(roots) do + if opts.R or #levelStack > 0 then + if entry.isDirectory then + dirs = dirs + 1 + else + files = files + 1 + end + end + writeEntry(entry, levelStack) +end + +if not opts.C then + writeCount(dirs, files) +end + diff --git a/src/main/resources/assets/opencomputers/loot/openos/etc/profile.lua b/src/main/resources/assets/opencomputers/loot/openos/etc/profile.lua index 8f8fecb2d..9fc3df6d1 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/etc/profile.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/etc/profile.lua @@ -3,8 +3,11 @@ local tty = require("tty") local fs = require("filesystem") if tty.isAvailable() then - tty:write("\27[40m\27[37m") - tty.clear() + if io.stdout.tty then + io.write("\27[40m\27[37m") + tty.clear() + end + tty.setCursorBlink(true) end dofile("/etc/motd") @@ -31,7 +34,7 @@ os.setenv("IFS", " ") os.setenv("MANPATH", "/usr/man:.") os.setenv("PAGER", "/bin/more") os.setenv("PS1", "\27[40m\27[31m$HOSTNAME$HOSTNAME_SEPARATOR$PWD # \27[37m") -os.setenv("LS_COLORS", "{FILE=0xFFFFFF,DIR=0x66CCFF,LINK=0xFFAA00,['*.lua']=0x00FF00}") +os.setenv("LS_COLORS", "di=0;36:fi=0:ln=0;33:*.lua=0;32") shell.setWorkingDirectory(os.getenv("HOME")) diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/core/devfs/01_hw.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/core/devfs/01_hw.lua index 50e4a3147..8c8b8e231 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/core/devfs/01_hw.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/core/devfs/01_hw.lua @@ -40,8 +40,8 @@ end function adapter_api.create_toggle(read, write, switch) return { - read = function() return tostring(read()) end, - write = function(value) + read = read and function() return tostring(read()) end, + write = write and function(value) value = text.trim(tostring(value)) local on = value == "1" or value == "true" local off = value == "0" or value == "false" diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_ls.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_ls.lua index 5e7eb6bec..57e178153 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_ls.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_ls.lua @@ -2,6 +2,8 @@ local fs = require("filesystem") local shell = require("shell") local tty = require("tty") local unicode = require("unicode") +local tx = require("transforms") +local text = require("text") local dirsArg, ops = shell.parse(...) @@ -32,7 +34,6 @@ if #dirsArg == 0 then end local ec = 0 -local gpu = tty.gpu() local fOut = tty.isAvailable() and io.output().tty local function perr(msg) io.stderr:write(msg,"\n") ec = 2 end local function stat(names, index) @@ -56,42 +57,30 @@ local function stat(names, index) return info end local function toArray(i) local r={} for n in i do r[#r+1]=n end return r end -local restore_color = function() end local set_color = function() end -local prev_color -local function colorize() return prev_color end +local function colorize() return end if fOut and not ops["no-color"] then - local LSC = os.getenv("LS_COLORS") - if type(LSC) == "string" then - LSC = require("serialization").unserialize(LSC) + local LSC = tx.foreach(text.split(os.getenv("LS_COLORS") or "", {":"}, true), function(e) + local parts = text.split(e, {"="}, true) + return parts[2], parts[1] + end) + colorize = function(info) + return + info.isLink and LSC.ln or + info.isDir and LSC.di or + LSC['*'..info.ext] or + LSC.fi end - if not LSC then - perr("ls: unparsable value for LS_COLORS environment variable") - else - prev_color = gpu.getForeground() - restore_color = function() gpu.setForeground(prev_color) end - colorize = function(info) - return - info.isLink and LSC.LINK or - info.isDir and LSC.DIR or - LSC['*'..info.ext] or - LSC.FILE or - prev_color - end - set_color=function(c) - if gpu.getForeground() ~= c then - io.stdout:flush() - gpu.setForeground(c) - end - end + set_color=function(c) + io.write(string.char(0x1b), "[", c or "", "m") end end local msft={reports=0,proxies={}} function msft.report(files, dirs, used, proxy) local free = proxy.spaceTotal() - proxy.spaceUsed() - restore_color() - local pattern = "%5i File(s) %11i bytes\n%5i Dir(s) %11s bytes free\n" - io.write(string.format(pattern, files, used, dirs, tostring(free))) + set_color() + local pattern = "%5i File(s) %s bytes\n%5i Dir(s) %11s bytes free\n" + io.write(string.format(pattern, files, tostring(used), dirs, tostring(free))) end function msft.tail(names) local fsproxy = fs.get(names.path) @@ -123,7 +112,7 @@ function msft.final() for proxy,report in pairs(msft.proxies) do table.insert(groups, {proxy=proxy,report=report}) end - restore_color() + set_color() print("Total Files Listed:") for _,pair in ipairs(groups) do local proxy, report = pair.proxy, pair.report @@ -263,7 +252,7 @@ local function display(names) local format = "%s-r%s %+"..tostring(max_size_width).."s %"..tostring(max_date_width).."s" local meta = string.format(format, file_type, write_mode, size, modDate) local item = info.name..link_target - return {{color = prev_color, name = meta}, {color = colorize(info), name = item}} + return {{name = meta}, {color = colorize(info), name = item}} end elseif ops["1"] or not fOut then lines.n = #names @@ -332,7 +321,7 @@ local header = function() end if #dirsArg > 1 or ops.R then header = function(path) if not first_display then print() end - restore_color() + set_color() io.write(path,":\n") end end @@ -366,11 +355,20 @@ for _,dir in ipairs(dirsArg) do table.insert(file_set, dir) end end + io.output():setvbuf("line") -if #file_set > 0 then display(sort(file_set)) end -displayDirList(dir_set) -msft.final() + +local ok, msg = pcall(function() + if #file_set > 0 then display(sort(file_set)) end + displayDirList(dir_set) + msft.final() +end) + io.output():flush() io.output():setvbuf("no") -restore_color() +set_color() + +assert(ok, msg) + return ec + diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/core/lua_shell.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/core/lua_shell.lua index 82f4dbe32..ad7997a63 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/core/lua_shell.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/core/lua_shell.lua @@ -68,7 +68,7 @@ local function findKeys(t, r, prefix, name) end end -tty.setReadHandler({hint = function(line, index) +local read_handler = {hint = function(line, index) line = (line or "") local tail = line:sub(index) line = line:sub(1, index - 1) @@ -85,7 +85,7 @@ tty.setReadHandler({hint = function(line, index) table.insert(hints, key .. tail) end return hints -end}) +end} io.write("\27[37m".._VERSION .. " Copyright (C) 1994-2017 Lua.org, PUC-Rio\n") io.write("\27[33mEnter a statement and hit enter to evaluate it.\n") @@ -94,7 +94,7 @@ io.write("Press Ctrl+D to exit the interpreter.\n\27[37m") while tty.isAvailable() do io.write(env._PROMPT) - local command = io.read() + local command = tty:read(read_handler) if not command then -- eof return end diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/filesystem.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/filesystem.lua index 2171f4279..2d6d7740d 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/filesystem.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/filesystem.lua @@ -24,7 +24,6 @@ end local function saveConfig() local root = filesystem.get("/") if root and not root.isReadOnly() then - filesystem.makeDirectory("/etc") local f = filesystem.open("/etc/filesystem.cfg", "w") if f then f:write("autorun="..tostring(isAutorunEnabled)) diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/term.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/term.lua index 207c681c0..3ce1df9ff 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/term.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/term.lua @@ -194,9 +194,6 @@ function term.write(value, wrap) end function term.read(history, dobreak, hint, pwchar, filter) - if not io.stdin.tty then - return io.read("*L") - end history = history or {} local handler = history handler.hint = handler.hint or hint diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tty.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tty.lua index e54531f24..b860dbb77 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/tty.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tty.lua @@ -3,7 +3,6 @@ local event = require("event") local kb = require("keyboard") local component = require("component") local computer = require("computer") -local process = require("process") local keys = kb.keys local tty = {} @@ -19,11 +18,6 @@ tty.window = tty.internal = {} -function tty.setReadHandler(handler) - checkArg(1, handler, "table") - process.info().data.handler = handler -end - function tty.key_down_handler(handler, cursor, char, code) local data = cursor.data local c = false @@ -268,15 +262,17 @@ function tty.internal.build_vertical_reader() end -- read n bytes, n is unused -function tty.read(_, handler, cursor) +function tty.read(self, handler, cursor) checkArg(1, handler, "table", "number") checkArg(2, cursor, "table", "nil") - if type(handler) == "number" then - -- standard read as a stream, asking for n bytes - handler = process.info().data.handler or {} + if not io.stdin.tty or io.stdin.stream ~= self then + return io.stdin:readLine(false) end + if type(handler) ~= "table" then + handler = {} + end handler.index = 0 cursor = cursor or tty.internal.build_vertical_reader() @@ -324,7 +320,10 @@ function tty.setCursor(x, y) window.x, window.y = x, y end -function tty.write(_, value) +function tty.write(self, value) + if not io.stdout.tty or io.stdout.stream ~= self then + return io.write(value) + end local gpu = tty.gpu() if not gpu then return @@ -352,7 +351,8 @@ function tty.write(_, value) value = window.ansi_escape:sub(5) end for _,catt in ipairs(color_attributes) do - local colors = {0x0,0xff0000,0x00ff00,0xffff00,0x0000ff,0xff00ff,0x00ffff,0xffffff} + -- B6 is closer to cyan in 4 bit color + local colors = {0x0,0xff0000,0x00ff00,0xffff00,0x0000ff,0xff00ff,0x00B6ff,0xffffff} catt = catt - 29 local method = "setForeground" if catt > 10 then diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/vt100.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/vt100.lua index f32488909..4e6111b50 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/vt100.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/vt100.lua @@ -1,3 +1,4 @@ +local text = require("text") local vt100 = {} -- runs patterns on ansi until failure @@ -25,15 +26,26 @@ local rules = {} -- [%d+;%d+;..%d+m rules[{"%[", "[%d;]*", "m"}] = function(_, _, number_text) local numbers = {} - number_text:gsub("[^;]*", function(num) - local n = tonumber(num) or 0 - if n == 0 then + -- prefix and suffix ; act as reset + -- e.g. \27[41;m is actually 41 followed by a reset + number_text = ";" .. number_text:gsub("^;$","") .. ";" + local parts = text.split(number_text, {";"}) + local last_was_break + for _,part in ipairs(parts) do + local num = tonumber(part) + if not num then + num = last_was_break and 0 + last_was_break = true + else + last_was_break = false + end + if num == 0 then numbers[#numbers + 1] = 40 numbers[#numbers + 1] = 37 - else - numbers[#numbers + 1] = n + elseif num then + numbers[#numbers + 1] = num end - end) + end return numbers end diff --git a/src/main/resources/assets/opencomputers/robot.names b/src/main/resources/assets/opencomputers/robot.names index 27047dffa..1635f27cc 100644 --- a/src/main/resources/assets/opencomputers/robot.names +++ b/src/main/resources/assets/opencomputers/robot.names @@ -88,6 +88,7 @@ Robby # Forbidden Planet Roomba # Under your couch... wait. Rosie # The Jetsons Shakey # The first general-purpose mobile robot that could reason about its actions. +SHODAN # System Shock Skynet # Terminator Space Core # Portal SpiritedDusty # Contributor diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/BasicTextSegment.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/BasicTextSegment.scala index 07ad82e7a..365577fe4 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/BasicTextSegment.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/BasicTextSegment.scala @@ -1,7 +1,6 @@ package li.cil.oc.client.renderer.markdown.segment -import li.cil.oc.client.renderer.markdown.Document -import li.cil.oc.client.renderer.markdown.MarkupFormat +import li.cil.oc.client.renderer.markdown.{Document, MarkupFormat} import net.minecraft.client.gui.FontRenderer trait BasicTextSegment extends Segment { @@ -58,11 +57,21 @@ trait BasicTextSegment extends Segment { while (pos < s.length) { pos += 1 val width = stringWidth(s.take(pos), renderer) - if (width >= maxWidth) { - if (lastBreak > 0 || fullWidth <= maxLineWidth || s.exists(breaks.contains)) - if (maxWidth == maxLineWidth && fullWidth == maxLineWidth && !s.exists(breaks.contains)) return s.length - else return lastBreak + 1 - else return pos - 1 + val exceedsLineLength = width >= maxWidth + if (exceedsLineLength) { + val mayUseFullLine = maxWidth == maxLineWidth + val canFitInLine = fullWidth <= maxLineWidth + val matchesFullLine = fullWidth == maxLineWidth + if (lastBreak >= 0) { + return lastBreak + 1 // Can do a soft split. + } + if (mayUseFullLine && matchesFullLine) { + return s.length // Special case for exact match. + } + if (canFitInLine && !mayUseFullLine) { + return 0 // Wrap line, use next line. + } + return pos - 1 // Gotta split hard. } if (pos < s.length && breaks.contains(s.charAt(pos))) lastBreak = pos } diff --git a/src/main/scala/li/cil/oc/server/machine/luac/UnicodeAPI.scala b/src/main/scala/li/cil/oc/server/machine/luac/UnicodeAPI.scala index 0be3f1ead..c327815c4 100644 --- a/src/main/scala/li/cil/oc/server/machine/luac/UnicodeAPI.scala +++ b/src/main/scala/li/cil/oc/server/machine/luac/UnicodeAPI.scala @@ -81,7 +81,7 @@ class UnicodeAPI(owner: NativeLuaArchitecture) extends NativeLuaAPI(owner) { var width = 0 var end = 0 while (width < count) { - width += FontUtils.wcwidth(value(end)) + width += math.max(1, FontUtils.wcwidth(value(end))) end += 1 } if (end > 1) lua.pushString(value.substring(0, end - 1)) diff --git a/src/main/scala/li/cil/oc/server/machine/luaj/UnicodeAPI.scala b/src/main/scala/li/cil/oc/server/machine/luaj/UnicodeAPI.scala index b791210e1..2f008bddb 100644 --- a/src/main/scala/li/cil/oc/server/machine/luaj/UnicodeAPI.scala +++ b/src/main/scala/li/cil/oc/server/machine/luaj/UnicodeAPI.scala @@ -53,7 +53,7 @@ class UnicodeAPI(owner: LuaJLuaArchitecture) extends LuaJAPI(owner) { var width = 0 var end = 0 while (width < count) { - width += FontUtils.wcwidth(value(end)) + width += math.max(1, FontUtils.wcwidth(value(end))) end += 1 } if (end > 1) LuaValue.valueOf(value.substring(0, end - 1))