- replace the initial "exec /usr/local/bin/tclkit $0" line with the path to your Tcl interpreter
- replace the "set ScriptURL..." line to point to the URL of this script.
- replace the "set StyleURL..." line to point to the CSS file.
#!/bin/sh #\ exec /usr/local/bin/tclkit $0 source utils.tcl source format.tcl source worddiff.tcl package require http # ScriptURL defines the URL of this script set ScriptURL http://mini.net/revhist.cgi # TclhistURL defines the URL of the tclhist page set TclhistURL http://mini.net/tclhist # WikiURL defines the URL of the Wiki whose history is being displayed set WikiURL http://wiki.tcl.tk # StyleURL defines the URL of the stylesheet used to display this page set StyleURL /wikihist.css # WikiName is the name of the Wiki to be displayed set WikiName "Tcler's Wiki" # This proc determines whether the specified page exists. # It does this by looking for the revision history from Tclhist. # If the returned page doesn't match the expected format, there is # no revision history for this page. # Note that this proc uses the revision history rather than the # actual page -- this is done because I'm guessing that the # revision history is a less taxing operation on the server. proc pageExists {PageNumber} { global TclhistURL set URL $TclhistURL/$PageNumber* set Token [::http::geturl $URL] set PageContents [::http::data $Token] ::http::cleanup $Token # The page contents returned should start with a revision number and date. # The call to regexp returns an indication of whether it does. regexp {^\d+ \d+} $PageContents - } # This proc returns the contents of the specified revision number # of the specified Wiki page. If no revision number is specified, # the latest revision is obtained. # As coded, this obtains it from the CVS repository. If desired, # this could be recoded to get it from a tclhist archive. proc getPageContents {PageNumber {RevNumber Latest}} { global TclhistURL if {[string equal $RevNumber "Latest"]} { set URL $TclhistURL/$PageNumber } else { set URL $TclhistURL/$PageNumber.$RevNumber } set Token [::http::geturl $URL] set PageContents [::http::data $Token] ::http::cleanup $Token return $PageContents } # This proc determines the title of the specified Wiki page. proc getPageTitle {PageNumber} { # Grab a copy of the latest version of the page from CVS. # The title is found on the first line of this page. set PageContents [getPageContents $PageNumber] set TitleLine [lindex [split $PageContents \n] 0] # Now, extract the title from the title line regexp {^Title:\s*(.*)$} $TitleLine junk Title return $Title } # This procedure splits long lines into lines no longer than 80 characters proc splitlongline { line } { set maxlen 80 set thisline "" set lines [list] set opentag 0 foreach word [split $line " "] { if { $opentag || [string first "<" $word] > -1 } { set opentag 1 append thisline " $word" if { [string last ">" $word] > [string last "<" $word] } { set opentag 0 } } else { if { [string equal $thisline ""] } { append thisline $word } else { if { [string length "$thisline $word"] > $maxlen } { if { [llength $lines] > 0 } { set thisline "<span class=\"continue\">... </span>$thisline" } lappend lines $thisline set thisline $word } else { append thisline " $word" } } } } if { [llength $lines] > 0 } { set thisline "<span class=\"continue\">... </span>$thisline" } lappend lines $thisline return [join $lines \n] } # This proc substitutes any special characters with their HTML equivalents. proc quoteHtml {s} { string map { & & < < > > } $s } # For the given Wiki page number, this proc examines Tclhist to determine # the revision number and check-in time of all revisions. The list # returned is ordered from latest revision to earliest revision. # Every revision results in two items in the list, the first being # the minor revision number, and the second being the time that # this revision was checked in, given as the number of # seconds since the Tcl epoch. proc getRevisionList {PageNumber} { global TclhistURL set RevisionList {} set URL $TclhistURL/$PageNumber* set Token [::http::geturl $URL] set AllRevs [::http::data $Token] ::http::cleanup $Token foreach RevisionEntry [split $AllRevs \n] { # Look for all lines with revision numbers. They start with the # revision number and the date in seconds since the Tcl epoch. if {[regexp {^(\d+) (\d+)} $RevisionEntry - RevNumber RevTime]} { lappend RevisionList $RevNumber $RevTime } } return $RevisionList } # This procedure generates the revision history HTML page # for the specified Wiki page number proc genRevisionHistory {PageNumber} { global WikiURL global ScriptURL global StyleURL # Generate the content type for the page (HTML) puts "Content-type: text/html" puts "Pragma: no-cache" puts "" # Determine the title of this page set PageTitle [quoteHtml [getPageTitle $PageNumber]] # Now, generate the page header. puts "<html>" puts "" puts "<head>" puts " <title>Revision History of $PageTitle</title>" puts " <meta content=\"no-cache\" http-equiv=\"Pragma\">" puts " <meta content=\"Mon, 04 Dec 1999 21:29:02 GMT\" http-equiv=\"Expire\">" puts " <link type=\"text/css\" href=\"$StyleURL\" rel=\"stylesheet\">" puts "</head>" puts "" puts "<body bgcolor=\"\#ffffff\">" puts " <h2>" puts " <a href=\"$WikiURL/$PageNumber\">$PageTitle</a>" puts " </h2>" puts " <b>Revision History</b>" puts "" puts " <p>" puts " Legend: (current) = difference with current version," puts " (last) = difference with preceding version," puts " <em>date</em> = that day's version" puts " </p>" puts "" # Now, obtain a list of the revisions and the time that they were # checked in. set RevisionList [getRevisionList $PageNumber] # Now that we've got the list of revisions, we need to generate the # revision information to display on the page. puts " <ul>" set FirstEntry true foreach {RevNumber RevDate} $RevisionList { set DateString [clock format $RevDate -gmt 1] puts " <li>" puts " Version $RevNumber:" if { !$FirstEntry } { puts " (<a href=\"$ScriptURL/$PageNumber-$RevNumber\">current</a>)" } else { set FirstEntry false } if { $RevNumber != 1 } { puts " (<a href=\"$ScriptURL/$PageNumber-$RevNumber-[expr $RevNumber - 1]\">last</a>)" } puts " ..." puts " <a href=\"$ScriptURL/$PageNumber.$RevNumber\">$DateString</a>" } puts " </ul>" puts "" puts "</body>" puts "" puts "</html>" } # This proc renders the specified revision of the specified page number proc renderPageRevision {PageNumber RevNumber} { global StyleURL global ScriptURL global WikiURL set PageContents [getPageContents $PageNumber $RevNumber] # Extract the page title and check-in date, # then strip off the title, date, and site # (first 4 lines of the contents). set TitleLine [lindex [split $PageContents \n] 0] regexp {^Title:\s*(.*)$} $TitleLine junk PageTitle set PageTitle [quoteHtml $PageTitle] set PageContents [join [lrange [split $PageContents \n] 3 end] \n] # Generate the content type for the page (HTML) puts "Content-type: text/html" puts "Pragma: no-cache" puts "" # Now, generate the page header. puts "<html>" puts "" puts "<head>" puts " <title>Revision $RevNumber of $PageTitle</title>" puts " <meta content=\"no-cache\" http-equiv=\"Pragma\">" puts " <meta content=\"Mon, 04 Dec 1999 21:29:02 GMT\" http-equiv=\"Expire\">" puts " <link type=\"text/css\" href=\"$StyleURL\" rel=\"stylesheet\">" puts "</head>" puts "" puts "<body bgcolor=\"\#ffffff\">" puts " <h2>" puts " <small>Revision $RevNumber of</small> <a href=\"$ScriptURL/$PageNumber\">$PageTitle </a>" puts " </h2>" puts "" set HTMLContents\ [lindex [::Wikit::Format::StreamToHTML\ [::Wikit::Format::TextToStream $PageContents]\ "$WikiURL/"] 0] puts $HTMLContents puts "" puts "</body>" puts "" puts "</html>" } # This proc renders the page showing the difference between two revisions # of a page. If only one revision number is given, the difference between # the current page and the specified page is shown. proc renderPageDiff {PageNumber RevNumber {OldRevNumber "Unspecified"}} { global StyleURL global ScriptURL # If the old revision number isn't specified, we do a diff between # the current contents of the page and the specified revision. if { [string equal $OldRevNumber "Unspecified"] } { set PageContents [getPageContents $PageNumber] set OldContents [getPageContents $PageNumber $RevNumber] } else { set PageContents [getPageContents $PageNumber $RevNumber] set OldContents [getPageContents $PageNumber $OldRevNumber] } # Determine the title of the page set TitleLine [lindex [split $PageContents \n] 0] regexp {^Title:\s*(.*)$} $TitleLine junk PageTitle set PageTitle [quoteHtml $PageTitle] # Strip the page header information from the pages set PageContents [join [lrange [split $PageContents \n] 3 end] \n] set OldContents [join [lrange [split $OldContents \n] 3 end] \n] # Generate the HTML description of the differences set LongHtmlChanges [doHtmlDiff context $OldContents $PageContents] # Split long lines set HtmlChanges "" foreach line [split $LongHtmlChanges \n] { append HtmlChanges "[splitlongline $line]\n" } # Generate the content type for the page (HTML) puts "Content-type: text/html" puts "Pragma: no-cache" puts "" # Now, generate the page header. puts "<html>" puts "" puts "<head>" if { [string equal $OldRevNumber "Unspecified"] } { puts " <title>Differences between latest version and version $RevNumber of $PageTitle</title>" } else { puts " <title>Differences between version $RevNumber and version $OldRevNumber of $PageTitle</title>" } puts " <meta content=\"no-cache\" http-equiv=\"Pragma\">" puts " <meta content=\"Mon, 04 Dec 1999 21:29:02 GMT\" http-equiv=\"Expire\">" puts " <link type=\"text/css\" href=\"$StyleURL\" rel=\"stylesheet\">" puts "</head>" puts "" puts "<body bgcolor=\"\#ffffff\">" puts " <h2>" if { [string equal $OldRevNumber "Unspecified"] } { puts " <small>Differences between latest version and version $RevNumber of</small> <a href=\"$ScriptURL/$PageNumber\">$PageTitle </a>" } else { puts " <small>Differences between version $RevNumber and version $OldRevNumber of</small> <a href=\"$ScriptURL/$PageNumber\">$PageTitle </a>" } puts " </h2>" puts { <p> Legend: <br> </p> <blockquote> <pre><span class="context">gray text: context matter</span> <span class="old">red text: old text, or that which has been removed.</span> <span class="new">green text: new text, interesting new knowledge.</span> <span class="newpage">yellow text: new text, brand new page, interestly fresh knowledge.</span></pre> </blockquote> } puts " <p>" puts " Differences:" puts " </p>" puts "" # Display the differences if { [string length $HtmlChanges] == 0 } { puts " <p>" puts " There were no differences between the two versions of the page." puts " </p>" puts "" } else { puts "<blockquote><pre class=\"diff\">$HtmlChanges</pre></blockquote>" puts "" } puts "</body>" puts "" puts "</html>" } catch { if { [info exists env(PATH_INFO)] } { set PathInfo [file tail $env(PATH_INFO)] } else { set PathInfo {} } set ShowInstructions false set NoSuchFile false if { [regexp {^(\d+)(\D)(\d*)$} $PathInfo - page sep rev] } { if { [pageExists $page] } { switch -- $sep { . { renderPageRevision $page $rev } - { renderPageDiff $page $rev } default { set ShowInstructions true } } } else { set NoSuchFile true } } elseif { [regexp {^(\d+)(\D)(\d*)(\D)(\d*)$} $PathInfo\ - page sep1 rev1 sep2 rev2] } { if { [pageExists $page] } { if { ( $sep1 == "-" ) && ( $sep2 == "-" ) } { renderPageDiff $page $rev1 $rev2 } else { set ShowInstructions true } } else { set NoSuchFile true } } elseif { [regexp {^(\d+)$} $PathInfo - page] } { if { [pageExists $page] } { genRevisionHistory $page } else { set NoSuchFile true } } else { set ShowInstructions true } if { $ShowInstructions } { puts "Content-type: text/plain" puts "Pragma: no-cache" puts " This is the historical archive of the $WikiName Examples: See the revision history of page 12: $ScriptURL/12 Retrieve version 5 of page 12: $ScriptURL/12.5 Compare version 5 of page 12 with latest version: $ScriptURL/12-5 Compare version 5 of page 12 with version 2: $ScriptURL/12-5-2 " } elseif { $NoSuchFile } { puts "Content-type: text/plain" puts "Pragma: no-cache" puts "" puts "There is no revision history for Wiki page $page." } } err if 0 { puts #################### puts $err puts $errorInfo }
12may04 jcw - Shane, as always, your contributions are a delight to try out. I've installed it on this site as an experiment for now. Need to look into server load (and making sure bots don't clog it), but I can see no issues with it. Thanks also to Pascal for sharing the diff code.Try http://mini.net/tclrevs/11426 for example. Note that the latest version is there but revisions before it may lag by up to 24 hours, since CVS updates are merged only once a day. The http://mini.net/tclhist/ mechanism also continues to be available.
LV If some day someone else, like me, wanders by using Lynx or some other non-graphical web browser, perhaps we could brain storm on how we could modify this code so that it was not color-dependant for reporting differences. Right now, non-graphical web browsers, as well as color blind users, are unable to see the differences.
LV 2007 August 16 The above code and discussions are relative to version 1 of the wikit. I suspect that it is not relevant to the current incarnation of the wikit, which I think was reimplemented.