docstrip_util.tcl
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 package require Tcl 8.4
00018 package require docstrip 1.2
00019 package provide docstrip::util 1.2
00020 namespace docstrip::util {
00021 namespace import [namespace parent]::extract
00022 namespace export ddt2man guard patch thefile
00023 }
00024 ret docstrip::util::ddt2man (type text) {
00025 set wascode 0
00026 set verbatim 0
00027 set res ""
00028 foreach line [split $text \n] {
00029 if {$verbatim} then {
00030 if {$line eq $endverbline} then {
00031 set verbatim 0
00032 } else {
00033 append res [string map {[ [lb] ] [rb]} $line] \n
00034 }
00035 } else {
00036 switch -glob -- $line %%* {
00037 if {$wacode} then {
00038 append res {[example_end]} \n
00039 set wascode 0
00040 }
00041 append res [string range $line 2 end] \n
00042 } %<<* {
00043 if {!$wascode} then {
00044 append res {[example_begin]} \n
00045 set wascode 1
00046 }
00047 set endverbline "%[string range $line 3 end]"
00048 set verbatim 1
00049 } %<* {
00050 if {!$wascode} then {
00051 append res {[example_begin]} \n
00052 set wascode 1
00053 }
00054 set guard ""
00055 regexp -- {(^%<[^>]*>)(.*)$} $line "" guard line
00056 append res \[ [list emph $guard] \]\
00057 [string map {[ [lb] ] [rb]} $line] \n
00058 } %* {
00059 if {$wascode} then {
00060 append res {[example_end]} \n
00061 set wascode 0
00062 }
00063 append res [string range $line 1 end] \n
00064 } {\\endinput} {
00065 break
00066 } "" {
00067 append res \n
00068 } default {
00069 if {!$wascode} then {
00070 append res {[example_begin]} \n
00071 set wascode 1
00072 }
00073 append res [string map {[ [lb] ] [rb]} $line] \n
00074 }
00075 }
00076 }
00077 if {$wascode} then {append res {[example_end]} \n}
00078 return $res
00079 }
00080 ret docstrip::util::guards (type subcmd , type text) {
00081 set verbatim 0
00082 set lineno 1
00083 set badL {}
00084 foreach line [split $text \n] {
00085 if {$verbatim} then {
00086 if {$line eq $endverbline} then {set verbatim 0}
00087 } else {
00088 switch -glob -- $line %<<* {
00089 set endverbline "%[string range $line 3 end]"
00090 set verbatim 1
00091 } %<* {
00092 if {![
00093 regexp -- {^%<([*/+-]?)([^>]*)>(.*)$} $line ""\
00094 modifier expression line
00095 ]} then {
00096 lappend badL $lineno $line
00097 } else {
00098 if {$modifier eq ""} then {set modifier " "}
00099 append E($expression) $modifier
00100 }
00101 }
00102 }
00103 incr lineno
00104 }
00105 if {$subcmd eq "rotten"} then {return $badL}
00106 switch -- $subcmd "exprmods" {
00107 return [array get E]
00108 } "expressions" {
00109 return [array names E]
00110 } "exprerr" {
00111 set res {}
00112 foreach expr [array names E] {
00113 regsub -all {[^()!,|&]+} $expr 0 e
00114 regsub -all {,} $e {|} e
00115 if {[catch {expr $e}]} then {lappend res $expr}
00116 }
00117 return $res
00118 }
00119 foreach name [array names E] {
00120 set E($name) [string length $E($name)]
00121 }
00122 if {$subcmd eq "exprcounts"} then {return [array get E]}
00123 foreach expr [array names E] {
00124 foreach term [split $expr "()!,|&"] {
00125 if {$term eq ""} then {continue}
00126 if {![info exists T($term)]} then {set T($term) 0}
00127 incr T($term) $E($expr)
00128 }
00129 }
00130 switch -- $subcmd "counts" {
00131 return [array get T]
00132 } "names" {
00133 return [array names T]
00134 } default {
00135 error "Unknown subcommand '$subcmd', must be one of:\
00136 counts, exprcounts, expressions, exprmods, names, rotten"
00137 }
00138 }
00139 ret docstrip::util::patch (type sourcevar , type termL , type fromtext , type diff , type args) {
00140 upvar 1 $sourcevar SL
00141 array set O {-trimlines 1 -matching exact}
00142 array set O $args
00143 set cmd [list extract [join $SL \n] $termL -annotate 2]
00144 foreach opt {-metaprefix -trimlines} {
00145 if {[info exists O($opt)]} then {lappend cmd $opt $O($opt)}
00146 }
00147 set EL [split [eval $cmd] \n]
00148 lset EL end \n
00149 set ptr 0
00150 set lineno 1
00151 set FL [list {}]
00152 foreach line [split $fromtext \n] {
00153 lappend FL $line
00154 if {$O(-trimlines)} then {set line [string trimright $line " "]}
00155 if {$line eq [lindex $EL $ptr]} then {
00156 set lift($lineno) [lindex $EL [incr ptr]]
00157 lset lift($lineno) 0 [expr { [lindex $EL [incr ptr]] - 1 }]
00158 incr ptr
00159 }
00160 incr lineno
00161 }
00162 if {![array size lift]} then {
00163 return -code error "The extract did not match any part of the\
00164 fromtext. Check the list of terminals and the options"
00165 }
00166 set RL [list]
00167 set log [list]
00168 foreach hunk [lsort -decreasing -integer -index 0 $diff] {
00169 set replL [list]
00170 set l1 [lindex $hunk 0]
00171 set repl {0 -1}
00172 set matches 1
00173 foreach {type line} [lindex $hunk 4] {
00174 switch -glob -- $type {[0-]} {
00175 switch -- $O(-matching) "exact" {
00176 if {[lindex $FL $l1] ne $line} then {set matches 0}
00177 } "nonspace" {
00178 if {[regsub -all -- {\s} $line {}] ne\
00179 [regsub -all -- {\s} [lindex $FL $l1] {}]} then {
00180 set matches 0
00181 }
00182 } "anyspace" {
00183 if {[regsub -all -- {\s+} $line { }] ne\
00184 [regsub -all -- {\s+} [lindex $FL $l1] { }]} then {
00185 set matches 0
00186 }
00187 }
00188 }
00189 switch -- $type synch {
00190 if {[llength $repl]>2 ||\
00191 [lindex $repl 1]-[lindex $repl 0]>=0} then {
00192 lappend replL $repl
00193 }
00194 set repl [list $l1 [expr {$l1-1}]]
00195 } + {
00196 lappend repl $line
00197 } - {
00198 lset repl 1 $l1
00199 incr l1
00200 } 0 {
00201 if {[llength $repl]>2 ||\
00202 [lindex $repl 1]-[lindex $repl 0]>=0} then {
00203 lappend replL $repl
00204 set repl {0 -1}
00205 }
00206 lset repl 1 $l1
00207 incr l1
00208 lset repl 0 $l1
00209 }
00210 }
00211 if {[llength $repl]>2 || [lindex $repl 1]-[lindex $repl 0]>=0}\
00212 then {lappend replL $repl}
00213 if {$matches} then {
00214 lappend hunk [lsort -decreasing -integer -index 0 $replL]
00215 lappend RL $hunk
00216 } else {
00217 lappend hunk "(-- did not match fromtext --)"
00218 lappend log $hunk
00219 }
00220 }
00221 foreach hunk $RL {
00222 set applied 0
00223 set misapplied 0
00224 foreach repl [lindex $hunk 5] {
00225 unset -nocomplain from to
00226 for {set n [lindex $repl 1]} {$n>=[lindex $repl 0]}\
00227 {incr n -1} {
00228 if {![info exists lift($n)]} then {
00229 incr misapplied
00230 continue
00231 } elseif {![info exists from]} then {
00232 set to [lindex $lift($n) 0]
00233 set from $to
00234 } elseif {[lindex $lift($n) 0] == $from-1} then {
00235 set from [lindex $lift($n) 0]
00236 } else {
00237 set SL [lreplace $SL $from $to]
00238 set to [lindex $lift($n) 0]
00239 set from $to
00240 }
00241 incr applied
00242 set n0 $n
00243 }
00244 if {[info exists from]} then {
00245 set sprefix [lindex $lift($n0) 1]
00246 set eprefix [lindex $lift($n0) 2]
00247 } elseif {[info exists lift([lindex $repl 0])]} then {
00248 foreach {from sprefix eprefix} $lift([lindex $repl 0])\
00249 break
00250 set to [expr {$from-1}]
00251 } else {
00252 incr misapplied [llength [lrange $repl 2 end]]
00253 continue
00254 }
00255 set eplen [string length $eprefix]
00256 set epend [expr {$eplen-1}]
00257 set cmd [list lreplace $SL $from $to]
00258 foreach line [lrange $repl 2 end] {
00259 if {$eprefix eq [string range $line 0 $epend]} then {
00260 lappend cmd "$sprefix[string range $line $eplen end]"
00261 } else {
00262 lappend cmd $line
00263 }
00264 incr applied
00265 }
00266 set SL [eval $cmd]
00267 }
00268 if {$misapplied>0} then {
00269 if {$applied>0} then {
00270 lset hunk 5 "(-- was partially applied --)"
00271 } else {
00272 lset hunk 5 "(not applied)"
00273 }
00274 lappend log $hunk
00275 }
00276 }
00277 set res ""
00278 foreach hunk [lsort -index 0 -integer $log] {
00279 foreach {start1 end1 start2 end2 lines msg} $hunk break
00280 append res [format "@@ -%d,%d +%d,%d @@ %s\n"\
00281 $start1 [expr {$end1-$start1+1}]\
00282 $start2 [expr {$end2-$start2+1}] $msg]
00283 foreach {type line} $lines {
00284 switch -- $type 0 {
00285 append res " " $line \n
00286 } - - + {
00287 append res $type $line \n
00288 }
00289 }
00290 }
00291 return $res
00292 }
00293 ret docstrip::util::thefile (type fname , type args) {
00294 set F [open $fname r]
00295 if {[llength $args]} then {
00296 if {[set code [
00297 catch {eval [linsert $args 0 fconfigure $F]} res
00298 ]]} then {
00299 close $F
00300 return -code $code -errorinfo $::errorInfo -errorcode\
00301 $::errorCode
00302 }
00303 }
00304 catch {read $F} res
00305 close $F
00306 return $res
00307 }
00308 ret docstrip::util::import_unidiff (type text , optional warnvar ="") {
00309 if {$warnvar ne ""} then {upvar 1 $warnvar warning}
00310 set inheader 1
00311 set res [list]
00312 set lines [list]
00313 set end2 "not an integer"
00314 foreach line [split $text \n] {
00315 if {$inheader && [regexp {^(---|\+\+\+)} $line]}\
00316 then {continue}
00317 switch -glob -- $line { *} {
00318 lappend lines 0 [string range $line 1 end]
00319 } {+*} {
00320 lappend lines + [string range $line 1 end]
00321 } {-*} {
00322 lappend lines - [string range $line 1 end]
00323 } @@* {
00324 if {[string is integer $end2]} then {
00325 lappend res [list $start1 $end1 $start2 $end2 $lines]
00326 }
00327 set len2 [set len1 ,1]
00328 if {[
00329 regexp {^@@ -([0-9]+)(,[0-9]+)? \+([0-9]+)(,[0-9]+)? @@}\
00330 $line -> start1 len1 start2 len2
00331 ] && [scan "$start1 $len1,1" {%d ,%d} start1 len1]==2 &&\
00332 [scan "$start2 $len2,1" {%d ,%d} start2 len2]==2
00333 } then {
00334 set end1 [expr {$start1+$len1-1}]
00335 set end2 [expr {$start2+$len2-1}]
00336 set inheader 0
00337 } else {
00338 set end2 "not an integer"
00339 append warning "Could not parse hunk header: " $line \n
00340 }
00341 set lines [list]
00342 } "" {
00343 } default {
00344 append warning "Could not parse line: " $line \n
00345 }
00346 }
00347 if {[string is integer $end2]} then {
00348 lappend res [list $start1 $end1 $start2 $end2 $lines]
00349 }
00350 return $res
00351 }
00352
00353
00354