File: version.tcl

package info (click to toggle)
tkman 2.0.6-3
  • links: PTS
  • area: non-free
  • in suites: hamm, slink
  • size: 876 kB
  • ctags: 296
  • sloc: tcl: 7,327; makefile: 250; sh: 6
file content (359 lines) | stat: -rw-r--r-- 12,935 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
# find a safe section for database build dump
# auto change bars iff RCS'ed old version (interpret diff/ed change marks) (italics=>new, strikethrough=>deleted, bold italics=>changed=>deleted+inserted.  unfortunately on the level of lines)
# show diff's at first, always?
# X order: load page, attach highlights, report RCS info, user edit highlights, zap inserted text, recompute highlights ("update"?), save highlights (whew!)
# compute diff's on rman -f ascii.  co -p << v1 | diff -w << v2 2> /dev/null (whew!)
# + search for old version in RCS directory
# X should probably use diff in repositioning highlights, but RCS copies likely to be scarce so not worth it
# make this faster!  two nroffs, diff, I'm dying here.  possible to background this?
# + cache diffs?  where?  in RCS? maybe not writable; in control of sysadmin.  info? not appropriate.  fstnd?  tmp? what about namespace collisions?
# X prefer new directory "RCSdiff" if creatable/writable.  if not, /tmp (if c/w)
# + alternative for RCSdiff? => /diff within corresponding cat!
# + database building option (alongside man pages, Texinfo) to make diff caches for all pages with RCS
# have to put RCS files in a directory named RCS.  don't support RCS in same directory as file being RCS'ed since that would confuse other man pagers
# more concise diffs with wdiff.  store on basically same format, except give start as <line>.<char>


set man(versiondiff) 1; set manx(versiondiff-v) {1 0}; set manx(versiondiff-t) {"yes" "no"}
set manx(edregexp) {^([0-9]+),?([0-9]*)([adc])([0-9]+),?([0-9]*)$}

set manx(vdiff) $man(vdiff)
set manx(iswdiff) [string match "wdiff*" $man(vdiff)]
# meta character madness to do this in Makefile
if $manx(iswdiff) {append manx(vdiff) " -w {\ndiffdstart\n} -x {\ndiffdend\n} -y {\ndiffastart\n} -z {\ndiffaend\n}"}


proc manVersionDiff {f w} {
	global man manx

	if {[string match "*/RCS/*,v" $f]} {
		set rcsf $f
		set dir [file dirname [file dirname $f]]
		set tail [file tail $f]; set tail [string range $tail 0 [expr [string length $tail]-3]]
		set f $dir/$tail
	} elseif {[regexp {(.*)/(([^/]+)\.([^\.]+))} $f all dir tail rootname suffix]} {
		set rcsf "$dir/RCS/$tail,v"
	} else return
	if {![file readable $rcsf]} {return ""}

	if {$manx(effcols)!=""}  {
		set fid [open $manx(longtmp) "w"]; puts $fid ".ll $man(columns)\n.hym 20"; close $fid
	}

	set cachedir $dir
	if {[regsub {/man([^/]+)$} $dir {/cat\1} d2]} {set cachedir $d2}
	append cachedir $manx(effcols)
	if {![file writable $cachedir] && ![file writable [file dirname $cachedir]]} {
		if {[regexp {^(/usr)?/(.*)man/man(.*)$} $dir all junk prefix suffix]} {
			set cachedir "$man(fsstnddir)/${prefix}cat$suffix$manx(effcols)"
		}
	}
	append cachedir "/RCSdiff"

	set cachefile "$cachedir/$tail"
	set existingcachefile [lfirst [glob -nocomplain "$cachefile$manx(zoptglob)"]]
	if {[file readable $existingcachefile] && [file mtime $existingcachefile]>[file mtime $f] && [file mtime $existingcachefile]>[file mtime $rcsf]} {
		# use cache
		# slightly different than manManPipe
		if {[regexp $manx(zregexp) $existingcachefile]} {set pipe "|$man(zcat) "} else {set pipe ""}
		set fid [open "$pipe$existingcachefile"]; set diffs [read $fid]; close $fid
#puts "using cached"

	} else {
		cursorBusy
		cd $dir
		manWinstdout $w "Computing diffs for $tail $manx(effcols) ..." 1

		### find first version with changes (if any)
		set search 1
		set insym 0
		set fid [open "|$man(rlog) $tail"]
		while {$search && [gets $fid line]!=-1} {
			if {!$insym} {
				# look for "<ws>checkpoint: <rev>"
				if {[regexp "^\[ \t\]+" $line]} {
					if {[regexp "^\[ \t\]+checkpoint: (\[0-9\\.\]+)\$" $line all vnum]} {
						set search 0
					}
					continue

				} else {set insym 0}
				# and fall through
			}
			if {[regexp {^revision ([0-9\.]+)$} $line all vnum]} {
#puts "testing $vnum"
				# gotta collect the stderr, ugh
				catch {exec $man(rcsdiff) -r$vnum $tail} info
#puts "checking\n$info"
				foreach line [split $info "\n"] {
					if {[regexp $manx(edregexp) $line]} {
#puts "match on $vnum"
						set search 0; break
					}
				}
			}
		}
		catch {close $fid}

#		if !$isdiff {return ""} => even if no diff, cache this knowledge so faster next time


		### collect diffs
		# diff needs at least one of them to be a real file.  want text of previous version around anyhow
		set tmpf /tmp/tkman[pid]
# $man(changeleft)
		set format "$man(format) | $manx(rman) -f ASCII -N"
# $man(zaphy)
#		if $manx(iswdiff) {append format " | sed s/\[ \t\n\]\+/\n/"}
#puts "creating $tmpf (old)"
#puts "exec $man(co) -p$vnum $tail | $format > $tmpf"
		catch {eval exec $man(co) -p$vnum $tail | $format > $tmpf} info

#puts "creating diffs vs v$vnum"
#puts $manx(vdiff)
# use "|open..." and read line at a time
		catch {set diffs [eval exec [manManPipe $tail] | $format | $manx(vdiff) $tmpf -]} diffs
#puts $diffs
		file delete -force $tmpf
		if {$manx(effcols)!=""} {file delete -force $manx(longtmp)}

		# just save lines from old RCS file, as already have new lines!
		# this works especially well as documents tends to grow
		set newdiffs {}

		if $manx(iswdiff) {
		set linenum 1; set linelen 0; set startl 0; set linec 0
		set indiffd 0
		# very expensive but only have to do it once per update of documentation
		# could greatly benefit from a few options to wdiff...
		foreach line [split $diffs "\n"] {
#puts "$linenum: $line"
			if {[string match "diff*" $line]} {
				incr linenum -1; # introduced an extra \n to make sure signal get a line of its own
				if {$line=="diffdstart"} {
					set startl $linenum; set startc $linec
					set indiffd 1
				} elseif {$line=="diffdend"} {
					append newdiffs "[expr $linenum-$startl].${linelen}d0.0@$startl.$startc\n"
					append newdiffs $dtxt; set dtxt ""
					# don't count lines brought in from old version
					set linenum $startl; set linec $startc
					set indiffd 0
				} elseif {$line=="diffastart"} {
					set startl $linenum; set startc $linec
					set indiffa 1
				} elseif {$line=="diffaend"} {
					append newdiffs "0.0a[expr $linenum-$startl].${linelen}@$startl.$startc\n"
					set indiffa 0
				}
			} else {
				if {$linenum==$startl} {incr linec $linelen} else {set linec $linelen}
				set linelen [string length $line]
				if {$indiffd} {append dtxt $line "\n"}
				incr linenum
				# else ignore
				# unfortunately have to examine all lines to get line information from wdiff
			}
		}

		} else {	# diff

		set skip 0; set keep 0
		set lines [split $diffs "\n"]
		for {set linenum 0} {$linenum<[llength $lines]} {incr linenum} {
			set line [lindex $lines $linenum]
			if {$keep} {incr keep -1; append newdiffs [string range $line 2 end] "\n"; continue
			} elseif {$skip} {incr skip -1; continue}
			if {![regexp $manx(edregexp) $line all n1 n2 cmd n3 n4]} break
			if {$n2==""} {set n2 $n1}; if {$n4==""} {set n4 $n3}
			set lcntold [expr $n2-$n1+1]; set lcntnew [expr $n4-$n3+1]
			if {$cmd=="a"} {
				# already have added lines, throw them all out
				set skip $lcntnew
				append newdiffs "0.0a$lcntnew.0@$n3.0\n"
			} elseif {$cmd=="c"} {
				# changed: keep old, skip separator and new

				# first verify that changes aren't just formatting changes
				# (not the same as diff's -B as formatting change may span lines)
				set nregexp "\[ \t\n|\]+"
				set cold ""; set cnew ""
				set i 0
				for {set i 0} {$i<$lcntold} {incr i} {append cold [string range [lindex $lines [expr $linenum+1+$i]] 2 end] "\n"}
				for {set i 0} {$i<$lcntnew} {incr i} {append cnew [string range [lindex $lines [expr $linenum+1+$lcntold+1+$i]] 2 end] "\n"}
				regsub -all $nregexp [string trim $cold] " " ncold
				regsub -all $nregexp [string trim $cnew] " " ncnew

				if {$ncold!=$ncnew} {
#puts "diff: $ncold\n   => $ncnew"
					set keep $lcntold
					set skip [expr 1+$lcntnew]
					append newdiffs "$lcntold.0c$lcntnew.0@$n3.0\n"
				} else {
#puts "bogus diff: $ncold"
					incr linenum [expr $lcntold+1+$lcntnew]
				}

			} else { # d
				# deleted from old, bring them along
				set keep $lcntold
				append newdiffs "$lcntold.0c0.0@$n3.0\n"
			}
		}
		}

		set diffs $newdiffs
#puts $f\n$newdiffs
#puts $diffs
#exit 0

		# cache diffs if possible
		set writedir $cachedir
		set mkdirs {}
		while {![file exists $writedir]} {
			lappend mkdirs $writedir
			set writedir [file dirname $writedir]
		}
		if {[file writable $writedir]} {
			foreach mkdir [lreverse $mkdirs] {file mkdir $mkdir}
		}
#puts "$cachedir"


		if {[file writable $cachedir]} {
			if [file writable $cachedir] {
				if {$existingcachefile!=""} {file delete $existingcachefile}
				#manWinstdout $w "Cacheing diffs ..." 1
				set fid [open $cachefile "w"]; puts $fid $diffs; close $fid
				eval exec $man(compress) $cachefile
			} else {manTextPlug $w.show 1.0 "Couldn't cache version differences: $cachedir not writable" b}
		}
		cursorUnset
	}

	return $diffs
}


proc manVersionDiffMakeCache {w t} {
	global mani manx man

	manTextOpen $w
	foreach sect $mani(manList) {
		foreach dir $mani($sect,dirs) {
			# dump this information info main window so can report "no RCS" , "not writable", et cetera
			$t insert end $dir b "     "
			set rcsdir "$dir/RCS"

			set cachedir $dir
			if {[regsub {/man([^/]+)$} [file dirname $dir] {/cat\1} d2]} {set cachedir $d2}
			append cachedir $manx(effcols)
			if {![file writable $cachedir] && ![file writable [file dirname $cachedir]]} {
				if {[regexp {^(/usr)?/(.*)man/man(.*)$} $dir all junk prefix suffix]} {
					set cachedir "$man(fsstnddir)/${prefix}cat$suffix$manx(effcols)"
				}
			}
			append cachedir "/RCSdiff"
			set writedir $cachedir
			while {![file exists $writedir]} {set writedir [file dirname $writedir]}

			set errmsg ""
			if {![file exists $rcsdir]} {set errmsg "no versioning information (RCS directory)"
			} elseif {![file readable $rcsdir]} {set errmsg "$rcsdir not readable"
			} elseif {![file writable $writedir]} {set errmsg "$cachedir not writable/creatable (need perissions on $writedir)"
			}

			$t insert end "[expr {$errmsg==""?"CACHEING":$errmsg}]\n"
			update idletasks; $t see end
			if {$errmsg!=""} continue

			foreach rcsfile [lsort -dictionary [glob -nocomplain "$rcsdir/*,v"]] {
				foreach cols $manx(columns-v) {
					set tmp $manx(effcols); set manx(effcols) [expr {$cols==65?"":"@$cols"}]
					manVersionDiff $rcsfile $w
					set manx(effcols) $tmp
				}
			}
		}
	}
	manTextClose $w
}


proc manVersion {w t f} {
	global manx

	set diffcnt 0
	if {[set diffs [manVersionDiff $f $w]]==""} {return $diffcnt}
	set lcregexp {([0-9]+)\.([0-9]+)}
	set cmdregexp "$lcregexp\(\[acd\]\)($lcregexp)@($lcregexp)"

	### apply diffs
	set deltal 0; set deltac 0; set atl 0; set atc 0
	set dell 0; set delc 0
	set tags ""
	# invariant: start with command
#	manOutline $t 0 *
	foreach line [split $diffs "\n"] {
#		$t yview [expr $atl+$deltal].0-5l; update idletasks; after 1000
#puts "inspecting $line"
		# lines deleted, which we recover (insert) now
		if {$dell} {
#			$t insert $ts "$line" $tags
#			if {$dell!=$dell0} {$t insert "$ts lineend" "\n" $tags; incr deltal} else {incr atl}
			$t insert $ts "|$line\n" $tags; incr deltal
			set ts "[expr $atl+$deltal].0"
			incr dell -1
			continue
		} elseif {$delc} {
#puts "$t insert $ts |$line| |$tags|"
			$t insert $ts $line $tags
			set cbi "$ts linestart"; if {[$t get $cbi]==""} {append cbi "+1c"}; # skip over outline image
			if {[$t get $cbi]!="|"} {$t insert $cbi "|"}
			append ts "+${delc}c"
			set delc 0; # next insert might be on same line
			continue
		}
#puts "cmd?  $line"
		if {![regexp $cmdregexp $line all dell delc cmd inslc insl insc atlc atl atc]} break
		set dell0 $dell
		incr diffcnt
#puts "applying $dell $delc *$cmd* $inslc $insl $insc @ $atlc $atl $atc"
		set ts "[expr $atl+$deltal].[expr $atc+$deltac]"
		if {$cmd=="a" || $cmd=="c"} {
			# add tag to existing text
			if {!$insl} {set te "$ts+${insc}c"} else {set te "[expr $atl+$deltal+$insl].${insc}"}
			$t tag add diff$cmd $ts $te
			for {set ci 0} {$ci<$insl} {incr ci} {
				set cbi "[expr $atl+$deltal+$ci].0"
				if {[$t get $cbi]==""} {append cbi "+1c"}; # skip over outline image
				if {[$t get $cbi]!="|"} {$t insert $cbi "|"}
			}
#puts "$t tag add diff$cmd $ts $te"
		}
		if {$cmd=="c" || $cmd=="d"} {
			# insert old stuff, bump delta
			# keep initial "<" as marker in left margin and as something for zapped newlines
			set tagshere [$t tag names $ts]
			set tags [concat [lmatches areajs* $tagshere] [lmatches elide $tagshere] diffd]
		} 
	}

	return $diffcnt
}

# zap everything in RCSdiff directories
proc manVersionClear {} {
	global man manx

	# doesn't respects settings in Paths
	foreach dir $manx(paths) {
		# if catman, may be only copy of page (SGI only has formatted)
		if {[string matches "*/catman*" $dir]} continue

		# don't complain if can't write
		catch {eval file delete -force [glob $dir/cat*/RCSdiff/*]}
		catch {eval file delete -force [glob $dir/cat*/RCSdiff]}
	}
	# what about $man(fsstnddir)?
}