633 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Prolog
		
	
	
	
	
	
			
		
		
	
	
			633 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Prolog
		
	
	
	
	
	
/*  $Id$
 | 
						|
 | 
						|
    Part of SWI-Prolog
 | 
						|
 | 
						|
    Author:        Jan Wielemaker
 | 
						|
    E-mail:        wielemak@science.uva.nl
 | 
						|
    WWW:           http://www.swi-prolog.org
 | 
						|
    Copyright (C): 1985-2007, University of Amsterdam
 | 
						|
 | 
						|
    This program is free software; you can redistribute it and/or
 | 
						|
    modify it under the terms of the GNU General Public License
 | 
						|
    as published by the Free Software Foundation; either version 2
 | 
						|
    of the License, or (at your option) any later version.
 | 
						|
 | 
						|
    This program is distributed in the hope that it will be useful,
 | 
						|
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
    GNU General Public License for more details.
 | 
						|
 | 
						|
    You should have received a copy of the GNU General Public
 | 
						|
    License along with this library; if not, write to the Free Software
 | 
						|
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
						|
 | 
						|
    As a special exception, if you link this library with other files,
 | 
						|
    compiled with a Free Software compiler, to produce an executable, this
 | 
						|
    library does not by itself cause the resulting executable to be covered
 | 
						|
    by the GNU General Public License. This exception does not however
 | 
						|
    invalidate any other reasons why the executable file might be covered by
 | 
						|
    the GNU General Public License.
 | 
						|
*/
 | 
						|
 | 
						|
:- module(settings,
 | 
						|
	  [ setting/4,			% :Name, +Type, +Default, +Comment
 | 
						|
	    setting/2,			% :Name, ?Value
 | 
						|
	    set_setting/2,		% :Name, +Value
 | 
						|
	    set_setting_default/2,	% :Name, +Value
 | 
						|
	    restore_setting/1,		% :Name
 | 
						|
	    load_settings/1,		% +File
 | 
						|
	    load_settings/2,		% +File, +Options
 | 
						|
	    save_settings/0,
 | 
						|
	    save_settings/1,		% +File
 | 
						|
	    current_setting/1,		% Module:Name
 | 
						|
	    setting_property/2,		% ?Setting, ?Property
 | 
						|
	    list_settings/0,
 | 
						|
 | 
						|
	    convert_setting_text/3	% +Type, +Text, -Value
 | 
						|
	  ]).
 | 
						|
:- use_module(library(error)).
 | 
						|
:- use_module(library(broadcast)).
 | 
						|
:- use_module(library(debug)).
 | 
						|
:- use_module(library(option)).
 | 
						|
 | 
						|
/** <module> Setting management
 | 
						|
 | 
						|
This library allows management  of   configuration  settings  for Prolog
 | 
						|
applications. Applications define settings  in   one  or  multiple files
 | 
						|
using the directive setting/4 as illustrated below:
 | 
						|
 | 
						|
==
 | 
						|
:- use_module(library(setting)).
 | 
						|
 | 
						|
:- setting(version, atom,   '1.0', 'Current version').
 | 
						|
:- setting(timeout, number,    20, 'Timeout in seconds').
 | 
						|
==
 | 
						|
 | 
						|
The directive is subject to   term_expansion/2,  which guarantees proper
 | 
						|
synchronisation of the  database  if   source-files  are  reloaded. This
 | 
						|
implies it is *not* possible to call setting/4 as a predicate.
 | 
						|
 | 
						|
Settings are local to a  module.  This   implies  they  are defined in a
 | 
						|
two-level namespace. Managing settings  per   module  greatly simplifies
 | 
						|
assembling large applications from multiple   modules that configuration
 | 
						|
through  settings.  This  settings  management  library  ensures  proper
 | 
						|
access, loading and saving of settings.
 | 
						|
 | 
						|
@see	library(config) distributed with XPCE provides an alternative
 | 
						|
	aimed at graphical applications.
 | 
						|
@author	Jan Wielemaker
 | 
						|
*/
 | 
						|
 | 
						|
:- dynamic
 | 
						|
	st_value/3,			% Name, Module, Value
 | 
						|
	st_default/3,			% Name, Module, Value
 | 
						|
	local_file/1.			% Path
 | 
						|
 | 
						|
:- multifile
 | 
						|
	current_setting/6.		% Name, Module, Type, Default, Comment, Source
 | 
						|
 | 
						|
:- meta_predicate
 | 
						|
	setting(:, +, +, +),
 | 
						|
	setting(:, ?),
 | 
						|
	set_setting(:, +),
 | 
						|
	set_setting_default(:, +),
 | 
						|
	current_setting(:),
 | 
						|
	restore_setting(:).
 | 
						|
 | 
						|
curr_setting(Name, Module, Type, Default, Comment) :-
 | 
						|
	current_setting(Name, Module, Type, Default0, Comment, _Src),
 | 
						|
	(   st_default(Name, Module, Default1)
 | 
						|
	->  Default = Default1
 | 
						|
	;   Default = Default0
 | 
						|
	).
 | 
						|
 | 
						|
%%	setting(Name, Type, Default, Comment) is det.
 | 
						|
%
 | 
						|
%	Define a setting. Name denotes the name of the setting, Type its
 | 
						|
%	type. Default is the value before  it is modified. Default refer
 | 
						|
%	to environment variables  and  use   arithmetic  expressions  as
 | 
						|
%	defined by eval_default/4.
 | 
						|
%
 | 
						|
%	@param Name	Name of the setting (an atom)
 | 
						|
%	@param Type	Type for setting.  One of =any= or a type defined
 | 
						|
%			by must_be/2.
 | 
						|
%	@param Default  Default value for the setting.
 | 
						|
%	@param Comment	Atom containing a (short) descriptive note.
 | 
						|
 | 
						|
 | 
						|
setting(Name, Type, Default, Comment) :-
 | 
						|
	throw(error(context_error(nodirective,
 | 
						|
				  setting(Name, Type, Default, Comment)),
 | 
						|
		    _)).
 | 
						|
 | 
						|
:- multifile
 | 
						|
	system:term_expansion/2.
 | 
						|
 | 
						|
system:term_expansion((:- setting(QName, Type, Default, Comment)),
 | 
						|
		    Expanded) :-
 | 
						|
	prolog_load_context(module, M0),
 | 
						|
	strip_module(M0:QName, Module, Name),
 | 
						|
	must_be(atom, Name),
 | 
						|
	to_atom(Comment, CommentAtom),
 | 
						|
	eval_default(Default, Module, Type, Value),
 | 
						|
	check_type(Type, Value),
 | 
						|
	(   current_setting(Name, Module, _, _, _, OldLoc)
 | 
						|
	->  format(string(Message),
 | 
						|
		   'Already defined at: ~w', [OldLoc]),
 | 
						|
	    throw(error(permission_error(redefine, setting, Module:Name),
 | 
						|
			context(Message, _)))
 | 
						|
	;   source_location(File, Line)
 | 
						|
	->  Expanded = settings:current_setting(Name, Module, Type, Default,
 | 
						|
						CommentAtom, File:Line)
 | 
						|
	).
 | 
						|
 | 
						|
to_atom(Atom, Atom) :-
 | 
						|
	atom(Atom), !.
 | 
						|
to_atom(String, Atom) :-
 | 
						|
	format(atom(Atom), '~s', String).
 | 
						|
 | 
						|
%%	setting(:Name, ?Value) is nondet.
 | 
						|
%
 | 
						|
%	True if Name is a currently defined setting with Value.
 | 
						|
%
 | 
						|
%	@error	existence_error(setting, Name)
 | 
						|
 | 
						|
setting(QName, Value) :-
 | 
						|
	strip_module(QName, Module, Name),
 | 
						|
	(   ground(Name)
 | 
						|
	->  (   st_value(Name, Module, Value0)
 | 
						|
	    ->  Value = Value0
 | 
						|
	    ;   curr_setting(Name, Module, Type, Default, _)
 | 
						|
	    ->	eval_default(Default, Module, Type, Value)
 | 
						|
	    ;	existence_error(setting, Module:Name)
 | 
						|
	    )
 | 
						|
	;   current_setting(Name, Module, _, _, _, _),
 | 
						|
	    setting(Module:Name, Value)
 | 
						|
	).
 | 
						|
 | 
						|
 | 
						|
:- dynamic
 | 
						|
	setting_cache/3.
 | 
						|
:- volatile
 | 
						|
	setting_cache/3.
 | 
						|
 | 
						|
%%	clear_setting_cache is det.
 | 
						|
%
 | 
						|
%	Clear the cache for evaluation of default values.
 | 
						|
 | 
						|
clear_setting_cache :-
 | 
						|
	retractall(setting_cache(_,_,_)).
 | 
						|
 | 
						|
%%	eval_default(+Default, +Module, +Type, -Value) is det.
 | 
						|
%
 | 
						|
%	Convert the settings default value. The notation allows for some
 | 
						|
%	`function-style' notations to make the library more generic:
 | 
						|
%
 | 
						|
%		* env(Name)
 | 
						|
%		Get value from the given environment variable. The value
 | 
						|
%		is handed to convert_setting_text/3 to convert the
 | 
						|
%		textual representation into a Prolog term.  Raises an
 | 
						|
%		existence_error of the variable is not defined.
 | 
						|
%
 | 
						|
%		* env(Name, Default)
 | 
						|
%		As env(Name), but uses the value Default if the variable
 | 
						|
%		is not defined.
 | 
						|
%
 | 
						|
%		* setting(Name)
 | 
						|
%		Ask the value of another setting.
 | 
						|
%
 | 
						|
%		* Expression
 | 
						|
%		If Type is numeric, evaluate the expression.  env(Var)
 | 
						|
%		evaluates to the value of an environment variable.
 | 
						|
%		If Type is =atom=, concatenate A+B+....  Elements of the
 | 
						|
%		expression can be env(Name).
 | 
						|
 | 
						|
:- multifile
 | 
						|
	eval_default/3.			% +Default, +Type, -Value
 | 
						|
 | 
						|
eval_default(Default, _, Type, Value) :-
 | 
						|
	eval_default(Default, Type, Val), !,
 | 
						|
	Value = Val.
 | 
						|
eval_default(Default, _, _, Value) :-
 | 
						|
	atomic(Default), !,
 | 
						|
	Value = Default.
 | 
						|
eval_default(Default, _, Type, Value) :-
 | 
						|
	setting_cache(Default, Type, Val), !,
 | 
						|
	Value = Val.
 | 
						|
eval_default(env(Name), _, Type, Value) :- !,
 | 
						|
	(   getenv(Name, TextValue)
 | 
						|
	->  convert_setting_text(Type, TextValue, Val),
 | 
						|
	    assert(setting_cache(env(Name), Type, Val)),
 | 
						|
	    Value = Val
 | 
						|
	;   existence_error(environment_variable, Name)
 | 
						|
	).
 | 
						|
eval_default(env(Name, Default), _, Type, Value) :- !,
 | 
						|
	(   getenv(Name, TextValue)
 | 
						|
	->  convert_setting_text(Type, TextValue, Val)
 | 
						|
	;   Value = Default
 | 
						|
	),
 | 
						|
	assert(setting_cache(env(Name), Type, Val)),
 | 
						|
	Value = Val.
 | 
						|
eval_default(setting(Name), Module, Type, Value) :- !,
 | 
						|
	strip_module(Module:Name, M, N),
 | 
						|
	setting(M:N, Value),
 | 
						|
	must_be(Type, Value).
 | 
						|
eval_default(Expr, _, Type, Value) :-
 | 
						|
	numeric_type(Type, Basic), !,
 | 
						|
	Val0 is Expr,
 | 
						|
	(   Basic == float
 | 
						|
	->  Val is float(Val0)
 | 
						|
	;   Basic = integer
 | 
						|
	->  Val is round(Val0)
 | 
						|
	;   Val = Val0
 | 
						|
	),
 | 
						|
	assert(setting_cache(Expr, Type, Val)),
 | 
						|
	Value = Val.
 | 
						|
eval_default(A+B, Module, atom, Value) :- !,
 | 
						|
	phrase(expr_to_list(A+B, Module), L),
 | 
						|
	atomic_list_concat(L, Val),
 | 
						|
	assert(setting_cache(A+B, atom, Val)),
 | 
						|
	Value = Val.
 | 
						|
eval_default(List, Module, list(Type), Value) :- !,
 | 
						|
	eval_list_default(List, Module, Type, Val),
 | 
						|
	assert(setting_cache(List, list(Type), Val)),
 | 
						|
	Value = Val.
 | 
						|
eval_default(Default, _, _, Default).
 | 
						|
 | 
						|
 | 
						|
%%	eval_list_default(+List, +Module, +ElementType, -DefaultList)
 | 
						|
%
 | 
						|
%	Evaluate the default for a list of values.
 | 
						|
 | 
						|
eval_list_default([], _, _, []).
 | 
						|
eval_list_default([H0|T0], Module, Type, [H|T]) :-
 | 
						|
	eval_default(H0, Module, Type, H),
 | 
						|
	eval_list_default(T0, Module, Type, T).
 | 
						|
 | 
						|
%%	expr_to_list(+Expression, +Module)// is det.
 | 
						|
%
 | 
						|
%	Process the components to create an  atom. Atom concatenation is
 | 
						|
%	expressed as A+B. Components may refer to envrionment variables.
 | 
						|
 | 
						|
expr_to_list(A+B, Module) --> !,
 | 
						|
	expr_to_list(A, Module),
 | 
						|
	expr_to_list(B, Module).
 | 
						|
expr_to_list(env(Name), _) --> !,
 | 
						|
	(   { getenv(Name, Text) }
 | 
						|
	->  [Text]
 | 
						|
	;   { existence_error(environment_variable, Name) }
 | 
						|
	).
 | 
						|
expr_to_list(env(Name, Default), _) --> !,
 | 
						|
	(   { getenv(Name, Text) }
 | 
						|
	->  [Text]
 | 
						|
	;   [Default]
 | 
						|
	).
 | 
						|
expr_to_list(setting(Name), Module) --> !,
 | 
						|
	{ strip_module(Module:Name, M, N),
 | 
						|
	  setting(M:N, Value)
 | 
						|
	},
 | 
						|
	[ Value ].
 | 
						|
expr_to_list(A, _) -->
 | 
						|
	[A].
 | 
						|
 | 
						|
:- if((\+ current_prolog_flag(version_data,yap(_,_,_,_)))).
 | 
						|
 | 
						|
%%	env(+Name:atom, -Value:number) is det.
 | 
						|
%%	env(+Name:atom, +Default:number, -Value:number) is det
 | 
						|
%
 | 
						|
%	Evaluate  environment  variables   on    behalf   of  arithmetic
 | 
						|
%	expressions.
 | 
						|
 | 
						|
:- arithmetic_function(env/1).
 | 
						|
:- arithmetic_function(env/2).
 | 
						|
 | 
						|
env(Name, Value) :-
 | 
						|
	(   getenv(Name, Text)
 | 
						|
	->  convert_setting_text(number, Text, Value)
 | 
						|
	;   existence_error(environment_variable, Name)
 | 
						|
	).
 | 
						|
env(Name, Default, Value) :-
 | 
						|
	(   getenv(Name, Text)
 | 
						|
	->  convert_setting_text(number, Text, Value)
 | 
						|
	;   Value = Default
 | 
						|
	).
 | 
						|
 | 
						|
:- endif.
 | 
						|
 | 
						|
%%	numeric_type(+Type, -BaseType)
 | 
						|
%
 | 
						|
%	True if Type is a numeric type   and  BaseType is the associated
 | 
						|
%	basic Prolog type. BaseType is  one   of  =integer=,  =float= or
 | 
						|
%	=number=.
 | 
						|
 | 
						|
numeric_type(integer, integer).
 | 
						|
numeric_type(nonneg, integer).
 | 
						|
numeric_type(float, float).
 | 
						|
numeric_type(between(L,_), Type) :-
 | 
						|
	( integer(L) -> Type = integer ; Type = float ).
 | 
						|
 | 
						|
 | 
						|
%%	set_setting(:Name, +Value) is det.
 | 
						|
%
 | 
						|
%	Change a setting. Performs existence   and type-checking for the
 | 
						|
%	setting. If the effective value  of   the  setting is changed it
 | 
						|
%	broadcasts the event below.
 | 
						|
%
 | 
						|
%		settings(changed(Module:Name, Old, New))
 | 
						|
%
 | 
						|
%	@error	existence_error(setting, Name)
 | 
						|
%	@error  type_error(Type, Value)
 | 
						|
 | 
						|
set_setting(QName, Value) :-
 | 
						|
	strip_module(QName, Module, Name),
 | 
						|
	must_be(atom, Name),
 | 
						|
	(   curr_setting(Name, Module, Type, Default0, _Comment),
 | 
						|
	    eval_default(Default0, Module, Type, Default)
 | 
						|
	->  (   Value == Default
 | 
						|
	    ->	retract_setting(Module:Name)
 | 
						|
	    ;	st_value(Name, Module, Value)
 | 
						|
	    ->	true
 | 
						|
	    ;	check_type(Type, Value)
 | 
						|
	    ->	setting(Module:Name, Old),
 | 
						|
	        retract_setting(Module:Name),
 | 
						|
	        assert_setting(Module:Name, Value),
 | 
						|
		broadcast(settings(changed(Module:Name, Old, Value))),
 | 
						|
		clear_setting_cache	% might influence dependent settings.
 | 
						|
	    )
 | 
						|
	;   existence_error(setting, Name)
 | 
						|
	).
 | 
						|
 | 
						|
retract_setting(Module:Name) :-
 | 
						|
	retractall(st_value(Name, Module, _)).
 | 
						|
 | 
						|
assert_setting(Module:Name, Value) :-
 | 
						|
	assert(st_value(Name, Module, Value)).
 | 
						|
 | 
						|
%%	restore_setting(:Name) is det.
 | 
						|
%
 | 
						|
%	Restore the value of setting Name   to  its default. Broadcast a
 | 
						|
%	change like set_setting/2 if  the  current   value  is  not  the
 | 
						|
%	default.
 | 
						|
 | 
						|
restore_setting(QName) :-
 | 
						|
	strip_module(QName, Module, Name),
 | 
						|
	must_be(atom, Name),
 | 
						|
	(   st_value(Name, Module, Old)
 | 
						|
	->  retract_setting(Module:Name),
 | 
						|
	    setting(Module:Name, Value),
 | 
						|
	    (	Old \== Value
 | 
						|
	    ->	broadcast(settings(changed(Module:Name, Old, Value)))
 | 
						|
	    ;	true
 | 
						|
	    )
 | 
						|
	;   true
 | 
						|
	).
 | 
						|
 | 
						|
%%	set_setting_default(:Name, +Default) is det.
 | 
						|
%
 | 
						|
%	Change the default for a setting.  The   effect  is  the same as
 | 
						|
%	set_setting/2, but the new value is  considered the default when
 | 
						|
%	saving and restoring  a  setting.  It   is  intended  to  change
 | 
						|
%	application defaults in a particular context.
 | 
						|
 | 
						|
set_setting_default(QName, Default) :-
 | 
						|
	strip_module(QName, Module, Name),
 | 
						|
	must_be(atom, Name),
 | 
						|
	(   current_setting(Name, Module, Type, Default0, _Comment, _Src)
 | 
						|
	->  retractall(settings:st_default(Name, Module, _)),
 | 
						|
	    retract_setting(Module:Name),
 | 
						|
	    (   Default == Default0
 | 
						|
	    ->	true
 | 
						|
	    ;	assert(settings:st_default(Name, Module, Default))
 | 
						|
	    ),
 | 
						|
	    eval_default(Default, Module, Type, Value),
 | 
						|
	    set_setting(Module:Name, Value)
 | 
						|
	;   existence_error(setting, Module:Name)
 | 
						|
	).
 | 
						|
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	       TYPES		*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
%%	check_type(+Type, +Term)
 | 
						|
%
 | 
						|
%	Type  checking  for  settings.  Currently  simply  forwarded  to
 | 
						|
%	must_be/2.
 | 
						|
 | 
						|
check_type(Type, Term) :-
 | 
						|
	must_be(Type, Term).
 | 
						|
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	       FILE		*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
%%	load_settings(File) is det.
 | 
						|
%%	load_settings(File, +Options) is det.
 | 
						|
%
 | 
						|
%	Load local settings from File. Succeeds  if File does not exist,
 | 
						|
%	setting the default save-file to File.  Options are:
 | 
						|
%
 | 
						|
%	  * undefined(+Action)
 | 
						|
%	  Define how to handle settings that are not defined.  When
 | 
						|
%	  =error=, an error is printed and the setting is ignored.
 | 
						|
%	  when =load=, the setting is loaded anyway, waiting for a
 | 
						|
%	  definition.
 | 
						|
 | 
						|
load_settings(File) :-
 | 
						|
	load_settings(File, []).
 | 
						|
 | 
						|
load_settings(File, Options) :-
 | 
						|
	absolute_file_name(File, Path,
 | 
						|
			   [ access(read),
 | 
						|
			     file_errors(fail)
 | 
						|
			   ]), !,
 | 
						|
	assert(local_file(Path)),
 | 
						|
	open(Path, read, In, [encoding(utf8)]),
 | 
						|
	read_setting(In, T0),
 | 
						|
	call_cleanup(load_settings(T0, In, Options), close(In)),
 | 
						|
	clear_setting_cache.
 | 
						|
load_settings(File, _) :-
 | 
						|
	absolute_file_name(File, Path,
 | 
						|
			   [ access(write),
 | 
						|
			     file_errors(fail)
 | 
						|
			   ]), !,
 | 
						|
	assert(local_file(Path)).
 | 
						|
load_settings(_, _).
 | 
						|
 | 
						|
load_settings(end_of_file, _, _) :- !.
 | 
						|
load_settings(Setting, In, Options) :-
 | 
						|
	catch(store_setting(Setting, Options), E,
 | 
						|
	      print_message(warning, E)),
 | 
						|
	read_setting(In, Next),
 | 
						|
	load_settings(Next, In, Options).
 | 
						|
 | 
						|
read_setting(In, Term) :-
 | 
						|
	read_term(In, Term,
 | 
						|
		  [ errors(dec10)
 | 
						|
		  ]).
 | 
						|
 | 
						|
%%	store_setting(Term, +Options)
 | 
						|
%
 | 
						|
%	Store setting loaded from file in the Prolog database.
 | 
						|
 | 
						|
store_setting(setting(Module:Name, Value), _) :-
 | 
						|
	curr_setting(Name, Module, Type, Default0, _Commentm), !,
 | 
						|
	eval_default(Default0, Module, Type, Default),
 | 
						|
	(   Value == Default
 | 
						|
	->  true
 | 
						|
	;   check_type(Type, Value)
 | 
						|
	->  retractall(st_value(Name, Module, _)),
 | 
						|
	    assert(st_value(Name, Module, Value)),
 | 
						|
	    broadcast(settings(changed(Module:Name, Default, Value)))
 | 
						|
	).
 | 
						|
store_setting(setting(Module:Name, Value), Options) :- !,
 | 
						|
	(   option(undefined(load), Options, load)
 | 
						|
	->  retractall(st_value(Name, Module, _)),
 | 
						|
	    assert(st_value(Name, Module, Value))
 | 
						|
	;   existence_error(setting, Module:Name)
 | 
						|
	).
 | 
						|
store_setting(Term, _) :-
 | 
						|
	type_error(setting, Term).
 | 
						|
 | 
						|
%%	save_settings is det.
 | 
						|
%%	save_settings(+File) is det.
 | 
						|
%
 | 
						|
%	Save modified settings to File.
 | 
						|
 | 
						|
save_settings :-
 | 
						|
	local_file(File), !,
 | 
						|
	save_settings(File).
 | 
						|
 | 
						|
save_settings(File) :-
 | 
						|
	absolute_file_name(File, Path,
 | 
						|
			   [ access(write)
 | 
						|
			   ]), !,
 | 
						|
	open(Path, write, Out,
 | 
						|
	     [ encoding(utf8),
 | 
						|
	       bom(true)
 | 
						|
	     ]),
 | 
						|
	write_setting_header(Out),
 | 
						|
	forall(current_setting(Name, Module, _, _, _, _),
 | 
						|
	       save_setting(Out, Module:Name)),
 | 
						|
	close(Out).
 | 
						|
 | 
						|
 | 
						|
write_setting_header(Out) :-
 | 
						|
	get_time(Now),
 | 
						|
	format_time(string(Date), '%+', Now),
 | 
						|
	format(Out, '/*  Saved settings~n', []),
 | 
						|
	format(Out, '    Date: ~w~n', [Date]),
 | 
						|
	format(Out, '*/~n~n', []).
 | 
						|
 | 
						|
save_setting(Out, Module:Name) :-
 | 
						|
	curr_setting(Name, Module, Type, Default, Comment),
 | 
						|
	(   st_value(Name, Module, Value),
 | 
						|
	    \+ ( eval_default(Default, Module, Type, DefValue),
 | 
						|
		 debug(setting, '~w <-> ~w~n', [DefValue, Value]),
 | 
						|
	         DefValue =@= Value
 | 
						|
	       )
 | 
						|
	->  format(Out, '~n%	~w~n', [Comment]),
 | 
						|
	    format(Out, 'setting(~q:~q, ~q).~n', [Module, Name, Value])
 | 
						|
	;   true
 | 
						|
	).
 | 
						|
 | 
						|
%%	current_setting(?Setting) is nondet.
 | 
						|
%
 | 
						|
%	True if Setting is a currently defined setting
 | 
						|
 | 
						|
current_setting(Setting) :-
 | 
						|
	ground(Setting), !,
 | 
						|
	strip_module(Setting, Module, Name),
 | 
						|
	current_setting(Name, Module, _, _, _, _).
 | 
						|
current_setting(Module:Name) :-
 | 
						|
	current_setting(Name, Module, _, _, _, _).
 | 
						|
 | 
						|
%%	setting_property(+Setting, +Property) is det.
 | 
						|
%%	setting_property(?Setting, ?Property) is nondet.
 | 
						|
%
 | 
						|
%	Query currently defined settings.  Property is one of
 | 
						|
%
 | 
						|
%		* comment(-Atom)
 | 
						|
%		* type(-Type)
 | 
						|
%		Type of the setting.
 | 
						|
%		* default(-Default)
 | 
						|
%		Default value.  If this is an expression, it is
 | 
						|
%		evaluated.
 | 
						|
 | 
						|
setting_property(Setting, Property) :-
 | 
						|
	ground(Setting), !,
 | 
						|
	Setting = Module:Name,
 | 
						|
	curr_setting(Name, Module, Type, Default, Comment), !,
 | 
						|
	setting_property(Property, Module, Type, Default, Comment).
 | 
						|
setting_property(Setting, Property) :-
 | 
						|
	Setting = Module:Name,
 | 
						|
	curr_setting(Name, Module, Type, Default, Comment),
 | 
						|
	setting_property(Property, Module, Type, Default, Comment).
 | 
						|
 | 
						|
setting_property(type(Type),       _, Type, _,        _).
 | 
						|
setting_property(default(Default), M, Type, Default0, _) :-
 | 
						|
	eval_default(Default0, M, Type, Default).
 | 
						|
setting_property(comment(Comment), _, _,    _,        Comment).
 | 
						|
 | 
						|
%%	list_settings
 | 
						|
%
 | 
						|
%	List settings to =current_output=.
 | 
						|
 | 
						|
list_settings :-
 | 
						|
	format('~`=t~72|~n'),
 | 
						|
	format('~w~t~20| ~w~w~t~40| ~w~n', ['Name', 'Value (*=modified)', '', 'Comment']),
 | 
						|
	format('~`=t~72|~n'),
 | 
						|
	forall(current_setting(Module:Setting),
 | 
						|
	       list_setting(Module:Setting)).
 | 
						|
 | 
						|
list_setting(Module:Name) :-
 | 
						|
	curr_setting(Name, Module, Type, Default0, Comment),
 | 
						|
	eval_default(Default0, Module, Type, Default),
 | 
						|
	setting(Module:Name, Value),
 | 
						|
	(   Value \== Default
 | 
						|
	->  Modified = (*)
 | 
						|
	;   Modified = ''
 | 
						|
	),
 | 
						|
        format('~w~t~20| ~q~w~t~40| ~w~n', [Module:Name, Value, Modified, Comment]).
 | 
						|
 | 
						|
 | 
						|
		 /*******************************
 | 
						|
		 *	      TYPES		*
 | 
						|
		 *******************************/
 | 
						|
 | 
						|
%%	convert_setting_text(+Type, +Text, -Value)
 | 
						|
%
 | 
						|
%	Converts from textual form to  Prolog   Value.  Used  to convert
 | 
						|
%	values obtained from the environment.  Public to provide support
 | 
						|
%	in user-interfaces to this library.
 | 
						|
%
 | 
						|
%	@error	type_error(Type, Value)
 | 
						|
 | 
						|
:- multifile
 | 
						|
	convert_text/3.			% +Type, +Text, -Value
 | 
						|
 | 
						|
convert_setting_text(Type, Text, Value) :-
 | 
						|
	convert_text(Type, Text, Value), !.
 | 
						|
convert_setting_text(atom, Value, Value) :- !,
 | 
						|
	must_be(atom, Value).
 | 
						|
convert_setting_text(boolean, Value, Value) :- !,
 | 
						|
	must_be(boolean, Value).
 | 
						|
convert_setting_text(integer, Atom, Number) :- !,
 | 
						|
	term_to_atom(Term, Atom),
 | 
						|
	Number is round(Term).
 | 
						|
convert_setting_text(float, Atom, Number) :- !,
 | 
						|
	term_to_atom(Term, Atom),
 | 
						|
	Number is float(Term).
 | 
						|
convert_setting_text(between(L,U), Atom, Number) :- !,
 | 
						|
	(   integer(L)
 | 
						|
	->  convert_setting_text(integer, Atom, Number)
 | 
						|
	;   convert_setting_text(float, Atom, Number)
 | 
						|
	),
 | 
						|
	must_be(between(L,U), Number).
 | 
						|
convert_setting_text(Type, Atom, Term) :-
 | 
						|
	term_to_atom(Term, Atom),
 | 
						|
	must_be(Type, Term).
 | 
						|
 | 
						|
 |