::mulsterTop

This little thing provides multi-line replacements in text files.

It's called this way:


tclsh mulster.tcl [options] fileini

where:

fileini is a name of file containing options for replacements, e.g.


INFILE=input file name.txt OUTFILE=output file name.txt IN=BEGIN(1,1) line #1 to find line #2 to find IN=END OUT=BEGIN line #1 OUT=END

options are:


-infile input-file-name -outfile output-file-name -mode exact | exact0 | glob | regexp -backup 0 | <dir> -keep 0 | 1 | false | true -single 0 | 1 | false | true -files 0 | 1 | false | true -charset charsetName (e.g. cp1251) -lineend lineEnding (e.g. \r\n) --

See README for details.

The mulster::Mulster class provides two methods to be called from Tcl module.

1. The main method performs the multi-line replacements. Declared as:


method main {fileini {mode 1} {backup BAK} {keep 0} {single 0} {charset ""} {lineend ""} {files 0}}

where:

fileini is a file name of options

mode = 0 (exact0, EXACT0) to match exact, without leading/tailing spaces

mode = 1 (exact, EXACT) to match exact, with all their leading/tailing spaces

mode = 2 (glob, GLOB) to match glob pattern

mode = 3 (regexp, re, REGEXP, RE) to match regexp pattern

mode = regexp-- to match regexp pattern and call regsub


Note: IN= line is to be found, OUT= line is to substitute, according to the regexp syntax accepted in Tcl. Note: With regexp and regsub, only one line is of use to IN=/OUT= block.

mode = regexp-all to match regexp pattern and call regsub -all

mode = regexp-nocase to match regexp pattern and call regsub -nocase

mode = regexp-expanded to match regexp pattern and call regsub -expanded

regexp mode can be combined, e.g. "regexp-all-nocase"

backup 0 means no input files' backuping

backup dir means backuping to dir directory

keep if 1, keeps input files' attributes/times in output files

single if 1, sets a standard string-for-string replacements

charset is a charset of input files, e.g. cp1251

lineend sets characters to end lines, e.g. \r\n (by default \n)

files if 1, OUT= lines to be file names to take the lines from

2. The mulsterList method performs in-memory replacements in a list. Declared as:


method mulsterList {lcont lin lout {r1 0} {r2 0} {mode 1} {single 0} {files 0}}

where:

lcont is a list to be processed

lin is a list of input lines (replaced)

lout is a list of output lines (replacing)

r1 and r2 set a range of replacements

mode is a search mode (as defined in getSearchMode method)

single if true, means a standard 'string for string' replacements

files if true, output lines are taken from files

The mulsterList method returns a list processed.


Commandsmulster, Top




synopsis [::mulster]mulster, Top

Puts a synopsis of mulster utility.

synopsis

proc ::mulster::synopsis {} { # Puts a synopsis of mulster utility. variable _ruff_preamble puts $_ruff_preamble }


Classesmulster, Top



Mulster [::mulster]mulster, Top

Method summary
getBooleanChecks and gets a boolean value.
getSearchModeGets a search mode as numeric from string.
initInitializes processing files.
mainProcesses files according to the options of 'fileini' file.
mulsterListPerforms replacements in a list.


getBoolean [::mulster::Mulster]Mulster, Top

Checks and gets a boolean value.

OBJECT getBoolean val ?defval?
Parameters
vala value to be checked
defvala default value; optional, default true
Return value

Returns a normalized value.


method getBoolean {val {defval true}} { # Checks and gets a boolean value. # # val - a value to be checked # defval - a default value # # Returns a normalized value. if {$val ni {0 1 false true}} {set val $defval} return $val }



getSearchMode [::mulster::Mulster]Mulster, Top

Gets a search mode as numeric from string.

OBJECT getSearchMode strmode
Parameters
strmodesearch mode in a string form ("exact", "RE" etc.)
Description

Numeric mode:

0to match exact, without their leading/tailing spaces (str trimmed)
1to match exact, with all their leading/tailing spaces
2to match glob
3to match regexp for IN= lines and substitute by OUT= lines
4to match regexp -all and call 'regsub -all'
8to match regexp -nocase and call 'regsub -nocase'
16to match regexp -expanded and call 'regsub -expanded'

The mode can combine 4,8,16, e.g. 12 means "regexp -all -nocase".

Return value

Returns the numeric mode.


method getSearchMode {strmode} { # Gets a search mode as numeric from string. # # strmode - search mode in a string form ("exact", "RE" etc.) # # Returns the numeric mode. # # Numeric mode: # 0 - to match exact, without their leading/tailing spaces (str trimmed) # 1 - to match exact, with all their leading/tailing spaces # 2 - to match glob # 3 - to match regexp for IN= lines and substitute by OUT= lines # 4 - to match regexp -all and call 'regsub -all' # 8 - to match regexp -nocase and call 'regsub -nocase' # 16 - to match regexp -expanded and call 'regsub -expanded' # # The mode can combine 4,8,16, e.g. 12 means "regexp -all -nocase". if {[string is integer $strmode]} {return $strmode} set mode 0 switch -regexp -nocase -matchvar am $strmode { ^exact$ {set mode 1} ^glob$ {set mode 2} ^regexp$ - ^re$ {set mode 3} ^regexp\\s*(-.*)$ - ^re\\s*(-.*) { set am [split [string toupper [lindex $am 1]] " -"] if {"A" in $am || "ALL" in $am} { incr mode 4 } if {"N" in $am || "NOCASE" in $am} { incr mode 8 } if {"E" in $am || "EXPANDED" in $am} { incr mode 16 } incr mode 32 ;# to call 'regsub' anyway } } return $mode }



init [::mulster::Mulster]Mulster, Top

Initializes processing files.

OBJECT init infile outfile ?args?
Parameters
infileinput file name
outfileoutput file name
argsoptional arguments

method init {infile outfile args} { # Initializes processing files. # # infile - input file name # outfile - output file name # args - optional arguments array set _MMM {} my InitIO if {$infile ne ""} { set _MMM(fin) 1 set _MMM(infile) $infile } if {$outfile ne ""} { set _MMM(fout) 1 set _MMM(outfile) $outfile } set _MMM(IN_FILE) INFILE= ;# pattern for in file name set _MMM(OUT_FILE) OUTFILE= ;# pattern for out file name set _MMM(IN_BEGIN) IN=BEGIN ;# pattern for in lines start set _MMM(IN_END) IN=END ;# pattern for in lines finish set _MMM(OUT_BEGIN) OUT=BEGIN ;# pattern for out lines start set _MMM(OUT_END) OUT=END ;# pattern for out lines finish set _MMM(MODE) MODE= ;# pattern for exact/glob/regexp mode set _MMM(BACKUP) BACKUP= ;# pattern for backup dir name set _MMM(KEEP) KEEP= ;# pattern for keep mode set _MMM(SINGLE) SINGLE= ;# pattern for single mode set _MMM(CHARSET) CHARSET= ;# pattern for charset mode set _MMM(LINEEND) LINEEND= ;# pattern for lineend mode set _MMM(FILES) FILES= ;# pattern for files set _MMM(GRP1) {(.+)} ;# RE group for a file name set _MMM(GRP2) {\((.+),(.+)\)} ;# RE groups for range set _MMM(DEBUG) false ;# DEBUG mode if { [self next] != {} } { return [next {*}$args] } }



main [::mulster::Mulster]Mulster, Top

Processes files according to the options of 'fileini' file.

OBJECT main fileini ?mode? ?backup? ?keep? ?single? ?charset? ?lineend? ?files?
Parameters
fileinioptions file name
modea search mode (as defined in getSearchMode method); optional, default 1
backupif 0, means no backuping, otherwise - backup directory; optional, default BAK
keepif 1, keeps input files' attributes/times in output files; optional, default 0
singleif 1, standard 'one string for one string' replacements; optional, default 0
charseta charset of input files (e.g. cp1251); optional, default ""
lineenda line ending of input files (e.g. \r\n); optional, default ""
filesif 1, output lines are taken from files; optional, default 0
See also

getSearchMode


method main {fileini {mode 1} {backup BAK} {keep 0} {single 0} {charset {}} {lineend {}} {files 0}} { # Processes files according to the options of 'fileini' file. # # fileini - options file name # mode - a search mode (as defined in getSearchMode method) # backup - if 0, means no backuping, otherwise - backup directory # keep - if 1, keeps input files' attributes/times in output files # single - if 1, standard 'one string for one string' replacements # charset - a charset of input files (e.g. cp1251) # lineend - a line ending of input files (e.g. \r\n) # files - if 1, output lines are taken from files # # See also: # getSearchMode set mode [my getSearchMode $mode] set keep [my getBoolean $keep] set single [my getBoolean $single] set fileon [my getBoolean $files] if {!($_MMM(fin) + $_MMM(fout))} { my InitIO } set state NONE set _MMM(st) [set comments ""] set _MMM(nl) 0 set _MMM(fileini) $fileini set chini [my Topen $fileini] foreach _MMM(st) [split [read $chini] \n] { incr _MMM(nl) switch $state { NONE { # check for finish if {[regexp -nocase "^\\s*MODE=EXIT" $_MMM(st)]} { break # check for debug mode } elseif {[regexp -nocase "^\\s*MODE=DEBUG" $_MMM(st)]} { set _MMM(DEBUG) true # check for comments } elseif {[regexp "^\\s*#" $_MMM(st)]} { set comments "\n$_MMM(st)" } elseif {[my OptionIs $_MMM(MODE) $_MMM(GRP1)]} { # check for mode set mode [my getSearchMode $_MMM(match1)] # check for backup dir } elseif {[my OptionIs $_MMM(BACKUP) $_MMM(GRP1)]} { my FlushOut $mode $keep $charset $lineend $backup set backup $_MMM(match1) # check for keep state } elseif {[my OptionIs $_MMM(KEEP) $_MMM(GRP1)]} { my FlushOut $mode $keep $charset $lineend $backup set keep [my getBoolean $_MMM(match1)] # check for charset } elseif {[my OptionIs $_MMM(CHARSET) $_MMM(GRP1)]} { my FlushOut $mode $keep $charset $lineend $backup set charset $_MMM(match1) # check for lineend } elseif {[my OptionIs $_MMM(LINEEND) $_MMM(GRP1)]} { my FlushOut $mode $keep $charset $lineend $backup set lineend $_MMM(match1) # check for single state } elseif {[my OptionIs $_MMM(SINGLE) $_MMM(GRP1)]} { set single [my getBoolean $_MMM(match1)] # check for fileon state } elseif {[my OptionIs $_MMM(FILES) $_MMM(GRP1)]} { set fileon [my getBoolean $_MMM(match1)] # check for input file } elseif {[my OptionIs $_MMM(IN_FILE) $_MMM(GRP1)]} { my FlushOut $mode $keep $charset $lineend $backup set _MMM(fin) 1 set _MMM(infile) $_MMM(match1) # check for output file } elseif {[my OptionIs $_MMM(OUT_FILE) $_MMM(GRP1)]} { my CheckFiles 1 set _MMM(fout) 1 set _MMM(outfile) $_MMM(match1) # check for input lines beginning } elseif {[my OptionIs $_MMM(IN_BEGIN)] || [my OptionIs $_MMM(IN_BEGIN) $_MMM(GRP2)]} { my CheckFiles 1 my CheckFiles 2 lappend _MMM(inlist) [list $_MMM(match1) $_MMM(match2) $single $fileon $charset $lineend $mode $comments] set comments "" set state IN # check for output lines beginning } elseif {[my OptionIs $_MMM(OUT_BEGIN)]} { my CheckFiles 1 my CheckFiles 2 lappend _MMM(outlist) [list] set state OUT } } IN { # check for input lines ending if {[my OptionIs $_MMM(IN_END)]} { set state NONE } else { # collect the input lines set curl [lindex $_MMM(inlist) end] lappend curl $_MMM(st) set _MMM(inlist) [lreplace $_MMM(inlist) end end $curl] } } OUT { # check for output lines ending if {[my OptionIs $_MMM(OUT_END)]} { set state NONE } else { # collect the output lines set curl [lindex $_MMM(outlist) end] lappend curl $_MMM(st) set _MMM(outlist) [lreplace $_MMM(outlist) end end $curl] } } } } # flush out the collected lines if any my FlushOut $mode $keep $charset $lineend $backup close $chini }



mulsterList [::mulster::Mulster]Mulster, Top

Performs replacements in a list.

OBJECT mulsterList lcont lin lout ?r1? ?r2? ?mode? ?single? ?files?
Parameters
lconta list to be processed
lininput lines' list
loutoutput lines' list
r1first index of replacements to do; optional, default 0
r2last index of replacements to do; optional, default 0
modea search mode (as defined in getSearchMode method); optional, default 1
singleif true, means a standard 'string for string' replacements; optional, default 0
filesif true, output lines are taken from files; optional, default 0
Return value

Returns a list with replacements made.


method mulsterList {lcont lin lout {r1 0} {r2 0} {mode 1} {single 0} {files 0}} { # Performs replacements in a list. # # lcont - a list to be processed # lin - input lines' list # lout - output lines' list # r1 - first index of replacements to do # r2 - last index of replacements to do # mode - a search mode (as defined in getSearchMode method) # single - if true, means a standard 'string for string' replacements # files - if true, output lines are taken from files # # Returns a list with replacements made. set _MMM(repls) 0 set leni [llength $lin] if {!$leni} { return $lcont ;# nothing to replace } set lres [list] ;# resulting list set sti0 [lindex $lin 0] if {$mode==0} {set sti0 [string trim $sti0]} ;# for a quick 1st check if {$files} {set lout [my FilesContents $lout]} set leno [llength $lout] set lenc [llength $lcont] set ic [set ifnd 0] while {$ic < $lenc} { set stc [lindex $lcont $ic] if { [my CompareStr $mode $stc $sti0] } { ;# 1st line found for {set ii [set found 1]} {$ii<$leni && $found} {incr ii} { if {[set ic2 [expr {$ic + $ii}]] >= $lenc} { set found 0 ;# out of lcont's range } else { set sti [lindex $lin $ii] set sto [lindex $lcont $ic2] if {$mode==0} { ;# exact0 is 'trimmed exact' mode set found [my CompareStr $mode $sto [string trim $sti]] } else { set found [my CompareStr $mode $sto $sti] } } } if {$found} { incr ifnd ;# check a found ifnd-th bunch for the range (r1,r2) if {$ifnd>=$r1 && ($ifnd<=$r2 || !$r2)} { set rinc 1 for {set io 0} {$io<$leno} {incr io} { set stout [lindex $lout $io] if {$mode>3} { set rinc [regsub {*}[my ReOptions $mode] [lindex $lin $io] $stc $stout stout] } lappend lres $stout } incr ic $leni ;# go to a line just following the 'lin' bunch incr _MMM(repls) $rinc } else { ;# not in the range r1-r2, so skip this IN=/OUT= for {set ii 0} {$ii<$leni} {incr ii} { lappend lres [lindex $lcont $ic] incr ic } } continue } } if {$single} { ;# do standard string replacement foreach si $lin so $lout { set stc2 [string map [list $si $so] $stc] if {$stc2 ne $stc} { incr ifnd ;# check a found ifnd-th bunch for the range (r1,r2) if {$ifnd>=$r1 && ($ifnd<=$r2 || !$r2)} { set stc $stc2 incr _MMM(repls) } } } } lappend lres $stc ;# this line is not the 1st of 'lin' bunch incr ic } return $lres }

Document generated by Ruff!