cc1d8b9d6dfe2d32d19c0bd1e949e30d1f49ce6a
[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 == "reference" or 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 %s { %s }'
354 %{ val or '<nil>', option:cid(), item.type,
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 == "valueof" then
556 local values, err = self:_read_reference( v2 )
557
558 _assert( values,
559 'Variable "%s" in scheme "%s" has invalid ' ..
560 'reference specification:\n%s',
561 v.name, scheme, err )
562
563 t.type = "reference"
564 t.values = values
565 elseif k == "required" then
566 t[k] = _bool(v2)
567 else
568 t[k] = t[k] or v2
569 end
570 end
571 end
572
573 t.type = t.type or "variable"
574 t.datatype = t.datatype or "string"
575 t.required = t.required or false
576 end
577 end
578 end
579
580 -- Step 3: get all enums
581 for i, conf in ipairs( schemes ) do
582 for k, v in pairs( conf ) do
583 if v['.type'] == "enum" then
584
585 _req( TYPE_ENUM, v, { "value", "variable" } )
586
587 local r = _ref( TYPE_ENUM, v )
588 local p = _assert( self.packages[r[1]],
589 'Enum "%s" in scheme "%s" references unknown package "%s"',
590 v.value, scheme, r[1] )
591
592 local s = _assert( p.variables[r[2]],
593 'Enum "%s" in scheme "%s" references unknown section "%s"',
594 v.value, scheme, r[2] )
595
596 local t = _assert( s[r[3]],
597 'Enum "%s" in scheme "%s", section "%s" references ' ..
598 'unknown variable "%s"',
599 v.value, scheme, r[2], r[3] )
600
601 _assert( t.type == "enum",
602 'Enum "%s" in scheme "%s", section "%s" references ' ..
603 'variable "%s" with non enum type "%s"',
604 v.value, scheme, r[2], r[3], t.type )
605
606 if not t.values then
607 t.values = { [v.value] = v.title or v.value }
608 else
609 t.values[v.value] = v.title or v.value
610 end
611
612 if v.default then
613 _assert( not t.default,
614 'Enum "%s" in scheme "%s", section "%s" redeclares ' ..
615 'the default value of variable "%s"',
616 v.value, scheme, r[2], v.variable )
617
618 t.default = v.value
619 end
620 end
621 end
622 end
623
624 return self
625 end
626
627 -- Read a dependency specification
628 function UVL._read_dependency( self, values, deps )
629 local expr = "%$?[a-zA-Z0-9_]+"
630 if values then
631 values = ( type(values) == "table" and values or { values } )
632 for _, value in ipairs(values) do
633 local parts = luci.util.split( value, "%s*,%s*", nil, true )
634 local condition = { }
635 for i, val in ipairs(parts) do
636 local k, v = unpack(luci.util.split(val, "%s*=%s*", nil, true))
637
638 if k and (
639 k:match("^"..expr.."%."..expr.."%."..expr.."$") or
640 k:match("^"..expr.."%."..expr.."$") or
641 k:match("^"..expr.."$")
642 ) then
643 condition[k] = v or true
644 else
645 return nil
646 end
647 end
648
649 if not deps then
650 deps = { condition }
651 else
652 table.insert( deps, condition )
653 end
654 end
655 end
656
657 return deps
658 end
659
660 -- Read a validator specification
661 function UVL._read_validator( self, values, validators )
662 if values then
663 values = ( type(values) == "table" and values or { values } )
664 for _, value in ipairs(values) do
665 local validator
666
667 if value:match("^exec:") then
668 validator = value:gsub("^exec:","")
669 elseif value:match("^lua:") then
670 validator = self:_resolve_function( (value:gsub("^lua:","") ) )
671 end
672
673 if validator then
674 if not validators then
675 validators = { validator }
676 else
677 table.insert( validators, validator )
678 end
679 else
680 return nil
681 end
682 end
683
684 return validators
685 end
686 end
687
688 -- Read a reference specification (XXX: We should validate external configs too...)
689 function UVL._read_reference( self, values )
690 local val = { }
691 values = ( type(values) == "table" and values or { values } )
692
693 for _, value in ipairs(values) do
694 local ref = luci.util.split(value, ".")
695
696 if #ref == 2 or #ref == 3 then
697 self.uci.load_config(ref[1])
698 local co = self.uci.get_all(ref[1])
699
700 if not co then
701 return nil, 'Can not load config "%s" for reference "%s"'
702 %{ ref[1], value }
703 end
704
705 for k, v in pairs(co) do
706 if v['.type'] == ref[2] then
707 if #ref == 2 then
708 if v['.anonymous'] == true then
709 return nil, 'Illegal reference "%s" to an anonymous section'
710 % value
711 end
712 table.insert( val, k )
713 elseif v[ref[3]] then
714 table.insert( val, v[ref[3]] )
715 end
716 end
717 end
718 else
719 return nil, 'Malformed reference "%s"' % value
720 end
721 end
722
723 return val, nil
724 end
725
726 -- Resolve given path
727 function UVL._resolve_function( self, value )
728 local path = luci.util.split(value, ".")
729
730 for i=1, #path-1 do
731 local stat, mod = pcall(require, table.concat(path, ".", 1, i))
732 if stat and mod then
733 for j=i+1, #path-1 do
734 if not type(mod) == "table" then
735 break
736 end
737 mod = mod[path[j]]
738 if not mod then
739 break
740 end
741 end
742 mod = type(mod) == "table" and mod[path[#path]] or nil
743 if type(mod) == "function" then
744 return mod
745 end
746 end
747 end
748 end
749
750
751 --- Object representation of a scheme/config section.
752 -- @class module
753 -- @cstyle instance
754 -- @name luci.uvl.section
755
756 --- Section instance constructor.
757 -- @class function
758 -- @name section
759 -- @param scheme Scheme instance
760 -- @param co Configuration data
761 -- @param st Section type
762 -- @param c Configuration name
763 -- @param s Section name
764 -- @return Section instance
765 section = luci.util.class()
766
767 function section.__init__(self, scheme, co, st, c, s)
768 self.csection = co[s]
769 self.ssection = scheme.packages[c].sections[st]
770 self.cref = { c, s }
771 self.sref = { c, st }
772 self.scheme = scheme
773 self.config = co
774 self.type = luci.uvl.TYPE_SECTION
775 end
776
777 --- Get the config path of this section.
778 -- @return String containing the identifier
779 function section.cid(self)
780 return ( self.cref[1] or '?' ) .. '.' .. ( self.cref[2] or '?' )
781 end
782
783 --- Get the scheme path of this section.
784 -- @return String containing the identifier
785 function section.sid(self)
786 return ( self.sref[1] or '?' ) .. '.' .. ( self.sref[2] or '?' )
787 end
788
789 --- Get all configuration values within this section.
790 -- @return Table containing the values
791 function section.values(self)
792 return self.csection
793 end
794
795 --- Get the associated section information in scheme.
796 -- @return Table containing the scheme properties
797 function section.section(self)
798 return self.ssection
799 end
800
801 --- Get all option objects associated with this section.
802 -- @return Table containing all associated luci.uvl.option instances
803 function section.variables(self)
804 local v = { }
805 if self.scheme.packages[self.sref[1]].variables[self.sref[2]] then
806 for o, _ in pairs(
807 self.scheme.packages[self.sref[1]].variables[self.sref[2]]
808 ) do
809 table.insert( v, luci.uvl.option(
810 self.scheme, self.config, self.sref[2],
811 self.cref[1], self.cref[2], o
812 ) )
813 end
814 end
815 return v
816 end
817
818
819 --- Object representation of a scheme/config option.
820 -- @class module
821 -- @cstyle instance
822 -- @name luci.uvl.option
823
824 --- Section instance constructor.
825 -- @class function
826 -- @name option
827 -- @param scheme Scheme instance
828 -- @param co Configuration data
829 -- @param st Section type
830 -- @param c Configuration name
831 -- @param s Section name
832 -- @param o Option name
833 -- @return Option instance
834 option = luci.util.class()
835
836 function option.__init__(self, scheme, co, st, c, s, o)
837 self.coption = co[s] and co[s][o] or nil
838 self.soption = scheme.packages[c].variables[st][o]
839 self.cref = { c, s, o }
840 self.sref = { c, st, o }
841 self.scheme = scheme
842 self.config = co
843 self.type = luci.uvl.TYPE_OPTION
844 end
845
846 --- Get the config path of this option.
847 -- @return String containing the identifier
848 function option.cid(self)
849 return ( self.cref[1] or '?' ) .. '.' ..
850 ( self.cref[2] or '?' ) .. '.' ..
851 ( self.cref[3] or '?' )
852 end
853
854 --- Get the scheme path of this option.
855 -- @return String containing the identifier
856 function option.sid(self)
857 return ( self.sref[1] or '?' ) .. '.' ..
858 ( self.sref[2] or '?' ) .. '.' ..
859 ( self.sref[3] or '?' )
860 end
861
862 --- Get the value of this option.
863 -- @return The associated configuration value
864 function option.value(self)
865 return self.coption
866 end
867
868 --- Get the associated option information in scheme.
869 -- @return Table containing the scheme properties
870 function option.option(self)
871 return self.soption
872 end
873
874 --- Get the associated section information in scheme.
875 -- @return Table containing the scheme properties
876 function option.section(self)
877 return self.scheme.packages[self.sref[1]].sections[self.sref[2]]
878 end