This is a drop-in replacement for
TclHttpd's Not Found handler.
It provides additional functionality, before falling back to the default (which is to return a substituted file /notfound.tml)
First, some well-known suffixes are searched for in global/centralised locations: .css in /css, .js in /jscript, .{gif,jpg} in /images ... to which the request is redirected.
Otherwise a file called .nf is sought in directories from the current up to root, in order. The .nf file is substituted as a template.
Finally, if there are no .nf files, the /notfound.tml URL is returned, as the default behavior.
This is useful to permit, say, creation of new files on a wiki. The .nf template is free to perform whatever processing it wants (e.g. creating a new template for the file which wasn't found, then redirecting to the failed URL.)
# notfound handler to provide centralised repository of .css and image files
# also searches path from docroot down for files called .nf, which it
package require struct
rename Doc_NotFound Doc_Notfound_org
proc Doc_NotFound {sock} {
global Doc Referer
upvar #0 Httpd$sock data
#Stderr "Not Found $data(suffix)"
# try to redirect some well-known types .css, .jpg etc
# to centralised repositories
set redirect ""
switch [file extension $data(suffix)] {
.css {
set redirect [file join /css [file tail $data(suffix)]]
}
.js {
set redirect [file join /jscript [file tail $data(suffix)]]
}
.gif -
.jpg {
set redirect [file join /images [file tail $data(suffix)]]
}
}
if {($redirect ne "") && ([string trim $data(suffix) /] ne [string trim $redirect /])} {
Redirect_QuerySelf $sock $redirect
} else {
# fallback to using a .nf file in each directory on the path
# as a template to generate the desired file
if {[catch {Doc_GetPath $sock} path]} {
set path {}
}
foreach dir [::struct::list::Lreverse $path] {
set file [file join $dir .nf]
if {[file exists $file]} {
global Template
set html [TemplateInstantiate $sock $file $data(path) $data(suffix) dynamic \
$Template(templateInterp)]
# If the content type was set, use it. Otherwise, use the default.
if {[info exists data(contentType)]} {
set ctype $data(contentType)
} else {
set ctype "text/html"
}
if {$dynamic} {
return [Httpd_ReturnData $sock $ctype $html]
} else {
# Use ReturnFile so remote end knows it can cache the file.
# This file may have been generated by TemplateInstantiate above.
return [Httpd_ReturnFile $sock $ctype $data(path)]
}
}
}
# finally, default to the old behavior
Doc_Notfound_org $sock
}
}