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