Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4544f47
commit f0bbe0d
Showing
4 changed files
with
960 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
--- Reads configuration files into a Lua table. | ||
-- Understands INI files, classic Unix config files, and simple | ||
-- delimited columns of values. | ||
-- | ||
-- # test.config | ||
-- # Read timeout in seconds | ||
-- read.timeout=10 | ||
-- # Write timeout in seconds | ||
-- write.timeout=5 | ||
-- #acceptable ports | ||
-- ports = 1002,1003,1004 | ||
-- | ||
-- -- readconfig.lua | ||
-- require 'pl' | ||
-- local t = config.read 'test.config' | ||
-- print(pretty.write(t)) | ||
-- | ||
-- ### output ##### | ||
-- { | ||
-- ports = { | ||
-- 1002, | ||
-- 1003, | ||
-- 1004 | ||
-- }, | ||
-- write_timeout = 5, | ||
-- read_timeout = 10 | ||
-- } | ||
-- | ||
-- See the Guide for further @{06-data.md.Reading_Configuration_Files|discussion} | ||
-- | ||
-- Dependencies: none | ||
-- @module pl.config | ||
|
||
local type,tonumber,ipairs,io, table = _G.type,_G.tonumber,_G.ipairs,_G.io,_G.table | ||
|
||
local function split(s,re) | ||
local res = {} | ||
local t_insert = table.insert | ||
re = '[^'..re..']+' | ||
for k in s:gmatch(re) do t_insert(res,k) end | ||
return res | ||
end | ||
|
||
local function strip(s) | ||
return s:gsub('^%s+',''):gsub('%s+$','') | ||
end | ||
|
||
local function strip_quotes (s) | ||
return s:gsub("['\"](.*)['\"]",'%1') | ||
end | ||
|
||
local config = {} | ||
|
||
--- like io.lines(), but allows for lines to be continued with '\'. | ||
-- @param file a file-like object (anything where read() returns the next line) or a filename. | ||
-- Defaults to stardard input. | ||
-- @return an iterator over the lines, or nil | ||
-- @return error 'not a file-like object' or 'file is nil' | ||
function config.lines(file) | ||
local f,openf,err | ||
local line = '' | ||
if type(file) == 'string' then | ||
f,err = io.open(file,'r') | ||
if not f then return nil,err end | ||
openf = true | ||
else | ||
f = file or io.stdin | ||
if not file.read then return nil, 'not a file-like object' end | ||
end | ||
if not f then return nil, 'file is nil' end | ||
return function() | ||
local l = f:read() | ||
while l do | ||
-- does the line end with '\'? | ||
local i = l:find '\\%s*$' | ||
if i then -- if so, | ||
line = line..l:sub(1,i-1) | ||
elseif line == '' then | ||
return l | ||
else | ||
l = line..l | ||
line = '' | ||
return l | ||
end | ||
l = f:read() | ||
end | ||
if openf then f:close() end | ||
end | ||
end | ||
|
||
--- read a configuration file into a table | ||
-- @param file either a file-like object or a string, which must be a filename | ||
-- @param cnfg a configuration table that may contain these fields: | ||
-- <ul> | ||
-- <li> variablilize make names into valid Lua identifiers (default true)</li> | ||
-- <li> convert_numbers try to convert values into numbers (default true)</li> | ||
-- <li> trim_space ensure that there is no starting or trailing whitespace with values (default true)</li> | ||
-- <li> trim_quotes remove quotes from strings (default false)</li> | ||
-- <li> list_delim delimiter to use when separating columns (default ',')</li> | ||
-- </ul> | ||
-- @return a table containing items, or nil | ||
-- @return error message (same as @{config.lines} | ||
function config.read(file,cnfg) | ||
local f,openf,err | ||
cnfg = cnfg or {} | ||
local function check_cnfg (var,def) | ||
local val = cnfg[var] | ||
if val == nil then return def else return val end | ||
end | ||
local t = {} | ||
local top_t = t | ||
local variablilize = check_cnfg ('variabilize',true) | ||
local list_delim = check_cnfg('list_delim',',') | ||
local convert_numbers = check_cnfg('convert_numbers',true) | ||
local trim_space = check_cnfg('trim_space',true) | ||
local trim_quotes = check_cnfg('trim_quotes',false) | ||
local ignore_assign = check_cnfg('ignore_assign',false) | ||
local keysep = check_cnfg('keysep','=') | ||
local keypat = keysep == ' ' and '%s+' or '%s*'..keysep..'%s*' | ||
|
||
local function process_name(key) | ||
if variablilize then | ||
key = key:gsub('[^%w]','_') | ||
end | ||
return key | ||
end | ||
|
||
local function process_value(value) | ||
if list_delim and value:find(list_delim) then | ||
value = split(value,list_delim) | ||
for i,v in ipairs(value) do | ||
value[i] = process_value(v) | ||
end | ||
elseif convert_numbers and value:find('^[%d%+%-]') then | ||
local val = tonumber(value) | ||
if val then value = val end | ||
end | ||
if type(value) == 'string' then | ||
if trim_space then value = strip(value) end | ||
if trim_quotes then value = strip_quotes(value) end | ||
end | ||
return value | ||
end | ||
|
||
local iter,err = config.lines(file) | ||
if not iter then return nil,err end | ||
for line in iter do | ||
-- strips comments | ||
local ci = line:find('%s*[#;]') | ||
if ci then line = line:sub(1,ci-1) end | ||
-- and ignore blank lines | ||
if line:find('^%s*$') then | ||
elseif line:find('^%[') then -- section! | ||
local section = process_name(line:match('%[([^%]]+)%]')) | ||
t = top_t | ||
t[section] = {} | ||
t = t[section] | ||
else | ||
line = line:gsub('^%s*','') | ||
local i1,i2 = line:find(keypat) | ||
if i1 and not ignore_assign then -- key,value assignment | ||
local key = process_name(line:sub(1,i1-1)) | ||
local value = process_value(line:sub(i2+1)) | ||
t[key] = value | ||
else -- a plain list of values... | ||
t[#t+1] = process_value(line) | ||
end | ||
end | ||
end | ||
return top_t | ||
end | ||
|
||
return config |
Oops, something went wrong.