#!/usr/local/bin/wish8
#!/space/home/jc/bin/wish8
# NAME
# HTML - Display HTML text in a window
#
# SYNOPSIS
# HTML [file | URL]...
#
# DESCRIPTION
# This is a prototype HTML window. It is highly experimental at
# present. Use it at your own risk (or confusion). The main reason
# for this program is to have a "debug" version of an HTML viewer.
# The emphasis here is on informativeness, not fancy features.
#
# This program pops up a text window and fills it with the contents
# of a file. Files may be given as local file names or as URLs,
# which will be read using the w3cat(x) program as a subprocess.
# This program thus functions much like a web browser, but with
# lots of hooks to help debug HTML applications.
#
# If there are names on the command line, they are loaded into the
# "History" menu, and the first is loaded as the "current" file.
# The others may be loaded by using the History menu.
#
# As a special goodie, the command-line arg:
# T=foo
# says that we are the handler for the target=foo window. We set
# our visible name to H_foo so that others can send to us.
#
# CONFIG
# We read a config file, which should be called .htmlinit, and may
# be anywhere in the search path. It should be a wish script. It
# -fg can do anything, of course, but the recommended use is for
# setting global variables.
#
# OPTIONS
# There are a few, mostly kept in the opt() array. You might want
# to scan this script for opt(...) to get ideas.
#
# opt(TABLE)
# This is a value that says whether to handle
and related
# tags. This is not fully implemented yet. 1 means to use the
# code that just uses basic tk functions; 2 means to use the
# tkTable package.
#
# opt(ALT)
# If true, we will show the ALT= attribute for all images, as a
# label widget under the image. The default opt(ALT)==0 means
# that we show the ALT string initially, and replace it with the
# image when we get it.
#
# opt(IMG)
# If true, will attempt to load images and display them. If
# false, only the ALT text will be shown for the images. This is
# useful in testing whether a page will work for text-only
# browsers. The default is true.
#
# opt(TARGET)
# If true, will open new windows for URLs with TARGET attributes.
# The default is false, which means to show all documents in the
# single (_top) window.
#
# INITIALIZATION
# This program looks through the search path for .htmlinit, and
# sources the first such file that is found. This is done after our
# basic initialization of globals, so it can override any of them.
#
# REQUIRES
# Various parts of this program have been split off into separate
# modules, some of which run as separate processes. See the list of
# Source commands below. You should find all of them in the same
# directory where you found this file. Make sure that you put them
# into a directory that is in your search path, so we can find them
# at run time.
#
# H_cache is used as a separate process for maintaining a cache of
# images. If we can't send messages to H_cache, we will start one.
# This reqires that the send command be usable with your wish,
# which is sometimes a tricky setup. If you can't make it work, try
# recompiling your tk library with the SECURITY stuff disabled. And
# describe your problems to the comp.lang.tcl newsgroup. This isn't
# pretty, but sometimes that's all you can do.
#
# The w3cat program is used to fetch files from web servers. It is
# written in perl, so you'll need that language, too, if you want
# to access documents via the web.
#
# BUGS
# We haven't yet implemented all the bizarre features that some of
# the web vendors have foisted on us as "HTML". In fact, we don't
# quite have all of HTML 3.0 working yet. (That's why tables are
# turned off by default.) Stay tuned ...
#
# SEE ALSO
# w3cat(x)
# H_cache(x)
#
# AUTHOR
# Copyright 1998 by John Chambers
# You are free to use this code however you wish, as long as you 1)
# don't claim you wrote it; 2) don't try to sell it; and 3) send me
# copies of any significant additions you make.
set myname [wm title .]
set ME [lindex $myname 0]
if ![regsub {.*/} $ME {} me] {set me $ME}
if [info exists env(V_$me)] {set V $env(V_$me)} else {set V 1}
if [info exists env(B_$me)] {set B $env(B_$me)} else {set B 0}
if [info exists env(F_$me)] {set F $env(F_$me)} else {set F 5}
if [info exists env(R_$me)] {set R $env(R_$me)} else {set R ridge}
if {$V>1} {puts "$me: Started with V=$V."}
set D $V; # Obsolete debug flag.
set Target _top; # Target window name.
set Doc .f.d; # Top-level document widget.
set title {HTML viewer}
wm title . $title
set widget text; # Use canvas or text for main document.
set URLs {}; # URL list from command line.
set useH_cache 1; # Whether to send to H_cache.
set Hist {}; # History array (URLs).
#et curL {}; # Currend link's URL.
#et curN {}; # Currend widget's name.
#et curW {}; # Current document's window.
set curX 0; # Current X position of pointer.
set curY 0; # Current Y position of pointer.
# tk_setPalette grey60
if [info exists env(EDITOR)] {set E $env(EDITOR)} else {set E vi}
if [info exists env(TMPDIR)] {set Tmpdir $env(TMPDIR)} else {set Tmpdir /tmp}
if [info exists env(IMGDIR)] {set Imgdir $env(IMGDIR)} else {set Imgdir /tmp}
# Symbols used when creating widgets:
set BB {-padx 0 -pady 0 -highlightthickness 0 -bd $F -relief raised}
set MB {-padx 0 -pady 0 -highlightthickness 0 -bd $F -relief ridge}
set EB {-highlightthickness 0 -width 0 -bd $B -relief $R}
set FB {-bd $B -relief $R}
set IB {-bd $B -relief $R}
# The C() array holds global color names:
set C(bg) grey50 ;set Dsc(C:bg:title) {Document background color}
set C(fg) green ;set Dsc(C:fg:title) {Document foreground color}
set C(hl) cyan1 ;set Dsc(C:l1) {Hyperlink color}
set C(vl) cyan2 ;set Dsc(C:l2) {Visited link color}
set C(nm) orange ;set Dsc(C:nm) {HTML name color}
set C(E) yellow ;set Dsc(C:E) {Variable entry color}
set C(fg:title) white ;set Dsc(C:fg:title) {Title foreground color}
set C(bg:title) grey50 ;set Dsc(C:bg:title) {Title background color}
set C(T1) grey50 ;set Dsc(C:T1) {Table border color}
set C(T2) grey50 ;set Dsc(C:T2) {Table cell spacing color}
set C(T3) grey50 ;set Dsc(C:T3) {Table cell border color}
set mydir [file dirname [info script]]
if {$V>1} {puts "$me: mydir=\"$mydir\""}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# Default values for assorted global variables:
set bufsiz 1000
set section {}
set Ftag 3vsr
set Ltag Lnorm
set Lnum Lnk0
set LC { }; # Last char in window.
#et in(A) 0; # Whether we are in aset in(Aed (linked) section.
#et in(BODY) 0; # Whether we are in a ... section.
#et in(FONT) 0; # Whether we are in a ... section.
#et in(HEAD) 0; # Whether we are in a ... section.
#et in(H) 1; # Whether we are treating current text as HTML.
#et in(HDR) 0; # Whether we are in the header section of a document.
#et in(PLAINTEXT) 0; # Whether we are in a ... section.
#et in(PRE) 0; # Whether we are in a ...
section.
#et in(SCRIPT) 0; # Whether we are in a section.
#et in(TABLE) 0; # Whether we are in a section.
#et in(TCL) 0; # Whether we are processing embedded TCL code.
#et in(comment) 0; # Whether we are in a comment section.
#et in(except) 0; # Exception to HTML is in effect (PRE or SCRIPT)
set in(TITLE) 0; # Are we inside a tag?
set SP(TITLE) {}
set TxtLen(TITLE) 0
set N(HR) 0; # Counter for HR widgets.
set N(TARGET) 0; # Counter for target windows.
set isRmt 0; # True if current page is remote file.
set mark 0
set ll 0; # List level
set lm1 0; # Left margin for first lines.
set lm2 0; # Left margin for other lines.
set Link(0) {}
set L(T0) 0
set L(N0) 0
set L(i0) 0
set L(i1) [set ts 25]
set L(i2) [expr 2*$ts]
set TS(li0) {}
set TS(ll0) 0
set Mtab Mtab0 ;# Current mark for tabbing.
set scripts 0 ;# Number of scripts defined.
set off(X) 10 ;# X offset for popups
set off(Y) 10 ;# U offset for popups
set opt(_) 0 ;set Dsc(opt:_) {If true, show spaces as underscores}
set opt(B) 1 ;set Dsc(opt:B) {Widget border widths}
set opt(F) 1 ;set Dsc(opt:F) {Frame border widths}
set opt(ALT) 1 ;set Dsc(opt:ALT) {If true, always show ALT values}
set opt(BASEFONT) 3 ;set Dsc(opt:BASEFONT) {Starting font for documents}
set opt(FONTSIZE) 1 ;set Dsc(opt:FONTSIZE) {If true, accept font-size attributes}
set opt(IMG) 1 ;set Dsc(opt:IMG) {If true, attempt to show images}
set opt(H) 0 ;set Dsc(opt:HTML) {Whether to assume that new text is HTML}
set opt(MOTIF) 0 ;set Dsc(opt:MOTIF) {Act as much like MOTIF as possible}
set opt(Fonts) 0 ;set Dsc(opt:Fonts) {Whether to show the Fonts window}
set opt(Option) 0 ;set Dsc(opt:Option) {Whether to show the Option window}
set opt(Source) 0 ;set Dsc(opt:Source) {Whether to show the Source.tclindow}
set opt(Header) 0 ;set Dsc(opt:Header) {Whether to show the Header window}
set opt(POPUP) 1 ;set Dsc(opt:POPUP) {How to display popups}
set opt(TARGET) 0 ;set Dsc(opt:TARGET) {Whether to honor TARGET attributes}
set opt(TABLE) 0 ;set Dsc(opt:TABLE) {How to handle tables}
set opt(Width) 0 ;set Dsc(opt:Width) {Default width (chars) of main document}
set opt(Height) 0 ;set Dsc(opt:Height) {Default height (chars) of main document}
set opt(TCL) 0 ;set Dsc(opt:TCL) {Whether to eval tcl inclusions}
set opt(TTL) 0 ;set Dsc(opt:TITLE) {Whether to show titles in window in info area}
set opt(URL) 1 ;set Dsc(opt:_URL_) {Whether to show URL widget in info area}
set opt(MSG) 1 ;set Dsc(opt:_MSG_) {Whether to show message widget in info area}
set opt(LNK) 0 ;set Dsc(opt:_LNK_) {Whether to show hyperlink URLs in info area}
set opt(NAM) 0 ;set Dsc(opt:_NAM_) {Whether to show widget names in info area}
if {$V>1} {set opt(Debug) 1} else {set opt(Debug) 0}
set Dsc(opt:Debug) {Whether to show the Debug window}
set Dsc(var:V) {Debug/verbose level}
set Dsc(var:B) {Debug border widths}
set Dsc(var:F) {Debug frame widths}
set Dsc(var:R) {Debug border relief}
if [info exists env(MOTIF)] {set opt(MOTIF) $env(MOTIF)}
set Link(sel) {}
set doctype {}
set subtype {}
set imageViewer {wrapper xv -}
set imageBufsiz 1000
set imagePipe {}
set in(IMAGE) 0
#et UE(\ ) +
#et UE(\") %22
#et UE(#) %23
#et UE(%) %25
#et UE(&) %26
#et UE(+) %2B
#et UE(/) %2F
#et UE(:) %3A
#et UE(\;) %3B
#et UE(<) %3C
#et UE(=) %3D
#et UE(>) %3E
#et UE(?) %3F
#et UE(@) %40
#et UE(\[) %5B
#et UE(\\) %5C
#et UE(\]) %5D
#et UE(^) %5E
#et UE(`) %6D
#et UE(\{) %7B
#et UE(|) %7C
#et UE(\}) %7D
#et UE(~) %7E
if {$V>1} {puts "$me: after sets."}
# Border widths for some widgets:
set bd(DB) $B; # Debug borders.
set bd(IMG) 0; # Inline image borders.
set bd(LNK) 0; # Hyperlink borders.
set bd(T1) 1; # Table outer border width.
set bd(T2) 1;
set bd(T3) 1;
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# Load in some useful packages. We can't use Source until we've loaded it, so #
# the first line here is a bit complicated. Then Loadfile takes care of it #
# for us. #
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
set initfile {}
set PATH [split $env(PATH) :]
foreach d $PATH {if [file exists $d/Source.tcl] {source [set Path(Source.tcl) $d/Source.tcl];break}}
foreach d $PATH {
if [file readable $d/.htmlinit] {source [set Path([set initfile .htmlinit]) $d/.htmlinit]; break}
if [file readable $d/.htmlrc ] {source [set Path([set initfile .htmlrc ]) $d/.htmlrc]; break}
}
if {$V>1} {puts "$me: After loading init file \"$initfile\" ($Path($initfile))"}
Source pvar.w
Source Help.w
Source Verbose.w
Source forAllMatches.w
Source txtWin.w
Source Misc.w
Source H_err
Source H_find
Source H_font
Source H_form
Source H_html
Source H_tag
Source H_load
Source H_select
Source H_table
Source H_url
Source H_chars
Source H_link
Source H_mark
Source H_menu
Source H_size
Source H_text
Source H_wait
Source H_win
FontList
FontInit
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
if {$V>1} {puts "$me: before iconbitmap."}
foreach d [split $env(PATH) :] {
if [file exists $d/$me.xbm] {
if {$V>1} {puts "$me: Bitmap file \"$d/$me.xbm\""}
if [catch {image create bitmap rooticon -file "$d/$me.xbm"} x] {
Msg "Can't create image from file \"$d/$me.xbm\""
}
wm iconbitmap . @$d/$me.xbm
break
}
}
if {$V>1} {puts "$me: after iconbitmap."}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# We read in our init file last, so that everything above this point may be #
# treated as "defaults" that may be overridden by the config file. Also, if #
# there is a .htmlsettings file, we load it last, and we'll use it as a sort #
# of "dynamic" way of setting options. See LoadSettings and SaveSettings for #
# more details. #
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
LoadSettings
if {$opt(MOTIF)} {set tk_strictMotif 1}
if {$V>1} {puts "$me: After tk_strictMotif."}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# Set up a new font for the named widget (which should be a text widget). The
# args can all be null, in which case we default them to the current values
# for $w at the current font level. The global variable Ftag holds the current
# font code for the current text widget. The Ftags() array holds the font
# codes for each font-level+text-widget combination. All of this is to take
# care of the fact that font-related tags may be nested. For font level fl
# in the text widget w, we remember font info with the following:
# Fsize($fl$w) The font's HTML size, 1-7.
# Fspce($fl$w) The font's spacing: v=variable f=fixed.
# Fslnt($fl$w) The font's HTML slant: r=roman, i=italic.
# Fwght($fl$w) The font's HTML weight: m=medium, b=bold
# We may need to augment some of these eventually.
proc PushFont {size spce slnt wght} {
global V me curD Fsize Fspce Fslnt Fwght in opt
if {$V>1} {set id "$me/PushFont"}
if {$V>2} {puts "$id: curD=\"$curD\" size=$size spce=$spce slnt=$slnt wght=$wght."}
if [info exists in(FONT)] {set l2 $in(FONT)} else {set l2 [set in(FONT) 0]}
if {!$opt(FONTSIZE)} {set size 3}
set l1 $in(FONT)
set l2 [incT FONT]
set ff "$l2$curD"
if {$V>2} {puts "$id: l1=$l1 l2=$l2 ff=\"$ff\""}
# Get txt's values or defaults for any null args:
if {$size == {}} {if [info exists Fsize($l1$curD)] {set size $Fsize($l1$curD)} else {set size 3}}
if {$spce == {}} {if [info exists Fspce($l1$curD)] {set spce $Fspce($l1$curD)} else {set spce v}}
if {$slnt == {}} {if [info exists Fslnt($l1$curD)] {set slnt $Fslnt($l1$curD)} else {set slnt r}}
if {$wght == {}} {if [info exists Fwght($l1$curD)] {set wght $Fwght($l1$curD)} else {set wght m}}
if {$V>2} {puts "$id: size=$size spce=$spce slnt=$slnt wght=$wght for $ff."}
SetFont $curD $l2 $size $spce $slnt $wght
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
proc PopFont {} {
global V me curD Fsize Fspce Fslnt Fwght in opt
if {$V>1} {set id "$me/PopFont"}
if {$V>2} {puts "$id: curD=\"$curD\""}
set l1 $in(FONT)
set l2 [decT FONT]
set ff "$l2$curD"
if {$V>2} {puts "$id: l1=$l1 l2=$l2 ff=\"$ff\""}
if [info exists Fsize($ff)] {set size $Fsize($ff)} else {set size 3}
if [info exists Fspce($ff)] {set spce $Fspce($ff)} else {set spce v}
if [info exists Fslnt($ff)] {set slnt $Fslnt($ff)} else {set slnt r}
if [info exists Fwght($ff)] {set wght $Fwght($ff)} else {set wght m}
if {$V>2} {puts "$id: size=$size spce=$spce slnt=$slnt wght=$wght for $ff."}
SetFont $curD $l2 $size $spce $slnt $wght
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# Set all the font info for text widget t level l. This routine assumes that
# all the parameters are valid; they were calculated in PushFont and PopFont..
proc SetFont {t l size spce slnt wght} {
global V me Fsize Fspce Fslnt Fwght Ftag Ftags FF gotfont
global FontFoundry FontFamily FontHeight
set id "$me/SetFont"
set ff "$l$t"
if {$V>2} {puts "$id: t=\"$t\" l=$l size=$size spce=$spce slnt=$slnt wght=$wght."}
set Ftag "$size$spce$slnt$wght"
set Fsize($t) [set Fsize($ff) $size]; # HTML size (1-7)
set Fspce($t) [set Fspce($ff) $spce]; # Spacing: v=variable f=fixed
set Fslnt($t) [set Fslnt($ff) $slnt]; # Slant: s=straight i=italic
set Fwght($t) [set Fwght($ff) $wght]; # weight: n=normal b=bold
set Fdsc "-family $FF(fmly$spce) -weight $FF(wght$wght) -slant $FF(slnt$slnt) -size $FF(size$size)"
if {$V>2} {puts "$id: Ftag=\"$Ftag\""}
if ![info exists gotfont($Ftag)] {
if {$V>2} {puts "$id: FONT $Ftag $Fdsc"}
eval font create F$Ftag $Fdsc
set gotfont($Ftag) 1
} else {
if {$V>2} {puts "$id: FONT $Ftag is \"[font config F$Ftag]\""}
}
if {$V>2} {puts "$id: $t tag config F$Ftag -font F$Ftag"}
$t tag config F$Ftag -font F$Ftag
set Ftags($ff) F$Ftag
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
proc BR w {
global V me in LC wantNL curD
if {$LC == "\n"} {
if {$V>2} {puts "$me/BR: Ignored because LC is a newline."}
return
}
if {$in(PRE) > 0} {
if {$wantNL($curD) < 1} {
if {$V>2} {puts "$me/BR: Call Hnl because in(PRE)=$in(PRE) and wantNL($curD)=$wantNL($curD)."}
Hnl 1
}
} else {
if {$V>2} {puts "$me/BR: Call Hnl because in(PRE)=$in(PRE)."}
Hnl 1
}
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
proc ButtonBindings {w f l} {
global V me
if {$V>2} {puts "$me/ButtonBindings: w=\"$w\" f=\"$f\" link $l"}
bind $f "LinkButton1 $w $l %X %Y %W"
bind $f "LinkShiftB1 $w $l %X %Y %W"
bind $f "LinkButton3 $w $l %X %Y %W"
bind $f "LinkEnter $f $l %X %Y %W"
bind $f "LinkLeave $f $l %X %Y %W"
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# Clicking button 1 over anything associated with link n causes us to load #
# the link's URL into the text widget w. This routine is bound to button 1 in #
# several places, all of them leading here when the user clicks the button. #
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
proc LinkButton1 {txt n X Y W} {
global C V me Link curW curX curY target
set curX $X
set curY $Y
if {$V>1} {puts "LinkButton1: Click Link $n \"$Link($n)\" in $W at $X,$Y"}
if [regexp -nocase {^mailto:[ ]*"*(.*)"*$} $Link($n) {} rcpt] {
if {$V>1} {puts "LinkButton1: MAILTO rcpt=\"$rcpt\""}
exec M_EditSend $rcpt &
return
}
set U [url2URL $Link($n)]
if {$V>1} {puts "LinkButton1: U=\"$U\""}
if [winfo exists .doclink$n] {destroy .doclink$n}
if ![info exists target($n)] {set target($n) {}}
if [regexp {(.*)\.f\.d$} $txt {} w] {
LoadFile "$w" "$target($n)" "$U" GET
} elseif [regexp {(.*)\.d$} $txt {} w] {
LoadFile "$w" "$target($n)" "$U" GET
} else {
if {$V>1} {puts "LinkButton1: Can't determine parent window for \"$txt\""}
LoadFile "$curW" "$target($n)" "$U" GET
}
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# Button 3 over a link gives a menu of operations that can be done using the #
# link's URL as a parameter. #
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
proc LinkButton3 {txt n X Y W} {
global C V me Link curX curY
set curX $X
set curY $Y
if {$V>1} {puts "$me/LinkButton3: Click Link $n in $txt \"$Link($n)\""}
$txt tag config Lnk$n -foreground $C(vl)
if [winfo exists .b3menu] {destroy .b3menu}
menu .b3menu -tearoff 0
.b3menu add command -label {Copy URL to Clipboard} -command "Link2Cut $n"
.b3menu add command -label {Load to new window} -command "Link2Win $txt $n %X %Y"
.b3menu add command -label {Load to file} -command "Link2File $txt $n %X %Y"
if {$X && $Y} {tk_popup .b3menu $X $Y}
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# The Shift-B1 combination causes a new window to be started up for the link. #
# This can also be done by using B3's "Load to new window" menu item. #
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
proc LinkShiftB1 {txt n X Y W} {
global C V me Link curX curY
set curX $X
set curY $Y
if {$V>1} {puts "$me/LinkShiftB1: Click Link $n in $txt \"$Link($n)\""}
Link2Win $txt $n %X %Y
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# Here's a cute way to export data to the server's cut buffer: We create .sel #
# as a non-displayed widget, fill it with the data, and declare that it owns #
# the selection. Even though .sel never appears on the screen, we can paste #
# from it to another window. #
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
proc Link2Cut {n} {
global V me Link
if ![winfo exists .sel] {text .sel}
selection own .sel
.sel delete 1.0 end
if {$V>4} {puts "$id: .sel insert end \"$Link($n)\" sel"}
.sel insert end $Link($n) sel
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# Download a link into a file.
proc Link2File {txt n X Y} {
global C V me Link curX curY
set curX $X
set curY $Y
if {$V>1} {puts "Link2File: Load Link $n {$Link($n)}"}
$txt tag config Lnk$n -foreground $C(vl)
set url [url2URL $Link($n)]
set forkcmd "w3cat $url"
if {$V>1} {puts "Link2File: forkcmd=\"$forkcmd\""}
eval exec $forkcmd &
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# Fork off a copy of this program to do a new URL.
proc Link2Win {txt n X Y} {
global C V me Link curX curY
set curX $X
set curY $Y
if {$V>1} {puts "Link2Win: Click Link $n {$Link($n)}"}
$txt tag config Lnk$n -foreground $C(vl)
set url [url2URL $Link($n)]
set forkcmd "$me $url"
if {$V>1} {puts "Link2Win: forkcmd=\"$forkcmd\""}
eval exec $forkcmd &
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# Canonicalize a directory name. We reduce multiple slashes, and strip out
# uses of "/../". Maybe we should also delete initial "./".
proc Dir {d} {
global V me
if {$V>5} {puts "$me/Dir: Reducing: \"$d\""}
regsub -all {//+} $d / d
while [regsub {[^/]+/\.\./} $d {} d] {
if {$V>5} {puts "$me/Dir: Edited to \"$d\""}
}
if {$d == {}} {set d {./}}
if {$V>5} {puts "$me/Dir: Returned: \"$d\""}
return $d
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# Add a message to the Debug window. We also check to see if the scrollbar is #
# positioned at the bottom, and if so, we scroll to keep the bottom visible. #
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
proc Msg e {
global V me errmsg
set id "$me/Msg"
if {$V>1} {puts "$id: \"$e\""}
set errmsg $e
set tsy1 [lindex [set tsy [.dbg.txt.s.y get]] 1]
if {$V>4} {puts "$id: .dbg.txt.t insert end \"$e\\n\""}
.dbg.txt.t insert end $e\n
if {$tsy1 == 1.0} {
if {$V>1} {puts "$id: Scroll .dbg.txt.t to bottom"}
.dbg.txt.t yview moveto 1.0
}
update idletasks
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# Nesting tags need to be closed. For the optional cases, don't put an entry
# here, and we will simply ignore the end tags.
set Ntag {A B BIG BL BODY CENTER DIV DL EM FONT FORM H1 H2 H3 H4 H5 H6 HEAD HTML I OL OPTION PRE SCRIPT SELECT STRONG TABLE TCL TITLE UL comment}
proc NtagInit {} {
global in N Ntag
foreach t $Ntag {
set in($t) 0; # How deep we are nested within this tag.
set N($t) 0; # Count of times this tag has been encountered.
}
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# Note which section of the HTML document (head, body) we are in. This is set
# up as a proc for use in debugging; all we really do is save s in the global
# section variable..
proc Section s {
global V me section
if {$V>1} {puts "$me: Section <$s> (was <$section>)"}
set section $s
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# This does some simple-minded URL entity encoding.
proc URL {s} {
global V me
if {$V>4} {puts "$me/URL \"$s\""}
set v {}
while {[regexp {^(.*)([][;/?:@=&+<>\\#%{|}\\^~` ])(.*)$} $s {} init c tail]} {
if {$V>6} {puts "$me/URL: {$init} {$c} {$tail}"}
scan $c %c i
set h [format %02X $i]
set v "%$h$tail$v"
set s $init
}
if {$V>4} {puts "$me/URL: \"$s$v\" returned."}
return "$s$v"
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# For a target window, we fill it up with the appropriate widgets. #
# This is a bit ad hoc at present and we might well to do a bit of #
# reorganizing here if things eventually settle down. Note that for #
# the top-level target _top, the window name is actually null, with #
# "." used as an ad-hoc placeholder. #
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
proc TargetWin {w t} {
global V me B BB C F MB R SP widget
global curB curD curF curI curL curN curW namN Doc
global bd target Tfont Txt txtlvl Lbgn Lfgn Lfgl opt URLt
set id "$me/TargetWin"
if {$V>1} {puts "$id: Build \"$w\" target window."}
if {$w != {}} {
if ![winfo exists $w] {
if {$V>1} {puts "$id: Create toplevel window \"$w\""}
toplevel $w
set Doc $w
}
}
set curW $w
bind $w "EnterW $w %X %Y"
#
set curB $w.b
if {$V>1} {puts "$id: Create $curB to hold buttons."}
if [winfo exists $curB] {destroy $curB}
frame $curB -bd 3 -relief ridge; # Holds folder selection menus/buttons
#
set curI $w.i
if {$V>1} {puts "$id: Create $curI to hold information."}
if [winfo exists $curI] {destroy $curB}
frame $curI -bd $F -relief $R; # Holds information widgets.
#
set curF $w.f
if {$V>1} {puts "$id: Create $curF to frame document."}
frame $curF -bd $B -relief $R
frame $curF.s -bd 0; # Vertical scrollbar
#
set curD $curF.d
if {$V>1} {puts "$id: Create $curD to hold document."}
if {[set hght $opt(Height)] < 1} {set hght 20}
#
if ![info exists bd($curF)] {set bd($curF) $B}
if ![info exists bd($curD)] {set bd($curD) $B}
#
switch $widget {
text {
if {$V>1} {puts "$id: Create $curD to hold document in text widget."}
text $curD -tabs {4m left} \
-yscrollcommand "$curF.s.y set" \
-xscrollcommand "$curF.sbx set" \
-wrap word -bd $bd($curD) -relief $R \
-width $opt(Width) -height $hght -highlightthickness 0
HSP $curD {}
}
canvas {
if {$V>1} {puts "$id: Create $curD to hold document in canvas widget."}
canvas $curD -tabs {4m left} \
-yscrollcommand "$curF.s.y set" \
-xscrollcommand "$curF.sbx set" \
-wrap word -bd $bd($curD) -relief $R \
-width $opt(Width) -height $hght -highlightthickness 0
HSP $curD {}
}
default {
puts "$id: ### Unknown widget \"$widget\" ###"
exit 1
}
}
scrollbar $curF.s.y -command "$curD yview" -bd 1 -width 8 -orient vertical
scrollbar $curF.sbx -command "$curD xview" -bd 1 -width 8 -orient horizontal
button $curF.s.x -command "Load {$w}" -bd 1 -text * \
-padx 0 -pady 0 -highlightthickness 0
pack $curF.s.x -in $curF.s -side top -fill x
pack $curF.s.y -in $curF.s -side bottom -fill y -expand 1
pack $curF.sbx -in $curF -side top -fill x
pack $curF.s -in $curF -side left -fill y
pack $curD -in $curF -side left -fill both -expand 1
BindHelp $curF.s.x Reload
SetFont $curD 0 3 v r m
#
set Lbgn [lindex [$curD config -background] 4]
set Lfgn [lindex [$curD config -foreground] 4]
set Lfgl cyan
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# The globals curW and curD contain the names of the current window #
# and doc widget. This is one of the most important things to #
# understand here (and often one of the trickiest points to get #
# right). Also, we map the HTML target="_top" to our main window #
# here. However, at present we don't do much with targets. #
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
set target(_top) $curF
set Txt([set txtlvl 0]) $curD
if {$V>1} {puts "$id: curD=Txt($txtlvl)={$curD} target(_top)=\"$target(_top)\""}
#
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
if ![winfo exists $curB.bCmd] {
eval menubutton $curB.bCmd -text Cmds -menu $curB.bCmd.menu -padx 0 -pady 0 $MB
bind $curB.bCmd {menuCmd $curW %X %Y}
pack $curB.bCmd -in $curB -side left
set namN($curB.st.bCmd) {Commands}
menuCmd $curW 0 0
}
if ![winfo exists $curB.bOpt] {
eval menubutton $curB.bOpt -text Opts -menu $curB.bOpt.menu -padx 0 -pady 0 $MB
bind $curB.bOpt "menuOpt {$w} %X %Y"
pack $curB.bOpt -in $curB -side left
set namN($curB.st.bOpt) {Options}
menuOpt $w 0 0
}
if ![winfo exists $curB.bWin] {
eval menubutton $curB.bWin -text Wins -menu $curB.bWin.menu -padx 0 -pady 0 $MB
bind $curB.bWin {menuWin $curW %X %Y}
pack $curB.bWin -in $curB -side left
set namN($curB.st.bWin) {Control windows}
menuWin $curW 0 0
}
if ![winfo exists $curB.bHst] {
eval menubutton $curB.bHst -text Hist -menu $curB.bHst.menu -padx 0 -pady 0 $MB
bind $curB.bHst {menuHst L $curW %X %Y}
bind $curB.bHst {menuHst L $curW %X %Y}
bind $curB.bHst {menuHst H $curW %X %Y}
bind $curB.bHst {menuHst H $curW %X %Y}
pack $curB.bHst -in $curB -side left
set namN($curB.st.bHst) {URL history list}
menuHst 1 $curW 0 0
}
if ![winfo exists $curB.st] {
eval frame $curB.st -bd 1 -relief $R
eval button $curB.st.bBck $BB -text {<-} -command {"Stk \"$curD\" dn"}
eval menubutton $curB.st.bStk $MB -text {^} -menu $curB.st.bStk.menu
eval button $curB.st.bFwd $BB -text {->} -command {"Stk \"$curD\" up"}
bind $curB.st.bStk {menuStk L $curW %X %Y}
bind $curB.st.bStk {menuStk L $curW %X %Y}
bind $curB.st.bStk {menuStk H $curW %X %Y}
bind $curB.st.bStk {menuStk H $curW %X %Y}
pack $curB.st.bBck $curB.st.bStk $curB.st.bFwd -in $curB.st -side left
pack $curB.st -in $curB -side left
menuStk 1 $curW 0 0
BindHelp $curB.st.bBck {Previous URL}
BindHelp $curB.st.bStk {URL stack}
BindHelp $curB.st.bFwd {Next URL}
set namN($curB.st.bBck) {Previous URL}
set namN($curB.st.bStk) {URL stack}
set namN($curB.st.bFwd) {Next URL}
}
if ![winfo exists $curB.re] {
eval frame $curB.re -bd 1 -relief $R
eval button $curB.re.bWheel $BB -relief raised -textvariable waitchr -command Stop
eval button $curB.re.bAgain $BB -relief raised -text GET -command {{Load $curW}}
pack $curB.re.bAgain $curB.re.bWheel -in $curB.re -side left
pack $curB.re -in $curB -side left
BindHelp $curB.re.bWheel {Stop}
BindHelp $curB.re.bAgain {Reload}
set namN($curB.re.bAgain) {Reload document}
}
Verbose $curB -padx 0 -pady 0 -highlightthickness 0
set namN($curB.fVbs.m) {Verbose level}
set namN($curB.fVbs.v) {Verbose level}
#
# frame $curB.bd -bd $F -relief $R
# label $curB.bd.l -text B
# entry $curB.bd.v -textvariable B -highlightthickness 0 -width 0 -bd 0
# pack $curB.bd.l $curB.bd.v -in $curB.bd -side left
# pack $curB.bd -in $curB -side right
# set namN($curB.bd.l) {Border width}
# set namN($curB.bd.v) {Border width}
#
label $curB.target -text $t -bd $B -relief $R
pack $curB.target $curB -side left -expand 1
set namN($curB.target) {target}
#
button $curB.quit -text Quit -command exit -bd $F -highlightthickness 0 -fg red3 -padx 0 -pady 0
pack $curB.quit -in $curB -side right
set namN($curB.bquit) {Exit}
#
set DIR [pwd]/
#
entry $curI.t -textvariable title -bd 0 -font F4vsb \
-bd 0 -relief $R -width 0 -highlightthickness 0 -fg yellow -bg navy
set namN($curI.t) {document's title}
#
if ![info exists URLt($curD)] {set URLt($curD) {}}
if {$V>2} {puts "$id: URLt($curD)=\"$URLt($curD)\""}
entry $curI.u -textvariable URLt($curD) -bd 2 -relief $R -fg yellow -bg navy -highlightthickness 0
bind $curI.u "LoadFile {$w} {} \$URLt($curD) GET"
set namN($curI.u) {document's URL}
#
entry $curI.e -textvariable errmsg -bd 0 -bg black -fg orange
set namN($curI.e) {error message}
#
entry $curI.l -textvariable curL -bd 0 -bg black -fg green
set namN($curI.l) {hyperlink URL}
#
entry $curI.n -textvariable curN -bd 0 -bg black -fg cyan
bind $curI.n "EnterW $curI.n %X %Y"
set namN($curI.n) {widget name}
#
set C(bg) [lindex [$curD config -background] 4]
set C(fg) [lindex [$curD config -foreground] 4]
#
bind $curD "ResizeText $curD"
set litB B
set Tfont [$curD config -font]
if {$V>1} {puts "$id: Tfont=\"$Tfont\""}
set Bfont "$Tfont$litB"
if {$V>1} {puts "$id: Bfont=\"$Bfont\""}
set Nfont $Tfont
if {$V>1} {puts "$id: Nfont=\"$Nfont\""}
#
tagDefs $curD
Packs $w
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# Run thru the command-line args. We recognize the forms:
# -opt(foo)=bar
# -opt(foo)
# and set the appropriate option. A missing value is taken to be 1. All other
# command-line args are added to the URLs list. If there were any such URLs
# on the command line, we will start off by loading the first one.
set urls 0; # Number of URLs on the command line.
set URLs {}; # List of URLs
foreach a $argv {
if [regexp {^[-+]opt\(([A-Za-z0-9_]+)\)=(.*)$} $a {} n v] {
set opt($n) $v
} elseif [regexp {^[Tt]=(.+)$} $a {} n] {
set Target 1
if {$V>1} {puts "$me: Target window is \"$Target\""}
} elseif [regexp {^[-+]opt\(([A-Za-z0-9_]+)\)$} $a {} n] {
set opt($n) 1
} else {
lappend URLs $a
incr urls
}
}
if {$V>5} {
foreach n [lsort [array names bd]] {puts "$me: bd($n) = {$bd($n)}"}
foreach n [lsort [array names opt]] {puts "$me: opt($n) = {$opt($n)}"}
}
TargetWin {} $Target
update idletasks
HeaderWin $opt(Header)
SourceWin $opt(Source)
DebugWin $opt(Debug)
OptionWin $opt(Option)
update idletasks
HSP $curD {}; # Saved spaces for text widget.
set bd($curD) 0; # Border width for main document.
set wantNL($curD) 0; # Number of newlines to add before next text.
FindInit $curD
bind $curD "FindPat $curD"
bind $curD "NextPat $curD"
if {$V>1} {puts "$me: Built other windows."}
update idletasks
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# Finally, get the data and HTMLize it.
if {$urls > 0} {
set f [lindex $URLs 0]
if {$V>1} {puts "$me: Load first file \"$f\""}
LoadFile {} {} $f GET
}
set Hist $URLs
if {$V>1} {foreach o [array names opt] {puts "$me: opt($o)=\"$opt($o)\""}}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
set TxtLen($curD) 0
if {$V>2} {puts "$me: $curD now has TxtLen=$TxtLen($curD) chars."}
#trace variable TxtLen w pvar
bind all "EnterW %W %X %Y"
#bind all "LeaveW %W %X %Y"
if {$opt(TTL)} {titleon {}}