#!/bin/bash
#  config - configuration file support library

set -e

lib_load 'core/script'


######
# Library Initialization

# core_config_lib_init() - Other libs use config_init, so use lib_init.
core_config_lib_init() {
	# $config_types[] - List of valid types of configuration files
	# Default: ``sys user pkg``
	lib_setting_arrays -ro config_types
	config_types=( sys tool user usertool pkg )

	# $sys_configs[] - Lists the system library configurations to load.
	lib_setting_arrays sys_configs

	# $tool_configs[] - Lists the system tool configurations to load.
	lib_setting_arrays tool_configs

	# $user_configs[] - Lists the user library configurations to load.
	lib_setting_arrays user_configs

	# $usertool_configs[] - Lists the user tool configurations to load.
	lib_setting_arrays usertool_configs

	# $pkg_configs[] - Lists the package configurations to load.
	# Default: `none`
	lib_setting_arrays pkg_configs

	# $config_debug - If set to true `true`, permits errors in the
	# configuration to allow debugging.
	# Default: ``false``
	lib_setting_vars config_debug
	config_debug=${config_debug:-false}
	$debug || debug=$config_debug

	# $config_init_check - Set to `true` to in order to perform
	# early checking of configuration settings, after initialization
	# but prior to loading any configuration files.
	# Default: ``false``
	lib_setting_vars config_init_check
	config_init_check=${config_init_check:-false}
}


######
# Configuration Filenames

# config_sys_filename() - Prints system configuration filename
# (located in ``$confdir``)
config_sys_filename() { echo "$confdir/$1"; }

# config_sys_tool_filename() - Prints system tool configuration filename
# (located in ``$confdir/$script_group/$script_name``)
config_tool_filename() {
	config_sys_filename "$script_group/$script_name/$1"
}

# config_user_filename() - Prints user configuration filename
# (located in ``$user_confdir``)
config_user_filename() { echo "$user_confdir/$1"; }

# config_usertool_filename() - Prints user configuration filename
# (located in ``$user_confdir/$script_group/$script_name``)
config_usertool_filename() {
	config_user_filename "$script_group/$script_name/$1"
}

# config_pkg_filename() - Prints package configuration filename
# (located in ``$srcdir``)
config_pkg_filename() { echo "$1"; }


######
# Configuration Files

# config_script_filenames() - Prints name of script configuration filenames
config_script_filenames() {
	local kind=$1
	case "$kind" in
	(tool) echo "$script_name.conf"; echo "$script_name.local" ;;
	(usertool) echo "$script_name.conf" ;;
	esac
}

# config_lib_filenames() - Prints name of library configuration filenames
config_lib_filenames() {
	local kind=$1
	local name=$2
	case "$kind" in
	(sys|tool) echo "$name.conf"; echo "$name.local" ;;
	(user|usertool) echo "$name.conf" ;;
	esac
}


######
# Public Functions

# config_error() - Generates a warning or error, depending on whether
# ``$config_debug`` is true or false respectively.
config_error() {
	local level
	$config_debug && level=warn || level=error
	$level "$@"
}

######
# Configuration Initialization

# config_init() - Initializes all library and tool settings, prior to
# loading any configuration files.
config_init() {
	for_each_lib config_init_lib
	script_cmd_exec config_init
	# TODO: fix config_check callbacks to permit checking here
	if ! $config_init_check || config_check true; then
		info "configuration settings initialized"
	else
		config_dump
		config_error "configuration initialization failed"
	fi
}

# config_init_lib() - Initializes configuration settings for the named
# library ($1).
config_init_lib() { lib_cmd_exec "$1" config_init; }

######
# Configuration Validation

# config_check() - Ensures all configuration settings are valid
config_check() {
	info "checking configuration settings"
	for_each_lib config_check_lib
	script_cmd_exec config_check
}

# config_check_lib() - Ensures configuration settings are valid for the
# name library ($1).
config_check_lib() { lib_cmd_exec "$1" config_check; }


######
# Configuration File Reading

# config_read() - Reads a single configuration file ($1)
# $1 - filename
config_read() {
	local cfg=$1
	if [ ! -f "$cfg" ]; then
		! $config_debug || info "missing: $cfg"
		return
	fi
	debug "reading: $cfg ..."
	if [ -r "$cfg" ]; then
		eval "$(<"$cfg")"
	else
		eval "$(sudo cat "$cfg")"
	fi
}

# config_read_files() - Reads a list of configuration files ($@)
config_read_files() { for_each config_read "$@"; }


######
# Configuration File Loading

# config_load() - Reads all known configuration files.
config_load() { for_each config_load_kind "${config_types[@]}"; }

# config_load_kind() - Reads configuration files of the given type ($1).
# See ``$config_types[]`` for available types.
config_load_kind() {
	local kind=$1

	info "Loading $kind config files..."
	local -a files=(
		$(for_each_lib config_lib_files $kind)
		$(script_cmd_exec config_files $kind)
		$(config_script_filenames $kind)
		)

	files=( $(for_each "config_${kind}_filename" "${files[@]}") )

	eval "${kind}_configs=( \"\${files[@]}\" )"

	config_read_files "${files[@]}"
}

config_lib_files() {
	local kind=$1
	local name=$2
	config_lib_filenames $kind $name
	lib_cmd_exec $name config_files $kind
}

config_load_lib_kind() {
	local name=$1
	local kind=$2
	local -a files=( $(config_lib_files "$kind" "$name") )
	files=( $(for_each "config_${kind}_filename" "${files[@]}") )
	eval "${kind}_configs+=( \"\${files[@]}\" )"
	config_read_files "${files[@]}"
}

# config_load_lib() - Loads configuration files for the named library ($1)
config_load_lib() {
	local name=$1
	for_each "config_load_lib_kind $name" "${config_types[@]}"
}


######
# Config CLI

# config_usage() - Prints usage for the ``config`` command namespace.
config_usage() {
	cat <<USAGE
<cmd> [<var>]
Configuration Commands:
	files [(all|sys|user|pkg)]	Prints the list of config files
	names				Prints the list of setting names
	values <name>+			Prints the values of the named settings
	show [all|<lib>+]		Prints library settings
	load <library>+			Loads the named libraries
USAGE
}

# config_files() - Prints the named list ($1) of configuration files.
# $1 - Type of configuration files: ``all``, ``sys``, ``user``, or ``pkg``.
config_files() {
	case "$1" in
	(sys|user|pkg) _config_files "$1" ;;
	(''|all) config_files_all ;;
	*) error_usage "$1: unknown type, must be ${config_types[*]}" ;;
	esac
	echo
}

# config_files_all() - Prints a list of all configuration files.
config_files_all() {
	local -a _files
	for_each _config_files "${config_types[@]}"
}

_config_files() { eval "list_insert _files \"\${${1}_configs[*]}\""; }

# config_names() - Prints list of active configuration settings.
config_names() {
	local -a opts
	local class
	for class in "${package_libs[@]}" $(script_symbol); do
		local sym
		sym=$(lib_symbol $class)
		list_append opts $(settings_list_all "$sym")
	done
	echo "${opts[*]}"
}

# config_values() - Prints list of configuration settings name/value pairs
# for the given list of setting names ($@).
config_values() {
	min_args 1 "$@"
	for_each _config_value "$@"
}

_config_value() {
	local name=$1
	local class
	for class in "${package_libs[@]}" $(script_symbol); do
		local sym
		sym=$(lib_symbol $class)
		if in_list "$name" $(settings_list_all "$sym"); then
			_config_show "$sym" "$name"
		fi
	done
}

# config_show() - Prints active configuration settings for a given
# library ($1) or all active scripts (if none given).
# $1 - Library name (optional).  If `null`, prints all script settings.
config_show() {
	max_args 1 "$@"
	local class=${1:-all}

	local -a libs
	if [ "$class" = all ]; then
		libs=( "${lib_loaded[@]}" "$(script_symbol)" )
	else
		libs=( $class )
	fi

	if [ ${#libs[*]} -gt 1 ]; then
		for_each config_show "${libs[@]}"
		return
	fi

	local sym
	sym=$(lib_symbol "$class")
	! is_tool "$class" || sym="$(script_symbol)"

	local -a settings
	settings=( $(settings_list_all $sym) )
	if [ -z "${settings[*]}" ]; then
		return
	fi

	echo "######"
	echo "# '$class' settings"
	for_each "_config_show $sym" "${settings[@]}"
}

_config_show() {
	local sym=$1
	local var=$2
	if in_list "$var" $(settings_list_vars "$sym"); then
		_config_show_var "$var"
	elif in_list "$var" $(settings_list_arrays "$sym"); then
		_config_show_array "$var"
	elif in_list "$var" $(settings_list_assocs "$sym"); then
		_config_show_assoc "$var"
	elif in_list "$var" $(settings_list_funcs "$sym"); then
		_config_show_func "$var"
	else
		error "$sym: $var: not a valid variable name"
	fi
}

_config_show_var() {
	local var=$1
	local -n value=$var
	echo "$var=$(arg_quote "$value")"
}
_config_show_array() {
	local name=$1
	local -a list
	eval "list=( \"\${${name}[@]}\" )"
	list_quote list
	echo "$name=( ${list[*]} )"
}

_config_show_assoc() {
	local name=$1
	local -a keys
	eval "keys=( \"\${!${name}[@]}\" )"
	echo "$name=("
	for_each "_config_show_assoc_item $name" "${keys[@]}"
	echo -e "\t)"
}
_config_show_assoc_item() {
	local name=$1
	local key=$2
	local value
	eval "value=\${$name[\$key]}"
	key=$(arg_quote "$key")
	value=$(arg_quote "$value")
	echo -e "\t\t[$key]=$value"
}

_config_show_func() { eval declare -f "\$$1"; }

View the Developer Guide Index

View the Reference Manual Index


Generated on Fri Jul 28 14:34:53 PDT 2017 by mcsh d14 v0.23.0.