#!/bin/sh # # Copyright (C) 2002 Henning Spruth # # Written by Henning Spruth # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # This is a comment to wish \ exec wish $0 -- $* # # Read channel description file '$file' and store it in # the channel and channels global vars # proc ReadChannel {file} { global channel channels set name [file rootname [file tail $file]] set errCount 0 if {[catch {set fp [open $file "r"]} status]} { puts stderr "Can't read '$file': $status" } else { while {![eof $fp]} { set line [string trim [gets $fp]] if {$line!="" && ![string match "#*" $line]} { if {[regexp {^([A-z0-9\.]+):[ ]*(.*)} $line dummy \ param value]} { set channel($name.$param) $value } else { puts stderr "In file '$file': can't parse line '$line'" incr errCount } } } close $fp foreach field {name description url depth} { if {![info exists channel($name.$field)]} { puts stderr "In file '$file': missing field '$field'" incr errCount } } if {$errCount==0 && $channel($name.name)!=$name} { puts stderr "In file '$file': name attribute doesn't match" incr errCount } if {$errCount>0} { puts stderr "Ignoring channel definition '$file'" } else { lappend channels $name } } } # # Read all channels # proc GetChannels {} { global env global channel channels global twebDir set channels {} catch {unset channel} foreach file [glob -nocomplain $twebDir/*.channel] { ReadChannel $file } # display channels in alphabetic order set channels [lsort $channels] } # # Save a channel to disk # proc WriteChannel {name} { global channel fields global twebDir set fn $twebDir/${name}.channel if {[catch {set fp [open $fn "w"]} status]} { puts stderr "Can't write to '$file': $status" } else { foreach field $fields { puts $fp "$field: $channel($name.$field)" } close $fp } } # # Run a process # proc RunCommand {cmd} { global win sema set sema 0 if {[catch {set fp [open "| $cmd 2>@ stdout < /dev/null" "r"]} status]} { tk_dialog .error Error "Can't launch command '$cmd':\n$status" error 0 Dismiss } else { $win(log) insert end "Executing: $cmd\n" log fileevent $fp readable "UpdateLog $fp" fconfigure $fp -blocking 0 -buffering line tkwait variable sema } } # # Update the log window from a running process # proc UpdateLog {fp} { global win sema if {[eof $fp]} { catch {close $fp} $win(log) insert end "Done\n" log $win(log) yview moveto 1 set sema 1 } else { #$win(log) insert end "Gotit\n" log while {[set line [gets $fp]]!=""} { $win(log) insert end "$line\n" } $win(log) yview moveto 1 update } } # # # proc DoDownload {} { global channels channel global twebDir win set index [.main.list.lb curselection] if {$index != ""} { $win(log) delete 0.1 end set name [lindex $channels $index] set tmpDir $twebDir/${name}.temp file delete -force $tmpDir file mkdir $tmpDir # Try to determine how many dir levels to cut #set s [string trim $channel($name.url) /] if {[regexp {^[^/]*//[^/]+/(.*)} $channel($name.url) dummy path]} { # remove everything but slashes from the path regsub -all {[^/]} $path "" s # the remaining length is the number of slashes set n [string length $s] set extra "--cut-dirs=$n" } else { set extra "" } set cmd "/bin/sh -c \"cd $tmpDir; wget $channel($name.url) --no-host-directories $extra --recursive --convert-links --no-parent --level=$channel($name.depth)\"" RunCommand $cmd set zipFile $twebDir/${name}.zip file delete -force $zipFile set cmd "/bin/sh -c \"cd $tmpDir; zip -r $zipFile .\"" RunCommand $cmd #file delete -force $tmpDir UpdateListbox } } proc DoRsync {} { global channels channel global twebDir win set index [.main.list.lb curselection] if {$index != ""} { $win(log) delete 0.1 end set name [lindex $channels $index] set zipFile $twebDir/${name}.zip if {![file exists $zipFile]} { tk_dialog .error Error "File '$zipFile' does not exist" error 0 Dismiss } else { set cmd "rsync -v $twebDir/${name}.zip agenda::default/.tweb/${name}.zip" RunCommand $cmd } } } # # Update the GUI listbox with available channels # proc UpdateListbox {} { global channels channel global twebDir formatString set lb .main.list.lb $lb delete 0 end foreach c $channels { set zipFile $twebDir/$channel($c.name).zip if {[file exists $zipFile]} { set size "[expr [file size $zipFile]/1024]kB" } else { set size "-" } $lb insert end [format $formatString $channel($c.name) $size $channel($c.description)] } } # # Callback function that is invoked whenever the listbox # selection changes # proc ListboxCallback {lb} { set index [$lb curselection] #puts "index=$index" if {$index==""} { .main.bf.b2 configure -state disabled } else { .main.bf.b2 configure -state normal } } # # Switch between normal and edit mode # proc SwitchMode {mode} { switch $mode { main { pack forget .edit pack .main -side top -expand 1 -fill both } edit { pack forget .main pack .edit -side top -expand 1 -fill both } default { error "Illegal mode '$mode'" } } update } # # Start creating a new entry # proc DoNew {} { global channels channel fields .edit.entries.name configure -state normal foreach field $fields { .edit.entries.$field delete 0 end } SwitchMode edit } # # Edit an existing entry # proc DoEdit {} { global channels channel set index [.main.list.lb curselection] if {$index != ""} { .edit.entries.name configure -state normal set name [lindex $channels $index] foreach field {name description url depth} { .edit.entries.$field delete 0 end .edit.entries.$field insert 0 $channel($name.$field) } .edit.entries.name configure -state disabled SwitchMode edit } } # # Commit changes # proc CommitChange {} { global channels channel fields set name [.edit.entries.name get] set errMsg "" foreach field $fields { if {[.edit.entries.$field get]==""} { append errMsg "\nField '$field' must not be empty" } } if {$errMsg!=""} { tk_dialog .error Error "Error(s) in channel data:$errMsg" error 0 Dismiss } else { foreach field $fields { set channel($name.$field) [.edit.entries.$field get] } if {[lsearch $channels $name]<0} { lappend channels $name UpdateListbox } WriteChannel $name SwitchMode main } } # # Build all static GUI components # proc CreateGui {} { global win formatString set w [frame .main] # # The shell log window # set sf [frame $w.shell] set tx [text $sf.text -yscrollcommand "$sf.sb set" -font fixed -height 10] $tx tag configure log -foreground red set sb [scrollbar $sf.sb -command "$tx yview"] pack $sb -side right -fill y pack $tx -side right -fill both -expand 1 pack $sf -side bottom -fill both set win(log) $tx set w2 [frame $w.list] set la [label $w2.label -text [format $formatString "Name" "Size" "Description"] -anchor w -font fixed] set lb [listbox $w2.lb -yscrollcommand "$w2.sb set" -font fixed -width 70 -exportselection 0] bind $lb <> "ListboxCallback $lb" set sb [scrollbar $w2.sb -command "$lb yview"] pack $la -side top -fill x -anchor w pack $sb -side right -fill y pack $lb -side right -fill both -expand y set bf [frame $w.bf ] set b1 [button $bf.b1 -text New -command DoNew] set b2 [button $bf.b2 -text Edit -command DoEdit ] set b3 [button $bf.b3 -text "Download Pages" -command DoDownload] set b4 [button $bf.b4 -text "Rsync to Agenda" -command DoRsync] set b5 [button $bf.b5 -text "Quit" -command {destroy .}] pack $b1 $b2 $b3 $b4 $b5 -side top -fill x -anchor n pack $bf $w2 -side right -fill both -expand y # pack $bf $sb $lb -side right -fill both -expand y pack $w -side top -fill both -expand y # # The edit frame - note that this is not packed initially! # set w [frame .edit] set f [frame $w.entries] set row 0 foreach field {name description url depth} \ title {"Name" "Description" "URL" "Search depth"} \ description { "The name is a short string (must be a valid file name) describing the channel" "The description is used as more verbose information on the channel" "The URL points to the initial web page to get; if this is a directory it must end in a slash" "The search depth determines how many links are followed" } { set ds [label $f.desc_$field -text $description -anchor w] grid $ds -row $row -column 0 -sticky we -columnspan 2 incr row set la [label $f.label_$field -text $title -anchor w] set en [entry $f.$field ] grid $la -column 0 -row $row -sticky w grid $en -column 1 -row $row -sticky we incr row } grid columnconfigure $f 0 -weight 0 grid columnconfigure $f 1 -weight 1 set bf [frame $w.buttons] set bok [button $bf.ok -text OK -command {CommitChange}] set bcancel [button $bf.cancel -text Cancel -command {SwitchMode main}] pack $bok $bcancel -side left pack $bf $f -side bottom -fill both -expand 1 update #wm minsize . [winfo width .] [winfo height .] } wm title . "tweb-channels version 0.1" set twebDir $env(HOME)/.tweb set fields [list name description url depth] set formatString "%-15s %-6s %-30s" CreateGui GetChannels UpdateListbox