formating better for spm packages
This commit is contained in:
@@ -827,6 +827,12 @@ builtinCmds.df = function(...)
|
||||
end
|
||||
end
|
||||
|
||||
builtinCmds.ops = function()
|
||||
for i,v in pairs(math) do
|
||||
print(i)
|
||||
end
|
||||
end
|
||||
|
||||
local function listDir(dir, prefix)
|
||||
local ok, entries = pcall(syscall.listdir, dir)
|
||||
if not ok or not entries then return {} end
|
||||
@@ -981,6 +987,7 @@ local function doTabComplete(input, cursorPos)
|
||||
end
|
||||
end
|
||||
|
||||
local lastghost=""
|
||||
local function getUserInput()
|
||||
syscall.devctl(1,"sfgc",0x00FF00)
|
||||
syscall.write(1, userhost)
|
||||
@@ -1147,7 +1154,7 @@ end
|
||||
|
||||
local function runCommand(command)
|
||||
do
|
||||
local func = load("return "..command, "@equation", "t", {})
|
||||
local func = load("return "..command, "@equation", "t", math)
|
||||
if func then
|
||||
local ok, result = pcall(func)
|
||||
if ok and type(result)=="number" then print(result); return end
|
||||
@@ -95,6 +95,8 @@ local screenSizeStr = syscall.devctl(1, "size")
|
||||
local sizeX = tonumber(screenSizeStr:match("^(%d+)")) or 80
|
||||
|
||||
local list = fs.list(dir)
|
||||
list[#list+1] = "."
|
||||
list[#list+1] = ".."
|
||||
if not cloptions.a then
|
||||
for i = #list, 1, -1 do
|
||||
if list[i]:sub(1, 1) == "." then table.remove(list, i) end
|
||||
425
Src/hysh/data/bin/sed
Normal file
425
Src/hysh/data/bin/sed
Normal file
@@ -0,0 +1,425 @@
|
||||
--:Minify:--
|
||||
-- Supports: s/pat/repl/[gip], d, p, q, =, addr1[,addr2]cmd
|
||||
-- Addressing: line numbers, $, /regex/
|
||||
-- Flags: -n (silent), -e script, -i (in-place)
|
||||
|
||||
local name = syscall.getTask(syscall.getpid()).name
|
||||
|
||||
local scripts = {}
|
||||
local files = {}
|
||||
local silent = false
|
||||
local inplace = false
|
||||
local args = { ... }
|
||||
local i = 1
|
||||
|
||||
while i <= #args do
|
||||
local a = args[i]
|
||||
if a == "-n" then
|
||||
silent = true
|
||||
elseif a == "-i" then
|
||||
inplace = true
|
||||
elseif a == "-e" then
|
||||
i = i + 1
|
||||
if not args[i] then
|
||||
print(name .. ": option -e requires an argument"); syscall.exit(1); return
|
||||
end
|
||||
table.insert(scripts, args[i])
|
||||
elseif a:sub(1,2) == "-e" then
|
||||
table.insert(scripts, a:sub(3))
|
||||
elseif a == "--help" then
|
||||
print("Usage: " .. name .. " [OPTION]... SCRIPT [FILE...]")
|
||||
print(" " .. name .. " [OPTION]... -e SCRIPT... [FILE...]")
|
||||
print("Stream editor. Reads FILE(s) (or stdin) line by line,")
|
||||
print("applies SCRIPT, and writes results to stdout.")
|
||||
print("")
|
||||
print("Commands:")
|
||||
print(" s/REGEX/REPL/[flags] substitute (flags: g global, i ignore-case, p print)")
|
||||
print(" d delete line (skip to next)")
|
||||
print(" p print current line")
|
||||
print(" q quit")
|
||||
print(" = print current line number")
|
||||
print(" y/src/dst/ transliterate characters")
|
||||
print("")
|
||||
print("Addressing (prefix any command):")
|
||||
print(" N line number N")
|
||||
print(" $ last line")
|
||||
print(" /REGEX/ lines matching regex")
|
||||
print(" N,M line range")
|
||||
print(" N,/REGEX/ from line N until regex match")
|
||||
print("")
|
||||
print("Options:")
|
||||
print(" -n suppress default output")
|
||||
print(" -e SCRIPT add script expression")
|
||||
print(" -i edit file in-place")
|
||||
print(" --help display this help and exit")
|
||||
return
|
||||
elseif a:sub(1,1) == "-" then
|
||||
print(name .. ": unknown option: " .. a)
|
||||
syscall.exit(1); return
|
||||
else
|
||||
if #scripts == 0 then
|
||||
table.insert(scripts, a)
|
||||
else
|
||||
table.insert(files, a)
|
||||
end
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
if #scripts == 0 then
|
||||
print(name .. ": no script specified"); syscall.exit(1); return
|
||||
end
|
||||
|
||||
local script = table.concat(scripts, "\n")
|
||||
|
||||
local function patEscape(s)
|
||||
return s:gsub("([%(%)%.%%%+%-%*%?%[%^%$])", "%%%1")
|
||||
end
|
||||
|
||||
local function sedPatToLua(pat, icase)
|
||||
pat = pat:gsub("\\%(", "("):gsub("\\%)", ")")
|
||||
pat = pat:gsub("\\1", "%%1"):gsub("\\2", "%%2")
|
||||
return pat
|
||||
end
|
||||
|
||||
local function parseDelim(s, pos, delim)
|
||||
local out = {}
|
||||
while pos <= #s do
|
||||
local c = s:sub(pos, pos)
|
||||
if c == "\\" and pos < #s then
|
||||
pos = pos + 1
|
||||
local nc = s:sub(pos, pos)
|
||||
if nc == delim then
|
||||
table.insert(out, delim)
|
||||
elseif nc == "n" then
|
||||
table.insert(out, "\n")
|
||||
else
|
||||
table.insert(out, "\\" .. nc)
|
||||
end
|
||||
elseif c == delim then
|
||||
return table.concat(out), pos + 1
|
||||
else
|
||||
table.insert(out, c)
|
||||
end
|
||||
pos = pos + 1
|
||||
end
|
||||
return table.concat(out), pos
|
||||
end
|
||||
|
||||
local function parseAddr(s, pos)
|
||||
local c = s:sub(pos, pos)
|
||||
if c == "" then return nil, pos end
|
||||
if c:match("%d") then
|
||||
local numstr = s:match("^(%d+)", pos)
|
||||
return { type="line", n=tonumber(numstr) }, pos + #numstr
|
||||
elseif c == "$" then
|
||||
return { type="last" }, pos + 1
|
||||
elseif c == "/" then
|
||||
local pat, npos = parseDelim(s, pos + 1, "/")
|
||||
return { type="regex", pat=pat }, npos
|
||||
end
|
||||
return nil, pos
|
||||
end
|
||||
|
||||
local function parseCommands(src)
|
||||
local cmds = {}
|
||||
local pos = 1
|
||||
local len = #src
|
||||
|
||||
local function skip()
|
||||
while pos <= len and (src:sub(pos,pos) == " " or src:sub(pos,pos) == "\t") do
|
||||
pos = pos + 1
|
||||
end
|
||||
end
|
||||
|
||||
while pos <= len do
|
||||
skip()
|
||||
if pos > len then break end
|
||||
|
||||
local c = src:sub(pos, pos)
|
||||
if c == "\n" or c == ";" then
|
||||
pos = pos + 1
|
||||
elseif c == "#" then
|
||||
while pos <= len and src:sub(pos,pos) ~= "\n" do pos = pos + 1 end
|
||||
else
|
||||
|
||||
local addr1, addr2
|
||||
addr1, pos = parseAddr(src, pos)
|
||||
skip()
|
||||
if addr1 and pos <= len and src:sub(pos,pos) == "," then
|
||||
pos = pos + 1
|
||||
skip()
|
||||
addr2, pos = parseAddr(src, pos)
|
||||
end
|
||||
skip()
|
||||
|
||||
if pos > len then break end
|
||||
local cmd = src:sub(pos, pos)
|
||||
pos = pos + 1
|
||||
|
||||
if cmd == "s" then
|
||||
local delim = src:sub(pos, pos); pos = pos + 1
|
||||
local pat, p1 = parseDelim(src, pos, delim); pos = p1
|
||||
local repl, p2 = parseDelim(src, pos, delim); pos = p2
|
||||
local flags = ""
|
||||
while pos <= len and src:sub(pos,pos):match("[giIp]") do
|
||||
flags = flags .. src:sub(pos,pos); pos = pos + 1
|
||||
end
|
||||
table.insert(cmds, { addr1=addr1, addr2=addr2, cmd="s",
|
||||
pat=pat, repl=repl, flags=flags })
|
||||
|
||||
elseif cmd == "y" then
|
||||
local delim = src:sub(pos, pos); pos = pos + 1
|
||||
local srcch, p1 = parseDelim(src, pos, delim); pos = p1
|
||||
local dstch, p2 = parseDelim(src, pos, delim); pos = p2
|
||||
table.insert(cmds, { addr1=addr1, addr2=addr2, cmd="y",
|
||||
src=srcch, dst=dstch })
|
||||
|
||||
elseif cmd == "d" or cmd == "p" or cmd == "q" or cmd == "=" then
|
||||
table.insert(cmds, { addr1=addr1, addr2=addr2, cmd=cmd })
|
||||
|
||||
elseif cmd == "{" then
|
||||
local depth = 1
|
||||
local start = pos
|
||||
while pos <= len and depth > 0 do
|
||||
local ch = src:sub(pos,pos)
|
||||
if ch == "{" then depth = depth + 1
|
||||
elseif ch == "}" then depth = depth - 1 end
|
||||
pos = pos + 1
|
||||
end
|
||||
local inner = src:sub(start, pos - 2)
|
||||
local innerCmds = parseCommands(inner)
|
||||
for _, ic in ipairs(innerCmds) do
|
||||
ic.addr1 = ic.addr1 or addr1
|
||||
ic.addr2 = ic.addr2 or addr2
|
||||
end
|
||||
for _, ic in ipairs(innerCmds) do
|
||||
table.insert(cmds, ic)
|
||||
end
|
||||
|
||||
elseif cmd == "\n" or cmd == ";" then
|
||||
else
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return cmds
|
||||
end
|
||||
|
||||
local cmds = parseCommands(script)
|
||||
|
||||
local inRange = {}
|
||||
|
||||
local function addrMatch(cmd, lineNum, line, isLast, ci)
|
||||
local a1 = cmd.addr1
|
||||
local a2 = cmd.addr2
|
||||
|
||||
if not a1 then return true end
|
||||
|
||||
local function matchOne(addr, ln, l)
|
||||
if addr.type == "line" then return ln == addr.n
|
||||
elseif addr.type == "last" then return isLast
|
||||
elseif addr.type == "regex" then return l:find(sedPatToLua(addr.pat)) ~= nil
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
if not a2 then
|
||||
return matchOne(a1, lineNum, line)
|
||||
end
|
||||
|
||||
if inRange[ci] then
|
||||
local endMatch
|
||||
if a2.type == "line" then endMatch = (lineNum >= a2.n)
|
||||
elseif a2.type == "last" then endMatch = isLast
|
||||
elseif a2.type == "regex" then endMatch = (line:find(sedPatToLua(a2.pat)) ~= nil)
|
||||
end
|
||||
if endMatch then inRange[ci] = false end
|
||||
return true
|
||||
else
|
||||
if matchOne(a1, lineNum, line) then
|
||||
if a2.type == "line" and a2.n <= lineNum then
|
||||
else
|
||||
inRange[ci] = true
|
||||
end
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local function doSubst(line, pat, repl, flags)
|
||||
local global = flags:find("g") ~= nil
|
||||
local icase = flags:find("[iI]") ~= nil
|
||||
local luaPat = sedPatToLua(pat, icase)
|
||||
|
||||
local function buildRepl(whole, ...)
|
||||
local caps = { ... }
|
||||
local out = {}
|
||||
local rp = repl
|
||||
local ri = 1
|
||||
while ri <= #rp do
|
||||
local rc = rp:sub(ri, ri)
|
||||
if rc == "&" then
|
||||
table.insert(out, whole)
|
||||
elseif rc == "\\" and ri < #rp then
|
||||
ri = ri + 1
|
||||
local nc = rp:sub(ri, ri)
|
||||
if nc:match("%d") then
|
||||
local idx = tonumber(nc)
|
||||
table.insert(out, caps[idx] or "")
|
||||
elseif nc == "n" then
|
||||
table.insert(out, "\n")
|
||||
else
|
||||
table.insert(out, nc)
|
||||
end
|
||||
else
|
||||
table.insert(out, rc)
|
||||
end
|
||||
ri = ri + 1
|
||||
end
|
||||
return table.concat(out)
|
||||
end
|
||||
|
||||
local result
|
||||
local changed = false
|
||||
if global then
|
||||
result = line:gsub(luaPat, buildRepl)
|
||||
changed = (result ~= line)
|
||||
else
|
||||
local s, e, whole
|
||||
local parts = { line:find(luaPat) }
|
||||
if parts[1] then
|
||||
s = parts[1]; e = parts[2]
|
||||
local caps = {}
|
||||
for ci = 3, #parts do caps[#caps+1] = parts[ci] end
|
||||
local wmatch = line:sub(s, e)
|
||||
local replStr = buildRepl(wmatch, table.unpack(caps))
|
||||
result = line:sub(1, s-1) .. replStr .. line:sub(e+1)
|
||||
changed = true
|
||||
else
|
||||
result = line
|
||||
end
|
||||
end
|
||||
return result, changed
|
||||
end
|
||||
|
||||
local function doTranslit(line, src, dst)
|
||||
local out = {}
|
||||
for ci = 1, #line do
|
||||
local c = line:sub(ci, ci)
|
||||
local idx = src:find(c, 1, true)
|
||||
if idx and idx <= #dst then
|
||||
table.insert(out, dst:sub(idx, idx))
|
||||
else
|
||||
table.insert(out, c)
|
||||
end
|
||||
end
|
||||
return table.concat(out)
|
||||
end
|
||||
|
||||
local function processLines(lines, outputLines)
|
||||
local total = #lines
|
||||
for lineNum, line in ipairs(lines) do
|
||||
local isLast = (lineNum == total)
|
||||
local deleted = false
|
||||
local printed = false
|
||||
local quit = false
|
||||
|
||||
local bare = line:gsub("\n$", "")
|
||||
|
||||
for ci, cmd in ipairs(cmds) do
|
||||
if addrMatch(cmd, lineNum, bare, isLast, ci) then
|
||||
if cmd.cmd == "d" then
|
||||
deleted = true; break
|
||||
|
||||
elseif cmd.cmd == "p" then
|
||||
table.insert(outputLines, bare)
|
||||
|
||||
elseif cmd.cmd == "=" then
|
||||
table.insert(outputLines, tostring(lineNum))
|
||||
|
||||
elseif cmd.cmd == "q" then
|
||||
if not silent then table.insert(outputLines, bare) end
|
||||
quit = true; break
|
||||
|
||||
elseif cmd.cmd == "s" then
|
||||
local newLine, changed = doSubst(bare, cmd.pat, cmd.repl, cmd.flags)
|
||||
bare = newLine
|
||||
if changed and cmd.flags:find("p") then
|
||||
table.insert(outputLines, bare)
|
||||
end
|
||||
|
||||
elseif cmd.cmd == "y" then
|
||||
bare = doTranslit(bare, cmd.src, cmd.dst)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if quit then break end
|
||||
if not deleted and not silent then
|
||||
table.insert(outputLines, bare)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function readLines(fd)
|
||||
local lines = {}
|
||||
local buf = ""
|
||||
while true do
|
||||
local chunk = syscall.read(fd, 1024)
|
||||
if not chunk or chunk == "" then break end
|
||||
buf = buf .. chunk
|
||||
end
|
||||
for line in (buf .. "\n"):gmatch("([^\n]*)\n") do
|
||||
table.insert(lines, line)
|
||||
end
|
||||
if buf ~= "" and buf:sub(-1) ~= "\n" and lines[#lines] == "" then
|
||||
table.remove(lines)
|
||||
end
|
||||
return lines
|
||||
end
|
||||
|
||||
local function runOnFile(path)
|
||||
local fd
|
||||
if path then
|
||||
local ok, err = pcall(function() fd = syscall.open(path, "r") end)
|
||||
if not ok then
|
||||
print(name .. ": " .. path .. ": " .. tostring(err))
|
||||
return false
|
||||
end
|
||||
else
|
||||
fd = 0
|
||||
end
|
||||
|
||||
local lines = readLines(fd)
|
||||
if path then syscall.close(fd) end
|
||||
|
||||
inRange = {}
|
||||
|
||||
local outputLines = {}
|
||||
processLines(lines, outputLines)
|
||||
|
||||
if inplace and path then
|
||||
local wfd = syscall.open(path, "w")
|
||||
for _, ol in ipairs(outputLines) do
|
||||
syscall.write(wfd, ol .. "\n")
|
||||
end
|
||||
syscall.close(wfd)
|
||||
else
|
||||
for _, ol in ipairs(outputLines) do
|
||||
print(ol)
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
if #files == 0 then
|
||||
runOnFile(nil)
|
||||
else
|
||||
for _, f in ipairs(files) do
|
||||
local absf = f
|
||||
if absf:sub(1,1) ~= "/" then absf = syscall.getcwd() .. "/" .. f end
|
||||
runOnFile(absf)
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user