#JMN 2007 #public domain #experimental #VERY incomplete package require pattern package require patternlib package require struct::set package provide pattern::ms [namespace eval ::pattern::ms { variable version set version 1.0.12 }] #-------------------------------------------------- namespace eval ::pattern::ms { ::>pattern .. Create >IEnumerable >IEnumerable .. PatternVariable i ;#current index >IEnumerable .. PatternProperty Current >IEnumerable .. PatternPropertyRead Current {} { var o_list i return [lindex $o_list $i] } >IEnumerable .. PatternMethod MoveNext {} { var i incr i } >IEnumerable .. PatternMethod Reset {} { var i set i 0 } } #-------------------------------------------------- namespace eval ::pattern::ms { ::>pattern .. Create >Enumerator >Enumerator .. PatternVariable o_enumerable >Enumerator .. Constructor {IEnumerable_object} { var o_enumerable set o_enumerable $IEnumerable_object } >Enumerator .. PatternMethod atEnd {} { var i o_list return [expr {$i >= ([llength $o_list] -1)} ] } >Enumerator .. PatternMethod moveNext {} { var i incr i } >Enumerator .. PatternMethod moveFirst {} { var i set i 0 } >Enumerator .. PatternMethod item {} { var i o_list return [lindex $o_list $i] } } #-------------------------------------------------- namespace eval ::pattern::ms { ::>pattern .. Create >textstream >textstream .. PatternVariable o_fd ;#file descriptor >textstream .. Constructor {args} { set opts [dict merge { -mode r } $args] if {([dict get $opts -mode] eq "r") && ![file exists [dict get $opts -path]]} { error "file [dict get $opts -path] not found" } set o_fd [open [dict get $opts -path] [dict get $opts -mode]] return } >textstream .. PatternMethod Write {data} { var o_fd puts -nonewline $o_fd $data } >textstream .. PatternMethod WriteLine {{line ""}} { var o_fd puts $o_fd $line } >textstream .. PatternMethod WriteBlankLines {howmany} { var o_fd #!todo - work out proper line-ending and write in single call. if {$howmany > 0} { for {set i 0} {$i < $howmany} {incr i} { puts $o_fd "" } } } >textstream .. PatternMethod Read {{numbytes ""}} { var o_fd if {[string length $numbytes]} { return [read $o_fd $numbytes] } else { return [read $o_fd] } } >textstream .. PatternMethod ReadLine {} { var o_fd return [gets $o_fd] } >textstream .. PatternMethod ReadAll {} { var o_fd return [read $o_fd] ;#don't use size argument - we can't be sure it hasn't changed since opening (?) } >textstream .. PatternMethod Skip {numchars} { var o_fd seek $o_fd $numchars current } >textstream .. PatternMethod SkipLine {} { var o_fd gets $o_fd return } >textstream .. PatternMethod Close {} { var o_fd close $o_fd } } #------------------------------------------------------------------------------ # https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/file-object namespace eval ::pattern::ms { ::>pattern .. Create >fso_file >fso_file .. PatternVariable o_path >fso_file .. Constructor {args} { var this o_path set this @this@ set opts [dict merge { } $args] if {![file exists [dict get $opts -path]]} { error "cannot find file '[dict get $opts -path]'" } if {![file isfile [dict get $opts -path]]} { error "path '[dict get $opts -path]' does not appear to be a file" } set o_path [dict get $opts -path] return } >fso_file .. PatternProperty Name >fso_file .. PatternPropertyRead Name {} { var o_path return [file tail $o_path] ;#??? } >fso_file .. PatternPropertyWrite Name {newname} { var o_path file rename $o_path [file dirname $o_path]/$newname return } >fso_file .. PatternProperty Path >fso_file .. PatternPropertyRead Path {} { var o_path return $o_path } } #------------------------------------------------------------------------------ namespace eval ::pattern::ms { ::>pattern .. Create >fso_folder >fso_folder .. PatternVariable o_path >fso_folder .. PatternVariable o_files ;#collection >fso_folder .. Constructor {args} { var this ns o_path o_files set this @this@ set ns [$this .. Namespace] set opts [dict merge { } $args] if {![file exists [dict get $opts -path]]} { error "cannot find folder '[dict get $opts -path]'" } if {![file isdirectory [dict get $opts -path]]} { error "path '[dict get $opts -path]' does not appear to be a folder" } set o_path [dict get $opts -path] set o_files [::patternlib::>collection .. Create ${ns}::>col_files] return } #!todo - what happens to the object? destroy it? >fso_folder .. PatternMethod Delete {{force 0}} { var this o_path if {$force} { file delete -force $o_path } else { file delete $o_path } #?? # $this .. Destroy return } >fso_folder .. PatternProperty DateCreated >fso_folder .. PatternPropertyRead DateCreated {} { var o_path file stat $o_path info return $info(ctime) } >fso_folder .. PatternProperty DateLastAccessed >fso_folder .. PatternPropertyRead DateLastAccessed {} { var o_path return [file atime $o_path] } >fso_folder .. PatternProperty DateLastModified >fso_folder .. PatternPropertyRead DateLastModified {} { var o_path return [file mtime $o_path] } >fso_folder .. PatternProperty Files >fso_folder .. PatternPropertyRead Files {} { var ns o_path objectcounter o_files set filenames [glob -dir $o_path -type f -tail *] lappend filenames {*}[glob -dir $o_path -types {f hidden} -tail *] set NEW [::pattern::ms::>fso_file .. Create .] set files [list] set superfluous [struct::set difference [$o_files . names] $filenames] foreach doomed $superfluous { set f [$o_files . item $doomed] $f .. Destroy $o_files . del $doomed } set missing [struct::set difference $filenames [$o_files . names]] foreach fname $missing { if {[catch { set fobj [$NEW ${ns}::>fl_[incr objectcounter] -path $o_path/$fname] } errM]} { #There can exist characterSpecial files such as 'nul' that aren't identified as file or directory by Tcl 'file isfile' or 'file isdirectory' # yet were picked up by glob #(these shouldn't really exist - but can be accidentally created) #we don't want an error in creating an >fso_file for this to stop us accessing any other files in the folder #but we should at least be loud about it by emitting the error to stderr puts stder " } $o_files . add [$NEW ${ns}::>fl_[incr objectcounter] -path $o_path/$fname] $fname } return [$o_files . items] } } #------------------------------------------------------------------------------ #vba and vb6 File System Object (used the same COM component - Microsoft Scripting Runtime library scrrun.dll) namespace eval ::pattern::ms { ::>pattern .. Create >fso >fso .. PatternVariable objectcounter ;# >fso .. Constructor {args} { var this ns objectcounter set this @this@ set ns [$this .. Namespace] set objectcounter 0 } >fso .. PatternMethod CreateTextFile {path {bool 1}} { var ns objectcounter set ts [::pattern::ms::>textstream .. Create ${ns}::>ts_[incr objectcounter] -path $path -mode w] return $ts } >fso .. PatternMethod OpenTextFile {path mode {bool 1}} { var ns objectcounter switch -- [string tolower $mode] { 1 - forreading { set md r } 2 - forwriting { set md w } 8 - forappending { set md a } default { error "unknown file mode - $mode" } } set ts [::pattern::ms::>textstream .. Create ${ns}::>ts_[incr objectcounter] -mode $md -path $path] return $ts } >fso .. PatternMethod GetFolder {path} { var ns objectcounter set fld [::pattern::ms::>fso_folder .. Create ${ns}::>fld_[incr objectcounter] -path $path] return $fld } >fso .. PatternProperty Drives >fso .. PatternPropertyRead Drives {} { var ns error "unimplemented" #todo >fso_drive object and collection } }