# feature.tcl --
#
#	Common parts and tools needed by all the installable
#       features.
#
# AUTHORS:      Jacob Levy                      Laurent Demailly
#
# Copyright (c) 1997 Sun Microsystems, Inc.
# Copyright (c) 2000 by Scriptics Corporation.
# Copyright (c) 2004 ActiveState Corporation.
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
# RCS:  @(#) $Id: feature.tcl,v 1.3 2004/05/18 17:44:48 hobbs Exp $

# We provide the "safefeature" package
# WARNING: Only the policy package is ever supposed to require
#          this package. package loading here is used as a
#          convenience by the policy mechanism.

# Nb: the version number for the policy package itself and
# the package require safefeature line from policy.tcl need
# to be changed whenever this version here is changed
# because safefeature can not be required directly but only
# through policy and it's init (path problem)
package provide safefeature 1.1

if {[llength [info command ::safe::policy]] == 0} {
    error "the safefeature package should be loaded only by policy"
}

# We rely on the api set provided by policy 1.x (safe base + policy extensions,
# who called us in fact)  {ie mediated error, log, aliasing and invoking}
# we don't do the package require because it must have been installed
# by our loader (policy)
#
# package require policy 1.2

# Likewise, though we need the config parsing functions, we will
# not require it here.
# Nb: the cfg package must have been initialized before features are called
# otherwise cfg::implNs and cfg::slaveNs will certainly not be defined
#
# package require cfg 1.0

# Same for logging
# package require pluglog 1.0

namespace eval ::safefeature {
    # Public entry point:
    namespace export setup importAndCheck checkArgs safelog

    # List of the namespaces from which we need import functions
    # into our child namespaces
    # (empty namespace mean the function is defined in this namespace)
    variable importList
    set importList {
	cfg  {allowed getConstant}
	safe {error interpAlias invokeAndLog}
	safefeature {safelog}
	{}   {checkArgs}
    }

    # List of procs in the application specific code that our sub-features
    # might use:
    variable implCmdList {iget iexists ISet IUnset}

    # What is the implNs (will be filled at init time (when policy's
    # initialize this package)
    variable implNs
}

proc ::safefeature::safelog {args} {
    catch {uplevel 1 [list ::pluglog::log] $args}
}

# Utility function that import and check that the import actually
# worked from a source to a destination namespace a given command
proc ::safefeature::importAndCheck {destNs srcNs cmd} {
    if {$destNs eq $srcNs} { return }
    namespace eval $destNs [list namespace import ${srcNs}::${cmd}]
    if {[llength [info commands ${destNs}::${cmd}]] == 0} {
	error "failed import \"$cmd\" from \"${srcNs}::\"\
		    to \"${destNs}::\""
    }
}


# Utility function to check if some configure like arguments are
# in allowed set
proc ::safefeature::checkArgs {allowedList usage flag {withDash 1}} {
    safelog {} "flag=\"$flag\", withDash=$withDash,\
		allowedList=\"$allowedList\""
    if {$withDash} {
	if {![string match "-*" $flag]} {
	    error "bad option \"$flag\": $usage"
	}
	set testFlag [string range $flag 1 end]
    } else {
	set testFlag $flag
    }
    if {[lsearch -exact $allowedList $testFlag] >= 0} {
	return $flag
    } else {
	error "disallowed option \"$flag\": $usage"
    }
}

# Init
proc ::safefeature::init {} {

    # Where the actual implementation (application specific) is
    # The application has to provide this namespace and the "iget"
    # function in it (used in urlIsOk) and the actual implementation
    # for some application specific aliases (like the url features set)

    variable implNs $::cfg::implNs

    # If the iget proc does not yet exist in the slave
    # we get it from the "implNs"

    if {[llength [info commands ::cfg::iget]] == 0} {
	safelog {} "Installing iget in ::cfg"
	importAndCheck ::cfg $implNs iget
    }

    # Now setup ourselves (we want access to common utilities
    # (log, error...) too)
    setup [namespace current]

    return $implNs
}

# Setup common parts:
proc ::safefeature::setup {namespace} {
    safelog {} "setting up namespace $namespace"

    variable importList
    variable implCmdList
    variable implNs

    # Where we will install the alias in the slave
    # (for the aliases which implements new functionality and 
    #  hence are added  in a namespace in the target (url features set))

    set slaveNs $::cfg::slaveNs

    foreach {ns cmdList} $importList {
	if {[string equal $ns ""]} {
	    set ns [namespace current]
	}
	if {[string equal $ns $namespace]} {
	    safelog {} "setting up $namespace, skipping $cmdList self imports"
	    continue
	}
	foreach cmd $cmdList {
	    importAndCheck $namespace ::$ns $cmd
	}
    }

    # Transfer a copy of needed variables
    foreach var {implNs slaveNs} {
	namespace eval $namespace [list variable $var [set $var]]
    }

    # Add the application specific call backs
    foreach cmd $implCmdList {
	importAndCheck $namespace $implNs $cmd
    }

    # Most functions use "nsc" as a short cut for [namespace current]
    namespace eval $namespace [list variable nsc $namespace]
}
