1# rcs.tcl -- 2# 3# Utilities for RCS related operations. 4# 5# Copyright (c) 2005 by Colin McCormack <coldstore@users.sourceforge.net> 6# Copyright (c) 2005 by Andreas Kupries <andreas_kupries@users.sourceforge.net> 7# 8# See the file "license.terms" for information on usage and redistribution 9# of this file, and for a DISCLAIMER OF ALL WARRANTIES. 10# 11# RCS: @(#) $Id: rcs.tcl,v 1.4 2005/09/28 04:51:23 andreas_kupries Exp $ 12 13# ### ### ### ######### ######### ######### 14## Requisites. 15 16package require Tcl 8.4 17 18# ### ### ### ######### ######### ######### 19## API Implementation 20 21namespace eval rcs {} 22 23# ::rcs::text2dict -- 24# 25# Convert a text into a dictionary. The dictionary is keyed by line 26# numbers, and the value is the text of the corresponding line. The 27# first line has index/number 1. 28# 29# Arguments 30# - text The text to convert. 31# 32# Results 33# A dictionary containing the text in split form. 34# 35# Side effects 36# None 37 38proc ::rcs::text2dict {text} { 39 array set lines {} 40 set lnum 0 41 foreach line [split $text \n] { 42 set lines([incr lnum]) $line 43 } 44 return [array get lines] 45} 46 47# ::rcs::file2dict -- 48# 49# Convert a text stored in a file into a dictionary. The dictionary is 50# keyed by line numbers, and the value is the text of the 51# corresponding line. The first line has index/number 1. 52# 53# Arguments 54# - file The path of the file containing the text to convert. 55# 56# Results 57# A dictionary containing the text in split form. 58# 59# Side effects 60# None 61 62proc ::rcs::file2dict {filename} { 63 set chan [open $filename r] 64 set text [read $chan] 65 close $chan 66 67 return [text2dict $text] 68} 69 70# ::rcs::dict2text -- 71# 72# Converts a dictionary as created by the 2dict commands back into a 73# text. The dictionary is keyed by line numbers, and the value is the 74# text of the corresponding line. The first line has index/number 1. 75# The dictionary may have gaps in the line numbers. 76# 77# Arguments 78# - dict The dictionary to convert. 79# 80# Results 81# The text stored in the dictionary. 82# 83# Side effects 84# None 85 86proc ::rcs::dict2text {dict} { 87 array set lines $dict 88 set result {} 89 foreach lnum [lsort -integer [array names lines]] { 90 lappend result $lines($lnum) 91 } 92 return [join $result \n] 93} 94 95# ::rcs::dict2file -- 96# 97# Converts a dictionary as created by the 2dict commands back into a 98# text and stores it into the specified file. The dictionary is keyed 99# by line numbers, and the value is the text of the corresponding 100# line. The first line has index/number 1. The dictionary may have 101# gaps in the line numbers. 102# 103# Arguments 104# - filename The path to the file to store the reconstructed text into. 105# - dict The dictionary to convert. 106# 107# Results 108# None. 109# 110# Side effects 111# None 112 113proc ::rcs::dict2file {filename dict} { 114 set chan [open $filename w] 115 puts -nonewline $chan [dict2text $dict] 116 close $chan 117} 118 119# ::rcs::decodeRcsPatch -- 120# 121# Converts a text containing a RCS patch (diff -n format) into a list 122# of patch commands. Each element of the list is a list containing the 123# patch command and its arguments, in this order. 124# 125# The valid patch commands are 'a' and 'd'. 'a' has two arguments, the 126# index of the line where to add the text, and the text itself. The 127# 'd' command has two arguments as well, the index of the first line 128# to delete, and the number of lines to delete. 129# 130# Arguments 131# - patch The text in diff -n format, the patch to parse. 132# 133# Results 134# A list containing the patch as sequence of commands. 135# 136# Side effects 137# None 138 139proc ::rcs::decodeRcsPatch {patch} { 140 set patch [split $patch \n] 141 set plen [llength $patch] 142 set at 0 143 set res {} 144 145 while {$at < $plen} { 146 # I use an index into the list to avoid shifting the list 147 # elements down with each line processed. That is a lot of 148 # memcpy's. 149 150 set cmd [string trim [lindex $patch $at]] 151 incr at 152 153 switch -glob -- $cmd { 154 "" {} 155 a* { 156 foreach {start len} [split [string range $cmd 1 end]] break 157 158 set to [expr {$at + $len - 1}] 159 lappend res [list \ 160 a \ 161 $start \ 162 [join [lrange $patch $at $to] \n]] 163 incr to 164 set at $to 165 } 166 d* { 167 foreach {start len} [split [string range $cmd 1 end]] break 168 lappend res [list d $start $len] 169 } 170 default { 171 return -code error "Unknown patch command: '$cmd'" 172 } 173 } 174 } 175 176 return $res 177} 178 179# ::rcs::encodeRcsPatch -- 180# 181# Converts a list of patch commands into a text containing the same 182# command as a RCS patch (i.e. in diff -n format). See decodePatch for 183# a description of the input format. 184# 185# Arguments 186# - patch The patch as list of patch commands. 187# 188# Results 189# A text containing the patch in diff -n format. 190# 191# Side effects 192# None 193 194proc ::rcs::encodeRcsPatch {patch} { 195 set res {} 196 197 foreach cmd $patch { 198 foreach {op a b} $cmd break 199 200 switch -exact -- $op { 201 a { 202 # a = index of line where to add 203 # b = text to add 204 205 set lines [llength [split $b \n]] 206 207 lappend res "a$a $lines" 208 lappend res $b 209 } 210 d { 211 # a = index of first line to delete. 212 # b = #lines to delete. 213 214 lappend res "d$a $b" 215 } 216 default { 217 return -code error "Unknown patch command: '$op'" 218 } 219 } 220 } 221 222 return [join $res \n]\n 223} 224 225# ::rcs::applyRcsPatch -- 226# 227# Apply a patch in the format returned by decodeRcsPatch to a text in 228# the format returned by the xx2dict commands. The result is 229# dictionary containing the modified text. Use the dict2xx commands to 230# convert this back into a regular text. 231# 232# Arguments 233# - text The text (as dict) to patch 234# - patch The patch (as cmd list) to apply. 235# 236# Results 237# The modified text (as dict) 238# 239# Side effects 240# None 241 242proc ::rcs::applyRcsPatch {text patch} { 243 array set lines $text 244 245 foreach cmd $patch { 246 foreach {op a b} $cmd break 247 248 switch -exact -- $op { 249 a { 250 # a = index of line where to add 251 # b = text to add 252 253 if {[info exists lines($a)]} { 254 append lines($a) \n $b 255 } else { 256 set lines($a) $b 257 } 258 } 259 d { 260 # a = index of first line to delete. 261 # b = #lines to delete. 262 263 while {$b > 0} { 264 unset lines($a) 265 incr a 266 incr b -1 267 } 268 } 269 default { 270 return -code error "Unknown patch command: '$op'" 271 } 272 } 273 } 274 275 return [array get lines] 276} 277 278# ### ### ### ######### ######### ######### 279## Ready for use. 280 281package provide rcs 0.1 282