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