2241 lines
69 KiB
Tcl
2241 lines
69 KiB
Tcl
|
#!/bin/sh
|
||
|
# \
|
||
|
exec wish "$0" ${1+"$@"}
|
||
|
|
||
|
if {$tcl_version < 7.6} {
|
||
|
puts stderr "requires Tcl 7.6 or later"
|
||
|
exit
|
||
|
}
|
||
|
|
||
|
# teki.tcl --
|
||
|
#
|
||
|
# The Tcl Extension Kit/Installer. This file manages packages for
|
||
|
# Tcl/Tk
|
||
|
|
||
|
# Notes about the organization of this file:
|
||
|
# Major sections are delimited by a string of "-" characters -- e.g. "-------"
|
||
|
# At the beginning of each major section, the purpose and routines in that
|
||
|
# section are described.
|
||
|
# teki-specific packages reside in the "tekilib" directory below teki.tcl
|
||
|
|
||
|
#set tk_version 7.6
|
||
|
set home [string trimright [file dirname [info script]] ./]
|
||
|
set home [file join [pwd] $home tekilib]
|
||
|
lappend auto_path $home
|
||
|
set TekiInfo(library) $home
|
||
|
unset home
|
||
|
|
||
|
if [catch {package require http}] {
|
||
|
set TekiInfo(http) 0
|
||
|
} else {
|
||
|
set TekiInfo(http) 1
|
||
|
}
|
||
|
|
||
|
package require Debug
|
||
|
package require Undo
|
||
|
|
||
|
# Define some useful fonts
|
||
|
|
||
|
if [info exists tk_version] {
|
||
|
set TekiInfo(gui) 1
|
||
|
set majorversion [lindex [split $tk_version .] 0]
|
||
|
if {$majorversion >= 8} {
|
||
|
set TekiInfo(varfont) [font create -family Times -weight bold -size 18]
|
||
|
set TekiInfo(fixedfont) [font create -family Courier -size 10]
|
||
|
} else {
|
||
|
set TekiInfo(varfont) *-Times-Bold-R-Normal--*-240-*-*-*-*-*-*
|
||
|
set TekiInfo(fixedfont) -*-Courier-Medium-R-Normal--*-120-*-*-*-*-*-*
|
||
|
}
|
||
|
unset majorversion
|
||
|
} else {
|
||
|
set TekiInfo(gui) 0
|
||
|
}
|
||
|
|
||
|
if $TekiInfo(gui) {
|
||
|
package require Progress-Tk
|
||
|
package require Wise
|
||
|
} else {
|
||
|
package require Progress-Tcl
|
||
|
}
|
||
|
|
||
|
# -----------------------------------------------------------------------
|
||
|
#
|
||
|
# Error handling, general UI commands.
|
||
|
#
|
||
|
# This section contains code to handle errors and print messages.
|
||
|
# It defines basic functions whose interface can be a GUI (if we're
|
||
|
# running in wish) or a text based interface (if we're running in
|
||
|
# a shell)
|
||
|
|
||
|
# TekiError prints an error message
|
||
|
# TekiWarning is called to ask the user for confirmation or a decision
|
||
|
# (such as deleting a file or set of files)
|
||
|
|
||
|
if $TekiInfo(gui) {
|
||
|
proc TekiError {msg} {
|
||
|
bgerror "$msg"
|
||
|
}
|
||
|
proc TekiWarning {msg default options} {
|
||
|
Wise_Message "Warning" $msg $options 0
|
||
|
}
|
||
|
} else {
|
||
|
proc TekiError {msg} {
|
||
|
puts stderr $msg
|
||
|
}
|
||
|
proc TekiWarning {msg default options} {
|
||
|
global x
|
||
|
puts stderr "Warning: $msg"
|
||
|
set len [llength $options]
|
||
|
set done 0
|
||
|
while {!$done} {
|
||
|
for {set i 0} {$i < $len} {incr i} {
|
||
|
puts stderr "Type $i to [lindex $options $i]"
|
||
|
}
|
||
|
set x [string trim [gets stdin]]
|
||
|
if [string match {[0-9]} "$x"] {
|
||
|
set done [expr ($x >=0) && ($x < $len)]
|
||
|
}
|
||
|
}
|
||
|
return $x
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# -----------------------------------------------------------------------
|
||
|
#
|
||
|
# User Interface
|
||
|
#
|
||
|
# This section of code creates and manages the user interface. The UI
|
||
|
# consists of a main window with two parts (top and bottom), plus a menu
|
||
|
# bar. The top contains the readme file of the currently selected package,
|
||
|
# the bottom a list of packages installed on the system. The
|
||
|
# functions TekiCreateMenus and TekiCreateWindow create this interface.
|
||
|
# The function TekiCreateUI creates the menus and main window (by
|
||
|
# calling TekiCreateMenus and TekiCreateWindow).
|
||
|
#
|
||
|
# The function TekiUpdateReadme redraws the top portion, and should
|
||
|
# be called whenever the current package -- TekiInfo(currPackage) -- is changed.
|
||
|
# The function TekiUpdateBrowser redraws the bottom, and should be
|
||
|
# called whenever tclPkgInfo(installed) changes. The function
|
||
|
# TekiBrowserCallback is called when the user clicks in the bottom
|
||
|
# area, selecting a new package. The functions TekiDoFileInstall,
|
||
|
# TekiDoFileUninstall, and TekiDoFileExit, are called when the user
|
||
|
# selects File->Install, File->Uninstall, or File->Exit menu items,
|
||
|
# respectively. DoFileInstall pops up a file selection dialog,
|
||
|
# then verifies and installs the file.
|
||
|
|
||
|
set TekiInfo(currPackage) tcl[info tclversion]
|
||
|
|
||
|
#
|
||
|
# TekiCreateMenus
|
||
|
# Create the menu bar across the top of the window
|
||
|
#
|
||
|
# Arguments:
|
||
|
# None
|
||
|
#
|
||
|
proc TekiCreateMenus {} {
|
||
|
global tclPkgInfo TekiInfo
|
||
|
Debug_Enter TekiCreateMenus
|
||
|
frame .menu -relief raised -bd 2
|
||
|
pack .menu -side top -fill x
|
||
|
|
||
|
# File Menu
|
||
|
set m .menu.file.m
|
||
|
menubutton .menu.file -text File -menu $m -underline 0
|
||
|
pack .menu.file -side left
|
||
|
menu $m
|
||
|
$m add command -label "Install" -underline 0 -command TekiDoFileInstall
|
||
|
$m add command -label "Uninstall" -underline 1 -command TekiDoFileUninstall
|
||
|
$m add separator
|
||
|
$m add command -label "Exit" -underline 1 -command TekiDoFileExit
|
||
|
|
||
|
# WWW Menu
|
||
|
set m .menu.www.m
|
||
|
menubutton .menu.www -text WWW -menu $m -underline 0
|
||
|
pack .menu.www -side left
|
||
|
menu $m
|
||
|
$m add command -label "Update" -underline 0 -command TekiDoWWWUpdate
|
||
|
$m add command -label "Browse" -underline 1 -command TekiDoWWWBrowse
|
||
|
|
||
|
if {$TekiInfo(http) == 0} {
|
||
|
.menu.www.m entryconfigure Browse -state disabled
|
||
|
.menu.www.m entryconfigure Update -state disabled
|
||
|
}
|
||
|
|
||
|
Debug_Leave
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# TekiCreateWindow
|
||
|
# Create the main window -- this window lists the installed packages, and has a
|
||
|
# README section.
|
||
|
#
|
||
|
# Arguments:
|
||
|
# none
|
||
|
#
|
||
|
proc TekiCreateWindow {} {
|
||
|
global tclPkgInfo TekiInfo
|
||
|
Debug_Enter TekiCreateWindow
|
||
|
|
||
|
set w .browser
|
||
|
frame $w
|
||
|
pack $w -side bottom -fill x -expand no
|
||
|
scrollbar $w.scroll -command "$w.list yview"
|
||
|
listbox $w.list -font $TekiInfo(fixedfont) -relief sunken -width 100 \
|
||
|
-bd 2 -yscrollcommand "$w.scroll set" -setgrid 1 -height 12 \
|
||
|
-selectmode extended
|
||
|
pack $w.scroll -side right -fill y
|
||
|
pack $w.list -expand no -fill both
|
||
|
set TekiInfo(Browser) $w.list
|
||
|
TekiUpdateBrowser
|
||
|
|
||
|
# Create the README section of the frame
|
||
|
set w .readme
|
||
|
frame $w
|
||
|
pack $w -side top -fill both -expand yes
|
||
|
scrollbar $w.scroll -command "$w.text yview"
|
||
|
text $w.text -font $TekiInfo(fixedfont) -relief sunken -width 100 \
|
||
|
-bd 2 -yscrollcommand "$w.scroll set" -setgrid 1 -height 10
|
||
|
set TekiInfo(Readme) $w.text
|
||
|
pack $w.scroll -side right -fill y
|
||
|
pack $w.text -expand yes -fill both
|
||
|
TekiUpdateReadme
|
||
|
|
||
|
update
|
||
|
bind $TekiInfo(Browser) <Button-1> TekiBrowserCallback
|
||
|
bindtags $TekiInfo(Browser) [list Listbox all $TekiInfo(Browser)]
|
||
|
|
||
|
Debug_Leave
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# TekiUpdateReadme -
|
||
|
# Updates the readme window of the Info window.
|
||
|
# Called whenever the current package changes.
|
||
|
# The associated widget is a text widget.
|
||
|
#
|
||
|
proc TekiUpdateReadme {} {
|
||
|
global tclPkgInfo TekiInfo
|
||
|
Debug_Enter TekiUpdateReadme
|
||
|
set w $TekiInfo(Readme)
|
||
|
set currPkg [lindex $TekiInfo(currPackage) 0]
|
||
|
if {[string length $currPkg] == 0} {
|
||
|
return
|
||
|
}
|
||
|
set filename $tclPkgInfo($currPkg,infoFile)
|
||
|
set name $tclPkgInfo($currPkg,name)
|
||
|
|
||
|
$w delete 1.0 end
|
||
|
$w insert end "Information on the $currPkg package\n\n"
|
||
|
|
||
|
set m1 .menu.file.m
|
||
|
set m2 .menu.www.m
|
||
|
|
||
|
if {($name == "Tcl") || ($name == "Tk")} {
|
||
|
$m1 entryconfigure Uninstall -state disabled
|
||
|
$m2 entryconfigure Update -state disabled
|
||
|
} else {
|
||
|
$m1 entryconfigure Uninstall -state normal
|
||
|
if $TekiInfo(http) {
|
||
|
$m2 entryconfigure Update -state normal
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if {[string length $filename] &&
|
||
|
[file exists $filename] &&
|
||
|
[file readable $filename]} {
|
||
|
set f [open $filename r]
|
||
|
$w insert end [read $f]
|
||
|
close $f
|
||
|
} else {
|
||
|
$w insert end "No information available"
|
||
|
}
|
||
|
|
||
|
wm title . "TEKI -- $currPkg"
|
||
|
Debug_Leave
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# TekiUpdateBrowser -
|
||
|
# Updates the browser window.
|
||
|
# Called whenever the list of installed packages changes.
|
||
|
#
|
||
|
proc TekiUpdateBrowser {} {
|
||
|
global tclPkgInfo TekiInfo
|
||
|
Debug_Enter TekiUpdateBrowser
|
||
|
set w $TekiInfo(Browser)
|
||
|
$w delete 0 end
|
||
|
$w insert end "Packages installed:"
|
||
|
$w insert end [format "%-11s%-8s %-15s%-28s" "package" "version" "requires" "description"]
|
||
|
if {[winfo depth $w] > 1} {
|
||
|
set hot "-background #808080 -relief raised -borderwidth 1"
|
||
|
set normal "-background {} -relief flat"
|
||
|
} else {
|
||
|
set hot "-foreground white -background black"
|
||
|
set normal "-foreground {} -background {}"
|
||
|
}
|
||
|
|
||
|
set tclPkgInfo(installed) [lsort $tclPkgInfo(installed)]
|
||
|
foreach pkg $tclPkgInfo(installed) {
|
||
|
set des $tclPkgInfo($pkg,description)
|
||
|
set ver $tclPkgInfo($pkg,version)
|
||
|
set req $tclPkgInfo($pkg,requires)
|
||
|
|
||
|
# The following code does one of two things, depending
|
||
|
# on whether the package is part of another package.
|
||
|
set split [file split $pkg]
|
||
|
set lsplit [llength $split]
|
||
|
|
||
|
$w insert end [format "%-15s%-8s%-16s%-28s" $pkg $ver $req $des]
|
||
|
}
|
||
|
Debug_Leave
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# TekiBrowserCallback --
|
||
|
# This procedure is called when the user clicks on a package in the browser window.
|
||
|
# It is responsible for updating the UI to show details of the selected package
|
||
|
#
|
||
|
# Arguments:
|
||
|
# index The index of the character that the user clicked on.
|
||
|
#
|
||
|
proc TekiBrowserCallback {} {
|
||
|
global tclPkgInfo TekiInfo
|
||
|
Debug_Enter "TekiBrowserCallback"
|
||
|
set w $TekiInfo(Browser)
|
||
|
set packages {}
|
||
|
foreach i [$w curselection] {
|
||
|
if {$i < 2} {
|
||
|
# The first two elements are the labels, so don't allow the
|
||
|
# user to select these
|
||
|
$w selection clear $i
|
||
|
} else {
|
||
|
set info [$w get $i]
|
||
|
set pkg [lindex $info 0]
|
||
|
lappend packages $pkg
|
||
|
}
|
||
|
}
|
||
|
set TekiInfo(currPackage) $packages
|
||
|
TekiUpdateReadme
|
||
|
Debug_Leave
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# TekiCreateUI
|
||
|
# Create the TEKI User Interface in the main window (".")
|
||
|
# 1. Create the menus
|
||
|
# 2. Create & show the browse windows
|
||
|
#
|
||
|
proc TekiCreateUI {} {
|
||
|
global tclPkgInfo TekiInfo
|
||
|
Debug_Enter TekiCreateUI
|
||
|
TekiCreateMenus
|
||
|
TekiCreateWindow
|
||
|
set currPkg $TekiInfo(currPackage)
|
||
|
wm title . "TEKI -- $currPkg"
|
||
|
wm iconname . "TEKI"
|
||
|
Debug_Leave
|
||
|
}
|
||
|
|
||
|
#-----------------------------------------------------------------------
|
||
|
|
||
|
# WISE user interface support
|
||
|
|
||
|
# The following procedures control the WISE user interface (defined in the
|
||
|
# Wise package). The wizards are called by the Teki*Wizard procedures.
|
||
|
#
|
||
|
# The function TekiInteractiveInstall coordinates everything -- it
|
||
|
# reads/verifies the Teki file, calls the wizards to select various
|
||
|
# parameters, downloads any files that are needed, and calls TekiInstall
|
||
|
#
|
||
|
# The functions TekiNormalizeDirName
|
||
|
|
||
|
# Bring the name of a directory into "canonical" form. What a pain!
|
||
|
|
||
|
proc TekiNormalizeDirName {d} {
|
||
|
Debug_Enter TekiNormalizeDirName
|
||
|
set cwd [pwd]
|
||
|
switch [file pathtype $d] {
|
||
|
absolute -
|
||
|
volumerelative {
|
||
|
set path {}
|
||
|
set len 0
|
||
|
}
|
||
|
|
||
|
relative {
|
||
|
set path [file split $cwd]
|
||
|
set len [llength $path]
|
||
|
}
|
||
|
}
|
||
|
foreach c [file split $d] {
|
||
|
switch $c {
|
||
|
{.} {}
|
||
|
|
||
|
{..} {
|
||
|
incr len -1;
|
||
|
set path [lrange $path 0 [expr $len-1]]
|
||
|
}
|
||
|
|
||
|
default {
|
||
|
lappend path $c
|
||
|
incr len
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
Debug_Leave
|
||
|
return [eval file join $path]
|
||
|
}
|
||
|
|
||
|
# Try to make the directory writable
|
||
|
|
||
|
proc TekiMakeWritable {dir} {
|
||
|
global tcl_platform tcl_version
|
||
|
set majorversion [lindex [split $tcl_version .] 0]
|
||
|
switch $tcl_platform(platform) {
|
||
|
unix {
|
||
|
if {$majorversion >= 8} {
|
||
|
file attributes $dir -permissions 755
|
||
|
} else {
|
||
|
exec chmod a+wx $dir
|
||
|
}
|
||
|
}
|
||
|
|
||
|
mac* -
|
||
|
windows {
|
||
|
if {$majorversion >= 8} {
|
||
|
file attributes $dir -readonly 0
|
||
|
} else {
|
||
|
error "Permission denied"
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Make sure the directory exists, is writable, and is a directory.
|
||
|
# If $dir exists and is a file, flag an error
|
||
|
# If $dir doesn't exist, ask the user if they want to create it
|
||
|
# if they do, then try to create it, otherwise flag an error
|
||
|
# If the return value is 0, then the directory can't be used
|
||
|
# for installation. If the return value is 1, then we can proceed
|
||
|
|
||
|
proc TekiVerifyDir {dir} {
|
||
|
#
|
||
|
# Make the directory if needed
|
||
|
#
|
||
|
if {[file exists $dir]} {
|
||
|
if {![file isdirectory $dir]} {
|
||
|
TekiError "Error: $dir exists and is not a directory"
|
||
|
return 0
|
||
|
}
|
||
|
} else {
|
||
|
# Doesn't exist. Make it
|
||
|
set rv [TekiWarning "Directory $dir does not exist, create?" 0 {Ok Cancel}]
|
||
|
if {$rv == 0} {
|
||
|
if [catch {file mkdir $dir} err] {
|
||
|
TekiError "Error creating $dir\n $err"
|
||
|
return 0
|
||
|
}
|
||
|
} else {
|
||
|
# Cancelled by user
|
||
|
return 0
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# If we get to here, $dir exists and is a directory
|
||
|
# Try to make it writable if it's not
|
||
|
if {![file writable $dir]} {
|
||
|
if [catch {TekiMakeWritable $dir} err] {
|
||
|
TekiError "Error: $dir is not writable\n$err"
|
||
|
return 0
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 1
|
||
|
}
|
||
|
|
||
|
# Utility to delete an item from a list
|
||
|
|
||
|
proc TekiListDelete {list item} {
|
||
|
set index [lsearch $list $item]
|
||
|
if {$index != -1} {
|
||
|
set list [concat [lrange $list 0 [expr $index-1]] \
|
||
|
[lrange $list [expr $index+1] end]]
|
||
|
}
|
||
|
return $list
|
||
|
}
|
||
|
|
||
|
|
||
|
# The (Teki*Wizard) procedures encode the logic of the wizards.
|
||
|
# Each procedure corresponds to one wizard. The information gathered
|
||
|
# through the wizards is used to set elements in the global array TekiInfo()
|
||
|
#
|
||
|
# The entry point to the machine is the TekiTypeWizard (abbreviated
|
||
|
# "type"). At any time, pressing the cancel button aborts the wizards,
|
||
|
# pressing "finish" goes directly to "verify", pressing "back" goes back
|
||
|
# to the previous state, and, generally, pressing "next" goes on to
|
||
|
# the next state. The only exception is in the initial wizard where
|
||
|
# the user selects between "custom" and "typical" installation. If
|
||
|
# the user selects "typical" and selects "next", the installation
|
||
|
# parameters are reset to their defaults and the wizard goes directly
|
||
|
# to the verify state.
|
||
|
#
|
||
|
# To encode this logic, a sequence of procedure calls are executed, one
|
||
|
# for each wizard. Each wizard calls the next wizard directly. To
|
||
|
# implement the "back" button, the procedure just returns. To implement
|
||
|
# the "cancel" and "finish" buttons, unusual return codes
|
||
|
# ($TekiInfo(cancelCode) for cancel, $TekiInfo(finishCode) for finish)
|
||
|
# are returned by the wizard procedures and caught at the entry point
|
||
|
# of the first wizard.
|
||
|
#
|
||
|
# The wizards are:
|
||
|
# type - get installation type (custom or typical)
|
||
|
# packages - let the user select which packages to install (only used
|
||
|
# if there's more than one package available)
|
||
|
# archList - get the list of architectures to install for
|
||
|
# extras - ask whether to install examples, docs, and data
|
||
|
# codeDir - get target directory for code files
|
||
|
# docDir - get target directory for documentation (only if installing
|
||
|
# documentation)
|
||
|
# verify - verify all paramters
|
||
|
|
||
|
set TekiInfo(cancelCode) 5
|
||
|
set TekiInfo(finishCode) 6
|
||
|
|
||
|
# Reset the TekiInfo variables that corespond to the wizard parameters
|
||
|
# to their default values.
|
||
|
|
||
|
proc TekiWizardDefaults {} {
|
||
|
global TekiInfo newPackage
|
||
|
|
||
|
# reset to defaults
|
||
|
set TekiInfo(packages) $newPackage(defaultPackages)
|
||
|
set TekiInfo(archList) $newPackage(defaultArch)
|
||
|
if {$TekiInfo(archList) == "all"} {
|
||
|
set TekiInfo(archList) {}
|
||
|
foreach sys $TekiInfo(systemNames) {
|
||
|
lappend TekiInfo(archList) [lindex $sys 0]
|
||
|
}
|
||
|
}
|
||
|
set TekiInfo(extras) {}
|
||
|
if $newPackage(defaultInstallDoc) {
|
||
|
lappend TekiInfo(extras) doc
|
||
|
}
|
||
|
if $newPackage(defaultInstallData) {
|
||
|
lappend TekiInfo(extras) data
|
||
|
}
|
||
|
if $newPackage(defaultInstallExamples) {
|
||
|
lappend TekiInfo(extras) examples
|
||
|
}
|
||
|
set TekiInfo(codeDir) [info library]
|
||
|
set x [file dirname [file dirname $TekiInfo(codeDir)]]
|
||
|
set TekiInfo(docDir) [file join $x doc]
|
||
|
}
|
||
|
|
||
|
proc TekiTypeWizard {} {
|
||
|
global TekiInfo
|
||
|
# Ask if typical or custom install
|
||
|
while (1) {
|
||
|
set rv [Wise_Radiolist "Select installation type" \
|
||
|
{{typical typical 1} {custom custom 0}} 1]
|
||
|
switch $rv {
|
||
|
{next typical} -
|
||
|
{finish typical} {
|
||
|
TekiWizardDefaults
|
||
|
TekiVerifyWizard
|
||
|
}
|
||
|
{next custom} {
|
||
|
TekiPackagesWizard
|
||
|
}
|
||
|
{finish custom} {
|
||
|
TekiVerifyWizard
|
||
|
}
|
||
|
cancel {
|
||
|
return -code $TekiInfo(cancelCode)
|
||
|
}
|
||
|
default {
|
||
|
TekiError "Internal error:\n $rv\ndoesn't match case in switch"
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
proc TekiPackagesWizard {} {
|
||
|
global TekiInfo newPackage
|
||
|
set packages $newPackage(available)
|
||
|
|
||
|
#
|
||
|
# If there's only one package, just pretend they hit "next"
|
||
|
#
|
||
|
if {[llength $packages] != 1} {
|
||
|
while (1) {
|
||
|
set choices {}
|
||
|
foreach pkg $packages {
|
||
|
if {[lsearch $TekiInfo(packages) $pkg] == -1} {
|
||
|
lappend choices [list $newPackage($pkg,name) $pkg 0]
|
||
|
} else {
|
||
|
lappend choices [list $newPackage($pkg,name) $pkg 1]
|
||
|
}
|
||
|
}
|
||
|
set rv [Wise_Checklist "Select packages to install" $choices]
|
||
|
switch [lindex $rv 0] {
|
||
|
next {
|
||
|
set TekiInfo(packages) [lrange $rv 1 end]
|
||
|
TekiArchListWizard
|
||
|
}
|
||
|
back {
|
||
|
return
|
||
|
}
|
||
|
finish {
|
||
|
set TekiInfo(packages) [lrange $rv 1 end]
|
||
|
TekiVerifyWizard
|
||
|
}
|
||
|
cancel {
|
||
|
return -code $TekiInfo(cancelCode)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
set TekiInfo(packages) $packages
|
||
|
TekiArchListWizard
|
||
|
}
|
||
|
}
|
||
|
|
||
|
proc TekiArchListWizard {} {
|
||
|
global TekiInfo newPackage
|
||
|
|
||
|
set objFileCount 0
|
||
|
foreach pkg $TekiInfo(packages) {
|
||
|
incr objFileCount [llength $newPackage($pkg,objFiles)]
|
||
|
}
|
||
|
if {$objFileCount == 0} {
|
||
|
TekiExtrasWizard
|
||
|
return
|
||
|
}
|
||
|
|
||
|
while (1) {
|
||
|
#
|
||
|
# We need to rebuild the choices list each time through the
|
||
|
# loop so we can retain the users setting when they go on
|
||
|
# to the next wizard and then hit the "back" button. If
|
||
|
# we don't rebuild it, then the wizard contents might
|
||
|
# be out of sync with TekiInfo(archList), since this is
|
||
|
# changed on next/finish
|
||
|
#
|
||
|
set choices {}
|
||
|
foreach pkg $TekiInfo(packages) {
|
||
|
foreach pair $newPackage($pkg,objFiles) {
|
||
|
set name [lindex $pair 0]
|
||
|
if {[lsearch $choices [list $name]*] == -1} {
|
||
|
if {[lsearch $TekiInfo(archList) $name] == -1} {
|
||
|
lappend choices [list $name $name 0]
|
||
|
} else {
|
||
|
lappend choices [list $name $name 1]
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
set rv [Wise_Checklist "Select architectures to install" $choices]
|
||
|
switch [lindex $rv 0] {
|
||
|
next {
|
||
|
set TekiInfo(archList) [lrange $rv 1 end]
|
||
|
TekiExtrasWizard
|
||
|
}
|
||
|
back {
|
||
|
return
|
||
|
}
|
||
|
finish {
|
||
|
set TekiInfo(archList) [lrange $rv 1 end]
|
||
|
TekiVerifyWizard
|
||
|
}
|
||
|
cancel {
|
||
|
return -code $TekiInfo(cancelCode)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
proc TekiExtrasWizard {} {
|
||
|
global TekiInfo newPackage
|
||
|
|
||
|
#
|
||
|
# Figure out if there are any extras to install! If not, skip
|
||
|
# this step.
|
||
|
#
|
||
|
set docFileCount 0
|
||
|
set dataFileCount 0
|
||
|
set exampleFileCount 0
|
||
|
foreach pkg $TekiInfo(packages) {
|
||
|
incr docFileCount [llength $newPackage($pkg,docFiles)]
|
||
|
incr dataFileCount [llength $newPackage($pkg,dataFiles)]
|
||
|
incr exampleFileCount [llength $newPackage($pkg,exampleFiles)]
|
||
|
}
|
||
|
if {$docFileCount == 0} {
|
||
|
set TekiInfo(extras) [TekiListDelete $TekiInfo(extras) doc]
|
||
|
}
|
||
|
if {$dataFileCount == 0} {
|
||
|
set TekiInfo(extras) [TekiListDelete $TekiInfo(extras) data]
|
||
|
}
|
||
|
if {$exampleFileCount == 0} {
|
||
|
set TekiInfo(extras) [TekiListDelete $TekiInfo(extras) example]
|
||
|
}
|
||
|
if {($docFileCount + $dataFileCount + $exampleFileCount) == 0} {
|
||
|
TekiCodeDirWizard
|
||
|
return
|
||
|
}
|
||
|
|
||
|
while (1) {
|
||
|
set choices {}
|
||
|
if $docFileCount {
|
||
|
lappend choices [list "Install documentation" doc \
|
||
|
[expr [lsearch $TekiInfo(extras) doc] != -1]]
|
||
|
}
|
||
|
if $dataFileCount {
|
||
|
lappend choices [list "Install data files" data \
|
||
|
[expr [lsearch $TekiInfo(extras) data] != -1]]
|
||
|
}
|
||
|
if $exampleFileCount {
|
||
|
lappend choices [list "Install examples" examples \
|
||
|
[expr [lsearch $TekiInfo(extras) examples] != -1]]
|
||
|
}
|
||
|
set rv [Wise_Checklist "Select Extras to install" $choices]
|
||
|
switch [lindex $rv 0] {
|
||
|
next {
|
||
|
set TekiInfo(extras) [lrange $rv 1 end]
|
||
|
TekiCodeDirWizard
|
||
|
}
|
||
|
back {
|
||
|
return
|
||
|
}
|
||
|
finish {
|
||
|
set TekiInfo(extras) [lrange $rv 1 end]
|
||
|
TekiVerifyWizard
|
||
|
}
|
||
|
cancel {
|
||
|
return -code $TekiInfo(cancelCode)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
proc TekiCodeDirWizard {} {
|
||
|
global TekiInfo auto_path env tcl_pkgPath
|
||
|
while (1) {
|
||
|
# Give the user several standard options of where to install
|
||
|
# the code:
|
||
|
# option #1: the current value of the codeDir
|
||
|
# option #2: [info library]
|
||
|
# option #3: the TCLLIBPATH environment variable
|
||
|
# option #4: the parent of [info library]
|
||
|
# option #5: all the elements of tclPkgPath
|
||
|
# option #6: all the elements of auto_path
|
||
|
# option #7: "other"
|
||
|
# The logic makes sure each choice only appears once.
|
||
|
set choices {}
|
||
|
set dir [TekiNormalizeDirName $TekiInfo(codeDir)]
|
||
|
lappend choices [list $dir $dir 1]
|
||
|
set dir [TekiNormalizeDirName [info library]]
|
||
|
if {[lsearch $choices [list $dir $dir ?]] == -1} {
|
||
|
lappend choices [list $dir $dir 0]
|
||
|
}
|
||
|
if [info exists env(TCLLIBPATH)] {
|
||
|
set dir [TekiNormalizeDirName $env(TCLLIBPATH)]
|
||
|
if {[lsearch $choices [list $dir $dir ?]] == -1} {
|
||
|
lappend choices [list $dir $dir 0]
|
||
|
}
|
||
|
}
|
||
|
set dir [TekiNormalizeDirName [info library]/..]
|
||
|
if {[lsearch $choices [list $dir $dir ?]] == -1} {
|
||
|
lappend choices [list $dir $dir 0]
|
||
|
}
|
||
|
foreach dir $tcl_pkgPath {
|
||
|
set dir [TekiNormalizeDirName $dir]
|
||
|
if {[lsearch $choices [list $dir $dir ?]] == -1} {
|
||
|
lappend choices [list $dir $dir 0]
|
||
|
}
|
||
|
}
|
||
|
foreach dir $auto_path {
|
||
|
set dir [TekiNormalizeDirName $dir]
|
||
|
if {[lsearch $choices [list $dir $dir ?]] == -1} {
|
||
|
lappend choices [list $dir $dir 0]
|
||
|
}
|
||
|
}
|
||
|
lappend choices [list other other 0]
|
||
|
|
||
|
set rv [Wise_Radiolist "Select root directory for code files" $choices]
|
||
|
|
||
|
#
|
||
|
# If they choose other, let them chose a directory.
|
||
|
# After we have the candidate directory, make sure it
|
||
|
# exists and is writable.
|
||
|
#
|
||
|
set option [lindex $rv 0]
|
||
|
set rv [lrange $rv 1 end]
|
||
|
switch $option {
|
||
|
next -
|
||
|
finish {
|
||
|
if {$rv == "other"} {
|
||
|
set dir [Wise_GetDirName $dir]
|
||
|
if {$dir == ""} {
|
||
|
continue; # try again....
|
||
|
}
|
||
|
} else {
|
||
|
set dir $rv
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# Warn them if they're installing in a non-standard place
|
||
|
#
|
||
|
set found 0
|
||
|
foreach d $auto_path {
|
||
|
set nd [TekiNormalizeDirName $d]
|
||
|
if [string match $nd $dir] {
|
||
|
set found 1
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
if {!$found} {
|
||
|
foreach d $tcl_pkgPath {
|
||
|
set nd [TekiNormalizeDirName $d]
|
||
|
if [string match $nd $dir] {
|
||
|
set found 1
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if {!$found} {
|
||
|
TekiWarning "Warning: you are installing in a non-standard location
|
||
|
Be sure to add $dir to your TCLLIBPATH environment variable" 0 Ok
|
||
|
}
|
||
|
|
||
|
if {![TekiVerifyDir $dir]} {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
set TekiInfo(codeDir) $dir
|
||
|
if {$option == "next"} {
|
||
|
TekiDocDirWizard
|
||
|
} else {
|
||
|
TekiVerifyWizard
|
||
|
}
|
||
|
}
|
||
|
|
||
|
back {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
cancel {
|
||
|
return -code $TekiInfo(cancelCode)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
proc TekiDocDirWizard {} {
|
||
|
global TekiInfo
|
||
|
if {[lsearch $TekiInfo(extras) doc] == -1} {
|
||
|
TekiVerifyWizard
|
||
|
return
|
||
|
}
|
||
|
while (1) {
|
||
|
# Give the user several standard options of where to install
|
||
|
# the documentation:
|
||
|
# option #1: the current value of the docDir
|
||
|
# option #2: [info library]/../../doc
|
||
|
# option #3: $codeDir/doc
|
||
|
# option #4: "other"
|
||
|
# The logic makes sure each choice only appears once.
|
||
|
|
||
|
set choices {}
|
||
|
set dir [TekiNormalizeDirName $TekiInfo(docDir)]
|
||
|
lappend choices [list $dir $dir 1]
|
||
|
set dir [TekiNormalizeDirName [info library]/../../doc]
|
||
|
if {[lsearch $choices [list $dir $dir ?]] == -1} {
|
||
|
lappend choices [list $dir $dir 0]
|
||
|
}
|
||
|
set dir [TekiNormalizeDirName $TekiInfo(codeDir)/doc]
|
||
|
if {[lsearch $choices [list $dir $dir ?]] == -1} {
|
||
|
lappend choices [list $dir $dir 0]
|
||
|
}
|
||
|
lappend choices [list other other 0]
|
||
|
|
||
|
set rv [Wise_Radiolist "Select root directory for documentation files" $choices]
|
||
|
|
||
|
set option [lindex $rv 0]
|
||
|
set rv [lrange $rv 1 end]
|
||
|
switch $option {
|
||
|
next -
|
||
|
finish {
|
||
|
if {$rv == "other"} {
|
||
|
set dir [Wise_GetDirName $dir]
|
||
|
if {$dir == ""} {
|
||
|
continue; # try again....
|
||
|
}
|
||
|
} else {
|
||
|
set dir $rv
|
||
|
}
|
||
|
|
||
|
if {![TekiVerifyDir $dir]} {
|
||
|
continue
|
||
|
}
|
||
|
set TekiInfo(docDir) $dir
|
||
|
TekiVerifyWizard
|
||
|
}
|
||
|
|
||
|
back {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
cancel {
|
||
|
return -code $TekiInfo(cancelCode)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
proc TekiVerifyWizard {} {
|
||
|
global TekiInfo newPackage
|
||
|
set installDoc [expr [lsearch $TekiInfo(extras) doc] != -1]
|
||
|
set installData [expr [lsearch $TekiInfo(extras) data] != -1]
|
||
|
set installExamples [expr [lsearch $TekiInfo(extras) examples] != -1]
|
||
|
set msg "Install these packages:\n"
|
||
|
foreach pkg $TekiInfo(packages) {
|
||
|
set msg [format "%s %s %s\n" $msg \
|
||
|
$newPackage($pkg,name) \
|
||
|
$newPackage($pkg,version)]
|
||
|
}
|
||
|
set msg [format "%s\n...for these architectures:\n" $msg]
|
||
|
foreach arch $TekiInfo(archList) {
|
||
|
set msg [format "%s %s\n" $msg $arch]
|
||
|
}
|
||
|
set msg [format "%s\nInstall code in \n %s\n" $msg $TekiInfo(codeDir)]
|
||
|
if {$installDoc} {
|
||
|
set msg [format "%s\nInstall documentation in \n %s\n" \
|
||
|
$msg $TekiInfo(docDir)]
|
||
|
}
|
||
|
if {$installExamples} {
|
||
|
set msg [format "%s\nInstall examples in \n %s\n" \
|
||
|
$msg $TekiInfo(codeDir)]
|
||
|
}
|
||
|
if {$installData} {
|
||
|
set msg [format "%s\nInstall data files in \n %s\n" \
|
||
|
$msg $TekiInfo(codeDir)]
|
||
|
}
|
||
|
set rv [TekiWarning $msg 0 {Finish {< Back} Cancel}]
|
||
|
if {$rv == 0} {
|
||
|
return -code $TekiInfo(finishCode)
|
||
|
}
|
||
|
if {$rv == 2} {
|
||
|
return -code $TekiInfo(cancelCode)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# Display copyrights for all selected packages.
|
||
|
# Returns a list of packages for which the user accepted the copyright
|
||
|
#
|
||
|
proc TekiDisplayCopyrights {pkgList} {
|
||
|
global tclPkgInfo TekiInfo newPackage auto_path env
|
||
|
|
||
|
Debug_Enter TekiDisplayCopyrights
|
||
|
set packages {}
|
||
|
set accept 0
|
||
|
set dontaccept 1
|
||
|
set acceptrest 2
|
||
|
set abort 3
|
||
|
set code 0
|
||
|
foreach {pkg fn} $pkgList {
|
||
|
if {($code != $acceptrest) &&
|
||
|
[file exists $fn]} {
|
||
|
set f [open $fn r]
|
||
|
set msg [read $f]
|
||
|
close $f
|
||
|
set name $newPackage($pkg,name)
|
||
|
set code [Wise_Message "Copyright for $name" \
|
||
|
$msg {Accept {Do not Accept} {Accept Rest} Abort}]
|
||
|
if {$code == $accept} {
|
||
|
lappend packages $pkg
|
||
|
}
|
||
|
if {$code == $acceptrest} {
|
||
|
lappend packages $pkg
|
||
|
}
|
||
|
if {$code == $dontaccept} {
|
||
|
TekiWarning "Skipping installation of $name" 0 {Ok}
|
||
|
}
|
||
|
if {$code == $abort} {
|
||
|
TekiWarning "Aborting installation" 0 {Ok}
|
||
|
return {}
|
||
|
}
|
||
|
} else {
|
||
|
lappend packages $pkg
|
||
|
}
|
||
|
}
|
||
|
Debug_Leave
|
||
|
return $packages
|
||
|
}
|
||
|
|
||
|
# The following procedure is called by DoFileInstall and
|
||
|
# DoWWWBrowse to
|
||
|
# 1. Read/verify the teki file
|
||
|
# 2. Gather the installation parameters
|
||
|
# 3. Fetch & display copyright notices
|
||
|
# 4. Install the extension
|
||
|
#
|
||
|
# It does a web based install if $web is true, a file system
|
||
|
# based install if $web if false
|
||
|
|
||
|
proc TekiInteractiveInstall {tekiFile web} {
|
||
|
global tclPkgInfo TekiInfo newPackage auto_path env
|
||
|
Debug_Enter TekiInteractiveInstall
|
||
|
|
||
|
# 1. Read and verify it
|
||
|
|
||
|
catch {unset newPackage}
|
||
|
set code [catch {TekiReadFile $tekiFile} err]
|
||
|
if $code {
|
||
|
TekiError "Error reading $tekiFile\n $err\nerrorCode = $code"
|
||
|
catch {unset newPackage}
|
||
|
return
|
||
|
}
|
||
|
set code [catch {TekiVerifyFile} err]
|
||
|
if $code {
|
||
|
TekiError "Invalid Teki file $tekiFile:\n$err"
|
||
|
catch {unset newPackage}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
# 2. Collect all the information from the user.
|
||
|
|
||
|
TekiWizardDefaults
|
||
|
set code [catch TekiTypeWizard err]
|
||
|
if {$code == $TekiInfo(cancelCode)} {
|
||
|
return
|
||
|
}
|
||
|
if {$code != $TekiInfo(finishCode)} {
|
||
|
TekiError $err
|
||
|
return
|
||
|
}
|
||
|
|
||
|
# 3. Fetch/display all the copyright notices. If web based install,
|
||
|
# download the copyright files. The wwwCleanup variable is a list
|
||
|
# of all the files in tmpdir that we have to delete when we're done.
|
||
|
|
||
|
set fileList {}
|
||
|
set nocopyright {}
|
||
|
set tmpdir $TekiInfo(tmpDir)
|
||
|
set index 0
|
||
|
if $web {
|
||
|
foreach pkg $TekiInfo(packages) {
|
||
|
set inFile $newPackage($pkg,copyright)
|
||
|
set srcURL $newPackage($pkg,srcURL)
|
||
|
set outFile [file join $tmpdir copyright$index]
|
||
|
if [string length $inFile] {
|
||
|
if [catch {TekiDownloadFile $srcURL/$inFile $outFile} err] {
|
||
|
TekiWarning "Couldn't find copyright file\n$srcURL/$inFile\n$err\n\nskipping package" 0 {Ok}
|
||
|
} else {
|
||
|
lappend fileList $pkg $outFile
|
||
|
}
|
||
|
} else {
|
||
|
lappend nocopyright $pkg
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
foreach pkg $TekiInfo(packages) {
|
||
|
set srcDir $newPackage($pkg,srcDir)
|
||
|
set copyright $newPackage($pkg,copyright)
|
||
|
set inFile [file join [file dirname $tekiFile] $srcDir $copyright]
|
||
|
if [string length $copyright] {
|
||
|
if {![file exists $inFile]} {
|
||
|
TekiWarning "Couldn't find copyright file\n$inFile\n\nskipping package" 0 {Ok}
|
||
|
} else {
|
||
|
lappend fileList $pkg $inFile
|
||
|
}
|
||
|
} else {
|
||
|
lappend nocopyright $pkg
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
set TekiInfo(packages) [concat $nocopyright [TekiDisplayCopyrights $fileList]]
|
||
|
if $web {
|
||
|
foreach file $fileList {
|
||
|
catch {file delete -force $file}
|
||
|
}
|
||
|
}
|
||
|
if {$TekiInfo(packages) == ""} {
|
||
|
Debug_Leave
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
# 4. Install it!
|
||
|
|
||
|
set installDoc [expr [lsearch $TekiInfo(extras) doc] != -1]
|
||
|
set installData [expr [lsearch $TekiInfo(extras) data] != -1]
|
||
|
set installExamples [expr [lsearch $TekiInfo(extras) examples] != -1]
|
||
|
|
||
|
TekiInstall [file dirname $tekiFile] $web \
|
||
|
$TekiInfo(codeDir) $TekiInfo(docDir) \
|
||
|
$TekiInfo(packages) $TekiInfo(archList) \
|
||
|
$installData $installDoc $installExamples
|
||
|
|
||
|
TekiGetAllPkgInfo
|
||
|
TekiUpdateBrowser
|
||
|
catch {unset newPackage}
|
||
|
Debug_Leave
|
||
|
}
|
||
|
|
||
|
#-----------------------------------------------------------------------
|
||
|
#
|
||
|
# File menu callbacks --
|
||
|
#
|
||
|
# TekiDoFileInstall is called when the user select the Install item in the
|
||
|
# File menu
|
||
|
# TekiDoFileUninstall is called when the user select the Uninstall item in the
|
||
|
# File menu
|
||
|
# TekiDoFileExit is called when the user select the Exit item in the File menu
|
||
|
|
||
|
#
|
||
|
# TekiDoFileInstall -- called from the "File->Install" menu item.
|
||
|
#
|
||
|
|
||
|
|
||
|
proc TekiDoFileInstall {} {
|
||
|
global tclPkgInfo TekiInfo newPackage auto_path env
|
||
|
Debug_Enter TekiDoFileInstall
|
||
|
|
||
|
catch {unset newPackage}
|
||
|
# Get the teki install file
|
||
|
set file [tk_getOpenFile -title "Open TEKI file" -filetypes {{{Tek Files} .tek} {{All Files} *}}]
|
||
|
if {$file == ""} {
|
||
|
return;
|
||
|
}
|
||
|
TekiInteractiveInstall $file 0
|
||
|
Debug_Leave
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# TekiDoFileUninstall {} {
|
||
|
#
|
||
|
# Uninstall the current package, update UI
|
||
|
#
|
||
|
proc TekiDoFileUninstall {} {
|
||
|
global tclPkgInfo tcl_library TekiInfo
|
||
|
Debug_Enter TekiDoFileUninstall
|
||
|
|
||
|
set packages {}
|
||
|
foreach pkg $TekiInfo(currPackage) {
|
||
|
set name $tclPkgInfo($pkg,name)
|
||
|
if {($name == "Tcl") || ($name == "Tk")} {
|
||
|
tk_dialog .error "Error" "Can't uninstall $pkg" {} 0 Ok
|
||
|
continue
|
||
|
}
|
||
|
lappend packages $pkg
|
||
|
}
|
||
|
TekiUninstall $packages
|
||
|
TekiGetAllPkgInfo
|
||
|
TekiUpdateBrowser
|
||
|
set w $TekiInfo(Browser)
|
||
|
$w selection set end
|
||
|
set info [$w get end]
|
||
|
set TekiInfo(currPackage) [lindex $info 0]
|
||
|
TekiUpdateReadme
|
||
|
|
||
|
Debug_Leave
|
||
|
}
|
||
|
|
||
|
proc TekiDoFileExit {} {
|
||
|
Debug_Enter TekiDoFileExit
|
||
|
exit
|
||
|
}
|
||
|
|
||
|
#-----------------------------------------------------------------------
|
||
|
#
|
||
|
# WWW menu callbacks --
|
||
|
#
|
||
|
|
||
|
#
|
||
|
# Update the currently selected package.
|
||
|
# 1. Fetching the .tek file using the package's the URL field
|
||
|
# 2. Report the current version number and the new version number.
|
||
|
# Ask the user if they want to proceed.
|
||
|
# 3. If so, copy all the package files into a temporary directory
|
||
|
# and install from there
|
||
|
#
|
||
|
proc TekiDoWWWUpdate {} {
|
||
|
global tclPkgInfo TekiInfo
|
||
|
|
||
|
Debug_Enter TekiDoWWWUpdate
|
||
|
Debug_Leave
|
||
|
}
|
||
|
|
||
|
proc TekiWWWBrowseInstall {} {
|
||
|
global TekiInfo
|
||
|
set tmpDir $TekiInfo(tmpDir)
|
||
|
|
||
|
# Gather files for a batch install, and generate/use a bundle file
|
||
|
set filelist {}
|
||
|
set pkgList {}
|
||
|
set index 0
|
||
|
foreach i [.toc.list curselection] {
|
||
|
if {$i == 0} {
|
||
|
# skip labels :-)
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
# The first element of the selection is the package number,
|
||
|
# which is the index in TekiInfo(contents). Retrieve it so
|
||
|
# we know which package the user selected.
|
||
|
set info [.toc.list get $i]
|
||
|
set index [lindex $info 0]
|
||
|
incr index -1
|
||
|
set tuple [lindex $TekiInfo(contents) $index]
|
||
|
set name [lindex $tuple 0]
|
||
|
set inFile [lindex $tuple 5]
|
||
|
set pkg [lindex $tuple 6]
|
||
|
set tmpDir $TekiInfo(tmpDir)
|
||
|
set outFile [file join $tmpDir tekiTmp$index]
|
||
|
if [catch {TekiDownloadFile $inFile $outFile} err] {
|
||
|
set rv [TekiWarning "Couldn't install package $name\nError occured while downloading\n$inFile\n$err" 0 {Ok Cancel}]
|
||
|
if {$rv == 0} {
|
||
|
foreach f $filelist {
|
||
|
file delete -force $f
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
} else {
|
||
|
lappend filelist $outFile
|
||
|
lappend pkgList $pkg
|
||
|
incr index
|
||
|
}
|
||
|
}
|
||
|
if {[llength $filelist] == 0} {
|
||
|
TekiWarning "No valid packages found to install" 0 {Ok}
|
||
|
return
|
||
|
}
|
||
|
set bundleFile [file join $tmpDir tekiBundle.tek]
|
||
|
set f [open $bundleFile w]
|
||
|
puts $f {# TekiFile 1.0
|
||
|
# Automatically generated TEKI bundle file
|
||
|
}
|
||
|
foreach file $filelist {
|
||
|
puts $f "TekiReadFile $file"
|
||
|
}
|
||
|
puts $f "set newPackage(defaultPackages) [list $pkgList]"
|
||
|
close $f
|
||
|
TekiInteractiveInstall $bundleFile 1
|
||
|
file delete -force $bundleFile
|
||
|
foreach f $filelist {
|
||
|
file delete -force $f
|
||
|
}
|
||
|
}
|
||
|
|
||
|
proc TekiWWWBrowseReadme {} {
|
||
|
global TekiInfo
|
||
|
set packages {}
|
||
|
set tmpDir $TekiInfo(tmpDir)
|
||
|
foreach i [.toc.list curselection] {
|
||
|
if {$i == 0} {
|
||
|
# skip labels :-)
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
# The first element of the selection is the package number,
|
||
|
# which is the index in TekiInfo(contents). Retrieve it so
|
||
|
# we know which package the user selected.
|
||
|
set info [.toc.list get $i]
|
||
|
set index [lindex $info 0]
|
||
|
incr index -1
|
||
|
set tuple [lindex $TekiInfo(contents) $index]
|
||
|
set name [lindex $tuple 0]
|
||
|
set ver [lindex $tuple 1]
|
||
|
set url [lindex $tuple 4]
|
||
|
if {[string length $url] == 0} {
|
||
|
set rv [TekiWarning "No information available on $name $ver" 0 {Ok Cancel}]
|
||
|
if {$rv == 1} {
|
||
|
return
|
||
|
}
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
set outFile [file join $tmpDir readme]
|
||
|
if [catch {TekiDownloadFile $url $outFile} err] {
|
||
|
set rv [TekiWarning "Coulnd't find readme for $name $ver:\n $err" 0 {Ok Cancel}]
|
||
|
if {$rv == 1} {
|
||
|
return
|
||
|
}
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
set f [open $outFile r]
|
||
|
set msg [read $f]
|
||
|
close $f
|
||
|
file delete -force $outFile
|
||
|
set code [Wise_Message "Information for $name" $msg {Ok Cancel}]
|
||
|
if {$code == 1} {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
proc TekiWWWBrowseAll {} {
|
||
|
global TekiInfo
|
||
|
.toc.list delete 0 end
|
||
|
set str [format "%-4s%-11s%-8s %-15s%-28s" "#" "package" "version" "requires" "description"]
|
||
|
.toc.list insert end $str
|
||
|
set i 1
|
||
|
foreach tuple $TekiInfo(contents) {
|
||
|
set pkg [lindex $tuple 0]
|
||
|
set ver [lindex $tuple 1]
|
||
|
set req [lindex $tuple 2]
|
||
|
set des [lindex $tuple 3]
|
||
|
.toc.list insert end [format "%-4d%-15s%-8s%-16s%-28s" $i $pkg $ver $req $des]
|
||
|
incr i
|
||
|
}
|
||
|
}
|
||
|
|
||
|
proc TekiWWWBrowseCompat {} {
|
||
|
global tcl_version tk_version TekiInfo
|
||
|
.toc.list delete 0 end
|
||
|
set str [format "%-4s%-11s%-8s %-15s%-28s" "#" "package" "version" "requires" "description"]
|
||
|
.toc.list insert end $str
|
||
|
set i 1
|
||
|
foreach tuple $TekiInfo(contents) {
|
||
|
set sat 1
|
||
|
foreach pair [lindex $tuple 2] {
|
||
|
set pkg [lindex $pair 0]
|
||
|
set ver [lindex $pair 1]
|
||
|
Debug_Print $pkg $ver
|
||
|
if {($pkg == "Tcl") && ![package vsatisfies $tcl_version $ver]} {
|
||
|
set sat 0
|
||
|
}
|
||
|
if {($pkg == "Tk") && ![package vsatisfies $tk_version $ver]} {
|
||
|
set sat 0
|
||
|
}
|
||
|
}
|
||
|
if $sat {
|
||
|
set pkg [lindex $tuple 0]
|
||
|
set ver [lindex $tuple 1]
|
||
|
set req [lindex $tuple 2]
|
||
|
set des [lindex $tuple 3]
|
||
|
.toc.list insert end [format "%-4d%-15s%-8s%-16s%-28s" $i $pkg $ver $req $des]
|
||
|
}
|
||
|
incr i
|
||
|
}
|
||
|
}
|
||
|
|
||
|
proc TekiDoWWWBrowse {} {
|
||
|
global TekiInfo
|
||
|
set url $TekiInfo(browseURL)
|
||
|
set tmpDir $TekiInfo(tmpDir)
|
||
|
set outFile [file join $tmpDir contents.tkr]
|
||
|
if [catch {TekiDownloadFile $url $outFile} err] {
|
||
|
TekiWarning "Couldn't find browse information file\n$err" 0 {Ok}
|
||
|
return
|
||
|
}
|
||
|
TekiReadTOCFile $outFile
|
||
|
file delete -force $outFile
|
||
|
|
||
|
|
||
|
# Ok, TekiInfo(contents) should contain a list of
|
||
|
# name version requires descript readme-url tek-file-url
|
||
|
|
||
|
if {[info commands .toc] == ""} {
|
||
|
toplevel .toc
|
||
|
set w .toc
|
||
|
set numButtons 5
|
||
|
button $w.install -text Install -command TekiWWWBrowseInstall
|
||
|
button $w.readme -text "View Readme" -command TekiWWWBrowseReadme
|
||
|
button $w.showall -text "Show All Packages" -command TekiWWWBrowseAll
|
||
|
button $w.showcompat -text "Show Compatible Packages" -command TekiWWWBrowseCompat
|
||
|
button $w.cancel -text "Close" -command "wm withdraw .toc"
|
||
|
scrollbar $w.scroll -command "$w.list yview"
|
||
|
listbox $w.list -font $TekiInfo(fixedfont) -relief sunken -width 100 \
|
||
|
-bd 2 -yscrollcommand "$w.scroll set" -setgrid 1 -height 12 \
|
||
|
-selectmode extended
|
||
|
grid $w.scroll -column $numButtons -row 0 -sticky ns
|
||
|
grid $w.list -column 0 -columnspan $numButtons -row 0 -sticky nsew
|
||
|
grid $w.install -column 0 -row 1
|
||
|
grid $w.readme -column 1 -row 1
|
||
|
grid $w.showall -column 2 -row 1
|
||
|
grid $w.showcompat -column 3 -row 1
|
||
|
grid $w.cancel -column 4 -row 1 -columnspan 2
|
||
|
}
|
||
|
Wise_CenterWindow .toc
|
||
|
wm transient .toc .
|
||
|
TekiWWWBrowseAll
|
||
|
}
|
||
|
|
||
|
#-----------------------------------------------------------------------
|
||
|
#
|
||
|
# WWW support --
|
||
|
#
|
||
|
# This section contains code to download files and directories from
|
||
|
# a URL into a temp file.
|
||
|
|
||
|
#
|
||
|
# Get a decent value for the temporary directory
|
||
|
#
|
||
|
switch $tcl_platform(platform) {
|
||
|
windows {set TekiInfo(tmpDir) C:/TEMP}
|
||
|
unix {set TekiInfo(tmpDir) /tmp}
|
||
|
}
|
||
|
if {[info exists env(TMP)] && [file isdirectory $env(TMP)]} {
|
||
|
set TekiInfo(tmpDir) $env(TMP)
|
||
|
}
|
||
|
if {[info exists env(TMPDIR)] && [file isdirectory $env(TMPDIR)]} {
|
||
|
set TekiInfo(tmpDir) $env(TMPDIR)
|
||
|
}
|
||
|
if {[info exists env(TEMP)] && [file isdirectory $env(TEMP)]} {
|
||
|
set TekiInfo(tmpDir) $env(TEMP)
|
||
|
}
|
||
|
if {[info exists env(TEMPDIR)] && [file isdirectory $env(TEMPDIR)]} {
|
||
|
set TekiInfo(tmpDir) $env(TEMPDIR)
|
||
|
}
|
||
|
set TekiInfo(browseURL) http://www2.cs.cornell.edu/zeno/bsmith/contents.tkc
|
||
|
|
||
|
# -----------------------------------------------------------------------
|
||
|
#
|
||
|
# Routines to read and verify .tek files
|
||
|
#
|
||
|
|
||
|
# Todo:
|
||
|
# Bullet-proof this section using a safe interpreter
|
||
|
#
|
||
|
|
||
|
proc TekiReadTOC1.0 {filename} {
|
||
|
global TekiInfo
|
||
|
if [catch {source $filename} err] {
|
||
|
global errorCode errorInfo
|
||
|
error "Error reading version 1.0 TEKI TOC file $filename.
|
||
|
Execute \"source $filename\" in a Tcl shell to find error" \
|
||
|
$errorInfo $errorCode
|
||
|
}
|
||
|
}
|
||
|
|
||
|
proc TekiReadTOCFile {filename} {
|
||
|
Debug_Enter TekiReadTOCFile
|
||
|
set f [open $filename r]
|
||
|
set x [split [gets $f]]
|
||
|
close $f
|
||
|
if {([llength $x] != 3) ||
|
||
|
[string compare [lindex $x 0] #] ||
|
||
|
[string compare [lindex $x 1] TekiTOC] } {
|
||
|
error "Invalid Teki Table-of-contents file"
|
||
|
}
|
||
|
set tekiFileVersion [lindex $x 2]
|
||
|
if [catch {TekiReadTOC$tekiFileVersion $filename} err] {
|
||
|
global errorCode errorInfo
|
||
|
error $err $errorInfo $errorCode
|
||
|
}
|
||
|
Debug_Leave
|
||
|
}
|
||
|
|
||
|
proc TekiReadVersion1.0 {filename} {
|
||
|
global newPackage
|
||
|
if [catch {source $filename} err] {
|
||
|
global errorCode errorInfo
|
||
|
error "Error reading version 1.0 TEKI file $filename.
|
||
|
Execute \"source $filename\" in a Tcl shell to find error" \
|
||
|
$errorInfo $errorCode
|
||
|
}
|
||
|
}
|
||
|
|
||
|
proc TekiReadFile {filename} {
|
||
|
Debug_Enter ReadTekiFile
|
||
|
set cwd [pwd]
|
||
|
cd [file dirname $filename]
|
||
|
set filename [file tail $filename]
|
||
|
set f [open $filename r]
|
||
|
set x [split [gets $f]]
|
||
|
close $f
|
||
|
if {([llength $x] != 3) ||
|
||
|
[string compare [lindex $x 0] #] ||
|
||
|
[string compare [lindex $x 1] TekiFile] } {
|
||
|
cd $cwd
|
||
|
error "Invalid Teki file"
|
||
|
}
|
||
|
set tekiFileVersion [lindex $x 2]
|
||
|
if [catch {TekiReadVersion$tekiFileVersion $filename} err] {
|
||
|
global errorCode errorInfo
|
||
|
cd $cwd
|
||
|
error $err $errorInfo $errorCode
|
||
|
}
|
||
|
cd $cwd
|
||
|
Debug_Leave
|
||
|
}
|
||
|
|
||
|
proc TekiVerifyFile {} {
|
||
|
global newPackage
|
||
|
foreach attr {available defaultPackages defaultArch
|
||
|
defaultInstallDoc defaultInstallExamples defaultInstallData} {
|
||
|
if ![info exists newPackage($attr)] {
|
||
|
error "Teki file does not define attribute '$attr'"
|
||
|
}
|
||
|
}
|
||
|
foreach pkg $newPackage(available) {
|
||
|
foreach attr {name version description requires updateURL srcURL
|
||
|
registerURL srcDir destDir infoFile tclFiles dataFiles
|
||
|
docDir docFiles exampleFiles objFiles copyright} {
|
||
|
if ![info exists newPackage($pkg,$attr)] {
|
||
|
error "Package $pkg does not define attribute '$attr'"
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
foreach pkg $newPackage(defaultPackages) {
|
||
|
if ![info exists newPackage($pkg,name)] {
|
||
|
error "Teki file defines invalid package $pkg in as part of defaultPackages"
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# ---------------------------------------------------------------------- -
|
||
|
#
|
||
|
# TekiInstall
|
||
|
#
|
||
|
# Install a package.
|
||
|
# The file has been read and verified, and the packages variable refers
|
||
|
# to good packages (they exist in newPackage, haven't already been
|
||
|
# installed, etc). The errors we have to worry about are errors copying
|
||
|
# files.
|
||
|
|
||
|
#
|
||
|
# TekiDownloadFile --
|
||
|
#
|
||
|
# Copy $url into $tmpDir/$file, making directories as needed
|
||
|
#
|
||
|
# Throws an error if user presses cancel button during download
|
||
|
# or another error occurs.
|
||
|
# The caller must delete the file when done.
|
||
|
|
||
|
proc TekiCancelDownload {} {
|
||
|
global TekiInfo
|
||
|
wm withdraw .dl
|
||
|
http_reset $TekiInfo(token)
|
||
|
}
|
||
|
|
||
|
proc TekiDownloadProgressEnd {} {
|
||
|
wm withdraw .dl
|
||
|
}
|
||
|
|
||
|
proc TekiDownloadProgressStart {url} {
|
||
|
global TekiInfo
|
||
|
if {[info commands .dl] == ""} {
|
||
|
toplevel .dl
|
||
|
label .dl.l -font $TekiInfo(fixedfont)
|
||
|
scale .dl.s -orient horizontal -sliderrelief flat \
|
||
|
-sliderlength 0 -showvalue 0
|
||
|
button .dl.cancel -text Cancel -command TekiCancelDownload
|
||
|
grid .dl.l -row 0 -column 0 -sticky ew
|
||
|
grid .dl.s -row 1 -column 0 -sticky ew
|
||
|
grid .dl.cancel -row 2 -column 0
|
||
|
} else {
|
||
|
.dl.s configure -sliderlength 0
|
||
|
}
|
||
|
wm title .dl "Downloading $url"
|
||
|
.dl.l configure -text $url
|
||
|
Wise_CenterWindow .dl
|
||
|
}
|
||
|
|
||
|
proc TekiDownloadProgress {token max curr} {
|
||
|
global TekiInfo
|
||
|
set TekiInfo(token) $token
|
||
|
if {$max > 0} {
|
||
|
set width [winfo width .dl.s]
|
||
|
set pixels [expr int($curr*$width/$max)]
|
||
|
.dl.s configure -sliderlength $pixels
|
||
|
update idletasks
|
||
|
}
|
||
|
}
|
||
|
|
||
|
proc TekiDownloadFile {url dest} {
|
||
|
global errorInfo errorCode
|
||
|
file mkdir [file dirname $dest]
|
||
|
set f [open $dest w]
|
||
|
fconfigure $f -translation binary
|
||
|
TekiDownloadProgressStart $url
|
||
|
set code [catch {http_get $url -channel $f -progress TekiDownloadProgress} token]
|
||
|
TekiDownloadProgressEnd
|
||
|
close $f
|
||
|
if {$code} {
|
||
|
catch {file delete -force $dest}
|
||
|
error "HTTP error:\n $token\n\nwhile fetching\n $url" \
|
||
|
$errorInfo $errorCode
|
||
|
}
|
||
|
upvar #0 $token state
|
||
|
|
||
|
# The variable state(http) contains the server response
|
||
|
# code. If the code is [45]xx, there's a problem with the
|
||
|
# URL.
|
||
|
if {[string match {[45]*} [lindex $state(http) 1]]} {
|
||
|
catch {file delete -force $dest}
|
||
|
set err [lrange $state(http) 2 end]
|
||
|
error "HTTP error:\n $err\n\nwhile fetching\n $url" \
|
||
|
}
|
||
|
|
||
|
# Handle URL redirects
|
||
|
if {$state(status) == "reset"} {
|
||
|
file delete -force $dest
|
||
|
uplevel #0 unset $token
|
||
|
error "Connection reset by user"
|
||
|
}
|
||
|
foreach {name value} $state(meta) {
|
||
|
if {[regexp -nocase ^location$ $name]} {
|
||
|
file delete -force $dest
|
||
|
TekiDownloadFile [string trim $value] $dest
|
||
|
}
|
||
|
}
|
||
|
uplevel #0 unset $token
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# Copy from $src to $dest
|
||
|
# Make directories as needed
|
||
|
# Download file is source is a URL
|
||
|
# Example:
|
||
|
# src = http://www2.cs.cornell.edu/bsmith/tmp/tcl-files/foo.tcl
|
||
|
# dest = /usr/local/lib/tcl/dp4.0/tcl-files/foo.tcl
|
||
|
#
|
||
|
proc TekiCopy {src dest} {
|
||
|
file mkdir [file dirname $dest]
|
||
|
if [string match http://* $src] {
|
||
|
TekiDownloadFile $src $dest
|
||
|
} else {
|
||
|
file copy $src $dest
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# TekiInstall --
|
||
|
#
|
||
|
# Install the packages passed in. Can be called
|
||
|
# with through the command line or the Teki GUI.
|
||
|
#
|
||
|
# Parameters:
|
||
|
#
|
||
|
# dirBase -- directory where teki file is located. Other
|
||
|
# file (code, etc) are given by the srcDir relative
|
||
|
# to this directory
|
||
|
# web -- if true if we're doing a Web-based install and the
|
||
|
# $srcURL for each package is used to locate files to copy.
|
||
|
# If false, $dirBase/$srcDir for each package gives the
|
||
|
# place where the source files are located.
|
||
|
# codePrefix -- prefix for installing code, data, and examples. For
|
||
|
# example, /usr/local/lib/tcl
|
||
|
# docPrefix -- prefix for installing documentation. (e.g., /usr/man)
|
||
|
# packages -- list of packages to install (members of newPackage(installed))
|
||
|
# archList -- list of archtectures, (members of TekiInfo(systemNames))
|
||
|
# installData -- Boolean indicating whether to install data files
|
||
|
# installDoc -- Boolean indicating whether to install documentation
|
||
|
# installExamples -- Boolean indicating whether to install examples
|
||
|
#
|
||
|
# Notes
|
||
|
# Installs documentation in $docPrefix/$newPackage(destDir)
|
||
|
# Installs tcl files, object files, data, and examples in
|
||
|
# $codePrefix/$newPackage(destDir)
|
||
|
#
|
||
|
#
|
||
|
proc TekiInstall {dirBase web codePrefix docPrefix packages archList
|
||
|
{installData 1} {installDoc 1} {installExamples 1}} {
|
||
|
global newPackage tcl_platform TekiInfo errorCode errorInfo
|
||
|
|
||
|
Debug_Enter TekiInstall
|
||
|
|
||
|
if {![file isdirectory $codePrefix]} {
|
||
|
TekiError "Error: Destination directory $codePrefix doesn't exist"
|
||
|
return
|
||
|
}
|
||
|
|
||
|
foreach pkg $packages {
|
||
|
|
||
|
if $web {
|
||
|
set srcDir $newPackage($pkg,srcURL)
|
||
|
} else {
|
||
|
set srcDir [file join $dirBase $newPackage($pkg,srcDir)]
|
||
|
}
|
||
|
|
||
|
set steps {
|
||
|
"Making directory"
|
||
|
"Creating pkgIndex.tcl"
|
||
|
"Information file"
|
||
|
"Tcl files"
|
||
|
}
|
||
|
if {$installData && [string length $newPackage($pkg,dataFiles)]} {
|
||
|
lappend steps "Data files"
|
||
|
}
|
||
|
if {$installExamples && [string length $newPackage($pkg,exampleFiles)]} {
|
||
|
lappend steps "Examples"
|
||
|
}
|
||
|
if {$installDoc && [string length $newPackage($pkg,docFiles)]} {
|
||
|
lappend steps "Documentation"
|
||
|
}
|
||
|
foreach pair $newPackage($pkg,objFiles) {
|
||
|
set arch [lindex $pair 0]
|
||
|
if {[string match "all" $archList] ||
|
||
|
([lsearch -exact $archList $arch] != -1)} {
|
||
|
lappend steps "Files for $arch"
|
||
|
}
|
||
|
}
|
||
|
Progress_StepInit "Installing $pkg" $steps
|
||
|
Undo_Add $pkg Progress_StepEnd
|
||
|
|
||
|
|
||
|
|
||
|
# Make the destination directory. If it exists, verify that
|
||
|
# we should replace it
|
||
|
|
||
|
set destDir [file join $codePrefix $newPackage($pkg,destDir)]
|
||
|
if [file isdirectory $destDir] {
|
||
|
set rval [TekiWarning \
|
||
|
"Warning: Directory $destDir already exists.
|
||
|
Delete will replace it
|
||
|
Cancel will abort the installation of this package
|
||
|
Abort will abort the installation of this and all remaining packages" \
|
||
|
1 {Delete Cancel Abort}]
|
||
|
if {$rval == 2} {
|
||
|
Undo_All $pkg
|
||
|
break
|
||
|
}
|
||
|
if {$rval == 1} {
|
||
|
Undo_All $pkg
|
||
|
continue
|
||
|
}
|
||
|
if [catch {file delete -force $destDir} err] {
|
||
|
set rval [TekiWarning \
|
||
|
"Warning: Couldn't delete $destDir\n$err
|
||
|
Cancel will abort the installation of this package
|
||
|
Abort will abort the installation of all packages" \
|
||
|
0 {Cancel Abort}]
|
||
|
Undo_All $pkg
|
||
|
if {$rval == 0} {
|
||
|
continue
|
||
|
} else {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
Progress_StepPrint
|
||
|
|
||
|
|
||
|
|
||
|
# Create $destDir, open $destDir/pkgIndex.tcl, and start writing it...
|
||
|
|
||
|
set fn [file join $destDir pkgIndex.tcl]
|
||
|
if [catch {file mkdir [file dirname $fn]} err] {
|
||
|
set rval [TekiWarning \
|
||
|
"Couldn't create target directory $destDir\n$err
|
||
|
Cancel will abort the installation of this package
|
||
|
Abort will abort the installation of all packages" \
|
||
|
0 {Cancel Abort}]
|
||
|
Undo_All $pkg
|
||
|
if {$rval == 0} {
|
||
|
continue
|
||
|
} else {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
Undo_Add $pkg "file delete -force \"$destDir\""
|
||
|
|
||
|
if [catch {set pkgIndex [open $fn w]} err] {
|
||
|
set rval [TekiWarning \
|
||
|
"Couldn't create pkgIndex.tcl in $destDir\n$err
|
||
|
Cancel will abort the installation of this package
|
||
|
Abort will abort the installation of all packages" \
|
||
|
0 {Cancel Abort}]
|
||
|
Undo_All $pkg
|
||
|
if {$rval == 0} {
|
||
|
continue
|
||
|
} else {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
Undo_Add $pkg "close $pkgIndex"
|
||
|
|
||
|
puts $pkgIndex "
|
||
|
# This file was automatically generated by TEKI
|
||
|
global tclPkgInfo
|
||
|
if {!\[info exists tclPkgInfo(installed)\] ||
|
||
|
\[lsearch \$tclPkgInfo(installed) $pkg\] == -1} {
|
||
|
lappend tclPkgInfo(installed) $pkg
|
||
|
|
||
|
set tclPkgInfo($pkg,codePrefix) [list $codePrefix]
|
||
|
set tclPkgInfo($pkg,docPrefix) [list $docPrefix]
|
||
|
set tclPkgInfo($pkg,installData) $installData
|
||
|
set tclPkgInfo($pkg,installDoc) $installDoc
|
||
|
set tclPkgInfo($pkg,installExamples) $installExamples
|
||
|
|
||
|
set tclPkgInfo($pkg,name) [list $newPackage($pkg,name)]
|
||
|
set tclPkgInfo($pkg,version) [list $newPackage($pkg,version)]
|
||
|
set tclPkgInfo($pkg,description) [list $newPackage($pkg,description)]
|
||
|
set tclPkgInfo($pkg,requires) [list $newPackage($pkg,requires)]
|
||
|
set tclPkgInfo($pkg,tekiFile) [list $newPackage($pkg,tekiFile)]
|
||
|
set tclPkgInfo($pkg,updateURL) [list $newPackage($pkg,updateURL)]
|
||
|
set tclPkgInfo($pkg,registerURL) [list $newPackage($pkg,registerURL)]
|
||
|
set tclPkgInfo($pkg,destDir) [list $newPackage($pkg,destDir)]
|
||
|
set tclPkgInfo($pkg,tclFiles) [list $newPackage($pkg,tclFiles)]
|
||
|
set tclPkgInfo($pkg,systemNames) [list $TekiInfo(systemNames)]"
|
||
|
|
||
|
Progress_StepPrint
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
# Copy in all the platform independent files to $destDir
|
||
|
# Preserve the package directory hierarchy
|
||
|
|
||
|
set infoFile $newPackage($pkg,infoFile)
|
||
|
if [string length $infoFile] {
|
||
|
if [catch {TekiCopy $srcDir/$infoFile $destDir/$infoFile} err] {
|
||
|
set rval [TekiWarning \
|
||
|
"Couldn't copying\n$infoFile\nto\n$destDir\n$err
|
||
|
Cancel will abort the installation of this package
|
||
|
Abort will abort the installation of all packages
|
||
|
Ignore will ignore this error and continue installation" \
|
||
|
0 {Cancel Abort Ignore}]
|
||
|
if {$rval == 0} {
|
||
|
Undo_All $pkg
|
||
|
continue
|
||
|
}
|
||
|
if {$rval == 1} {
|
||
|
Undo_All $pkg
|
||
|
break
|
||
|
}
|
||
|
set f {}
|
||
|
} else {
|
||
|
set f [file join $destDir $newPackage($pkg,infoFile)]
|
||
|
}
|
||
|
} else {
|
||
|
set f {}
|
||
|
}
|
||
|
puts $pkgIndex " set tclPkgInfo($pkg,infoFile) [list $f]"
|
||
|
Progress_StepPrint
|
||
|
|
||
|
set code 2
|
||
|
foreach file $newPackage($pkg,tclFiles) {
|
||
|
if [catch {TekiCopy $srcDir/$file $destDir/$file} err] {
|
||
|
set code [TekiWarning \
|
||
|
"Error copying\n$file\nto\n$destDir\n$err
|
||
|
Cancel will abort the installation of this package
|
||
|
Abort will abort the installation of all packages" \
|
||
|
0 {Cancel Abort}]
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if {$code == 0} {
|
||
|
Undo_All $pkg
|
||
|
continue
|
||
|
}
|
||
|
if {$code == 1} {
|
||
|
Undo_All $pkg
|
||
|
break
|
||
|
}
|
||
|
Progress_StepPrint
|
||
|
|
||
|
set dataFiles {}
|
||
|
if $installData {
|
||
|
set dataFiles $newPackage($pkg,dataFiles)
|
||
|
foreach file $dataFiles {
|
||
|
if [catch {TekiCopy $srcDir/$file $destDir/$file} err] {
|
||
|
set code [TekiWarning \
|
||
|
"Error copying data file\n$file\nto\n$destDir\n$err
|
||
|
Cancel will abort the installation of this package
|
||
|
Abort will abort the installation of all packages
|
||
|
Ignore will ignore this error and continue installation" \
|
||
|
0 {Cancel Abort Ignore}]
|
||
|
if {$code != 2} {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if {$code == 0} {
|
||
|
Undo_All $pkg
|
||
|
continue
|
||
|
}
|
||
|
if {$code == 1} {
|
||
|
Undo_All $pkg
|
||
|
break
|
||
|
}
|
||
|
Progress_StepPrint
|
||
|
}
|
||
|
puts $pkgIndex " set tclPkgInfo($pkg,dataFiles) [list $dataFiles]"
|
||
|
|
||
|
set exampleFiles {}
|
||
|
if $installExamples {
|
||
|
set exampleFiles $newPackage($pkg,exampleFiles)
|
||
|
foreach file $exampleFiles {
|
||
|
if [catch {TekiCopy $srcDir/$file $destDir/$file} err] {
|
||
|
set code [TekiWarning \
|
||
|
"Error copying\n$file\nto\n$destDir\n$err
|
||
|
Cancel will abort the installation of this package
|
||
|
Abort will abort the installation of all packages
|
||
|
Ignore will ignore this error and continue installation" \
|
||
|
0 {Cancel Abort Ignore}]
|
||
|
if {$code != 2} {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if {$code == 0} {
|
||
|
Undo_All $pkg
|
||
|
continue
|
||
|
}
|
||
|
if {$code == 1} {
|
||
|
Undo_All $pkg
|
||
|
break
|
||
|
}
|
||
|
Progress_StepPrint
|
||
|
}
|
||
|
puts $pkgIndex " set tclPkgInfo($pkg,exampleFiles) [list $exampleFiles]"
|
||
|
|
||
|
set docFiles {}
|
||
|
set docSrcDir $srcDir/$newPackage($pkg,docDir)
|
||
|
set docDestDir [file join $docPrefix $newPackage($pkg,destDir)]
|
||
|
if $installDoc {
|
||
|
#
|
||
|
# Make the destination directory. If it exists, verify that
|
||
|
# we should replace it
|
||
|
#
|
||
|
if [file isdirectory $docDestDir] {
|
||
|
set rval [TekiWarning \
|
||
|
"Warning: Directory $docDestDir already exists.
|
||
|
Delete will replace it
|
||
|
Cancel will abort the installation of this package
|
||
|
Abort will abort the installation of all packages" \
|
||
|
1 {Delete Cancel Abort}]
|
||
|
if {$rval == 1} {
|
||
|
Undo_All $pkg
|
||
|
continue
|
||
|
}
|
||
|
if {$rval == 2} {
|
||
|
Undo_All $pkg
|
||
|
break
|
||
|
}
|
||
|
if [catch {file delete -force $docDestDir} err] {
|
||
|
set code [TekiWarning \
|
||
|
"Error: Couldn't delete $docDestDir:\n$err
|
||
|
Cancel will abort the installation of this package
|
||
|
Abort will abort the installation of all packages
|
||
|
Ignore will ignore this error and continue installation" \
|
||
|
0 {Cancel Abort Ignore}]
|
||
|
if {$code == 0} {
|
||
|
Undo_All $pkg
|
||
|
continue
|
||
|
}
|
||
|
if {$code == 1} {
|
||
|
Undo_All $pkg
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
# Create $docDestDir
|
||
|
|
||
|
if [catch {file mkdir $docDestDir} err] {
|
||
|
set code [TekiWarning \
|
||
|
"Error creating documentation target directory $docDestDir\n$err
|
||
|
Cancel will abort the installation of this package
|
||
|
Abort will abort the installation of all packages
|
||
|
Ignore will ignore this error and continue installation" \
|
||
|
0 {Cancel Abort Ignore}]
|
||
|
if {$code == 0} {
|
||
|
Undo_All $pkg
|
||
|
continue
|
||
|
}
|
||
|
if {$code == 1} {
|
||
|
Undo_All $pkg
|
||
|
break
|
||
|
}
|
||
|
} else {
|
||
|
Undo_Add $pkg "file delete -force \"$docDestDir\""
|
||
|
}
|
||
|
|
||
|
|
||
|
# Copy all the documentation files
|
||
|
|
||
|
foreach file $newPackage($pkg,docFiles) {
|
||
|
if [catch {TekiCopy $docSrcDir/$file $docDestDir/$file} err] {
|
||
|
set code [TekiWarning \
|
||
|
"Error copying\n$file\nto\n$docDestDir\n$err
|
||
|
Cancel will abort the installation of this package
|
||
|
Abort will abort the installation of all packages
|
||
|
Ignore will ignore this error and continue installation" \
|
||
|
0 {Cancel Abort Ignore}]
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
if {$code == 0} {
|
||
|
Undo_All $pkg
|
||
|
continue
|
||
|
}
|
||
|
if {$code == 1} {
|
||
|
Undo_All $pkg
|
||
|
break
|
||
|
}
|
||
|
Progress_StepPrint
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
# Copy in all the platform dependent files
|
||
|
# If we get an error and have to abort in the middle,
|
||
|
# we'll throw an error to break out of the nested
|
||
|
# loops.
|
||
|
|
||
|
set installedArchs {}
|
||
|
set code [catch {
|
||
|
foreach pair $newPackage($pkg,objFiles) {
|
||
|
set system [lindex $pair 0]
|
||
|
#
|
||
|
# system is a name for the system, such as Solaris
|
||
|
# one Windows. $archList is either the
|
||
|
# string "all" or a list of these names
|
||
|
#
|
||
|
if {[string match "all" $archList] ||
|
||
|
([lsearch -exact $archList $system] != -1)} {
|
||
|
set fileList {}
|
||
|
lappend installedArchs $system
|
||
|
foreach file [lindex $pair 1] {
|
||
|
if [catch {TekiCopy $srcDir/$file $destDir/$file} err] {
|
||
|
set rval [TekiWarning \
|
||
|
"Error copying\n$file\nto\n$destDir\n$err\n
|
||
|
Cancel will abort the installation of this package
|
||
|
Abort will abort the installation of all packages
|
||
|
Ignore will ignore this error and continue installation" \
|
||
|
0 {Cancel Abort Ignore}]
|
||
|
switch $rval {
|
||
|
0 {
|
||
|
Undo_All $pkg
|
||
|
error {} 5; # continue
|
||
|
}
|
||
|
|
||
|
1 {
|
||
|
Undo_All $pkg
|
||
|
error {} 6; # break
|
||
|
}
|
||
|
|
||
|
2 {}
|
||
|
}
|
||
|
}
|
||
|
lappend fileList $file
|
||
|
}
|
||
|
Progress_StepPrint
|
||
|
puts $pkgIndex " set tclPkgInfo($pkg,objFiles,$system) [list $fileList]"
|
||
|
}
|
||
|
}
|
||
|
} err]
|
||
|
switch $code {
|
||
|
0 {}
|
||
|
5 continue
|
||
|
6 break
|
||
|
default {
|
||
|
error $err $errorInfo $errorCode
|
||
|
}
|
||
|
}
|
||
|
puts $pkgIndex " set tclPkgInfo($pkg,archList) [list $installedArchs]"
|
||
|
|
||
|
# Finish up!
|
||
|
|
||
|
# puts $pkgIndex " TekiPackageInit $pkg"
|
||
|
|
||
|
puts $pkgIndex "}"
|
||
|
set tclfilesdir "$codePrefix/$pkg"
|
||
|
set outstr [concat package ifneeded dp 4.0 \[list LoadLib $tclfilesdir\]]
|
||
|
puts $pkgIndex $outstr
|
||
|
puts $pkgIndex "proc LoadLib dir {"
|
||
|
set outstr [concat foreach file \[glob \$dir/library/*.tcl\]]
|
||
|
puts -nonewline $pkgIndex " $outstr"
|
||
|
puts $pkgIndex " {"
|
||
|
set outstr [concat uplevel #0 source \[list \$file\]]
|
||
|
puts $pkgIndex " $outstr"
|
||
|
puts $pkgIndex " }"
|
||
|
|
||
|
set outstr [concat uplevel #0 load \[list \$dir/$file\]]
|
||
|
puts $pkgIndex " $outstr"
|
||
|
puts $pkgIndex "}"
|
||
|
close $pkgIndex
|
||
|
Undo_Clear $pkg
|
||
|
Progress_StepEnd
|
||
|
}
|
||
|
TekiGetAllPkgInfo
|
||
|
Debug_Leave
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# TekiUninstall --
|
||
|
#
|
||
|
# Uninstall all the packages passed in. Can be called with through the
|
||
|
# command line or the Teki GUI.
|
||
|
#
|
||
|
proc TekiUninstall {pkgList} {
|
||
|
global tclPkgInfo
|
||
|
set noWarn 0
|
||
|
foreach pkg $pkgList {
|
||
|
if {!$noWarn} {
|
||
|
set rval [TekiWarning "Uninstall package $pkg ?" \
|
||
|
0 {Yes {Yes All} Cancel}]
|
||
|
if {$rval == 2} {
|
||
|
return
|
||
|
}
|
||
|
if {$rval == 1} {
|
||
|
set noWarn 1
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Delete all the code/data/example files.
|
||
|
# These are stored under the $codePrefix directory
|
||
|
set codePrefix $tclPkgInfo($pkg,codePrefix)
|
||
|
set destDir [file join $codePrefix $tclPkgInfo($pkg,destDir)]
|
||
|
if [catch {file delete -force $destDir} err] {
|
||
|
TekiError "Error: Couldn't delete $destDir:\n$err\nAborting uninstallation"
|
||
|
break
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# delete doc files
|
||
|
#
|
||
|
if {$tclPkgInfo($pkg,installDoc)} {
|
||
|
set docPrefix $tclPkgInfo($pkg,docPrefix)
|
||
|
set dir [file join $docPrefix $tclPkgInfo($pkg,destDir)]
|
||
|
if [catch {file delete -force $dir} err] {
|
||
|
TekiError "Error: Couldn't delete $dir:\n$err\nAborting uninstallation"
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# ---------------------------------------------------------------------
|
||
|
#
|
||
|
# Support procedures for all extensions. The following two procedures
|
||
|
# are used at runtime to source/load the right files (including architecture
|
||
|
# dependent files). TekiPackageInit is called by the pkgIndex.tcl file
|
||
|
# with the package name, after the tclPkgInfo variables have been set up.
|
||
|
# It determines if the OS is supported, and if so, makes the right calls
|
||
|
# to package ifneeded. TekiPackageSetup is called when package require
|
||
|
# is called. It loads all the files (including architecture dependent
|
||
|
# files
|
||
|
#
|
||
|
|
||
|
#
|
||
|
# Predefined system names
|
||
|
#
|
||
|
set TekiInfo(systemNames) {
|
||
|
{Solaris SunOS 5*}
|
||
|
{SunOS SunOS 4*}
|
||
|
{HPUX HP* 9*}
|
||
|
{Linux Linux* 2*}
|
||
|
{FreeBSD FreeBSD* 2*}
|
||
|
{Win95/NT Win* *}
|
||
|
}
|
||
|
|
||
|
proc TekiPackageInit {pkg} {
|
||
|
global tclPkgInfo tcl_platform
|
||
|
|
||
|
set os $tcl_platform(os)
|
||
|
set ver $tcl_platform(osVersion)
|
||
|
|
||
|
set system {}
|
||
|
foreach tuple $tclPkgInfo($pkg,systemNames) {
|
||
|
set osPattern [lindex $tuple 1]
|
||
|
set verPattern [lindex $tuple 2]
|
||
|
if {[string match $osPattern $os] &&
|
||
|
[string match $verPattern $ver]} {
|
||
|
set system [lindex $tuple 0]
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if [string length $system] {
|
||
|
set name $tclPkgInfo($pkg,name)
|
||
|
set version $tclPkgInfo($pkg,version)
|
||
|
package ifneeded $name $version "TekiPackageSetup $pkg $system"
|
||
|
}
|
||
|
}
|
||
|
|
||
|
proc TekiPackageSetup {pkg system} {
|
||
|
global tclPkgInfo
|
||
|
set currDir [pwd]
|
||
|
set prefix $tclPkgInfo($pkg,codePrefix)
|
||
|
set destDir [file join $prefix $tclPkgInfo($pkg,destDir)]
|
||
|
cd $destDir
|
||
|
set tclFiles $tclPkgInfo($pkg,tclFiles)
|
||
|
if [info exists tclPkgInfo($pkg,objFiles,$system)] {
|
||
|
set objFiles $tclPkgInfo($pkg,objFiles,$system)
|
||
|
} else {
|
||
|
set objFiles {}
|
||
|
}
|
||
|
foreach f $tclFiles {
|
||
|
catch {uplevel #0 "source $f"}
|
||
|
}
|
||
|
foreach f $objFiles {
|
||
|
if [string match *.tcl $f] {
|
||
|
catch {uplevel #0 "source $f"}
|
||
|
} else {
|
||
|
catch {uplevel #0 "load $f"}
|
||
|
}
|
||
|
}
|
||
|
cd $currDir
|
||
|
}
|
||
|
|
||
|
proc TekiGetAllPkgInfo {} {
|
||
|
global tcl_pkgPath auto_path tclPkgInfo tcl_version tk_version
|
||
|
catch {unset tclPkgInfo}
|
||
|
set tclver tcl$tcl_version
|
||
|
set tclPkgInfo(installed) $tclver
|
||
|
set tclPkgInfo($tclver,name) Tcl
|
||
|
set tclPkgInfo($tclver,requires) {}
|
||
|
set tclPkgInfo($tclver,version) $tcl_version
|
||
|
set tclPkgInfo($tclver,description) "Tcl core"
|
||
|
set tclPkgInfo($tclver,infoFile) {}
|
||
|
|
||
|
if [info exists tk_version] {
|
||
|
set tkver tk$tk_version
|
||
|
lappend tclPkgInfo(installed) $tkver
|
||
|
set tclPkgInfo($tkver,name) Tk
|
||
|
set tclPkgInfo($tkver,requires) [list [list Tcl $tcl_version]]
|
||
|
set tclPkgInfo($tkver,version) $tk_version
|
||
|
set tclPkgInfo($tkver,description) "Tk core"
|
||
|
set tclPkgInfo($tkver,infoFile) {}
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# Get list of all pkgIndex.tcl files
|
||
|
#
|
||
|
set fileList {}
|
||
|
foreach dir [concat $auto_path [list $tcl_pkgPath]] {
|
||
|
if {![catch {glob [file join $dir pkgIndex.tcl] [file join $dir * pkgIndex.tcl]} files]} {
|
||
|
set fileList [concat $files $fileList]
|
||
|
}
|
||
|
}
|
||
|
set code 0
|
||
|
foreach file $fileList {
|
||
|
if [catch {source $file} err] {
|
||
|
if {$code != 1} {
|
||
|
set code [TekiWarning \
|
||
|
"Error sourcing $file:\n $err?" 0 {Ignore {Ignore All} Abort}]
|
||
|
}
|
||
|
if {$code == 2} {
|
||
|
exit
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# ---------------------------------------------------------------------
|
||
|
#
|
||
|
# Initialization code
|
||
|
#
|
||
|
|
||
|
TekiGetAllPkgInfo
|
||
|
if $TekiInfo(gui) {
|
||
|
TekiCreateUI
|
||
|
wm deiconify .
|
||
|
}
|
||
|
|
||
|
if {$tcl_version < 8.0} {
|
||
|
proc fcopy args {
|
||
|
eval unsupported0 $args
|
||
|
}
|
||
|
}
|