* luci/libs: uvl: first round of uci schemes
[project/luci.git] / libs / uvl / root / usr / bin / uvl
1 #!/usr/bin/lua
2 --[[
3
4 UCI Validation Layer - Command Line Utility
5 (c) 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
6 (c) 2008 Steven Barth <steven@midlink.org>
7
8 Licensed under the Apache License, Version 2.0 (the "License");
9 you may not use this file except in compliance with the License.
10 You may obtain a copy of the License at
11
12         http://www.apache.org/licenses/LICENSE-2.0
13
14 $Id$
15
16 ]]--
17
18 require("luci.uvl")
19 require("luci.util")
20
21
22 function getopt( arg, options )
23         options = options or ""
24         local tab = {}
25         local args = {}
26         for k, v in ipairs(arg) do
27                 if v:sub(1, 2) == "--" then
28                         local x = v:find( "=", 1, true )
29                         if x then
30                                 tab[ v:sub( 3, x-1 ) ] = v:sub( x+1 )
31                         else
32                             tab[ v:sub( 3 ) ] = true
33                         end
34                 elseif v:sub( 1, 1 ) == "-" then
35                         local y = 2
36                         local l = #v
37                         local jopt
38                         while ( y <= l ) do
39                                 jopt = v:sub( y, y )
40                                 if options:find( jopt, 1, true ) then
41                                         if y < l then
42                                                 tab[ jopt ] = v:sub( y+1 )
43                                                 y = l
44                                         else
45                                                 tab[ jopt ] = arg[ k + 1 ]
46                                                 arg[ k + 1 ] = ""
47                                         end
48                                 else
49                                         tab[ jopt ] = true
50                                 end
51                                 y = y + 1
52                         end
53             elseif #v > 0 then
54                 table.insert(args, v)
55             end
56         end
57         return tab, args
58 end
59
60 function genspec(conf)
61         require("luci.model.uci")
62         require("luci.uvl.datatypes")
63
64         local uci     = luci.model.uci.cursor()
65         local ok, err = uci:load(conf)
66
67         if not ok then
68                 print("Can not load config:", err)
69                 os.exit(1)
70         else
71                 local function _guess_datatype(v)
72                         if type(v) == "table" then v = v[1] end
73
74                         for _, type in ipairs({
75                                 "boolean", "integer", "float", "ip4addr", "ip6addr",
76                                 "macaddr", "directory", "file"
77                         }) do
78                                 if luci.uvl.datatypes[type](v) then
79                                         return type
80                                 end
81                         end
82                         return "string"
83                 end
84
85
86                 local co = uci:get_all(conf)
87                 local ct = { }
88                 local ca = { }
89                 local so = { }
90                 local to = { }
91
92                 -- count section types
93                 for _, section in pairs(co) do
94                         ct[section['.type']] = ( ct[section['.type']] or 0 ) + 1
95                         ca[section['.type']] = section['.anonymous']
96                         so[section['.type']] = so[section['.type']] or { }
97                         to[section['.type']] = to[section['.type']] or { }
98
99                         for option, value in pairs(section) do
100                                 if option:sub(1,1) ~= "." then
101                                         so[section['.type']][option] = _guess_datatype(value)
102                                         to[section['.type']][option] = ( type(value) == "table" and "list" or "variable" )
103                                 end
104                         end
105                 end
106
107                 -- package name
108                 print( "package %s" % conf )
109
110                 -- write section schemes
111                 for type, count in luci.util.kspairs(ct) do
112                         print( "\nconfig section" )
113                         print( "\toption name   '%s'" % type )
114                         print( "\toption title  'Section %s'" % type )
115                         print( "\toption package        '%s'"% conf )
116                         print( "\toption named  %s" % ( ca[type] and 'false' or 'true' ) )
117                         print( "\toption unique %s" % ( ct[type] > 1 and 'false' or ( ca[type] and 'false' or 'true' ) ) )
118                         print( "\toption dynamic        false" )
119                         print( "\toption required       false" )
120
121                         -- write option schemes
122                         for opt, val in luci.util.kspairs(so[type]) do
123                                 print( "\nconfig variable" )
124                                 print( "\toption name   '%s'" % opt )
125                                 print( "\toption title  'Option %s'" % opt )
126                                 print( "\toption section        '%s.%s'" %{ conf, type } )
127                                 print( "\toption datatype       '%s'" % so[type][opt] )
128
129                                 if to[type][opt] ~= "variable" then
130                                         print( "\toption type   '%s'" % to[type][opt] )
131                                 end
132                         end
133
134                         print("")
135                 end
136
137         end
138 end
139
140
141 local options, arguments = getopt( arg )
142
143 if #arguments ~= 2 or options.help then
144         print([=[
145
146 uvl - UCI Validation Layer
147 $Id$
148 (c) 2008 Jo-Philipp Wich, Steven Barth
149
150 Usage:
151         uvl --help
152         uvl [--silent] [--schemedir=DIR]
153                 [--no-strict-sections] [--no-strict-options] [--no-strict-validators]
154                 [--no-strict-lists] {verify|genspec} config[.section[.option]]
155
156 Options:
157         --help
158                 Display this help message.
159
160         --silent
161                 Don't produce any output.
162
163         --schemedir=DIR
164                 Use DIR as scheme directory.
165
166         --no-strict-sections
167                 Don't treat sections found in config but not in scheme as error.
168
169         --no-strict-options
170                 Don't treat options found in config but not in scheme as error.
171
172         --no-strict-validators
173                 Don't invalidate config if an external validator fails.
174
175         --no-strict-lists
176                 Don't invalidate lists that are stored options.
177
178 Actions:
179         verify
180                 Validate given configuration, section or option.
181
182         genspec
183                 Generate a scheme skeleton from given configuration.
184         ]=])
185         os.exit(255)
186 elseif arguments[1] == "verify" then
187         luci.uvl.STRICT_UNKNOWN_SECTIONS =
188                 ( options['no-strict-sections'] and false or true )
189         luci.uvl.STRICT_UNKNOWN_OPTIONS =
190                 ( options['no-strict-options'] and false or true )
191         luci.uvl.STRICT_EXTERNAL_VALIDATORS =
192                 ( options['no-strict-validators'] and false or true )
193         luci.uvl.STRICT_LIST_TYPE =
194                 ( options['no-strict-lists'] and false or true )
195
196         local uvl = luci.uvl.UVL(
197                 type(options.schemedir) == "string" and options.schemedir or nil
198         )
199
200         local cso = luci.util.split( arguments[2], "." )
201         local ok, err = uvl:validate( unpack(cso) )
202
203         if ok then
204                 if not options.silent then
205                         print( string.format(
206                                 '%s "%s" validates fine!',
207                                         ( #cso == 1 and "Config" or
208                                                 ( #cso == 2 and "Section" or "Option" ) ),
209                                         table.concat(cso, ".")
210                         ) )
211                 end
212                 os.exit( 0 )
213         else
214                 if not options.silent then print( err and err:string() or "Unknown error" ) end
215                 os.exit( 1 )
216         end
217 else
218         genspec( arguments[2] )
219 end