lua-users home
lua-l archive

Re: Lua in a windowing enviroment

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Ram Firestone wrote:
Twice before I have hacked the lua IO library (liolib) to optionally read and write from functions instead of IO descriptors. I did
this so that I could get the IO library to work in a windowing environment. Since I have changed jobs I no longer have this old code
and I am about to hack it again. However before I do I thought I would check to see if there is some better or standardized way of
doing this now. If not, I'll go ahead and hack it again. After doing it twice before it will probably only take me a few hours now
:-P
Also if any lua support guys are listening I was wondering if it might be better to just add this to the library at some point. The
way I hacked it before the library worked exactly the way it used to unless you registered IO functions with it, in which case it
would switch over to using them. I'm not sure if this fits the vision or not but it seems convenient. Just a thought. Ram
It is possible to hijack the IO in the standard DLLs using pure Lua plus a little help from C. Attached is my Lua script that does this (its a re-implementation of the standard lua.c in Lua). All you need to provide is the m.puts, m.gets and m.setmetatable functions.
--
Regards,
Dave Nichols
Match-IT Limited
Tel: 0845 1300 510
Fax: 0845 1300 610
mailto:dave.nichols@make247.co.uk
http://www.make247.co.uk
Email Disclaimer: The contents of this electronic mail message and any attachments (collectively "this message") are confidential, possibly privileged and intended only for its addressee ("the addressee"). If received in error, please delete immediately without disclosing its contents to anyone. Neither the sender nor its management or employees will in any way be responsible for any advice, opinion, conclusion or other information contained in this message or arising from it's disclosure.
--{{{ history
--11/08/06 DCN Created
--05/09/06 DCN Fix results returning problem from _G.console
--}}}
--{{{ description
--provide a console facility for use in Match-IT
--that uses a Clarion window for the UI
--}}}
m = require'match_it'
--{{{ override file: operations for stdin/out/err
local function name(self)
 if self == io.stdin then return 'stdin' end
 if self == io.stdout then return 'stdout' end
 if self == io.stderr then return 'stderr' end
 return tostring(self)
end
local inmeta = {}
inmeta.__index = inmeta
inmeta.close = function(self) return nil,'attempt to close '..name(self) end --not allowed to close
inmeta.flush = function(self) return true end --flush is a no-op
inmeta.setvbuf = function(self) return true end --setvbuf is a no-op
inmeta.seek = function(self) return nil,'attempt to seek '..name(self) end --seek is not allowed
inmeta.__tostring = function(self) return 'file ('..name(self)..')' end
inmeta.lines = function(self) return function() return m.gets(-1) end end
--{{{ function inmeta.read(self,...)
function inmeta.read(self,...)
 if #{...} == 0 then return m.gets(-1) end --read to eol
 local results = {}
 local file = {}
 local line, char
 for arg,v in ipairs{...} do
 if type(v) == 'number' then
 results[arg] = m.gets(v) --read n chars, nb: n=0 means test for eof
 elseif type(v) == 'string' then
 if string.sub(v,1,2) == '*l' then
 results[arg] = m.gets(-1) --read to eol
 elseif string.sub(v,1,2) == '*a' then
 file = {}
 line = 0
 repeat
 line = line + 1
 file[line] = m.gets(-1) --read to eol
 until not file[line]
 results[arg] = table.concat(file,'\n')
 elseif string.sub(v,1,2) == '*n' then
 line = ''
 char = m.gets(1)
 while char and (char == '' or char == ' ' or char == '\t') do --skip white space
 char = m.gets(1)
 end
 while char and char ~= '' and char ~= ' ' and char ~= '\t' do --carry on until white space
 line = line..char
 char = m.gets(1)
 end
 results[arg] = tonumber(line)
 else
 assert(false,'bad argument #'..arg..' (invalid format)')
 end
 else
 assert(false,'bad argument #'..arg..' (expected string, got '..type(v)..')')
 end
 end
 return unpack(results)
end
--}}}
m.setmetatable(io.stdin,inmeta)
local outmeta = {}
outmeta.__index = outmeta
outmeta.close = function(self) return nil,'attempt to close '..name(self) end --not allowed to close
outmeta.flush = function(self) return true end --flush is a no-op
outmeta.setvbuf = function(self) return true end --setvbuf is a no-op
outmeta.seek = function(self) return nil,'attempt to seek '..name(self) end --seek is not allowed
outmeta.__tostring = function(self) return 'file ('..name(self)..')' end
--{{{ function outmeta.write(self,...)
function outmeta.write(self,...)
 for arg,v in ipairs{...} do
 if type(v) == 'number' then
 m.puts(string.format('%.14g',v))
 elseif type(v) == 'string' then
 m.puts(v)
 else
 assert(false,'bad argument #'..arg..' (expected string, got '..type(v)..')')
 end
 end
 return true
end
--}}}
m.setmetatable(io.stdout,outmeta)
m.setmetatable(io.stderr,outmeta)
--}}}
--redefine standard stuff to use file:read/write
--{{{ function _G.print(...)
--print stuff to stdout (nb: not to default output)
function _G.print(...)
 for i,v in ipairs{...} do
 if i > 1 then io.stdout:write('\t') end
 io.stdout:write(tostring(v))
 end
 io.stdout:write('\n')
end
--}}}
--{{{ function _G.debug.debug()
function _G.debug.debug()
 local cmd, ok, msg
 repeat
 io.stderr:write("Lua_debug> ")
 cmd = io.stdin:read()
 if not cmd or cmd == 'cont' then return end
 ok, msg = pcall(loadstring,cmd,"=(debug command)")
 if not ok then
 io.stderr:write(msg)
 io.stderr:write('\n')
 else
 ok, msg = pcall(msg)
 if not ok then
 io.stderr:write(msg)
 io.stderr:write('\n')
 end
 end
 until false
end
--}}}
--{{{ function _G.io.close(file)
function _G.io.close(file)
 if file then
 return file:close()
 else
 return io.output():close()
 end
end
--}}}
--{{{ function _G.io.flush()
function _G.io.flush()
 return io.output():flush()
end
--}}}
--{{{ function _G.io.lines(filename)
local _io_lines = io.lines --note original 'cos we need to use it
function _G.io.lines(filename)
 if filename then
 return _io_lines(filename)
 else
 return io.input():lines()
 end
end
--}}}
--{{{ function _G.io.read(...)
function _G.io.read(...)
 return io.input():read(...)
end
--}}}
--{{{ function _G.io.write(...)
function _G.io.write(...)
 return io.output():write(...)
end
--}}}
--lua.c implemented in Lua
--{{{ function _G.console(...)
--15/08/06 DCN @@TBD@@ how to allow a Ctrl-C to interrupt things
--16/08/06 DCN But this is a general problem when running errant Lua scripts in M-IT
--console behaves like pcall,
--i.e. it'll return true+results if OK, or nil+error message if not
function _G.console(args)
 local LUA_RELEASE = "Lua 5.1.1"
 local LUA_COPYRIGHT = "Copyright (C) 1994-2006 Lua.org, PUC-Rio"
 local PROMPT = '> '
 local PROMPT2 = '>> '
 --{{{ local function print_usage(args)
 
 local function print_usage(args)
 io.stderr:write("\n"..
 "Args: "..(args or '').."\n"..
 "Usage: console('[options] [script [args]]')\n"..
 "Available options are:\n"..
 " -e stat execute string 'stat'\n"..
 " -l name require library 'name'\n"..
 " -i enter interactive mode after executing 'script'\n"..
 " -v show version information\n"..
 " -- stop handling options\n"..
 "no arguments is the same as \"-i\"\n"..
 "arguments with embedded spaces must be quoted using \"\n")
 end
 
 --}}}
 --{{{ local function l_message(msg)
 
 local function l_message(msg)
 io.stderr:write(msg..'\n')
 end
 
 --}}}
 --{{{ local function report(status,msg)
 
 --report OK or bad result
 --status is nil for an error, then the msg is the error
 --returns its input params in all cases
 
 local function report(status,msg)
 if not status then
 collectgarbage('collect')
 if msg then
 if not tostring(msg) then
 l_message('(error object is not a string)')
 else
 l_message(msg)
 end
 else
 l_message('(error with no message)')
 end
 end
 return status,msg
 end
 
 --}}}
 --{{{ local function print_version()
 
 local function print_version()
 l_message('Lua console: '..LUA_RELEASE.." "..LUA_COPYRIGHT)
 end
 
 --}}}
 --{{{ local function getargs(args,n)
 
 local function getargs(args,n)
 local results = {}
 for i,v in ipairs(args) do
 if i >= n then results[#results+1]=v end
 end
 results.n = #results
 return results
 end
 
 --}}}
 --{{{ local function docall(func,err,...)
 
 --NB: The return from here is false,error message
 -- or true,{results}
 -- i.e. always 2 things but the 2nd thing is a table on success (possibly empty)
 
 local function docall(func,err,...)
 if func then
 local arg = {...}
 local results = {xpcall(function() return func(unpack(arg)) end,debug.traceback)}
 local status = results[1]
 table.remove(results,1)
 if status then
 return status, results
 else
 return report(status, results[1])
 end
 else
 return report(func,err)
 end
 end
 
 --}}}
 --{{{ local function dofile(name,...)
 
 local function dofile(name,...)
 local func, err = loadfile(name)
 if func then
 return docall(func,nil,...)
 else
 return report(func,err)
 end
 end
 
 --}}}
 --{{{ local function dostring(s,name)
 
 local function dostring(s,name)
 return docall(loadstring(s,name))
 end
 
 --}}}
 --{{{ local function dolibrary(name)
 
 local function dolibrary(name)
 return docall(require,'require not defined',name)
 end
 
 --}}}
 --{{{ local function readline(prompt)
 
 local function readline(prompt)
 if prompt then io.stdout:write(prompt) end
 return io.stdin:read("*l")
 end
 
 --}}}
 --{{{ local function incomplete(status,func)
 
 local function incomplete(status,func)
 if not func and string.find(status,".*near '%<eof%>'$") then return true end
 return false
 end
 
 --}}}
 --{{{ local function loadline()
 
 local function loadline()
 local line2, func, status
 local line = readline(PROMPT)
 if not line then return nil,nil end --no input
 if string.sub(line,1,1) == '=' then
 line = "return "..string.match(line,"^=(.*)") --map '=' to 'return'
 end
 repeat --until get complete line
 func,status = loadstring(line,"=stdin")
 if not incomplete(status,func) then return func,status end
 line2 = readline(PROMPT2)
 if not line2 then return nil,nil end --no more input
 line = line..line2
 until false
 end
 
 --}}}
 --{{{ local function dotty()
 
 local function dotty()
 local func, status, results, ok, msg
 repeat
 func,status = loadline()
 if not func then
 if not status then break end --end of input
 report(func,status) --syntax error
 else
 status,results = docall(func)
 if status and #results > 0 then
 ok,msg = pcall(print,unpack(results))
 if not ok then
 l_message("error calling 'print' ("..msg..")")
 end
 end
 end
 until false
 --io.stdout:write('\n')
 return
 end
 
 --}}}
 --{{{ local function handle_args(args)
 
 --args is: [options] [script [args]]
 --Available options are:
 -- -e stat execute string 'stat'
 -- -l name require library 'name'
 -- -i enter interactive mode after executing 'script'
 -- -v show version information
 -- -- stop handling options
 --no arguments is the same as -i
 
 local function handle_args(args)
 
 if (args or '') == '' then print_version(); return true,true,nil end --no arguments, go interactive
 
 --{{{ put all the args 'words' in t{}
 
 local s = args .. ' ' -- ending space
 local t = {} -- table to collect fields
 local fieldstart = 1
 repeat
 -- next field is quoted? (start with '"'?)
 if string.find(s, '^"', fieldstart) then
 local a, c
 local i = fieldstart
 repeat
 -- find closing quote
 a, i, c = string.find(s, '"("?)', i+1)
 until c ~= '"' -- quote not followed by quote?
 if not i then error('unmatched "') end
 local f = string.sub(s, fieldstart+1, i-1)
 table.insert(t, (string.gsub(f, '""', '"')))
 fieldstart = string.find(s, ' ', i) + 1
 else -- unquoted; find next space
 local nexti = string.find(s, ' ', fieldstart)
 table.insert(t, string.sub(s, fieldstart, nexti-1))
 fieldstart = nexti + 1
 end
 until fieldstart > string.len(s)
 
 --}}}
 
 local i, ii, opt, interactive, status, results, filename, v
 
 --{{{ validate and process immediate options
 
 i = 0
 
 while i < #t do
 i = i + 1; v = t[i]
 if string.sub(v,1,1) ~= '-' then break end --not an option
 opt = string.sub(v,1,2)
 if opt == '--' then
 if opt ~= v then print_usage(args); return false,true,'bad -- option: '..args end
 break
 elseif opt == '-v' then
 print_version()
 elseif opt == '-i' then
 interactive = true
 elseif opt == '-e' then
 i = i + 1; opt = t[i]
 if (opt or '') == '' then print_usage(args); return false,true,'bad -e option: '..args end
 --delay this
 elseif opt == '-l' then
 i = i + 1; opt = t[i]
 if (opt or '') == '' then print_usage(args); return false,true,'bad -l option: '..args end
 --delay this
 else
 print_usage(args); return false,true,'unknown option: '..args
 end
 end
 
 --}}}
 --{{{ process delayed options
 
 i = 0
 
 while i < #t do
 i = i + 1; v = t[i]
 ii = i
 if string.sub(v,1,1) ~= '-' then ii = ii - 1; break end --not an option
 opt = string.sub(v,1,2)
 if opt == '--' then
 break
 elseif opt == '-v' then
 --already done this
 elseif opt == '-i' then
 --already done this
 elseif opt == '-e' then
 i = i + 1; opt = t[i]
 status, results = dostring(opt,"=<command line>")
 if not status then return status,interactive,results end
 elseif opt == '-l' then
 i = i + 1; opt = t[i]
 status, results = dolibrary(opt)
 if not status then return status,interactive,results end
 end
 end
 
 --}}}
 
 if t[ii+1] then
 filename = t[ii+1]
 _G.arg = getargs(t,ii+2)
 status,results = dofile(filename,unpack(_G.arg))
 if not status then return status,interactive,results end
 else
 status = true
 end
 
 return status,interactive,results
 
 end
 
 --}}}
 --{{{ local function handle_luainit()
 
 local function handle_luainit()
 local status,err
 if LUA_INIT and LUA_INIT ~= '' then
 if string.sub(LUA_INIT,1,1) == '@' then
 return dofile(string.gsub(LUA_INIT,'@','',1))
 else
 return dostring(LUA_INIT,"=LUA_INIT")
 end
 else
 return true
 end
 end
 
 --}}}
 local interactive, status, results
 status, results = handle_luainit() ; if not status then error(results) end
 status, interactive, results = handle_args(args); if not status then error(results) end
 if interactive then dotty() end
 _ARGS = args --so can see them interactively as a debug aid
 _RESULTS = results --when coming back in here after executing a statement
 return unpack(results or {})
end
--}}}

AltStyle によって変換されたページ (->オリジナル) /