File: glimpse.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 (429 lines) | stat: -rw-r--r-- 14,072 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
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
#--------------------------------------------------
#
# manGlimpse -- full text search
#
#--------------------------------------------------

proc manGlimpse {name {opts ""} {w .man}} {
	global man manx

	# index over currently selected paths, and (always) stray cats
	set dirs $man(glimpsestrays)

	set len [llength $name]
	if {$len>=2} {
		if {[string match "-*" $name]} {
			set name [lindex $name end]; set opts [concat $opts [lrange $name 0 [expr $len-1-1]]]
		} else {
			set name [tr $name " \t\n" ";"]
		}
	}
	if {$man(indexglimpse)=="distributed"} {
		foreach d $manx(paths) {
			if {$man($d)} {	lappend dirs $d }
		}
	} else {
# can't restrict unified glimpse searches to directories in Paths
# because list of directories can exceed a buffer in agrep
#		set first "-F "
#		foreach d $manx(paths) {
#			append auxopts $first [stringregexpesc $d]
#			set first ","
#		}
	}

	manGlimpse2 $name $dirs $opts $w
}

proc manGlimpse2 {name dirs {auxopts ""} {w .man}} {
	global man manx mani sbx env stat STOP

	if $manx(shift) {set manx(shift) 0; set w [manInstantiate]}
	set t $w.show; set wi $w.info

	# set name to search for and name to show
	if {$name==""} {set name $manx(man$w)}
	if {$name==""} {
		manWinstderr $w "Type in regular expression for full text search"
		return
	}
	set showname $name

	# set options
	#set opts "-ly"
	set opts "-y"
	if {$man(regexp,case)!=-1 || [string tolower $name]==$name} {append opts "i"}
	if {$man(maxglimpse)!="none"} {append opts " -L $man(maxglimpse):0:5"}

	# kill -w for everybody -- use at your own risk: gotta keep for "perl" and others
	set g1 "$man(glimpse) $auxopts $opts"
#	regsub -- {-([^ /]*)w} "$man(glimpse) $auxopts $opts" {-Z\1} g1
	# kill -N for excerpts search
	regsub -- {-([^ /]*)N} $g1 {-Z\1} g2


	set foundList ""
	set errorList ""

	# FIRST SEARCH index only to estimate number of matches

	foreach d $dirs {
		# this error reported at startup, so silently skip over missing .glimpse_* here
		if ![file readable $d/.glimpse_index] continue
DEBUG {puts "index search: $g1 -H $d $name"}

		# would be considerably(?) more efficient if Glimpse could handle a list of directories
		# to search for matches, rather than multiple exec's
		# HACK: -N spits out block matches to stderr, which is an error to Tcl, so 2>/dev/null
		# protect name with braces as may include semicolon (glimpse AND operator)
		if {![catch {set matches [eval exec "$g1 -N -H $d {$name} 2>/dev/null"]} info]} {
			set foundList [concat $foundList [lsort [split $matches "\n"]]]
		} else {set errorList [concat $errorList [list "error with glimpsing $d:"] [split $info "\n"]]}
	}
	set fIndexonly [expr [llength $foundList]>$man(maxglimpseexcerpt)]


	# SECOND SEARCH extracts those matches, if < $man(maxglimpseexcerpt), 

	if {!$fIndexonly} {
		set foundList ""; # replace existing hit list with one with excerpts
		set errorList ""

		# -w and -z together gives stderr message -- probably a bug in glimpse
		# => but we always kill -w anyhow, so nothing to worry about here
		#set redirect "" -- and don't have to redirect anymore, in absence of -N and (-w and -z)

		set STOP 0
		cursorBusy

		foreach d $dirs {
			if ![file readable $d/.glimpse_index] continue
			set glz ""; if {[file readable $d/.glimpse_filters] && [file size $d/.glimpse_filters]>1} {set glz "-z"}
			manWinstdout $w "Glimpsing for \"$showname\" in $d ..."; update; # not "update idletasks" because want to accept stop requests from keyboard (should change "man" to "STOP" and reprogram so can stop with a click too)
DEBUG {puts "$g2 -H $d $name"}
			if {$STOP} {set STOP 0; break}
			if {![catch {set matches [eval exec "$g2 $glz -O -H $d {$name} 2>/dev/null"]} info]} {
# || [string match "There are matches to *" $info] || [string match "*-d or -w option is not supported for this pattern*" $info]
				set foundList [concat $foundList [split $matches "\n"]]
			} else {set errorList [concat $errorList [list "error with glimpsing $d:"] [split $info "\n"]]}
		}
	}


	## format the result

	set foundform ""
	set found 0

	foreach errmsg $errorList {lappend foundform "$errmsg\n" i}
	if {[llength $errorList]} {lappend errmsg "\n\n" {}}

	foreach page $foundList {
		# The following not true?
		# If there are more subMatchVar's than parenthesized subexpressions within exp, or if a
		# particular subexpression in exp doesn't match the string (e.g. because it was in a
		# portion of the expression that wasn't matched), then the corresponding subMatchVar
		# will be set to ``-1 -1'' if -indices has been specified or to an empty string otherwise.
		if {$page==""} {
			# nothing
		} elseif {[string match "/*" $page]} {
			lappend foundform "[bolg [string trimright $page :] ~]\n" manref
			incr found
		} else {
			lappend foundform "     $page\n" sc
		}
	}
	manWinstdout $w ""
	cursorUnset


	set error [string length $errorList]
	if {!$found && !$error} {
		manWinstderr $w "$name not found in full text search"
		# don't destroy old list
	} else {
		manNewMode $w glimpse; incr stat(glimpse)
		set mani(glimpse,update) [clock seconds]
		set form {}
		lappend form " Glimpse full text search for \"$name\"\n\n" {}

		if $error {
			lappend form "Errors while Glimpsing:\n\n" {}
		}

#		set cnt [expr {$error?"errors":$found]}]

		set mani(glimpse,form) [concat $form $foundform]
		set mani(glimpse,cnt) $found
		set mani(glimpse,shortvolname) "glimpse"

		# seed regexp and isearch strings
#		set manx(search,string$w) [tr [tr [llast $name] ";" ".*"] "," "|"]
		set manx(search,string$w) [tr [tr [llast $name] ";" "|"] "," "|"]
		set sbx(lastkeys-old$t) [llast $name]

		.vols entryconfigure "*glimpse*" -state normal
# -label "glimpse hit list ($cnt for \"$name\")"
		manShowSection $w glimpse

		if {!$fIndexonly} "
			after 1000 {
				searchboxSearch \$manx(search,string$w) 1 \$man(regexp,case) search $t
				foreach {s e} \[$t tag ranges search] {$t tag remove search \$s+1c \$e}
			}
		"
	}
}


set mani(glimpseindex,update) 0
proc manGlimpseIndexShow {} {
	global man manx mani curwin

	set gi [file join $man(glimpsestrays) ".glimpse_index"]
	if {$man(indexglimpse)!="unified" || ![file readable $gi] || $mani(glimpseindex,update)>[file mtime $gi]} return

	set fid [open $gi]; set index0 "\n"; append indexglimpse [read $fid]; close $fid
	regsub -all "\002\[^\n\]+" $indexglimpse "" indexnotrail
	regsub -all "\[\001-\011\013-\037\]+" $indexnotrail "" indexnoctrl
	regsub -all "\n\[_-\]+\n" $indexnoctrl "\n" indexnobox
	regsub -all "\n\[^\n\]\[^\n\]?\[^\n\]?\n" $indexnobox "\n" indexno123; # misses some?
	regsub -all "\n\[^_a-z\]\[^\n\]*\n" $indexno123 "\n" indexnonoise

	set index {}; set sub {}; set och2 "__"
	foreach i [lsort $indexnonoise] {
		set ch2 [string range $i 0 1]
		if {$ch2!=$och2} {lappend index [join $sub "\t"] manref "\n\n" {}; set sub {}; set och2 $ch2}
		lappend sub $i
	}
	if {$sub!=""} {lappend index [join $sub "\t"] manref}

	set mani(glimpseindex,update) [clock seconds]
	set mani(glimpseindex,form) $index
	set mani(glimpseindex,cnt) [llength $indexnonoise]
}


proc manGlimpseIndex {{w .man}} {
	global man manx mani stat

	# may have changed glimpse strays dir since startup
	set var "manx($man(glimpsestrays),latest)"
	if ![info exists $var] {set $var 0}

	# index over all paths, whether currently on or not

	# pairs of (dest-dir-of-index list-of-dirs-to-index)
	if {$man(indexglimpse)=="distributed"} {
		set dirpairs {}
		if [llength $mani($man(glimpsestrays),dirs)] {
			lappend dirpairs [list $man(glimpsestrays) $mani($man(glimpsestrays),dirs)]
		}
		foreach dir $manx(paths) {
			lappend dirpairs [list $dir $mani($dir,dirs)]
		}
	} else {
		set dirs $mani($man(glimpsestrays),dirs)
		foreach dir $manx(paths) {
			set dirs [concat $dirs $mani($dir,dirs)]
		}
		set dirpairs [list [list $man(glimpsestrays) $dirs]]
	}

	set buildsec [expr [lfirst [time {manGlimpseIndex2 $dirpairs $w}]]/1000000]

	if {$buildsec<[expr 60*60]} {set buildfmt "%M:%S"} {set buildfmt "%T"}
	if {$buildsec>30 || $man(time-lastglimpse)==-1} {
		set man(time-lastglimpse) [clock format $buildsec -format $buildfmt]
	}
	incr stat(glimpse-builds)

	.occ.db entryconfigure "*Glimpse*" -label "Glimpse Index (last $man(time-lastglimpse))"

	# now update Glimpse warnings -- done at every Help
	#manManpathCheck
}


proc manGlimpseIndex2 {dirpairs {w .man}} {
	global man manx mani env

	manNewMode $w glimpse
	set t $w.show; set wi $w.info

	manWinstdout $w "Rebuilding Glimpse database ... "
	set mani(glimpse,shortvolname) "Glimpse"
	manShowSection $w glimpse
	.vols entryconfigure "*glimpse*" -state normal

	manTextOpen $w; update idletasks
	set cnt [llength $dirpairs]; set cur 1
	set foneup 0

	foreach pair $dirpairs {
		foreach {dir dirs} $pair break
		$t insert end "Working on $dir" b " ($cur of $cnt), "
		set dircnt [llength $dirs]; set dirtxt [expr $dircnt==1?"directory":"directories"]
		$t insert end "$dircnt $dirtxt"
		$t insert end "\n\n"
		$t see end; update idletasks

		if {!$dircnt} {
			$t insert end "Nothing to index.\n"
			incr cur; $t insert end "\n\n"
			continue
		}

		set gzt ".glimpse_filters"
		set gf "$dir/.glimpse_filenames"
		set gz "$dir/$gzt"
		set gfe [expr [llength [glob -nocomplain $dir/.glimpse_{filenames,index}]]==2]


		# see if index is out of date
		set outofdate [expr {!$gfe || [file size $gf]==0 || ([file exists $gz] && [file mtime $gz]>[file mtime $gf]) || [file mtime $gf]<$manx($dir,latest)}]

		if {!$outofdate} {
			$t insert end "Glimpse index still current.\n" indent

			set foneup 1
			# could use perl-style continue expression here
			incr cur; $t insert end "\n\n"
			continue
		}


		# directory writable?
		if {![file writable $dir]} {
			$t insert end "Glimpse index out of date but directory not writable" indent
			if {$gfe} {
				$t insert end " ... but old Glimpse files found\n" indent
				$t insert end "Full text seaching available here using existing files.\n" indent
			} else {
				$t insert end " ... and Glimpse files not found\n" indent
				$t insert end "No full text searching available here.\n" {indent bi}
			}

			incr cur; $t insert end "\n\n"
			continue
		}


		# create .glimpse_exclude to ignore RCS, RCSdiff
		set gex "$dir/.glimpse_exclude"
		if ![file exists $gex] {
			set fid [open $gex "w"]
			puts $fid {.glimpse_exclude$}
			puts $fid {~$}
			puts $fid {.bak$}
			puts $fid {/RCS$}
			puts $fid {/RCSdiff$}
			close $fid
		}
		# "If a file is in both .glimpse_exclude and .glimpse_include it will be excluded"
		# "Symbolic links are followed by glimpseindex only if they are specifically included here"
		set gin "$dir/.glimpse_include"
		if ![file exists $gin] {
			set fid [open $gin "w"]
			puts $fid "*.*"; # same test for valid page names as elsewhere
			close $fid
		}


		# see if .glimpse_filters file needed, and if so make one
		# (but don't overwrite any existing .glimpse_filters)
		if ![file exists $gz] {
			set fcat [expr [lsearch -regexp $dirs {/cat[^/]*$}]!=-1]
#|/catman/
			set fhp [expr [lsearch -regexp $dirs {\.Z$}]!=-1]
			set fz 0
			foreach d $dirs {
				# cd into directory so get the short file names (important for /usr/man!)
				cd $d
				if {[lsearch -regexp [glob -nocomplain *] $manx(zregexp)]!=-1} {set fz 1; break}
			}

#			$t insert end "* * * create $gzt file here: fcat=$fcat, fhp=$fhp, fz=$fz\n"
			set fid [open $gz "w"]
			# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
			# create file according to man(compress) and manx(zglob)  #
			# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
			if $fhp {puts $fid "*.Z/*\tzcat <"}
			if {$fz} {
				set zcat [file tail [lfirst $man(zcat)]]
				switch -glob -- $zcat {
					gz* {puts $fid "*.z\t$man(zcat)\n*.Z\t$man(zcat)\n*.gz\t$man(zcat)"}
# would like to do this
#					gz* {puts $fid "*.{z,Z,gz}\t$man(zcat)"}
					default {
						# works for zcat, pcat and (one hopes) anything else to come
						# (string trimright because of HP "zcat < ")
						puts $fid "*.$manx(zglob)\t[string trimright $man(zcat) { <}]"
					}
				}
			}
			# strip AFTER decompression
			if $fcat {puts $fid "*/cat*/*\trman <"}
			close $fid
		}


		### try to index or re-index directory
		if [catch {set fid [open "|$man(glimpseindex) -z -H $dir $dirs"]} info] {
			# other problems ... like what?
DEBUG {puts "error on: $man(glimpseindex) -z -H $dir $dirs]: $info"}
			$t insert end "$info\n" bi
			catch {close $fid}; # fid not set?
		} else {
DEBUG {puts "$man(glimpseindex) -z -H $dir $dirs"}
			# could think about reporting $dir and $dirs in text buffer
			fconfigure $fid -buffering line; # doesn't seem to make any difference on a pipe(?)
			set blankok 0
			while {![eof $fid]} {
				gets $fid line
				if {![regexp {(^This is)} $line] && ($line!="" || $blankok)} {
					$t insert end "$line\n" tt; $t see end; update idletasks
					set blankok 1
				}
				update idletasks
			}
			if [catch {close $fid} info] { $t insert end "ERRORS\n" {indent bi} $info indent2 "\n" }

			if {[file size $gf]==0} {
				$t insert end "No files could be indexed.  No full text searching available here.\n" {indent bi}
				if [file exists $gz] {
					$t insert end "Try checking your $gzt file in $dir.  If $gzt wasn't created by TkMan, try deleting it and letting TkMan create one of its own.\n" indent
				}
			} else {
				# give glimpse files same permissions as directory
				catch {
					file stat $dir dirstat
					set perm [format "0%o" [expr $dirstat(mode)&0666]]
					foreach setperm [glob $dir/.glimpse_*] {file attributes $setperm -permissions $perm}
				}
			}
		}

		incr cur
		$t insert end "\n\n"
	}

	if {$foneup} {
		$t insert end "\nTo force re-indexing of directories that TkMan claims are current, remove all Glimpse index files in that directory, as with `rm <dir> .glimpse_*'.\n" i
	}

	$t see end
	manTextClose $w

	set mani(glimpse,form) [list [$t get 1.0 end]]

	manWinstdout $w ""
}

proc manGlimpseClear {} {
	global man manx
	foreach [concat $manx(paths) $man(fsstnddir)] {
		# zaps .glimpse_filters, which may have been carefully constructed manually
		catch {eval file delete -force [glob $dir/.glimpse_*]}
	}
}