libs/uvl: convert uvlc to nixio.fs api, fix error condition when no schema is found
[project/luci.git] / libs / uvl / root / usr / bin / uvl
index b724c8a..e5db5c3 100755 (executable)
@@ -1,3 +1,4 @@
+#!/usr/bin/lua
 --[[
 
 UCI Validation Layer - Command Line Utility
@@ -10,7 +11,7 @@ You may obtain a copy of the License at
 
         http://www.apache.org/licenses/LICENSE-2.0
 
-$Id: uvl.lua 2873 2008-08-17 21:43:56Z jow $
+$Id$
 
 ]]--
 
@@ -56,18 +57,101 @@ function getopt( arg, options )
        return tab, args
 end
 
+function genspec(conf)
+       require("luci.model.uci")
+       require("luci.uvl.datatypes")
+
+       local uci     = luci.model.uci.cursor()
+       local ok, err = uci:load(conf)
+
+       if not ok then
+               print("Can not load config:", err)
+               os.exit(1)
+       else
+               local function _guess_datatype(v)
+                       if type(v) == "table" then v = v[1] end
+
+                       for _, type in ipairs({
+                               "boolean", "integer", "float", "ip4addr", "ip6addr",
+                               "macaddr", "directory", "file"
+                       }) do
+                               if luci.uvl.datatypes[type](v) then
+                                       return type
+                               end
+                       end
+                       return "string"
+               end
+
+
+               local co = uci:get_all(conf)
+               local ct = { }
+               local ca = { }
+               local so = { }
+               local to = { }
+
+               -- count section types
+               for _, section in pairs(co) do
+                       ct[section['.type']] = ( ct[section['.type']] or 0 ) + 1
+                       ca[section['.type']] = section['.anonymous']
+                       so[section['.type']] = so[section['.type']] or { }
+                       to[section['.type']] = to[section['.type']] or { }
+
+                       for option, value in pairs(section) do
+                               if option:sub(1,1) ~= "." then
+                                       so[section['.type']][option] = _guess_datatype(value)
+                                       to[section['.type']][option] = ( type(value) == "table" and "list" or "variable" )
+                               end
+                       end
+               end
+
+               -- package name
+               print( "package %s" % conf )
+
+               -- write section schemes
+               for type, count in luci.util.kspairs(ct) do
+                       print( "\nconfig section" )
+                       print( "\toption name   '%s'" % type )
+                       print( "\toption title  'Section %s'" % type )
+                       print( "\toption package        '%s'"% conf )
+                       print( "\toption named  %s" % ( ca[type] and 'false' or 'true' ) )
+                       print( "\toption unique %s" % ( ct[type] > 1 and 'false' or ( ca[type] and 'false' or 'true' ) ) )
+                       print( "\toption dynamic        false" )
+                       print( "\toption required       false" )
+
+                       -- write option schemes
+                       for opt, val in luci.util.kspairs(so[type]) do
+                               print( "\nconfig variable" )
+                               print( "\toption name   '%s'" % opt )
+                               print( "\toption title  'Option %s'" % opt )
+                               print( "\toption section        '%s.%s'" %{ conf, type } )
+                               print( "\toption datatype       '%s'" % so[type][opt] )
+
+                               if to[type][opt] ~= "variable" then
+                                       print( "\toption type   '%s'" % to[type][opt] )
+                               end
+                       end
+
+                       print("")
+               end
+
+       end
+end
+
+
 local options, arguments = getopt( arg )
 
-if #arguments == 0 or options.help then
+if #arguments ~= 2 or options.help then
        print([=[
+
 uvl - UCI Validation Layer
-$Id: uvl.lua 2873 2008-08-17 21:43:56Z jow $
+$Id$
 (c) 2008 Jo-Philipp Wich, Steven Barth
 
 Usage:
        uvl --help
-       uvl [--silent] [--no-strict-sections] [--no-strict-options]
-               [--no-strict-validators] config[.section[.option]]
+       uvl [--silent] [--schemedir=DIR] [--configdir=DIR] [--no-strict-sections] \
+               [--no-strict-options] [--no-strict-validators] [--no-strict-lists] \
+               {verify|verify-scheme|genspec} config[.section[.option]]
 
 Options:
        --help
@@ -76,6 +160,12 @@ Options:
        --silent
                Don't produce any output.
 
+       --schemedir=DIR
+               Use DIR as scheme directory.
+
+       --configdir=DIR
+               Use DIR as config directory.
+
        --no-strict-sections
                Don't treat sections found in config but not in scheme as error.
 
@@ -83,34 +173,69 @@ Options:
                Don't treat options found in config but not in scheme as error.
 
        --no-strict-validators
-               Don't invalidate config if an external validators fails.
+               Don't invalidate config if an external validator fails.
+
+       --no-strict-lists
+               Don't invalidate lists that are stored options.
+
+Actions:
+       verify
+               Validate given configuration, section or option.
 
+       verify-scheme
+               Validate given scheme against the reference meta scheme.
+
+       genspec
+               Generate a scheme skeleton from given configuration.
        ]=])
        os.exit(255)
-else
+elseif arguments[1] == "verify" or arguments[1] == "verify-scheme" then
        luci.uvl.STRICT_UNKNOWN_SECTIONS =
-               ( options['no-strict-sections'] and false or true )
+               ( not options['no-strict-sections'] and true or false )
        luci.uvl.STRICT_UNKNOWN_OPTIONS =
-               ( options['no-strict-options'] and false or true )
+               ( not options['no-strict-options'] and true or false )
        luci.uvl.STRICT_EXTERNAL_VALIDATORS =
-               ( options['no-strict-validators'] and false or true )
+               ( not options['no-strict-validators'] and true or false )
+       luci.uvl.STRICT_LIST_TYPE =
+               ( not options['no-strict-lists'] and true or false )
+
+       local uvl = luci.uvl.UVL(
+               type(options.schemedir) == "string" and options.schemedir,
+               type(options.configdir) == "string" and options.configdir
+       )
+
+       local cso = luci.util.split( arguments[2], "." )
+       local ok, err
+
+       if arguments[1] == "verify-scheme" then
+               uvl:read_scheme( 'schema', cso[1] )
 
-       local uvl = luci.uvl.UVL( options['schemedir'] )
-       local cso = luci.util.split( arguments[1], "." )
-       local ok, err = uvl:validate( unpack(cso) )
+               local uci = uvl.uci.cursor(
+                       type(options.configdir) == "string"
+                               and options.configdir or uvl.schemedir .. '/default'
+               )
+
+               ok, err = uvl:validate_config( cso[1], uci:get_all(cso[1]) )
+               if err then err.code = luci.uvl.errors.ERR_SCHEME end
+       else
+               ok, err = uvl:validate( unpack(cso) )
+       end
 
        if ok then
                if not options.silent then
                        print( string.format(
-                               '%s "%s.%s.%s" validates fine!',
-                                       ( #cso == 1 and "Config" or
-                                               ( #cso == 2 and "Section" or "Option" ) ),
-                                       cso[1], cso[2], cso[3]
+                               '%s "%s" validates fine!',
+                                       ( arguments[1] == "verify-scheme" and "Scheme" or
+                                               ( #cso == 1 and "Config" or
+                                                       ( #cso == 2 and "Section" or "Option" ) ) ),
+                                       table.concat(cso, ".")
                        ) )
                end
                os.exit( 0 )
        else
-               if not options.silent then print( err ) end
+               if not options.silent then print( err and err:string() or "Unknown error" ) end
                os.exit( 1 )
        end
+else
+       genspec( arguments[2] )
 end