View source with formatted comments or as raw
    1/*  Part of SWI-Prolog
    2
    3    Author:        Jan Wielemaker
    4    E-mail:        J.Wielemaker@vu.nl
    5    WWW:           http://www.swi-prolog.org
    6    Copyright (c)  2007-2016, University of Amsterdam
    7                              VU University Amsterdam
    8    All rights reserved.
    9
   10    Redistribution and use in source and binary forms, with or without
   11    modification, are permitted provided that the following conditions
   12    are met:
   13
   14    1. Redistributions of source code must retain the above copyright
   15       notice, this list of conditions and the following disclaimer.
   16
   17    2. Redistributions in binary form must reproduce the above copyright
   18       notice, this list of conditions and the following disclaimer in
   19       the documentation and/or other materials provided with the
   20       distribution.
   21
   22    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   23    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   24    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   25    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   26    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   27    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   28    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   29    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   30    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   31    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   32    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   33    POSSIBILITY OF SUCH DAMAGE.
   34*/
   35
   36:- module(settings,
   37          [ setting/4,                  % :Name, +Type, +Default, +Comment
   38            setting/2,                  % :Name, ?Value
   39            set_setting/2,              % :Name, +Value
   40            set_setting_default/2,      % :Name, +Value
   41            restore_setting/1,          % :Name
   42            load_settings/1,            % +File
   43            load_settings/2,            % +File, +Options
   44            save_settings/0,
   45            save_settings/1,            % +File
   46            current_setting/1,          % Module:Name
   47            setting_property/2,         % ?Setting, ?Property
   48            list_settings/0,
   49            list_settings/1,            % +Module
   50
   51            convert_setting_text/3      % +Type, +Text, -Value
   52          ]).   53:- use_module(library(error)).   54:- use_module(library(broadcast)).   55:- use_module(library(debug)).   56:- use_module(library(option)).   57:- use_module(library(arithmetic)).   58:- set_prolog_flag(generate_debug_info, false).   59
   60/** <module> Setting management
   61
   62This library allows management  of   configuration  settings  for Prolog
   63applications. Applications define settings  in   one  or  multiple files
   64using the directive setting/4 as illustrated below:
   65
   66==
   67:- use_module(library(settings)).
   68
   69:- setting(version, atom,   '1.0', 'Current version').
   70:- setting(timeout, number,    20, 'Timeout in seconds').
   71==
   72
   73The directive is subject to   term_expansion/2,  which guarantees proper
   74synchronisation of the  database  if   source-files  are  reloaded. This
   75implies it is *not* possible to call setting/4 as a predicate.
   76
   77Settings are local to a  module.  This   implies  they  are defined in a
   78two-level namespace. Managing settings  per   module  greatly simplifies
   79assembling large applications from multiple   modules that configuration
   80through  settings.  This  settings  management  library  ensures  proper
   81access, loading and saving of settings.
   82
   83@see    library(config) distributed with XPCE provides an alternative
   84        aimed at graphical applications.
   85@author Jan Wielemaker
   86*/
   87
   88:- dynamic
   89    st_value/3,                     % Name, Module, Value
   90    st_default/3,                   % Name, Module, Value
   91    local_file/1.                   % Path
   92
   93:- multifile
   94    current_setting/6.              % Name, Module, Type, Default, Comment, Source
   95
   96:- meta_predicate
   97    setting(:, +, +, +),
   98    setting(:, ?),
   99    set_setting(:, +),
  100    set_setting_default(:, +),
  101    current_setting(:),
  102    restore_setting(:).  103
  104:- predicate_options(load_settings/2, 2, [undefined(oneof([load,error]))]).  105
  106curr_setting(Name, Module, Type, Default, Comment, Src) :-
  107    current_setting(Name, Module, Type, Default0, Comment, Src),
  108    (   st_default(Name, Module, Default1)
  109    ->  Default = Default1
  110    ;   Default = Default0
  111    ).
  112
  113%!  setting(:Name, +Type, +Default, +Comment) is det.
  114%
  115%   Define a  setting.  Name denotes  the name of the  setting, Type
  116%   its type.  Default is the  value before it is  modified. Default
  117%   can  refer  to  environment  variables and  can  use  arithmetic
  118%   expressions as defined by eval_default/4.
  119%
  120%   If a second declaration for  a   setting  is  encountered, it is
  121%   ignored  if  Type  and  Default  are    the  same.  Otherwise  a
  122%   permission_error is raised.
  123%
  124%   @param Name     Name of the setting (an atom)
  125%   @param Type     Type for setting.  One of =any= or a type defined
  126%                   by must_be/2.
  127%   @param Default  Default value for the setting.
  128%   @param Comment  Atom containing a (short) descriptive note.
  129
  130
  131setting(Name, Type, Default, Comment) :-
  132    throw(error(context_error(nodirective,
  133                              setting(Name, Type, Default, Comment)),
  134                _)).
  135
  136:- multifile
  137    system:term_expansion/2.  138
  139system:term_expansion((:- setting(QName, Type, Default, Comment)),
  140                    Expanded) :-
  141    \+ current_prolog_flag(xref, true),
  142    prolog_load_context(module, M0),
  143    strip_module(M0:QName, Module, Name),
  144    must_be(atom, Name),
  145    to_atom(Comment, CommentAtom),
  146    eval_default(Default, Module, Type, Value),
  147    check_type(Type, Value),
  148    source_location(File, Line),
  149    (   current_setting(Name, Module, OType, ODef, _, OldLoc),
  150        (   OType \=@= Type
  151        ;    ODef \=@= Default
  152        ),
  153        OldLoc \= (File:_)
  154    ->  format(string(Message),
  155               'Already defined at: ~w', [OldLoc]),
  156        throw(error(permission_error(redefine, setting, Module:Name),
  157                    context(Message, _)))
  158    ;   Expanded = settings:current_setting(Name, Module, Type, Default,
  159                                            CommentAtom, File:Line)
  160    ).
  161
  162to_atom(Atom, Atom) :-
  163    atom(Atom),
  164    !.
  165to_atom(String, Atom) :-
  166    format(atom(Atom), '~s', String).
  167
  168%!  setting(:Name, ?Value) is nondet.
  169%
  170%   True when Name is a currently   defined setting with Value. Note
  171%   that setting(Name, Value) only enumerates   the  settings of the
  172%   current  module.  All  settings   can    be   enumerated   using
  173%   setting(Module:Name, Value). This predicate is  =det= if Name is
  174%   ground.
  175%
  176%   @error  existence_error(setting, Name)
  177
  178setting(Module:Name, Value) :-
  179    (   nonvar(Name), nonvar(Module)
  180    ->  (   st_value(Name, Module, Value0)
  181        ->  Value = Value0
  182        ;   curr_setting(Name, Module, Type, Default, _, _)
  183        ->  eval_default(Default, Module, Type, Value)
  184        ;   existence_error(setting, Module:Name)
  185        )
  186    ;   current_setting(Name, Module, _, _, _, _),
  187        setting(Module:Name, Value)
  188    ).
  189
  190
  191:- dynamic
  192    setting_cache/3.  193:- volatile
  194    setting_cache/3.  195
  196%!  clear_setting_cache is det.
  197%
  198%   Clear the cache for evaluation of default values.
  199
  200clear_setting_cache :-
  201    retractall(setting_cache(_,_,_)).
  202
  203%!  eval_default(+Default, +Module, +Type, -Value) is det.
  204%
  205%   Convert the settings default value. The notation allows for some
  206%   `function-style' notations to make the library more generic:
  207%
  208%           * env(Name)
  209%           Get value from the given environment variable. The value
  210%           is handed to convert_setting_text/3 to convert the
  211%           textual representation into a Prolog term.  Raises an
  212%           existence_error of the variable is not defined.
  213%
  214%           * env(Name, Default)
  215%           As env(Name), but uses the value Default if the variable
  216%           is not defined.
  217%
  218%           * setting(Name)
  219%           Ask the value of another setting.
  220%
  221%           * Expression
  222%           If Type is numeric, evaluate the expression.  env(Var)
  223%           evaluates to the value of an environment variable.
  224%           If Type is =atom=, concatenate A+B+....  Elements of the
  225%           expression can be env(Name).
  226
  227:- multifile
  228    eval_default/3.                 % +Default, +Type, -Value
  229
  230eval_default(Default, _, _Type, Value) :-
  231    var(Default),
  232    !,
  233    Value = Default.
  234eval_default(Default, _, Type, Value) :-
  235    eval_default(Default, Type, Val),
  236    !,
  237    Value = Val.
  238eval_default(Default, _, _, Value) :-
  239    atomic(Default),
  240    !,
  241    Value = Default.
  242eval_default(Default, _, Type, Value) :-
  243    setting_cache(Default, Type, Val),
  244    !,
  245    Value = Val.
  246eval_default(env(Name), _, Type, Value) :-
  247    !,
  248    (   getenv(Name, TextValue)
  249    ->  convert_setting_text(Type, TextValue, Val),
  250        assert(setting_cache(env(Name), Type, Val)),
  251        Value = Val
  252    ;   existence_error(environment_variable, Name)
  253    ).
  254eval_default(env(Name, Default), _, Type, Value) :-
  255    !,
  256    (   getenv(Name, TextValue)
  257    ->  convert_setting_text(Type, TextValue, Val)
  258    ;   Val = Default
  259    ),
  260    assert(setting_cache(env(Name), Type, Val)),
  261    Value = Val.
  262eval_default(setting(Name), Module, Type, Value) :-
  263    !,
  264    strip_module(Module:Name, M, N),
  265    setting(M:N, Value),
  266    must_be(Type, Value).
  267eval_default(Expr, _, Type, Value) :-
  268    numeric_type(Type, Basic),
  269    !,
  270    arithmetic_expression_value(Expr, Val0),
  271    (   Basic == float
  272    ->  Val is float(Val0)
  273    ;   Basic = integer
  274    ->  Val is round(Val0)
  275    ;   Val = Val0
  276    ),
  277    assert(setting_cache(Expr, Type, Val)),
  278    Value = Val.
  279eval_default(A+B, Module, atom, Value) :-
  280    !,
  281    phrase(expr_to_list(A+B, Module), L),
  282    atomic_list_concat(L, Val),
  283    assert(setting_cache(A+B, atom, Val)),
  284    Value = Val.
  285eval_default(List, Module, list(Type), Value) :-
  286    !,
  287    eval_list_default(List, Module, Type, Val),
  288    assert(setting_cache(List, list(Type), Val)),
  289    Value = Val.
  290eval_default(Default, _, _, Default).
  291
  292
  293%!  eval_list_default(+List, +Module, +ElementType, -DefaultList)
  294%
  295%   Evaluate the default for a list of values.
  296
  297eval_list_default([], _, _, []).
  298eval_list_default([H0|T0], Module, Type, [H|T]) :-
  299    eval_default(H0, Module, Type, H),
  300    eval_list_default(T0, Module, Type, T).
  301
  302%!  expr_to_list(+Expression, +Module)// is det.
  303%
  304%   Process the components to create an  atom. Atom concatenation is
  305%   expressed as A+B. Components may refer to envrionment variables.
  306
  307expr_to_list(A+B, Module) -->
  308    !,
  309    expr_to_list(A, Module),
  310    expr_to_list(B, Module).
  311expr_to_list(env(Name), _) -->
  312    !,
  313    (   { getenv(Name, Text) }
  314    ->  [Text]
  315    ;   { existence_error(environment_variable, Name) }
  316    ).
  317expr_to_list(env(Name, Default), _) -->
  318    !,
  319    (   { getenv(Name, Text) }
  320    ->  [Text]
  321    ;   [Default]
  322    ).
  323expr_to_list(setting(Name), Module) -->
  324    !,
  325    { strip_module(Module:Name, M, N),
  326      setting(M:N, Value)
  327    },
  328    [ Value ].
  329expr_to_list(A, _) -->
  330    [A].
  331
  332%!  env(+Name:atom, -Value:number) is det.
  333%!  env(+Name:atom, +Default:number, -Value:number) is det
  334%
  335%   Evaluate  environment  variables   on    behalf   of  arithmetic
  336%   expressions.
  337
  338:- arithmetic_function(env/1).  339:- arithmetic_function(env/2).  340
  341env(Name, Value) :-
  342    (   getenv(Name, Text)
  343    ->  convert_setting_text(number, Text, Value)
  344    ;   existence_error(environment_variable, Name)
  345    ).
  346env(Name, Default, Value) :-
  347    (   getenv(Name, Text)
  348    ->  convert_setting_text(number, Text, Value)
  349    ;   Value = Default
  350    ).
  351
  352
  353%!  numeric_type(+Type, -BaseType)
  354%
  355%   True if Type is a numeric type   and  BaseType is the associated
  356%   basic Prolog type. BaseType is  one   of  =integer=,  =float= or
  357%   =number=.
  358
  359numeric_type(integer, integer).
  360numeric_type(nonneg, integer).
  361numeric_type(float, float).
  362numeric_type(between(L,_), Type) :-
  363    ( integer(L) -> Type = integer ; Type = float ).
  364
  365
  366%!  set_setting(:Name, +Value) is det.
  367%
  368%   Change a setting. Performs existence   and type-checking for the
  369%   setting. If the effective value  of   the  setting is changed it
  370%   broadcasts the event below.
  371%
  372%           settings(changed(Module:Name, Old, New))
  373%
  374%   @error  existence_error(setting, Name)
  375%   @error  type_error(Type, Value)
  376
  377set_setting(QName, Value) :-
  378    strip_module(QName, Module, Name),
  379    must_be(atom, Name),
  380    (   curr_setting(Name, Module, Type, Default0, _Comment, _Src),
  381        eval_default(Default0, Module, Type, Default)
  382    ->  setting(Module:Name, Old),
  383        (   Value == Default
  384        ->  retract_setting(Module:Name)
  385        ;   st_value(Name, Module, Value)
  386        ->  true
  387        ;   check_type(Type, Value)
  388        ->  retract_setting(Module:Name),
  389            assert_setting(Module:Name, Value)
  390        ),
  391        (   Old == Value
  392        ->  true
  393        ;   broadcast(settings(changed(Module:Name, Old, Value))),
  394            clear_setting_cache     % might influence dependent settings
  395        )
  396    ;   existence_error(setting, Name)
  397    ).
  398
  399retract_setting(Module:Name) :-
  400    retractall(st_value(Name, Module, _)).
  401
  402assert_setting(Module:Name, Value) :-
  403    assert(st_value(Name, Module, Value)).
  404
  405%!  restore_setting(:Name) is det.
  406%
  407%   Restore the value of setting Name   to  its default. Broadcast a
  408%   change like set_setting/2 if  the  current   value  is  not  the
  409%   default.
  410
  411restore_setting(QName) :-
  412    strip_module(QName, Module, Name),
  413    must_be(atom, Name),
  414    (   st_value(Name, Module, Old)
  415    ->  retract_setting(Module:Name),
  416        setting(Module:Name, Value),
  417        (   Old \== Value
  418        ->  broadcast(settings(changed(Module:Name, Old, Value)))
  419        ;   true
  420        )
  421    ;   true
  422    ).
  423
  424%!  set_setting_default(:Name, +Default) is det.
  425%
  426%   Change the default for a setting.  The   effect  is  the same as
  427%   set_setting/2, but the new value is  considered the default when
  428%   saving and restoring  a  setting.  It   is  intended  to  change
  429%   application defaults in a particular context.
  430
  431set_setting_default(QName, Default) :-
  432    strip_module(QName, Module, Name),
  433    must_be(atom, Name),
  434    (   current_setting(Name, Module, Type, Default0, _Comment, _Src)
  435    ->  retractall(settings:st_default(Name, Module, _)),
  436        retract_setting(Module:Name),
  437        (   Default == Default0
  438        ->  true
  439        ;   assert(settings:st_default(Name, Module, Default))
  440        ),
  441        eval_default(Default, Module, Type, Value),
  442        set_setting(Module:Name, Value)
  443    ;   existence_error(setting, Module:Name)
  444    ).
  445
  446
  447                 /*******************************
  448                 *             TYPES            *
  449                 *******************************/
  450
  451%!  check_type(+Type, +Term)
  452%
  453%   Type  checking  for  settings.  Currently  simply  forwarded  to
  454%   must_be/2.
  455
  456check_type(Type, Term) :-
  457    must_be(Type, Term).
  458
  459
  460                 /*******************************
  461                 *             FILE             *
  462                 *******************************/
  463
  464%!  load_settings(File) is det.
  465%!  load_settings(File, +Options) is det.
  466%
  467%   Load local settings from File. Succeeds  if File does not exist,
  468%   setting the default save-file to File.  Options are:
  469%
  470%     * undefined(+Action)
  471%     Define how to handle settings that are not defined.  When
  472%     =error=, an error is printed and the setting is ignored.
  473%     when =load=, the setting is loaded anyway, waiting for a
  474%     definition.
  475
  476load_settings(File) :-
  477    load_settings(File, []).
  478
  479load_settings(File, Options) :-
  480    absolute_file_name(File, Path,
  481                       [ access(read),
  482                         file_errors(fail)
  483                       ]),
  484    !,
  485    assert(local_file(Path)),
  486    open(Path, read, In, [encoding(utf8)]),
  487    read_setting(In, T0),
  488    call_cleanup(load_settings(T0, In, Options), close(In)),
  489    clear_setting_cache.
  490load_settings(File, _) :-
  491    absolute_file_name(File, Path,
  492                       [ access(write),
  493                         file_errors(fail)
  494                       ]),
  495    !,
  496    assert(local_file(Path)).
  497load_settings(_, _).
  498
  499load_settings(end_of_file, _, _) :- !.
  500load_settings(Setting, In, Options) :-
  501    catch(store_setting(Setting, Options), E,
  502          print_message(warning, E)),
  503    read_setting(In, Next),
  504    load_settings(Next, In, Options).
  505
  506read_setting(In, Term) :-
  507    read_term(In, Term,
  508              [ syntax_errors(dec10)
  509              ]).
  510
  511%!  store_setting(Term, +Options)
  512%
  513%   Store setting loaded from file in the Prolog database.
  514
  515store_setting(setting(Module:Name, Value), _) :-
  516    curr_setting(Name, Module, Type, Default0, _Commentm, _Src),
  517    !,
  518    eval_default(Default0, Module, Type, Default),
  519    (   Value == Default
  520    ->  true
  521    ;   check_type(Type, Value)
  522    ->  retractall(st_value(Name, Module, _)),
  523        assert(st_value(Name, Module, Value)),
  524        broadcast(settings(changed(Module:Name, Default, Value)))
  525    ).
  526store_setting(setting(Module:Name, Value), Options) :-
  527    !,
  528    (   option(undefined(load), Options, load)
  529    ->  retractall(st_value(Name, Module, _)),
  530        assert(st_value(Name, Module, Value))
  531    ;   existence_error(setting, Module:Name)
  532    ).
  533store_setting(Term, _) :-
  534    type_error(setting, Term).
  535
  536%!  save_settings is det.
  537%!  save_settings(+File) is det.
  538%
  539%   Save modified settings to File.
  540
  541save_settings :-
  542    local_file(File),
  543    !,
  544    save_settings(File).
  545
  546save_settings(File) :-
  547    absolute_file_name(File, Path,
  548                       [ access(write)
  549                       ]),
  550    !,
  551    open(Path, write, Out,
  552         [ encoding(utf8),
  553           bom(true)
  554         ]),
  555    write_setting_header(Out),
  556    forall(current_setting(Name, Module, _, _, _, _),
  557           save_setting(Out, Module:Name)),
  558    close(Out).
  559
  560
  561write_setting_header(Out) :-
  562    get_time(Now),
  563    format_time(string(Date), '%+', Now),
  564    format(Out, '/*  Saved settings~n', []),
  565    format(Out, '    Date: ~w~n', [Date]),
  566    format(Out, '*/~n~n', []).
  567
  568save_setting(Out, Module:Name) :-
  569    curr_setting(Name, Module, Type, Default, Comment, _Src),
  570    (   st_value(Name, Module, Value),
  571        \+ ( eval_default(Default, Module, Type, DefValue),
  572             debug(setting, '~w <-> ~w~n', [DefValue, Value]),
  573             DefValue =@= Value
  574           )
  575    ->  format(Out, '~n%\t~w~n', [Comment]),
  576        format(Out, 'setting(~q:~q, ~q).~n', [Module, Name, Value])
  577    ;   true
  578    ).
  579
  580%!  current_setting(?Setting) is nondet.
  581%
  582%   True if Setting is a currently defined setting
  583
  584current_setting(Setting) :-
  585    ground(Setting),
  586    !,
  587    strip_module(Setting, Module, Name),
  588    current_setting(Name, Module, _, _, _, _).
  589current_setting(Module:Name) :-
  590    current_setting(Name, Module, _, _, _, _).
  591
  592%!  setting_property(+Setting, +Property) is det.
  593%!  setting_property(?Setting, ?Property) is nondet.
  594%
  595%   Query currently defined settings.  Property is one of
  596%
  597%           * comment(-Atom)
  598%           * type(-Type)
  599%           Type of the setting.
  600%           * default(-Default)
  601%           Default value.  If this is an expression, it is
  602%           evaluated.
  603%           * source(-File:-Line)
  604%           Location where the setting is defined.
  605
  606setting_property(Setting, Property) :-
  607    ground(Setting),
  608    !,
  609    Setting = Module:Name,
  610    curr_setting(Name, Module, Type, Default, Comment, Src),
  611    !,
  612    setting_property(Property, Module, Type, Default, Comment, Src).
  613setting_property(Setting, Property) :-
  614    Setting = Module:Name,
  615    curr_setting(Name, Module, Type, Default, Comment, Src),
  616    setting_property(Property, Module, Type, Default, Comment, Src).
  617
  618setting_property(type(Type), _, Type, _, _, _).
  619setting_property(default(Default), M, Type, Default0, _, _) :-
  620    eval_default(Default0, M, Type, Default).
  621setting_property(comment(Comment), _, _, _, Comment, _).
  622setting_property(source(Src), _, _, _, _, Src).
  623
  624%!  list_settings is det.
  625%!  list_settings(+Module) is det.
  626%
  627%   List settings to =current_output=. The   second  form only lists
  628%   settings on the matching module.
  629%
  630%   @tbd    Compute the required column widths
  631
  632list_settings :-
  633    list_settings(_).
  634
  635list_settings(Spec) :-
  636    spec_term(Spec, Term),
  637    TS1 = 25,
  638    TS2 = 40,
  639    format('~`=t~72|~n'),
  640    format('~w~t~*| ~w~w~t~*| ~w~n',
  641           ['Name', TS1, 'Value (*=modified)', '', TS2, 'Comment']),
  642    format('~`=t~72|~n'),
  643    forall(current_setting(Term),
  644           list_setting(Term, TS1, TS2)).
  645
  646spec_term(M:S, M:S) :- !.
  647spec_term(M, M:_).
  648
  649
  650list_setting(Module:Name, TS1, TS2) :-
  651    curr_setting(Name, Module, Type, Default0, Comment, _Src),
  652    eval_default(Default0, Module, Type, Default),
  653    setting(Module:Name, Value),
  654    (   Value \== Default
  655    ->  Modified = (*)
  656    ;   Modified = ''
  657    ),
  658    format('~w~t~*| ~q~w~t~*| ~w~n', [Module:Name, TS1, Value, Modified, TS2, Comment]).
  659
  660
  661                 /*******************************
  662                 *            TYPES             *
  663                 *******************************/
  664
  665%!  convert_setting_text(+Type, +Text, -Value)
  666%
  667%   Converts from textual form to  Prolog   Value.  Used  to convert
  668%   values obtained from the environment.  Public to provide support
  669%   in user-interfaces to this library.
  670%
  671%   @error  type_error(Type, Value)
  672
  673:- multifile
  674    convert_text/3.                 % +Type, +Text, -Value
  675
  676convert_setting_text(Type, Text, Value) :-
  677    convert_text(Type, Text, Value),
  678    !.
  679convert_setting_text(atom, Value, Value) :-
  680    !,
  681    must_be(atom, Value).
  682convert_setting_text(boolean, Value, Value) :-
  683    !,
  684    must_be(boolean, Value).
  685convert_setting_text(integer, Atom, Number) :-
  686    !,
  687    term_to_atom(Term, Atom),
  688    Number is round(Term).
  689convert_setting_text(float, Atom, Number) :-
  690    !,
  691    term_to_atom(Term, Atom),
  692    Number is float(Term).
  693convert_setting_text(between(L,U), Atom, Number) :-
  694    !,
  695    (   integer(L)
  696    ->  convert_setting_text(integer, Atom, Number)
  697    ;   convert_setting_text(float, Atom, Number)
  698    ),
  699    must_be(between(L,U), Number).
  700convert_setting_text(Type, Atom, Term) :-
  701    term_to_atom(Term, Atom),
  702    must_be(Type, Term).
  703
  704
  705                 /*******************************
  706                 *            SANDBOX           *
  707                 *******************************/
  708
  709:- multifile
  710    sandbox:safe_meta_predicate/1.  711
  712sandbox:safe_meta_predicate(settings:setting/2)