tar.tcl

Go to the documentation of this file.
00001 /*  tar.tcl --*/
00002 /* */
00003 /*        Creating, extracting, and listing posix tar archives*/
00004 /* */
00005 /*  Copyright (c) 2004    Aaron Faupell <afaupell@users.sourceforge.net>*/
00006 /* */
00007 /*  See the file "license.terms" for information on usage and redistribution*/
00008 /*  of this file, and for a DISCLAIMER OF ALL WARRANTIES.*/
00009 /*  */
00010 /*  RCS: @(#) $Id: tar.tcl,v 1.11 2007/02/09 06:03:56 afaupell Exp $*/
00011 
00012 package provide tar 0.4
00013 
00014 namespace ::tar {}
00015 
00016 ret  ::tar::parseOpts (type acc , type opts) {
00017     array set flags $acc
00018     foreach {x y} $acc {upvar $x $x}
00019     
00020     set len [llength $opts]
00021     set i 0
00022     while {$i < $len} {
00023         set name [string trimleft [lindex $opts $i] -]
00024         if {![info exists flags($name)]} {return -code error "unknown option \"$name\""}
00025         if {$flags($name) == 1} {
00026             set $name [lindex $opts [expr {$i + 1}]]
00027             incr i $flags($name)
00028         } elseif {$flags($name) > 1} {
00029             set $name [lrange $opts [expr {$i + 1}] [expr {$i + $flags($name)}]]
00030             incr i $flags($name)
00031         } else {
00032             set $name 1
00033         }
00034         incr i
00035     }
00036 }
00037 
00038 ret  ::tar::pad (type size) {
00039     set pad [expr {512 - ($size % 512)}]
00040     if {$pad == 512} {return 0}
00041     return $pad
00042 }
00043 
00044 ret  ::tar::readHeader (type data) {
00045     binary scan $data a100a8a8a8a12a12a8a1a100a6a2a32a32a8a8a155 \
00046                       name mode uid gid size mtime cksum type \
00047                       linkname magic version uname gname devmajor devminor prefix
00048                                
00049     foreach x {name mode type linkname magic uname gname prefix mode uid gid size mtime cksum version devmajor devminor} {
00050         set $x [string trim [set $x] "\x00"]
00051     }
00052     set mode [string trim $mode " \x00"]
00053     foreach x {uid gid size mtime cksum version devmajor devminor} {
00054         set $x [format %d 0[string trim [set $x] " \x00"]]
00055     }
00056 
00057     return [list name $name mode $mode uid $uid gid $gid size $size mtime $mtime \
00058                  cksum $cksum type $type linkname $linkname magic $magic \
00059                  version $version uname $uname gname $gname devmajor $devmajor \
00060                  devminor $devminor prefix $prefix]
00061 }
00062 
00063 ret  ::tar::contents (type file) {
00064     set fh [::open $file]
00065     while {![eof $fh]} {
00066         array set header [readHeader [read $fh 512]]
00067         if {$header(name) == ""} break
00068         lappend ret $header(prefix)$header(name)
00069         seek $fh [expr {$header(size) + [pad $header(size)]}] current
00070     }
00071     close $fh
00072     return $ret
00073 }
00074 
00075 ret  ::tar::stat (type tar , optional file ={)} {
00076     set fh [::open $tar]
00077     while {![eof $fh]} {
00078         array  header =  [readHeader [read $fh 512]]
00079         if {$header(name) == ""} break
00080         seek $fh [expr {$header(size) + [pad $header(size)]}] current
00081         if {$file != "" && "$header(prefix)$header(name)" != $file} {continue}
00082          header = (type) [string map {0 file 5 directory 3 characterSpecial 4 blockSpecial 6 fifo 2 link} $header(type)]
00083          header = (mode) [string range $header(mode) 2 end]
00084         lappend ret $header(prefix)$header(name) [list mode $header(mode) uid $header(uid) gid $header(gid) \
00085                     size $header(size) mtime $header(mtime) type $header(type) linkname $header(linkname) \
00086                     uname $header(uname) gname $header(gname) devmajor $header(devmajor) devminor $header(devminor)]
00087     }
00088     close $fh
00089     return $ret
00090 }
00091 
00092 ret  ::tar::get (type tar , type file) {
00093     set fh [::open $tar]
00094     fconfigure $fh -encoding binary -translation lf -eofchar {}
00095     while {![eof $fh]} {
00096         array set header [readHeader [read $fh 512]]
00097         if {$header(name) == ""} break
00098         set name [string trimleft $header(prefix)$header(name) /]
00099         if {$name == $file} {
00100             set file [read $fh $header(size)]
00101             close $fh
00102             return $file
00103         }
00104         seek $fh [expr {$header(size) + [pad $header(size)]}] current
00105     }
00106     close $fh
00107     return {}
00108 }
00109 
00110 ret  ::tar::untar (type tar , type args) {
00111     set nooverwrite 0
00112     set data 0
00113     set nomtime 0
00114     set noperms 0
00115     parseOpts {dir 1 file 1 glob 1 nooverwrite 0 nomtime 0 noperms 0} $args
00116     if {![info exists dir]} {set dir [pwd]}
00117     set pattern *
00118     if {[info exists file]} {
00119         set pattern [string map {* \\* ? \\? \\ \\\\ \[ \\\[ \] \\\]} $file]
00120     } elseif {[info exists glob]} {
00121         set pattern $glob
00122     }
00123 
00124     set ret {}
00125     set fh [::open $tar]
00126     fconfigure $fh -encoding binary -translation lf -eofchar {}
00127     while {![eof $fh]} {
00128         array set header [readHeader [read $fh 512]]
00129         if {$header(name) == ""} break
00130         set name [string trimleft $header(prefix)$header(name) /]
00131         if {![string match $pattern $name] || ($nooverwrite && [file exists $name])} {
00132             seek $fh [expr {$header(size) + [pad $header(size)]}] current
00133             continue
00134         }
00135 
00136         set name [file join $dir $name]
00137         if {![file isdirectory [file dirname $name]]} {
00138             file mkdir [file dirname $name]
00139             lappend ret [file dirname $name] {}
00140         }
00141         if {[string match {[0346]} $header(type)]} {
00142             set new [::open $name w+]
00143             fconfigure $new -encoding binary -translation lf -eofchar {}
00144             fcopy $fh $new -size $header(size)
00145             close $new
00146             lappend ret $name $header(size)
00147         } elseif {$header(type) == 5} {
00148             file mkdir $name
00149             lappend ret $name {}
00150         } elseif {[string match {[12]} $header(type)] && $::tcl_platform(platform) == "unix"} {
00151             catch {file delete $name}
00152             if {![catch {file link [string map {1 -hard 2 -symbolic} $header(type)] $name $header(linkname)}]} {
00153                 lappend ret $name {}
00154             }
00155         }
00156         seek $fh [pad $header(size)] current
00157         if {![file exists $name]} continue
00158 
00159         if {$::tcl_platform(platform) == "unix"} {
00160             if {!$noperms} {
00161                 catch {file attributes $name -permissions [string range $header(mode) 2 end]}
00162             }
00163             catch {file attributes $name -owner $header(uid) -group $header(gid)}
00164             catch {file attributes $name -owner $header(uname) -group $header(gname)}
00165         }
00166         if {!$nomtime} {
00167             file mtime $name $header(mtime)
00168         }
00169     }
00170     close $fh
00171     return $ret
00172 }
00173 
00174 ret  ::tar::createHeader (type name , type followlinks) {
00175     foreach x {linkname uname gname prefix devmajor devminor} {set $x ""}
00176     
00177     if {$followlinks} {
00178         file stat $name stat
00179     } else {
00180         file lstat $name stat
00181     }
00182     
00183     set type [string map {file 0 directory 5 characterSpecial 3 blockSpecial 4 fifo 6 link 2 socket A} $stat(type)]
00184     set gid [format %o $stat(gid)]
00185     set uid [format %o $stat(uid)]
00186     set mtime [format %o $stat(mtime)]
00187     
00188     if {$::tcl_platform(platform) == "unix"} {
00189         set uname [file attributes $name -owner]
00190         set gname [file attributes $name -group]
00191         set mode 1[file attributes $name -permissions]
00192         if {$stat(type) == "link"} {set linkname [file link $name]}
00193     } else {
00194         set mode 100644
00195         if {$stat(type) == "directory"} {set mode 100755}
00196     }
00197     
00198     set size 0
00199     if {$stat(type) == "file"} {
00200         set size [format %o $stat(size)]
00201     }
00202     
00203     set name [string trimleft $name /]
00204     if {[string length $name] > 255} {
00205         return -code error "path name over 255 chars"
00206     } elseif {[string length $name] > 100} {
00207         set prefix [string range $name 0 end-100]
00208         set name [string range $name end-99 end]
00209     }
00210 
00211     set header [binary format a100A8A8A8A12A12A8a1a100A6a2a32a32a8a8a155a12 \
00212                               $name $mode\x00 $uid\x00 $gid\x00 $size\x00 $mtime\x00 {} $type \
00213                               $linkname ustar\x00 00 $uname $gname $devmajor $devminor $prefix {}]
00214 
00215     binary scan $header c* tmp
00216     set cksum 0
00217     foreach x $tmp {incr cksum $x}
00218 
00219     return [string replace $header 148 155 [binary format A8 [format %o $cksum]\x00]]
00220 }
00221 
00222 ret  ::tar::recurseDirs (type files , type followlinks) {
00223     foreach x $files {
00224         if {[file isdirectory $x] && ([file type $x] != "link" || $followlinks)} {
00225             if {[set more [glob -dir $x -nocomplain *]] != ""} {
00226                 eval lappend files [recurseDirs $more $followlinks]
00227             } else {
00228                 lappend files $x
00229             }
00230         }
00231     }
00232     return $files
00233 }
00234 
00235 ret  ::tar::writefile (type in , type out , type followlinks) {
00236      puts -nonewline $out [createHeader $in $followlinks]
00237      set size 0
00238      if {[file type $in] == "file" || ($followlinks && [file type $in] == "link")} {
00239          set in [::open $in]
00240          fconfigure $in -encoding binary -translation lf -eofchar {}
00241          set size [fcopy $in $out]
00242          close $in
00243      }
00244      puts -nonewline $out [string repeat \x00 [pad $size]]
00245 }
00246 
00247 ret  ::tar::create (type tar , type files , type args) {
00248     set dereference 0
00249     parseOpts {dereference 0} $args
00250     
00251     set fh [::open $tar w+]
00252     fconfigure $fh -encoding binary -translation lf -eofchar {}
00253     foreach x [recurseDirs $files $dereference] {
00254         writefile $x $fh $dereference
00255     }
00256     puts -nonewline $fh [string repeat \x00 1024]
00257 
00258     close $fh
00259     return $tar
00260 }
00261 
00262 ret  ::tar::add (type tar , type files , type args) {
00263     set dereference 0
00264     parseOpts {dereference 0} $args
00265     
00266     set fh [::open $tar r+]
00267     fconfigure $fh -encoding binary -translation lf -eofchar {}
00268     seek $fh -1024 end
00269 
00270     foreach x [recurseDirs $files $dereference] {
00271         writefile $x $fh $dereference
00272     }
00273     puts -nonewline $fh [string repeat \x00 1024]
00274 
00275     close $fh
00276     return $tar
00277 }
00278 
00279 ret  ::tar::remove (type tar , type files) {
00280     set n 0
00281     while {[file exists $tar$n.tmp]} {incr n}
00282     set tfh [::open $tar$n.tmp w]
00283     set fh [::open $tar r]
00284 
00285     fconfigure $fh  -encoding binary -translation lf -eofchar {}
00286     fconfigure $tfh -encoding binary -translation lf -eofchar {}
00287 
00288     while {![eof $fh]} {
00289         array set header [readHeader [read $fh 512]]
00290         if {$header(name) == ""} {
00291             puts -nonewline $tfh [string repeat \x00 1024]
00292             break
00293         }
00294         set name $header(prefix)$header(name)
00295         set len [expr {$header(size) + [pad $header(size)]}]
00296         if {[lsearch $files $name] > -1} {
00297             seek $fh $len current
00298         } else {
00299             seek $fh -512 current
00300             fcopy $fh $tfh -size [expr {$len + 512}]
00301         }
00302     }
00303 
00304     close $fh
00305     close $tfh
00306 
00307     file rename -force $tar$n.tmp $tar
00308 }
00309 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1