Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

ZIRC - The 100% Zsh IRC client :)



As you can probably tell by the topic, I had some free time this weekend
and have created quite possibly the *stupidest* ZSH program ever.

ZIRC is an IRC client.  It hooks into ZLE's asynchronous select()
support and ZSH's builtin TCP support to do everything.  Not a *single*
external, non-zsh command was used in this program.  If you're typing
something at the prompt and someone messages you, it'll simply interrupt
your current prompt, print out the message and let you continue with
your typing.

How to use (additional docs at the top of the script):

*) Source the file (. ./zirc)

*) (Optional) Run zsh_aliases, this sets up some aliases to make it a
bit more comfortable to use.
   me => zirc_action
   msg => zirc_msg
   last => zirc_last
   connect => zirc_connect
   etc...

*) Important commands: command(alias)
   zirc_switch(sw):       change channel focus
   zirc_last(last):       change channel focus to last place with a message
   zirc_query(query):     change focus to username
   zirc_msg(msg):         message to focused user/channel
   zirc_pmsg(pmsg):       message to any user/channel
   zirc_quit(quit):       quit with optional quit message
   zirc_nick(nick):       change nickname
   zirc_connect(connect): connect to IRC server
   zirc_part(part):       Leave channel 
   zirc_help(help):       List zirc commands
   zirc_action(me):       Perform action in focused channel/user

*) Screenshot: 
  (I have my prompt setup to incorporate $ZIRC_CURRENT to show current
  focus)
  http://dump.aeruder.net/zirc.png

I suppose if you were in some not too busy channels, this thing could be
pretty useful, but really, its mostly designed to just be a joke so you
can tell your friends:

"Yea, well, my shell has an IRC client"

Have fun,
Andrew Ruder

P.S. I'm sure there are still some bugs, but I've just got some other
things I should probably be doing this weekend, 0.2 for bug fixes :)

-- 
Andrew Ruder <andy@xxxxxxxxxxx>
http://www.aeruder.net
#!/bin/zsh
# vim:noet fdm=marker sw=4 ts=4

ZIRC_VERSION="0.1"

# ZIRC - A 100% ZSH IRC client
#
# Copyright (C) 2006 by Andrew Ruder <andy@xxxxxxxxxxx>
#
# This IRC client is pretty ridiculous, I made it entirely as a joke, but it
# actually works fairly decently for as little work has been put into it.
# And of course, afaik, there are *no* external commands used in the making
# of this program.  No grep, no sed, nothing... pretty cool :)
#
# Guide to using it:
#   Step 1) Source this file.
#           . ./zirc or whatever
#           You can even put this into your .zshrc, it won't hurt anything
#           besides the creation of several _zirc_*/zirc_* functions.
#   Step 2) (Optional) Run zsh_aliases
#           This sets up several aliases to make it easier to use.  Basically
#           it strips the zirc_ front end off of everything.  If you want to
#           see the alias list, type echo $functions[zsh_aliases]
#   Step 3) Run zirc_connect
#           It'll explain the syntax and the fairly standardized environment
#           variables controlling its behavior.
#   Step 4) Use zirc_msg to message, zirc_pmsg to private message.
#           zirc_switch can change to another channel. (zirc_switch somechan)
#           or
#           zirc_query can change to anything
#   Step 5) zirc_last is very handy as it switches to the channel/user that
#           last said something to you.
#
#   Prompt integration: a few environment variables can help you on your way.
#     $ZIRC_CURRENT = current focus
#     $ZIRC_NICK    = current nickname
#     $ZIRC_SERVER  = connected server
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.

[[ -z "$modules[zsh/net/tcp]" ]] && zmodload zsh/net/tcp
[[ -z "$modules[zsh/zselect]" ]] && zmodload zsh/zselect

ZIRC_PARAMS=( ZIRC_NICK ZIRC_USER ZIRC_NAME ZIRC_PORT ZIRC_HOST )

if ! [[ -z "$ZIRC_FD" ]] && ! [[ -z "$functions[zirc_disconnect]" ]] ; then
	zirc_disconnect
fi

ZIRC_VARS=( ZIRC_LAST ZIRC_CHANNELS ZIRC_CURRENT ZIRC_LOWERCASER ZIRC_FD ZIRC_SERVER "${ZIRC_PARAMS[@]}" )
ZIRC_UNSETSTRING="unset ${ZIRC_VARS[*]}"
eval "$ZIRC_UNSETSTRING"

typeset -g "$ZIRC_VARS[@]"

typeset -A ZIRC_PARAM_DEFS
ZIRC_PARAM_DEFS[ZIRC_NICK]="Nickname:\$IRCNICK:$USERNAME"
ZIRC_PARAM_DEFS[ZIRC_USER]="Username:\$IRCUSER:$USERNAME"
ZIRC_PARAM_DEFS[ZIRC_PORT]="Port:\$IRCPORT:6667"
ZIRC_PARAM_DEFS[ZIRC_NAME]="Real name:\$IRCNAME:John Doe"
ZIRC_PARAM_DEFS[ZIRC_HOST]="Host name:\$IRCHOST:localhost"

# zirc_connect parameter handling {{{

# Print out help for the environment variables using the
# ZIRC_PARAM_DEFS and ZIRC_PARAMS variables as a reference
function _zirc_param_help {
	local a b
	echo -e "Environment variables:"
	for a in "${ZIRC_PARAMS[@]}"; do
		b=( ${(s/:/)${ZIRC_PARAM_DEFS[$a]}} )
		echo -e "\t${b[2]} - ${b[1]} (default ${b[3]})"
	done
}

# Fills in the variables in ZIRC_PARAMS using the correct
# fallback values specified in ZIRC_PARAM_DEFS
function _zirc_param_populate {
	local a b
	for a in "$ZIRC_PARAMS[@]"; do
		b=( ${(s/:/)${ZIRC_PARAM_DEFS[$a]}} )
		if [ -z "${(e)b[2]}" ]; then
			eval "${a}=\"\${b[3]}\""
		else
			eval "${a}=\"\${(e)b[2]}\""
		fi
	done
}

# }}}


# Lowercasing/string comparison code {{{

# Lowercasing according to rfc1459 strictly
function _zirc_lowercase_strict_rfc1459 {
	local msg="${(L)1}"
	msg=${msg//\[/\{}
	msg=${msg//]/\}}
	msg=${msg//\\/\|}
	echo "${msg}"
}

# Lowercasing according to rfc1459
function _zirc_lowercase_rfc1459 {
	local msg="$(_zirc_lowercase_strict_rfc1459 "$1")"
	msg=${msg//\~/^}
	echo "${msg}"
}

# Lowercasing using standard ascii rules
function _zirc_lowercase_ascii {
	echo "${(L)1}"
}

# Lowercasing according to the current server's specs
function _zirc_lowercase {
	"$ZIRC_LOWERCASER" "$1"
}

# Case-insensitive compare according to the case-mapping= spec for the server
function _zirc_compare {
	[[ "$("$ZIRC_LOWERCASER" "$1")" == "$("$ZIRC_LOWERCASER" "$2")" ]]
}

# Determine if a prefix/word is referring to you
function _zirc_isme {
	_zirc_compare "$ZIRC_NICK" "$(_zirc_prefix_nick "$1")"
}

# }}}


# data from server parsing {{{

# Parse a line like
#   :blah!host blah3 blah4 :Message with lots of words
# and output
#   blah!host
#   blah3
#   blah4
#   Message with lots of words
function _zirc_parse_server_line {
	local readval args
	args=()
	readval=( "${(f)$(_zirc_parse_server_prefix "$1")}" )
	args+=( "${readval[1]}" )
	while ! [[ -z "${readval[2]}" ]] ; do
		readval=( "${(f)$(_zirc_parse_server_word "${readval[2]}")}" ) #" Bug in vim highlighting
		if ! [[ -z "${readval[1]}" ]]; then
			args+=( "${readval[1]}" )
		fi
	done
	echo "${(F)args}"
}

# Parse a line like
#   :blah!host blah3 blah4
# and print out
#   blah!host
#   blah3 blah4
function _zirc_parse_server_prefix {
	local line prefix
	local EXTENDED_GLOB ; EXTENDED_GLOB=1
	line="${1## #}"
	if [[ "${line[1]}" != ":" ]]; then
		prefix=""
	else
		prefix="${line%% *}"
		if [[ "$prefix" == "$line" ]]; then
			line=""
		else
			line="${line#* }"
		fi
	fi
	echo "${prefix#:}"
	echo "$line"
}

# Parse out a single word from a line, so
#   blah3 blah4 :Message
# prints out:
#   blah3
#   blah4 :Message
# again:
#   blah4
#   :Message
# again
#   Message
#   <empty line> 
function _zirc_parse_server_word {
	local line word 
	local EXTENDED_GLOB ; EXTENDED_GLOB=1
	line="${1## #}"
	if [[ "${line[1]}" != ":" ]]; then
		word="${line%% *}"
		if [[ "$word" == "$line" ]]; then
			line=""
		else
			line="${line#* }"
		fi
	else
		word="${line#:}"
		line=""
	fi
	echo "$word"
	echo "$line"
}

# Split a prefix and get nick (nick!host)
#   Input: nick!host or nick
#   Output: 
#     <empty-line>
function _zirc_prefix_nick {
	local a
	a=( "${(f)$(_zirc_split_prefix "$1")}" )
	echo "${a[1]}"
}

# Split a prefix and get host (nick!host)
#  Input: nick!host
#  Output:
#    host
#  Input: nick
#  Output:
#    <empty-line>
function _zirc_prefix_host {
	local a
	a=( "${(f)$(_zirc_split_prefix "$1")}" )
	echo "${a[2]}"
}

# Split a prefix
# Input: nick!host
# Output:
#   nick
#   host
# Input: nick
# Output:
#   nick
#   <empty-line>
function _zirc_split_prefix {
	local a
	a=( ${(s:!:)1} )
	[[ ${#a} == "1" ]] && a+=( '' )
	echo "${(F)a}"
}

# }}}


# CTCP handling {{{

# Print out the generic ctcp message
# Args: prefix command where ctcp msg
function _zirc_generic_ctcp {
	local pref="$1" comm="$2"
	local where="$3" ctcp="$4"
	local msg="$5"
	local who="$(_zirc_prefix_nick "$pref")"

	_zirc_echo -n "<CTCP> <$who"
	_zirc_isme "$where" || _zirc_echo -n ":$where"
	_zirc_echo "> $ctcp $msg"
}

# Write out a CTCP request
# Args: prefix command where ctcp msg
function _zirc_ctcp_request_write {
	local msg
	if ! [[ -z "$2" ]] && ! [[ -z "$3" ]]; then
		msg="$2 $3"
	else
		msg="$2"
	fi

	_zirc_write "`printf "PRIVMSG %s :\001%s\001" "$1" "$msg"`"
}

# Write out a CTCP reply
# Args: where ctcp message 
function _zirc_ctcp_reply_write {
	local msg
	if ! [[ -z "$2" ]] && ! [[ -z "$3" ]]; then
		msg="$2 $3"
	else
		msg="$2"
	fi

	_zirc_write "`printf "NOTICE %s :\001%s\001" "$1" "$msg"`"
}

# Handle a VERSION request
function _zirc_ctcp_request_VERSION {
	_zirc_generic_ctcp "$@"
	local pref="$1"
	local who="$(_zirc_prefix_nick "$pref")"

	_zirc_ctcp_reply_write "$who" "VERSION" "ZIRC $ZIRC_VERSION - 100% zsh!!"
}

# Handle a PING request
function _zirc_ctcp_request_PING {
	local pref="$1" msg="$5"
	local who="$(_zirc_prefix_nick "$pref")"
	_zirc_echo "Received a CTCP PING from $who"

	_zirc_ctcp_reply_write "$who" "PING" "$5"
}

# Handle an ACTION
function _zirc_ctcp_reply_ACTION {
	local pref="$1" where="$3" msg="$5"
	local who="$(_zirc_prefix_nick "$pref")"

	if ( _zirc_isme "$where" ); then
		_zirc_compare "$ZIRC_CURRENT" "$who" || ZIRC_LAST="$who"
		_zirc_echo "*** * $who $msg"
	else
		_zirc_echo -n "* $who"
		_zirc_compare "$ZIRC_CURRENT" "$where" || {
			ZIRC_LAST="$where"
			_zirc_echo -n ":$where"
		}
		_zirc_echo " $msg"
	fi
}

# Handle ACTION requests just like replies
function _zirc_ctcp_request_ACTION {
	_zirc_ctcp_reply_ACTION "$@"
}

# Handle all other CTCP requests
function _zirc_ctcp_request_other {
	_zirc_generic_ctcp "$@"
}

# Handle all other CTCP replies
function _zirc_ctcp_reply_other {
	_zirc_generic_ctcp "$@"
}

# Parse the CTCP stuff and call the appropriate CTCP function above
# args: pref command where ctcp+message
function _zirc_command_CTCP {
	local pref="$1" comm="$2"
	local who="$(_zirc_prefix_nick "$pref")"
	shift 2
	local where="$1" msg="$2"
	local ctcp func args ctype
	
	if [[ "$((#msg))" == "1" ]]; then
		msg="${msg#?}"
	fi

	if [[ "$((##${msg[-1]}))" == "1" ]]; then
		msg="${msg%?}"
	fi
	
	ctcp="${(U)msg%% *}"
	msg="${msg#* }"
	if [[ "$msg" == "$ctcp" ]]; then
		msg=""
	fi
	ctype="request"
	[[ "$comm" == "NOTICE" ]] && ctype="reply"

	func="_zirc_ctcp_${ctype}_${ctcp}"

	if [[ -z "${functions[${func}]}" ]]; then
		func="_zirc_ctcp_${ctype}_other"
	fi

	args=( "$pref" "$comm" "$where" "$ctcp" "$msg" )

	"${func}" "$args[@]"
}

# }}}


# Server command handling {{{

# An echo that should be used by anything automatically
# called (commands).  It invalidates the current prompt line first.
function _zirc_echo {
	zle -I
	echo "$@"
}

# Handle private messages
function _zirc_command_PRIVMSG {
	local pref="$1" comm="$2"
	shift 2
	local who="$(_zirc_prefix_nick "$pref")"
	local where="$1" msg="$2"
	if [[ "$((#msg))" == "1" ]]; then
		_zirc_command_CTCP "$pref" "$comm" "$where" "$msg"
		return 0
	fi

	if ( _zirc_isme "$where" ); then
		_zirc_compare "$ZIRC_CURRENT" "$who" || ZIRC_LAST="$who"
		_zirc_echo "*** <$who> $msg"
	else
		_zirc_echo -n "<$who"
		_zirc_compare "$where" "$ZIRC_CURRENT" || {
			_zirc_echo -n ":$where"
			ZIRC_LAST="$where"
		}
		_zirc_echo "> $msg"
	fi
}

# Handle nickname changes
function _zirc_command_NICK {
	local pref="$1" newnick="$3"
	local who="$(_zirc_prefix_nick "$pref")"

	if [[ "$who" == "$ZIRC_CURRENT" ]]; then
		ZIRC_CURRENT="${newnick}"
	fi

	_zirc_isme "$pref" && ZIRC_NICK="$newnick"

	_zirc_echo "$who is now known as $newnick"
}

# Handle channel joins
function _zirc_command_JOIN {
	local pref="$1" comm="$2"
	shift 2
	local who="$(_zirc_prefix_nick "$pref")"
	local at="$(_zirc_prefix_host "$pref")"
	local where="$1"

	_zirc_echo "$who ($at) has joined $where"
	_zirc_isme "$pref" && { 
		ZIRC_LAST="$ZIRC_CURRENT"
		ZIRC_CURRENT="$where" 
		ZIRC_CHANNELS+=("$where") 
	}
}

# Handle channel parts
function _zirc_command_PART {
	local pref="$1" comm="$2" where="$3" msg="$4"
	local who="$(_zirc_prefix_nick "$pref")"

	_zirc_echo "$who has left $where ($msg)"
	_zirc_isme "$pref" && {
		[[ ZIRC_CURRENT == "$where" ]] && ZIRC_CURRENT="$ZIRC_LAST"
		ZIRC_CHANNELS[(r)$where]=()
	}
}

# Handle people quitting
function _zirc_command_QUIT {
	local pref="$1" comm="$2" msg="$3"
	local who="$(_zirc_prefix_nick "$pref")"

	_zirc_echo "$who has quit IRC ($msg)"
}

# Handle topic changes
function _zirc_command_TOPIC {
	local pref="$1" comm="$2" where="$3" msg="$4"
	local who="$(_zirc_prefix_nick "$pref")"

	_zirc_echo "$who has changed the topic in $where to '$msg'"
}

# Handle mode changes
function _zirc_command_MODE {
	local pref="$1" comm="$2" object="$3" mode="$4"
	local who="$(_zirc_prefix_nick "$pref")"
	local rest
	rest=( "${@[4,-1]}" )
	rest=( " "${^rest} )
	rest=${(j::)rest}

	_zirc_echo "$who sets mode $mode ${object}${rest}"
}

# Handle invites
function _zirc_command_INVITE {
	local pref="$1" comm="$2" where="$4"
	local who="$(_zirc_prefix_nick "$pref")"

	_zirc_echo "$who has invited you to $where"
}

# Handle kicks
function _zirc_command_KICK {
	local pref="$1" comm="$2" where="$3" user="$4" msg="$5"
	local who="$(_zirc_prefix_nick "$pref")"

	_zirc_echo "$user was kicked from $where by $who ($msg)"
}

# Handle pong messages (shouldn't get these generally, but just in case)
function _zirc_command_PONG {
}

# Handle notices, although we just forward this onto the privmsg code
function _zirc_command_NOTICE {
	_zirc_command_PRIVMSG "$@"
	return
}

# Handle wallops
function _zirc_command_WALLOPS {
	local pref="$1" comm="$2" msg="$3"
	local who="$(_zirc_prefix_nick "$pref")"
	_zirc_echo "Wallops from $who: $msg"
}

# Handle IRC Errors
function _zirc_command_ERROR {
	local pref="$1" comm="$2"
	shift 2
	_zirc_echo "ERROR: ${(j: :)@}"
}

# Handle server pings
function _zirc_command_PING {
	local pref="$1" comm="$2"
	shift 2
	_zirc_write "PONG :$1"
}

# RPL_WELCOME handler... this will (among other things)
# inform us of our true nickname
function _zirc_command_numeric_001 {
	ZIRC_NICK="$3"
}

# RPL_ISUPPORT this tells us how to handle casemapping
function _zirc_command_numeric_005 {
	case "$4" in
		*casemapping=rfc1459*)
			ZIRC_LOWERCASER="_zirc_lowercase_rfc1459"
			;;
		*casemapping=strict-rfc1459*)
			ZIRC_LOWERCASER="_zirc_lowercase_strict_rfc1459"
			;;
		*casemapping=ascii*)
			ZIRC_LOWERCASER="_zirc_lowercase_ascii"
			;;
	esac
}

# Handle numeric commands
function _zirc_command_numeric {
	local pref="$1" comm="$2"
	_zirc_echo "-- ${(j: :)@[4,-1]}"
	if ! [[ -z "$functions[_zirc_command_numeric_${comm}]" ]]; then
		_zirc_command_numeric_"$comm" "$@"
	fi
}

# catchall for unhandled commands
function _zirc_command_other {
	local EXTENDED_GLOB=1
	local pref="$1" comm="$2"
	shift 2
	if [[ "$comm" == [0-9]## ]]; then
		_zirc_command_numeric "$pref" "$comm" "$@"
		return 0
	fi
}

# }}}


# ZLE hooks {{{

# Parse a single line of incoming data
function _zirc_handle_incoming_data_piece {
	local line comm func output
	line="$1"

	line=( "${(f)$(_zirc_parse_server_line "$line")}" )

	comm="${(U)line[2]}"
	func="_zirc_command_${comm}"

	if [[ -z "${functions[${func}]}" ]]; then
		func="_zirc_command_other"
	fi

	"$func" "$line[@]"
}

# The ZLE informs this when there is data.  Grab the data, and then
# grab any additional data available (using zselect) and passing all
# of this back off to the _zirc_handle_incoming_data_piece function
function _zirc_handle_incoming_data {
	local fds line

	if [[ "$1" != "$ZIRC_FD" ]]; then
		zle -I
		echo "ZIRC: Handling some other file handle???"
		return 1
	fi

	while true; do
		if ! read -r line <&$ZIRC_FD; then
			zle -I
			zirc_disconnect
			return 1
		fi
		if [[ "$((##${line[-1]}))" == "13" ]]; then
			line="${line%?}"
		fi

		_zirc_handle_incoming_data_piece "$line"

		fds=()
		zselect -r -t 0 -a fds $ZIRC_FD
		if [[ "${#fds}" != "2" ]]; then
			break
		fi
	done
}

# }}}


# User commands and utility functions used by them {{{

# Write out a message to the server.  It puts on the correct line ending
# for the IRC protocol too
function _zirc_write {
	local a

	if [[ -z "$ZIRC_FD" ]]; then
		return 1
	fi

	if [[ -z "$1" ]]; then
		return 0
	fi

	a="$@"
	printf "%s\r\n" "$a" >&$ZIRC_FD
}

# Send the initial connection lines to the server
function _zirc_send_connect_lines {
	_zirc_write "NICK $ZIRC_NICK"
	_zirc_write "USER $ZIRC_USER $ZIRC_HOST $ZIRC_SERVER :$ZIRC_NAME"
}

# Input:
#   channel name (or part of a channel name)
# Output:
#   shortest channel that matches *<input>
function _zirc_channel_match {
	local chan="$1"
	local chans shortest a

	chan="*${chan}"
	chans=( "${ZIRC_CHANNELS[@]}" )
	chans=( ${(e):-\${(M)chans:#$chan}} )

	if [[ "${#chans}" == "0" ]]; then
		echo ""
		return 1
	fi

	shortest=" "; shortest="${(l:1000:: :)shortest}"

	for a in "${chans[@]}"; do
		(( ${#a} < ${#shortest} )) && shortest="${a}"
	done	
	
	echo "${shortest}"
	return 0
}

# Switch focus.  This uses _zirc_channel_match so the globbing will make it
# easier to switch channels.
#
#   zirc_switch step
#
# would change to #gnustep if you were correctly connected to that channel
function zirc_switch {
	local chan="$1"
	local chans shortest a
	zirc_connected || { echo "Not connected" ; return 1 }
	if [[ -z "$chan" ]]; then
		echo "Usage: $0 <channel>"
		echo "Switches current channel focus (Current: '${ZIRC_CURRENT}')"
		echo "This command is only used for channels, use zirc_query for a more"
		echo "general solution."
		return 1
	fi

	chan="$(_zirc_channel_match "$chan")"

	if [[ -z "$chan" ]]; then
		echo "Could not find anything for '$1'"
		echo "Current channels: ${(j:,:)ZIRC_CHANNELS}"
		return 1
	fi

	ZIRC_LAST="$ZIRC_CURRENT"
	ZIRC_CURRENT="${chan}"
	echo "Switched to $ZIRC_CURRENT"
	return 0
}

# Just switch focus to the argument.  Don't check against anything else.
function zirc_query {
	local query="$1"
	zirc_connected || { echo "Not connected" ; return 1 }
	if [[ -z "$query" ]]; then
		echo "Usage: $0 <user/channel>"
		echo "Switches current focus. (Current: '${ZIRC_CURRENT}')"
		echo "No expansion will be done on the parameter.  If you are switching"
		echo "to a channel, you may try zirc_switch instead as it will expand to"
		echo "the best match without you having to deal with the escaping."
		return 1
	fi
	ZIRC_LAST="$ZIRC_CURRENT"
	ZIRC_CURRENT="${query%% *}"
	echo "Switched to $ZIRC_CURRENT"
}

# Switch to the last focused window or wherever there was a message last
function zirc_last {
	local a
	[[ -z "$ZIRC_LAST" ]] && return 0
	a="$ZIRC_CURRENT"
	ZIRC_CURRENT="${ZIRC_LAST}"
	ZIRC_LAST="$a"
	echo "Switched to $ZIRC_CURRENT"
}

# Quit (with optional quit message)
function zirc_quit {
	local msg="${(j: :)@}"
	zirc_connected || { echo "Not connected" ; return 1 }

	[[ -z "$msg" ]] && msg="ZIRC $ZIRC_VERSION - 100% zsh, woot."

	_zirc_write "QUIT :$msg"
}

# Print out current focus.  Could be used to hook into prompts
function zirc_focus {
	zirc_connected || { echo "Not connected" ; return 1 }
	echo "$ZIRC_CURRENT"
}

# Print out zirc_* functions
function zirc_help {
	local commands
	commands=( "${(k)functions[@]}" )
	commands=( ${(M)commands:#zirc_*} )
	commands=( ${(o)commands} )
	commands=( "	"${^commands} )
	echo "ZIRC Commands:"
	echo "${(F)commands}"
	return 0
}

# Change nickname to argument
function zirc_nick {
	local nick="$1"
	zirc_connected || { echo "Not connected" ; return 1 }

	if [[ -z "$nick" ]]; then
		echo "Usage: $0 <nick>"
		echo "Change nickname to <nick>."
		return 1
	fi

	_zirc_write "NICK ${nick%% *}"
}

# Message the current focus
function zirc_msg {
	local msg="${(j: :)@}"
	zirc_connected || { echo "Not connected" ; return 1 }
	if [[ -z "$msg" ]]; then
		echo "Usage: $0 <message>"
		echo "Will message the current focus <message>."
		echo "Use zirc_query/zirc_switch to change focus. (Current: '${ZIRC_CURRENT}')"
		return 1
	fi
	if [[ -z "$ZIRC_CURRENT" ]]; then
		echo "No current focus.  Use zirc_query/zirc_switch to change focus."
		return 1
	fi

	echo "<$ZIRC_NICK:$ZIRC_CURRENT> ${msg}"
	_zirc_write "PRIVMSG ${ZIRC_CURRENT} :${msg}"
}

# Message someone (temporarily changing focus then calling zirc_msg)
function zirc_pmsg {
	local who="$1"
	shift
	local msg="${(j: :)@}"
	local temp_current temp_result

	zirc_connected || { echo "Not connected" ; return 1 }
	if [[ -z "$msg" ]] || [[ -z "$who" ]]; then
		echo "Usage: $0 <person> <message>"
		echo "Will message the <person> the message <message>."
		echo "Also see zirc_msg."
		return 1
	fi
	temp_current="$ZIRC_CURRENT"
	ZIRC_CURRENT="$who"
	zirc_msg "$msg"
	temp_result="$?"
	ZIRC_CURRENT="$temp_current"
	return "$temp_result"
}

# Join a channel
function zirc_join {
	local chan="$1"
	zirc_connected || { echo "Not connected" ; return 1 }
	if [[ -z "$chan" ]]; then
		echo "Usage: $0 <channel>"
		echo "Will join the channel <channel>.  If <channel> starts with a letter"
		echo "or a number, it will be prepended with a '#'"
		return 1
	fi
	[[ "$chan" == [a-zA-Z0-9]* ]] && chan="#${chan}"

	_zirc_write "JOIN ${chan%% *}"
}

# Leave a channel (with optional part message)
function zirc_part {
	local chan="$1" unchan="$1"
	shift
	local msg="${(j: :)@}"
	zirc_connected || { echo "Not connected" ; return 1 }
	if [[ -z "$chan" ]]; then
		echo "Usage: $0 <channel> [<msg>]"
		echo "Will leave the channel <channel> with optional part message <msg>."
		echo "This routine will use the same matching method as zirc_switch so"
		echo "you don't have to type the '#' or any other tricky characters" #'" ft=zsh sucks
		return 1
	fi
	chan="$(_zirc_channel_match "$chan")"

	if [[ -z "$chan" ]]; then
		echo "Could not find anything for '$unchan'"
		echo "Current channels: ${(j:,:)ZIRC_CHANNELS}"
		return 1
	fi

	_zirc_write "PART ${chan%% *} :${msg}"
}

# Change the topic in current focus
function zirc_topic {
	local topic="${(j: :)@}"
	zirc_connected || { echo "Not connected" ; return 1 }
	if [[ -z "$topic" ]]; then
		echo "Usage: $0 <topic>"
		echo "Change the topic in the current channel.  Use zirc_switch"
		echo "to change the channel (Current: '$ZIRC_CURRENT')."
		return 1
	fi

	_zirc_write "TOPIC ${ZIRC_CURRENT} :${topic}"
}

# Send a CTCP action to the current focus
function zirc_action {
	local msg="${(j: :)@}"
	zirc_connected || { echo "Not connected" ; return 1 }
	if [[ -z "$msg" ]]; then
		echo "Usage: $0 <message>"
		echo "Send 3rd person action <message>."
		echo "Sends to the current focus (Current: '$ZIRC_CURRENT')."
		return 1
	fi

	echo "* $ZIRC_NICK:$ZIRC_CURRENT ${msg}"
	_zirc_write "$(printf "PRIVMSG $ZIRC_CURRENT :\001ACTION ${msg}\001")"
}

# Print out current topic
function zirc_checktopic {
	zirc_connected || { echo "Not connected" ; return 1 }
	_zirc_write "TOPIC ${ZIRC_CURRENT}"
}

# Returns success if currently connected
function zirc_connected {
	! [[ -z "$ZIRC_FD" ]]
}

# Connect to a server
function zirc_connect {
	if ! [[ -z "$ZIRC_FD" ]]; then
		zirc_disconnect
		zirc_connect
		return
	fi

	if [[ -z "$1" ]]; then
		echo -e "Usage: $0 <server>"
		_zirc_param_help
		return 1
	fi

	ZIRC_SERVER="$1"
	ZIRC_LOWERCASER="_zirc_lowercase_rfc1459"
	ZIRC_CURRENT=""
	ZIRC_CHANNELS=()

	_zirc_param_populate

	ztcp "$ZIRC_SERVER" "$ZIRC_PORT" || return 1
	ZIRC_FD="$REPLY"
	zle -F "$ZIRC_FD" _zirc_handle_incoming_data

	_zirc_send_connect_lines

	echo "Connected to $ZIRC_SERVER on fd #$ZIRC_FD"
	return 0
}

# Disconnect from a server
function zirc_disconnect {
	if [[ -z "$ZIRC_FD" ]]; then
		return 0
	fi
	echo "Disconnecting from $ZIRC_SERVER..."
	zle -F "$ZIRC_FD"
	ztcp -c "$ZIRC_FD"
	eval "$ZIRC_UNSETSTRING"
	return 0
}

# Setup aliases
function zirc_aliases {
	echo "Setting up some convenience aliases"

	alias me='zirc_action'
	alias nick='zirc_nick'
	alias msg='zirc_msg'
	alias pmsg='zirc_pmsg'
	alias quit='zirc_quit'
	alias ctopic='zirc_topic'
	alias topic='zirc_checktopic'
	alias part='zirc_part'
	alias last='zirc_last'
	alias join='zirc_join'
	alias sw='zirc_switch'
	alias query='zirc_query'
	alias connect='zirc_connect'
	alias help='zirc_help'
}
# }}}3


Messages sorted by: Reverse Date, Date, Thread, Author