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:
+2
-2
@@ -1,7 +1,7 @@
|
||||
local agcfg = {
|
||||
["halyde"] = {
|
||||
["maindir"] = "",
|
||||
["version"] = "1.15.0",
|
||||
["version"] = "2.0.0",
|
||||
["description"] = "A universal, customizable and feature-packed operating system for OpenComputers.",
|
||||
["directories"] = {
|
||||
"halyde/apps",
|
||||
@@ -82,7 +82,7 @@ local agcfg = {
|
||||
},
|
||||
["edit"] = {
|
||||
["maindir"] = "",
|
||||
["version"] = "1.2.0",
|
||||
["version"] = "1.2.1",
|
||||
["description"] = "The default text editor for Halyde.",
|
||||
["files"] = {
|
||||
"halyde/apps/edit.lua",
|
||||
|
||||
@@ -2,6 +2,7 @@ local file = ...
|
||||
local fs = import("filesystem")
|
||||
local event = import("event")
|
||||
local component = import("component")
|
||||
local unicode = import("unicode")
|
||||
local gpu = component.gpu
|
||||
local width, height = gpu.getResolution()
|
||||
local scrollPosX, scrollPosY = 1, 1
|
||||
|
||||
@@ -2,6 +2,7 @@ local args = {...}
|
||||
local target = args[1]
|
||||
args = nil
|
||||
local fs = import("filesystem")
|
||||
local unicode = import("unicode")
|
||||
local maxLength = 0
|
||||
local margin = 2 -- minimum space between filename and size
|
||||
local dirTable = {}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
local loadfile = ...
|
||||
local filesystem = loadfile("/halyde/lib/filesystem.lua")(loadfile)
|
||||
|
||||
_G._OSVERSION = "Halyde 1.15.0"
|
||||
_G._OSVERSION = "Halyde 2.0.0"
|
||||
_G._OSLOGO = ""
|
||||
local handle, tmpdata = filesystem.open("/halyde/config/oslogo.ans", "r"), nil
|
||||
repeat
|
||||
|
||||
+145
-170
@@ -1,3 +1,4 @@
|
||||
local unicode = import("unicode")
|
||||
local event = import("event")
|
||||
--local keyboard = import("keyboard")
|
||||
|
||||
@@ -86,8 +87,8 @@ function termlib.write(text, textWrap)
|
||||
if text:find("\a") then
|
||||
computer.beep()
|
||||
end
|
||||
text = "\27[0m" .. text:gsub("\t", " ")
|
||||
text = tostring(text)
|
||||
text = "\27[0m" .. text:gsub("\t", " ")
|
||||
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.
|
||||
-- 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
|
||||
end
|
||||
|
||||
-- god i hope this silly claude code works first try
|
||||
-- Fluxdrive caught vibe coding???
|
||||
function _G.read(readHistoryType, prefix, defaultText)
|
||||
function _G.read(readHistoryType, prefix, defaultText, maxChars)
|
||||
checkArg(1, readHistoryType, "string", "nil")
|
||||
checkArg(2, prefix, "string", "nil")
|
||||
checkArg(3, defaultText, "string", "nil")
|
||||
local curtext = defaultText or ""
|
||||
local prefix = prefix or ""
|
||||
local textCursorPos = unicode.wlen(curtext) + 1 -- Position within the text (1-based)
|
||||
checkArg(4, maxChars, "number", "nil")
|
||||
maxChars = maxChars or math.huge
|
||||
|
||||
local RHIndex
|
||||
local text = defaultText or ""
|
||||
|
||||
local historyIdx
|
||||
if readHistoryType then
|
||||
if not termlib.readHistory[readHistoryType] then
|
||||
termlib.readHistory[readHistoryType] = {curtext}
|
||||
elseif termlib.readHistory[readHistoryType][#termlib.readHistory[readHistoryType]] ~= "" then
|
||||
table.insert(termlib.readHistory[readHistoryType], curtext)
|
||||
termlib.readHistory[readHistoryType] = {text}
|
||||
elseif termlib.readHistory[readHistoryType][#termlib.readHistory[readHistoryType] ] ~= "" then
|
||||
table.insert(termlib.readHistory[readHistoryType], text)
|
||||
end
|
||||
RHIndex = #termlib.readHistory[readHistoryType]
|
||||
historyIdx = #termlib.readHistory[readHistoryType]
|
||||
end
|
||||
|
||||
local cursorPosX, cursorPosY = termlib.cursorPosX, termlib.cursorPosY
|
||||
|
||||
-- Track maximum text length to ensure proper clearing across wrapped lines
|
||||
local maxTextLength = unicode.wlen(prefix .. curtext)
|
||||
|
||||
-- Function to calculate how many lines text will occupy
|
||||
local function calculateLines(text)
|
||||
local totalWidth = unicode.wlen(prefix .. text)
|
||||
local width = gpu.getResolution()
|
||||
return math.ceil(totalWidth / width)
|
||||
local cur = unicode.len(text)+1
|
||||
if prefix then termlib.write(prefix) end
|
||||
local startX, startY = termlib.cursorPosX, termlib.cursorPosY
|
||||
local fg, bg = gpu.getForeground(), gpu.getBackground()
|
||||
local cursorBlink = true
|
||||
local function get(idx)
|
||||
idx=startX+idx-1
|
||||
return gpu.get(idx%width,startY+(idx//width))
|
||||
end
|
||||
|
||||
-- Track maximum lines used
|
||||
local maxLinesUsed = calculateLines(curtext)
|
||||
|
||||
-- Function to redraw the input line with cursor
|
||||
local function redrawLine()
|
||||
local startX, startY = cursorPosX, cursorPosY
|
||||
|
||||
-- Calculate current and max lines needed
|
||||
local currentLines = calculateLines(curtext)
|
||||
local linesToClear = math.max(maxLinesUsed, currentLines)
|
||||
|
||||
-- Clear all potentially used lines
|
||||
for i = 0, linesToClear - 1 do
|
||||
termlib.cursorPosX, termlib.cursorPosY = 1, startY + i
|
||||
if startY + i <= height then
|
||||
local width = gpu.getResolution()
|
||||
termlib.write(string.rep(" ", width))
|
||||
local function checkScroll(y)
|
||||
for i=1,y-height do
|
||||
scrollDown()
|
||||
startY=startY-1
|
||||
end
|
||||
return math.min(y,height)
|
||||
end
|
||||
local function set(idx,chr,rev)
|
||||
if chr==nil or chr=="" then return end
|
||||
if rev then
|
||||
gpu.setForeground(bg)
|
||||
gpu.setBackground(fg)
|
||||
else
|
||||
gpu.setForeground(fg)
|
||||
gpu.setBackground(bg)
|
||||
end
|
||||
idx=startX+idx-1
|
||||
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
|
||||
|
||||
-- Reset cursor to start position
|
||||
termlib.cursorPosX, termlib.cursorPosY = startX, startY
|
||||
|
||||
-- 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))
|
||||
local function strDef(a,b)
|
||||
if #a==0 then return b end
|
||||
return a
|
||||
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 cursorWhite = true
|
||||
local function reprint(new)
|
||||
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
|
||||
local args = {event.pull("key_down", "clipboard", 0.5)}
|
||||
|
||||
if args[1] == "key_down" and args[4] then
|
||||
cursorWhite = true
|
||||
local keycode = args[4]
|
||||
local key = keyboard.keys[keycode]
|
||||
|
||||
-- Handle arrow keys
|
||||
if key == "up" and readHistoryType then
|
||||
RHIndex = RHIndex - 1
|
||||
if RHIndex <= 0 then
|
||||
RHIndex = 1
|
||||
if args and args[1] == "key_down" and args[4] then
|
||||
local key = keyboard.keys[args[4]]
|
||||
if key=="up" and readHistoryType then
|
||||
historyIdx=math.max(historyIdx-1,1)
|
||||
reprint(termlib.readHistory[readHistoryType][historyIdx])
|
||||
elseif key=="down" and readHistoryType then
|
||||
historyIdx=math.min(historyIdx+1,#termlib.readHistory[readHistoryType])
|
||||
reprint(termlib.readHistory[readHistoryType][historyIdx])
|
||||
elseif key=="left" then
|
||||
moveCur(-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
|
||||
curtext = termlib.readHistory[readHistoryType][RHIndex]
|
||||
textCursorPos = unicode.wlen(curtext) + 1
|
||||
redrawLine()
|
||||
|
||||
elseif key == "down" and readHistoryType then
|
||||
RHIndex = RHIndex + 1
|
||||
if RHIndex > #termlib.readHistory[readHistoryType] then
|
||||
RHIndex = #termlib.readHistory[readHistoryType]
|
||||
updateHistory()
|
||||
elseif key=="enter" then
|
||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),false)
|
||||
break
|
||||
elseif not (args[3]<32 or (args[3]>0x7F and args[3]<=0x9F)) then
|
||||
add(unicode.char(args[3]) or " ")
|
||||
updateHistory()
|
||||
end
|
||||
curtext = termlib.readHistory[readHistoryType][RHIndex]
|
||||
textCursorPos = unicode.wlen(curtext) + 1
|
||||
redrawLine()
|
||||
|
||||
elseif key == "left" then
|
||||
-- Move cursor left
|
||||
if textCursorPos > 1 then
|
||||
textCursorPos = textCursorPos - 1
|
||||
redrawLine()
|
||||
elseif args and args[1]=="clipboard" then
|
||||
local clip = args[3]
|
||||
if not args[3] then goto continue end
|
||||
while isLine(unicode.sub(clip,1,1)) do clip=unicode.sub(clip,2) end
|
||||
while isLine(unicode.sub(clip,-1)) do clip=unicode.sub(clip,1,-2) end
|
||||
add(clip)
|
||||
updateHistory()
|
||||
else
|
||||
cursorBlink=not cursorBlink
|
||||
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),cursorBlink)
|
||||
end
|
||||
::continue::
|
||||
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
|
||||
termlib.readHistory[readHistoryType][RHIndex] = curtext
|
||||
if termlib.readHistory[readHistoryType][#termlib.readHistory[readHistoryType]]=="" then
|
||||
table.remove(termlib.readHistory[readHistoryType],#termlib.readHistory[readHistoryType])
|
||||
end
|
||||
redrawLine()
|
||||
if historyIdx<#termlib.readHistory[readHistoryType] then
|
||||
table.remove(termlib.readHistory[readHistoryType],historyIdx)
|
||||
table.insert(termlib.readHistory[readHistoryType],text)
|
||||
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
|
||||
table.remove(termlib.readHistory[readHistoryType], 1)
|
||||
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
|
||||
-- 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
|
||||
return text
|
||||
end
|
||||
|
||||
+79
-51
@@ -2,9 +2,11 @@ local loadfile = ... -- raw loadfile from boot.lua
|
||||
local component, computer
|
||||
|
||||
if loadfile then
|
||||
unicode = loadfile("/halyde/lib/unicode.lua")(loadfile)
|
||||
component = loadfile("/halyde/lib/component.lua")(loadfile)
|
||||
computer = _G.computer
|
||||
elseif import then
|
||||
unicode = import("unicode")
|
||||
component = import("component")
|
||||
computer = import("computer")
|
||||
end
|
||||
@@ -44,7 +46,10 @@ function filesystem.absolutePath(path) -- returns the address and absolute path
|
||||
checkArg(1, path, "string")
|
||||
path = filesystem.canonical(path)
|
||||
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))
|
||||
if not address then
|
||||
address = computer.getBootAddress()
|
||||
@@ -69,48 +74,36 @@ function filesystem.exists(path) -- check if path exists
|
||||
return component.invoke(address, "exists", absPath)
|
||||
end
|
||||
|
||||
local function readUniChar(readByte)
|
||||
local function inRange(min,max,...)
|
||||
for _,v in ipairs({...}) do
|
||||
if not (v and v>=min and v<max) then return false end
|
||||
local function readBytes(self,n)
|
||||
n = n or 1
|
||||
if n==1 then
|
||||
local byte = self:read(1)
|
||||
if byte==nil then return nil end
|
||||
return string.byte(byte)
|
||||
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
|
||||
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
|
||||
-- ASCII character (0xxxxxxx)
|
||||
local function iterateBytes(self)
|
||||
return function()
|
||||
local byte = readBytes(self,1)
|
||||
if byte==nil then self:close() end
|
||||
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
|
||||
|
||||
-- Invalid UTF-8 byte sequence
|
||||
return nil
|
||||
local function iterateUnicodeChars(self)
|
||||
return unicode.iterate(iterateBytes(self))
|
||||
end
|
||||
|
||||
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
|
||||
local limit = string.len(content)+1
|
||||
local out = nil
|
||||
-- error("amount "..amount..", limit "..limit)
|
||||
if readcursor<limit then
|
||||
if amount==math.huge then
|
||||
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)
|
||||
end
|
||||
end
|
||||
function properHandle.readBytes(self,n)
|
||||
n = n or 1
|
||||
if n==1 then return string.byte(self:read(1)) end
|
||||
local bytes, res = {string.byte(self:read(n),1,n)}, 0
|
||||
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
|
||||
properHandle.readBytes = readBytes
|
||||
properHandle.readUnicodeChar = readUnicodeChar
|
||||
properHandle.iterateBytes = iterateBytes
|
||||
properHandle.iterateUnicodeChars = iterateUnicodeChars
|
||||
function properHandle.write(self, data)
|
||||
checkArg(2, data, "string")
|
||||
return component.invoke(self.address, "write", self.handle, data)
|
||||
@@ -202,9 +184,12 @@ function filesystem.list(path)
|
||||
if path == "/mnt" then
|
||||
-- list drives
|
||||
local returnTable = {}
|
||||
local tmpAddress = computer.tmpAddress()
|
||||
for address, _ in component.list("filesystem") do
|
||||
if address~=tmpAddress then
|
||||
table.insert(returnTable, address:sub(1, 3) .. "/")
|
||||
end
|
||||
end
|
||||
return returnTable
|
||||
else
|
||||
local address, absPath = filesystem.absolutePath(path)
|
||||
@@ -295,4 +280,47 @@ function filesystem.makeDirectory(path)
|
||||
return component.invoke(address, "makeDirectory", absPath)
|
||||
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)
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user