[gnu.emacs] IRC-mode v 1.2

tale@PAWL.RPI.EDU (David C Lawrence) (08/02/89)

This is the second release of this GNU Emacs client for the Internet Relay
Chat.  Changes since the first release include:

   The bug with sending messages greater than 255 characters to
   an implicit sendlist was fixed.  A small bug with setting the window
   point of a non-selected *IRC* buffer was fixed.  The command parser
   was cleaned up a little so the commands /WHO and /WHOIS can live happily
   together.  

   M-x irc now makes a little bit more sense to have bound to a key.  By
   default it will just go to the buffer of the IRC process if one exists.
   If none does then buffer *IRC* will be used or re-used and a new connexion
   opened.  With a prefix argument then a new IRC process is created in
   addition to any pre-existing ones.  Multiple IRC sessions should be
   able to co-exist peacefully in one Emacs.

   Public messages which were sent by the server as private messages are
   now represented as public to lower the confusion level.

   New commands were added:
    
     o /NAMES to show the nicknames of users on a per-channel basis.
     o /TRACE for IRC Operators to monitor the links between servers.
     o /STAMP for including time-stamping in IRC messages and at intervals.
     o /ALIAS to add preferred names for commands.
     o /REHASH for IRC Operators to reread their server configuration file.

   /HELP for commands has been improved.  Each command now includes a
   \"Usage\" line to show any arguments for the command.

   : now behaves differently than ; when typed as the first character on a
   line in the input region.  It will insert the name of the last person who
   sent a private message.

   Now when you are ignoring someone a message to that effect will be sent
   out if that user sends a private message or invitation to you.
 
   If a message recipient's name is not found it is now sent to whatever
   was provided.  This should make sending to people hiding on channels
   less than 0 a slightly less aggravating experience.

   Many of the functions have been changed to improve the way they get
   called interactively.  For commands expecting a channel number (optional
   or not) a prefix argument will do.  Most commands which take a user's
   name as an argument will now accept it via a completing-read.

   There are more default bindings for commands.

   A texinfo manual on IRC and IRC-mode should be available shortly.

This is rather large so I've split it into three shar files just to help it
along the mailer routes.  Once all files are unpacked simply rejoin them
with "cat irc-[123] > irc.el".  Edit the first two declared variables,
irc-server and irc-port, to suitable defaults for your site.  Then put
ircl.el in one of the directories in load-path and byte-compile for some speed
in loading and processing.  It can be made autoloadable in .emacs with

   (autoload 'irc "irc" "An interface to the Internet Relay Chat." t)

My thanks to Scanner, Nathan Glasser, Fred Douglis, Geoff Goodfellow and
Chris Davis for their helpful comments regarding IRC-mode.  Further comments
are welcome.

Dave
-- 
 (setq mail '("tale@pawl.rpi.edu" "tale@itsgw.rpi.edu" "tale@rpitsmts.bitnet"))

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 1 (of 3)."
# Contents:  irc-1
# Wrapped by tale@life.pawl.rpi.edu on Wed Aug  2 09:11:47 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'irc-1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'irc-1'\"
else
echo shar: Extracting \"'irc-1'\" \(41172 characters\)
sed "s/^X//" >'irc-1' <<'END_OF_FILE'
X;;;;;;;;;;;;;;;;;;;;;;;;;;; -*- Mode: Emacs-Lisp -*- ;;;;;;;;;;;;;;;;;;;;;;;;;;
X;; irc.el --- A user interface for the Internet Relay Chat
X;; Author          : David C Lawrence           <tale@pawl.rpi.edu>
X;; Created On      : Wed Jun 14 22:22:57 1989
X;; Last Modified By: David C Lawrence
X;; Last Modified On: Wed Aug  2 08:30:39 1989
X;; Update Count    : 2
X;; Status          : Seemingly stable.
X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
X;;  Copyright (C) 1989  David C Lawrence
X
X;;  This program is free software; you can redistribute it and/or modify
X;;  it under the terms of the GNU General Public License as published by
X;;  the Free Software Foundation; either version 1, or (at your option)
X;;  any later version.
X
X;;  This program is distributed in the hope that it will be useful,
X;;  but WITHOUT ANY WARRANTY; without even the implied warranty of
X;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X;;  GNU General Public License for more details.
X
X;;  You should have received a copy of the GNU General Public License
X;;  along with this program; if not, write to the Free Software
X;;  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
X
X;; Comments and/or bug reports about this interface should be directed to:
X;;     Dave Lawrence          <tale@{pawl,itsgw}.rpi.edu>
X;;     76 1/2 13th Street     +1 518 273 5385
X;;     Troy NY 12180          Generally available on IRC as "tale"
X
X;; History 		
X;; 2-Aug-1989		David C Lawrence	(tale at imagine.pawl.rpi.edu)
X;;    Last Modified: Wed Aug  2 08:18:37 1989 #2
X;;    * Fixed a minor bug in splitting lines that have an implicit sendlist.
X;;    * Fixed a strange bug in setting the point when *IRC* buffer was visible
X;;      but not selected.
X;;    * Cleaned up the command parsing section to better identify commands
X;;    * Added some more status symbols to who lines.
X;;    * Added aliasing for commands.
X;;    * Added /names, /alias, /unalias and /stamp
X;;    * Added operator status.  Suggested by scanner@itsgw.rpi.edu
X;;    * Added time stamping for both messages and a general sentinel.
X;;    * Changed how ':' works as the first character on a line.  It now expands
X;;      to user who last sent you a private message.  Suggested indirectly by
X;;      Geoff Goodfellow <geoff@fernwood.mpk.ca.us>
X;;    * Ignored users get a message to that effect when they send a PRIVMSG
X;;      or INVITE to the client.
X;;    * Changed what happens when a recipients name is not found.  Rather
X;;      than printing a warning message it will instead attempt to send it
X;;      to exactly the name specified and add it to irc-wholist.  Bourne
X;;      shell programmers will recognize this as resembling the way globbing
X;;      is done.  Came up in conversation with Chris Davis <ckd@bass.bu.edu>.
X;;    * M-x irc either returns to buffer if process is running or just
X;;      reuses *IRC* (without erasing) if no irc-porcess by default. Suggested
X;;      by someone whose name escapes me.  (Fred Douglis, maybe?)
X;;    * Some general tidying of code, especially interactive calling.  Might
X;;      have broken some things with typos though.
X;;    * Added more bindings, some more useful than others.  C-h m looks messy.
X
X;; 22-Jun-1989		David C Lawrence        (tale at imagine.pawl.rpi.edu)
X;;    Last Modified: Thu Jun 22 23:55:01 1989 #1
X;;    * First public release.
X
X;; Defined variables
X(provide 'irc)
X
X(defvar irc-server ""
X  "*A host running the IRC daemon.
XIRC servers generally restrict which machines can maintain connexions with
Xthem, so you'll probably have to find a server in your local domain.")
X
X(defvar irc-port "irc"
X  "*The port on which the IRC server responds.
XMany sites don't have irc as a named service (ie, it has no entry in
X/etc/inetd.conf) so you might have to set this to a number; the most
Xcommon configuration is to have IRC respond on port 6667.")
X
X(defvar irc-oops "Oops ... please ignore that."
X  "*The text to send to the original IRC message recipient when using /OOPS.")
X
X(defvar irc-message-stamp nil
X  "*Mark messages received in IRC with the time of their arrival if non-nil.
XIf this is the symbol 'private' or 'public' then only messages of the specified
Xtype are marked with the time.  WALL messages are always time-stamped.")
X
X(defvar irc-time-stamp 10
X  "*How often to insert a time-stamp into *IRC* buffers.
XThe first time one is based from the hour the IRC process was started so that
Xvalues which divide evenly into 60 minutes (like the default of 10) will split
Xthe hour evenly (as in 13:10, 13:20, 13:30, et cetera).  To disable the
Xtime-stamping set this variable to 0.  This can be set with the /STAMP command.
X
XNote that the way this works you won't always observe the exact amount of
Xminutes between messages, depending on various factors (mostly idle time).
XA time-stamp should never appear more than two minutes late, though.")
X
X(make-variable-buffer-local
X (defvar irc-nick (user-login-name)
X   "*The nickname with which to enter IRC.
XThe default value is set from your login name.  Using /NICKNAME changes it."))
X
X(defvar irc-spacebar-pages t
X  "*When this variable is non-nil, the following keys are in effect when
Xpoint is in the output region.
X
XSPC      scroll-forward    DEL           scroll-backward
XTAB      previous-line     LFD or RET    next-line")
X
X(defvar irc-maximum-size 20480
X  "*Maximum size that the *IRC* buffer can attain, in bytes.
XThe default value of 20k represents an average of about 512 lines, or roughly
X22 screens on a standard 24 line monitor.")
X
X(defvar irc-mode-hook nil
X  "*Hook to run after starting irc-mode but before connecting to the server.")
X
X(defvar irc-pop-on-signal 4
X  "*An integer value means to display the *IRC* buffer when a signal is issued.
XThe number represents roughly how much of the Emacs screen to use when
Xpopping up the IRC window if only one window is visible.  The reciprocal
Xis used, so a value of 1 causes the window to appear full-screen, 2 makes
Xthe window half of the screen, 3 makes it one third, et cetera.  If the value
Xis not an integer then no attempt is made to show the *IRC* buffer if it
Xis not already visible.")
X
X(defvar irc-max-history 40
X  "*The maximum number of messages retained by irc-mode.
XThis limits messages sent, not messages received.  They are stored to be
Xeasily recalled by irc-history-prev and irc-history-next (C-c C-p and C-c C-n
Xby default).")
X
X(defvar irc-confirm t
X  "*If non-nil, provide confirmation for messages sent on IRC.
XIt should be noted that confirmation only indicates where irc-mode
Xtried to send the message, not whether it was actually received.
XUse the /CONFIRM command to change.")
X
X(defvar irc-processes nil
X  "All currently open streams to irc-servers are kept in this list.
XIt is used so that the 'irc' function knows whether to start a new process
Xby default.")
X
X(make-variable-buffer-local
X (defvar irc-signals '((private t) (invite t) (wall t)
X                       (public) (join) (nick) (user))
X   "Events in IRC that should get signalled when they occur.
XGenerally the signal is an audible beep.  The value of irc-signals is an
Xassociation list of events recognized by irc-mode and is maintained with
Xthe /SIGNAL command."))
X
X(make-variable-buffer-local
X (defvar irc-ignores nil
X   "A list of users whose events will be ignored.
XMessages or other actions (like joining a channel) generated by anyone in
Xthe list will not be displayed or signalled.  This list is maintained with
Xthe /IGNORE and /UNIGNORE commands."))
X
X(make-variable-buffer-local
X (defvar irc-notifies '(join nick)
X   "Events in IRC that should get notification messages.
XCurrently only the \"join\"and \"nick\" events are supported due to limitations
Xof the servers but that is expected to change in the near future.
XThis list is maintained with the /NOTIFY command."))
X
X(make-variable-buffer-local
X (defvar irc-history nil
X   "A list of messages which irc-mode has processed.
XThis includes both successfully and unsuccessfully sent messages, starting
Xwith the most recent first.  irc-max-history limits the number of items
Xthat can appear in the list when using irc-add-to-history."))
X
X(make-variable-buffer-local
X (defvar irc-default-to "*;"
X   "The default recipient of a message if no : or ; is provided.
X\"*\" means the current channel, no matter what it is."))
X
X(defvar irc-mode-map nil
X  "The keymap which irc-mode uses.
X
XCurrently set to: \\{irc-mode-map}")
X
X(defvar irc-alias-alist
X  '(("QUERY" . "send")        ; For people used to the C client
X    ("N" . "names")           ; ditto
X    ("W" . "who")             ; one more time
X    ("?" . "help")            ; nice abbrev
X    ("TF" . "time tut.fi")    ; surprising useful-won't work if tut.fi isn't up
X    ("IDLE" . "who")          ; for people like me who are too used to Connect
X    ("LONGWHO" . "whois")     ; from the first release of irc-mode
X    ("WHAT" . "list") ("L" . "list") ; /WHAT from Connect
X    ("EXIT" . "quit") ("BYE" . "quit") ("STOP" . "quit")) ; Plenty of ways out
X  "An association list of command aliases used in irc-mode.
XThis is the first list checked when irc-mode is looking for a command and it
Xis maintained with the /ALIAS and /UNALIAS commands.")
X
X(defconst irc-command-alist
X  '(("WHO" . "who")           ; For a list of users and their channels
X    ("HELP" . "help")         ; Get help on the /COMMANDs
X    ("INFO" . "info")         ; Information about the IRC author
X    ("LIST" . "list")         ; Show a list of channels and topics
X    ("OOPS" . "oops")         ; Resend a misdirected message
X    ("OPER" . "oper")         ; Login as an IRC operator
X    ("QUIT" . "quit")         ; Exit IRC
X    ("SEND" . "send")         ; Set the implicit send list for messages
X    ("TIME" . "time")         ; Get the current time from a server
X    ("MSG" . "privmsg")       ; Send a private message to someone
X    ("ADMIN" . "admin")       ; Get information about IRC admins
X    ("LINKS" . "links")       ; Show which servers are in the IRC-net
X    ("NAMES" . "names")       ; Display names of users on each channel
X    ("QUOTE" . "quote")       ; Send raw text to the server
X    ("TOPIC" . "topic")       ; Change the topic of the current channel
X    ("USERS" . "users")       ; Show users signed on at a server
X    ("WHOIS" . "whois")       ; Get slightly longer information about a user
X    ("STAMP" . "stamp")       ; Set time notification interval
X    ("INVITE" . "invite")     ; Ask another user to join your channel
X    ("NOTIFY" . "notify")     ; Change which events give notification
X    ("SIGNAL" . "signal")     ; Change which events give a signal
X    ("SUMMON" . "summon")     ; Ask a user not on IRC to join it
X    ("NICKNAME" . "nick")     ; Change your IRC nickname
X    ("CONFIRM" . "confirm")   ; Set message confirmation on or off
X    ("REDIRECT" . "redirect") ; Resend the last message to someone else
X    ("AWAY" . "away") ("HERE" . "here") ; Give some indication of your presence
X    ("JOIN" . "join") ("LEAVE" . "leave") ; Join or leave a channel
X    ("ALIAS" . "alias") ("UNALIAS" . "unalias") ; Add/remove command aliases
X    ("IGNORE" . "ignore") ("UNIGNORE" . "unignore")) ; (Un)Ignore a user
X  "An association list of the regular commands to which all users have access.
XForm is (\"COMMAND\" . \"function\") where \"function\" is that last element in
Xan irc-execute-* symbol.  See also irc-alias-alist and irc-operator-alist.")
X
X(defconst irc-operator-alist
X  '(("KILL" . "kill")         ; Forcibly remove a user
X    ("WALL" . "wall")         ; Send a message to everyone on IRC
X    ("TRACE" . "trace")       ; Show the links between servers
X    ("REHASH" . "rehash"))    ; Reread irc.conf
X  "As association list of commands which only an IRC Operator can use.
XIt is kept as a separate list so that regular users won't wonder how
Xcome the commands don't work for them.")
X
X(defconst irc-version "IRC-mode Version 1.2"
X  "The currently loaded version of irc-mode")
X
X;; keymap
X;; what i tend to like for keys might be very different from what most people
X;; find useful; in fact i tend to type the /COMMANDs more than use any bindings
X
X;; there are more things bound here just so people can see the different
X;; things available to them
X(or irc-mode-map
X    (progn
X      (setq irc-mode-map (make-keymap))
X      (define-key irc-mode-map "\C-j" 'irc-process-input)
X      (define-key irc-mode-map "\C-m" 'irc-process-input)
X      (define-key irc-mode-map "\C-i"      'irc-tab)
X      (define-key irc-mode-map "\C-c\C-a"  'irc-execute-alias)
X      (define-key irc-mode-map "\C-c\C-c"  'irc-execute-names)
X      (define-key irc-mode-map "\C-c\C-h"  'irc-execute-help)
X      (define-key irc-mode-map "\C-c\C-i"  'irc-execute-invite)
X      (define-key irc-mode-map "\C-c\C-j"  'irc-execute-join)
X      (define-key irc-mode-map "\C-c\C-l"  'irc-execute-list)
X      (define-key irc-mode-map "\C-c\C-m"  'irc-history-menu)
X      (define-key irc-mode-map "\C-c\C-n"  'irc-history-next)
X      (define-key irc-mode-map "\C-c\C-o"  'irc-execute-oops)
X      (define-key irc-mode-map "\C-c\C-p"  'irc-history-prev)
X      (define-key irc-mode-map "\C-c\C-q"  'irc-execute-leave)
X      (define-key irc-mode-map "\C-c\C-r"  'irc-execute-redirect)
X      (define-key irc-mode-map "\C-c\C-s"  'irc-execute-send)
X      (define-key irc-mode-map "\C-c\C-u"  'irc-kill-input)
X      (define-key irc-mode-map "\C-c\C-w"  'irc-execute-who)
X      (define-key irc-mode-map "\C-c\C-?"  'irc-kill-input)
X      ;; it's nice to bind to a key while in development, but regular users
X      ;; wonder about it in production.
X      ;; (define-key irc-mode-map "\C-c " 'irc-pong)
X      (define-key irc-mode-map "\C-ca" 'irc-execute-admin)
X      (define-key irc-mode-map "\C-ci" 'irc-execute-info)
X      (define-key irc-mode-map "\C-ck" 'irc-execute-quit)
X      (define-key irc-mode-map "\C-cl" 'irc-execute-links)
X      (define-key irc-mode-map "\C-cn" 'irc-news)
X      (define-key irc-mode-map "\C-co" 'irc-execute-oper)
X      (define-key irc-mode-map "\C-cp" 'irc-yank-prev-command)
X      (define-key irc-mode-map "\C-cq" 'irc-execute-quote)
X      (define-key irc-mode-map "\C-cs" 'irc-execute-summon)
X      (define-key irc-mode-map "\C-cu" 'irc-execute-users)
X      (define-key irc-mode-map "\C-cv" 'irc-version)
X      (define-key irc-mode-map "\C-?"  'irc-del-backward-char)
X      ;; make any self-inserting keys call irc-self-insert
X      (mapcar (function
X               (lambda (key)
X                 (define-key irc-mode-map key 'irc-self-insert)))
X              (where-is-internal 'self-insert-command nil nil))))
X
X;; filters (mostly irc-parse-*)
X;; Filtering of server messages from reception to insertion in the buffer
X;; are all done on this page.  In particular, if a new server message has
X;; to be dealt with, it should be added in the irc-parse-server-msg function.
X(defun irc-filter (proc str)
X  "Filtering procedure for IRC server messages.
XIt waits until everything up to a newline is accumulated before handing the
Xstring over to irc-parse-server-msg to be processed.  If irc-pop-on-signal
Xis an integer and a signal is issued then the *IRC* buffer will be displayed."
X  (let* ((ibuf (process-buffer proc))
X         bell irc-mark-to-point new-point old-irc-mark win)
X    (save-excursion
X      (set-buffer ibuf)
X      ;; still can't tell why i need this; sure, i probably change point
X      ;; in ibuf.  but so what?  set-window-point should clean up after that.
X      ;; it works with it though and not without it, so it stays.
X      (save-excursion 
X        (irc-truncate-buffer irc-maximum-size) ; trim buffer if needed
X        (setq irc-mark-to-point   ; so we can restore relative position later
X              (- (point) (setq old-irc-mark (goto-char irc-mark)))
X              ;; just glue str to the end of any partial line that's there
X              irc-scratch (concat irc-scratch str))
X        ;; see if it is time for a message
X        (irc-check-time)
X        (while (string-match "\n" irc-scratch) ; do as many lines as possible
X          ;; first use irc-scratch for the dp returned by irc-parse-server-msg
X          (setq irc-scratch (irc-parse-server-msg irc-scratch)
X                bell (cdr irc-scratch) ; issue a signal?
X                ;; now irc-scratch is what it was, minus the line parsed
X                irc-scratch (car irc-scratch))
X          (if (not bell) ()
X            ;; issue a signal;  no need to trash someone's kbd-macro over it
X            (ding 'no-terminate)
X            (minibuffer-message " [Bell in %s]" (buffer-name ibuf))))
X        ;; if point was in front of the irc-mark, new-point is figured relative
X        ;; to the old mark, otherwise it is relative to the new one
X        (setq new-point (+ (if (< irc-mark-to-point 0) old-irc-mark irc-mark)
X                           irc-mark-to-point))))
X    ;; update point based on whether the buffer is visible
X    ;; we have a real problem here if there is more than one window displaying
X    ;; the process-buffer and the user is not in the first canonical one.
X    ;; i haven't yet determined a nice way to solve this
X    (if (setq win (get-buffer-window ibuf))
X        (set-window-point win new-point)
X      (save-excursion (set-buffer ibuf) (goto-char new-point)))
X    ;; if *IRC* isn't visible, a bell was issued and irc-pop-on-signal is an
X    ;; integer then show the buffer.
X    (if (not (and (integerp irc-pop-on-signal) bell (not win))) ()
X      (setq win (selected-window))
X      (if (/= (count-windows 'no-mini) 1)
X          (display-buffer ibuf) ; don't futz with sizes if more than 1 window
X        ;; we might be in the mininbuffer at the moment, so insure that
X        ;; this happens starting in the current regular window
X        (select-window (next-window win 'no-mini))
X        ;; full screen doesn't get handled well by the algorithm for the rest
X        (if (= irc-pop-on-signal 1)
X            (set-window-buffer (selected-window) ibuf)
X          (split-window nil (- (screen-height)
X                               (/ (screen-height) irc-pop-on-signal)))
X          (display-buffer ibuf)
X          ;; perhaps we need to go back to the minibuffer
X          (select-window win))))))
X
X(defun irc-parse-server-msg (str)
X  "Take the first line from STR and pass it on to the appropriate irc-parse-*
Xfunction.  If the message is one which irc-mode doesn't recognize, just display
Xit as the raw server message.
X
XIt returns a dotted-pair whose car is the remainder of STR after the first
Xnewline and whose cdr is either t or nil, indicating whether a signal should
Xbe issued."
X  (let ((loc 0) (line (substring str 0 (string-match "\n" str))))
X    ;; need to double % signs or formatting later down the line will attempt
X    ;; to interpret them.
X    (while (string-match "%" line loc)
X      (setq line (concat (substring line 0 (match-end 0)) "%"
X                         (substring line (match-end 0)))
X            loc (1+ (match-end 0))))
X    (cons
X     ;; the part of str not being parsed.
X     (substring str (1+ (string-match "\n" str)))
X     (cond
X      ;; each function here should return t or nil indicating whether
X      ;; to issue a signal.  Some of these regexps are fugly because
X      ;; of inconsistent protocol use by the servers.  Fortunately Jarkko
X      ;; is fixing that.
X      ((string-match "^:\\S +\\s +CHANNEL" line) (irc-parse-channel line))
X      ((string-match "^:\\S +\\s +INVITE" line) (irc-parse-invite line))
X      ((string-match "^:\\S +\\s +MSG" line) (irc-parse-public line))
X      ((string-match "^:\\S +\\s +NICK" line) (irc-parse-nick line))
X      ((string-match "^:\\S +\\s +WALL" line) (irc-parse-wall line))
X      ((string-match "^:\\S +\\s +QUIT" line) (irc-parse-quit line))
X      ;; sigh.  just ^NOTICE was fine until someone used the protocol wrong
X      ((string-match "^\\(: \\)?NOTICE" line) (irc-parse-notice line))
X      ;; ditto!!  private messages should just be for messages between users!!
X      ((string-match "^\\(:\\S *\\s +\\)?PRIVMSG" line) (irc-parse-priv line))
X      ((string-match "^PING" line) (irc-pong))
X      ((string-match "^ERROR" line) (irc-parse-error line))
X      ((string-match "^WHOREPLY" line) (irc-parse-whoreply line))
X      ((string-match "^NAMREPLY" line) (irc-parse-namreply line))
X      ((string-match "^LINREPLY" line) (irc-parse-linreply line))
X      (t (irc-insert str)
X         (irc-insert
X          "(Please let tale@pawl.rpi.edu know about this; it might be a bug.)")
X         nil)))))
X
X(defun irc-parse-channel (str)
X  "Examine a CHANNEL message from the IRC server.
XCHANNEL indicates a user entering or leaving the channel which you are on.
XIf the user is not being ignored and 'join' is in irc-notifies, a message
Xis inserted indicating the change.  A message is always provided as
Xconfirmation if the user is the irc-mode user.
X
XThis function returns t if a bell should be issued for the 'join' event,
Xnil otherwise."
X  (let ((user (substring str 1 (string-match "\\s CHANNEL " str)))
X        (channel (string-to-int (substring str (match-end 0)))))
X    ;; make sure that user is in the wholist since we have to take
X    ;; this sort of information where we can until Jarkko supports
X    ;; global ENTER/QUIT messages (which he might not do ...)
X    (irc-maintain-list 'irc-wholist user 'add)
X    (if (string= user irc-nick)
X        (progn
X          (if (zerop channel)
X              (irc-insert "You have left channel %d."
X                          (get 'irc-channel 'o-chan))
X            (irc-insert "You are now a member of channel %d." channel))
X          nil)                  ; don't issue a bell for our own join
X      (if (or (member-general user irc-ignores 'string=)
X              (not (memq 'join irc-notifies))) ()
X        (if (zerop channel)
X            (irc-insert "*** %s has left channel %d ***" user irc-channel)
X          (irc-insert "*** %s has joined channel %d ***" user channel))
X        (irc-signal user 'join))))) ; check for signal for join
X
X(defun irc-parse-invite (str)
X  "Examine an INVITE message from the IRC server.
XINVITE is sent when one user invites another to a channel.
XIf the inviter is not being ignored a message is inserted in the buffer.
X
XThis function returns t if a bell should be issued for the 'invite' event,
Xnil otherwise."
X  (let ((user (substring str 1 (string-match "\\s +INVITE " str)))
X        (to (substring str (match-end 0)
X                       (string-match "\\s +" str (match-end 0))))
X        (channel (substring str (match-end 0))))
X    ;; glom a new name, if necessary
X    (irc-maintain-list 'irc-wholist user 'add)
X    (if (member-general user irc-ignores 'string=)
X        (irc-send (concat "PRIVMSG " user " :You are being ignored."))
X      (irc-insert "*** %s invites %s to join channel %s ***" user
X                  ;; i wish the downcases weren't necessary, but the servers
X                  ;; are inconsistent.  anyway, this should return "you" 99%
X                  ;; of the time; if it doesn't something weird is happening.
X                  (if (string= (downcase to) (downcase irc-nick)) "you" to)
X                  channel)
X      (irc-signal user 'invite))))
X
X(defun irc-parse-public (str)
X  "Examine a MSG message from the IRC server.
XMSG is sent when someone has sent a message to a channel.  In reality,
Xsometimes PRIVMSG is used but irc-parse-private should hand those off to
Xto here.
X
XThis function returns t if a bell should be issued for the 'public' event,
Xnil otherwise."
X  (let ((user (substring str 1 (string-match "\\s MSG :" str)))
X        (msg (substring str (match-end 0))))
X    ;; even here we can't guarantee that the sender has already been noted
X    ;; someplace else like join or nick -- the sender might be someplace
X    ;; else and sending to this channel with PRIVMSG.
X    (irc-maintain-list 'irc-wholist user 'add)
X    (if (member-general user irc-ignores 'string=) ()
X      (irc-insert "\n ->%s From %s to %d:" 
X                  (if (and irc-message-stamp
X                           (not (eq 'private irc-message-stamp)))
X                      (concat " (" (irc-get-time) ")")
X                    "")
X                  user irc-channel)
X      (irc-insert-message msg)
X      (irc-signal user 'public))))
X
X(defun irc-parse-priv (str)
X  "Examine a PRIVMSG message from the IRC server.
XPRIVMSG is intended to be used for private message sent between users.
XThis is not always the case at the moment; servers will use it like either
XNOTICE or MSG on occasion.
X
XIf it really is a private message, this function returns t if a signal should
Xbe issued for the 'private' event, nil otherwise."
X  ;; This is really gross because it kludges in the fact that PRIVMSG can
X  ;; be used to send notification of a change of channel topic.  Actually,
X  ;; topic changes are handled poorly all around by the servers because
X  ;; only the person who changed the topic gets notification.
X  ;; Also have to kludge in the fact that TIME to a remote host gives back
X  ;; a PRIVMSG with no sender but with a leading :.  ARGHGHGHG!!
X  (let (from to msg)
X    (if (string-match "^:\\S +\\s +PRIVMSG\\s +" str)
X        ;; there was a sender.  this is a real private message.
X        (setq from (substring str 1 (string-match "\\s +PRIVMSG\\s +" str))
X              to (substring str (match-end 0)
X                            (string-match "\\s +:" str (match-end 0))))
X      (setq from nil          ; no sender.  schade.  broken protocol.
X            to (substring str 9 (string-match "\\s :" str))))
X    (setq msg (substring str (match-end 0)))
X    (if (not from)
X        ;; not really a private message.  whatever it was just show it
X        ;; and don't worry about signalling it.
X        (progn (irc-insert msg) nil)
X      (if (and (= (string-to-int to) irc-channel) (not (zerop irc-channel)))
X          ;; whoops.  more brain-damage.  this really should be indicated
X          ;; as a public message since it went to the channel.
X          (irc-parse-public (concat ":" from " MSG :" msg))
X        ;; sigh.  this function gets called too much.
X        (irc-maintain-list 'irc-wholist from 'add)
X        ;; skip the messages if sender is being ignored
X        (if (member-general from irc-ignores 'string=)
X            (irc-send (concat "PRIVMSG " user " :You are being ignored."))
X          (irc-insert "\n >>%s Private message from %s:"
X                      (if (and irc-message-stamp
X                               (not (eq 'public irc-message-stamp)))
X                          (concat " (" (irc-get-time) ")")
X                        "")
X                      from)
X          (setq irc-last-private (concat from ":"))
X          (or (string= (downcase to) (downcase irc-nick))
X              ;; this should never show up.  if it does something is broken.
X              (irc-insert " (apparently to %s)" to))
X          (irc-insert-message msg)
X          (irc-signal from 'private))))))
X
X(defun irc-parse-quit (str)
X  "Examine a QUIT message from the IRC server.
XQUIT is used to tell of a user's departure from IRC.  It is currently sent
Xby the servers to those clients which are on the same channel as the
Xdeparting user.
X
XThis function returns t if a signal should be issued for the 'join' event,
Xsince it also signals someone leaving the channel.  It returns nil if no
Xbell should be issued."
X  (let ((user (substring str 1 (string-match "\\s +QUIT" str))))
X    (irc-maintain-list 'irc-wholist user 'remove)
X    (if (member-general user irc-ignores 'string=) ()
X      (irc-insert "*** %s has left IRC ***" user)
X      ;; currently just the join event; some modification will need to be made
X      ;; here when/if Jarkko has QUIT sent to everyone, not just the channel
X      (irc-signal user 'join))))
X
X(defun irc-parse-wall (str)
X  "Examine a WALL message from the IRC server.
XWALL is sent by IRC operators to everyone on IRC.  A WALL message will
Xalways be displayed even if the sender is being ignored.
X
XThis function returns t if a signal should be issued for the 'wall' event,
Xnil otherwise."
X  (let ((user (substring str 1 (string-match "\\s +WALL\\s +:" str)))
X        (msg (substring str (match-end 0))))
X    ;; sigh.  okay class, can anyone tell me why we're calling this yet again?
X    (irc-maintain-list 'irc-wholist user 'add)
X    (irc-insert "\n ## Message from %s at %s to everyone:" (irc-get-time) user)
X    (irc-insert-message msg)
X    (irc-signal user 'wall)))
X
X(defun irc-parse-nick (str)
X  "Examine a NICK message from the IRC server.
XNICK is sent when a user's nickname is changed, but it is only sent to the
Xpeople on the same channel as the user.  If the person changing names is
Xbeing ignored, this fact is tracked across the change.  If notification
Xis not enabled for 'nick' then no message is inserted.
X
XThis function returns t if a signal should be issued for the 'nick' event,
Xnil otherwise."
X  (let ((old (substring str 1 (string-match "\\s NICK " str)))
X        (new (substring str (match-end 0))))
X    (irc-maintain-list 'irc-wholist old 'remove)
X    (irc-maintain-list 'irc-wholist new 'add)
X    ;; hey!!  someone changed the server, I think.  I could of sworn it
X    ;; used to reply when you changed your own name!!  @*$%##@!!
X    ;; --found the problem.  it only sends the message if you are on a channel.
X    ;; so much for confirmation of your change.  have to kludge it into ERROR.
X    ;; note that if the following segment becomes usable again (as Jarkko
X    ;; indicated it might) then the quotes arond the whole thing need to be
X    ;; removed.  they're only there becase of strangeness with eval-defun.
X;"    (if (string= old irc-nick)
X;        (progn (setq irc-nick new)
X;               (put 'irc-nick 'o-nick old)
X;               (irc-insert "You will now be known as %s." new) nil)"
X    (if (member-general old irc-ignores 'string=)
X        ;; track the 
X        (progn (irc-maintain-list 'irc-ignores old 'remove)
X               (irc-maintain-list 'irc-ignores new 'add)
X               nil)           ; no signal for ignored user
X      (if (or (not (memq 'nick irc-notifies)) (string= new irc-nick)) () 
X        (irc-insert "*** %s is now known as %s ***" old new)
X        (irc-signal old 'user)))))
X
X(defun irc-parse-error (str)
X  "Examine an ERROR message from the IRC server.
XERROR is used when something bogus happens like an unparsable command
Xis issued to the server.  Usually this will not happen unless something
Xlike /QUOTE is used.  This message is also used when a user attempts to
Xchange to a name that already exists.
X
XReturns nil; currently no signals are issued for an error."
X  (string-match "\\s +:" str)
X  (irc-insert (substring str (match-end 0)))
X  (if (string-match "Nickname\\s +\\S *\\s +\\(already\\|not\\s +chan\\)" str)
X      (progn
X        ;; either we couldn't change the current nickname
X        (setq irc-nick (or (get 'irc-nick 'o-nick)
X                           ;; or we never even had one
X                           "NO NAME YET (/NICK to set one)"))
X        (set-buffer-modified-p (buffer-modified-p))
X        (irc-insert (if (get 'irc-nick 'o-nick)
X                        "Hmmm ... looks like you're still %s."
X                      "%s") irc-nick)))
X  nil)
X
X(defun irc-parse-notice (str)
X  "Examine a NOTICE message from the IRC server.
XNOTICE is the catch-all for IRC messages; if it can't be classified as
Xone of the other currently existing messages then the information is
Xsent as NOTICE.  This message is overused, even when it another could be
Xused instead.  For example, if an attempt is made to send to a nickname
Xwhich is not on IRC the error reply is sent via NOTICE.
X
XNo signal is issued for NOTICE because it is way too random with what it
Xmeans."
X  (string-match "\\s +:" str)
X  (let ((msg (substring str (match-end 0))))
X    (irc-insert msg)
X    (cond
X     ((string-match "^\\*\\*\\* Error: No such nickname (\\(.+\\))$" msg)
X      ;; oops.  we sent to someone who wasn't really there.
X      (irc-maintain-list 'irc-wholist
X                         (substring msg (match-beginning 1) (match-end 1))
X                         'remove))
X     ((string-match "^Good afternoon, gentleman\\. I am a HAL 9000" msg)
X      ;; we've been granted operator priviledges.  the string is for mode-line
X      (setq irc-operator " Operator")
X      (set-buffer-modified-p (buffer-modified-p)))))
X  nil)
X
X(defun irc-parse-whoreply (str)
X  "Examine a WHOREPLY message from the IRC server.
XThe message is formatted into a line that is more easily understood than
Xthe raw data.  People who have marked themselves as AWAY are preceded by a
X'-' (provided the AWAY message has propogated to this client's server);
Xusers being ignored are marked by a '#'; IRC Operators are shown with a '*';
Xand IRC Operators who are away are shown as '='.  '#' has priority over
Xthe others because if a user is being ignored the other information
Xabout that user's status is not really relevant.
X
XNo signals are issued for lines from the WHOREPLY."
X  (string-match "^WHOREPLY\\s +" str)
X  (setq str (substring str (match-end 0)))
X  (let (split) ; make this a list of strings of each data item.
X    ;; the elements of 'split' are:
X    ;; 0 - full name     2 - nickname     4 - hostname      6 - channel
X    ;; 1 - status        3 - server       5 - login name
X    (while (not (string-match "^:" str))
X      (setq split (cons (substring str 0 (string-match "\\(\\s +\\|$\\)" str))
X                        split)
X            str (substring str (match-end 0))))
X    (setq split (cons str split))
X    (if (string= (nth 1 split) "S") ()
X        ;; if it isn't the bogus header, add nick
X        (irc-maintain-list 'irc-wholist (nth 2 split) 'add))
X    (irc-insert (concat
X                 (if (member-general (nth 2 split) irc-ignores 'string=) "#"
X                   (cond ((string= "H"  (nth 1 split)) " ")
X                         ((string= "H*" (nth 1 split)) "*")
X                         ((string= "G"  (nth 1 split)) "-")
X                         ((string= "G*" (nth 1 split)) "=")
X                         ((string= "S"  (nth 1 split)) " ")
X                         (t (nth 1 split)))) ; no clue what it is; just use it
X                 (nth 2 split)
X                 ;; pad some spaces in
X                 (make-string (- 10 (length (nth 2 split))) 32)
X                 (format "%4s " (if (zerop (string-to-int (nth 6 split)))
X                                    ;; bogus header; translate * to "Chan"
X                                    (if (string= "*" (nth 6 split)) "Chan" "")
X                                  (nth 6 split)))
X                 (nth 5 split) "@" (nth 4 split) " ("
X                 (substring (car split) 1) ")")))
X  nil)
X
X(defun irc-parse-linreply (str)
X  "Examine a LINREPLY message from the IRC server.
XLINREPLY is used to answer a LINKS request to show all the servers on line.
X\"Links\" is a bit of a misnomer since little information regarding the
Xactual structure of the IRCnet can be gained from these messages.
X
XNo signals are issued for lines from the LINREPLY."
X  (string-match "^LINREPLY\\s +\\(\\S +\\)\\s +" str)
X  (irc-insert "Server: %s (%s)"
X              (substring str (match-beginning 1) (match-end 1))
X              (substring str (match-end 0)))
X  nil)
X
X(defun irc-parse-namreply (str)
X  "Examine a NAMREPLY message from the IRC server.
XNAMREPLY is used in repsonse to NAMES to indicate what users are on what
Xchannels.  All users on secret or private channels which the client is not
Xon are grouped together on one private channel.
X
XNo signals are issued for NAMREPLYs."
X  (string-match "^NAMREPLY\\s +\\S +\\s +\\(\\S +\\)\\s +" str)
X  (let* ((channel (substring str (match-beginning 1) (match-end 1)))
X         (users (substring str (match-end 0)))
X         (to-insert (format "%7s "
X                            (if (string= "*" channel) "Private" channel)))
X         nick)
X    ;; yet another source of information for irc-wholist.
X    (while (string-match "^\\(\\S +\\)\\(\\s \\|$\\)" users)
X      (setq nick (substring users 0 (match-end 1))
X            users (substring users (match-end 0)))
X      (irc-maintain-list 'irc-wholist nick 'add)
X      ;; parsing by name also means we can format a long line nicely
X      ;; question: why do programmers so frequently use "we" in comments?
X      (if (<= (+ (length to-insert) (length nick)) 78)
X          (setq to-insert (concat to-insert " " nick))
X        (irc-insert to-insert)
X        (setq to-insert (make-string 8 32))))
X    (irc-insert to-insert))
X  nil)
X
X(defun irc-pong ()
X  "Send a PONG message with the hostname as an argument.
XThis is usually used to answer a PING message."
X  ;; it's interactive so it can be bound during testing.
X  (interactive) (irc-send (concat "PONG " (system-name))) nil)
X
X(defun irc-insert-message (msg)
X  "Format MSG by word-wrapping into 75 characters or less.
XIf a word is too long to be split this way then it is broken at the 75th
Xcharacter and continued on the next line as if white space had been there.
XEach line is prefixed with the string \" - \" and passed to irc-insert for
Xthe actual insertion into the buffer."
X  (let (line)
X    (while (> (length msg) 75)
X      (setq line (substring msg 0 76) msg (substring msg 76))
X      (cond ((string-match "^\\s +" msg)
X             ;; broke at whitespace; strip leading space from next line
X             (setq msg (substring msg 1)))
X            ((string-match "\\s +$" line)
X             ;; trailing whitespace on line.  might as well just nuke it all.
X             (setq line (substring line 0 (match-beginning 0))))
X            ((string-match "\\(\\s +\\)\\S +$" line)
X             ;; broke in a word, but it's wrappable.  just eat one space.
X             (setq msg (concat (substring line (1+ (match-beginning 1))) msg)
X                   line (substring line 0 (match-beginning 0)))))
X      (irc-insert (concat " - " line)))
X    (irc-insert (concat " - " msg))))
X
X(defun irc-insert (format &rest args)
X  "Insert before irc-mark the string created by FORMAT with substituted ARGS.
XWord-wrapping is done to make lines of length less than or equal to 79
Xcharacters.  If a word is too long to be wrapped it is cut at the 79th column
Xas though white space were there."
X  (let ((str (apply 'format format args)) line)
X    (save-excursion
X      (goto-char irc-mark)
X      (while (> (length str) 79)
X        (setq line (substring str 0 80) str (substring str 80))
X        ;; yes, it's just the same as in irc-insert-message
X        (cond ((string-match "^\\s +" str)
X               ;; broke at whitespace; strip leading space from next line
X               (setq str (substring str 1)))
X              ((string-match "\\s +$" line)
X               ;; trailing whitespace on line.  might as well just nuke it all.
X               (setq line (substring line 0 (match-beginning 0))))
X              ((string-match "\\(\\s +\\)\\S +$" line)
X               ;; broke in a word, but it's wrappable.  just eat one space.
X               (setq str (concat (substring line (1+ (match-beginning 1))) str)
X                     line (substring line 0 (match-beginning 0)))))
X        (insert-before-markers (concat line "\n")))
X      (insert-before-markers (concat str "\n")))))
X
X;; simple key functions -- self-insert, tab, destructive backspace
X(defun irc-self-insert (arg)
X  "Normally just inserts the typed character in the input region.
XIf point is in the output region, irc-spacebar-pages is non-nil and a space
Xis typed, scroll-up (aka window-forward) otherwise point moves to end of input
Xregion and inserts the character.
X
XIf the character to be inserted is a colon or semi-colon and it is the first
Xnon-white space charavter on the line then the input region is updated to
Xbegin with the last explicit sendlist, irc-last-explicit.
X
XInserts the character ARG times if self-inserting.  An argument is not
Xpassed to scroll-up if paging with the spacebar."
X  (interactive "p")
X  (let* ((in-region (>= (point) irc-mark))
X         ;; it's times like this that i wish someone would tell me what
X         ;; a good indentation style is for this expression
X         (expand-colon
X          (and
X           (or (= last-input-char ?:) (= last-input-char ?\;))
X           (string-match
X            "^\\s *$"
X            (buffer-substring irc-mark (if in-region (point) (point-max)))))))
X    (if (not expand-colon)
X        (if in-region (self-insert-command arg)
X          (if (and irc-spacebar-pages (= last-input-char 32))
X              ;; it's nice to be able to return to the input region just by
X              ;; pounding on the spacebar repeatedly.
X              (condition-case EOB (scroll-up nil)
X                (end-of-buffer (goto-char (point-max))))
X            (goto-char (point-max))
X            (self-insert-command arg)))
X      (or in-region (goto-char (point-max)))
X      ;; kill white space.  This also takes out previous lines in input region.
X      (delete-region irc-mark (point))
X      (insert (if (= last-input-char ?:) irc-last-private irc-last-explicit))
X      ;; put in the extra characters if need be.
X      (if (> arg 1) (self-insert-command (1- arg))))))
X
X(defun irc-del-backward-char (arg)
X  "If in the input region, delete ARG characters before point, restricting
Xdeletion to the input region.  If in the output region and irc-spacebar-pages
Xthen scroll-down (aka window-back) otherwise do nothing."
END_OF_FILE
if test 41172 -ne `wc -c <'irc-1'`; then
    echo shar: \"'irc-1'\" unpacked with wrong size!
fi
# end of 'irc-1'
fi
echo shar: End of archive 1 \(of 3\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 3 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 3 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

tale@PAWL.RPI.EDU (David C Lawrence) (08/02/89)

By the way, this needs a nifty name.  I'm wide open for suggestions.

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 2 (of 3)."
# Contents:  irc-2
# Wrapped by tale@life.pawl.rpi.edu on Wed Aug  2 09:11:48 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'irc-2' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'irc-2'\"
else
echo shar: Extracting \"'irc-2'\" \(38561 characters\)
sed "s/^X//" >'irc-2' <<'END_OF_FILE'
X  (interactive "p")
X  (if (> (point) irc-mark)
X      ;; only delete as far back as irc-mark at most
X      (if (> arg (- (point) irc-mark)) (delete-region (point) irc-mark)
X        (delete-backward-char arg))
X    (if (and (< (point) irc-mark) irc-spacebar-pages) (scroll-down nil)
X      (ding))))
X
X(defun irc-tab ()
X  "If point is in the input region then tab-to-tab-stop.  If it is in the
Xoutput region, go to the previous line if irc-spacebar-pages; do nothing
Xotherwise."
X  (interactive)
X  (if (>= (point) irc-mark) (tab-to-tab-stop)
X    (if irc-spacebar-pages (previous-line 1)
X      (ding))))
X
X;; top-level -- entry, sentinel and mode
X(defun irc (new-buffer)
X  "Enter the Internet Relay Chat conferencing system.
XIf no connexion to an irc-server is open, then one is started.  If no buffer
X*IRC* exists then it is created otherwise the existing buffer is used.  If
Xa connexion is already active then the most recently started IRC session
Xis switched to in the current window.  This makes binding 'irc' to a key
Xmuch more convenient.
X
XWith prefix argument NEW-BUFFER, another *IRC* buffer is created and a
Xnew IRC session is started.  This is provided so that multiple IRC
Xsessions can co-exist in one Emacs, which is sometimes a useful thing."
X  (interactive "P")
X  (let ((buffer (if new-buffer (generate-new-buffer "*IRC*")
X                  (get-buffer-create "*IRC*")))
X        proc)
X    (if (and (not new-buffer) irc-processes)
X        ;; just head for the most recent session
X        (switch-to-buffer (process-buffer (car irc-processes)))
X      (switch-to-buffer buffer)
X      (goto-char (point-max))
X      (insert "IRC-mode for GNU Emacs -- comments to tale@pawl.rpi.edu."
X              "  C-c n for news.\n\n")
X      (irc-mode)
X      (condition-case NOT-IRCED 
X          (progn
X            (setq proc (open-network-stream "irc" buffer irc-server irc-port))
X            (set-process-filter proc 'irc-filter)
X            (set-process-sentinel proc 'irc-sentinel)
X            (irc-send (format "USER %s %s %s %s" (user-login-name)
X                              (system-name) irc-server (user-full-name)))
X            (irc-send (concat "NICK " irc-nick))
X            ;; a new process, so initialize the variables.  they aren't set
X            ;; in irc-mode so that irc-mode can be called at any time.
X            (setq irc-away     nil    irc-channel 0     irc-history-index -1
X                  irc-operator nil    irc-scratch ""    irc-last-command  ""
X                  irc-last-explicit "*;" irc-last-private "*;"
X                  irc-processes (cons proc irc-processes)
X                  irc-last-time (irc-get-time)
X                  irc-total-time (string-to-int (substring irc-last-time 3))
X                  ;; this next bit of messiness just ups irc-last-stamp
X                  ;; in an effort to make nice numbers out of the time
X                  ;; stamps -- ie, if the time is now 13:53 with an interval
X                  ;; of 15 minutes, this makes it 13:45
X                  irc-last-stamp 0
X                  irc-last-stamp (progn
X                                   (while (< (+ irc-last-stamp irc-time-stamp)
X                                             irc-total-time)
X                                     (setq irc-last-stamp (+ irc-last-stamp
X                                                             irc-time-stamp)))
X                                   irc-last-stamp)))
X        (error (irc-insert "Sorry ... couldn't connect to %s at %s.\n\n"
X                           irc-port irc-server))))))
X
X(defun irc-mode ()
X  "To understand some documentation given with irc-mode variables and
Xfunctions, \"output region\" is defined as everything before the irc-mark.
Xirc-mark is a marker kept by irc-mode to know where to insert new text
Xfrom IRC.  Text in the output region cannot be modified by the most common
Xmethods of typing a self-inserting character or pressing delete.
X
XThe input region is everything which follows irc-mark.  It is what
Xgets processed by irc-mode when you type LFD or RET.  If irc-spacebar-pages
Xis non-nil, the following keys are in effect when the cursor is in the
Xoutput region:
X
XSPC             scroll-forward       DEL     scroll-backward
XLFD or RET      next-line            TAB     previous-line
X
XLocal keys:
X\\{irc-mode-map}"
X  (interactive)
X  (kill-all-local-variables)
X  (setq major-mode 'irc-mode mode-name "IRC")
X  (make-local-variable 'irc-away)          ; for the mode-line 
X  (make-local-variable 'irc-channel)       ; for sendlists and broken PRIVMSGs
X  (make-local-variable 'irc-wholist)       ; for sendlists
X  (make-local-variable 'irc-operator)      ; for special priviledges
X  (make-local-variable 'irc-history-index) ; for the message history
X  (make-local-variable 'irc-scratch)       ; for accumulating server messages
X  (make-local-variable 'irc-last-command)  ; for the command history
X  (make-local-variable 'irc-last-explicit) ; for sendlist ; auto-expansion
X  (make-local-variable 'irc-last-private)  ; for sendlist : auto-expansion
X  (make-local-variable 'irc-last-stamp)    ; for time-sentinel
X  (make-local-variable 'irc-last-time)     ; ditto
X  (make-local-variable 'irc-total-time)    ; here too
X  ;; too many ways to get unbalanced parens (most notably ":-)")
X  (set (make-local-variable 'blink-matching-paren) nil)
X  ;; closest we can come to "natural" terminal scrolling
X  (set (make-local-variable 'scroll-step) 1)
X  (set (make-local-variable 'mode-line-format)
X       (list (purecopy "--- %14b") 'global-mode-string
X             (purecopy "  %[(") 'mode-name 'irc-operator 'minor-mode-alist
X             (purecopy ")%]---") 'irc-nick 'irc-away (purecopy "-%-")))
X  (set-marker (set (make-local-variable 'irc-mark) (make-marker)) (point-max))
X  (buffer-enable-undo)
X  ;; "invisible subwindows" or whatever you would like to call them would be
X  ;; nice.  That way I could make the output-region read-only.  The two things
X  ;; most likely to screw up the buffer are backward-kill-word and kill-region
X  (use-local-map irc-mode-map)
X  (run-hooks 'irc-mode-hook))
X
X(defun irc-sentinel (proc stat)
X  "The sentinel for the IRC connexion.
XTakes the normal sentinel arguments PROCESS and STATUS."
X  ;; ignore anything but finished; i don't know what to do with the others
X  (cond ((string= stat "finished\n")
X         (save-excursion
X           (set-buffer (process-buffer proc))
X           (goto-char (point-max))
X           (irc-insert "\nIRC session finished.\n"))
X         ;; all that needs to be done is a little maintenance ...
X         (setq irc-processes (delq proc irc-processes)))))
X
X;; processing input
X(defun irc-process-input ()
X  "If in the input region, parse it for messages and commands.
XIn the output region, next-line if irc-spacebar-pages, otherwise do nothing.
X
XLines are examined on a line-at-a-time basis moving from irc-mark to point-max.
XNormally only one line will be there, but there can be many, as in the result
Xfrom a cut-and-paste operation.  Lines are limited to 250 characters; commands
Xwhich are longer are ignored but messages are split into smaller segments
Xto fit the limitation.  If a message needs to be split this way, \" >>\" is
Xappended to each line which is split to indicate that the line is continued
Xin the next message."
X  (interactive)
X  ;; do the simple stuff for the output region
X  (if (< (point) irc-mark) (if irc-spacebar-pages (next-line 1) (ding))
X    (irc-check-time)
X    ;; the input region is more work ...
X    ;; first, toast extraneous spaces, tabs and newlines at end of input region
X    (delete-region (goto-char (point-max))
X                   (if (re-search-backward "[^ \t\n]" irc-mark t)
X                       (1+ (point)) (point)))
X    ;; nuke the white space at the beginning of input region, too
X    (delete-region (goto-char irc-mark)
X                   (progn (re-search-forward "\\s *") (point)))
X    (setq irc-history-index -1)            ; reset the history scroll location
X    (let ((to-do (buffer-substring irc-mark (point-max))) line ass)
X      ;; check to see if the input region is empty
X      (if (string= "" to-do) (message "(nothing sent to the irc-server)")
X        (while (string< "" to-do)    ; plug away, line-at-a-time
X          (setq line (substring to-do 0 (string-match "\n\\|$" to-do))
X                ;; to-do is now one line less
X                to-do (substring to-do (match-end 0)))
X          ;; go to the end of the line
X          (re-search-forward (regexp-quote line))
X          ;; if there are more lines after this, just hop over the newline
X          ;; otherwise add a newline so messages show up in the right place
X          (if (looking-at "\n") (forward-char 1) (insert "\n"))
X          (set-marker irc-mark (point))
X          (cond
X           ((string-match "^/" line)  ; it's a command
X            (if (< (length line) 250)
X                (irc-execute-command (setq irc-last-command
X                                           (substring line 1)))
X              ;; can't use error because that kills the function
X              (ding) (message "IRC commands can't exceed 250 characters.")))
X           ((string< "" line)         ; ignore null lines
X            ;; "a specified sendlist" -- was there one?
X            (setq ass (irc-find-to line 'explicit))
X            (if (and ass (string-match "^[^:;]" line))
X                ;; a real sendlist was specified -- update irc-last-explicit
X                (setq irc-last-explicit (irc-find-to line)))
X            (if (> (length line) 250) ; sigh.  this is going to be fugly
X                (let ((send (substring line 0 251))
X                      (keep (substring line 251)))
X                  (cond      ; shape up space for sending
X                   ((string-match "^[ \t]" keep)
X                    ;; probably broke at the end of a word; take out the space
X                    ;; for the next message.  tab is wretched; servers convert
X                    ;; them to a single space anyway so trying to preserve
X                    ;; spacing with it is futile
X                    (setq keep (substring keep 1)))
X                   ((string-match "[ \t]+\\([^ \t]*\\)$" send)
X                    ;; broken in a word or before one; glue last bit on to
X                    ;; the front of keep
X                    (setq keep (concat (substring send (match-beginning 1))
X                                       keep)
X                          send (substring send 0 (match-beginning 0)))))
X                  (setq send (substring send 0 (string-match "[ \t]*$" send))
X                        line (concat send " >>")
X                        ;; make next line of to-do ready to go to same place
X                        to-do (concat (if ass ";" "") keep to-do))
X                  ;; this seems the easiest way to make sure point is where
X                  ;; it should be for the following insertion
X                  (re-search-backward (concat "^" (regexp-quote send)))
X                  (re-search-forward  (regexp-quote send))
X                  (insert " >>\n" (if ass ";" ""))
X                  ;; get rid of the leading space we just pushed down
X                  (delete-char 1)
X                  (beginning-of-line)))  ; make sure point is okay for messages
X            (irc-add-to-hist             ; put it in the history list
X             (irc-execute-msg (concat (if (not ass) irc-default-to) line)))))))
X      ;; finally make sure irc-mark is at the start of a fresh input region
X      (set-marker irc-mark (goto-char (point-max))))))
X
X(defun irc-execute-command (str)
X  "Execute the \"/\" command of STR.  STR should not begin with a slash.
XCommands are first looked up in the irc-alias-alist; if it is found there
Xthen the alias gets passed recursively with any arguments the original
Xhad.  The irc-command-alist is checked next and finally the irc-operator-alist.
XA command is considered \"found\" when it matches either exactly or
Xunambiguously starting at the first character.  That is, J would match JOIN,
Xbut OIN would not."
X  (let* ((case-fold-search t)
X         (cmd (substring str 0 (string-match "\\(\\s +\\|$\\)" str)))
X         (text (substring str (match-end 0)))
X         (ambig (irc-check-list
X                 (mapcar 'car (append irc-alias-alist irc-command-alist
X                                      (if irc-operator irc-operator-alist)))
X                 cmd 'start-only))
X         matches)
X    ;; if no matches are found the command might still be a valid command
X    ;; name hiding behind non-operator status.  i don't like messages that
X    ;; lie and say "Unknown command '/REHASH'" so this should make it not lie.
X    (if (and (not irc-operator) (null ambig))
X        (setq ambig (irc-check-list (mapcar 'car irc-operator-alist) cmd t)))
X    ;; first determine any ambiguities among the lists
X    (if (null ambig)
X        ;; no matches at all were found
X        (irc-insert "Unknown command '/%s'.  Type /HELP for help."
X                    (upcase cmd))
X      ;; this is here for when a regular command gets aliased.  it shows up as
X      ;; being ambiguous but it really isn't later on.
X      (if (member-general (car ambig) (cdr ambig) 'string=)
X          (setq ambig (cdr ambig)))
X      (if (> (length ambig) 1)
X          (irc-insert "Ambiguous command '/%s'.  Could be %s." (upcase cmd)
X                      (irc-subst-comma
X                       (mapconcat (function (lambda (arg)
X                                              (concat "/" arg))) ambig ", ")
X                       "or"))
X        ;; alias list has highest priority
X        (setq matches (irc-check-list (mapcar 'car irc-alias-alist) cmd t))
X        (if matches
X            ;; call this function again with the text as argument
X            (irc-execute-command
X             (concat (cdr (assoc (car matches) irc-alias-alist))
X                     ;; the servers won't grok trailing whitespace for some
X                     ;; messages so only use it to separate an argument
X                     (if (string< "" text) " ") text))
X          ;; next try the command alist
X          (setq matches (irc-check-list (mapcar 'car irc-command-alist) cmd t))
X          (if matches
X              ;; call the appropriate irc-execute-* function
X              (funcall (intern-soft
X                        (concat "irc-execute-"
X                                (cdr (assoc (car matches)
X                                            irc-command-alist)))) text)
X            ;; no matches yet.  last resort is the operator alist
X            (setq matches (irc-check-list (mapcar 'car irc-operator-alist)
X                                          cmd t))
X            (if matches
X                (if irc-operator
X                    (funcall (intern-soft
X                              (concat "irc-execute-"
X                                      (cdr (assoc (car matches)
X                                                  irc-operator-alist)))) text)
X                  (irc-insert "Only IRC Operators can use the /%s command."
X                              (upcase (car matches)))))))))))
X
X(defun irc-send (str)
X  "Send STR to process in the current buffer.
XA CR-LFD pair is appended automatically as per the 'official' IRC protocol,
Xbut it seems unnecessary.  Returns its argument STR."
X  (send-string (get-buffer-process (current-buffer)) (concat str "\r\n"))
X  str)
X
X;; sending messages to people
X(defun irc-execute-privmsg (str)
X  "Usage: /MSG recipient(s) message
X
XThis command is provided simply for compatability with the C client.  It is
Xpreferable instead to just type the name of the user followed by a semi-colon
Xor colon and then the message. That is, \"tale;hi!\" will send the message
X\"hi!\" to the user with the nickname which unambiguously matches \"tale\".
XA semi-colon or colon at the beginning of the line means to send to the last
Xrecipient explicity specified; typing a semi-colon at the beginning of a line
Xexpands it to the last recipient(s) specified while typing a colon at the
Xbeginning of the line automatically expands to the last person to have sent
Xyou a private message.  The recipients for a message can be a comma separated
Xlist of users and/or channels."
X  (irc-add-to-hist
X   (irc-execute-msg (concat
X                     (setq irc-last-explicit
X                           (concat (substring
X                                    str 0 (string-match "\\s +\\|$" str)) ";"))
X                     (substring str (match-end 0))))))
X
X(defun irc-execute-msg (str)
X  "Send a message to a channel or another user.  Returns its argument STR,
Xmunged slightly to indicate where it was attempted to be sent."
X  ;; this really is an indirect fucntion of the UI (ie, not through a /COMMAND)
X  ;; so it isn't interactive
X  (let (tolist (orig str) icw confirm)
X    (if (string-match "^[;:]" str)
X        ;; a little bit of fill-in-the-blank
X        (setq str (concat irc-last-explicit (substring str 1)))
X      (if (not (irc-find-to str 'explicit))
X          ;; prepend an implicit sendlist if need be
X          (if irc-default-to (setq str (concat irc-default-to str))
X            (irc-insert "You have no default sendlist."))))
X    (if (irc-find-to str 'explicit)
X        (setq icw (irc-find-to str)
X              tolist (irc-burst-comma (substring icw 0 (1- (length icw))))
X              str (irc-find-message str)))
X    (setq
X     confirm
X     (delq                              ; whee.  lisp indentation is fun.
X      nil
X      (mapcar (function
X               (lambda (to)
X                 (if (not (zerop (string-to-int to)))
X                     (if (= (string-to-int to) irc-channel)
X                         (progn (irc-send (concat "MSG :" str)) to)
X                       ;; new in 1.2 -- you _can_ send to a channel you
X                       ;; are not on
X                       (ir-send (concat "PRIVMSG " to " :" str))
X                       to)
X                   (setq icw (irc-check-list irc-wholist to))
X                   (cond
X                    ((string= to "*")
X                     (if (zerop irc-channel)
X                         (progn (irc-insert "You are not on any channel.") nil)
X                       (irc-send (concat "MSG :" str))
X                       (int-to-string irc-channel)))
X                    ((string= to "0")
X                     (irc-insert "You can't send to channel 0.") nil)
X                    ((= (length icw) 1)
X                     (irc-send (concat "PRIVMSG " (car icw) " :" str))
X                     (car icw))
X                    ((not icw)
X                     ;; wox.  no one found, but we'll do a nonomatch.  try
X                     ;; sending it anyway and let the server bitch if necessary
X                     (irc-maintain-list 'irc-wholist to 'add)
X                     (irc-send (concat "PRIVMSG " to " :" str))
X                     to)
X                    (t
X                     (irc-insert "Ambiguous recipient \"%s\"; could be %s."
X                                 to (irc-subst-comma
X                                     (mapconcat 'eval icw ", ") "or")) nil)))))
X              tolist)))
X    (if (and confirm irc-confirm)
X        (irc-insert "(message sent to %s)"
X                    (irc-subst-comma (mapconcat 'eval confirm ", ") "and"))
X      (if (not confirm) (irc-insert "(message not sent)")))
X    orig))
X
X(defun irc-execute-oops (newto)	; one of my favourites. 
X  "Usage: /OOPS intended-recipient
X
XSend irc-oops to recipient(s) of last message and resend message to
X'intended-recipient'.  This command is handy when you've just sent a message
Xto the wrong place and you want the person/people who saw it to know that they
Xshould just disregard it.  The message which was originally sent then gets
Xforwarded to its proper destination."
X  (interactive)
X  ;; first do the oops message
X  (irc-execute-msg (concat (irc-find-to (car irc-history)) irc-oops))
X  ;; then resend the original
X  (irc-execute-redirect newto))
X
X(defun irc-execute-redirect (newto)
X  "Usage: /REDIRECT additional-recipient
X
XSend to 'additional-recipient' the last message which you sent.  This 
Xcommand can be fairly easily duplicated using the history mechanism by hand
Xbut it is provided to make it even easier."
X  (interactive (list
X                (read-string
X                 (format "New recipient(s)? %s"
X                         (if irc-default-to
X                             (concat "[RET for "
X                                     (substring irc-default-to 0
X                                                (1- (length irc-default-to)))
X                                     "] ")
X                           "")))))
X  (if (not (string-match "^[a-zA-Z0-9-_,|{*]*$" newto))
X      ;; perhaps crapping out here is too harsh
X      (irc-insert "%s is not a valid sendlist.  Message not redirected." newto)
X    (if (and (not (interactive-p)) (string= "" newto))
X        (call-interactively 'irc-execute-redirect)
X      (setq newto (if (string= "" newto) irc-default-to (concat newto ";"))
X            irc-last-explicit newto)
X      (irc-add-to-hist
X       (irc-execute-msg (concat newto
X                                (irc-find-message (car irc-history))))))))
X
X;; /commands for the server
X(defun irc-execute-quote (msg)
X  "Usage: /QUOTE string
X
XThis command is used to send 'string' directly to the IRC server without
Xany local processing.  Warning: this has the potential to screw up some
Xthings in irc-mode, particularly if it is used to change your nickname or
Xto switch channels."
X  (interactive "sString to send to server: ")
X  (if (string-match "^\\s *$" msg)
X      (irc-insert "(nothing was sent to the IRC server)")
X    (irc-send msg)))
X
X(defun irc-execute-who (channel)
X  "Usage: /WHO [ channel ]
X
XGet a list of the users on IRC.  Optional argument 'channel' means to show
Xjust the users on that channel, with * representing the current channel.
X
XEach user is indicated on a separate line with their nickname, channel, login
Xname, host and real name.  The first column indicates their status --
X' ' for here, '-' for away, '*' for an operator, '=' for an away operator
Xand '#' for someone being ignored.  Servers don't propogate the information
Xabout who is away so you will probably only see people on your server
Xcorrectly marked regarding their presence.
X
XUsers who are either on a channel greater than 1000 or who are on no channel
Xhave nothing listed in the Chan column.  Users who are on channels less than
Xzero do not appear in the list.
X
XIf this function is called interactively then the prefix argument is used
Xas the channel to query.  No argument means all of them and an argument of -
Xmeans the current channel."
X  (interactive (if current-prefix-arg
X                   (if (eq current-prefix-arg '-) '("*")
X                     (list (int-to-string
X                            (prefix-numeric-value current-prefix-arg))))
X                 '("0")))
X  ;; make * be the current channel, even though the server groks it.
X  (if (string-match "^\\s *\\*\\(\\s .*\\)?$" channel)
X      (setq channel (list (int-to-string irc-channel))))
X  ;; if channel converts to 0 then we will get fresh information about
X  ;; who is present.
X  (if (zerop (string-to-int channel))
X      (setq irc-wholist nil))
X  (irc-send (concat "WHO " channel))
X  ;; a nice blank line at the top of the list
X  (irc-insert ""))
X
X(defun irc-execute-list (&optional channel)
X  "Usage: /LIST [ channel ]
X
XGet a list of the discussions that are on IRC.  The optional channel argument
Xis supposed to show just that channel but this is not currently supported
Xby most servers."
X  ;; according to Comms LIST can take an optional channel number.
X  ;; don't believe it -- it doesn't.  I send one anyway just in case it
X  ;; gets fixed; in the meantime servers seem to ignore any extra stuff
X  (interactive)
X  (irc-send (concat "LIST " channel))
X  ;; put a blank line before the list
X  (irc-insert ""))
X
X(defun irc-execute-links (&optional cruft)
X  "Usage: /LINKS
X
XShow the names of all the servers which can communicate with your server.
XThe links can go down isolating different parts of the IRC-net, so this
Xis a good way to find out how extensive it is at the moment.  Any arguments
Xto this command are ignored."
X  (interactive)
X  (irc-send "LINKS")
X  (irc-insert ""))
X
X(defun irc-execute-admin (server)       ; what an evil thought
X  "Usage: /ADMIN [ server ]
X
XGet information about the IRC administrator for 'server'; if server is not
Xsupplied just query for the server to which you are connected."
X  (interactive "sAdministrative info about which server? ")
X  (irc-send (concat "ADMIN " server))
X  (irc-insert ""))
X
X(defun irc-execute-time (&optional server)
X  "Usage: /TIME [ server ]
X
XGet the current time on 'server'; is no server is provided use the one to which
Xyou are connected.  When called with a interactively with a prefix-argument
Xthe server name is read using the minibuffer.
X
XQuerying other servers can be handy given that people on IRC are spread out
X from the west coast of the United States to Finland.  The question \"What
Xtime is it in Finland?\" comes up so frequently that an alias -- /TF -- has
Xbeen provided by default to get the answer.  This alias should work as long
Xas tut.fi is connected to your IRC-net."
X  (interactive (if current-prefix-arg
X                   (list (read-string "Get time at which server? "))
X                 '("")))
X  (irc-send (concat "TIME " server)))
X
X(defun irc-execute-join (channel)
X  "Usage: /JOIN channel
X
XJoin 'channel' on IRC.  If channel is not provided it is requested in the
Xminibuffer; when called interactively channel is set to the prefix argument if
Xone is present.  Use /LEAVE to exit the channel."
X  (interactive (if current-prefix-arg
X                   (list (int-to-string
X                          (prefix-numeric-value current-prefix-arg)))
X                 '("")))
X  (if (string= channel "")
X      (setq channel (read-string "Channel to join? ")))
X  (if (string-match "^\\s *$" channel) ()  ; well, so much for that
X    ;; the seeming no-op is nice for turning random garbage into "0"
X    (irc-send (concat "CHANNEL " (int-to-string (string-to-int channel))))
X    ;; this is only here so we can give a "left channel" message 
X    (put 'irc-channel 'o-chan irc-channel)
X    (setq irc-channel (string-to-int channel))))
X
X(defun irc-execute-leave (&optional cruft)
X  "Usage: /LEAVE
X
XLeave your current channel and join no other.  Any arguments to this command
Xare ignored."
X  (interactive)
X  ;; for the left channel n message
X  (put 'irc-channel 'o-chan irc-channel)
X  (setq irc-channel 0)
X  (irc-send "CHANNEL 0"))
X
X(defun irc-execute-nick (name)
X  "Usage: /NICKNAME name
X
XChange your nickname in IRC.  A nickname can contain alphanumeric characters,
Xunderscores (_), hyphens (-) or the special characters vertical bar (|) and
Xleft brace ({), which are alphabetic characters in Finnish.  The name cannot
Xstart with a hyphen or number and only the first nine characters are used.
X
XUnfortunately, due to the way confirmation from the servers work, it might be
Xfalsely reported that your nickname was successfully changed when it was not.
XThe server will come back and say so and finally irc-mode will wise-up and
Xnote that your nickname was not changed."
X  (interactive "sNew nickname? ")
X  (while (not (string-match "^\\([a-zA-Z|{_][a-zA-Z0-9-_|{]*\)$" name))
X    (setq name (read-string
X                (format "\"%s\" is not valid.  New nickname? " name))))
X  (if (> (length name) 9) (setq name (substring name 0 9)))
X  (if (string= "" name)
X      (if (interactive-p)
X          (irc-insert "Nickname not changed.")
X        (call-interactively 'irc-execute-nick))
X    (irc-insert "You will now be known as \"%s\"." name)
X    (put 'irc-nick 'o-nick irc-nick)
X    (setq irc-nick name)
X    (set-buffer-modified-p (buffer-modified-p))
X    (irc-send (concat "NICK " name))))
X
X(defun irc-execute-quit (&optional text)
X  "Usage: /QUIT
X
XExit IRC.  The connexion is closed but the buffer is left behind.
XArguments to this command are not ignored; if any are present then
Xthe session is not exited as a safety precaution against seemingly
Xunintentional use of the command."
X  (interactive)
X  (if (and text (string< "" text))
X      (irc-insert "/QUIT takes no arguments.")
X    (irc-send "QUIT")))
X
X(defun irc-execute-away (&optional text)
X  "Usage: /AWAY message
X
XMark yourself as away, giving TEXT to people who send you private messages.
XWithout any arguments it will just insert a message about your current status."
X  (interactive "sReason for being away: ")
X  (if (string= "" text)
X      (if irc-away
X          (irc-insert "You are marked as away: '%s'." irc-away)
X        (irc-insert "You are not currently marked as being away."))
X    (irc-send (concat "AWAY " text))
X    (setq irc-away (concat " [" text "]")))
X  (set-buffer-modified-p (buffer-modified-p)))
X
X(defun irc-execute-here (&optional cruft)
X  "Usage: /HERE
X
XMark yourself as present (ie, not \"away\") on IRC.  Any arguments to this
Xcommand are ignored."
X  (interactive)
X  (irc-send "AWAY")
X  (setq irc-away nil)
X  (set-buffer-modified-p (buffer-modified-p)))
X
X(defun irc-execute-whois (user)
X  "Usage: /WHOIS user
X
XGet a two line description of who and where 'user' is.  If user is not
Xprovided it is read from the minibuffer with a completing-read."
X  (interactive '(""))
X  (setq user (irc-read-user "Who is who? " user))
X  (if (string< "" user) (irc-send (concat "WHOIS " user))))
X
X(defun irc-execute-topic (topic)
X  "Usage: /TOPIC topic
X
XMake 'topic' the description of the current channel; this description is
Xshown to people looking at the channel listing."
X  (interactive (list (if (zerop irc-channel) ""
X                       (read-string (format "Topic for channel %d? "
X                                            irc-channel)))))
X  (if (zerop irc-channel)
X      (irc-insert "You aren't on any channel.")
X    (if (and (not (interactive-p)) (string-match "^\\s *$" topic))
X        (call-interactively 'irc-execute-topic)
X      (irc-send (concat "TOPIC " topic)))))
X
X(defun irc-execute-oper (oper)          ; for crimes against humanity
X  "Usage: /OPER [ name [ password ]]
X
XAttempt to become an IRC Operator.  Can take the name of the operator
Xand the password as arguments.  If name is missing then it will be read
Xfrom the minibuffer; if password is missing it will be read and hidden
Xin the minibuffer.
X
XIf you become an operator then the word \"Operator\" will appear in the
Xminibuffer along with the mode name."
X  (interactive "sOperator name? ")
X  (string-match "^\\s *\\(\\S *\\)\\s *\\(\\S *\\).*$" oper)
X  (let* ((name   (substring oper (match-beginning 1) (match-end 1)))
X         (passwd (substring oper (match-beginning 2) (match-end 2)))
X         (noname (string= "" name)))
X    (if (and (interactive-p) noname) () ; just drop right through
X      (if noname (call-interactively 'irc-execute-oper)
X        (if (string= "" passwd)
X            (setq passwd
X                  (irc-read-passwd (format "Password for operator %s? "
X                                           name))))
X        (irc-send (concat "OPER " name " " passwd))))))
X
X(defun irc-execute-summon (user)
X  "Usage: /SUMMON user
X
XSummon a user not on IRC to join IRC.  The argument provided may either be
Xa user name on the local machine or user@server, where server is another
Xmachine on the IRC-net.  The user must be signed on to the specified server."
X  (interactive "sUser to summon to IRC? ")
X  (let ((nouser (string-match "^\\s *$" user)))
X    (if (and (interactive-p) nouser) ()  ; guess s/he didn't really mean it ...
X      (if nouser (call-interactively 'irc-execute-summon)
X        (irc-send (concat "SUMMON " user))))))
X
X(defun irc-execute-users (host)         ; this is entirely too violent
X  "Usage: /USERS [ server ]
X
XGet a list of the users signed on to 'server'.  If no server name is provided
Xthen the server to which you are connected is used.  When called interactively
Xa prefix argument means to prompt for the server to query."
X  (interactive (if current-prefix-arg
X                   (list (read-string "List users on which host? "))
X                 '("")))
X  (irc-insert "")
X  (irc-send (concat "USERS " host)))
X
X(defun irc-execute-info (&optional cruft)
X  "Usage: /INFO
X
XShow some information about the programmer of IRC.  Arguments to this command
Xare ignored."
X  (interactive) (irc-send "INFO"))
X
X(defun irc-execute-kill (user)
X  "Usage: /KILL user
X
XForcibly remove a user from IRC.  This command is reserved for IRC Operators."
X  (interactive '(""))
X  (setq user (irc-read-user "Nuke which user? " user))
X  (if (string< "" user) (irc-send (concat "KILL " user))))
X
X(defun irc-execute-invite (user)
X  "Usage: /INVITE user [ channel ]
X
XAsk 'user' on IRC to join 'channel'.  If channel is 0, * or not provided then
Xthe invitation defaults to your current channel.  If you are not on any channel
Xand channel is 0 or not provided then no invitation is sent -- you can't
Xinvite someone to go private.  When called interactively channel is set to
Xthe prefix argument; with no argument or - the current channel is assumed."
X  (interactive (list
X                (int-to-string
X                 (if (and current-prefix-arg (not (eq current-prefix-arg '-)))
X                     (prefix-numeric-value current-prefix-arg)
X                   irc-channel))))
X  (if (interactive-p)
X      (progn
X        (if (and (zerop irc-channel) (string= "0" user))
X            (setq user (read-string "Invite to which channel? ")))
X        ;; this is so irc-read-user will force a completing-read
X        ;; something needs to come up as "name" so that "channel" comes up in
X        ;; the right place.  a tiny kludge but the results are the same
X        (setq user (concat ". " user))))
X  (string-match "^\\s *\\(\\S *\\)\\s *\\(\\S *\\).*$" user)
X  (let* ((name    (substring user (match-beginning 1) (match-end 1)))
X         (channel (substring user (match-beginning 2) (match-end 2)))
X         (noname  (string= "" name)))
X    (cond
X     (noname (call-interactively 'irc-execute-invite)) ; no arguments ...
X     ((and (zerop irc-channel) (zerop (string-to-int channel)))
X      (irc-insert "You are not on any channel.  No invitation sent."))
X     (t (setq name (irc-read-user (format "Invite whom to channel %d? "
X                                          (string-to-int channel))
X                                  (if (string= "." name) "" name)))
X        (if (string< "" name)
X            (irc-send (concat "INVITE " name " " channel)))))))
X
X(defun irc-execute-names (channel)
X  "Usage: /NAMES [ channel ]
X
XShow which channels everyone is on.  Optional argument 'channel' (which can
Xbe provided as a prefix argument if called interactively) means to show
Xjust the users on that channel.  * or an interactive prefix argument of -
Xmeans to show people on the current channel.
X
XEach line starts with a column for the channel number and is followed by the
Xnicknames of the people on that channel.  Users who are on private channels
Xor who are not on any channel are listed as \"Private\".  Users who are
Xon secret channels (channels less than 0) are not shown at all."
X  (interactive (if current-prefix-arg
X                   (list
X                    (if (eq current-prefix-arg '-) '("*")
X                      (list (int-to-string
X                             (prefix-numeric-value current-prefix-arg)))))
X                 '("0")))
X  ;; server doesn't understand * for current channel.  but we want to be
X  ;; nice and consistent in the client so we take it.
X  (if (string-match "^\\s *\\*\\(\\s .*\\)?$" channel)
X      (setq channel (int-to-string irc-channel)))
X  ;; have to do some weird things here.  servers don't grok a NAMES 0
X  ;; at all so have to make anything that appears to be 0 really disappear.
X  ;; names also provides us with fresh information on who is around.
X  (if (zerop (string-to-int channel))
X      (setq irc-wholist nil channel ""))
X  (irc-send (concat "NAMES " channel))
X  ;; need a header here.  server is not gratuitous as in WHOREPLY.
X  (irc-insert "\nChannel  Users"))
X
X(defun irc-execute-wall (msg)
X  "Usage: /WALL message
X
XSend 'message' to everyone on IRC.  This can only be done by IRC Operators."
X  (interactive "sMessage for everyone: ")
X  (if (and (not (interactive-p)) (string= "" msg))
X      (call-interactively irc-execute-wall)
X    (if (string< "" msg)
X        (irc-send (concat "WALL " msg)))))
X
X(defun irc-execute-rehash (&optional cruft)
X  "Usage: /REHASH
X
XForce the server to which you are connected to reread it's irc.conf file.
XArguments are ignored.  This command is only available to IRC Operators."
X  ;; what a joy this was to write
X  (interactive) (irc-send "REHASH"))
X
X(defun irc-execute-trace (server)
X  "Usage: /TRACE [ server ]
X
XFind the route from the server to which you are attached to 'server'; if the
Xserver argument is not provided then the servers to which the current server
Xis directly connected are listed.  This command is only available to IRC
XOperators."
X  (interactive (list (if current-prefix-arg
X                         (read-string "Trace route to which server? ")
X                       "")))
X  (string-match "^\\s *\\(\\S *\\).*$" server)
X  (irc-send (concat "TRACE "
X                    (substring server (match-beginning 1) (match-end 1))))
X  (irc-insert ""))
X
X;; /command just for the client  (need /stamp /alias /unalias)
X(defun irc-execute-send (slist)
X  "Usage: /SEND [ sendlist | - ]
X
XSet the default sendlist for IRC messages.  This is a comma separated list
Xof the intended recipient(s) of messages which do not have an explicit
Xsendlist.  '-' as an argument means to disable the default sendlist; every
Xmessage sent then must have an explicit recipient provided with the message.
XWithout any arguments this command just displays the current default sendlist.
X
XEach item specified is checked to see whether you can send there; ambiguous
Xreferences to users are not allowed nor are channels which you are not on.
X\"*\" is always allowed and means to send to the current channel.
XIf no item in the new list can be set then the sendlist is not changed."
X  (interactive "sDefault recipient(s) for messages? ")
X  ;; blast some whitespace
X  (setq slist (irc-nuke-whitespace slist))
X  (let (matches)
X    ;; first the easiest case
X    (if (string= "-" slist) (setq irc-default-to nil)
X      (setq matches
X            (delq nil                   ; more indentation fun.  can someone
X                  (mapcar               ; recommend a good style manual?
X                   (function
X                    (lambda (arg)
X                      (setq matches (irc-check-list irc-wholist arg))
X                      (cond
X                       ((string= arg "*") arg)
END_OF_FILE
if test 38561 -ne `wc -c <'irc-2'`; then
    echo shar: \"'irc-2'\" unpacked with wrong size!
fi
# end of 'irc-2'
fi
echo shar: End of archive 2 \(of 3\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 3 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 3 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

tale@PAWL.RPI.EDU (David C Lawrence) (08/02/89)

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 3 (of 3)."
# Contents:  irc-3
# Wrapped by tale@life.pawl.rpi.edu on Wed Aug  2 09:11:48 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'irc-3' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'irc-3'\"
else
echo shar: Extracting \"'irc-3'\" \(39278 characters\)
sed "s/^X//" >'irc-3' <<'END_OF_FILE'
X                       ((string= arg "0")
X                        (irc-insert "You can't send to channel 0.") nil)
X                       ((not (zerop (string-to-int arg)))
X                        (if (= (string-to-int arg) irc-channel) arg
X                          (irc-insert "You are not on channel %s." arg) nil))
X                       ((= (length matches) 1) (car matches))
X                       ((eq matches nil)
X                        (irc-insert "No names found to match \"%s\"." arg) nil)
X                       (t
X                        (irc-insert "Ambiguous recipient \"%s\"; could be %s."
X                                    to (irc-subst-comma
X                                        (mapconcat 'eval matches ", ") "or"))
X                        nil)))) (irc-burst-comma slist))))
X      (if matches
X          (setq irc-default-to (concat (mapconcat 'eval matches ",") ";"))
X        (or (string= "" slist)  ; only print the error if tried to set it.
X            (irc-insert "(no matches -- sendlist not changed)"))))
X    (if (not irc-default-to) (irc-insert "Your default sendlist is disabled.")
X      (irc-insert
X       "You are sending to %s."
X       (irc-subst-comma
X        (mapconcat 'eval
X                   (irc-burst-comma
X                    (substring irc-default-to 0
X                               (1- (length irc-default-to)))) ", ") "and")))))
X
X(defun irc-execute-notify (notify)
X  "Usage: /NOTIFY [ [+]event | -event ] [...]
X
XSet the list of events to notify you about with a message.  Notification
Xis a one-line message inserted when someone causes that event to occur.
XEvents are added with +event or simply event; they are removed with -event.
X+ adds all supported events and - removes all supported events.  More than
Xone event can be specified in the arguments.  In case of conflict, the argument
Xwhich appears later overrides the argument with which it conflicts.
X
XCurrently supported by /NOTIFY are the 'join' and 'nick' events.  Join happens
Xwhenever someone enters or leaves a channel which you are on.  Nick occurs
Xwhen someone changes nicknames; recognition of this event is currently limited
Xto when the person making the change is on the same channel as you."
X  (interactive "sNotify for events: ")
X  ;; die scurvy whitespace
X  (setq notify (irc-nuke-whitespace notify))
X  (let ((recog '(join nick)) (str notify) sym off event)
X    (while (string< "" notify)
X      ;; multiple args are okay.  we'll do one at a time.
X      (setq str (substring notify 0 (string-match "\\s +\\|$" notify))
X            notify (substring notify (match-end 0)))
X      (string-match "^\\([-+]?\\)\\(.*\\)$" str)
X      (setq off (string= "-" (substring str (match-beginning 1) (match-end 1)))
X            event (substring str (match-beginning 2) (match-end 2))
X            sym (if (string= "" event) nil
X                  (car (delq nil              ; do some minor pattern matching
X                             (mapcar          ; to find the intended event
X                              (function
X                               (lambda (arg)
X                                 (if (string-match
X                                      (concat "^" (regexp-quote event))
X                                      (prin1-to-string arg))
X                                     arg))) recog)))))
X      (cond
X       ((and (string= "" event) off) (setq irc-notifies nil))
X       ;; the only way for this to happen and not the above is str == "+"
X       ((string= "" event) (setq irc-notifies recog))
X       ((null sym) (irc-insert "Notify: Unknown argument '%s'." event))
X       (t (setq irc-notifies (if off (delq sym irc-notifies)
X                               (if (not (memq sym irc-notifies))  ; avoid
X                                   (cons sym irc-notifies)        ; redundancy
X                                 irc-notifies))))))
X    (if irc-notifies
X        (irc-insert "Notification is currently enabled for %s."
X                    (irc-subst-comma (mapconcat 'prin1-to-string irc-notifies
X                                                ", ") "and"))
X      (irc-insert "Notification is currently disabled."))))
X
X(defun irc-execute-confirm (str)
X  "Usage: /CONFIRM [ + | - ]
X
XTurn on message confirmation with + or off with -.  Any other arguments or no
Xarguments just gives a message about the current setting.
X
XMessage confirmation is a line indicating to whom a message was sent.
XOccasionally this will say that a message has been sent to someone who
Xwas not present but another message soon after will set the record straight."
X  (interactive "sSet confimation on (+) or off (-)? ")
X  ;; grab the first arg
X  (string-match "^\\s *\\(\\S *\\).*$" str)
X  (setq str (match-beginning 1) (match-end 1))
X  (cond ((string= str "+") (setq irc-confirm t))
X        ((string= str "-") (setq irc-confirm nil)))
X  (irc-insert "Message confirmation is %s." (if irc-confirm "on" "off")))
X
X(defun irc-execute-ignore (user)
X  "Usage: /IGNORE user
X
XIgnore another user on IRC.  Any events by this person (except for WALL)
Xare not displayed.  With no arguments a list of all currently ignored people.
X
XIRC-mode will track the ignored user across nickname changes if it notices the
Xchange.  If the user sends either a private message or an invitation to you
Xwhile being ignored a message will be sent to that person saying \"You are
Xbeing ignored.\"  To undo this command, use /UNIGNORE."
X  (interactive '(""))
X  (if (or (interactive-p) (not (string= "" user)))
X      (setq user (irc-read-user "Ignore which user? " user)))
X  (if (string= "" user)
X      (if irc-ignores
X          (irc-insert "You are currently ignoring %s."
X                      (irc-subst-comma (mapconcat 'eval irc-ignores ", ")
X                                       "and"))
X        (irc-insert "You are not ignoring anyone."))
X    (irc-insert "You are now ignoring %s." user)
X    (irc-maintain-list 'irc-ignores user 'add)))
X
X(defun irc-execute-unignore (user)
X  "Usage: /UNIGNORE user | + | -
X
XStop ignoring a user who has been /IGNOREd.  The special arguments + or -
Xmean to stop ignoring everyone who is being ignored."
X  ;; how long a doc string could I write for this?
X  (interactive '(""))
X  (if (null irc-ignores)
X      (irc-insert "You are not ignoring anyone.")
X    (if (string-match "^\\s *\\([-+]\\)\\(\\s |$\\)")
X        (progn (setq irc-ignores nil)
X               (irc-insert "You are no longer ignoring anyone."))
X      (setq user (irc-read-user "Stop ignoring whom? " user irc-ignores))
X      (if (string= "" user) ()
X        (irc-insert "You are no longer ignoring %s." user)
X        (irc-maintain-list 'irc-ignores user 'remove)))))
X
X(defun irc-execute-signal (sigs)
X  "Usage: /SIGNAL [ + | - | [+]event | -event ] [...]
X
XSet the events which will get signals (aks bells or dings) when they
Xoccur.  Events supported are:
X
X  private -- private messages      join   -- channel joining/leaving
X  public  -- public messages       invite -- invitations
X  wall    -- broadcast messages    nick   -- nickname changes
X
XWithout any arguments /SIGNAL simply prints a message about what signals
Xare currently enabled.  With event or +event turn on all signalling for that
Xevent.  Remove all signals for an event with -event.  /SIGNAL + or /SIGNAL -
Xadds or removes all signals respectively.  Multiple arguments are accepted;
Xlater ones take precedence over the ones which came before them.  For example,
X'/SIGNAL - +w +i' would turn off all signals and then turn on signalling only
Xfor wall messages and invitations."
X  (interactive "sSet signal: ")
X  ;; blow some whitespace away.  curiously this doesn't work correctly in debug
X  (setq sigs (irc-nuke-whitespace sigs))
X  (let ((recog '(private public wall invite join nick)) str sym on off event)
X    (while (string< "" sigs)
X      ;; take one argument at a time
X      (setq str  (substring sigs 0 (string-match "\\s +\\|$" sigs))
X            sigs (substring sigs (match-end 0)))
X      (string-match "^\\([-+]?\\)\\(.*\\)$" str)
X      (setq off (string= "-" (substring str (match-beginning 1) (match-end 1)))
X            event (substring str (match-beginning 2) (match-end 2))
X            sym (if (string= "" event) nil
X                  (car (delq nil
X                             (mapcar
X                              (function
X                               (lambda (arg)
X                                 (if (string-match
X                                      (concat "^" (regexp-quote event))
X                                      (prin1-to-string arg))
X                                     arg))) recog)))))
X      (cond
X       ((and (string= "" event) off)
X        (setq irc-signals (mapcar 'list recog)))
X       ((string= "" event)
X        (setq irc-signals (mapcar
X                           (function (lambda (arg) (list arg t))) recog)))
X       ((null sym) (irc-insert "Signal: Unknown argument '%s'." event))
X       (t (if off (setcdr (assoc sym irc-signals) nil)
X            (setcdr (assoc sym irc-signals) '(t))))))
X    (setq on (delq nil
X                   (mapcar        ; test against t because I have plans
X                    (function     ; to couple users and events
X                     (lambda (arg)
X                       (if (eq (nth 1 (assoc arg irc-signals)) t)
X                           arg))) recog)))
X    (if on
X      (irc-insert (concat "Signalling is enabled for "
X                          (irc-subst-comma
X                           (mapconcat 'prin1-to-string on ", ") "and") "."))
X      (irc-insert "All signalling is currently disabled."))))
X
X(defun irc-execute-stamp (stamp)
X  "Usage: /STAMP [ + | - | [+]event | -event | interval ] [...]
X
XSet time-stamping for IRC.  + means to turn it on for all messages from users
Xand - means to turn it off for them.  +event or just event will turn it on for
Xthat class of message and -event means to disable it for those messages.  An
Xinteger interval means to insert a message indicating the time every N minutes,
Xwhere N is the interval.  With no arguments simply insert a message indicating
Xthe current time-stamps.
X
XThe current time in HH:MM format can appear two different ways in IRC.  One is
Xto have it associate with 'event'; two events, 'private' and 'public' messages,
Xare supported this way.  The other is to have it as a stand-alone message
Xindicating the current time.  Both can be very useful in noting when someone
Xactually sent you a message or when another event happened if you happen to be
Xaway for a while.  The accuracy of the interval timer is currently limited to
X0-2 minutes beyond the interval.  It can be turned off by setting the interval
Xto 0."
X  (interactive "sSet time-stamp: ")
X  ;; whee.  napalm would feel particularly good here.
X  (setq stamp (irc-nuke-whitespace stamp))
X  (let (str sym event off)
X    (while (string< "" stamp)
X      ;; as the args go marching one by one the last one stopped ... <ahem>
X      (setq str   (substring stamp 0 (string-match "\\s +\\|$" stamp))
X            stamp (substring stamp (match-end 0)))
X      (string-match "^\\([-+]?\\)\\(.*\\)$" str)
X      (setq off (string= "-" (substring str (match-beginning 1) (match-end 1)))
X            event (substring str (match-beginning 2) (match-end 2))
X            sym (cond ((string= "" event) nil)
X                      ((string-match (concat "^" (regexp-quote event))
X                                     "private") 'private)
X                      ((string-match (concat "^" (regexp-quote event))
X                                     "public")  'public)
X                      ((natnump (car (read-from-string event)))
X                       (car (read-from-string event)))))
X      ;; the following cond is really what sets eveything
X      (cond ((and (string= "" event) off) (setq irc-message-stamp nil))
X            ((string= "" event) (setq irc-message-stamp t))
X            ((null sym) (irc-insert "Stamp: Unknown argument '%s'." event))
X            ((natnump sym) (setq irc-time-stamp sym))
X            (off (setq irc-message-stamp
X                       (car (delq sym (if (eq irc-message-stamp t)
X                                          '(private public)
X                                        (list irc-message-stamp))))))
X            (t (setq irc-message-stamp
X                     (cond ((null irc-message-stamp) sym)
X                           ((or (eq irc-message-stamp t)
X                                (eq irc-message-stamp sym)) irc-message-stamp)
X                           (t t)))))))
X  (irc-insert "%s messages get time-stamps.%s"
X              (cond ((eq irc-message-stamp t) "Private and public")
X                    ((null irc-message-stamp) "No")
X                    (t (capitalize (prin1-to-string irc-message-stamp))))
X              (if (zerop irc-time-stamp) ""
X                (format "  The time-stamp interval is %d minutes."
X                        irc-time-stamp))))
X
X(defun irc-execute-alias (alias)
X  "Usage: /ALIAS [ alias [ command [ args for command ]]]
X
XAllow 'alias' to be equivalent to 'command'.
XFor example, \"/ALIAS tf time tut.fi\" will make typing \"/tf\" be equivalent
Xto having issued the command \"/time tut.fi\".  Aliases can only be made
Xto existing commands not other aliases.  They are also only recognized when
Xin the command name position of a line.  If given with no arguments then
Xall aliases are displayed; if given with just an alias name then the alias
Xwith that name will be shown.  Aliases can be removed with /UNALIAS."
X  (interactive "sName for alias? ")
X  (if (interactive-p)
X      (setq alias (concat alias " "
X                          (read-string (format "Alias '%s' to which command? "
X                                               alias)))))
X  (setq alias (irc-nuke-whitespace alias))
X  (string-match "^/?\\(\\S *\\)\\s */?\\(\\S *\\)\\s *\\(.*\\)$" alias)
X  (let ((new (upcase (substring alias (match-beginning 1) (match-end 1))))
X        (cmd (upcase (substring alias (match-beginning 2) (match-end 2))))
X        (arg         (substring alias (match-beginning 3) (match-end 3)))
X        match)
X    (cond
X     ((string= "" new)
X      (let ((aliases irc-alias-alist))
X        (while aliases
X          (irc-insert "\"/%s\" is aliased to \"/%s\"."
X                      (car (car aliases)) (cdr (car aliases)))
X          (setq aliases (cdr aliases)))))
X     ((string= "" cmd)
X      (let ((alias (assoc new irc-alias-alist)))
X        (if alias
X            (irc-insert "\"/%s\" is aliased to \"/%s\"."
X                        (car alias) (cdr alias))
X          ;; this could possibly have done some matching to see whether
X          ;; just an abbrev was being given, but we'll just take it as given
X          (irc-insert "\"/%s\" is not aliased." new))))
X     (t  ; okay, we've got at least a command.  let's try and make this as
X         ; painless as possible. 
X      (setq match
X            (irc-check-list (mapcar 'car (append irc-command-alist
X                                                 (if irc-operator
X                                                     irc-operator-alist)))
X                            cmd 'start-only))
X      ;; try not to confuse a regular user with commands he couldn't use
X      ;; anyway, but let him at it if that's what he really wants.  it'll
X      ;; just come through as an error from the server in the long run ...
X      (if (and (not match) (not irc-operator))
X          (setq match (irc-check-list (mapcar 'car irc-operator) cmd t)))
X      (if (/= (length match) 1)
X          (if match
X              (irc-insert "'/%s' is an ambiguous command.  Could be %s." cmd
X                          (irc-subst-comma (mapconcat 'eval match ", ") "or"))
X            (irc-insert "Command not found: '/%s'." cmd))
X        ;; alias might be in the list already; if it is, just replace it
X        (if (assoc new irc-alias-alist)
X            (setcdr (assoc new irc-alias-alist) ; no trailing space if no arg
X                    (concat (downcase (car match))
X                            (if (string= "" arg) "" " ") arg))
X          (setq irc-alias-alist
X                (cons (cons new (concat (downcase (car match))
X                                        (if (string= "" arg) "" " ") arg))
X                      irc-alias-alist)))
X        (irc-insert "\"/%s\" has been aliased to \"/%s\"." new 
X                    (cdr (assoc new irc-alias-alist))))))))
X
X(defun irc-execute-unalias (alias)
X  "Usage: /UNALIAS alias
X
XRemove the 'alias' for a command."
X  ;; well, that's a pretty dull doc string.
X  (interactive (list (completing-read "Unalias which command? "
X                                      (cons '("" . "") irc-alias-alist)
X                                      nil t)))
X  (string-match "^\\s *\\(\\S *\\)\\s *$" alias)
X  (setq alias (substring alias (match-beginning 1) (match-end 1)))
X  (if (string= "" alias)
X      (if (not (interactive-p))
X          (call-interactively 'irc-execute-unalias))
X    (let ((match (irc-check-list (mapcar 'car irc-alias-alist) alias t)))
X      (if (/= (length match) 1)
X          (if match
X              (irc-insert "'%s' is an ambiguous alias.  Could be %s."
X                          (upcase alias)
X                          (irc-subst-comma (mapconcat 'eval match ", ") "or"))
X            (irc-insert "No alias found to match '%s'." (upcase alias)))
X        (setq irc-alias-alist (delq (assoc (car match) irc-alias-alist)
X                                    irc-alias-alist))
X        (irc-insert "'%s' is no longer aliased." (car match))))))
X  
X(defun irc-execute-help (topic)
X  "Usage: /HELP topic
X
XGet the documentation for 'command'.  If no command is given then a list
Xof the possible topics is shown.  Note that commands for IRC Operators will
Xnot appear in the help topics when not an IRC Operator."
X  (interactive "sHelp for which command? ")
X  (string-match "^\\s *\\(\\S *\\)\\s *$" topic)
X  (setq topic (substring topic (match-beginning 1) (match-end 1)))
X  (if (string= topic "")
X      (let ((str "Help is available for the following IRC-mode commands:\n")
X            (topics (mapcar 'car
X                            (append irc-command-alist
X                                    (if irc-operator irc-operator-alist)))))
X        (while topics
X          (setq str
X                (concat str
X                        (format "\n%14s%14s%14s%14s%14s"
X                                (nth 0 topics)
X                                (or (nth 1 topics) "") (or (nth 2 topics) "")
X                                (or (nth 3 topics) "") (or (nth 4 topics) "")))
X                topics (nthcdr 5 topics)))
X        (with-output-to-temp-buffer "*Help*" (princ str)))
X    (let ((match (irc-check-list (mapcar 'car (append irc-command-alist
X                                                      (if irc-operator
X                                                          irc-operator-alist)))
X                                 topic 'start-only)))
X      (if (and (not match) (not irc-operator))
X          (setq match  
X                (irc-check-list (mapcar 'car irc-operator-alist) topic t)))
X      (if (/= (length match) 1)
X          (if match
X              (irc-insert "Ambiguous help topic '%s'; could be %s."
X                          (upcase topic)
X                          (irc-subst-comma (mapconcat 'eval match ", ") "or"))
X            (irc-insert "No help is available for '%s'." (upcase topic)))
X        (setq match (car match))
X        (with-output-to-temp-buffer "*Help*"
X          (princ (documentation
X                  (intern-soft
X                   (concat "irc-execute-"
X                           (cdr (assoc match
X                                       (if (assoc match irc-command-alist)
X                                           irc-command-alist
X                                         irc-operator-alist))))))))))))
X
X;; miscellaneous irc-* commands
X(defun irc-truncate-buffer (size)
X  "Remove as many lines from the beginning of the buffer as is necessary
Xto get it under SIZE characters.  This function is used by irc-mode
Xto prevent an irc-session from consuming gross amounts of space."
X  (if (< (buffer-size) size) ()
X    (save-excursion
X      ;; first go to the lowest point posssible that would do it
X      (goto-char (- (point-max) size))
X      ;; get to the end of this line
X      (end-of-line)
X      (if (< (point) irc-mark)
X          ;; just to make sure we don't toast pending input
X          (delete-region 1 (1+ (point)))
X        (message "Warning: %s exceeding %s characters.  Couldn't truncate."
X                 (buffer-name (current-buffer)) size)))))
X
X(defun irc-read-passwd (&optional prompt)
X  "Allow user to type a string without it showing.  Returns string.
XIf optional PROMPT non-nil, use it as the prompt string in the minibuffer."
X  ;; this is based on a similar function in telnet.el
X  ;; the major drawback is that while being prompted for a password
X  ;; it stays in this routine until C-g, RET or LFD is typed.
X  (let ((passwd "") (echo-keystrokes 0) char)
X    (if prompt (message prompt))
X    (while (not (or (= (setq char (read-char)) 13) (= char 10)))
X      ;; naughty bit.  take C-h to mean DEL.
X      (if (or (= char 8) (= char 127))
X          (if (> (length passwd) 0)
X              (setq passwd (substring passwd 0 (1- (length passwd)))))
X        (setq passwd (concat passwd (char-to-string char))))
X      (if prompt (message (concat prompt (make-string (length passwd) ?*)))))
X    (if prompt (message ""))
X    passwd))
X
X(defun irc-read-user (prompt user &optional list)
X  "Prompting with PROMPT, read an IRC nickname from the minibuffer.
XSecond argument USER is a string which is checked for a non-ambiguous match
Xbefore the minibuffer read is done.  Optional third argument LIST is a
Xlist to use for checking rather than the irc-wholist.
X
XIt returns either the name of a user or an empty string (\"\")."
X  (string-match "^\\s *\\(\\S *\\)" user) ; just want one name
X  (setq user (substring user (match-beginning 1) (match-end 1)))
X  (let ((completion-ignore-case t) (list (if list list irc-wholist)) match)
X    (if (or (string= "" user)
X            (/= (length (setq match (irc-check-list list user))) 1))
X        (completing-read (format "%s%s"
X                                 (if (string= "" user) ""
X                                   (format (if (zerop (length match))
X                                               "No names match '%s'.  "
X                                             "'%s' is ambiguous.  ")
X                                           user))
X                                 prompt)
X                         ;; build the list for completing-read.  a
X                         ;; null string is there so that it can exit
X                         ;; without anything, since we require matches
X                         (mapcar 'list (cons "" list))
X                         nil t user)
X      (car match))))
X
X(defun irc-nuke-whitespace (str)
X  "One string argument.  Returns it with surrounding whitespace removed."
X  ;; i hate stupid extra spaces when parsing
X  (string-match "^\\s *" str)
X  (substring str (match-end 0) (string-match "\\s *$" str)))
X
X(defun irc-subst-comma (str newsep)
X  "Return the string formed by substituting for the last \", \" in STR
Xthe string NEWSEP followed by a space.  For example:
X  (irc-subst-comma \"1, 2, 3\" \"or\") => \"1, 2 or 3\"
X
XThis function is especially designed for making message from irc-mode
Xmore grammatically correct and the strings which it operates on should
Xbe carefully chosen so as to avoid possibly blowing away a comma that
Xreally wasn't separating elements in a list."
X  ;; did you know that example up there can't appear starting in column 0
X  ;; without screwing up lisp-indent-line?
X  (if (string-match ", [^,]*$" str)
X      (concat (substring str 0 (match-beginning 0)) " " newsep
X              (substring str (1+ (match-beginning 0))))
X    str))
X
X(defun irc-get-time ()
X  "Return the hour and minutes of the current time in the form \"HH:MM\"."
X  (let ((time (current-time-string)))
X    (substring time (string-match "..:.." time) (match-end 0))))
X
X(defun irc-check-time ()
X  "Check to see whether it is time to insert a current-time message into
Xthe *IRC* buffer."
X  (let* ((time (irc-get-time))
X         (old-minute (string-to-int (substring irc-last-time 3)))
X         (new-minute (string-to-int (substring time 3))))
X  (if (zerop irc-time-stamp) ()
X    ;; check the time sentinel
X    (if (string= irc-last-time time) ()
X      ;; time has gone stomping on by ...
X      (setq new-minute (+ new-minute (if (< new-minute old-minute) 60 0))
X            irc-last-time time
X            irc-total-time (+ irc-total-time (- new-minute old-minute)))
X      (if (< (- irc-total-time irc-last-stamp) irc-time-stamp) ()
X        ;; it's time for a new message
X        (irc-insert "*** It is now %s ***" time)
X        (setq irc-last-stamp irc-total-time))))))
X
X(defun irc-signal (user event)
X  "Return t if a ding should be issued for a USER/EVENT pair.
XCurrently only the event part of things is supported by /SIGNAL."
X  (let ((signal (cdr (assoc event irc-signals))))
X    (or (memq t signal) (member-general user signal 'string=)
X        (member-general user (cdr (assoc 'user irc-signals)) 'string=))))
X
X(defun irc-check-list (list item &optional start-only)
X  "See if LIST has string ITEM.  Returns a list of possible matches.  The list
Xreturned is based on the following precedence rules:  if there is an exact
Xmatch, it is returned.  If there are any strings in the list whose beginning
Xmatch the item, they are returned.  If that fails and optional argument
XSTART-ONLY is missing or nil, strings which have the item match anywhere are
Xreturned.  As a last resort, nil is returned.
XThis function is not case-sensitive."
X  (let (return (case-fold-search t) (item (regexp-quote item)))
X    (if (setq return
X              (delq nil                         ; whole words
X                    (mapcar (function   
X                             (lambda (arg)
X                               (if (string-match (concat "^" item "$") arg)
X                                   arg))) list)))
X        return
X      (if (setq return
X                (delq nil                       ; beginnings
X                      (mapcar (function
X                               (lambda (arg)
X                                 (if (string-match (concat "^" item) arg)
X                                     arg))) list)))
X          return
X        (if start-only
X            nil
X          (delq nil
X                (mapcar (function               ; anywhere
X                         (lambda (arg)        
X                           (if (string-match (concat "." item) arg) arg)))
X                        list)))))))
X
X(defun irc-maintain-list (list item func)
X  "Maintain a LIST of strings by adding or removing string ITEM.
XThird argument FUNC should be 'add or t or to make sure the item is in
Xthe list or 'remove or nil to make sure item is out of the list."
X  (cond
X   ((memq func '(add t))
X    (if (member-general item (eval list) 'string=) ()
X      ;; sigh.  with random adding of names via sending messages to people
X      ;; that irc-mode doesn't know about a name can be here in the wrong
X      ;; case.  this has the potential to screw things up big so we'll ditch
X      ;; the old one in favour of whatever is being added.
X      (let* ((case-fold-search t)
X             (old (delq nil
X                        (mapcar
X                         (function
X                          (lambda (arg)
X                            (if (string-match (concat "^" (regexp-quote item)
X                                                      "$") arg)
X                                arg))) (eval list)))))
X        (while old
X          (irc-maintain-list list (car old) 'remove)
X          (setq old (cdr old)))
X        (set list (cons item (eval list))))))
X   ((memq func '(remove nil))
X    (set list  
X         (delq nil (mapcar (function (lambda (arg)
X                                       (if (string= item arg) nil arg)))
X                           (eval list)))))))
X
X(defun irc-burst-comma (str)
X  "Take a comma or space separated STR and turn it into a list of its elements.
XExample: \"1, 2,3,4,  6  7\" becomes the list (\"7\" \"6\" \"4\" \"3\" \"2\" \"1\")."
X  (let (list sub (beg 0))
X    (string-match "" str)
X    (while (string-match ",+\\|\\s +\\|,+\\s +" str beg)
X      (if (not (string= (setq sub (substring str beg (match-beginning 0))) ""))
X          (setq list (cons sub list)))
X      (setq beg (match-end 0)))
X    (if (/= (length str) beg) (cons (substring str beg) list) list)))
X
X;; miscellaneous other commands (usually from other sources)
X
X;; this makes up for not being able to provide a :test to memq.
X;; member-general by Bard Bloom <bard@theory.lcs.mit.com>
X(defun member-general (x l comparison)
X  "Is X a member of L under COMPARISON?"
X  (let ((not-found t))
X    (while (and l not-found)
X      (setq not-found (not (funcall comparison x (car l)))
X            l         (cdr-safe l)))
X    (not not-found)))
X
X;; wish i could remember who I got this from; I had to patch it to work
X;; with the minibuffer correctly but it is mostly untouched.
X(defun walk-windows (proc &optional no-mini)
X  "Applies PROC to each visible window (after selecting it, for convenience).
XOptional arg NO-MINI non-nil means don't apply PROC to the minibuffer
Xeven if it is active."
X  (let* ((real-start (selected-window))
X          (start (next-window real-start no-mini))
X          (current start) done)
X     (while (not done)
X       (select-window current)
X       (funcall proc)
X       (setq current (next-window current no-mini))
X       (setq done (eq current start)))
X     (select-window real-start)))
X
X(defun count-windows (&optional no-mini)
X   "Returns the number of visible windows.
XOptional arg NO-MINI non-nil means don't count the minibuffer
Xeven if it is active."
X   (let ((count 0))
X     (walk-windows (function (lambda () (setq count (1+ count)))) no-mini)
X     count))
X
X;; swiped from minibuf.el, but made exclusive to * Minibuf-n*.
X(defun minibuffer-message (format &rest args)
X  "Print a temporary message at the end of the Minibuffer.
XAfter 2 seconds or when a key is typed, erase it."
X  (if (zerop (minibuffer-depth)) (apply 'message format args)
X    (let (p)
X      (save-excursion
X        (set-buffer (concat " *Minibuf-" (1- (minibuffer-depth)) "*"))
X        (unwind-protect
X            (progn
X              (setq p (goto-char (point-max)))
X              (insert (apply 'format format args))
X              (sit-for 2))
X          (delete-region p (point-max)))))))
X
X(defun irc-find-to (str &optional explicit)
X  "Find the part of STRING that IRC-mode will interpret as the sendlist.
XIf no explicit list is found, irc-default-to is returned.  The string returned
Xis either : or ; terminated.
X
XIf optional EXPLICIT is non-nil, then return t if a sendlist was explicitly
Xspecified, nil if the sendlist was implicit."
X  (let ((matched (string-match "^[A-Za-z0-9-_|{,*]*[;:]" str)))
X    (if matched (if explicit t (substring str 0 (match-end 0)))
X      (if explicit nil irc-default-to))))
X
X(defun irc-find-message (string)
X  "Find the message that IRC will see if STR were sent.  For messages
Xsent with explicit lists, this is everything following the colon or
Xsemi-colon.  For everything else, it is just the string."
X  (substring string (length (irc-find-to string))))
X
X;; functions for the irc-history list
X(defun irc-add-to-hist (str)
X  "Put STRing at the head of the irc-history list."
X  (if (string-match "^[:;]" str)
X      (setq str
X            (concat irc-last-explicit (substring str 1 (length str)))))
X  (setq irc-history (append (list str) irc-history))
X  (and (> (length irc-history) irc-max-history)
X       (setq irc-history (reverse (cdr (reverse irc-history))))))
X
X(defun irc-yank-prev-command ()
X  "Put the last IRC /command in the input-region."
X  (interactive)
X  (delete-region irc-mark (goto-char (point-max)))
X  (insert "/" irc-last-command)
X  (goto-char (1+ irc-mark)))
X
X(defun irc-history-prev (arg)
X  "Select the previous message in the IRC history list.  ARG means
Xselect that message out of the list (0 is the first)."
X  (interactive "P")
X  (let ((str (nth (or arg (1+ irc-history-index)) irc-history)))
X    (if (not str)
X        (message "No message %d in history." (or arg (1+ irc-history-index)))
X      (delete-region irc-mark (goto-char (point-max)))
X      (insert str)
X      (goto-char irc-mark)
X      (setq irc-history-index (or arg (1+ irc-history-index))))))
X
X(defun irc-history-next (arg)
X  "Select the next message in the IRC history list.  With prefix ARG
Xselect that message out of the list (same as irc-history-prev if
Xcalled with a prefix arg)."
X  (interactive "P")
X  (if arg (irc-history-prev arg)
X    (if (= irc-history-index -1)
X        (message "No next message in history.")
X      (delete-region irc-mark (goto-char (point-max)))
X      (insert (if (zerop irc-history-index) ""
X                (nth (1- irc-history-index) irc-history)))
X      (setq irc-history-index (1- irc-history-index)))))
X
X(defun irc-kill-input ()
X  "Delete the input region and start out fresh.  This function is recommended
Xover any other way of killing the input-region interactively because it
Xalso resets the index for the history list."
X  (interactive)
X  (delete-region irc-mark (goto-char (point-max)))
X  (setq irc-history-index -1))
X
X(defun irc-history-menu ()
X  "List the history of messages kept by irc-mode in another buffer."
X  (interactive)
X  (let ((pop-up-windows t) (hist irc-history) (line 0))
X    (save-excursion
X      (set-buffer (get-buffer-create "*IRC History*"))
X      (fundamental-mode)
X      (erase-buffer)
X      (while hist
X        (insert (format "%2d: %s\n" line (car hist)))
X        (setq hist (cdr hist))
X        (setq line (1+ line)))
X      (if (zerop line)
X          (insert "No messages have been sent to IRC yet."))
X      (set-buffer-modified-p nil)
X      (goto-char (point-min)))
X    (display-buffer "*IRC History*")))
X
X;; stuff about irc-mode
X(defun irc-version (&optional arg)
X  "Print the current version of irc.el in the minibuffer.  With optional
XARG, insert it in the current buffer."
X  (interactive "P")
X  (if arg (insert irc-version) (princ irc-version)))
X
X(defun irc-news ()
X  "Shows news about latest changes to irc.el.  Even shows news about
Xold changes to irc.el -- what a wonderous function indeed."
X  (interactive)
X  (save-excursion
X    (set-buffer (get-buffer-create "*IRC-mode News*"))
X    (erase-buffer)
X    (insert "Latest changes to Irc mode:
X
XFor functions, use C-h f to describe the function.  Variables use C-h v.
X
X 2 Aug 89 -- IRC-mode has gotten some enhancements and a few small bugs
X   fixed.  The bug with sending messages greater than 255 characters to
X   an implicit sendlist was fixed.  A small bug with setting the window
X   point of a non-selected *IRC* buffer was fixed.  The command parser
X   was cleaned up a little so the commands /WHO and /WHOIS can live happily
X   together.  
X
X   M-x irc now makes a little bit more sense to have bound to a key.  By
X   default it will just go to the buffer of the IRC process if one exists.
X   If none does then buffer *IRC* will be used or re-used and a new connexion
X   opened.  With a prefix argument then a new IRC process is created in
X   addition to any pre-existing ones.  Multiple IRC sessions should be
X   able to co-exist peacefully in one Emacs.
X
X   Public messages which were sent by the server as private messages are
X   now represented as public to lower the confusion level.
X
X   New commands were added:
X    
X     o /NAMES to show the nicknames of users on a per-channel basis.
X     o /TRACE for IRC Operators to monitor the links between servers.
X     o /STAMP for including time-stamping in IRC messages and at intervals.
X     o /ALIAS to add preferred names for commands.
X     o /REHASH for IRC Operators to reread their server configuration file.
X
X   /HELP for commands has been improved.  Each command now includes a
X   \"Usage\" line to show any arguments for the command.
X
X   : now behaves differently than ; when typed as the first character on a
X   line in the input region.  It will insert the name of the last person who
X   sent a private message.
X
X   Now when you are ignoring someone a message to that effect will be sent
X   out if that user sends a private message or invitation to you.
X 
X   If a message recipient's name is not found it is now sent to whatever
X   was provided.  This should make sending to people hiding on channels
X   less than 0 a slightly less aggravating experience.
X
X   Many of the functions have been changed to improve the way they get
X   called interactively.  For commands expecting a channel number (optional
X   or not) a prefix argument will do.  Most commands which take a user's
X   name as an argument will now accept it via a completing-read.
X
X   There are more default bindings for commands.
X
X   A texinfo manual on IRC and IRC-mode should be available shortly.
X
X   My thanks to Scanner, Nathan Glasser, Fred Douglis, Geoff Goodfellow and
X   Chris Davis for their helpful comments regarding IRC-mode.  Further comments
X   are welcome.
X
X22 Jun 89 -- I would have to put everything in here for the first entry in
X   order to have something to reference against \"Latest changes\".  I
X   shan't do that.  This first entry shall just say that this is IRC-mode
X   version 1.0Gimel, in its first distribution to testers.  Play with the
X   /HELP command to see what it can offer you.  Send comments/bug reports
X   to me (Dave Lawrence, <tale@pawl.rpi.edu>) and I will try to tend to 
X   them as quickly as possible.
X
X   One thing to note that I don't think is documented in any functions is
X   is how sending works.  Basically, any line that you send which does not
X   start with a / gets processed in the following way: if you have a string
X   which is all alpha-numeric characters (with items seperated by commas)
X   up to and including a colon or semi-colon, it is called your explicit
X   sendlist.  The elements of that list will be processed for sending to the
X   recipients you name.  Partial matching of names is also done.  If a line
X   starts with a colon or semi-colon then that last used explicit sendlist
X   is considered the sendlist for the message.  If no such prefix list exists
X   then something called your implicit sendlist is used.  This list is set
X   with /SEND (aka /QUERY).  In all of these lists the character \"*\" is
X   recognized as meaning \"whatever channel I happen to be on when I am
X   sending\".
X   
X   Look at some of the bindings in C-h m (describe-mode).  Since C-p is a
X   normal movement command, scrolling through your message history is
X   instead bound to C-c C-p (and C-c C-n for going the other way).  C-c C-u
X   will always kill the current line of input no matter what your position.
X   While a user-variable (irc-max-history) is available to determine how
X   many messages should be retained in the history, only the last command
X   is remembered for command-history.  It is available with C-c p.
X
X   One last suggestion -- try M-x edit-options followed by a search through
X   the options buffer for irc.  This should put you in an area where the
X   available irc user-variables are all together, with their documentation
X   strings and current values.
X
X   I hope you enjoy my implementation of an IRC client.")
X    (goto-char (point-min)))
X  (display-buffer "*IRC-mode News*"))
END_OF_FILE
if test 39278 -ne `wc -c <'irc-3'`; then
    echo shar: \"'irc-3'\" unpacked with wrong size!
fi
# end of 'irc-3'
fi
echo shar: End of archive 3 \(of 3\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 3 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0