#!/bin/sh
# \
exec tclkit "$0" ${1+"$@"}
proc Puts {args} {}
Puts ">> start\t [clock clicks]"
proc DoCommit {} {
mk::file commit doc
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
proc InitDatabase {} {
global rootName fdHist
set fdHist [open ${rootName}.hist a+]
catch {puts $fdHist "\n# [GetTimeStamp]"}
package require Mk4tcl 1.1
catch {mk::file open doc wtry.dat -nocommit} ;# not in scripted doc
mk::view layout doc.scripts {name date:I size:I checked:I text:M}
mk::view layout doc.pages {name date:I contents:M who count:I
format:I page cached:I refs}
mk::view layout doc.archived {name date:I diff:M who last:I lines:I}
if ![mk::view size doc.pages] { # the core pages
set date [clock seconds]
foreach name {Home WiKit Search Help Recent History 6 7 8 9} {
mk::row append doc.pages name $name format 1 page \n date $date
}
DoCommit
}
# convert database format 0 to 1 if necessary
if {[mk::get doc.pages!0 page] == ""} {
proc -A- {id} {
return "\[[mk::get doc.pages!$id name]\]"
}
proc -P- {} {}
proc -HR- {} {return ----}
proc -UL- {text} {return " * $text\n"}
proc -OL- {text} {return " 1. $text\n"}
proc -DL- {label text} {return " $label: $text\n"}
proc -PRE- {text} {return " $text\n"}
mk::loop c doc.pages {
set page [mk::get $c contents]
if [regexp -nocase {.\.(tcl|txt)$} [mk::get $c name]] {
mk::set $c page $page format 0
} else {
set page [subst -nobackslashes -novariables $page]
mk::set $c page $page format 1
}
Puts "conv $c" ;#\n============\n$page\n========="
}
}
}
proc AcquireLock {{maxAge 3600}} {
global rootName
set lockFile ${rootName}.lock
foreach try {1 2 3 4 5 6 7 8 9 10} {
if ![catch {open $lockFile {CREAT EXCL WRONLY}} fd] {
close $fd
return 1
}
after 3000
}
# if the file is older than maxAge, we grab the lock anyway
return [expr {[clock seconds] > [file mtime $lockFile] + $maxAge}]
}
proc ReleaseLock {} {
global rootName
file delete ${rootName}.lock
}
proc PageLookup {name} {
set n [mk::select doc.pages -count 1 name $name]
if {$n == ""} {
set n [mk::view size doc.pages]
mk::set doc.pages!$n name $name
DoCommit
}
return $n
}
proc SavePage {id text who {name ""}} {
if {$name != ""} {
set type [expr {![regexp {.\.(txt|tcl)$} $name]}]
Puts "save page $id title [list $name] type $type"
mk::set doc.pages!$id name $name format $type
}
set text [string trimright $text]
append text \n
mk::set doc.pages!$id date [clock seconds] page $text who $who
AddLogEntry $id
DoCommit
}
proc PageType {id} {
return [mk::get doc.pages!$id format]
}
proc AddLogEntry {id} {
global fdHist
set c doc.pages!$id
# allow failure if log file cannot be written
catch {
seek $fdHist 0 end
set pos [tell $fdHist]
mk::row append doc.archived name [mk::get $c name] \
date [mk::get $c date] \
who [mk::get $c who] last $pos
puts $fdHist ""
puts $fdHist [ExportAsTcl $id]
flush $fdHist
}
}
proc GetTimeStamp {} {
clock format [clock seconds] -gmt 1 -format {%Y/%m/%d %T}
}
# generate a pseudo "Set" command which can be used to restore a page
proc ExportAsTcl {id} {
array set a [mk::get doc.pages!$id]
if {$a(date) == 0 || [string length $a(page)] <= 1} {
return
}
list Set $id name $a(name) date $a(date) who $a(who) \
format $a(format) page $a(page)
}
proc ExportAll {file} {
set fd [open $file w]
puts $fd "\n# [GetTimeStamp]"
puts $fd "\nReset [mk::view layout doc.pages]"
mk::loop c doc.pages {
set s [ExportAsTcl [mk::cursor position c]]
if {$s != ""} {
puts $fd "\n$s"
}
}
close $fd
}
proc ImportAll {file} {
proc Reset {layout} {
global fdHist
mk::view size doc.pages 0
DoCommit
mk::view layout doc.pages $layout
DoCommit
puts $fdHist "\n# [GetTimeStamp]"
puts $fdHist "\nReset [mk::view layout doc.pages]"
}
proc Set {id args} {
eval mk::set doc.pages!$id $args
AddLogEntry $id
}
source $file
DoCommit
file rename -force $file ${file}.bak
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
proc Wiki {name args} {
if {$name == "-"} {
set name [mk::get doc.pages![lindex $args 0] name]
}
link - $name "$::env(SCRIPT_NAME)/[join $args ?]"
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
proc Expand {type str} {
set str [TextToStream $str]
Puts "
TYPE $type STR '$str'
"
Context_$type $str
}
proc InfoProc {ref} {
set id [PageLookup $ref]
set date [mk::get doc.pages!$id date]
if {$date == 0} {
append id @ ;# enter edit mode for missing links
}
list $id [mk::get doc.pages!$id name] $date
}
proc Context_HTML {str} {
puts [StreamToHTML $str $::env(SCRIPT_NAME)/ InfoProc]
}
proc Context_Edit {str} {
StreamToText $str
}
proc Context_Tk {str} {
global D
set result [StreamToTk $str InfoProc]
Puts "\nURLS:"
foreach {a b c} [lindex $result 1] {
Puts "$a $b\t$c"
set tag $a$b
$D tag bind $tag "$D tag configure $tag -foreground red"
$D tag bind $tag "$D tag configure $tag -foreground blue"
if {$a == "u" || $a == "x"} {
$D tag configure $tag -font {Times 12 underline}
}
if {$a == "g"} {
set id [PageLookup $c]
$D tag bind $tag "ShowPage $id"
}
}
return [lindex $result 0]
}
proc OldRefList {str} {
proc -A- {id} {
return "\[[mk::get doc.pages!$id name]\]"
}
proc -P- {} {}
proc -HR- {} {return ----}
proc -UL- {text} {return " * $text\n"}
proc -OL- {text} {return " 1. $text\n"}
proc -DL- {label text} {return " $label: $text\n"}
proc -PRE- {text} {return " $text\n"}
subst -nobackslashes -novariables $str
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
proc ProcessCGI {} {
package require cgi 0.7
input "n=1"
suffix ""
admin_mail_addr jcw@equi4.com
#debug -on
cgi_eval {
set path ""
catch {set path $::env(PATH_INFO)}
set query ""
catch {set query $::env(QUERY_STRING)}
set cmd ""
if {![regexp {^/([0-9]+)(.?)$} $path x N cmd] || $N >= [mk::view size doc.pages]} {
set N 0
}
#puts "path $path query $query N $N $::env(PATH_INFO)"
set n doc.pages!$N
set self $::env(SCRIPT_NAME)
set host $::env(REMOTE_HOST)
# not used?
set http http://$::env(SERVER_NAME)
if {$::env(SERVER_PORT) != 80} {
append http : $::env(SERVER_PORT)
}
append http $self
set name [mk::get $n name]
# set up a few standard URLs
set Refs "[mk::get $n count] [Wiki References $N!]"
set Links "[Wiki Expand $N*]"
set Edit "Edit [Wiki - $N@]"
set Home "Go to [Wiki - 0]"
set About "About [Wiki - 1]"
set Search "[Wiki Search 2]"
set date [mk::get $n date]
if {$date != 0} {
set date [clock format $date -gmt 1 -format {%d %b %Y, %R GMT}]
}
set sep " - "
set menu [italic "Updated $date [nbspace] - [nbspace] $Edit[nl]"]
append menu [font size=-1 "$Search - $Links - $Refs - $About - $Home"]
# editScript is used generate an editing form
set editScript {
puts [h2 [Wiki - $N]]
form $self/$N {
set C [mk::get $n page]
textarea C=$C rows=30 cols=72 wrap=virtual
p
submit_button "= Save "
if {$date != 0} {
puts " [nbspace] [nbspace] [nbspace] "
puts [italic "Last saved on [bold $date]"]
}
}
#parray ::env
}
# now dispatch on the type of request
switch -- $cmd {
@ { # called to generate an edit page
cgi_http_head {
cgi_content_type
pragma no-cache
}
cgi_title $name
cgi_body {
eval $editScript
}
}
! { # called to generate a page with references
cgi_title $name
h3 "RRRRRRRRRRReferences are not yet available..."
}
* { # called to generate a page with references
cgi_title $name
h3 "EXXXXXXXXXXpansions are not yet available..."
}
default { # called to optionally save, and then display a page
catch {
if {[import C] != "" && $N != ""} {
SavePage $N $C $::env(REMOTE_HOST)
}
}
cgi_http_head {
cgi_content_type
pragma no-cache
}
cgi_title $name
cgi_body {
puts [h2 [Wiki - $N!]]
set C [mk::get $n page]
switch [PageType $N] {
0 {
preformatted {puts [quote_html $C]}
}
1 {
puts [Expand HTML $C]
}
}
hr noshade
puts $menu
}
}
}
}
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
proc ShowPage {{id ""}} {
global D urlSeq currMode
set urlSeq 0
set id [History $id]
set name [mk::get doc.pages!$id name]
set page [mk::get doc.pages!$id page]
wm title . $name
.n.back configure -text Back -command "ShowPage"
.n.home configure -text Home -command "ShowPage 0"
.n.edit configure -text Edit -command "EditPage"
.n.mode configure -text Search:
set currMode ""
$D configure -state normal -font {Times 12}
$D delete 1.0 end
#set cmd "$D insert end {$name} title {\n\n} body "
set cmd "$D insert end {$name} title "
switch [PageType $id] {
0 {
Puts "FIXED: <$page>"
append cmd [list \n\n$page fixed] ;# because Expand Tk inserts \n's
}
1 {
append cmd [Expand Tk $page]
Puts \n===============================$cmd
}
}
eval $cmd
Puts "EVAL: $cmd\n"
$D configure -state disabled
focus .n.enter
}
proc EditPage {} {
global D currMode pageStack
set id [lindex $pageStack end]
.n.back configure -text Cancel -command "ShowPage $id" -state normal
.n.home configure -text Save -command \
"SavePage $id \[$D get 1.0 end\] local \[.n.enter get\]; ShowPage $id"
.n.edit configure -text History -command "ShowPage 5" ;# and more!!!
.n.mode configure -text "Edit Title:"
set currMode [mk::get doc.pages!$id name]
$D configure -state normal -font {Courier 10}
$D delete 1.0 end
$D insert end [mk::get doc.pages!$id page] fixed \n fixed
$D mark set insert 1.0
focus $D
}
proc History {page} {
global pageStack
if {$page == ""} { # pop last page
set pageStack [lreplace $pageStack end end]
set page [lindex $pageStack end]
} else { # push specified page
if {$page != [lindex $pageStack end]} {
lappend pageStack $page
}
}
set state normal
if {[llength $pageStack] <= 1} {set state disabled}
.n.back configure -state $state
Puts "history $page: $pageStack"
return $page
}
proc LoadPreferences {} {
global Prefs
array set Prefs {
}
catch { array set Prefs [mk::get doc.scripts!10 text] }
}
proc SavePreferences {} {
global Prefs
# avoid commits if preferences did not change
catch {
set new [array get Prefs]
if {[catch {mk::get doc.scripts!10 text} old] || $new != $old} {
Puts $new
mk::set doc.scripts!10 text $new
DoCommit
}
}
}
proc LocalInterface {} {
global D currMode pageStack
set pageStack ""
frame .n
pack .n -fill x
frame .s
pack .s -side bottom -fill x
button .n.back -width 7
button .n.home -width 7
button .n.edit -width 7
label .n.mode -width 7 -anchor e
entry .n.enter -textvariable currMode
button .n.help -width 6 -text Help -command "ShowPage 3"
pack .n.back .n.home .n.edit .n.mode -side left -padx 4 -pady 4
pack .n.enter -side left -padx 4 -expand 1 -fill x
pack .n.help -side left -padx 4 -pady 4
label .s.status
pack .s.status -side left
set S .s.status
scrollbar .scroll -command ".details yview"
text .details -yscrollcommand ".scroll set" -width 72 \
-height 20 -state disabled -wrap word -font {Times 12}
pack .scroll -side right -fill y
pack .details -expand 1 -fill both
set D .details
$D tag configure title -font {Times 18 bold} -lmargin1 3 -lmargin2 3
$D tag configure fixed -font {Courier 10} -lmargin1 3 -lmargin2 3
$D tag configure body -font {Times 12} -lmargin1 3 -lmargin2 3
$D tag configure url -foreground blue
$D tag configure ul -font {Times 12} -lmargin1 3 -lmargin2 30 -tabs 30
$D tag configure ol -font {Times 12} -lmargin1 3 -lmargin2 30 -tabs 30
$D tag configure dt -font {Times 12} -lmargin1 3 -lmargin2 30 -tabs 30
$D tag configure dl -font {Times 12} -lmargin1 30 -lmargin2 30 -tabs 30
$D tag configure i -font {Times 12 italic}
$D tag configure b -font {Times 12 bold}
$D tag configure bi -font {Times 12 bold italic}
# support for horizontal lines
$D tag configure thin -font {Times 4}
$D tag configure hr -relief sunken -borderwidth 1 -wrap none
bind $D {%W tag configure hr -tabs %w}
ShowPage 0
#raise .
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# format codes are:
# 0: old style wiki
# 1: package require format1 1.0
# script numbers are:
# 0: wtry.tcl
# 1: cgi.tcl
# 2: format1.tcl
# 3: Html_library.tcl
# 4: http.tcl
set rootName [file join [file dirname [info script]] \
[file rootname [info script]]]
if ![AcquireLock] {puts "Can't lock: ${rootName}.lock"; exit 1} ;# the laziest way out
Puts ">> locked\t [clock clicks]"
set failed [catch {
if [catch {source format1.tcl}] {
eval [mk::get doc.scripts!2 text]
}
package require Format1
namespace import Format1::*
InitDatabase
if [file exists ${rootName}.export] {
ExportAll ${rootName}.export
}
if [file exists ${rootName}.import] {
ImportAll ${rootName}.import
}
if [info exists ::env(SCRIPT_NAME)] {
if [catch {source cgi.tcl}] {
eval [mk::get doc.scripts!1 text]
}
ProcessCGI
} else {
if {[info command winfo] == ""} {
foreach hasTk [info loaded] {
if {$hasTk == "{} Tk"} {load "" Tk; break}
}
}
package require Tk 8.0
Puts ">> Tk 8.0\t [clock clicks]"
if [catch {source Html_library.tcl}] {
eval [mk::get doc.scripts!3 text]
}
Puts ">> html_lib\t [clock clicks]"
if [catch {source http.tcl}] {
eval [mk::get doc.scripts!4 text]
}
package require http 2.0
Puts ">> http_lib\t [clock clicks]"
LoadPreferences
if [info exists Prefs(geometry)] {
after idle {
update idletasks
wm geometry . $Prefs(geometry)
}
}
LocalInterface
bind . {
set Prefs(geometry) [winfo geometry .]
set Quit 1
}
vwait Quit
SavePreferences
}
} errMsg]
set savedInfo $errorInfo
ReleaseLock
if $failed {
error $errMsg $savedInfo
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -