* luci/libs: uvl: remove a brain-dead unpack()
[project/luci.git] / libs / uvl / luasrc / uvl.lua
1 --[[
2
3 UCI Validation Layer - Main Library
4 (c) 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
5 (c) 2008 Steven Barth <steven@midlink.org>
6
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
10
11         http://www.apache.org/licenses/LICENSE-2.0
12
13 $Id$
14
15 ]]--
16
17
18 --- UVL - UCI Validation Layer
19 -- @class       module
20 -- @cstyle      instance
21
22 module( "luci.uvl", package.seeall )
23
24 require("luci.fs")
25 require("luci.util")
26 require("luci.model.uci")
27 require("luci.uvl.loghelper")
28 require("luci.uvl.datatypes")
29 require("luci.uvl.validation")
30 require("luci.uvl.dependencies")
31
32
33 TYPE_SECTION  = 0x01
34 TYPE_VARIABLE = 0x02
35 TYPE_ENUM     = 0x03
36
37 --- Boolean; default true;
38 -- treat sections found in config but not in scheme as error
39 STRICT_UNKNOWN_SECTIONS    = true
40
41 --- Boolean; default true;
42 -- treat options found in config but not in scheme as error
43 STRICT_UNKNOWN_OPTIONS     = true
44
45 --- Boolean; default true;
46 -- treat failed external validators as error
47 STRICT_EXTERNAL_VALIDATORS = true
48
49 --- Boolean; default true;
50 -- treat list values stored as options like errors
51 STRICT_LIST_TYPE           = true
52
53
54 local default_schemedir = "/lib/uci/schema"
55
56 local function _assert( condition, fmt, ... )
57         if not condition then
58                 return assert( nil, string.format( fmt, ... ) )
59         else
60                 return condition
61         end
62 end
63
64
65 --- Object constructor
66 -- @class                       function
67 -- @name                        UVL
68 -- @param schemedir     Path to the scheme directory (optional)
69 -- @return                      Instance object
70 UVL = luci.util.class()
71
72 function UVL.__init__( self, schemedir )
73         self.schemedir  = schemedir or default_schemedir
74         self.packages   = { }
75         self.beenthere  = { }
76         self.uci                = luci.model.uci
77         self.dep                = luci.uvl.dependencies
78         self.log        = luci.uvl.loghelper
79         self.datatypes  = luci.uvl.datatypes
80 end
81
82
83 --- Parse given scheme and return the scheme tree.
84 -- @param scheme        Name of the scheme to parse
85 -- @return                      Table containing the parsed scheme or nil on error
86 -- @return                      String containing the reason for errors (if any)
87 function UVL.get_scheme( self, scheme )
88         if not self.packages[scheme] then
89                 local ok, err = pcall( self.read_scheme, self, scheme )
90                 if not ok then
91                         return nil, self.log.scheme_error( scheme, err )
92                 end
93         end
94         return self.packages[scheme], nil
95 end
96
97 --- Return a table containing the dependencies of specified section or option.
98 -- @param config        Name of the configuration or parsed scheme object
99 -- @param section       Type of the section
100 -- @param option        Name of the option (optional)
101 -- @return                      Table containing the dependencies or nil on error
102 -- @return                      String containing the reason for errors (if any)
103 function UVL.get_dependencies( self, config, section, option )
104         config = ( type(config) == "string" and self:get_scheme(config) or config )
105
106         local deps = { }
107         local dt
108
109         if not config.sections[section] then return deps end
110
111         if option and config.variables[section][option] then
112                 dt = config.variables[section][option].depends
113         else
114                 dt = config.sections[section].depends
115         end
116
117         if dt then
118                 for _, d in ipairs(dt) do
119                         local sdeps = { }
120                         for k, v in pairs(d) do
121                                 local r = self.dep._parse_reference( k )
122                                 if r then
123                                         sdeps[r] = v
124                                 else
125                                         return nil,
126                                                 'Ambiguous dependency reference "%s" for object "%s" given'
127                                                         %{ k, self.log.id( config.name, section, option ) }
128                                 end
129                         end
130                         table.insert( deps, sdeps )
131                 end
132         end
133         return deps
134 end
135
136 --- Validate given configuration, section or option.
137 -- @param config        Name of the configuration to validate
138 -- @param section       Name of the section to validate (optional)
139 -- @param option        Name of the option to validate (optional)
140 -- @return                      Boolean indicating whether the given config validates
141 -- @return                      String containing the reason for errors (if any)
142 function UVL.validate( self, config, section, option )
143         if config and section and option then
144                 return self:validate_option( config, section, option )
145         elseif config and section then
146                 return self:validate_section( config, section )
147         elseif config then
148                 return self:validate_config( config )
149         end
150 end
151
152 --- Validate given configuration.
153 -- @param config        Name of the configuration to validate
154 -- @return                      Boolean indicating whether the given config validates
155 -- @return                      String containing the reason for errors (if any)
156 function UVL.validate_config( self, config )
157
158         if not self.packages[config] then
159                 local ok, err = pcall( self.read_scheme, self, config )
160                 if not ok then
161                         return false, self.log.scheme_error( config, err )
162                 end
163         end
164
165         self.uci.load_config( config )
166         self.beenthere = { }
167
168         local co = self.uci.get_all( config )
169         local sc = { }
170
171         if not co then
172                 return false, 'Unable to load configuration "%s"' % config
173         end
174
175         local function _uci_foreach( type, func )
176                 local ok, err
177                 for k, v in pairs(co) do
178                         if co[k]['.type'] == type then
179                                 sc[type] = sc[type] + 1
180                                 ok, err = func( k, v )
181                                 if not ok then
182                                         err = self.log.config_error( config, err )
183                                         break
184                                 end
185                         end
186                 end
187                 return ok, err
188         end
189
190         for k, v in pairs( self.packages[config].sections ) do
191                 sc[k] = 0
192                 local ok, err = _uci_foreach( k,
193                         function(s)
194                                 local sect = luci.uvl.section( self, co, k, config, s )
195                                 return self:_validate_section( sect )
196                         end
197                 )
198                 if not ok then return false, err end
199         end
200
201         if STRICT_UNKNOWN_SECTIONS then
202                 for k, v in pairs(co) do
203                         if not self.beenthere[config..'.'..k] then
204                                 return false, self.log.config_error( config,
205                                         'Section "%s" not found in scheme'
206                                                 % self.log.id( config, co[k]['.type'] ) )
207                         end
208                 end
209         end
210
211         for _, k in ipairs(luci.util.keys(sc)) do
212                 local s = self.packages[config].sections[k]
213
214                 if s.required and sc[k] == 0 then
215                         return false, self.log.config_error( config,
216                                 'Required section "%s" not found in config' % k )
217                 elseif s.unique and sc[k] > 1 then
218                         return false, self.log.config_error( config,
219                                 'Unique section "%s" occurs multiple times in config' % k )
220                 end
221         end
222
223         return true, nil
224 end
225
226 --- Validate given config section.
227 -- @param config        Name of the configuration to validate
228 -- @param section       Name of the section to validate
229 -- @return                      Boolean indicating whether the given config validates
230 -- @return                      String containing the reason for errors (if any)
231 function UVL.validate_section( self, config, section )
232
233         if not self.packages[config] then
234                 local ok, err = pcall( self.read_scheme, self, config )
235                 if not ok then
236                         return false, self.log.scheme_error( config, err )
237                 end
238         end
239
240         self.uci.load_config( config )
241         self.beenthere = { }
242
243         local co = self.uci.get_all( config )
244
245         if not co then
246                 return false, 'Unable to load configuration "%s"' % config
247         end
248
249         if co[section] then
250                 return self:_validate_section( luci.uvl.section(
251                         self, co, co[section]['.type'], config, section
252                 ) )
253         else
254                 return false, 'Section "%s" not found in config. Nothing to do.'
255                         % self.log.id( config, section )
256         end
257 end
258
259 --- Validate given config option.
260 -- @param config        Name of the configuration to validate
261 -- @param section       Name of the section to validate
262 -- @param option        Name of the option to validate
263 -- @return                      Boolean indicating whether the given config validates
264 -- @return                      String containing the reason for errors (if any)
265 function UVL.validate_option( self, config, section, option )
266
267         if not self.packages[config] then
268                 local ok, err = pcall( self.read_scheme, self, config )
269                 if not ok then
270                         return false, self.log.scheme_error( config, err )
271                 end
272         end
273
274         self.uci.load_config( config )
275         self.beenthere = { }
276
277         local co = self.uci.get_all( config )
278
279         if not co then
280                 return false, 'Unable to load configuration "%s"' % config
281         end
282
283         if co[section] and co[section][option] then
284                 return self:_validate_option( luci.uvl.option(
285                         self, co, co[section]['.type'], config, section, option
286                 ) )
287         else
288                 return false, 'Option "%s" not found in config. Nothing to do.'
289                         % self.log.id( config, section, option )
290         end
291 end
292
293
294 function UVL._validate_section( self, section )
295
296         if section:values() then
297                 if section:section().named == true and
298                    section:values()['.anonymous'] == true
299                 then
300                         return false, self.log.section_error( section,
301                                 'The section of type "%s" is stored anonymously in config but must be named'
302                                         % section:sid() )
303                 end
304
305                 for _, v in ipairs(section:variables()) do
306                         local ok, err = self:_validate_option( v )
307
308                         if not ok then
309                                 return ok, self.log.section_error( section, err )
310                         end
311                 end
312
313                 local ok, err = luci.uvl.dependencies.check( self, section )
314
315                 if not ok then
316                         return false, err
317                 end
318         else
319                 return false, 'Option "%s" not found in config' % section:sid()
320         end
321
322         if STRICT_UNKNOWN_OPTIONS and not section:section().dynamic then
323                 for k, v in pairs(section:values()) do
324                         if k:sub(1,1) ~= "." and not self.beenthere[
325                                 section:cid() .. '.' .. k
326                         ] then
327                                 return false, 'Option "%s" not found in scheme'
328                                         % self.log.id( section:sid(), k )
329                         end
330                 end
331         end
332
333         return true, nil
334 end
335
336 function UVL._validate_option( self, option, nodeps )
337
338         local item = option:option()
339         local val  = option:value()
340
341         if not item and not ( option:section() and option:section().dynamic ) then
342                 return false, 'Option "%s" not found in scheme' % option:cid()
343
344         elseif item then
345                 if item.required and not val then
346                         return false, 'Mandatory variable "%s" does not have a value'
347                                 % option:cid()
348                 end
349
350                 if item.type == "enum" and val then
351                         if not item.values or not item.values[val] then
352                                 return false,
353                                         'Value "%s" of given option "%s" is not defined in enum { %s }'
354                                                 %{ val or '<nil>', option:cid(),
355                                                    table.concat( luci.util.keys(item.values), ", " ) }
356                         end
357                 elseif item.type == "list" and val then
358                         if type(val) ~= "table" and STRICT_LIST_TYPE then
359                                 return false,
360                                         'Option "%s" is defined as list but stored as plain value'
361                                                 % option:cid()
362                         end
363                 end
364
365                 if item.datatype and val then
366                         if self.datatypes[item.datatype] then
367                                 val = ( type(val) == "table" and val or { val } )
368                                 for i, v in ipairs(val) do
369                                         if not self.datatypes[item.datatype]( v ) then
370                                                 return false,
371                                                         'Value%s "%s" of given option "%s" does not validate as datatype "%s"'
372                                                                 %{ ( #val>1 and ' #' .. i or '' ), v,
373                                                                    option:cid(), item.datatype }
374                                         end
375                                 end
376                         else
377                                 return false, 'Unknown datatype "%s" encountered'
378                                         % item.datatype
379                         end
380                 end
381
382                 if not nodeps then
383                         return luci.uvl.dependencies.check( self, option )
384                 end
385
386                 local ok, err = luci.uvl.validation.check( self, option )
387                 if not ok and STRICT_EXTERNAL_VALIDATORS then
388                         return false, self.log.validator_error( option, err )
389                 end
390         end
391
392         return true, nil
393 end
394
395 --- Find all parts of given scheme and construct validation tree.
396 -- This is normally done on demand, so you don't have to call this function
397 -- by yourself.
398 -- @param scheme        Name of the scheme to parse
399 function UVL.read_scheme( self, scheme )
400         local schemes = { }
401         local files = luci.fs.glob(self.schemedir .. '/*/' .. scheme)
402
403         if files then
404                 for i, file in ipairs( files ) do
405                         _assert( luci.fs.access(file), "Can't access file '%s'", file )
406
407                         self.uci.set_confdir( luci.fs.dirname(file) )
408                         self.uci.load( luci.fs.basename(file) )
409
410                         table.insert( schemes, self.uci.get_all( luci.fs.basename(file) ) )
411                 end
412
413                 return self:_read_scheme_parts( scheme, schemes )
414         else
415                 error( 'Can not find scheme "%s" in "%s"' %{ scheme, self.schemedir } )
416         end
417 end
418
419 -- Process all given parts and construct validation tree
420 function UVL._read_scheme_parts( self, scheme, schemes )
421
422         -- helper function to construct identifiers for given elements
423         local function _id( c, t )
424                 if c == TYPE_SECTION then
425                         return string.format(
426                                 'section "%s.%s"',
427                                         scheme, t.name or '?' )
428                 elseif c == TYPE_VARIABLE then
429                         return string.format(
430                                 'variable "%s.%s.%s"',
431                                         scheme, t.section or '?.?', t.name or '?' )
432                 elseif c == TYPE_ENUM then
433                         return string.format(
434                                 'enum "%s.%s.%s"',
435                                         scheme, t.variable or '?.?.?', t.value or '?' )
436                 end
437         end
438
439         -- helper function to check for required fields
440         local function _req( c, t, r )
441                 for i, v in ipairs(r) do
442                         _assert( t[v], 'Missing required field "%s" in %s', v, _id(c, t) )
443                 end
444         end
445
446         -- helper function to validate references
447         local function _ref( c, t )
448                 local k
449                 if c == TYPE_SECTION then
450                         k = "package"
451                 elseif c == TYPE_VARIABLE then
452                         k = "section"
453                 elseif c == TYPE_ENUM then
454                         k = "variable"
455                 end
456
457                 local r = luci.util.split( t[k], "." )
458                 r[1] = ( #r[1] > 0 and r[1] or scheme )
459
460                 _assert( #r == c, 'Malformed %s reference in %s', k, _id(c, t) )
461
462                 return r
463         end
464
465         -- helper function to read bools
466         local function _bool( v )
467                 return ( v == "true" or v == "yes" or v == "on" or v == "1" )
468         end
469
470         -- Step 1: get all sections
471         for i, conf in ipairs( schemes ) do
472                 for k, v in pairs( conf ) do
473                         if v['.type'] == 'section' then
474
475                                 _req( TYPE_SECTION, v, { "name", "package" } )
476
477                                 local r = _ref( TYPE_SECTION, v )
478
479                                 self.packages[r[1]] =
480                                         self.packages[r[1]] or {
481                                                 ["name"]      = r[1];
482                                                 ["sections"]  = { };
483                                                 ["variables"] = { };
484                                         }
485
486                                 local p = self.packages[r[1]]
487                                           p.sections[v.name]  = p.sections[v.name]  or { }
488                                           p.variables[v.name] = p.variables[v.name] or { }
489
490                                 local s = p.sections[v.name]
491
492                                 for k, v2 in pairs(v) do
493                                         if k ~= "name" and k ~= "package" and k:sub(1,1) ~= "." then
494                                                 if k == "depends" then
495                                                         s["depends"] = _assert(
496                                                                 self:_read_dependency( v2, s["depends"] ),
497                                                                 'Section "%s" in scheme "%s" has malformed ' ..
498                                                                 'dependency specification in "%s"',
499                                                                 v.name or '<nil>', scheme or '<nil>', k
500                                                         )
501                                                 elseif k == "dynamic" or k == "unique" or
502                                                        k == "required" or k == "named"
503                                                 then
504                                                         s[k] = _bool(v2)
505                                                 else
506                                                         s[k] = v2
507                                                 end
508                                         end
509                                 end
510
511                                 s.dynamic  = s.dynamic  or false
512                                 s.unique   = s.unique   or false
513                                 s.required = s.required or false
514                                 s.named    = s.named    or false
515                         end
516                 end
517         end
518
519         -- Step 2: get all variables
520         for i, conf in ipairs( schemes ) do
521                 for k, v in pairs( conf ) do
522                         if v['.type'] == "variable" then
523
524                                 _req( TYPE_VARIABLE, v, { "name", "section" } )
525
526                                 local r = _ref( TYPE_VARIABLE, v )
527
528                                 local p = _assert( self.packages[r[1]],
529                                         'Variable "%s" in scheme "%s" references unknown package "%s"',
530                                         v.name, scheme, r[1] )
531
532                                 local s = _assert( p.variables[r[2]],
533                                         'Variable "%s" in scheme "%s" references unknown section "%s"',
534                                         v.name, scheme, r[2] )
535
536                                 s[v.name] = s[v.name] or { }
537
538                                 local t = s[v.name]
539
540                                 for k, v2 in pairs(v) do
541                                         if k ~= "name" and k ~= "section" and k:sub(1,1) ~= "." then
542                                                 if k == "depends" then
543                                                         t["depends"] = _assert(
544                                                                 self:_read_dependency( v2, t["depends"] ),
545                                                                 'Invalid reference "%s" in "%s.%s.%s"',
546                                                                 v2, v.name, scheme, k
547                                                         )
548                                                 elseif k == "validator" then
549                                                         t["validators"] = _assert(
550                                                                 self:_read_validator( v2, t["validators"] ),
551                                                                 'Variable "%s" in scheme "%s" has malformed ' ..
552                                                                 'validator specification in "%s"',
553                                                                 v.name, scheme, k
554                                                         )
555                                                 elseif k == "required" then
556                                                         t[k] = _bool(v2)
557                                                 else
558                                                         t[k] = v2
559                                                 end
560                                         end
561                                 end
562
563                                 t.type     = t.type     or "variable"
564                                 t.datatype = t.datatype or "string"
565                                 t.required = t.required or false
566                         end
567                 end
568         end
569
570         -- Step 3: get all enums
571         for i, conf in ipairs( schemes ) do
572                 for k, v in pairs( conf ) do
573                         if v['.type'] == "enum" then
574
575                                 _req( TYPE_ENUM, v, { "value", "variable" } )
576
577                                 local r = _ref( TYPE_ENUM, v )
578                                 local p = _assert( self.packages[r[1]],
579                                         'Enum "%s" in scheme "%s" references unknown package "%s"',
580                                         v.value, scheme, r[1] )
581
582                                 local s = _assert( p.variables[r[2]],
583                                         'Enum "%s" in scheme "%s" references unknown section "%s"',
584                                         v.value, scheme, r[2] )
585
586                                 local t = _assert( s[r[3]],
587                                         'Enum "%s" in scheme "%s", section "%s" references ' ..
588                                         'unknown variable "%s"',
589                                         v.value, scheme, r[2], r[3] )
590
591                                 _assert( t.type == "enum",
592                                         'Enum "%s" in scheme "%s", section "%s" references ' ..
593                                         'variable "%s" with non enum type "%s"',
594                                         v.value, scheme, r[2], r[3], t.type )
595
596                                 if not t.values then
597                                         t.values = { [v.value] = v.title or v.value }
598                                 else
599                                         t.values[v.value] = v.title or v.value
600                                 end
601
602                                 if v.default then
603                                         _assert( not t.default,
604                                                 'Enum "%s" in scheme "%s", section "%s" redeclares ' ..
605                                                 'the default value of variable "%s"',
606                                                 v.value, scheme, r[2], v.variable )
607
608                                         t.default = v.value
609                                 end
610                         end
611                 end
612         end
613
614         return self
615 end
616
617 -- Read a dependency specification
618 function UVL._read_dependency( self, values, deps )
619         local expr = "%$?[a-zA-Z0-9_]+"
620         if values then
621                 values = ( type(values) == "table" and values or { values } )
622                 for _, value in ipairs(values) do
623                         local parts     = luci.util.split( value, "%s*,%s*", nil, true )
624                         local condition = { }
625                         for i, val in ipairs(parts) do
626                                 local k, v = unpack(luci.util.split(val, "%s*=%s*", nil, true))
627
628                                 if k and (
629                                         k:match("^"..expr.."%."..expr.."%."..expr.."$") or
630                                         k:match("^"..expr.."%."..expr.."$") or
631                                         k:match("^"..expr.."$")
632                                 ) then
633                                         condition[k] = v or true
634                                 else
635                                         return nil
636                                 end
637                         end
638
639                         if not deps then
640                                 deps = { condition }
641                         else
642                                 table.insert( deps, condition )
643                         end
644                 end
645         end
646
647         return deps
648 end
649
650 -- Read a validator specification
651 function UVL._read_validator( self, values, validators )
652         if values then
653                 values = ( type(values) == "table" and values or { values } )
654                 for _, value in ipairs(values) do
655                         local validator
656
657                         if value:match("^exec:") then
658                                 validator = value:gsub("^exec:","")
659                         elseif value:match("^lua:") then
660                                 validator = self:_resolve_function( (value:gsub("^lua:","") ) )
661                         end
662
663                         if validator then
664                                 if not validators then
665                                         validators = { validator }
666                                 else
667                                         table.insert( validators, validator )
668                                 end
669                         else
670                                 return nil
671                         end
672                 end
673
674                 return validators
675         end
676 end
677
678 -- Resolve given path
679 function UVL._resolve_function( self, value )
680         local path = luci.util.split(value, ".")
681
682         for i=1, #path-1 do
683                 local stat, mod = pcall(require, table.concat(path, ".", 1, i))
684                 if stat and mod then
685                         for j=i+1, #path-1 do
686                                 if not type(mod) == "table" then
687                                         break
688                                 end
689                                 mod = mod[path[j]]
690                                 if not mod then
691                                         break
692                                 end
693                         end
694                         mod = type(mod) == "table" and mod[path[#path]] or nil
695                         if type(mod) == "function" then
696                                 return mod
697                         end
698                 end
699         end
700 end
701
702
703 --- Object representation of a scheme/config section.
704 -- @class       module
705 -- @cstyle      instance
706 -- @name        luci.uvl.section
707
708 --- Section instance constructor.
709 -- @class                       function
710 -- @name                        section
711 -- @param scheme        Scheme instance
712 -- @param co            Configuration data
713 -- @param st            Section type
714 -- @param c                     Configuration name
715 -- @param s                     Section name
716 -- @return                      Section instance
717 section = luci.util.class()
718
719 function section.__init__(self, scheme, co, st, c, s)
720         self.csection = co[s]
721         self.ssection = scheme.packages[c].sections[st]
722         self.cref     = { c, s }
723         self.sref     = { c, st }
724         self.scheme   = scheme
725         self.config   = co
726         self.type     = luci.uvl.TYPE_SECTION
727 end
728
729 --- Get the config path of this section.
730 -- @return      String containing the identifier
731 function section.cid(self)
732         return ( self.cref[1] or '?' ) .. '.' .. ( self.cref[2] or '?' )
733 end
734
735 --- Get the scheme path of this section.
736 -- @return      String containing the identifier
737 function section.sid(self)
738         return ( self.sref[1] or '?' ) .. '.' .. ( self.sref[2] or '?' )
739 end
740
741 --- Get all configuration values within this section.
742 -- @return      Table containing the values
743 function section.values(self)
744         return self.csection
745 end
746
747 --- Get the associated section information in scheme.
748 -- @return      Table containing the scheme properties
749 function section.section(self)
750         return self.ssection
751 end
752
753 --- Get all option objects associated with this section.
754 -- @return      Table containing all associated luci.uvl.option instances
755 function section.variables(self)
756         local v = { }
757         if self.scheme.packages[self.sref[1]].variables[self.sref[2]] then
758                 for o, _ in pairs(
759                         self.scheme.packages[self.sref[1]].variables[self.sref[2]]
760                 ) do
761                         table.insert( v, luci.uvl.option(
762                                 self.scheme, self.config, self.sref[2],
763                                 self.cref[1], self.cref[2], o
764                         ) )
765                 end
766         end
767         return v
768 end
769
770
771 --- Object representation of a scheme/config option.
772 -- @class       module
773 -- @cstyle      instance
774 -- @name        luci.uvl.option
775
776 --- Section instance constructor.
777 -- @class                       function
778 -- @name                        option
779 -- @param scheme        Scheme instance
780 -- @param co            Configuration data
781 -- @param st            Section type
782 -- @param c                     Configuration name
783 -- @param s                     Section name
784 -- @param o                     Option name
785 -- @return                      Option instance
786 option = luci.util.class()
787
788 function option.__init__(self, scheme, co, st, c, s, o)
789         self.coption = co[s] and co[s][o] or nil
790         self.soption = scheme.packages[c].variables[st][o]
791         self.cref    = { c, s, o }
792         self.sref    = { c, st, o }
793         self.scheme  = scheme
794         self.config  = co
795         self.type    = luci.uvl.TYPE_OPTION
796 end
797
798 --- Get the config path of this option.
799 -- @return      String containing the identifier
800 function option.cid(self)
801         return ( self.cref[1] or '?' ) .. '.' ..
802                    ( self.cref[2] or '?' ) .. '.' ..
803                    ( self.cref[3] or '?' )
804 end
805
806 --- Get the scheme path of this option.
807 -- @return      String containing the identifier
808 function option.sid(self)
809         return ( self.sref[1] or '?' ) .. '.' ..
810                    ( self.sref[2] or '?' ) .. '.' ..
811                    ( self.sref[3] or '?' )
812 end
813
814 --- Get the value of this option.
815 -- @return      The associated configuration value
816 function option.value(self)
817         return self.coption
818 end
819
820 --- Get the associated option information in scheme.
821 -- @return      Table containing the scheme properties
822 function option.option(self)
823         return self.soption
824 end
825
826 --- Get the associated section information in scheme.
827 -- @return      Table containing the scheme properties
828 function option.section(self)
829         return self.scheme.packages[self.sref[1]].sections[self.sref[2]]
830 end