# This is used to set the section name used for the documentation in the file.
# It is placed here to make it easy to change the name of the file without
# having to change all of the documentation references.  It also makes it easy
# to move items to different files.
set doc_section Server
set doc_type Server


# This is just a quick doc note about what this file is.
doc add "Scripts ($doc_type)/$doc_section" "~/.quirc/server.tcl

This file contains the server specific aliases and events.  It has most of the aliases you will use on IRC."


# Everything in this file is organized alphabetically, EVENTS first, ALIASES
# second, and PROCEDURES last.  Special thanks to hero- for large amounts of
# the cool parts of this script.  Way to go hero!

# EVENTS

proc event_connect {} {
    fdisplay CONNECTED
    foreach channel [channels] {
	title [pathname $channel] [fparse TITLE_CHANNEL_NOTOPIC $channel]
    }
}

proc event_ctcp { prefix target ctcps } {
    set reply ""
    set nick [lindex [split $prefix "!"] 0]

    if { ![set ::[index]::ctcp_cloak] } {

	foreach ctcp $ctcps {
	    switch -- [lindex $ctcp 0] {
		"ERRMSG" {
		    append reply "\001ERRMSG [lindex $ctcp 1]\001"
		}
		"FINGER" {
		    append reply "\001FINGER $::dynamic::default_desc ($::env(USER)@$::env(HOSTNAME)) Idle [idle] seconds.\x01"
		}
		"PING" {
		    append reply "\x01PING [lindex $ctcp 1]\x01"
		}
		"SOURCE" {
		    append reply "\001SOURCE http://quirc.org/quirc-[version].tar.gz\001"
		}
		"TIME" {
		    append reply "\x01TIME [clock format [clock seconds]]\x01"
		}
		"USERINFO" {
		    append reply "\001USERINFO $::dynamic::ctcp_userinfo\001"
		}
		"VERSION" {
		    append reply "\x01VERSION QuIRC:[version]:[exec uname -s] [exec uname -r] ([exec uname -m]):http://quirc.org/\x01"
		}
		"CLIENTINFO" {
		    switch -- [lindex $ctcp 1] {
			"" {
			    append reply "\001CLIENTINFO ACTION CLIENTINFO DCC ERRMSG FINGER PING SOURCE TIME USERINFO VERSION  :Use CLIENTINFO <COMMAND> to get more specific information.\001"
			}
			"ACTION" {
			    append reply "\001CLIENTINFO ACTION contains action descriptions for atmosphere.\001"
			}
			"CLIENTINFO" {
			    append reply "\001CLIENTINFO CLIENTINFO gives information about available CTCP commands.\001"
			}
			"DCC" {
			    append reply "\001CLIENTINFO DCC requests a Direct Client Connection (SEND/CHAT).\001"
			}
			"ERRMSG" {
			    append reply "\001CLIENTINFO ERRMSG returns error messages.\001"
			}
			"FINGER" {
			    append reply "\001CLIENTINFO FINGER shows real name, login name and idle time of user.\001"
			}
			"PING" {
			    append reply "\001CLIENTINFO PING returns the arguments it receives.\001"
			}
			"SOURCE" {
			    append reply "\001CLIENTINFO SOURCE returns a download location for this client version.\001"
			}
			"TIME" {
			    append reply "\001CLIENTINFO TIME tells you the time on the user's host.\001"
			}
			"USERINFO" {
			    append reply "\001CLIENTINFO USERINFO returns user defined information.\001"
			}
			"VERSION" {
			    append reply "\001CLIENTINFO VERSION shows client type, version and environment.\001"
			}
			default {
			    append reply "\001ERRMSG CLIENTINFO: [lindex $ctcp 1] is not a valid type.\001"
			}
		    }
		}
		"DCC" {
		    set filename [lindex [split [lindex $ctcp 1] " "] 1]
		    set templist [string tolower [split [lindex $ctcp 1] " "]]
		    if { [llength $templist]==4 } {
			if {[lindex $templist 0]=="chat"&&[lindex $templist 1]=="chat"} {
			    acceptlist add chat $nick [lindex $templist 2] [lindex $templist 3]
			}
			if {[lindex $templist 0]=="send"} {
			    acceptlist add send $nick [lindex $templist 2] [lindex $templist 3] $filename
			}
		    }
		    if { [llength $templist]>=5 } {
			if {[lindex $templist 0]=="send"} {
			    acceptlist add send $nick [lindex $templist 2] [lindex $templist 3] $filename [lindex $templist 4]
			}
		    }
		}
	    }
	}
	if { [string length $reply] } {
	    quote "NOTICE $nick :$reply"
	}
    }

    set opmessage 0
    set retval 0
    if { [string index $target 0]=="@" } {
    	set opmessage 1
    	set target [string range $target 1 end]
    }
    foreach ctcp $ctcps {
	switch -- [lindex $ctcp 0] {
	    "ACTION" {
		if { $opmessage } {
		    set text [fparse OP_ACTION $nick [lindex $ctcp 1]]
		} else {
		    set text [fparse ACTION $nick [lindex $ctcp 1]]
		}
		if { [ischannel $target] } {
		    if { [onchannel $target] } {
			echo $text [pathname channel $target]
		    }
		} else {
		    createquery $nick
		    echo $text [pathname query $nick]
		}
		set retval 2
	    }
	    default {
		if { [ischannel $target] } {
		    set pathname [pathname channel $target]
		} else {
		    set pathname [pathname status]
		}
		if { [lindex $ctcp 1]!="" } {
		    if { $opmessage } {
			echo [fparse OP_CTCP_DATA [lindex $ctcp 0] $nick [lindex $ctcp 1]] $pathname
		    } else {
			echo [fparse CTCP_DATA [lindex $ctcp 0] $nick [lindex $ctcp 1]] $pathname
		    }
		} else {
		    if { $opmessage } {
			echo [fparse OP_CTCP [lindex $ctcp 0] $nick [lindex $ctcp 1]] $pathname
		    } else {
			echo [fparse CTCP [lindex $ctcp 0] $nick [lindex $ctcp 1]] $pathname
		    }
		}
		set retval 2
	    }
	}
    }
    return $retval
}

proc event_ctcpreply { prefix target ctcps } {
    set nick [lindex [split $prefix "!"] 0]
    foreach ctcp $ctcps {
	if { [lindex $ctcp 0]=="PING" && [regexp {^[0-9]+\.[0-9]+$} [lindex $ctcp 1]] } {
	    fdisplay CTCP_REPLY_PING $nick [expr [microtime]-[lindex $ctcp 1]]
	} else {
	    if { [lindex $ctcp 1]!="" } {
		fdisplay CTCP_REPLY_DATA [lindex $ctcp 0] $nick [lindex $ctcp 1]
	    } else {
		fdisplay CTCP_REPLY [lindex $ctcp 0] $nick
	    }
	}
    }
    return 2
}

proc event_dcc_accept { args } {
    if { [lindex $args 0]=="chat" } {
	set wn .dccaccept[lindex $args 1]
	toplevel $wn
	wm title $wn "DCC Chat Accept Query"
	pack [message $wn.message -text "[lindex $args 2] wishes to chat with you at [lindex $args 3]:[lindex $args 4].  Do you accept?" -width 300]
	bind $wn <<Enter>> "::template::acceptlist [index] accept [lindex $args 1]; destroy $wn"
	bind $wn <KeyPress-Escape> "::template::acceptlist [index] reject [lindex $args 1]; destroy $wn"
	pack [button $wn.buttonyes -text "yes" -default active -command "::template::acceptlist [index] accept [lindex $args 1]; destroy $wn"] -side left
	pack [button $wn.buttonno -text "no" -command "::template::acceptlist [index] reject [lindex $args 1]; destroy $wn"] -side right
    }
    if { [lindex $args 0]=="send" } {
	set exists 0
        if { [file exists $::dynamic::dcc_directory/[lindex $args 5]] } {
	    set exists 1
	}
	if { $::dynamic::dcc_auto_accept && !$exists } {
	    acceptlist accept [lindex $args 1]
	    return
	}
	if { $::dynamic::dcc_auto_accept && $exists && $::dynamic::dcc_auto_overwrite } {
	    acceptlist overwrite [lindex $args 1]
	    return
	}
	set wn .dccaccept[lindex $args 1]
	toplevel $wn
	wm title $wn "DCC File Accept Query"
	set yes "::template::acceptlist [index] accept [lindex $args 1]; destroy $wn"
	set no "::template::acceptlist [index] reject [lindex $args 1]; destroy $wn"
	set yestext "Yes"
	set notext "No"
	if { $exists } {
	    set yes "::template::acceptlist [index] overwrite [lindex $args 1]; destroy $wn"
	    set no "::template::acceptlist [index] reject [lindex $args 1]; destroy $wn"
	    set yestext "Yes (Overwrite)"
	    set notext "No"
	}
	#           $::dcc_auto_accept
	#           $::dcc_auto_overwrite
	#           $::dcc_auto_rename
	#           $::dcc_auto_resume
	#           Rename overides overwrite.  Resume overwrides rename unless the
	#           file is the same size or smaller.
	#           -What about mutual exclusivity here?
	#	    pack [message $wn.message -text "[lindex $args 2] wishes to send you [lindex $args 5] with size [lindex $args 6] from [lindex $args 3]:[lindex $args 4].  Do you accept?" -width 300]
	pack [message $wn.message -text "[lindex $args 2] wishes to send you [lindex $args 5] with size [lindex $args 6] from [lindex $args 3]:[lindex $args 4].  Do you accept?" -width 300]
	if { !$exists } { bind $wn <<Enter>> $yes }
	bind $wn <KeyPress-Escape> $no
	pack [button $wn.buttonyes -text $yestext -default active -command $yes] -side left
	if { $exists } {
	    frame $wn.renframe
	    pack [entry $wn.renframe.entry]
	    pack [button $wn.renframe.button -text "Yes (Rename)" -command "::template::acceptlist [index] rename [lindex $args 1] \[$wn.renframe.entry get]; destroy $wn"]
	    $wn.renframe.entry insert 0 [lindex $args 5]
	    pack $wn.renframe -side left -padx 10
	    bind $wn <<Enter>> "::template::acceptlist [index] rename [lindex $args 1] \[$wn.renframe.entry get]; destroy $wn"
	    focus $wn.renframe.entry
	}
	pack [button $wn.buttonno -text $notext -command $no] -side right
    }
    bind $wn <Destroy> "::template::acceptlist [index] reject [lindex $args 1]; destroy $wn"
    return 0
}

set ::[index]::wlistq ""

proc event_dcc_connect { pathname nick ip port type args } {
    if {$type!="chat"} { set pathname ".files[index]" }
    if {$type!="filesend" && $type!="fileget"} {
	fdisplay DCC_CONNECTED $pathname
    }
    if {$type=="filesend"} {
	set pathfile [lindex $args 0]
	set file [lindex $args 1]
	set size [lindex $args 2]
	set index [lindex $args 3]
	set entryindex [lindex $args 4]
	$pathname.list delete $entryindex $entryindex
	$pathname.list insert $entryindex [fparse DCC_FILE_WAITING $index]
	fdisplay DCC_FILE_CONNECTED $index
	fdisplay DCC_FILE_TRANSFERING $index $pathfile $file $size
    }
    if {$type=="fileget"} {
	set pathfile [lindex $args 0]
	set file [lindex $args 1]
	set size [lindex $args 2]
	set index [lindex $args 3]
	set entryindex [lindex $args 4]
	if { $size==0 } {
	    set size "???"
	}
	$pathname.list delete $entryindex $entryindex
	$pathname.list insert $entryindex [fparse DCC_FILE_WAITING $index]
	fdisplay DCC_FILE_CONNECTED $index
	fdisplay DCC_FILE_RECEIVING $index $file $pathfile $size
    }
    return 2;
}

proc event_dcc_getstatus { entryindex nick pathfile file written size index } {
    set pathname ".files[index]"
    $pathname.list delete $entryindex $entryindex
    if { $size } {
	$pathname.list insert $entryindex [fparse DCC_FILE_GETSTATUS $index $file $nick $written $size]
    } else {
	$pathname.list insert $entryindex [fparse DCC_FILE_GETSTATUS_NOSIZE $index $file $nick $written]
    }
    return 2;
}

proc event_dcc_sendstatus { entryindex nick pathfile file acknowledged transmitted size index } {
    set pathname ".files[index]"
    $pathname.list delete $entryindex $entryindex
    $pathname.list insert $entryindex [fparse DCC_FILE_SENDSTATUS $index $file $nick $acknowledged $size]
    return 2;
}

proc event_dcc_text { nick text } {
    if { [string range $text 0 7]=="\001ACTION " } {
	if { [string index $text [expr [string length $text]-1]]=="\001" } {
	    set text [string range $text 8 [expr [string length $text]-2]]
	} else {
	    set text [string range $text 8 end]
	}
	fdisplay DCC_CHAT_ACTION $nick $text
    } else {
	fdisplay DCC_CHAT_TEXT $nick $text
    }
    return 2;
}

proc event_disconnect {} {
    condis "Connect" "connect"
    fdisplay DISCONNECTED
}

proc event_join { prefix channel args } {
    # If we're joining, create the channel window, set some channel variables
    # and send some commands to gather information about the channel.
    if { [lindex [split $prefix "!"] 0]==[mynick] } {
	createchannel $channel
	set chan [string tolower $channel]
	set ::[index]::banlist($chan) ""
	set ::[index]::banwho($chan) ""
	set ::[index]::bantime($chan) ""
	lappend ::[index]::blistq $chan
	lappend ::[index]::wlistq $chan
	quote "MODE $chan +b"
        quote "MODE $chan"
	quote "WHO $chan"
    }
    # If we're supposed to mute the join message, do so.
    set mute [concat $::dynamic::mute_all $::dynamic::mute_join]
    foreach chan $mute {
	if { [string tolower $chan]==[string tolower $channel] } {
	    if { [lindex [split $prefix "!"] 0]!=[mynick] } {
		return 2
	    }
	}
    }
}

proc event_kick { prefix args } {
    if { [string tolower [lindex $args 1]]==[string tolower [mynick]]
	 && $::dynamic::auto_rejoin } {
	quote "JOIN [lindex $args 0]"
    }
    # If we're supposed to mute the kick message, do so.
    set mute [concat $::dynamic::mute_all $::dynamic::mute_kick]
    foreach channel $mute {
	if { [string tolower $channel]==[string tolower [lindex $args 0]] } {
	    if { [string tolower [lindex $args 1]]!=[string tolower [mynick]] &&
		 [string tolower [lindex [split $prefix "!"] 0]]!=[string tolower [mynick]] } {
		return 2
	    }
	}
    }
    if { [onchannel [lindex $args 0]] } {
	echo [fparse KICK [lindex $args 1] $prefix [lindex $args 2]] [pathname channel [lindex $args 0]]
    }
    return 2
}

proc event_mode { prefix args } {
    if { [ischannel [lindex $args 0]] } {

	# FIXME - The following comment is what should happen:
	# If the mode has our prefix, display it.
	# If the mode does not have a our prefix, check the following:
	# If we're the target of a +o, +b, or +v, display it.
	# Otherwise, don't display it (should show some of i,m,n,p,s,t,k,l,R,c)
	set mute [concat $::dynamic::mute_all $::dynamic::mute_mode]
	foreach channel $mute {
	    if { [string tolower $channel]==[string tolower [lindex $args 0]]
		 && [lindex [split $prefix "!"] 0]!=[mynick] } {
		return 2
	    }
	}

	if { [onchannel [lindex $args 0]] } {
	    echo [fparse MODE_CHANNEL [join [lrange $args 1 end]] $prefix] [pathname channel [lindex $args 0]]
	}
    } else {
	echo [fparse MODE [lindex $args 1]] [pathname status]
    }
    return 2
}

proc event_mode+b { args } {
    set chan [string tolower [lindex $args 1]]
    regsub -all \\. $chan -DOT- pathchan
    lappend ::[index]::banlist($chan) [lindex $args 2]
    lappend ::[index]::bantime($chan) [clock seconds]
    lappend ::[index]::banwho($chan) [lindex $args 0]
    if { [winfo exists .bans([index]+$pathchan)] == 1 } {
	.bans([index]+$pathchan).f.blist delete 0 end
	update_gblist [lindex $args 1]
    }
    return 0
}

proc event_mode-b { args } {
    set chan [string tolower [lindex $args 1]]
    regsub -all \\. $chan -DOT- pathchan
    set index [lsearch -exact [string tolower [set ::[index]::banlist($chan)]] [string tolower [lindex $args 2]]]
    set ::[index]::banlist($chan) [lreplace [set ::[index]::banlist($chan)] $index $index]
    set ::[index]::bantime($chan) [lreplace [set ::[index]::bantime($chan)] $index $index]
    set ::[index]::banwho($chan) [lreplace [set ::[index]::banwho($chan)] $index $index]
    if { [winfo exists .bans([index]+$pathchan)] == 1 } {
	.bans([index]+$pathchan).f.blist delete 0 end
	update_gblist [lindex $args 1]
    }
    return 0
}

proc event_nick { prefix args } {
    set nick [lindex [split $prefix "!"] 0]
    set text [fparse NICK $nick [lindex $args 0]]
    set mute [concat $::dynamic::mute_all $::dynamic::mute_nick]
    if { $nick==[mynick] } {
	echo [pathname status] 1 $text
    }
    if { [mynick]!=$nick } {
	foreach channel [channels] {
	    # If we're supposed to mute the nick message, do so.
	    if { [ison $nick $channel] &&
		 ![member [string tolower $mute] [string tolower $channel]] } {
		echo [pathname channel $channel] 1 $text
	    }
	}
	if { [pathname query $nick]!="" } {
	    echo [pathname query $nick] 1 $text
	}
    } else {
	foreach channel [channels] {
	    echo [pathname channel $channel] 0 $text
	}
	foreach query [queries] {
	    echo [pathname query $query] 0 $text
	}
    }
    return 2
}

proc event_noticetext { prefix target text } {
    set nick [lindex [split $prefix "!"] 0]
    if { [string match "*.*" $nick]} {
	fdisplay SERVER_NOTICE $prefix $text
    } elseif { $nick=="" } {
	echo $text [pathname status]
    } else {
	if { !([ischannel $target]&&![onchannel $target]) } {
	    set opmessage 0
	    if { [string index $target 0] == "@"} {
		set opmessage 1
		set target [string range $target 1 end]
	    }
	    if { [ischannel [string tolower $target]] } {
		if {$opmessage} {
		    fdisplay OP_NOTICE $target $nick $text
		} else {
		    fdisplay CHANNEL_NOTICE $target $nick $text
		}
	    } else {
		if { $nick == "MemoServ" || $nick == "ChanServ" || $nick == "NickServ" } {
		    fdisplay SERVICES_NOTICE $nick $text
		} else {
		    fdisplay NOTICE $nick $text
		}
	    }
	}
    }
    return 2
}

proc event_part { prefix args } {
    # If we're supposed to mute the kick message, do so.
    set mute [concat $::dynamic::mute_all $::dynamic::mute_part]
    foreach channel $mute {
	if { [string tolower $channel]==[string tolower [lindex $args 0]] &&
	     [string tolower [lindex [split $prefix "!"] 0]]!=[string tolower [mynick]] } {
	    return 2
	}
    }
    if { [onchannel [lindex $args 0]] } {
	if { [lindex $args 1]!="" } {
	    echo [fparse PART_REASON $prefix [lindex $args 0] [lindex $args 1]] [pathname channel [lindex $args 0]]
	} else {
	    echo [fparse PART $prefix [lindex $args 0]] [pathname channel [lindex $args 0]]
	}
    }
    return 2
}

proc event_pong { prefix args } {
    if { [regexp {^[0-9]+\.[0-9]+$} [lindex $args 1]] } {
	set lagtime [expr [microtime]-[lindex $args 1]]
	set ::[index]::lagtext [fparse SERVER_LAG $lagtime]
	if { [currentindex]==[index] } {
	    .buttonbar.lag configure -text [fparse SERVER_LAG $lagtime]
	}
	if { $::dynamic::lag_time!=0 } {
	    if { $lagtime>$::dynamic::lag_time } {
		set ::internal::saveme $::dynamic::do_rawview
		set ::dynamic::do_rawview 0
		quote "PING [microtime]"
		set ::dynamic::do_rawview $::internal::saveme
	    } else {
		after [expr int(($::dynamic::lag_time-$lagtime)*1000)] "set ::internal::saveme \$::dynamic::do_rawview; set dynamic::do_rawview 0; ::template::quote [index] \"PING \[microtime]\"; set ::dynamic::do_rawview \$::internal::saveme"
	    }
	}
	return 2
    }
}

proc event_quit { prefix args } {
    set nick [lindex [split $prefix "!"] 0]
    set text [fparse QUIT $prefix [lindex $args 0]]
    set mute [concat $::dynamic::mute_all $::dynamic::mute_quit]
    foreach channel [channels] {
	if { [ison $nick $channel] } {
	    # If we're supposed to mute the quit message, do so.
	    if { ![member [string tolower $mute] [string tolower $channel]] ||
		 [string tolower $nick]==[string tolower [mynick]] } {
		echo $text [pathname channel $channel]
	    }
	}
    }
    if { [pathname query $nick]!="" } {
	echo $text [pathname query $nick]
    }
    return 2
}

proc event_raw { prefix command args } {
    # proc event_userlist_raw
    # proc event_userlist_???
    # FIXME

    # proc event_raw { prefix command args } { script }
    # proc event_??? { prefix args } { script }
    # proc event_unparsednumeric { prefix command args } { script }
    # proc event_numeric { prefix command args } { script }

    # event_raw_unparsed
    # event_raw
    # Specific Events (i.e. event_privmsg or event_301)
    # event_mymode(+/-)?
    # event_mode(+/-)?
    # Internal Events
    # event_unparsednumeric
    # If any previous events return 2, this event will not occur.
    # event_unparsed
    # If any previous events return 2, this event will not occur.

    # proc event_raw_unparsed { data } { script }
    # proc event_raw { prefix command args } { script }
    # proc event_mode { prefix args } { script }
    # proc event_mymode+? { prefix args } { script }
    # proc event_mymode-? { prefix args } { script }
    # proc event_mode+v { prefix target parameter } { script }
    # proc event_mode-v { prefix target parameter } { script }
    # proc event_mode+b { prefix target parameter } { script }
    # proc event_mode-b { prefix target parameter } { script }
    # proc event_mode+l { prefix target parameter } { script }
    # proc event_mode+k { prefix target parameter } { script }
    # proc event_mode-k { prefix target parameter } { script }
    # proc event_mode+o { prefix target parameter } { script }
    # proc event_mode-o { prefix target parameter } { script }
    # proc event_mode+? { prefix target } { script }
    # proc event_mode-? { prefix target } { script }
    # proc event_text { prefix target text } { script }
    # proc event_ctcp { prefix target ctcps } { script }
    # proc event_noticetext { prefix target text } { script }
    # proc event_ctcpreply { prefix target ctcps } { script }

    set noevents 0
    set nomoremasks 0
    set parsed 0
    set ::parsed 0
    foreach pair [userlist match $prefix] {
	if { $nomoremasks } { break }
	set ::parsed parsed
	set procname "event_userlist_[string tolower $command]"
	set ret 0
	foreach script [set ::[index]::scripts] {
	    if { [info commands ::[index]::${script}::$procname]!="" } {
		set tempret [eval "::[index]::${script}::$procname [list $pair] [list [userlist set $pair]] [escape $prefix] $args"]
		if { $tempret=="" } { set tempret 0 }
		set ret [expr $ret|$tempret]
		if { [expr $ret&2] } { set parsed 1 }
		if { [expr $ret&4] } { set noevents 1 }
		if { [expr $ret&8] } { set nomoremasks 1 }
		if { [expr $ret&1] } { break }
	    }
	}
    }
    unset ::parsed
    return $parsed*2+$noevents*4
}

proc event_raw_unparsed { data } {
    if { $::dynamic::do_rawview } {
	if { ![regexp {^(\:[^ ]+ |)PONG [^ ]+ \:?[0-9]+\.[0-9]+.*} $data] } {
	    .raw.text insert end "$data\n"
	}
    }
}

proc event_text { prefix target text } {
    set nick [lindex [split $prefix "!"] 0]
    
    if { [string index $target 0]=="@" } {
	set newtext [fparse OP_TEXT $nick $text]
	set target [string range $target 1 end]
    } else {
	set newtext [fparse TEXT $nick $text]
    }
    if { [ischannel $target] } {
	if { [onchannel $target] } {
	    set pathname [pathname channel $target]
	    echo $newtext $pathname
	}
    } else {
	createquery $nick
	set pathname [pathname query $nick]
	echo $newtext $pathname
    }
    
    # The translation stuff, Check if info is loaded, if not, return.
    if { [info command ::convert] == "" } {
	return 2
    }
    
    if { [regexp "^Binary: (\[01]+)" $text match rest] } {
	if { [expr [string length $rest] % 8] != 0 } {
	    return
	}
	echo [fparse CONVERT [fparse TEXT $nick [convert BINARY TEXT $rest]]] $pathname
    } elseif { [regexp "^Hex: (\[0-9a-fA-f]+)" $text match rest] } {
	if { [expr [string length $rest] % 2] != 0 } {
	    return
	}
	echo [fparse CONVERT [fparse TEXT $nick [convert HEX TEXT $rest]]] $pathname
    } elseif { [regexp "^Rot13: (.*)" $text match rest] } {
	echo [fparse CONVERT [fparse TEXT $nick [convert ROT13 TEXT $rest]]] $pathname
    } elseif { [regexp "^Morse: (.*)" $text match rest] } {
	echo [fparse CONVERT [fparse TEXT $nick [convert MORSE TEXT $rest]]] $pathname
    }
    
    return 2
}

proc event_topic { prefix args } {
    title [pathname [lindex $args 0]] [fparse TITLE_CHANNEL [lindex $args 0] [lindex $args 1]]
}

proc event_userlist_notice { mask flags prefix args } {
    if { [member $flags ignore_all] || [member $flags ignore_notice] } {
	# Stop all future event processing.
	return 15
    }
}

proc event_userlist_privmsg { mask flags prefix args } {
    # FIXME - This needs to be split up into CTCP and TEXT events.
    # The ignore should handle public and private messages and CTCPs seperate
    # The ignore_all flag could be handled in the raw section.  Just do a
    # prefix check.  Good or no?
    if { [member $flags ignore_all] || [member $flags ignore_privmsg] } {
	# Stop all future event processing.
	return 15
    }
}

proc event_001 { prefix args } {
    if { ![winfo exists .buttonbar.lag] } {
	pack [label .buttonbar.lag -foreground $::dynamic::theme_lag_foreground -background $::dynamic::theme_lag_background] -side right
    }
    if { $::dynamic::lag_time!=0 } {
	set ::internal::saveme $::dynamic::do_rawview
	set ::dynamic::do_rawview 0
	quote "PING [microtime]"
	set ::dynamic::do_rawview $::internal::saveme
    }
    if { $::dynamic::auto_rejoin_on_connect } {
	set channels ""
	set keys ""
	foreach channel [channels] {
	    set key [channelmode k $channel]
	    if { $key=="" } {
		lappend channels $channel
	    } else {
		set channels [linsert $channels 0 $channel]
		set keys [linsert $keys 0 $key]
	    }
	}
	if { $channels!="" } { quote "JOIN [join $channels ,] [join $keys ,]" }
    }
    if { [info exists ::[index]::auto_start] } {
	eval [set ::[index]::auto_start]
	unset ::[index]::auto_start
    }
    if { [info exists ::dynamic::default_mode] &&
	 $::dynamic::default_mode!="" } {
	quote "MODE [mynick] $::dynamic::default_mode"
    }
    return 0
}

proc event_004 { prefix args } {
    if { $::dynamic::rename_servers } {
	renamewindow [pathname status] [lindex $args 1]
    }
}

# Nick/Userhost part of WHOIS response
proc event_311 { prefix args } {
    set cc [string toupper [string range [lindex $args 3] [expr [string last . [lindex $args 3]] + 1] end]]
    if { ![is_ip_addr [lindex $args 3]] && [array names ::country $cc]!="" } {
	set cnt [string tolower $::country($cc)]
    } else { set cnt "unknown" }
    fdisplay WHOIS_NICKHOST [lindex $args 1] [lindex $args 2] [lindex $args 3] $cnt
    fdisplay WHOIS_NAME [join [lrange $args 5 end]]
    return 2
}

# End of WHO reply
proc event_315 { prefix args } {
    set chan [string tolower [lindex $args 1]]
    upvar ::[index]::wlistq wlistq
    if { [lsearch -exact $wlistq $chan] >= 0 } { 
	set ::[index]::wlistq [lreplace $wlistq [lsearch -exact $wlistq $chan] [lsearch -exact $wlistq $chan]]
	return 2
    }
}

# Idle/Signon time part of WHOIS response
proc event_317 { prefix args } {
    #:vancouver.bc.ca.dal.net 317 Hynato Hynato 4641 937165949 :seconds idle, signon time
    #:irc.webbernet.net 317 Hynato Hynato 29 :seconds idle
    set so [lindex $args 3]
    set i [lindex $args 2]
    set days 0
    set hours 0
    set mins 0
    if { $i > 86399 } {
	set days [expr $i / 86400]
	set i [expr $i - $days * 86400]
    }
    if { $i > 3599 } {
	set hours [expr $i / 3600]
	set i [expr $i - $hours * 3600]
    }
    if { $i > 59 } {
	set mins [expr $i / 60]
	set i [expr $i - $mins * 60]
    }
    set daystr "" 
    set hourstr ""
    set minstr ""
    set secstr ""
    
    if { $days != 0 } { set daystr "$days days " }
    if { $hours != 0 } { set hourstr "$hours hours " }
    if { $mins != 0 } { set minstr "$mins minutes " }
    if { $i != 0 || ($mins == 0 && $hours == 0 && $days == 0)} { set secstr "$i seconds" }
    if { $days == 1 } { set daystr "$days day " }
    if { $hours == 1 } { set hourstr "$hours hour " }
    if { $mins == 1 } { set minstr "$mins minute " }
    if { $i == 1 } { set secstr "$i second" }
    if { [catch {set signon [clock format $so]}] } {
	fdisplay WHOIS_IDLE_NOSIGNON $daystr$hourstr$minstr$secstr
    } else {
	fdisplay WHOIS_IDLE $daystr$hourstr$minstr$secstr [clock format $so]
    }
    return 2
}

# Channel URL
proc event_328 { args } {
    if { [lsearch -exact [string tolower [channels]] [string tolower [lindex $args 2]]]!=-1} {
	fdisplay URL_CHANNEL [lindex $args 2] [lindex $args 3]
    } else {
	fdisplay URL [lindex $args 2] [lindex $args 3]
    }
    return 2
}

# Reply to TOPIC query
proc event_332 { prefix args } {
    title [pathname [lindex $args 1]] [fparse TITLE_CHANNEL [lindex $args 1] [lindex $args 2]]
}

# WHO reply
proc event_352 { prefix args } {
    upvar ::[index]::wlistq wlistq
    if { [lsearch -exact $wlistq [string tolower [lindex $args 1]]] != -1 } {
	return 2
    }
}

proc event_367 { args } {
    set chan [string tolower [lindex $args 2]]
    if { [info exists ::[index]::blistq] &&
         [lsearch -exact [set ::[index]::blistq] $chan] >= 0 } {
	lappend ::[index]::banlist($chan) [lindex $args 3]
	lappend ::[index]::bantime($chan) [lindex $args 5]
	lappend ::[index]::banwho($chan)  [lindex $args 4]
	return 2
    }
}


proc event_368 { args } {
    set chan [string tolower [lindex $args 2]]
    if { [info exists ::[index]::blistq] &&
         [lsearch -exact [set ::[index]::blistq] $chan] >= 0 } {
	set ::[index]::blistq [lreplace [set ::[index]::blistq] [lsearch -exact [set ::[index]::blistq] $chan] [lsearch -exact [set ::[index]::blistq] $chan]]
	return 2
    }
}

proc event_433 { args } {
    if { [lindex $args 2] == $::dynamic::default_nick } {
 	quote "NICK $::dynamic::default_altnick1"
    } elseif { [lindex $args 2] == $::dynamic::default_altnick1 } {
 	quote "NICK $::dynamic::default_altnick2"
    }
    return 0
}

# ALIASES

# /ame
doc add "Aliases ($doc_type)/$doc_section/ame" "/ame \[-one] <message>

Sends an action to all the channels you are currently joined.  If -one is specified, it will send to only the current server, otherwise it will send to all open servers.  (If you would like to improve this alias, you could add options to message queries and chat windows as well.)"

alias ame {
    set one 0
    set rest $arg
    if { [lindex [split $arg " "] 0] == "-one" } {
	set one 1
	set rest [lrange [split $arg " "] 1 end]
    }
    if { $one } {
	foreach channel [channels] {
	    quote "PRIVMSG $channel :\001ACTION $rest\001"
	    echo "\0032* [mynick] $rest" [pathname channel $channel] 0
	}
    } else {
	foreach server [servers] {
	    evalserver $server "
		foreach \channel \[channels] {
	            quote \"PRIVMSG \$channel :\\001ACTION [escape $rest]\\001\"
	            echo \"\0032* \[mynick] [escape $rest]\" \[pathname channel \$channel] 0
		}
	    "
	}
    }
}

# /amsg
doc add "Aliases ($doc_type)/$doc_section/amsg" "/amsg \[-one] <message>

Sends a message to all the channels you are currently joined.  If -one is specified, it will send to only the current server, otherwise it will send to all open servers.  (If you would like to improve this alias, you could add options to message queries and chat windows as well.)"

alias amsg {
    set one 0
    set rest $arg
    if { [lindex [split $arg " "] 0] == "-one" } {
	set one 1
	set rest [lrange [split $arg " "] 1 end]
    }
    if { $one } {
	foreach channel [channels] {
	    say $rest [pathname channel $channel]
	}
    } else {
	foreach server [servers] {
	    evalserver $server "
		foreach \channel \[channels] {
		    say [escape $rest] \[pathname channel \$channel]
		}
	    "
	}
    }
}

# /anick
doc add "Aliases ($doc_type)/$doc_section/anick" "/anick <nickname>

Changes your nickname on all the servers you are currently connected to."

alias anick {
    foreach server [servers] {
	evalserver $server "/nick $arg"
    }
}

# /away
doc add "Aliases ($doc_type)/$doc_section/away" "/away \[-one|-all] \[<reason>]

If a reason is given, sets you away on the server(s).  If no reason is given, sets you not away on the given server(s).  If -one is specified, the away command will only apply to the current server, otherwise it will apply to all running servers.  -all is the same as not specifying an option."

alias away {
    set command [string tolower [lindex [split $arg " "] 0]]
    if { $command=="-one" || $command=="-all" } {
	set rest [join [lrange [split $arg " "] 1 end]]
	if { $command=="-one" } {
	    quote "AWAY :$rest"
	} else {
	    foreach server [servers] {
		if { $rest=="" } {
		    evalserver $server "quote AWAY"
		} else {
		    evalserver $server "quote \"AWAY :[escape $rest]\""
		}
	    }
	}
    } else {
	foreach server [servers] {
	    if { $arg=="" } {
		evalserver $server "quote AWAY"
	    } else {
		evalserver $server "quote \"AWAY :[escape $arg]\""
	    }
	}
    }
}

# /ban
doc add "Aliases ($doc_type)/$doc_section/ban" "/ban \[<channel>] <nick or mask> \[<type>]

Bans the given nick from the given, or current, channel.  If no type is given, type 3 (*!*user@*.domain.name) is assumed.  If the nick contains the ! character, it will simply ban it, ignoring any given type."

alias ban {
    eval "ban $arg"
}

# /bk
doc add "Aliases ($doc_type)/$doc_section/bk" "/bk <nick> \[<reason>]

Bans, then kicks the given nick from the current channel using the given reason."

alias bk {
    set nick [lindex [split $arg " "] 0]
    set reason [join [lrange [split $arg " " ] 1 end]]
    quote "mode [channel] +b [mask 3 $nick![userhost $nick ]]"
    quote "kick [channel] $nick :$reason"
}

# /blist
doc add "Aliases ($doc_type)/$doc_section/blist" "/blist

Displays the ban list for the current channel."

alias blist {
    set chan [string tolower [channel]]
    set bans [set ::[index]::banlist($chan)]
    set bnicks [set ::[index]::banwho($chan)]
    set btime ""
    foreach time [set ::[index]::bantime($chan)] {
	lappend btime [clock format $time]
    }
    echo [format "\0030,7 %-3s \0030,3 %-[max 3 [llongest $bans]]s \0030,2 %-[max 4 [llongest $bnicks]]s \0030,6 %-[max 4 [llongest $btime]]s " "Num" "Ban" "Nick" "Time"]
    if { [llength $bans] == 0 } { echo "The ban list is empty." }
    for { set i 0 } { $i < [llength $bans] } { incr i } {
	echo [format " %-3s  %-[max 3 [llongest $bans]]s  %-[max 4 [llongest $bnicks]]s  %-[max 4 [llongest $btime]]s" [expr $i + 1] [lindex $bans $i] [lindex $bnicks $i] [lindex $btime $i]]
    }
}

# /c
doc add "Aliases ($doc_type)/$doc_section/c" "/c \[<channel>] \[-/+]<mode letter(s)> \[<mode parameter> ...]

Changed the mode on the given, or current, channel.  If no sign is specified, + is assumed.  This command may not work properly in channel names that start with +."

alias c {
    set chan [lindex [split $arg " "] 0]
    if { [string index $chan 0]!="#" && [string index $chan 0]!="&" } {
	set arg [join [linsert [split $arg " "] 0 [channel]]]
    }
    set list [split $arg " "]
    if { [string index [lindex $list 1] 0]!="+" && [string index [lindex $list 1] 0]!="-" } {
	quote "MODE [lindex $list 0] +[join [lrange $list 1 end]]"
    } else {
	quote "MODE [lindex $list 0] [join [lrange $list 1 end]]"
    }
}

# /calc
doc add "Aliases ($doc_type)/$doc_section/calc" "/calc \$<variable name>|<expression>

If a variable is given (ex. \$::tcl_patchLevel), show the value of that variable.  If an expression is given (ex. 5+\[string length \"whee\"]), evaluate it using TCL's expr."

alias calc {
    if { [string first $arg " "]==-1 && [string index $arg 0]=="\$" } {
	uplevel "#0" "
          say \"QuICK Recall: \\$arg = \[set [string range $arg 1 end]]\"
        "
    } else {
	say "QuICK Calc: $arg = [expr $arg]"
    }
}

# /channel
doc add "Aliases ($doc_type)/$doc_section/channel" "/channel \[<channel>\[,<channel>...] \[<key>\[,<key>...]]]

Alias for /join."

alias channel {
    /join $arg
}

# /cloak
doc add "Aliases ($doc_type)/$doc_section/cloak" "/cloak

Makes QuIRC ignore all CTCP requests (including DCC).  /decloak reverses.  This is a server specific setting and affects the current server."

alias cloak {
    if { [set ::[index]::ctcp_cloak] } {
	echo " \0030,14 CLOAK \003 Cloaking System Already Active"
    } else {
	echo " \0030,14 CLOAK \003 Engaging Cloaking System"
	set ::[index]::ctcp_cloak 1
    }
}

# /colors
doc add "Aliases ($doc_type)/$doc_section/colors" "/colors \[-say]

An alias for /colours."

alias colors {
    /colours $arg
}


# /colours
doc add "Aliases ($doc_type)/$doc_section/colours" "/colours \[-say]

Displays a color chart.  If -say is specified, it will output a colour chart to the current channel, query, or chat."

alias colours {
    if { $arg=="-say" } {
	say "0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15 "
	say "1,0 0 0,1 1 1,2 2 1,3 3 1,4 4 1,5 5 1,6 6 1,7 7 1,8 8 1,9 9 1,10 10 1,11 11 1,12 12 1,13 13 1,14 14 1,15 15 "
    } else {
	echo "0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15 "
	echo "1,0 0 0,1 1 1,2 2 1,3 3 1,4 4 1,5 5 1,6 6 1,7 7 1,8 8 1,9 9 1,10 10 1,11 11 1,12 12 1,13 13 1,14 14 1,15 15 "
    }
}

# /ctcp
doc add "Aliases ($doc_type)/$doc_section/ctcp" "/ctcp <nick> <type>

Sends a ctcp message to the nick.  If you want to send a ping, use /ping nick."

alias ctcp {
    quote "PRIVMSG [lindex [split $arg " "] 0] :\x01[join [lrange [split $arg " "] 1 end]]\x01"
}

# /ctcpreply
doc add "Aliases ($doc_type)/$doc_section/ctcpreply" "/ctcpreply <nick> <type>

Simulates a ctcp reply to the nick."

alias ctcpreply {
    quote "NOTICE [lindex [split $arg " "] 0] :\x01[join [lrange [split $arg " "] 1 end]]\x01"
}

# /cycle
doc add "Aliases ($doc_type)/$doc_section/cycle" "/cycle

Parts and joins the current channel."

alias cycle {
    quote "PART [channel]"
    quote "JOIN [channel] [channelmode k [channel]]"
}

# /dcc
doc add "Aliases ($doc_type)/$doc_section/dcc" "/dcc <command> <nick> \[<file>]

If command is chat, start a DCC chat session with the given nick.  DCC sessions cause a direct connection to the other person's computer.  If you are behind a firewall, DCC sends may not work properly.

If command is send, it will send the given file to the nick.  There is great filename completion available via the tab key."

alias dcc {
    set arglist [split $arg " "]
    if { [llength $arglist]>2 && [string length [lindex $arglist 2]]>0 } {
	set filename [join [lrange $arglist 2 end]]
	if { [string index $filename 0]=="\"" && [string index $filename [expr [string length $filename]-1]]=="\"" } {
	    set filename [string range $filename 1 [expr [string length $filename]-2]]
	}
	set filename [lindex [glob $filename] 0]
	set arglist [lreplace $arglist 2 end $filename]
    }
    if { [llength $arglist]>2 && [lindex $arglist 2]=="" } {
	set arglist [lreplace $arglist 2 end]
    }
    callproc ::template::dcc $arglist [index]
}

# /decloak
doc add "Aliases ($doc_type)/$doc_section/decloak" "/decloak

Turns off cloaking for the current server."

alias decloak {
    if { ![set ::[index]::ctcp_cloak] } {
	echo " \0030,14 CLOAK \003 Cloaking System was not engaged."
    } else {
	set ::[index]::ctcp_cloak 0
	echo " \0030,14 CLOAK \003 Dropping Cloak"
    }
}

# /deop
doc add "Aliases ($doc_type)/$doc_section/deop" "/deop <nick> \[<nick> ...]

Deops the given nicks in the current channel."

alias deop {
    foreach nick [split $arg] {
	modequeue add [channel] -o $nick
    }
    modequeue flush [channel]
}

# /devoice
doc add "Aliases ($doc_type)/$doc_section/deop" "/devoice <nick> \[<nick> ...]

Devoices the given nicks in the current channel."

alias devoice {
    foreach nick [split $arg] {
	modequeue add [channel] -v $nick
    }
    modequeue flush [channel]
}

# /fixnicklist
doc add "Aliases ($doc_type)/$doc_section/fixnicklist" "/fixnicklist

On some occaisions due to the limitations of the IRC protocol, the nicklist display for voiced people will become incorrect.  This alias allows you to retrieve a new corrected nicklist from the NAMES response."

alias fixnicklist {
    fixnicklist [channel]
}

# /fk
doc add "Aliases ($doc_type)/$doc_section/fk" "/fk <mask> \[<reason>]

Kicks anyone in the channel who's nick!user@host matches the given mask.  If a reason is given, it is provided.  The mask may contain any of the following wildcards: ? * \[chars] (\\ to escape)."

alias fk {
    set mask [string tolower [lindex [split $arg " "] 0]]
    set reason [join [lrange [split $arg " "] 1 end]]
    if { [string length $reason] > 0 } { set reason " \[$reason]" }
    foreach nick [nicks [channel]] {
 	if { [string match $mask [string tolower $nick![userhost $nick]]] && $nick != [mynick] } {
 	    quote "KICK [channel] $nick :filter: $mask$reason" }
    }
}

# /gblist
doc add "Aliases ($doc_type)/$doc_section/gblist" "/gblist

Displays a graphical ban list for the current channel.  New bans may be added by entering a space seperated list of masks into the entry box.  Bans can be removed by optionally selecting some, and then right clicking on them."

set ::internal::banwindows ""

alias gblist {
    regsub -all \\. [string tolower [channel]] -DOT- chan
    set cur [currentindex]
    if { [winfo exists .bans($cur+$chan)] } {
	destroy .bans($cur+$chan)
    } else {
	# Toplevel Ban Window
	toplevel .bans($cur+$chan)

	# Set up list of ban windows so they can be changed in realtime.
	lappend ::internal::banwindows .bans($cur+$chan)
	bind .bans($cur+$chan) <Destroy> "
	    set index \[lsearch -exact \$::internal::banwindows .bans($cur+$chan)]
	    set ::internal::banwindows \[lreplace \$::internal::banwindows \$index \$index]
	"

	# Banlist Label
	label .bans($cur+$chan).top -font $::dynamic::theme_banlist_font -relief groove -anchor w -bg $::dynamic::theme_banlist_title_background -fg $::dynamic::theme_banlist_title_foreground

	# Banlist and Scrollbar
	frame .bans($cur+$chan).f
	listbox .bans($cur+$chan).f.blist -font $::dynamic::theme_banlist_font -relief flat -bg $::dynamic::theme_banlist_background -fg $::dynamic::theme_banlist_foreground -yscroll ".bans($cur+$chan).f.blist_vscroll set" -exportselection no -selectmode extended
	scrollbar .bans($cur+$chan).f.blist_vscroll -command ".bans($cur+$chan).f.blist yview"

	# Entry Box
	entry .bans($cur+$chan).add -font $::dynamic::theme_banlist_font -relief groove -bg $::dynamic::theme_banlist_entry_background -fg $::dynamic::theme_banlist_entry_foreground -insertbackground $::dynamic::theme_banlist_entry_insertbackground

	# If the right mouse button is hit, if there are zero or one things
        # selected, select the one just hit.  If there are more than one things
        # selected, add the current thing on.  Then send remove bans for all
        # of the selected items.
	bind .bans($cur+$chan).f.blist <Button-3> "
	    if { \[llength \[.bans($cur+$chan).f.blist curselection]]<2 } {
		.bans($cur+$chan).f.blist selection clear 0 end
		.bans($cur+$chan).f.blist selection set @%x,%y
	    } else {
		.bans($cur+$chan).f.blist selection set @%x,%y
	    }
	    foreach idx \[.bans($cur+$chan).f.blist curselection] {
                ::template::modequeue $cur add [channel] -b \[lindex \[set ::${cur}::banlist([string tolower [channel]])] \$idx]
            }
            ::template::modequeue $cur flush [channel]
        "
	bind .bans($cur+$chan).add <<Enter>> "
           foreach ban  \[split \[.bans($cur+$chan).add get]] {
	       ::template::modequeue $cur add [channel] +b \$ban
           }
           ::template::modequeue $cur flush [channel]
           .bans($cur+$chan).add delete 0 end
        "

	# Pack header, list/scroll, and buttons/entry
	pack .bans($cur+$chan).top -fill x

	pack .bans($cur+$chan).f.blist -side left -expand yes -fill both
	pack .bans($cur+$chan).f.blist_vscroll -fill y -expand yes
	pack .bans($cur+$chan).f -fill both -expand yes

	pack .bans($cur+$chan).add -fill x

	#Bring initial focus to entry widget.
        focus .bans($cur+$chan).add

	# Fill the banlist, and set the window title and header.
	update_gblist [channel]
    }
    return 0
}

# /invite
doc add "Aliases ($doc_type)/$doc_section/invite" "/invite <nick> \[<channel>]

Sends an invite message to nick, inviting them to the given, or current, channel."

alias invite {
    if { ![ischannel [lindex [split $arg " "] 1]] } { set arg [join [linsert [split $arg " "] 1 [channel]]] }
    quote "INVITE $arg"
}

# /j
doc add "Aliases ($doc_type)/$doc_section/j" "/j \[<channel>\[,<channel>...] \[<key>\[,<key>...]]]

Alias for /join."

alias j {
    /join $arg
}

# /join
doc add "Aliases ($doc_type)/$doc_section/join" "/join \[<channel>\[,<channel>...] \[<key>\[,<key>...]]]

Joins the given channels using the given keys.  If there are less keys provided than channels, the keys will apply to the first channels.  If no arguments are given, it will join the current channel."

alias join {
    if { $arg=="" } { set arg [channel] }
    if { ![ischannel [lindex [split $arg " "] 0]] } { set arg "#$arg" }
    quote "JOIN $arg"
}

# /k
doc add "Aliases ($doc_type)/$doc_section/k" "/k \[<channel>] <nick> \[<reason>]

Alias for /kick."

alias k {
    /kick $arg
}

# /kb
doc add "Aliases ($doc_type)/$doc_section/kb" "/kb <nick> \[<reason>]

Kicks, then bans the given nick from the current channel using the given reason."

alias kb {
    set nick [lindex [split $arg " "] 0]
    set reason [join [lrange [split $arg " " ] 1 end]]
    quote "kick [channel] $nick :$reason"
    quote "mode [channel] +b [mask 3 $nick![userhost $nick ]]"
}

# /kick
doc add "Aliases ($doc_type)/$doc_section/kick" "/kick \[<channel>] <nick> \[<reason>]

Kicks the given user off the given, or current, channel using the given reason."

alias kick {
    if { ![ischannel [lindex [split $arg " "] 0]] } { set arg [join [linsert [split $arg " "] 0 [channel]]] }
    quote "KICK [join [lrange [split $arg " "] 0 1]] :[join [lrange [split $arg " "] 2 end]]"
}

# /kill
doc add "Aliases ($doc_type)/$doc_section/kill" "/kill <nick> <reason>

Presuming you have oper priveledges, this will remove the given user from your server or network, stating the specified reason."

alias kill {
    quote "KILL [lindex [split $arg " "] 0] :[lrange [split $arg " "] 1 end]"
}

# /mb
doc add "Aliases ($doc_type)/$doc_section/mb" "/mb \[<channel>]

Bans everyone, but you, in the given, or current, channel."

alias mb {
    if { $arg=="" } { set arg [channel] }
    foreach nick [nicks $arg] {
	if { $nick != [mynick] } {
	    queueban $arg $nick
        }
    }
    modequeue flush $arg
}

# /mdop
doc add "Aliases ($doc_type)/$doc_section/mdop" "/mdop \[<channel>]

Deops everyone, but you, in the given, or current, channel."

alias mdop {
    if { $arg=="" } { set arg [channel] }
    foreach nick [nicks $arg] {
	if { $nick!=[mynick] && [isop $nick $arg] } {
	    modequeue add $arg -o $nick
	}
    }
    modequeue flush $arg
}

# /me
doc add "Aliases ($doc_type)/$doc_section/me" "/me <action>

Performs the given action in the current window (channel/query/chat)  i.e. it sends a CTCP ACTION command. (* Somebody does something.)"

alias me {
    if { [currentwindow]==".main" } { return }
    if { [string match ".chat*" [currentwindow]] } {
	dccquote [windowname] "\001ACTION $arg\001"
    } else {
	if { [string match ".status*" [currentwindow]] } { return }
	quote "PRIVMSG [windowname] :\001ACTION $arg\001"
    }
    fdisplay OUTGOING_ACTION [currentwindow] $arg
}

# /mop
doc add "Aliases ($doc_type)/$doc_section/mop" "/mop \[<channel>]

Ops everyone in the given, or current, channel who isn't already an op."

alias mop {
    if { $arg=="" } { set arg [channel] }
    foreach nick [nicks $arg] {
	if {![isop $nick $arg]} {
	    modequeue add $arg +o $nick
	}
    }
    modequeue flush $arg
}

# /msg
doc add "Aliases ($doc_type)/$doc_section/msg" "/msg <nick> <message>

Sends a private message to the given nick."

alias msg {
    quote "PRIVMSG [lindex [split $arg " "] 0] :[join [lrange [split $arg " "] 1 end]]"
    if { [pathname query [lindex [split $arg " "] 0]]!="" } {
	fdisplay OUTGOING_TEXT [pathname query [lindex [split $arg " "] 0]] [join [lrange [split $arg " "] 1 end]]
    } else {
	fdisplay OUTGOING_MSG [lindex [split $arg " "] 0] [join [lrange [split $arg " "] 1 end]]
    }
}

# /mub
doc add "Aliases ($doc_type)/$doc_section/mub" "/mub \[<channel>]

Clears the banlist for the given, or current, channel."

alias mub {
    if { $arg=="" } { set arg [channel] }
    foreach ban [set ::[index]::banlist([string tolower $arg])] {
	modequeue add $arg -b $ban
    }
    modequeue flush $arg
}

# /muv
doc add "Aliases ($doc_type)/$doc_section/muv" "/muv \[<channel>]

De-voices everyone in the given, or current, channel."

alias muv {
    if { $arg=="" } { set arg [channel] }
    foreach nick [nicks $arg] {
	modequeue add $arg -v $nick
    }
    modequeue flush $arg
}

# /mv
doc add "Aliases ($doc_type)/$doc_section/mv" "/mv \[<channel>]

Voices everyone who isn't already voiced in the given, or current, channel."

alias mv {
    if { $arg=="" } { set arg [channel] }
    foreach nick [nicks $arg] {
	if {![isvoice $nick $arg]} {
	    modequeue add $arg +v $nick
	}
    }
    modequeue flush $arg
}

# /notice
doc add "Aliases ($doc_type)/$doc_section/notice" "/notice <nick> <message>

Sends a notice message to the given nick."

alias notice {
    quote "NOTICE [lindex [split $arg " "] 0] :[join [lrange [split $arg " "] 1 end]]"
    fdisplay OUTGOING_NOTICE [lindex [split $arg " "] 0] [join [lrange [split $arg " "] 1 end]]
}

# /op
doc add "Aliases ($doc_type)/$doc_section/op" "/op <nick> \[<nick> ...]

Ops all of the given nicks in the current channel."

alias op {
    quote "MODE [channel] +oooooo $arg"
}

# /ops
doc add "Aliases ($doc_type)/$doc_section/ops" "/ops <message>

Sends a notice to @#channel where #channel is the current channel window."

alias ops {
    quote "NOTICE @[channel] :$arg"
    fdisplay OUTGOING_OPS [channel] $arg
}

# /part
doc add "Aliases ($doc_type)/$doc_section/part" "/part \[<channel>] \[<reason>]

Parts the given, or current, channel.  Any specified reason will be sent as the part reason."

alias part {
    if { ![ischannel [lindex [split $arg " "] 0]] } { set arg [join [linsert [split $arg " "] 0 [channel]]] }
    if { [join [lrange [split $arg " "] 1 end]]=="" } {
        quote "PART [lindex [split $arg " "] 0]"
    } else {
        quote "PART [lindex [split $arg " "] 0] :[join [lrange [split $arg " "] 1 end]]"
    }
}

# /ping
doc add "Aliases ($doc_type)/$doc_section/part" "/ping \[<target>]

Pings the given nick or channel.  If no target is given, a ping is sent to the target of the current window."

alias ping {
    if { $arg=="" } { set arg [windowname] }
    quote "PRIVMSG [lindex [split $arg " "] 0] :\x01PING [microtime]\x01"
}

# /query
doc add "Aliases ($doc_type)/$doc_section/query" "/query <nick> \[message]

Start a query session with the given nick, sending any given message."

alias query {
    set nick [lindex [split $arg " "] 0]
    set message [join [lrange [split $arg " "] 1 end]]
    createquery $nick
    totop [windowindex [pathname query $nick]]
    if { $message!="" } { say $message [pathname query $nick] }
}

# /quit
doc add "Aliases ($doc_type)/$doc_section/quit" "/quit \[<message>]

Quits from the current server, sending the quit message if given.  If no quit mesage is given, the default_quit message will be sent.  If the ~/.quirc/quit.txt file exists, it will use a random line from there."

alias quit {
    set ::[index]::intentional_disconnect 1
    if { $arg!="" } {
	quote "QUIT :$arg"
    } else {
	random_quit
	quote "QUIT :$::dynamic::default_quit"
    }
}

# /quote
doc add "Aliases ($doc_type)/$doc_section/quote" "/quote <data>

Sends the given data directly to the server without interpreting it."

alias quote {
    quote $arg
}

# /reload
doc add "Aliases ($doc_type)/$doc_section/reload" "/reload <script>

Simply unloads and then reloads the given script."

alias reload {
    unscript $arg
    script $arg
}

# /say
doc add "Aliases ($doc_type)/$doc_section/say" "/say <something>

Says something to the current channel, query or DCC."

alias say {
    say $arg
}

# /sv
doc add "Aliases ($doc_type)/$doc_section/sv" "/sv

Shows the client version and machine information to the current session."

alias sv {
    say "\x02QuIRC [version]\x02 - [exec uname -s] [exec uname -r] ([exec uname -m])"
}

# /topic
doc add "Aliases ($doc_type)/$doc_section/topic" "/topic \[<channel>] \[<topic>]

If the topic is specified, the new topic will be set in the given, or current, channel.  If no topic is specified, the current topic will be displayed."

alias topic {
    if { ![ischannel [lindex [split $arg " "] 0]] } { set arg [join [linsert [split $arg " "] 0 [channel]]] }
    if { [join [lrange [split $arg " "] 1 end]]=="" } {
        quote "TOPIC [lindex [split $arg " "] 0]"
    } else {
        quote "TOPIC [lindex [split $arg " "] 0] :[join [lrange [split $arg " "] 1 end]]"
    }
}

# /umode
doc add "Aliases ($doc_type)/$doc_section/umode" "/umode <mode>

Sends the given personal mode request."

alias umode {
    /mode [mynick] $arg
}

# /unban
doc add "Aliases ($doc_type)/$doc_section/unban" "/unban <mask>|<nick>|<number>

When given a nick or mask, it will use the available mask information to unban all bans that affect that particular nick or mask.  If a number is given, it will remove that particular ban.  To get a list of bans and their numbers, use /blist."

alias unban {
    if { [string first "!" $arg] > -1 } {
        set umask $arg
    } elseif { [regexp {^[0-9]} $arg]  } {
        quote "MODE [channel] -b [lindex [set ::[index]::banlist([string tolower [channel]])] [expr $arg - 1] ]"
        return 0
    } else {
        set nick [lindex [split $arg " "] 0]
        set umask [string tolower $nick![userhost $nick]]
    }
    foreach ban [set ::[index]::banlist([string tolower [channel]])] {
        if { [string match $umask [string tolower $ban]] || [string match [string tolower $ban] $umask] } {
            quote "mode [channel] -b $ban"
        }
    }
    return 0
}

# /unload
doc add "Aliases ($doc_type)/$doc_section/unload" "/unload <script>

Unloads the given script by deleting the script's namespace and calling event_unload within the script."

alias unload {
    unscript $arg
}

# /untopic
doc add "Aliases ($doc_type)/$doc_section/untopic" "/untopic \[<channel>]

Unsets the topic for the given, or current channel."

alias untopic {
    if { $arg=="" } { set arg [channel] }
    quote "TOPIC $arg :"
}

# /ver
doc add "Aliases ($doc_type)/$doc_section/ver" "/ver \[<target>]

Sends a CTCP VERSION to the given nick or channel.  If no target is specified, it will take send a version request to target as represented by the current window.  If the window is the dcc files window, or the server window, it will ask for the server version."

alias ver {
    if { $arg=="" } { set arg [windowname] }
    if { [currentwindow]==[pathname status] || [currentwindow]==[pathname files] } {
	quote "VERSION"
    } else {
	if { [string match =* $arg] } { set arg [string range $arg 1 end] }
	quote "PRIVMSG [lindex [split $arg " "] 0] :\x01VERSION\x01"
    }
}

# /voice
doc add "Aliases ($doc_type)/$doc_section/voice" "/voice <nick> \[<nick> ...]

Voices all of the given nicks in the current channel.  This will allow them to speak in a moderated (+m) channel."

alias voice {
    quote "MODE [channel] +vvvvvv $arg"
}

# /w
doc add "Aliases ($doc_type)/$doc_section/w" "/w \[<channel>]

Does a who on the given, or current, channel."

alias w {
    if { $arg=="" } { set arg [channel] }
    quote "WHO $arg"
}

# /wall
doc add "Aliases ($doc_type)/$doc_section/wall" "/wall <message>

Similar to /ops, only it sends a BX-like ops message."

alias wall {
    quote "NOTICE @[channel] :\[\x002Qwall/[channel]\x002\] $arg"
    echo [currentwindow] 0 "\[\x002Qwall/[channel]\x002\] $arg"
}

# /wallops
doc add "Aliases ($doc_type)/$doc_section/wallops" "/wallops <message>

Sends a wallops message to all users with +w set."

alias wallops {
    quote "WALLOPS :$arg"
}

# /wi
doc add "Aliases ($doc_type)/$doc_section/wi" "/wi \[<nick> \[<nick> ...]]

Does a whois on the given list of nicks.  If no nick is given, it will use your nick."

alias wi {
    if { $arg=="" } {
	quote "whois [mynick]"
    } else { 
 	foreach user [split $arg] {
	    quote "WHOIS $user"
 	}
    }
}

# /wii
doc add "Aliases ($doc_type)/$doc_section/wii" "/wii \[<nick> \[<nick> ...]]

Does a whois (with idle time check) on the given list of nicks.  If no nick is given, it will use your nick."

alias wii {
    if { $arg=="" } {
	quote "whois [mynick]"
    } else { 
 	foreach user [split [string trim $arg]] {
	    if { $user != "" } { quote "WHOIS $user $user" }
 	}
    }
}

# PROCEDURES

doc add "Procedures (Server, Script)/$doc_section/update_gblist" "update_gblist <channel>

Updates the graphical banlist.  For use when the real banlist changes."

proc update_gblist { arg } {
    set chan [string tolower $arg]
    regsub -all \\. $chan -DOT- pathchan
    set bans  [set ::[index]::banlist($chan)]
    set bnicks  [set ::[index]::banwho($chan)]
    set btime ""
    foreach time [set ::[index]::bantime($chan)] {
	lappend btime [clock format $time]
    }

    .bans([index]+$pathchan).f.blist configure -width [string length [format " %-3s  %-[max 3 [llongest $bans]]s  %-[max 4 [llongest $bnicks]]s  %-[max 4 [llongest $btime]]s " "Num" "Ban" "Nick" "Time"]]

    for { set i 0 } { $i < [llength $bans] } { incr i } {
	.bans([index]+$pathchan).f.blist insert end [format " %-3s  %-[max 3 [llongest $bans]]s  %-[max 4 [llongest $bnicks]]s  %-[max 4 [llongest $btime]]s" [expr $i + 1] [lindex $bans $i] [lindex $bnicks $i] [lindex $btime $i]]
    }

    wm title .bans([index]+$pathchan) "Ban list for $arg \[[index]] ([llength $bans])"
    .bans([index]+$pathchan).top configure -text [format " %-3s  %-[max 3 [llongest $bans]]s  %-[max 4 [llongest $bnicks]]s  %-[max 4 [llongest $btime]]s" "Num" "Ban" "Nick" "Time"]
}
