This file is indexed.

/usr/share/tcltk/tcllib1.17/debug/debug.tcl is in tcllib 1.17-dfsg-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  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
# Debug - a debug narrative logger.
# -- Colin McCormack / originally Wub server utilities
#
# Debugging areas of interest are represented by 'tokens' which have 
# independantly settable levels of interest (an integer, higher is more detailed)
#
# Debug narrative is provided as a tcl script whose value is [subst]ed in the 
# caller's scope if and only if the current level of interest matches or exceeds
# the Debug call's level of detail.  This is useful, as one can place arbitrarily
# complex narrative in code without unnecessarily evaluating it.
#
# TODO: potentially different streams for different areas of interest.
# (currently only stderr is used.  there is some complexity in efficient
# cross-threaded streams.)

# # ## ### ##### ######## ############# #####################
## Requisites

package require Tcl 8.5

namespace eval ::debug {
    namespace export -clear \
	define on off prefix suffix header trailer \
	names 2array level setting parray pdict \
	nl tab hexl
    namespace ensemble create -subcommands {}
}

# # ## ### ##### ######## ############# #####################
## API & Implementation

proc ::debug::noop {args} {}

proc ::debug::debug {tag message {level 1}} {
    variable detail
    if {$detail($tag) < $level} {
	#puts stderr "$tag @@@ $detail($tag) >= $level"
	return
    }

    variable prefix
    variable suffix
    variable header
    variable trailer
    variable fds

    if {[info exists fds($tag)]} {
	set fd $fds($tag)
    } else {
	set fd stderr
    }

    # Assemble the shown text from the user message and the various
    # prefixes and suffices (global + per-tag).

    set themessage ""
    if {[info exists prefix(::)]}   { append themessage $prefix(::)   }
    if {[info exists prefix($tag)]} { append themessage $prefix($tag) }
    append themessage $message
    if {[info exists suffix($tag)]} { append themessage $suffix($tag) }
    if {[info exists suffix(::)]}   { append themessage $suffix(::)   }

    # Resolve variables references and command invokations embedded
    # into the message with plain text.
    set code [catch {
	set smessage [uplevel 1 [list ::subst -nobackslashes $themessage]]
	set sheader  [uplevel 1 [list ::subst -nobackslashes $header]]
	set strailer [uplevel 1 [list ::subst -nobackslashes $trailer]]
    } __ eo]

    # And dump an internal error if that resolution failed.
    if {$code} {
	if {[catch {
	    set caller [info level -1]
	}]} { set caller GLOBAL }
	if {[string length $caller] >= 1000} {
	    set caller "[string range $caller 0 200]...[string range $caller end-200 end]"
	}
	foreach line [split $caller \n] {
	    puts -nonewline $fd "@@(DebugError from $tag ($eo): $line)"
	}
	return
    }

    # From here we have a good message to show. We only shorten it a
    # bit if its a bit excessive in size.

    if {[string length $smessage] > 4096} {
	set head [string range $smessage 0 2048]
	set tail [string range $smessage end-2048 end]
	set smessage "${head}...(truncated)...$tail"
    }

    foreach line [split $smessage \n] {
	puts $fd "$sheader$tag | $line$strailer"
    }
    return
}

# names - return names of debug tags
proc ::debug::names {} {
    variable detail
    return [lsort [array names detail]]
}

proc ::debug::2array {} {
    variable detail
    set result {}
    foreach n [lsort [array names detail]] {
	if {[interp alias {} debug.$n] ne "::Debug::noop"} {
	    lappend result $n $detail($n)
	} else {
	    lappend result $n -$detail($n)
	}
    }
    return $result
}

# level - set level and fd for tag
proc ::debug::level {tag {level ""} {fd {}}} {
    variable detail
    # TODO: Force level >=0.
    if {$level ne ""} {
	set detail($tag) $level
    }

    if {![info exists detail($tag)]} {
	set detail($tag) 1
    }

    variable fds
    if {$fd ne {}} {
	set fds($tag) $fd
    }

    return $detail($tag)
}

proc ::debug::header  {text} { variable header  $text }
proc ::debug::trailer {text} { variable trailer $text }

proc ::debug::define {tag} {
    if {[interp alias {} debug.$tag] ne {}} return
    off $tag
    return
}

# Set a prefix/suffix to use for tag.
# The global (tag-independent) prefix/suffix is adressed through tag '::'.
# This works because colon (:) is an illegal character for user-specified tags.

proc ::debug::prefix {tag {theprefix {}}} {
    variable prefix
    set prefix($tag) $theprefix

    if {[interp alias {} debug.$tag] ne {}} return
    off $tag
    return
}

proc ::debug::suffix {tag {theprefix {}}} {
    variable suffix
    set suffix($tag) $theprefix

    if {[interp alias {} debug.$tag] ne {}} return
    off $tag
    return
}

# turn on debugging for tag
proc ::debug::on {tag {level ""} {fd {}}} {
    variable active
    set active($tag) 1
    level $tag $level $fd
    interp alias {} debug.$tag {} ::debug::debug $tag
    return
}

# turn off debugging for tag
proc ::debug::off {tag {level ""} {fd {}}} {
    variable active
    set active($tag) 1
    level $tag $level $fd
    interp alias {} debug.$tag {} ::debug::noop
    return
}

proc ::debug::setting {args} {
    if {[llength $args] == 1} {
	set args [lindex $args 0]
    }
    set fd stderr
    if {[llength $args] % 2} {
	set fd   [lindex $args end]
	set args [lrange $args 0 end-1]
    }
    foreach {tag level} $args {
	if {$level > 0} {
	    level $tag $level $fd
	    interp alias {} debug.$tag {} ::debug::debug $tag
	} else {
	    level $tag [expr {-$level}] $fd
	    interp alias {} debug.$tag {} ::debug::noop
	}
    }
    return
}

# # ## ### ##### ######## ############# #####################
## Convenience commands.
# Format arrays and dicts as multi-line message.
# Insert newlines and tabs.

proc ::debug::nl  {} { return \n }
proc ::debug::tab {} { return \t }

proc ::debug::parray {a {pattern *}} {
    upvar 1 $a array
    if {![array exists array]} {
	error "\"$a\" isn't an array"
    }
    pdict [array get array] $pattern
}

proc ::debug::pdict {dict {pattern *}} {
    set maxl 0
    set names [lsort -dict [dict keys $dict $pattern]]
    foreach name $names {
	if {[string length $name] > $maxl} {
	    set maxl [string length $name]
	}
    }
    set maxl [expr {$maxl + 2}]
    set lines {}
    foreach name $names {
	set nameString [format (%s) $name]
	lappend lines [format "%-*s = %s" \
			   $maxl $nameString \
			   [dict get $dict $name]]
    }
    return [join $lines \n]
}

proc ::debug::hexl {data {prefix {}}} {
    set r {}

    # Convert the data to hex and to characters.
    binary scan $data H*@0a* hexa asciia

    # Replace non-printing characters in the data with dots.
    regsub -all -- {[^[:graph:] ]} $asciia {.} asciia

    # Pad with spaces to a full multiple of 32/16.
    set n [expr {[string length $hexa] % 32}]
    if {$n < 32} { append hexa   [string repeat { } [expr {32-$n}]] }
    #puts "pad H [expr {32-$n}]"

    set n [expr {[string length $asciia] % 32}]
    if {$n < 16} { append asciia [string repeat { } [expr {16-$n}]] }
    #puts "pad A [expr {32-$n}]"

    # Reassemble formatted, in groups of 16 bytes/characters.
    # The hex part is handled in groups of 32 nibbles.
    set addr 0
    while {[string length $hexa]} {
	# Get front group of 16 bytes each.
	set hex    [string range $hexa   0 31]
	set ascii  [string range $asciia 0 15]
	# Prep for next iteration
	set hexa   [string range $hexa   32 end]  
	set asciia [string range $asciia 16 end]

	# Convert the hex to pairs of hex digits
	regsub -all -- {..} $hex {& } hex

	# Add the hex and latin-1 data to the result buffer
	append r $prefix [format %04x $addr] { | } $hex { |} $ascii |\n
	incr addr 16
    }

    # And done
    return $r
}

# # ## ### ##### ######## ############# #####################

namespace eval debug {
    variable detail     ; # map: TAG -> level of interest
    variable prefix     ; # map: TAG -> message prefix to use
    variable suffix     ; # map: TAG -> message suffix to use
    variable fds        ; # map: TAG -> handle of open channel to log to.
    variable header  {} ; # per-line heading, subst'ed
    variable trailer {} ; # per-line ending, subst'ed

    # Notes:
    # - The tag '::' is reserved. "prefix" and "suffix" use it to store
    #   the global message prefix / suffix.
    # - prefix and suffix are applied per message.
    # - header and trailer are per line. And should not generate multiple lines!
}

# # ## ### ##### ######## ############# #####################
## Ready

package provide debug 1.0.5
return