home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World 1998 October
/
PCWorld_1998-10_cd.bin
/
software
/
prehled
/
komix
/
DATA.Z
/
rwmethods.tcl
< prev
next >
Wrap
Text File
|
1997-08-21
|
15KB
|
588 lines
#---------------------------------------------------------------------------
#
# (c) Cadre Technologies Inc. 1995
#
# File: @(#)rwmethods.tcl /main/titanic/4
# Author: Harm Leijendeckers
# Description: Implementation of methods of class Report
#
#---------------------------------------------------------------------------
# SccsId = @(#)rwmethods.tcl /main/titanic/4 21 Aug 1997 Copyright 1995 Cadre Technologies Inc.
###########################################################################
#
# Public methods.
#
###########################################################################
constructor Report {class this} {
set this [GCObject::constructor $class $this]
$this init
return $this
}
# Initialize attributes. It's also called in the constructor.
method Report::init {this args} {
set errormsg "wrong # args: should be \"Report::init ?-linelen num?\
?-pagelen num?\""
# check arguments
set ret [scan $args "%s%d%s%d" what1 val1 what2 val2]
if { $ret != -1 && $ret != 2 && $ret != 4 } { error $errormsg }
set oldLinelen [$this linelen]
set oldPagelen [$this pagelen]
$this linelen 0
$this pagelen 0
for { set i 0 } { $i < [expr $ret/2] } { incr i } {
if { [Report::lchkfirst args -linelen] } {
$this linelen [lvarpop args]
}
if { [Report::lchkfirst args -pagelen] } {
$this pagelen [lvarpop args]
}
}
if ![lempty $args] {
$this linelen oldLinelen
$this pagelen oldPagelen
error $errormsg
}
# set linelen to parameter if used else use the m4 variable
# and if that is not set use the default value
if { [$this linelen] <= 0 } { $this linelen [m4_var get M4_a_printer_llen] }
if { [$this linelen] <= 0 } { $this linelen 130 }
# set pagelen to parameter if used else use the m4 variable
# and if that is not set use the default value
if { [$this pagelen] <= 0 } {
$this pagelen [m4_var get M4_a_printer_plen]
}
if { [$this pagelen] <= 0 } { $this pagelen 61 }
$this toPrint ""
$this lineno 0
$this pageno 0
$this date [clock format [clock seconds] -format "%d %h %Y %X"]
$this headerList ""
$this footerList ""
return
}
# Main print procedure.
method Report::print {this str args} {
if ![Report::lchkfirst args verbatim] {
# substitute all tabs in str for one space
regsub -all (\t) $str " " str
# substitute all newlines in str for one space
if {! [Report::lchkfirst args keepnl]} {
regsub -all (\r?\n) $str " " str
}
}
set strLen [string first "\n" $str]
if {$strLen == -1} {
set strLen [string length $str]
set doSplit 0
} else {
set doSplit 1
}
# number
set len [lindex $args 0]
expr { [Report::isInt $len] ? [lvarpop args]
: [set len $strLen] }
# 'left', 'center', 'right' or 'fill'
switch -exact [lindex $args 0] {
left { set align left; lvarpop args }
center { set align center; lvarpop args }
right { set align right; lvarpop args }
fill { set align fill; lvarpop args }
default { set align left }
}
# 'break'
set break [Report::lchkfirst args break]
# format str according to len, align and break and add str to toPrint
if { ((! $doSplit) && ($strLen <= $len)) || $len == 0 } {
# strlen(str) < len
lappend new_s [Report::align $str $len $align]
} else {
# strlen(str) > len
if { $break } {
# truncate str
regsub -all (\r?\n) $str " " str
lappend new_s [list [string range $str 0 [expr $len-1]]]
} else {
# break == off so divide str over more lines
foreach line [split $str "\n"] {
while { [string length $line] > $len } {
set sub [string range $line 0 [expr $len-1]]
set line [string range $line $len [string length $line]]
lappend new_s [list $sub]
}
# strlen(str) <= len so add aligned str
lappend new_s [Report::align $line $len $align]
}
}
}
# add new_s to global toPrint
set tmp [$this toPrint]
lappend tmp [join $new_s]
$this toPrint $tmp
# look if the next argument is 'line'
if { [Report::lchkfirst args line] } { $this line }
# look if the next argument is 'page'
if { [Report::lchkfirst args page] } { $this page }
if ![lempty $args] {
error [format "print command: unknown argument(s): %s" $args]
}
return
}
# Print all outstanding output and a newline.
method Report::line {this} {
set tp [$this toPrint]
$this toPrint ""
# look if we need to print a header first
$this doHeader
$this doFooter
if [lempty $tp] {
puts ""
$this lineno [expr [$this lineno]+1]
return
}
for { set i 0 } { $i < [Report::maxelem $tp] } { incr i } {
set s ""
foreach j $tp {
if { [Report::isSeparator $j] } {
set elem [lindex $j 1]
} else {
if [lempty [lindex $j $i]] {
set elem [join [Report::align " " \
[string length [lindex $j 0]] fill]]
} else { set elem [lindex $j $i] }
}
append s $elem
}
# delete trailing spaces
set s [string trimright $s]
$this doHeader
# if s is longer than linelen, we only have to print
# s again with length linelen
if { [string length $s] > [$this linelen] } {
$this print "$s" [$this linelen] line
} else {
puts $s
$this lineno [expr [$this lineno]+1]
$this doFooter
}
}
return
}
# Print all outstanding lines and fill the rest of the page with empty lines.
method Report::page {this} {
# print outstanding lines
if ![lempty [$this toPrint]] { $this line }
# and fill the rest of the page with empty lines
while { ![$this doFooter] } { $this line }
return
}
# Print a separator. This can be any kind of string.
method Report::separator {this s args} {
set tmp [$this toPrint]
lappend tmp [list {} $s]
$this toPrint $tmp
# look if the next argument is 'line'
if { [Report::lchkfirst args line] } { $this line }
# look if the next argument is 'page'
if { [Report::lchkfirst args page] } { $this page }
if ![lempty $args] {
error [format "separator command: unknown argument(s): %s" $args]
}
return
}
# Print a number of spaces, default is one.
method Report::space {this args} {
if [lempty $args] { set args 1 }
if { [llength $args] > 1 ||
( [llength $args] == 1 && ![Report::isInt $args] ) } {
error [format "space command: unknown argument(s): %s" $args]
return
}
$this print " " $args
}
method Report::file {this fileName {indent 0}} {
set fp [open $fileName]
set fstatus [gets $fp s]
while { $fstatus != -1 } {
$this print " " $indent fill
set s [Report::expandTabs $s $indent 8]
$this print $s [expr [$this linelen]-$indent] line
set fstatus [gets $fp s]
}
close $fp
return
}
# Add a header to the headerlist. A header is a set of Tcl commands.
method Report::header {this cmdstr} {
lappend hl [expr [info level]-1]
lappend hl $cmdstr
# append to the end of headerList
set tmp [$this headerList]
lappend tmp $hl
$this headerList $tmp
return
}
# Add a footer to the footerlist. A footer is a set of Tcl commands.
method Report::footer {this cmdstr {len -1}} {
if { $len < 1 } { set len [Report::countLines $cmdstr] }
lappend fl [expr [info level]-1]
lappend fl $len
lappend fl $cmdstr
# insert at the beginning of footerList
$this footerList [linsert [$this footerList] 0 $fl]
return
}
# Remove a header, footer or both on this level or on all levels.
method Report::remove {this what args} {
set argerror "wrong # args: should be \"remove header|footer|hf <all>\""
set all [lvarpop args]
if { ![lempty $args] || ($all != "" && $all != "all") } {
error $argerror
}
if { $what == "hf" } {
$this remove header $all
$this remove footer $all
return
}
if { $all != "all" } {
if { $what == "footer" } {
$this footerList [lrange [$this footerList] 1 end]
return
}
if { $what == "header" } {
$this headerList [lrange [$this headerList] \
0 [expr [llength [$this headerList]]-2]]
return
}
error $argerror
}
# do a smart deletion of all the headers or footers on this
# level
if { $what == "header" || $what == "footer" } {
# walk through list and delete headers on
# current level and headers which should have
# been deleted by the user after leaving a
# level.
if { $what == "header" } {
set origList [$this headerList]
} else {
set origList [Report::lreverse [$this footerList]]
}
set callLevel [expr [info level]-2]
set prevLevel -1
set newL {}
foreach i $origList {
if { [lindex $i 0] >= $callLevel || [lindex $i 0] < $prevLevel } {
break
}
lappend newL $i
set prevLevel [lindex $i 0]
}
if { $what == "header" } {
$this headerList $newL
} else {
$this footerList [Report::lreverse $newL]
}
return
}
error $argerror
}
# Return 1 if there is output to be printed, otherwise 0
method Report::queued {this} {
if [lempty [$this toPrint]] {
return 0
}
return 1
}
###########################################################################
#
# Private methods. Don't call them outside a method.
#
###########################################################################
# Execute headerList if lineno == 0.
method Report::doHeader {this} {
if { ([$this lineno] != 0 && [$this lineno] <= [$this pagelen])
|| [$this pagelen] == -1 } {
return
}
$this pageno [expr [$this pageno]+1]
# set the pagelen to unlimited so the header is always printed
# as if it fits on one page
set oldpagelen [$this pagelen]
$this pagelen -1
$this lineno 1
foreach i [$this headerList] {
if { [info level] < [lindex $i 0] } {
# skip headers which were defined on a lower level
# than the current level
continue
}
# execute the header on the level it was defined
uplevel #[lindex $i 0] [lindex $i 1]
}
$this pagelen $oldpagelen
# if the header needed to be printed on more than one page
# the current lineno is bigger then pagelen, so adjust it
$this lineno [expr [$this lineno]%[$this pagelen]]
if { ![$this lineno] } { $this lineno [$this pagelen] }
return
}
# Execute footerList if lineno+footerlength == pagelen.
method Report::doFooter {this} {
if { [$this pagelen] == -1 } { return 0 }
# calculate current footerlength
set len 0
foreach i [$this footerList] { incr len [lindex $i 1] }
# if we have got to print the footer
if { [expr $len+[$this lineno]] > [$this pagelen] } {
set oldpagelen [$this pagelen]
$this pagelen -1
foreach i [$this footerList] {
# execute the footer on the level it was defined
uplevel #[lindex $i 0] [lindex $i 2]
}
# if the user forgot a "print line" we do it
if ![lempty [$this toPrint]] { $this line }
$this pagelen $oldpagelen
# if lineno < pagelen the footer was shorter than expected
# so print some empty lines to cover it
for { } \
{ [$this lineno] <= [$this pagelen] } \
{ $this lineno [expr [$this pageno]+1] } \
{ puts "" }
$this lineno 0
return 1
}
return 0
}
###########################################################################
#
# Public methods, implemented as procedures.
#
###########################################################################
# Correct version of lrange, used as long as the bug in lrange is still there.
proc Report::lrange {list start end} {
set result ""
if { $end == "end" } { set end [expr [llength $list]-1] }
for { set i $start } { $i <= $end } { incr i } {
lappend result [lindex $list $i]
}
return $result
}
# Align string 's' according to 'len' and 'align' (== left|right|center|fill).
proc Report::align {s len align} {
set retval ""
if { $len <= [string length $s] } {
set retval [string range $s 0 [expr $len-1] ]
} else {
switch -exact $align {
right { set retval [format [format "%%%ds" $len] $s] }
center { set tmp [format [format "%%%ds" [expr ($len+\
[string length $s])/2] ] $s]
set retval [format [format "%%-%ds" $len] $tmp] }
fill { set tmp $s
while { [string length $tmp] < $len } {
append tmp $s
}
set retval [string range $tmp 0 [expr $len-1] ] }
default { set retval [format [format "%%-%ds" $len] $s] }
}
}
return [list $retval]
}
# Return 1 if parameter is an integer, else 0.
proc Report::isInt {int} {
return [expr { [scan $int "%d%s" int rest] == 1 }]
}
# Return 1 if the first element of 'list' == 'first' and delete that first
# element, else return 0.
proc Report::lchkfirst {list first} {
upvar 1 $list l
if { [lindex $l 0] == $first } {
lvarpop l
return 1
}
return 0
}
# Return 1 if parameter is a separater, else 0.
proc Report::isSeparator {args} {
set args [join $args]
return [expr {[llength $args] == 2 &&
[string length [lindex $args 0]] == 0 &&
[string length [lindex $args 1]] != 0}]
}
# Return the number of elements of the biggest subtree in 'args'.
proc Report::maxelem {args} {
set args [join $args]
set retval 0
foreach i $args {
if { [Report::isSeparator $i] } { continue }
if { [llength $i] > $retval } { set retval [llength $i] }
}
return $retval
}
# Count the number of times that word "line" occurs in string 's'.
proc Report::countLines {s} {
set num 0
while { [regsub "line" $s "" s] } { incr num }
return $num
}
# Expand tabs to spaces in string 's', tablength is variable, default = 8.
proc Report::expandTabs {s indent {tablen 8}} {
if { $tablen < 0 } { set tablen 0 }
# add indent spaces
set s [format [format "%%%ds%%s" $indent] "" $s]
set pos [string first \t $s]
while { $pos != -1 } {
if { $tablen != 0 } {
set numspaces [expr $tablen-$pos%$tablen]
} else {
set numspaces 0
}
set spaces [Report::align " " $numspaces fill]
# join is needed otherwise spaces are enclosed in braces
regsub \t $s [join $spaces] s
set pos [string first \t $s]
}
# remove the indented spaces
set s [string range $s $indent end]
return $s
}
# Return the reverse of list 'l'.
proc Report::lreverse {l} {
if [lempty $l] { return {} }
set elem [lvarpop l]
return [concat [Report::lreverse $l] [list $elem]]
}