Lua program throwing error with stdin after completion - lua

I've written a lua program for OpenComputers in Minecraft which downloads a github repo to the computer, file structure and all. The code:
internet = require("internet")
io = require("io")
filesystem = require("filesystem")
handle = internet.request("https://api.github.com/repos/Vedvart/Minecraft/contents")
result = ""
for chunk in handle do result = result..chunk end
while(not (string.find(result, "download_url") == nil)) do
i, j = string.find(result, "download_url")
qi, qj = string.find(string.sub(result, j+4,-1), '"')
url = string.sub(result, j + 4, j + qi + 2)
if string.sub(url, -14) == '.gitattributes' then goto continue end
ni, nj = string.find(url, "Vedvart")
filepath = string.sub(url, nj+2, -1)
ri, rj = string.find(string.reverse(filepath), "/")
if not filesystem.isDirectory("/home/github/"..string.sub(filepath, 1, -ri)) then
print('had to make')
filesystem.makeDirectory("/home/github/"..string.sub(filepath, 1, -ri))
end
print('here 2')
file_content_handle = internet.request(url)
file_content = ''
for chunk in file_content_handle do file_content = file_content .. chunk end
print(filepath)
print(io)
print(filesystem.isDirectory("/home/github/"..string.sub(filepath, 1, -ri)))
file = io.open('/home/github/'..filepath, 'w')
print(file)
file:write(file_content)
file:close()
print('here 3')
print(file)
::continue::
result = string.sub(result, j+qi+3, -1)
print('here 4')
end
print('done')
The code actually runs fine, reaching the final print statement and successfully generating the file structure and downloading all files. However, after this, the program throws an error:
I've googled around, and struggled to even find the same error being thrown, much less in the same circumstances. It doesn't seem like the error is originating directly in my code, but other code seems to run fine, so something in my program must be triggering this error elsewhere. I'm not sure what is causing it - any help is appreciated.

Not sure why you need to require io. This is not necessary in vanilla Lua and looking at the example snippets at their website it shouldn't be necessary in OpenComputers either.
io = require("io") overwrites any existing global io library and maybe that newly required table does not have a stdin field.
Just an educated guess :)
If your code causes this error after being completed a change in a global variable is one of the few possible reasons.

Related

Prevent a file being written to more than once without file:close()

I'm currently working on a logging system. My problems arise when using for loops and file writing. Here is a small example:
file = io.open("text.txt","a") --text.txt can be literally anything
for i=1,8 do
if x == true then
file:write("X is true.")
elseif y == true then
file:write("Y is true.")
end
end
Is there a way to stop the file from being written to multiple times without using file:close()? I have a huge number of different file:write sections, and adding file:close() after all of them would be a massive problem.
If file:close() every time is a massive problem, then this is the custom logic you need.
myFileMetatable = {} --implement all necessary file operations here
function myFileMetatable.write(self, str)
if not self.written then
self.written = true
self.f:write(str)
end
end
function myFileMetatable.close(self)
self.f:close()
end
myFile = {}
function myFile.open(filename, mode)
local t = {f = io.open(filename, mode)}
setmetatable(t, {__index = myFileMetatable})
return t
end
--now you can do
file = myFile.open("test", "w")
file:write("test")
file:write("hello")
file:write("world")
file:close() --and only "test" will be written
Note that this is probably much better than replacing file:write(str) with something file_write(file, str), since you need to store somewhere the fact that the file has already been written to, which you cannot store inside the FILE* object and using a global variable for that will break when using multiple files. That's why I wrap the FILE* object in a table and use myFileMetatable to implement my own methods that I will need.
However, if you need just one file at a time and don't mind the global variable then this is more efficient.
file_written = false
function file_write(file, str)
if not file_written then
file_written = true
file:write(str)
end
end
file = io.open("test", "w")
file_write(file, "test")
file_write(file, "hello")
file_write(file, "world")
file:close()
Mind that it's not as pretty as the first example and you might face a problem in the future, if you decide to expand beyond one file.
How Egor Skriptunoff already said, I'll recommend you to write your own writing function. I'm normally using sth. like this:
local function writeFile(filePath, str)
local outfile = io.open(filePath, 'w')
outfile:write(str)
outfile:close()
end
For appending to the file easily change the mode from w to a.
For my specific case, I've found a solution - since I just want a single option to print, and it's the last option (rather than the first, and I should've specified this), I can just set a variable to what I want my output to be and write that at the end.
log = ""
if x == 2 then
log = "X is 2."
elseif y == 2 then
log = "Y is 2."
end
file:write(log)
For the last option, I'd refer anyone to the accepted answer which should be perfect.

Lua Sandbox "hacking"

So I use a program where I script mods in lua, the lua is in a sandbox state, meaning most functions are blocked like IO and OS, I can't even use REQUIRE to add libs.
I need to have a function that unzips files in one of my mods and I don't seem to find a way.
Is there anyway to do it?
If it's not possible in an easy way, is it possible to hack the program .exe or dlls to re-enable those functions in the lua?
Thank you in advance, Regards
There are decompression librarys in pure Lua, you should be able to embed these in any environment that allows loading Lua scripts: http://lua-users.org/wiki/CompressionAndArchiving
If you can't access any files at all, you could try a simple packer:
#!/usr/bin/env lua
local files = arg
local w = io.write
local function pack(...) return {...} end
w("files = {\n")
for i, filename in ipairs(arg) do
w('\t["' ..filename .. '"] = "')
local file = assert(io.open(filename, "r"), "Can't open file!")
local data = file:read("*a")
data = data:gsub("\a", "\\a")
:gsub("\\", "\\\\")
:gsub("\f", "\\f")
:gsub("\n", "\\n")
:gsub("\r", "\\r")
:gsub("\t", "\\t")
:gsub("\v", "\\v")
:gsub('"', '\\"')
:gsub("'", "\\'")
w(data, '",\n')
end
w("}\n")
w([[
function require(path)
local data = assert(files[path..".lua"], "file not found")
local func = assert(loadstring(data))
local _, ret = assert(pcall(func))
return ret
end
]])
w('require("', arg[1]:match("^(.-)%.lua$"),'")\n')
This should create a script like this:
$ ./packer.lua init.lua
files = {
["init.lua"] = "for k,v in pairs(arg) do\n\tprint(k,v)\nend\n",
}
function require(path)
local data = assert(files[path..".lua"], "file not found")
local func = assert(loadstring(data))
local _, ret = assert(pcall(func))
return ret
end
require("init")

Manually typing commands into console works but not in the program

I'm having a problem where I can do every single function in a command line version of lua however, when I run the program it won't throw any errors it just ends.I'm not sure how to diagnose this but I have tried getting an error to be thrown a couple times for different things and it will error and print the error.
power = peripheral.wrap("bottom")
mon = peripheral.wrap("top")
x,y = mon.getSize()
clearTerm = function()
term.clear()
term.setCursorPos(1,1)
end
clearBoth = function()
clearMon()
clearTerm()
end
intLen = function(bar)
tab = tostring(bar)
tab = string.len(tab)
return tab
end
checkPower = function()
total = power.getMaxEnergyStored()
local til = intLen(total)
local yy = math.floor(y/2)
local tol = math.floor(x-til)
mon.setCursorPos(yy+0,tol/2)
for z=1,til do mon.write("-") end
mon.setCursorPos(yy-1,tol/2)
mon.write(total)
while true do
current = power.getEnergyStored()
local cil = intLen(current)
local col = math.floor(x-cil)
mon.setCursorPos(yy+1,col/2)
mon.write(current)
sleep(1)
end
end
I'll leave a link to the pastebin of the full program here too.
First of all, you can add some outputs to your code. Just add stuff like
print "1" -- debug output
...
print "2" -- debug output
...
-- don't forget to remove these after you're done debugging!
to your code and see how many of them you see when running the program, this way you can narrow down when exactly the program crashes.
Also, I cannot find where the function clearMon() is defined, might that be source the problem, or is it defined elsewhere?

In trepl or luajit, how can I find the source code of a library I'm using?

Let's say I'm working with a lua library I installed using luarocks, and I want to see the definition of a function from that library. In ipython in could use
??function_name
to see the definition in the terminal, in matlab I could use
which function_name
then use my editor to look at the path returned by which. How could I do something similar to find the function definition for a lua library?
In 'plain' Lua/JIT, you can say debug.getinfo( func ) and will get a table containing (among others) the fields short_src, source and linedefined.
For Lua functions, short_src will be the filename or stdin if it was defined in the REPL. (source has a slightly different format, filenames are prefixed with an #, a = prefix is used for C functions or stuff defined interactively, and for loaded functions, it will be the actual string that was loaded.)
You can pack that up in a function like
function sourceof( f )
local info = debug.getinfo( f, "S" )
return info.short_src, info.linedefined
end
or maybe even start an editor and point it there, e.g. (for vim)
function viewsource( f )
-- get info & check it's actually from a file
local info = debug.getinfo( f, "S" )
local src, line = info.source, info.linedefined
if src == "=[C]" then return nil, "Is a C function." end
local path = src:match "^#(.*)$"
if path then
-- start vim (or an other editor if you adapt the format string)
return os.execute( ("vim -fR %q +%d"):format( path, line ) )
end
return nil, "Was defined at run time."
end
And just for fun, here's yet another version that returns the code if it can find it somewhere. (This will also work for functions that have been generated at run time, e.g. by calling load, and where no source file exists. You could also work in the other direction by dumping the loaded snippet into a temp file and opening that…)
-- helper to extract the source block defining the function
local function funclines( str, line1, lineN, filename )
-- if linedefined / lastlinedefined are 0, this is the main chunk's function
if line1 == 0 and lineN == 0 then
filename = filename and filename.." (main chunk)"
or "(chunk defined at runtime)"
return "-- "..filename.."\n"..str
end
-- add line info to file name or use placeholder
filename = filename and filename..":"..line1 or "(defined at runtime)"
-- get the source block
local phase, skip, grab = 1, line1-1, lineN-(line1-1)
local ostart, oend -- these will be the start/end offsets
if skip == 0 then phase, ostart = 2, 0 end -- starts at first line
for pos in str:gmatch "\n()" do
if phase == 1 then -- find offset of linedefined
skip = skip - 1 ; if skip == 0 then ostart, phase = pos, 2 end
else -- phase == 2, find offset of lastlinedefined+1
grab = grab - 1 ; if grab == 0 then oend = pos-2 ; break end
end
end
return "-- "..filename.."\n"..str:sub( ostart, oend )
end
function dumpsource( f )
-- get info & line numbers
local info = debug.getinfo( f, "S" )
local src, line, lastline = info.source, info.linedefined, info.lastlinedefined
-- can't do anything for a C function
if src == "=[C]" then return nil, "Is a C function." end
if src == "=stdin" then return nil, "Was defined interactively." end
-- for files, fetch the definition
local path = src:match "^#(.*)$"
if path then
local f = io.open( path )
local code = f:read '*a'
f:close( )
return funclines( code, line, lastline, path )
end
-- otherwise `load`ed, so `source`/`src` _is_ the source
return funclines( src, line, lastline )
end
A closing remark: If you paste code into a Lua/JIT REPL, locals disappear between definitions, because every line (or minimal complete group of lines) is its own chunk. The common fix (that you probably know) is to wrap everything into a block as do*paste*end, but an alternative is to load[[*paste*]]() (possibly with more =s like [===[ and ]===].) If you paste this way, the above dumpsource (or any other function using debug.getinfo) will then be able to get the source of the function(s). This also means that if you defined a nice function but it's gone from the history and the scroll buffer, you can recover it in this way (if you defined it by loading and not directly feeding the interpreter). Saving the source in a file will then also be possible without copy-pasting and not require editing out the >> prompts.

how do you properly use a pipe with lua to get the output of a program?

I am using Lua with the luaposix library to get the the output of some commands, and sometimes send some too. I am using this code, or variants of this to do my work, but I sometimes get stuck at posix.wait(cpid) or sometimes the command doesn't seem to finish.
-- returns true on connected, else false
function wifi.wpa_supplicant_status()
-- iw dev wlan0-1 link
local r,w = posix.pipe()
local cpid = posix.fork()
if cpid == 0 then --child writes to pipe
--close unused read end
local devnull = posix.open("/dev/null", posix.O_RDWR)
posix.close(r)
posix.dup2(devnull, 0)
posix.dup2(w, 1)
posix.dup2(devnull, 2)
local dir = wifi.wpa_supplicant_dir()
local iface = posix.basename(dir)
iface = string.gsub(iface, "wpa_supplicant%-",'')
posix.exec('/usr/sbin/iw', {'dev', iface, 'link'})
posix._exit(-1)
elseif cpid > 0 then
--parent reads from pipe, close write end
posix.close(w)
local buf = ''
while true do
local tmp = posix.read(r, 100)
if tmp ~= nil and #tmp > 0 then
buf = buf .. tmp
else
break
end
end
-- TODO, check exit value, to see if entry exists or not
while posix.wait(cpid) ~= cpid do print("waiting in wpa_supplicant_status") end
print("status is "..buf)
if string.find(buf, "Connected to", 1, true) then
return true
else
return false
end
end
end
This is what I understand what I have to do(to just get output):
create a single pipe
fork
if child,
close read end of pipe
dup2(write_end, stdout)
exec() to desired process
if parent
close write end of pipe
do blocking reads to read end of pipe, till you get a 0 byte read, which means the child process has terminated, closing the pipe
Am I missing something?
You should have a look at the answers in this question How do you construct a read-write pipe with lua?
to get an idea of the difficulties to implement this. One thing that strikes me is that nobody seems to be able to get it to work with only one child process.
However, In the example you give, it seems you only want to get the output of the command, so I would suggest simply doing this:
function os.capture(cmd)
local f = assert(io.popen(cmd, 'r'))
local s = assert(f:read('*a'))
f:close()
return s
end
function wifi.wpa_supplicant_status()
local dir = wifi.wpa_supplicant_dir()
local iface = posix.basename(dir):gsub("wpa_supplicant%-",'')
local cmd = ('/usr/sbin/iw dev %s link'):format(iface)
local buf = os.capture(cmd)
return buf:find("Connected to", 1, true) ~= nil
end
This is not even tested, but you should get the idea.

Resources