+proc leaving {lchan} {
+ foreach luser [array names nick_onchans] {
+ upvar #0 nick_onchans($luser) oc
+ set oc [grep tc {"$tc" != "$lchan"} $oc]
+ }
+ upvar #0 chan_nicks($lchan) nlist
+ unset nlist
+ upvar #0 chan_lastactivity($lchan) la
+ catch { unset la }
+}
+
+proc doleave {lchan} {
+ sendout PART $lchan
+ leaving $lchan
+}
+
+proc dojoin {lchan} {
+ global chan_nicks
+ sendout JOIN $lchan
+ set chan_nicks($lchan) {}
+}
+
+proc check_justme {lchan} {
+ global nick
+ upvar #0 chan_nicks($lchan) nlist
+ if {[llength $nlist] != 1} return
+ if {"[lindex $nlist 0]" != "[irctolower $nick]"} return
+ if {[chandb_exists $lchan]} {
+ set mode [chandb_get $lchan mode]
+ if {"$mode" != "*"} {
+ sendout MODE $lchan $mode
+ }
+ set topic [chandb_get $lchan topicset]
+ if {[string length $topic]} {
+ sendout TOPIC $lchan $topic
+ }
+ } else {
+ doleave $lchan
+ }
+}
+
+proc process_kickpart {chan user} {
+ global nick
+ check_nick $user
+ set luser [irctolower $user]
+ set lchan [irctolower $chan]
+ if {![ischan $chan]} { error "not a channel" }
+ if {"$luser" == "[irctolower $nick]"} {
+ leaving $lchan
+ } else {
+ upvar #0 nick_onchans($luser) oc
+ upvar #0 chan_nicks($lchan) nlist
+ set oc [grep tc {"$tc" != "$lchan"} $oc]
+ set nlist [grep tn {"$tn" != "$luser"} $nlist]
+ nick_case $user
+ if {![llength $oc]} {
+ nick_forget $luser
+ } else {
+ check_justme $lchan
+ }
+ }
+}
+
+proc msg_TOPIC {p c dest topic} {
+ prefix_nick
+ if {![ischan $dest]} return
+ recordlastseen_n $n "changing the topic on $dest" 1
+ note_topic [irctolower $dest] $n $topic
+}
+
+proc msg_KICK {p c chans users comment} {
+ set chans [split $chans ,]
+ set users [split $users ,]
+ if {[llength $chans] > 1} {
+ foreach chan $chans user $users { process_kickpart $chan $user }
+ } else {
+ foreach user $users { process_kickpart [lindex $chans 0] $user }
+ }
+}
+
+proc msg_KILL {p c user why} {
+ nick_forget $user
+}
+
+set nick_counter 0
+set nick_arys {onchans username unique}
+# nick_onchans($luser) -> [list ... $lchan ...]
+# nick_username($luser) -> <securely known local username>
+# nick_unique($luser) -> <counter>
+# nick_case($luser) -> $user (valid even if no longer visible)
+# nick_markid($luser) -> <after id for marktime>
+
+# chan_nicks($lchan) -> [list ... $luser ...]
+# chan_lastactivity($lchan) -> [clock seconds]
+
+proc lnick_forget {luser} {
+ global nick_arys chan_nicks
+ lnick_marktime_cancel $luser
+ foreach ary $nick_arys {
+ upvar #0 nick_${ary}($luser) av
+ catch { unset av }
+ }
+ foreach lch [array names chan_nicks] {
+ upvar #0 chan_nicks($lch) nlist
+ set nlist [grep tn {"$tn" != "$luser"} $nlist]
+ check_justme $lch
+ }
+}
+
+proc nick_forget {user} {
+ global nick_arys chan_nicks
+ lnick_forget [irctolower $user]
+ nick_case $user
+}
+
+proc nick_case {user} {
+ global nick_case
+ set nick_case([irctolower $user]) $user
+}
+
+proc msg_NICK {p c newnick} {
+ global nick_arys nick_case
+ prefix_nick
+ recordlastseen_n $n "changing nicks to $newnick" 0
+ recordlastseen_n $newnick "changing nicks from $n" 1
+ set luser [irctolower $n]
+ lnick_marktime_cancel $luser
+ set lusernew [irctolower $newnick]
+ foreach ary $nick_arys {
+ upvar #0 nick_${ary}($luser) old
+ upvar #0 nick_${ary}($lusernew) new
+ if {[info exists new]} { error "nick collision ?! $ary $n $newnick" }
+ if {[info exists old]} { set new $old; unset old }
+ }
+ upvar #0 nick_onchans($lusernew) oc
+ foreach ch $oc {
+ upvar #0 chan_nicks($ch) nlist
+ set nlist [grep tn {"$tn" != "$luser"} $nlist]
+ lappend nlist $lusernew
+ }
+ lnick_marktime_start $lusernew "Hi." 500
+ nick_case $newnick
+}
+
+proc nick_ishere {n} {
+ global nick_counter
+ upvar #0 nick_unique([irctolower $n]) u
+ if {![info exists u]} { set u [incr nick_counter].$n.[clock seconds] }
+ nick_case $n
+}
+
+proc msg_JOIN {p c chan} {
+ prefix_nick
+ recordlastseen_n $n "joining $chan" 1
+ set nl [irctolower $n]
+ set lchan [irctolower $chan]
+ upvar #0 nick_onchans($nl) oc
+ upvar #0 chan_nicks($lchan) nlist
+ if {![info exists oc]} {
+ global marktime_join_startdelay
+ lnick_marktime_start $nl "Welcome." $marktime_join_startdelay
+ }
+ lappend oc $lchan
+ lappend nlist $nl
+ nick_ishere $n
+}
+proc msg_PART {p c chan} {
+ prefix_nick
+ recordlastseen_n $n "leaving $chan" 1
+ process_kickpart $chan $n
+}
+proc msg_QUIT {p c why} {
+ prefix_nick
+ recordlastseen_n $n "leaving ($why)" 0
+ nick_forget $n
+}