• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lunarmodules / Penlight / 7869417034

12 Feb 2024 09:05AM UTC coverage: 88.938%. Remained the same
7869417034

Pull #464

github

Tieske
fix(docs): proper escape back-slash
Pull Request #464: fix(docs): proper escape back-slash

5443 of 6120 relevant lines covered (88.94%)

256.25 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.06
/lua/pl/config.lua
1
--- Reads configuration files into a Lua table.
2
--  Understands INI files, classic Unix config files, and simple
3
-- delimited columns of values. See @{06-data.md.Reading_Configuration_Files|the Guide}
4
--
5
--    # test.config
6
--    # Read timeout in seconds
7
--    read.timeout=10
8
--    # Write timeout in seconds
9
--    write.timeout=5
10
--    #acceptable ports
11
--    ports = 1002,1003,1004
12
--
13
--    -- readconfig.lua
14
--    local config = require 'config'
15
--    local t = config.read 'test.config'
16
--    print(pretty.write(t))
17
--
18
--    ### output #####
19
--    {
20
--      ports = {
21
--        1002,
22
--        1003,
23
--        1004
24
--      },
25
--      write_timeout = 5,
26
--      read_timeout = 10
27
--    }
28
--
29
-- @module pl.config
30

31
local type,tonumber,ipairs,io, table = _G.type,_G.tonumber,_G.ipairs,_G.io,_G.table
6✔
32

33
local function split(s,re)
34
    local res = {}
72✔
35
    local t_insert = table.insert
72✔
36
    re = '[^'..re..']+'
72✔
37
    for k in s:gmatch(re) do t_insert(res,k) end
450✔
38
    return res
72✔
39
end
40

41
local function strip(s)
42
    return s:gsub('^%s+',''):gsub('%s+$','')
354✔
43
end
44

45
local function strip_quotes (s)
46
    return s:gsub("['\"](.*)['\"]",'%1')
30✔
47
end
48

49
local config = {}
6✔
50

51
--- like `io.lines`, but allows for lines to be continued with '`\\`'.
52
-- @param file a file-like object (anything where read() returns the next line) or a filename.
53
-- Defaults to standard input.
54
-- @return an iterator over the lines, or nil
55
-- @return error 'not a file-like object' or 'file is nil'
56
function config.lines(file)
6✔
57
    local f,openf,err
58
    local line = ''
96✔
59
    if type(file) == 'string' then
96✔
60
        f,err = io.open(file,'r')
×
61
        if not f then return nil,err end
×
62
        openf = true
×
63
    else
64
        f = file or io.stdin
96✔
65
        if not file.read then return nil, 'not a file-like object' end
96✔
66
    end
67
    if not f then return nil, 'file is nil' end
96✔
68
    return function()
69
        local l = f:read()
390✔
70
        while l do
528✔
71
            -- only for non-blank lines that don't begin with either ';' or '#'
72
            if l:match '%S' and not l:match '^%s*[;#]' then
432✔
73
                -- does the line end with '\'?
74
                local i = l:find '\\%s*$'
300✔
75
                if i then -- if so,
300✔
76
                    line = line..l:sub(1,i-1)
8✔
77
                elseif line == '' then
294✔
78
                    return l
288✔
79
                else
80
                    l = line..l
6✔
81
                    line = ''
6✔
82
                    return l
6✔
83
                end
84
            end
85
            l = f:read()
184✔
86
        end
87
        if openf then f:close() end
96✔
88
    end
89
end
90

91
--- read a configuration file into a table
92
-- @param file either a file-like object or a string, which must be a filename
93
-- @tab[opt] cnfg a configuration table that may contain these fields:
94
--
95
--  * `smart`  try to deduce what kind of config file we have (default false)
96
--  * `variabilize` make names into valid Lua identifiers (default true)
97
--  * `convert_numbers` try to convert values into numbers (default true)
98
--  * `trim_space` ensure that there is no starting or trailing whitespace with values (default true)
99
--  * `trim_quotes` remove quotes from strings (default false)
100
--  * `list_delim` delimiter to use when separating columns (default ',')
101
--  * `keysep` separator between key and value pairs (default '=')
102
--
103
-- @return a table containing items, or `nil`
104
-- @return error message (same as @{config.lines}
105
function config.read(file,cnfg)
6✔
106
    local auto
107

108
    local iter,err = config.lines(file)
96✔
109
    if not iter then return nil,err end
96✔
110
    local line = iter()
96✔
111
    cnfg = cnfg or {}
96✔
112
    if cnfg.smart then
96✔
113
        auto = true
24✔
114
        if line:match '^[^=]+=' then
24✔
115
            cnfg.keysep = '='
6✔
116
        elseif line:match '^[^:]+:' then
18✔
117
            cnfg.keysep = ':'
6✔
118
            cnfg.list_delim = ':'
6✔
119
        elseif line:match '^%S+%s+' then
12✔
120
            cnfg.keysep = ' '
12✔
121
            -- more than two columns assume that it's a space-delimited list
122
            -- cf /etc/fstab with /etc/ssh/ssh_config
123
            if line:match '^%S+%s+%S+%s+%S+' then
12✔
124
                cnfg.list_delim = ' '
6✔
125
            end
126
            cnfg.variabilize = false
12✔
127
        end
128
    end
129

130

131
    local function check_cnfg (var,def)
132
        local val = cnfg[var]
768✔
133
        if val == nil then return def else return val end
768✔
134
    end
135

136
    local initial_digits = '^[%d%+%-]'
96✔
137
    local t = {}
96✔
138
    local top_t = t
96✔
139
    local variabilize = check_cnfg ('variabilize',true)
96✔
140
    local list_delim = check_cnfg('list_delim',',')
96✔
141
    local convert_numbers = check_cnfg('convert_numbers',true)
96✔
142
    local convert_boolean = check_cnfg('convert_boolean',false)
96✔
143
    local trim_space = check_cnfg('trim_space',true)
96✔
144
    local trim_quotes = check_cnfg('trim_quotes',false)
96✔
145
    local ignore_assign = check_cnfg('ignore_assign',false)
96✔
146
    local keysep = check_cnfg('keysep','=')
96✔
147
    local keypat = keysep == ' ' and '%s+' or '%s*'..keysep..'%s*'
96✔
148
    if list_delim == ' ' then list_delim = '%s+' end
96✔
149

150
    local function process_name(key)
151
        if variabilize then
246✔
152
            key = key:gsub('[^%w]','_')
192✔
153
        end
154
        return key
246✔
155
    end
156

157
    local function process_value(value)
158
        if list_delim and value:find(list_delim) then
660✔
159
            value = split(value,list_delim)
96✔
160
            for i,v in ipairs(value) do
450✔
161
                value[i] = process_value(v)
504✔
162
            end
163
        elseif convert_numbers and value:find(initial_digits) then
588✔
164
            local val = tonumber(value)
246✔
165
            if not val and value:match ' kB$' then
246✔
166
                value = value:gsub(' kB','')
24✔
167
                val = tonumber(value)
24✔
168
            end
169
            if val then value = val end
246✔
170
        elseif convert_boolean and value == 'true' then
342✔
171
           return true
6✔
172
        elseif convert_boolean and value == 'false' then
336✔
173
           return false
6✔
174
        end
175
        if type(value) == 'string' then
648✔
176
           if trim_space then value = strip(value) end
472✔
177
           if not trim_quotes and auto and value:match '^"' then
354✔
178
                trim_quotes = true
6✔
179
            end
180
           if trim_quotes then value = strip_quotes(value) end
364✔
181
        end
182
        return value
648✔
183
    end
184

185
    while line do
390✔
186
        if line:find('^%[') then -- section!
294✔
187
            local section = process_name(line:match('%[([^%]]+)%]'))
12✔
188
            t = top_t
12✔
189
            t[section] = {}
12✔
190
            t = t[section]
12✔
191
        else
192
            line = line:gsub('^%s*','')
282✔
193
            local i1,i2 = line:find(keypat)
282✔
194
            if i1 and not ignore_assign then -- key,value assignment
282✔
195
                local key = process_name(line:sub(1,i1-1))
312✔
196
                local value = process_value(line:sub(i2+1))
312✔
197
                t[key] = value
234✔
198
            else -- a plain list of values...
199
                t[#t+1] = process_value(line)
64✔
200
            end
201
        end
202
        line = iter()
392✔
203
    end
204
    return top_t
96✔
205
end
206

207
return config
6✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc