File: dns_browse

package info (click to toggle)
dns-browse 1.6-4
  • links: PTS
  • area: main
  • in suites: potato
  • size: 156 kB
  • ctags: 48
  • sloc: tcl: 612; perl: 610; sh: 152; makefile: 70
file content (804 lines) | stat: -rwxr-xr-x 21,730 bytes parent folder | download | duplicates (2)
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
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
#!/home/johnh/BIN/wish -f

#
# dns_browse
# Copyright (C) 1997 by John Heidemann
# $Id: dns_browse,v 1.22 1997/11/26 17:27:05 johnh Exp $
# 
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
# 

global dns_tree types
set dns_tree "dns_tree"
set maximal_types {A CNAME HINFO MX NS PTR TXT}
set default_types {A CNAME NS TXT}
set user_types {}
set required_types {i iz m mx}
set www_hosts_only 0

proc usage {} {
	puts {usage: gui [-t TYPE ...] starting_domain_name
options:
    -t TYPE show only records of these TYPE (repeat for multiple types)
		(the ``all'' type does everything I know about)
    -w      match only web hosts

Requires dns_tree to be in the path.
}
	exit 1
}

# toggle_window is from nam-1
proc toggle_window w {
	if ![winfo exists $w] { build$w $w }
	global created$w
	if ![info exists created$w] {
		set created$w 1
		wm transient $w .
		update idletasks
		set x [winfo rootx .]
		set y [winfo rooty .]
		incr y [winfo height .]
		incr y -[winfo reqheight $w]
		incr y -20
 		# adjust for virtual desktops
		incr x [winfo vrootx .]
		incr y [winfo vrooty .]
		if { $y < 0 } { set y 0 }
		if { $x < 0 } {
			set x 0
		} else {
			set right [expr [winfo screenwidth .] - \
					   [winfo reqwidth $w]]
			if { $x > $right } {
				set x $right
			}
		}
		wm geometry $w +$x+$y
		wm deiconify $w
	} elseif [winfo ismapped $w] {
		wm withdraw $w
	} else {
		wm deiconify $w
	}
}

#
# formatted_text is stolen from dontspace
# <http://www.isi.edu/~johnh/SOFTWARE/JACOBY/>
# (with permission :-)
#
proc formatted_text {w text} {
	# NEEDSWORK: font selection should be configurable.
	#
	# If you use this code elsewhere, please follow two conscious
	# style choices.  First, wide things are hard to read
	# (50 chars is about the most reasonable---consider newspaper
	# columns).  Second, we allow the user to resize the window.
	# (The user should always have control, even to do stupid things.)
	#
	frame $w.f

	set wt $w.f.t
	text $wt \
		-relief raised -bd 2 -yscrollcommand "$w.f.s set" \
		-setgrid true -wrap word \
		-width 60 -padx 4 -pady 4 \
		-font -*-Times-Medium-R-*-140-*
	set defFg [lindex [$wt configure -foreground] 4]
	set defBg [lindex [$wt configure -background] 4]
	$wt tag configure italic -font -*-Times-Medium-I-Normal-*-140-*
	$wt tag configure computer -font -*-Courier-Medium-R-Normal-*-120-*
	$wt tag configure big -font -*-Times-Bold-R-Normal-*-180-*
	$wt tag configure reverse -foreground $defBg -background $defFg
	pack $wt -side left -expand 1 -fill both

	set ws $w.f.s
	scrollbar $ws -relief flat -command "$w.f.t yview"
	pack $ws -side right -expand yes -fill both

	pack $w.f

	#
	# Scan the text for tags.
	#
	$wt mark set insert 0.0
	set t $text
	while { [regexp -indices {<([^@>]*)>} $t match inds] == 1 } {
		set start [lindex $inds 0]
		set end [lindex $inds 1]

		set keyword [string range $t $start $end]
		# puts stderr "tag $keyword found at $inds"

		# insert the left hand text into the thing
		set oldend [$wt index end]
		$wt insert end [string range $t 0 [expr $start-2]]
		formatted_text_purge_all_tags $wt $oldend insert

		# check for begin/end tag
		if { [string range $keyword 0 0] == "/" } {
			# end region
			set keyword [string trimleft $keyword "/"]
			if { [info exists tags($keyword)] == 0 } {
				error "end tag $keyword without beginning"
			}
			$wt tag add $keyword $tags($keyword) insert
			# puts stdout "tag $keyword added from $tags($keyword) to [$wt index insert]"
			unset tags($keyword)
		} else {
			if { [info exists tags($keyword)] == 1 } {
				error "nesting of begin tag $keyword"
			}
			set tags($keyword) [$wt index insert]
			# puts stdout "tag $keyword begins at [$wt index insert]"
		}

		# continue with the rest
		set t [string range $t [expr $end+2] end]
	}
	set oldend [$wt index end]
	$wt insert end $t
	formatted_text_purge_all_tags $wt $oldend insert
	#
	# Disable the text so the user can't mess with it.
	#
	$wt configure -state disabled
}
proc formatted_text_purge_all_tags {w start end} {
	# remote any bogus tags
	# puts stderr "Active tags at $start are [$w tag names $start]"
	foreach tag [$w tag names $start] {
		$w tag remove $tag $start $end
	}
}

proc build_formatted_text {w t} {
	if [winfo exists $w] { return }
	toplevel $w
	bind $w <Enter> "focus $w"
	wm withdraw $w
	wm iconname $w "nam about"
	wm title $w "nam about"

	frame $w.frame -borderwidth 2 -relief raised
	formatted_text $w.frame $t

	button $w.frame.ok -text " Dismiss " -borderwidth 2 -relief raised \
		-command "wm withdraw $w"
	pack $w.frame.ok -pady 6 -padx 6 -anchor e

	pack $w.frame -expand 1 -fill both
}

proc build.help w {
	build_formatted_text $w {
<big>dns_browse help</big>

The main pane shows a DNS hierarchy with indentation.

+/- in the first column indicates a level which can be expanded or contracted.
+? in the second column indicates a level that can be expanded but hasn't been tried yet.

Button-1 expands or contracts a level of the hierarchy.
Button-2 opens a new window showing only the clicked-on item and its children.
Button-3 prints out some debugging information (but you're not supposed to know that :-).

Multiple zones can be downloaded in parallel, but an in-progress zone cannot be contracted.

Record types:
lower-case records are internal:  i)informational, e)rror messages, iz) internal ``zones'' (hierarchy levels), m)essages.

Plans: clicking on www A/CNAMEs links should invoke a real web browser.

Known bugs: dns_tree (invoked to expand sub-levels) can hang due to bogus servers, not all records are supported.  Changing types and re-displaying a level deosn't change what's displayed.  Zones speaking for things outside of their zone don't work correctly.
	}
}

proc build.about w {
	build_formatted_text $w {
<big>dns_browse</big>

Copyright (c) 1997 by John Heidemann (johnh@isi.edu).

A hack in two movements.

The most recent version should be available at http://www.isi.edu/~johnh/SOFTWARE/DNS/index.html.

<small>
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
</small>
	}
}

proc show_info {w t} {
	global options
	set wid [widget_to_wid $w]
	if {![info exists options($wid,info_next_id)]} {
		set options($wid,info_next_id) 0
	}
	set id [incr options($wid,info_next_id)]
	set options($wid,info_active_id) $id
	$options($wid,info) configure -text $t
	update
	return $id
}

proc show_timed_info_expire {w id} {
	global options
	set wid [widget_to_wid $w]
	if {$options($wid,info_active_id) == $id} {
		show_info ""
	}
}

# flash a message, then hide it after a while
proc show_timed_info {w time text} {
	global options
	set id [show_info $w $text]
	after [expr 1000*$time] "show_timed_info_expire $id"
	update
}

proc widget_to_wid w {
	set second_dot [string first "." [string range $w 1 end]]
	if {$second_dot != -1} {
		set w [string range $w 2 [expr $second_dot]]
	} else {
		set w [string range $w 2 end]
	}
	return $w
}

proc show_all_types wid {
	global maximal_types options
	foreach type $maximal_types {
		set options($wid,show_$type) 1
	}
}

proc build_menu_with_binding {binding_w m label state key ul cmd} {
	$m add command -label $label -state $state -accelerator "^$key" -underline $ul -command $cmd
	bind $binding_w <Meta-$key> $cmd
	bind $binding_w <Control-$key> $cmd
}

proc build_menus {w binding_w dir} {
	global options

	set wid [widget_to_wid $w]

	frame $w.menu -relief groove -bd 2
	pack $w.menu -side top -fill x

	set padx 4

	set mb $w.menu.file
	set m $mb.m
	menubutton $mb -text "File" -menu $m -underline 0 \
		-borderwidth 1 
	menu $m
	build_menu_with_binding $binding_w $m "Open..." disabled o 0 {}
	build_menu_with_binding $binding_w $m "Duplicate" disabled d 0 {}
	build_menu_with_binding $binding_w $m "Close" normal w 0 "after idle {destroy $w}"
	$m add separator
	build_menu_with_binding $binding_w $m "Quit" normal q 0 {exit 0}

	pack $mb -side left -padx $padx

	set mb $w.menu.types
	set m $mb.m
	menubutton $mb -text "Types" -menu $m -underline 0 \
		-borderwidth 1 
	menu $m
	# also UINFO WKS
	global maximal_types
	foreach type $maximal_types {
		$m add checkbutton -label $type -variable options($wid,show_$type)
	}
	$m add separator
	$m add command -label "All" -command "show_all_types $wid"
	pack $mb -side left -padx $padx

	set mb $w.menu.options
	set m $mb.m
	menubutton $mb -text "Options" -menu $m -underline 0 \
		-borderwidth 1 
	menu $m
	$m add checkbutton -label "Hide new iz's" -variable options($wid,hide_new_izs)
	$m add checkbutton -label "Disable safety checks" -variable options($wid,no_safety)
	$m add checkbutton -label "Show only web hosts" -variable options($wid,www_hosts_only)
	pack $mb -side left -padx $padx

	set info $w.menu.info
	set options($wid,info) $info
	label $info -text ""
	pack $info -side left -padx $padx

	set ad $w.menu.ad
	label $ad -text "  dns_browse: $dir  " -relief groove
	pack $ad -side left -padx $padx -expand 1

	set mb $w.menu.help
	set m $mb.m
	menubutton $mb -text "Help" -menu $m -underline 0 \
		-borderwidth 1 
	menu $m
	$m add command -label "Help" -command {toggle_window .help}
	$m add command -label "About dns_browse" -command {toggle_window .about}
	pack $mb -side right -padx $padx
}

proc text_matching_tag {w index head} {
	set depth -1
	foreach tag [$w tag names $index] {
		if [string match "$head*" $tag] {
			set depth [string range $tag [string length $head] end]
			break
		}
	}
	return $depth
}

proc saved_text_insert {w index id} {
	global saved_text
	set t $saved_text($id)

	$w mark set imark $index

	# replay the dump
	set base ""
	foreach {key value index} $t {
		switch -exact $key {
			tagoff {
				if [info exists tags($value)] {
					$w tag add $value $tags($value) imark
					# puts "tagoff $index $value: $tags($value)"
					unset tags($value)
				}
			}
			tagon {
				set tags($value) [$w index imark]
				# puts "tagon $index $value: $tags($value)"
			}
			text {
				$w insert imark $value {}
				# puts "text $index $value"
			}
		}
	}
	# complete any hanging tags
	set search_id [array startsearch tags]
	while {[set i [array nextelement tags $search_id]] != ""} {
		# puts "d-tagoff - $i"
		$w tag add $i $tags($i) imark
	}
	array donesearch tags $search_id
}

proc saved_text_save {w beg end} {
	set t ""
	# first save tags that cross the whole range
	foreach tag [$w tag names $beg] {
		lappend t tagon $tag -
	}
	# then dump the range so we get internal chagnes
	return [concat $t [$w dump $beg $end]]
}

proc swap_line_tag {w linebeg lineend char newtag oldtag} {
	# puts "swap_line_tag: $linebeg $lineend"
	# change the sign of the current line
	# (insert first to preserve tag ranges)
	set len [string length $char]
	$w insert "$linebeg + $len char" $char
	$w delete $linebeg "$linebeg + $len char"
	$w tag remove $oldtag $linebeg $lineend
	$w tag add $newtag $linebeg $lineend
}

proc generate_fqdn {w index} {
	# start on current line, walk backwards
	set fqdn ""
	set beg [$w index "$index lineend"]
	set old_depth [expr [text_matching_tag $w $beg depth]+1]
	while {1} {
		set prev [$w tag prevrange elem $beg]
		if {$prev == ""} {
			break
		}
		set beg [lindex $prev 0]
		set end [lindex $prev 1]
		set new_depth [text_matching_tag $w $beg depth]
		set elem [$w get $beg $end]
		# puts "at $prev: $new_depth $elem ($beg-$end)"
		if {$new_depth >= $old_depth} {
			# only go up, not down or sideways
			continue
		}
		set fqdn "$fqdn.$elem"
		set old_depth $new_depth
	}
	return [string trimleft $fqdn {\.}]
}

proc expand_ns {w beg end base_depth} {
	# first find out the new subdomain
	set fqdn [generate_fqdn $w "$beg lineend"]
	$w mark set imark $end
	catch {
		fill_text $w imark $fqdn $base_depth
	} error
}

proc text_enable w {
	$w configure -state normal
}
proc text_disable w {
	$w configure -state disabled
}

proc act_add_tags {w index tags} {
	# puts "act_add_tags: $w $index $tags"
	text_enable $w
	foreach tag $tags {
		$w tag add $tag "$index linestart" "$index lineend + 1 line linestart"
	}
	text_disable $w
}

proc act_remove_tags {w index tags} {
	text_enable $w
	foreach tag $tags {
		$w tag remove highlight "$index linestart" "$index lineend"
	}
	text_disable $w
}

proc on_target {w index} {
	# puts "on_target $w $index: [$w tag names $index]"
	# sanity
	set on_target [lsearch -exact [$w tag names $index] target]
	$w tag remove target 0.0 end
	if {$on_target == -1} {
		return 0
	}
	return 1
}

proc if_on_target {cmd w index} {
	text_enable $w

	if [on_target $w $index] {
		$cmd $w $index
	}
	
	text_disable $w
}

proc act_plus {w index} {
	text_enable $w

	# find the bounds of the current line
	set linebeg [$w index "$index linestart"]
	set lineend [$w index "$index + 1 line linestart "]
	set depth [text_matching_tag $w $index depth]
	if {$depth == -1} {
		error "act_plus on line without depth"
	}

	# expand it
	set id [text_matching_tag $w $index save]
	if {$id == -1} {
		expand_ns $w $linebeg $lineend $depth
	} else {
		show_info $w "expanding"
		global saved_text
		saved_text_insert $w $lineend $id
		$w tag remove "save$id" $linebeg $lineend
		show_info $w ""
	}

	swap_line_tag $w $linebeg $lineend {- } minus plus
	text_disable $w
}

proc text_<=_depth {w index depth} {
	set beg end
	for {} {$depth >= 0} {incr depth -1} {
		set nextrange [$w tag nextrange "depth$depth" $index]
		if {$nextrange != ""} {
			set nextbeg [lindex $nextrange 0]
			if [$w compare $nextbeg <= $beg] {
				set beg $nextbeg
			}
		}
	}
	return $beg
}

proc act_minus {w index} {
	text_enable $w

	# find the bounds of the current line
	set linebeg [$w index "$index linestart"]
	set lineend [$w index "$index + 1 line linestart"]
	set depth [text_matching_tag $w $index depth]
	if {$depth == -1} {
		error "act_minus on line without depth"
	}

	# find what gets eliminated
	set delbeg [$w index $lineend]
	set delend [text_<=_depth $w $lineend $depth]

	# can't delete active text
	if {[$w tag nextrange expanding $delbeg $delend] != ""} {
		bell
		show_timed_info $w 3 "Cannot compress active trees"
		text_disable $w
		return
	}

	# delete it and save it
	global save_next_id saved_text
	set id [incr save_next_id]
	set saved_text($id) [saved_text_save $w $delbeg $delend]
	$w delete $delbeg $delend
	$w tag add "save$id" $linebeg $lineend

	swap_line_tag $w $linebeg $lineend {+} plus minus
	text_disable $w
}

proc act_new_window {w index} {
	set fqdn [generate_fqdn $w "$index lineend"]
	build_browser $fqdn $w
}

proc build_text w {
	frame $w.text
	set wt "$w.text.text"
	text $wt -relief sunken -bd 2 \
			-xscrollcommand "$w.text.xscroll set" \
			-yscrollcommand "$w.text.yscroll set" \
			-setgrid 1 -height 20 \
			-width 60 \
			-wrap none \
			-font {-*-Courier-Medium-R-*-140-*}
	scrollbar $w.text.xscroll -command "$w.text.text xview" -orient horizontal
	scrollbar $w.text.yscroll -command "$w.text.text yview"
	pack $w.text.xscroll -side bottom -fill x
	pack $w.text.yscroll -side right -fill y
	pack $w.text.text -expand yes -fill both
	pack $w.text -side bottom -expand yes -fill both

	# set up some tags
	$wt tag bind clickable <ButtonPress-1> {act_add_tags %W [%W index {@%x,%y}] target }
	$wt tag bind plus <ButtonRelease-1> {if_on_target act_plus %W [%W index {@%x,%y}] }
	$wt tag bind minus <ButtonRelease-1> {if_on_target act_minus %W [%W index {@%x,%y}] }
	#
	$wt tag bind clickable <ButtonPress-2> {act_add_tags %W [%W index {@%x,%y}] target }
	$wt tag bind plus <ButtonRelease-2> {if_on_target act_new_window %W [%W index {@%x,%y}] }
	$wt tag bind minus <ButtonRelease-2> {if_on_target act_new_window %W [%W index {@%x,%y}] }
	#
	$wt tag bind DEBUG <ButtonRelease-3> {set i [%W index {@%x,%y}]; puts "%W $i [%W tag names $i]"}
	$wt tag configure expanding -font {-*-Courier-Bold-R-*-140-*}
	$wt tag configure target -font {-*-Courier-Bold-R-*-140-*}
#	$wt tag configure ns -font {-*-Courier-Bold-R-*-140-*}

	return $wt
}

proc fill_text_line {w place line base_depth} {
	if {![regexp "^(\t*)(\[^\t\]+)\t+(\[^\t\]+)(.*)$" $line dummy new_tabs type value rest]} {
		error "fill_text_line: $line"
	}
	set new_depth [string length $new_tabs]
	set depth [expr $base_depth+$new_depth]
	set wtags {}
	switch -exact $type {
		m  {set ch "! "; set tags message }
		mx {set ch "! "; set tags {message expanding} }
		z  {set ch "  "; set tags {}; set wtags elem }
		NS {set ch "+?"; set tags {clickable plus ns}; set wtags elem }
		iz {set ch "- "; set tags {clickable minus iz}; set wtags elem }
		default {set ch "  "; set tags {}}
	}
	if {$base_depth > 0 && $new_depth == 0 && $type == "z"} {
		return 0
	}
	# puts "$depth $line"
	lappend tags depth$depth DEBUG
	set wtags [concat $tags $wtags]
	set base_tabs [string range "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" 1 $base_depth]
	$w insert $place "$ch$base_tabs$new_tabs$type\t" $tags \
			$value $wtags \
			"$rest\n" $tags
	return 1
}

proc fill_text_background {w index base_depth f iid} {
	text_enable $w
	global insertion_count
	while {1} {
		gets $f line
		if {$line == ""} {
			if [fblocked $f] {
				# no more input
				text_disable $w
				return
			}
			if [eof $f] {
				break
			}
			# empty line
		}
		incr insertion_count($iid) [fill_text_line $w $index $line $base_depth]
	}

	# eof
	catch { close $f }
	if {$insertion_count($iid) == 0} {
		fill_text_line $w $index "\te\tno-ouptut" $base_depth
	}

	# take down the message and fix the tags
	$w delete iend$iid "iend$iid + 1 line linestart"

	# apply options over ibeg$iid, iend$iid
	global options
	set wid [widget_to_wid $w]
	if {$options($wid,hide_new_izs)} {
		set index ibeg$iid
		while {[$w compare $index < iend$iid] != 0} {
			if {[lsearch -exact [$w tag names $index] iz] != -1} {
				act_minus $w $index
			}
			set index [$w index "$index + 1 line"]
		}
	}

	text_disable $w
}

proc fill_text {w index dir base_depth} {
	global dns_tree insertion_next_id insertion_count options maximal_types
	set iid [incr insertion_next_id]
	set insertion_count($iid) 0

	set wid [widget_to_wid $w]
	set opts ""
	if {$options($wid,no_safety)} {
		set opts "$opts -f"
	}
	if {$options($wid,www_hosts_only)} {
		set opts "$opts -m www"
	}
	foreach type $maximal_types {
		if {$options($wid,show_$type)} {
			set opts "$opts -t $type"
		}
	}
	set f [open "| $dns_tree $opts $dir" r]

	fconfigure $f -blocking false
	# set up insertion marker
	$w mark set ibeg$iid $index
	$w mark gravity ibeg$iid left
	$w mark set iend$iid $index
	$w mark gravity iend$iid left
	fill_text_line $w $index "\tmx\texpanding $dir" $base_depth
	$w mark gravity iend$iid right
	# asynchronously fill in text
	fileevent $f readable "fill_text_background $w iend$iid $base_depth $f $iid"
}

proc build_browser {dir old_w} {
#	set w [toplevel ".$dir"]
	global window_next_id
	set wid [incr window_next_id]
	set w [toplevel ".w$wid"]
	wm iconname $w $dir
	wm title $w "dns_browse: $dir"

	# set options
	global options user_types required_types maximal_types www_hosts_only
	if {$old_w == ""} {
		set options($wid,hide_new_izs) 1
		set options($wid,www_hosts_only) $www_hosts_only
		set options($wid,no_safety) 0
		global maximal_types
		foreach type $maximal_types {
			set options($wid,show_$type) 0
		}
		foreach type $user_types {
			set options($wid,show_$type) 1
		}
		foreach type $required_types {
			set options($wid,show_$type) 1
		}
	} else {
		set old_wid [widget_to_wid $old_w]
		foreach key [array names options] {
			if [string match "$old_wid,*" $key] {
				set part [string range $key [expr [string length $old_wid]+1] end]
				set options($wid,$part) $options($key)
			}
		}
	}

	set tw [build_text $w]
	build_menus $w $tw $dir
	bind $w <Enter> "focus $tw"

	# (set up an insertion mark)
	$tw mark set imark 0.0
	$tw mark gravity imark right

	fill_text $tw imark $dir 0
}

proc main {} {
	global argv
	global save_next_id insertion_next_id window_next_id
	set save_next_id 0
	set insertion_next_id 0
	set window_next_id 0

	wm withdraw .

	# option processing
	global user_types maximal_types default_types www_hosts_only
	if {[llength $argv] < 1} {
		usage
	}
	while {[string index [lindex $argv 0] 0] == "-"} {
		set optc [lindex $argv 0]
		set argv [lrange $argv 1 end]
		if {[llength $argv] > 1} {
			set optarg [lindex $argv 0]
		} else {
			set optarg {}
		}
		switch -exact -- $optc {
			-t	{
				lappend user_types $optarg
				set argv [lrange $argv 1 end]
			}
			-w	{
				set www_hosts_only 1
			}
			default { usage }
		}
	}
	if {$user_types == "all"} {
		set user_types $maximal_types
	}
	if {$user_types == ""} {
		set user_types $default_types
	}

	# argument processing
	foreach name $argv {
		build_browser $name {}
	}
}

main