blob: 657ffc7c8d4bb1c9ba6593cc92d88860b64c68ee [file] [log] [blame]
#
# init.tcl --
#
#
# Copyright 2002-2017 The Apache Rivet Team
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
package require rivetlib 3.2
# the ::rivet namespace is created in mod_rivet_commoc.c:Rivet_PerInterpInit
# namespace eval ::rivet {} ; ## create namespace
namespace eval ::Rivet {} ; ## create namespace
## ::Rivet::init
##
## Initialize the interpreter with all that Rivet goodness. This is called
## once when this file is loaded (down at the bottom) and sets up the interp
## for all things Rivet.
proc ::Rivet::init {} {
set ::Rivet::init [info script]
set ::Rivet::root [file dirname $::Rivet::init]
set ::Rivet::packages [file join $::Rivet::root packages]
set ::Rivet::rivet_tcl [file join $::Rivet::root rivet-tcl]
## Setup auto_path within the interp to include all the places
## we've stored Rivet's scripts: rivet-tcl, packages, packages-local,
## packages$tcl_version, init_script_dir, and .
## Put these at the head of the list.
set ::auto_path [linsert $::auto_path 0 $::Rivet::root \
$::Rivet::rivet_tcl $::Rivet::packages $::Rivet::packages-local]
## This will allow users to create proc libraries and tclIndex files
## in the local directory that can be autoloaded.
## Perhaps this must go to the front of the list to allow the user
## to override even Rivet's procs.
lappend ::auto_path ${::Rivet::packages}${::tcl_version} .
## As we moved the command set to the ::rivet namespace we
## still want to guarantee the commands to be accessible
## at the global level by putting them on the export list.
## Importing the ::rivet namespace is deprecated and we should
## make it clear in the manual.
if {[string is true -strict [::rivet::inspect ExportRivetNS]]
|| [string is true -strict [::rivet::inspect ImportRivetNS]]} {
set ::rivet::cmd_export_list \
[tcl_commands_export_list $::Rivet::rivet_tcl]
## init.tcl is run by mod_rivet (which creates the ::rivet
## namespace) but it gets run standalone by mkPkgindex during
## the installation phase. We have to make sure the procedure
## won't fail in this case, so we check for the existence of
## the variable.
namespace eval ::rivet {
## Commands in cmd_export_list are prefixed with ::rivet,
## so we have to remove it to build an export list.
set export_list [list]
foreach c $cmd_export_list {
lappend export_list [namespace tail $c]
}
namespace export {*}$export_list
}
}
## If we are running from within mod_rivet we have already
## defined ::rivet::exit (mod_rivet_common.c: Rivet_PerInterpInit)
## and we move Tcl's exit command out of the way and replace it with
## our own that handles bailing from a page request properly.
if {[info commands ::rivet::exit] != ""} {
rename ::exit ::Rivet::tclcore_exit
proc ::exit {code} {
if {![string is integer -strict $code]} { set code 0 }
::rivet::exit $code
}
}
## If Rivet was configured for backward compatibility, import commands
## from the ::rivet namespace into the global namespace.
if {[string is true -strict [::rivet::inspect ImportRivetNS]]} {
uplevel #0 { namespace import ::rivet::* }
}
#unset -nocomplain ::module_conf
}
###
## This routine gets called each time a new request comes in.
## It sets up the request namespace and creates a global command
## to replace the default global. This ensures that when a user
## uses global variables, they're actually contained within the
## namespace. So, everything gets deleted when the request is finished.
###
proc ::Rivet::initialize_request {} {
catch { namespace delete ::request }
namespace eval ::request {}
proc ::request::global {args} {
foreach arg $args {
uplevel "::global ::request::$arg"
}
}
}
## ::Rivet::handle_error
##
## If an ErrorScript has been specified, this routine will not be called.
proc ::Rivet::handle_error {} {
puts "<pre>$::errorInfo<hr/><p>OUTPUT BUFFER:</p>$::Rivet::script</pre>"
}
## ::Rivet::request_handling
##
## Process the actual request. This is the main handler for each request.
## This collects all of the necessary BeforeScripts, AfterScripts, and
## other bits and calls them in order.
proc ::Rivet::request_handling {} {
::try {
uplevel #0 ::Rivet::initialize_request
} on error {err} {
::rivet::apache_log_error crit \
"Rivet request initialization failed: $::errorInfo"
}
::try {
set script [::rivet::inspect BeforeScript]
if {$script ne ""} {
set ::Rivet::script $script
uplevel #0 $script
}
set script [::rivet::url_script]
if {$script ne ""} {
set ::Rivet::script $script
namespace eval ::request $script
}
set script [::rivet::inspect AfterScript]
if {$script ne ""} {
set ::Rivet::script $script
uplevel #0 $script
}
} trap {RIVET ABORTPAGE} {err opts} {
::Rivet::finish_request $script $err $opts AbortScript
} trap {RIVET THREAD_EXIT} {err opts} {
::Rivet::finish_request $script $err $opts AbortScript
} on error {err opts} {
::Rivet::finish_request $script $err $opts
} finally {
::Rivet::finish_request $script "" "" AfterEveryScript
}
}
## ::Rivet::finish_request
##
## Finish processing the request by checking our error state and executing
## whichever script we need to close things up. If this script results in
## an error, we'll try to call ErrorScript before bailing.
proc ::Rivet::finish_request {script errorCode errorOpts {scriptName ""}} {
set ::Rivet::errorCode $errorCode
set ::Rivet::errorOpts $errorOpts
if {$scriptName ne ""} {
set scriptBody [::rivet::inspect $scriptName]
::try {
uplevel #0 $scriptBody
} on ok {} {
return
} on error {} {
::rivet::apache_log_error err \
"Rivet $scriptName failed: $::errorInfo"
print_error_message "Rivet $scriptName failed"
}
}
set error_script [::rivet::inspect ErrorScript]
if {$error_script eq ""} {
set ::errorOutbuf $script ; ## legacy variable
set error_script ::Rivet::handle_error
}
::try {
set ::Rivet::script $script
uplevel #0 $error_script
} on error {err} {
::rivet::apache_log_error err "Rivet ErrorScript failed: $::errorInfo"
print_error_message "Rivet ErrorScript failed"
}
}
## ::Rivet::print_error_message
##
## This message should be transparently equivalent to the
## Rivet_PrintErrorMessage function in mod_rivet_generator.c
proc ::Rivet::print_error_message {error_header} {
puts "<strong>$error_header</strong><br/><pre>$::errorInfo</pre>"
}
## ::Rivet::tcl_commands_export_list
##
## this is temporary hack to export names of Tcl commands in rivet-tcl/.
## This function will be removed in future versions of Rivet and it's
## meant to provide a basic way to guarantee compatibility with older
## versions of Rivet (see code in ::Rivet::init)
proc ::Rivet::tcl_commands_export_list {tclpath} {
# we collect the commands in rivet-tcl by reading the tclIndex
# file and then we extract the command list from auto_index
namespace eval ::Rivet::temp {}
set ::Rivet::temp::tclpath $tclpath
namespace eval ::Rivet::temp {
variable auto_index
array set auto_index {}
# the auto_index in ${tclpath}/tclIndex is loaded
# this array is used to fetch a list of Rivet commands
# implemented in Rivet
set dir $tclpath
source [file join $tclpath tclIndex]
# Rivet Tcl commands not meant to go onto the export list must
# be unset from auto_index here
unset auto_index(::rivet::catch)
unset auto_index(::rivet::try)
}
set commands [namespace eval ::Rivet::temp {array names auto_index}]
# we won't leave anything behind
namespace delete ::Rivet::temp
return $commands
}
::Rivet::init
package provide Rivet 3.2