v2.0.0 - Overhauled the 'read' function in the terminal library, added a new Unicode library, and various functions added to the filesystem library.

In older versions, the 'read' function in the terminal library (termlib.lua) was most likely vibecoded by Fluxdrive, and was basically very inefficient. The new Unicode library includes functions for getting a code point from a character, and iterating Unicode characters from a string or an iterator function that returns every byte. It is now possible, in the filesystem library, to make virtual 'read streams' (filesystem.makeReadStream), which does the same thing as opening a file with some specific content. There are some new functions in read streams, which allows you to loop through bytes (open(...):iterateBytes), and loop through Unicode characters (open(...):iterateUnicodeChars). The edit app will be updated to v1.2.1 for importing the new Unicode library.
This commit is contained in:
Ponali
2025-07-07 17:45:52 +02:00
parent d386ec5eba
commit c0929bf639
7 changed files with 342 additions and 237 deletions
+2 -2
View File
@@ -1,7 +1,7 @@
local agcfg = { local agcfg = {
["halyde"] = { ["halyde"] = {
["maindir"] = "", ["maindir"] = "",
["version"] = "1.15.0", ["version"] = "2.0.0",
["description"] = "A universal, customizable and feature-packed operating system for OpenComputers.", ["description"] = "A universal, customizable and feature-packed operating system for OpenComputers.",
["directories"] = { ["directories"] = {
"halyde/apps", "halyde/apps",
@@ -82,7 +82,7 @@ local agcfg = {
}, },
["edit"] = { ["edit"] = {
["maindir"] = "", ["maindir"] = "",
["version"] = "1.2.0", ["version"] = "1.2.1",
["description"] = "The default text editor for Halyde.", ["description"] = "The default text editor for Halyde.",
["files"] = { ["files"] = {
"halyde/apps/edit.lua", "halyde/apps/edit.lua",
+1
View File
@@ -2,6 +2,7 @@ local file = ...
local fs = import("filesystem") local fs = import("filesystem")
local event = import("event") local event = import("event")
local component = import("component") local component = import("component")
local unicode = import("unicode")
local gpu = component.gpu local gpu = component.gpu
local width, height = gpu.getResolution() local width, height = gpu.getResolution()
local scrollPosX, scrollPosY = 1, 1 local scrollPosX, scrollPosY = 1, 1
+1
View File
@@ -2,6 +2,7 @@ local args = {...}
local target = args[1] local target = args[1]
args = nil args = nil
local fs = import("filesystem") local fs = import("filesystem")
local unicode = import("unicode")
local maxLength = 0 local maxLength = 0
local margin = 2 -- minimum space between filename and size local margin = 2 -- minimum space between filename and size
local dirTable = {} local dirTable = {}
+1 -1
View File
@@ -1,7 +1,7 @@
local loadfile = ... local loadfile = ...
local filesystem = loadfile("/halyde/lib/filesystem.lua")(loadfile) local filesystem = loadfile("/halyde/lib/filesystem.lua")(loadfile)
_G._OSVERSION = "Halyde 1.15.0" _G._OSVERSION = "Halyde 2.0.0"
_G._OSLOGO = "" _G._OSLOGO = ""
local handle, tmpdata = filesystem.open("/halyde/config/oslogo.ans", "r"), nil local handle, tmpdata = filesystem.open("/halyde/config/oslogo.ans", "r"), nil
repeat repeat
+145 -170
View File
@@ -1,3 +1,4 @@
local unicode = import("unicode")
local event = import("event") local event = import("event")
--local keyboard = import("keyboard") --local keyboard = import("keyboard")
@@ -86,8 +87,8 @@ function termlib.write(text, textWrap)
if text:find("\a") then if text:find("\a") then
computer.beep() computer.beep()
end end
text = "\27[0m" .. text:gsub("\t", " ")
text = tostring(text) text = tostring(text)
text = "\27[0m" .. text:gsub("\t", " ")
readBreak = 0 readBreak = 0
-- readBreak is for when, inside the for loop, there normally would have been an increase in the "i" variable because it has read more than one character. -- readBreak is for when, inside the for loop, there normally would have been an increase in the "i" variable because it has read more than one character.
-- unfortunately, changing the "i" variable would have unpredictable effects, so to not risk anything, this workaround was done. -- unfortunately, changing the "i" variable would have unpredictable effects, so to not risk anything, this workaround was done.
@@ -187,203 +188,177 @@ function _G.clear()
termlib.cursorPosX, termlib.cursorPosY = 1, 1 termlib.cursorPosX, termlib.cursorPosY = 1, 1
end end
-- god i hope this silly claude code works first try function _G.read(readHistoryType, prefix, defaultText, maxChars)
-- Fluxdrive caught vibe coding???
function _G.read(readHistoryType, prefix, defaultText)
checkArg(1, readHistoryType, "string", "nil") checkArg(1, readHistoryType, "string", "nil")
checkArg(2, prefix, "string", "nil") checkArg(2, prefix, "string", "nil")
checkArg(3, defaultText, "string", "nil") checkArg(3, defaultText, "string", "nil")
local curtext = defaultText or "" checkArg(4, maxChars, "number", "nil")
local prefix = prefix or "" maxChars = maxChars or math.huge
local textCursorPos = unicode.wlen(curtext) + 1 -- Position within the text (1-based)
local RHIndex local text = defaultText or ""
local historyIdx
if readHistoryType then if readHistoryType then
if not termlib.readHistory[readHistoryType] then if not termlib.readHistory[readHistoryType] then
termlib.readHistory[readHistoryType] = {curtext} termlib.readHistory[readHistoryType] = {text}
elseif termlib.readHistory[readHistoryType][#termlib.readHistory[readHistoryType]] ~= "" then elseif termlib.readHistory[readHistoryType][#termlib.readHistory[readHistoryType] ] ~= "" then
table.insert(termlib.readHistory[readHistoryType], curtext) table.insert(termlib.readHistory[readHistoryType], text)
end end
RHIndex = #termlib.readHistory[readHistoryType] historyIdx = #termlib.readHistory[readHistoryType]
end end
local cursorPosX, cursorPosY = termlib.cursorPosX, termlib.cursorPosY local cur = unicode.len(text)+1
if prefix then termlib.write(prefix) end
-- Track maximum text length to ensure proper clearing across wrapped lines local startX, startY = termlib.cursorPosX, termlib.cursorPosY
local maxTextLength = unicode.wlen(prefix .. curtext) local fg, bg = gpu.getForeground(), gpu.getBackground()
local cursorBlink = true
-- Function to calculate how many lines text will occupy local function get(idx)
local function calculateLines(text) idx=startX+idx-1
local totalWidth = unicode.wlen(prefix .. text) return gpu.get(idx%width,startY+(idx//width))
local width = gpu.getResolution()
return math.ceil(totalWidth / width)
end end
local function checkScroll(y)
-- Track maximum lines used for i=1,y-height do
local maxLinesUsed = calculateLines(curtext) scrollDown()
startY=startY-1
-- Function to redraw the input line with cursor end
local function redrawLine() return math.min(y,height)
local startX, startY = cursorPosX, cursorPosY end
local function set(idx,chr,rev)
-- Calculate current and max lines needed if chr==nil or chr=="" then return end
local currentLines = calculateLines(curtext) if rev then
local linesToClear = math.max(maxLinesUsed, currentLines) gpu.setForeground(bg)
gpu.setBackground(fg)
-- Clear all potentially used lines else
for i = 0, linesToClear - 1 do gpu.setForeground(fg)
termlib.cursorPosX, termlib.cursorPosY = 1, startY + i gpu.setBackground(bg)
if startY + i <= height then end
local width = gpu.getResolution() idx=startX+idx-1
termlib.write(string.rep(" ", width)) local setX, setY = (idx-1)%width+1, startY+((idx-1)//width+1)-1
setY = checkScroll(setY)
gpu.set(setX,setY,unicode.sub(chr,1,width-setX+1))
for i=1,math.ceil((#chr+setX-1)/width)+1 do
gpu.set(1,setY+i,unicode.sub(chr,2-setX+i*width,width+i*width-setX))
setY = checkScroll(setY)
end end
end end
local function strDef(a,b)
-- Reset cursor to start position if #a==0 then return b end
termlib.cursorPosX, termlib.cursorPosY = startX, startY return a
-- Update tracking variables
maxTextLength = math.max(maxTextLength, unicode.wlen(prefix .. curtext))
maxLinesUsed = math.max(maxLinesUsed, currentLines)
-- Draw text with cursor positioned correctly
local beforeCursor = curtext:sub(1, utf8.offset(curtext, textCursorPos) - 1 or 0)
local afterCursor = curtext:sub(utf8.offset(curtext, textCursorPos) or (#curtext + 1))
termlib.write(prefix .. beforeCursor)
termlib.write("\27[30m\27[107m" .. (afterCursor:sub(1, 1) ~= "" and afterCursor:sub(1, 1) or " ") .. "\27[0m") -- HUGE SHOUTOUT TO WAH FOR MAKING THE ESCAPE CODES WORK YEAAAAAAA
termlib.write(afterCursor:sub(2))
end end
local function curPos(cur)
-- component.ocelot.log(table.concat({cur,unicode.wlen(unicode.sub(text,1,cur)),unicode.len(text)}," "))
return unicode.wlen(unicode.sub(text,1,cur-1))+1
end
local function add(chr)
if type(chr)~="string" or #chr==0 then return end
if unicode.len(text)>=maxChars then return end
if maxChars<math.huge then
chr=unicode.sub(chr,1,maxChars-unicode.len(text))
end
text=unicode.sub(text,1,cur-1)..chr..unicode.sub(text,cur)
set(curPos(cur),chr,false)
cur=math.min(cur+unicode.len(chr),maxChars+1)
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
cursorBlink = true
set(curPos(cur+1),unicode.sub(text,cur+1),false)
end
local function moveCur(dir)
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),false)
cur=math.max(math.min(cur+dir,unicode.len(text)+1),1)
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
cursorBlink = true
end
local function isLine(chr)
return chr=="\n" or chr=="\r"
end
--[[ gpu.set(startX,startY,unicode.sub(text,1,width-startX))
for i=1,(#text+startX)//width-1 do
gpu.set(startX,startY+i,unicode.sub(text,1+i*width,width-startX+i*width))
end ]]
set(1,text,false)
set(curPos(cur)," ",true)
redrawLine() local function reprint(new)
local cursorWhite = true set(1,new..string.rep(" ",unicode.wlen(text)-unicode.wlen(new)+1),false)
cur=unicode.len(new)+1
text=new
set(curPos(cur)," ",true)
end
local function updateHistory()
if not readHistoryType then return end
termlib.readHistory[readHistoryType][historyIdx]=text
end
while true do while true do
local args = {event.pull("key_down", "clipboard", 0.5)} local args = {event.pull("key_down", "clipboard", 0.5)}
if args and args[1] == "key_down" and args[4] then
if args[1] == "key_down" and args[4] then local key = keyboard.keys[args[4]]
cursorWhite = true if key=="up" and readHistoryType then
local keycode = args[4] historyIdx=math.max(historyIdx-1,1)
local key = keyboard.keys[keycode] reprint(termlib.readHistory[readHistoryType][historyIdx])
elseif key=="down" and readHistoryType then
-- Handle arrow keys historyIdx=math.min(historyIdx+1,#termlib.readHistory[readHistoryType])
if key == "up" and readHistoryType then reprint(termlib.readHistory[readHistoryType][historyIdx])
RHIndex = RHIndex - 1 elseif key=="left" then
if RHIndex <= 0 then moveCur(-1)
RHIndex = 1 elseif key=="right" then
moveCur(1)
elseif key=="home" then
moveCur(-math.huge)
elseif key=="end" then
moveCur(math.huge)
elseif key=="back" and cur>1 then
text=unicode.sub(text,1,cur-2)..unicode.sub(text,cur)
cur=cur-1
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
cursorBlink = true
set(curPos(cur)+1,unicode.sub(text,cur+1).." ",false)
updateHistory()
elseif key=="delete" then
text = unicode.sub(text,1,cur-1)..unicode.sub(text,cur+1)
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
cursorBlink = true
if cur<=unicode.len(text) then
set(curPos(cur+1),unicode.sub(text,cur+1).." ",false)
end end
curtext = termlib.readHistory[readHistoryType][RHIndex] updateHistory()
textCursorPos = unicode.wlen(curtext) + 1 elseif key=="enter" then
redrawLine() set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),false)
break
elseif key == "down" and readHistoryType then elseif not (args[3]<32 or (args[3]>0x7F and args[3]<=0x9F)) then
RHIndex = RHIndex + 1 add(unicode.char(args[3]) or " ")
if RHIndex > #termlib.readHistory[readHistoryType] then updateHistory()
RHIndex = #termlib.readHistory[readHistoryType]
end end
curtext = termlib.readHistory[readHistoryType][RHIndex] elseif args and args[1]=="clipboard" then
textCursorPos = unicode.wlen(curtext) + 1 local clip = args[3]
redrawLine() if not args[3] then goto continue end
while isLine(unicode.sub(clip,1,1)) do clip=unicode.sub(clip,2) end
elseif key == "left" then while isLine(unicode.sub(clip,-1)) do clip=unicode.sub(clip,1,-2) end
-- Move cursor left add(clip)
if textCursorPos > 1 then updateHistory()
textCursorPos = textCursorPos - 1 else
redrawLine() cursorBlink=not cursorBlink
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),cursorBlink)
end
::continue::
end end
elseif key == "right" then
-- Move cursor right
if textCursorPos <= unicode.wlen(curtext) then
textCursorPos = textCursorPos + 1
redrawLine()
end
elseif key == "home" then
-- Move to beginning of line
textCursorPos = 1
redrawLine()
elseif key == "end" then
-- Move to end of line
textCursorPos = unicode.wlen(curtext) + 1
redrawLine()
elseif key == "back" then
-- Backspace - delete character before cursor
if textCursorPos > 1 then
local beforeCursor = curtext:sub(1, utf8.offset(curtext, textCursorPos - 1) - 1 or 0)
local afterCursor = curtext:sub(utf8.offset(curtext, textCursorPos) or (#curtext + 1))
curtext = beforeCursor .. afterCursor
textCursorPos = textCursorPos - 1
if readHistoryType then if readHistoryType then
termlib.readHistory[readHistoryType][RHIndex] = curtext if termlib.readHistory[readHistoryType][#termlib.readHistory[readHistoryType]]=="" then
table.remove(termlib.readHistory[readHistoryType],#termlib.readHistory[readHistoryType])
end end
redrawLine() if historyIdx<#termlib.readHistory[readHistoryType] then
table.remove(termlib.readHistory[readHistoryType],historyIdx)
table.insert(termlib.readHistory[readHistoryType],text)
end end
elseif key == "delete" then
-- Delete - delete character at cursor
if textCursorPos <= unicode.wlen(curtext) then
local beforeCursor = curtext:sub(1, utf8.offset(curtext, textCursorPos) - 1 or 0)
local afterCursor = curtext:sub(utf8.offset(curtext, textCursorPos + 1) or (#curtext + 1))
curtext = beforeCursor .. afterCursor
if readHistoryType then
termlib.readHistory[readHistoryType][RHIndex] = curtext
end
redrawLine()
end
elseif key == "enter" then
termlib.cursorPosX, termlib.cursorPosY = cursorPosX, cursorPosY
print(prefix .. curtext .. " ")
if readHistoryType then
while #termlib.readHistory[readHistoryType] > 50 do while #termlib.readHistory[readHistoryType] > 50 do
table.remove(termlib.readHistory[readHistoryType], 1) table.remove(termlib.readHistory[readHistoryType], 1)
end end
end end
return curtext
termlib.cursorPosX=1
termlib.cursorPosY=termlib.cursorPosY+math.ceil((unicode.wlen(text)+startX-1)/width)
if termlib.cursorPosY>height then scrollDown() end
elseif args[3] >= 32 and args[3] <= 126 then return text
-- Insert character at cursor position
local char = unicode.char(args[3]) or ""
local beforeCursor = curtext:sub(1, utf8.offset(curtext, textCursorPos) - 1 or 0)
local afterCursor = curtext:sub(utf8.offset(curtext, textCursorPos) or (#curtext + 1))
curtext = beforeCursor .. char .. afterCursor
textCursorPos = textCursorPos + 1
if readHistoryType then
termlib.readHistory[readHistoryType][RHIndex] = curtext
end
redrawLine()
end
elseif args[1] == "clipboard" then
-- Handle clipboard paste
local text = args[3] or ""
local beforeCursor = curtext:sub(1, utf8.offset(curtext, textCursorPos) - 1 or 0)
local afterCursor = curtext:sub(utf8.offset(curtext, textCursorPos) or (#curtext + 1))
curtext = beforeCursor .. text .. afterCursor
textCursorPos = textCursorPos + #text
if readHistoryType then
termlib.readHistory[readHistoryType][RHIndex] = curtext
end
redrawLine()
else
-- Cursor blink timing
cursorWhite = not cursorWhite
if cursorWhite then
redrawLine()
else
-- Show cursor as normal character or space
termlib.cursorPosX, termlib.cursorPosY = cursorPosX, cursorPosY
local beforeCursor = curtext:sub(1, utf8.offset(curtext, textCursorPos) - 1 or 0)
local afterCursor = curtext:sub(utf8.offset(curtext, textCursorPos) or (#curtext + 1))
termlib.write(prefix .. beforeCursor)
termlib.write(afterCursor:sub(1, 1) ~= "" and afterCursor:sub(1, 1) or " ")
termlib.write(afterCursor:sub(2))
end
end
end
end end
+79 -51
View File
@@ -2,9 +2,11 @@ local loadfile = ... -- raw loadfile from boot.lua
local component, computer local component, computer
if loadfile then if loadfile then
unicode = loadfile("/halyde/lib/unicode.lua")(loadfile)
component = loadfile("/halyde/lib/component.lua")(loadfile) component = loadfile("/halyde/lib/component.lua")(loadfile)
computer = _G.computer computer = _G.computer
elseif import then elseif import then
unicode = import("unicode")
component = import("component") component = import("component")
computer = import("computer") computer = import("computer")
end end
@@ -44,7 +46,10 @@ function filesystem.absolutePath(path) -- returns the address and absolute path
checkArg(1, path, "string") checkArg(1, path, "string")
path = filesystem.canonical(path) path = filesystem.canonical(path)
local address = nil local address = nil
if path:find("^/mnt/...") then if path:find("^/tmp") then
address = computer.tmpAddress()
path = path:sub(5)
elseif path:find("^/mnt/...") then
address = component.get(path:sub(6,8)) address = component.get(path:sub(6,8))
if not address then if not address then
address = computer.getBootAddress() address = computer.getBootAddress()
@@ -69,48 +74,36 @@ function filesystem.exists(path) -- check if path exists
return component.invoke(address, "exists", absPath) return component.invoke(address, "exists", absPath)
end end
local function readUniChar(readByte) local function readBytes(self,n)
local function inRange(min,max,...) n = n or 1
for _,v in ipairs({...}) do if n==1 then
if not (v and v>=min and v<max) then return false end local byte = self:read(1)
if byte==nil then return nil end
return string.byte(byte)
end end
return true local bytes, res = {string.byte(self:read(n),1,n)}, 0
for i=1,#bytes do
res = (res<<8)&0xFFFFFFFF | bytes[i]
end end
local function readByte0() return readByte() or 0 end return res
end
local byte = readByte() local function readUnicodeChar(self)
return unicode.readChar(function()
return self:readBytes(1)
end)
end
if byte < 0x80 then local function iterateBytes(self)
-- ASCII character (0xxxxxxx) return function()
local byte = readBytes(self,1)
if byte==nil then self:close() end
return byte return byte
elseif byte < 0xC0 then
-- Continuation byte (10xxxxxx), invalid at start position
return nil
elseif byte < 0xE0 then
-- 2-byte sequence (110xxxxx 10xxxxxx)
local byte2 = readByte0()
if inRange(0x80,0xC0,byte2) then
local code_point = ((byte & 0x1F) << 6) | (byte2 & 0x3F)
return code_point
end
elseif byte < 0xF0 then
-- 3-byte sequence (1110xxxx 10xxxxxx 10xxxxxx)
local byte2, byte3 = readByte0(), readByte0()
if inRange(0x80,0xC0,byte2,byte3)then
local code_point = ((byte & 0x0F) << 12) | ((byte2 & 0x3F) << 6) | (byte3 & 0x3F)
return code_point
end
elseif byte < 0xF8 then
-- 4-byte sequence (11110xxx 10xxxxxx 10xxxxxx 10xxxxxx)
local byte2, byte3, byte4 = readByte0(), readByte0(), readByte0()
if inRange(0x80,0xC0,byte2,byte3,byte4) then
local code_point = ((byte & 0x07) << 18) | ((byte2 & 0x3F) << 12) | ((byte3 & 0x3F) << 6) | (byte4 & 0x3F)
return code_point
end
end end
end
-- Invalid UTF-8 byte sequence local function iterateUnicodeChars(self)
return nil return unicode.iterate(iterateBytes(self))
end end
function filesystem.open(path, mode, buffered) -- opens a file and returns its handle function filesystem.open(path, mode, buffered) -- opens a file and returns its handle
@@ -151,7 +144,6 @@ function filesystem.open(path, mode, buffered) -- opens a file and returns its h
if buffered then if buffered then
local limit = string.len(content)+1 local limit = string.len(content)+1
local out = nil local out = nil
-- error("amount "..amount..", limit "..limit)
if readcursor<limit then if readcursor<limit then
if amount==math.huge then if amount==math.huge then
out = string.sub(content,math.min(readcursor,limit)) out = string.sub(content,math.min(readcursor,limit))
@@ -168,20 +160,10 @@ function filesystem.open(path, mode, buffered) -- opens a file and returns its h
return component.invoke(self.address, "read", self.handle, amount) return component.invoke(self.address, "read", self.handle, amount)
end end
end end
function properHandle.readBytes(self,n) properHandle.readBytes = readBytes
n = n or 1 properHandle.readUnicodeChar = readUnicodeChar
if n==1 then return string.byte(self:read(1)) end properHandle.iterateBytes = iterateBytes
local bytes, res = {string.byte(self:read(n),1,n)}, 0 properHandle.iterateUnicodeChars = iterateUnicodeChars
for i=1,#bytes do
res = (res<<8)&0xFFFFFFFF | bytes[i]
end
return res
end
function properHandle.readUnicodeChar(self)
return unicode.char(readUniChar(function()
return self:readBytes(1)
end))
end
function properHandle.write(self, data) function properHandle.write(self, data)
checkArg(2, data, "string") checkArg(2, data, "string")
return component.invoke(self.address, "write", self.handle, data) return component.invoke(self.address, "write", self.handle, data)
@@ -202,9 +184,12 @@ function filesystem.list(path)
if path == "/mnt" then if path == "/mnt" then
-- list drives -- list drives
local returnTable = {} local returnTable = {}
local tmpAddress = computer.tmpAddress()
for address, _ in component.list("filesystem") do for address, _ in component.list("filesystem") do
if address~=tmpAddress then
table.insert(returnTable, address:sub(1, 3) .. "/") table.insert(returnTable, address:sub(1, 3) .. "/")
end end
end
return returnTable return returnTable
else else
local address, absPath = filesystem.absolutePath(path) local address, absPath = filesystem.absolutePath(path)
@@ -295,4 +280,47 @@ function filesystem.makeDirectory(path)
return component.invoke(address, "makeDirectory", absPath) return component.invoke(address, "makeDirectory", absPath)
end end
local function randomHex(length)
local chars = "0123456789abcdef"
local result = ""
for i = 1, length do
local index = math.random(1, #chars)
result = result .. string.sub(chars, index, index)
end
return result
end
function filesystem.makeReadStream(content)
local properHandle = {}
local readcursor = 1
function properHandle.read(self, amount)
checkArg(2, amount, "number")
local limit = string.len(content)+1
local out = nil
if readcursor<limit then
if amount==math.huge then
out = string.sub(content,math.min(readcursor,limit))
else
out = string.sub(content,math.min(readcursor,limit),math.min(readcursor+amount-1,limit))
end
end
readcursor=readcursor+amount
if out=="" then
return nil
end
return out
end
properHandle.readBytes = readBytes
properHandle.readUnicodeChar = readUnicodeChar
properHandle.iterateBytes = iterateBytes
properHandle.iterateUnicodeChars = iterateUnicodeChars
function properHandle.write()
return nil
end
function properHandle.close()
content=nil
end
return properHandle
end
return(filesystem) return(filesystem)
+100
View File
@@ -0,0 +1,100 @@
local unicodeLib
local LLunicode
if table.copy then
unicodeLib = table.copy(unicode)
LLunicode = table.copy(unicode)
else
unicodeLib = {}
LLunicode = unicode
end
function unicodeLib.readCodePoint(readByte)
local function inRange(min,max,...)
for _,v in ipairs({...}) do
if not (v and v>=min and v<max) then return false end
end
return true
end
local byte = readByte()
if byte==nil then return end
if byte < 0x80 then
-- ASCII character (0xxxxxxx)
return byte
elseif byte < 0xC0 then
-- Continuation byte (10xxxxxx), invalid at start position
return nil
elseif byte < 0xE0 then
-- 2-byte sequence (110xxxxx 10xxxxxx)
local byte2 = readByte()
if byte2==nil then return nil end
if inRange(0x80,0xC0,byte2) then
local code_point = ((byte & 0x1F) << 6) | (byte2 & 0x3F)
return code_point
end
elseif byte < 0xF0 then
-- 3-byte sequence (1110xxxx 10xxxxxx 10xxxxxx)
local byte2, byte3 = readByte(), readByte()
if byte2==nil and byte3==nil then return nil end
if inRange(0x80,0xC0,byte2,byte3)then
local code_point = ((byte & 0x0F) << 12) | ((byte2 & 0x3F) << 6) | (byte3 & 0x3F)
return code_point
end
elseif byte < 0xF8 then
-- 4-byte sequence (11110xxx 10xxxxxx 10xxxxxx 10xxxxxx)
local byte2, byte3, byte4 = readByte(), readByte(), readByte()
if byte2==nil and byte3==nil and byte4==nil then return nil end
if inRange(0x80,0xC0,byte2,byte3,byte4) then
local code_point = ((byte & 0x07) << 18) | ((byte2 & 0x3F) << 12) | ((byte3 & 0x3F) << 6) | (byte4 & 0x3F)
return code_point
end
end
-- Invalid UTF-8 byte sequence
return nil
end
function unicodeLib.readChar(readByte)
checkArg(1,readByte,"function")
return LLunicode.char(unicodeLib.readCodePoint(readByte))
end
function unicodeLib.codepoint(chr)
checkArg(1,readByte,"string")
local ptr = 1
return readUniChar(function()
local byte = chr:byte(ptr)
ptr=ptr+1
return byte
end),ptr-1
end
function unicodeLib.iterate(readByte)
checkArg(1,readByte,"string","function")
if type(readByte)=="string" then
local str,ptr = readByte,0
readByte = function()
ptr=ptr+1
return str:byte(ptr)
end
end
return function()
local point = unicodeLib.readCodePoint(readByte)
if point==nil then return nil end
return LLunicode.char(point),point
end
end
unicodeLib.char = LLunicode.char
unicodeLib.charWidth = LLunicode.charWidth
unicodeLib.isWide = LLunicode.isWide
unicodeLib.len = LLunicode.len
unicodeLib.lower = LLunicode.lower
unicodeLib.reverse = LLunicode.reverse
unicodeLib.sub = LLunicode.sub
unicodeLib.upper = LLunicode.upper
unicodeLib.wlen = LLunicode.wlen
unicodeLib.wtrunc = LLunicode.wtrunc
return unicodeLib