# # hfsutils - tools for reading and writing Macintosh HFS volumes # Copyright (C) 1996-1998 Robert Leslie # # 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., 675 Mass Ave, Cambridge, MA 02139, USA. # # $Id: hfs.tcl,v 1.9 1998/08/31 04:27:18 rob Exp $ # proc ctime {secs} { return [clock format $secs -format "%c"] } proc getvol {var} { global curvol upvar $var vol if {! [info exists curvol]} { error "no volume is current" } set vol $curvol } proc hinfo {} { getvol vol if {[$vol islocked]} { set locked " (locked)" } else { set locked "" } puts stdout "Volume name is \"[$vol vname]\"$locked" puts stdout "Volume was created on [ctime [$vol crdate]]" puts stdout "Volume was last modified on [ctime [$vol mddate]]" puts stdout "Volume has [lindex [$vol size] 1] bytes free" } proc hmount {path {partno ""}} { global mounts curpath curvol set nparts [hfs nparts $path] if {$nparts >= 0} { puts stdout [concat "$path contains $nparts HFS" \ [ternary {$nparts == 1} "partition" "partitions"]] } if {[string length $partno] == 0} { if {$nparts > 0} { puts stderr "partition unspecified; selecting number 1" set partno 1 } elseif {$nparts == -1} { set partno 0 } else { set partno 1 } } set vol [hfs mount $path $partno] if {[info exists mounts($path)]} { humount $path } set curpath $path set curvol $vol set mounts($path) $vol hinfo } proc humount {{path {}}} { global mounts curpath curvol if {[string length $path] == 0} { if {! [info exists curpath]} { error "no volume is current" } set path $curpath } elseif {! [info exists mounts($path)]} { error "$path not mounted" } set vol $mounts($path) unset mounts($path) if {[string compare $vol $curvol] == 0} { unset curpath unset curvol } $vol umount } proc hvol {name} { global mounts curpath curvol if {[info exists mounts($name)]} { set curpath $name set curvol $mounts($name) return } error "unknown volume" } proc hpwd {} { getvol vol return "[join [$vol path] ":"]:" } proc hcd {{path ""}} { getvol vol set globbed [$vol glob [list $path]] if {[llength $globbed] != 1} { error "$path: ambiguous path" } set path [lindex $globbed 0] if {[string length $path] == 0} { set path "[$vol vname]:" } $vol cd $path } proc timestr {secs} { set ctime [ctime $secs] return "[string range $ctime 4 15][string range $ctime 19 23]" } proc ternary {test true false} { if {[uplevel expr $test]} { return $true } else { return $false } } proc hdir {{path ":"}} { getvol vol set globbed [$vol glob [list $path]] if {[llength $globbed] != 1} { error "$path: ambiguous path" } set path [lindex $globbed 0] foreach ent [$vol dir $path] { array set item $ent if {$item(kind) == "directory"} { puts stdout [format "d%s %9lu item%s %s %s" \ [ternary {[lsearch $item(flags) "invis"] >= 0} "i" " "] \ $item(size) \ [ternary {$item(size) == 1} " " "s"] \ [timestr $item(mddate)] \ $item(name)] } else { puts stdout [format "%s%s %4s/%4s %9lu %9lu %s %s" \ [ternary {[lsearch $item(flags) "locked"] >= 0} "F" "f"] \ [ternary {[lsearch $item(flags) "invis"] >= 0} "i" " "] \ $item(type) \ $item(creator) \ $item(rsize) \ $item(dsize) \ [timestr $item(mddate)] \ $item(name)] } } } proc hstat {{path ":"}} { getvol vol set globbed [$vol glob [list $path]] if {[llength $globbed] != 1} { error "$path: ambiguous path" } set path [lindex $globbed 0] array set item [$vol stat $path] foreach elt [lsort [array names item]] { if {[regexp {date$} $elt]} { set value [ctime $item($elt)] } else { set value $item($elt) } puts stdout [format "%-10s %s" "$elt:" $value] } } proc hmkdir {args} { getvol vol foreach arg [$vol glob $args] { $vol mkdir $arg } } proc hrmdir {args} { getvol vol foreach arg [$vol glob $args] { $vol rmdir $arg } } proc hcreate {path {type "TEXT"} {creator "UNIX"}} { getvol vol set file [$vol create $path $type $creator] $file close } proc htouch {args} { getvol vol foreach arg [$vol glob $args] { if [catch {$vol touch $arg}] { hcreate $arg } } } proc hdel {args} { getvol vol foreach arg [$vol glob $args] { $vol delete $arg } } proc hrename {src dst} { getvol vol set globbed [$vol glob [list $src]] if {[llength $globbed] != 1} { error "$src: ambiguous path" } set src [lindex $globbed 0] $vol rename $src $dst } proc hbless {path} { getvol vol set globbed [$vol glob [list $path]] if {[llength $globbed] != 1} { error "$path: ambiguous path" } set path [lindex $globbed 0] $vol bless $path } proc hcat {path} { getvol vol set globbed [$vol glob [list $path]] if {[llength $globbed] != 1} { error "$path: ambiguous path" } set path [lindex $globbed 0] set file [$vol open $path] while {1} { set buf [$file read 512] if {[string length $buf] == 0} { $file close break } regsub -all "\r" $buf "\n" buf puts -nonewline stdout $buf } } proc hcopyout {path {dest "."} {mode ""}} { getvol vol set globbed [$vol glob [list $path]] if {[llength $globbed] != 1} { error "$path: ambiguous path" } set path [lindex $globbed 0] if {[string length $mode] == 0} { array set item [$vol stat $path] if {$item(kind) == "directory"} { error "can't copy whole directories" } elseif {[regexp {^TEXT|ttro$} $item(type)]} { set mode text } else { set mode macb } } $vol copyout $mode $path $dest } proc hcopyin {path {dest ":"} {mode ""}} { getvol vol set globbed [$vol glob [list $path]] if {[llength $globbed] != 1} { error "$path: ambiguous path" } set path [lindex $globbed 0] if {[string length $mode] == 0} { if {[regexp {\.bin$} $path]} { set mode macb } elseif {[regexp {\.hqx$} $path]} { set mode binh } elseif {[regexp {\.(txt|c|h)$} $path]} { set mode text } elseif {[regexp {\.(sit|sea|cpt|tar|gz|Z|gif|jpg)$} $path]} { set mode raw } elseif {[catch {exec file -L $path} type] == 0 && \ [regexp {text} $type]} { set mode text } else { set mode raw } } $vol copyin $mode $path $dest } proc hformat {path {partno 0} {vname "Untitled"} {badblocks {}}} { global mounts if {[info exists mounts($path)]} { humount $path } hfs format $path $partno $vname $badblocks hmount $path $partno } ############################################################################### proc help {{what ""}} { if {[string length $what]} { if {[catch {info args h$what} msg]} { puts stdout "Sorry, no help for \"$what\"" } else { puts stdout "$what $msg" } } else { puts stdout { info Show current volume information mount Open a new HFS volume umount Close an HFS volume vol Select an open volume pwd Show the current working directory path cd Change the current working directory dir Show a directory listing stat Show details about a given path mkdir Create a new directory rmdir Delete an empty directory create Create an empty file touch Update modification date or create a file del Delete a file rename Rename a file, directory, or volume cat Display file's data fork contents copyout Copy a complete file out into the native filesystem copyin Copy a MacBinary/BinHex/text/raw file into HFS format Create an empty HFS volume } } } proc ? {args} { eval help $args } ############################################################################### proc version {} { puts stdout "[hfs version] - [hfs copyright]" } proc license {} { puts -nonewline stdout "\n[hfs license]" } proc author {} { puts stdout [hfs author] } if {[string compare [lindex $argv 0] "--license"] == 0} { license exit } version if {[string compare [lindex $argv 0] "--version"] == 0} { puts stdout "`$argv0 --license' for licensing information." exit } puts stdout "This is free software but comes with ABSOLUTELY NO WARRANTY." if {$hfs_interactive} { puts stdout "Type `license' for details." } puts stdout "" ############################################################################### proc echo {args} { puts stdout [join $args " "] } proc quit {} { exit } ############################################################################### # Apparently some shells don't grok "$@" correctly if {$argc == 1 && [string length [lindex $argv 0]] == 0} { incr argc -1 set argv [lreplace $argv 0 0] } if {$argc > 0} { eval hmount $argv } while {1} { if {$hfs_interactive} { puts -nonewline stdout "hfs> " flush stdout } if {[gets stdin line] == -1} { exit } while {! [info complete $line]} { if {[gets stdin more] == -1} { break } else { set line "$line$more" } } if {[string length [info procs "h[lindex $line 0]"]] > 0} { set result [catch {eval h$line} msg] } else { set result [catch {eval $line} msg] } if {[string length $msg] > 0} { if {$result == 1} { puts stdout "Error: $msg" if {! $hfs_interactive} { exit 1 } } else { puts stdout $msg } } }