Keith Vetter 2007-01-04 : I was playing around with VFS, specifically vfs::zip, and for fun wrote a tcl-only version of unzip. This is very simple version that only does basic listing and extracting, but it illuminated how to use vfs::zip. Also, it could easily be extended to more complex options.
It only handles options -l = list zip file, -q = quiet and -d directory options.
Also, it doesn't handle directories within the zip file. Doing so isn't hard, but I got bored. I'll leave that as an exercise for the next person.
##+########################################################################## # # tclUnzip.tsh -- a limited tcl only version of unzip for demo purposes # by Keith Vetter, Jan 4 2007 # package require vfs::zip array set OPT {dirList 0 quiet 0 toDir ""} ;# Options we handle array set stats {cnt 0 total 0} set usage "tclUnzip ?-l? ?-q? ?-d unzipDirectory? zipfile ?file list?" proc DoOneFile {fname} { global OPT stats incr stats(cnt) if {$OPT(dirList)} { if {[file isdirectory $fname]} { set size "<DIR> " } else { set size [file size $fname] incr stats(total) $size } set mtime [file mtime $fname] set date [clock format $mtime -format "%D"] set ftime [clock format $mtime -format "%H:%M"] set tail [file tail $fname] INFO [format "%9s %9s %5s %s" $size $date $ftime $tail] return } # Make output directory if needed if {! [file isdirectory $OPT(toDir)]} { set n [catch {file mkdir $OPT(toDir)}] if {! [file isdirectory $OPT(toDir)]} { DIE "Error: cannot create extraction directory: $OPT(toDir)" } } if {[file isdirectory $fname]} { INFO [format "%12s: %-30s %s" skipping [file tail $fname] directory] return } set outFile [file join $OPT(toDir) [file tail $fname]] if {[file exists $outFile]} { INFO [format "%12s: %-30s %s" skipping $outFile "file exists"] return } INFO [format "%12s: %-30s" extracting $outFile] file copy $fname $outFile } proc ParseArgs {} { global argc argv OPT for {set i 0} {$i < $argc} {incr i} { set arg [lindex $argv $i] switch -regexp -- $arg { ^-l$ { set OPT(dirList) 1 } ^-d$ { set OPT(toDir) [lindex $argv [incr i]]} ^-d { set OPT(toDir) [string range $arg 2 end]} ^-q$ { set OPT(quiet) 1 } ^-h$ - ^-?$ - ^--help { DIE $::usage } ^--$ { incr i; break } ^- { DIE "unknown option: \"$arg\"" } default { break } } } if {$OPT(dirList)} { set OPT(quiet) 0 } set zipFile [lindex $argv $i] incr i set argc [expr {$argc - $i}] if {$argc < 0} { DIE $::usage } set argv [lrange $argv $i end] if {$argv eq {}} { set argv "*" } if {! [file exists $zipFile]} { if {! [file exists "$zipFile.zip"]} { DIE "Error: cannot find either $zipFile or $zipFile.zip." } set zipFile "$zipFile.zip" } return $zipFile } proc DIE {msg} {puts stderr "$msg" ; exit } proc INFO {msg} {if {! $::OPT(quiet)} { puts $msg }} ################################################################ set zipFile [ParseArgs] ::vfs::zip::Mount [file normalize $zipFile] /__zip if {$tcl_interactive} return INFO "Archive: [file nativename [file normalize $zipFile]]" if {$OPT(dirList)} { INFO [format "%9s %9s %5s %s" Length "Date " Time Name] INFO [format "%9s %9s %5s %s" ------ "---- " ---- ----] } foreach arg $argv { foreach fname [lsort [glob -nocomplain /__zip/$arg]] { DoOneFile $fname } } if {$OPT(dirList)} { INFO [format "%9s %9s %5s %s" ------ "" "" ----] INFO [format "%9s %9s %5s %s%s" $stats(total) "" "" "$stats(cnt) file" \ [expr {$stats(cnt) == 1 ? "" : "s"}]] } return