home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / elisp / interfaces / irc.el < prev    next >
Encoding:
Text File  |  1991-03-30  |  141.8 KB  |  3,012 lines

  1. ;;;;;;;;;;;;;;;;;;;;;;;;;;; -*- Mode: Emacs-Lisp -*- ;;;;;;;;;;;;;;;;;;;;;;;;;;
  2. ;; irc.el --- A user interface for the Internet Relay Chat
  3. ;; Author          : David C Lawrence           <tale@pawl.rpi.edu>
  4. ;; Created On      : Wed Jun 14 22:22:57 1989
  5. ;; Last Modified By: Tom Hinds                  <rocker@bucsf.bu.edu>
  6. ;; Last Modified On: Sun 19 July 90
  7. ;; Update Count    : for ircd V2.5
  8. ;; Status          : Seemingly stable.
  9. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  10. ;;  Copyright (C) 1989, 1990  David C Lawrence
  11.  
  12. ;;  This program is free software; you can redistribute it and/or modify
  13. ;;  it under the terms of the GNU General Public License version 1
  14. ;;  as published by the Free Software Foundation.
  15.  
  16. ;;  This program is distributed in the hope that it will be useful,
  17. ;;  but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. ;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19. ;;  GNU General Public License for more details.
  20.  
  21. ;;  You should have received a copy of the GNU General Public License
  22. ;;  along with this program; if not, write to the Free Software
  23. ;;  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  24.  
  25. ;; Comments and/or bug reports about this interface should be directed to:
  26. ;;     Dave Lawrence          <tale@ai.mit.edu>
  27. ;;     76 1/2 13th Street     +1 518 273 5385
  28. ;;     Troy NY 12180          Generally available on IRC as "tale"
  29.  
  30. ;; Defined variables
  31. (provide 'irc)
  32. (defvar irc-server (or (getenv "IRCSERVER") "chaos.cs.brandeis.edu")
  33.   "*A host running the IRC daemon.
  34. IRC servers generally restrict which machines can maintain connexions with
  35. them, so you'll probably have to find a server in your local domain.")
  36.  
  37. (defvar irc-port 6667
  38.   "*The port on which the IRC server responds.
  39. Many sites don't have irc as a named service (ie, it has no entry in
  40. /etc/inetd.conf) so you might have to set this to a number; the most
  41. common configuration is to have IRC respond on port 6667.")
  42.  
  43. (defvar irc-oops "Oops ... please ignore that."
  44.   "*The text to send to the original IRC message recipient when using /OOPS.")
  45.  
  46. (defvar irc-message-stamp 'private
  47.   "Mark messages received in IRC with the time of their arrival if non-nil.")
  48.  
  49. (defvar irc-time-stamp 0
  50.   "How often, in minutes, to insert a time-stamp into *IRC* buffers.")
  51.  
  52. (make-variable-buffer-local
  53.  (defvar irc-nick (or (getenv "IRCNICK") (user-login-name))
  54.    "The nickname with which to enter IRC.
  55. The default value is set from your login name.  Using /NICKNAME changes it."))
  56.  
  57. (defvar irc-noglob nil
  58.   "*If non-nil the nickname globbing will not be done.")
  59.  
  60. (defvar irc-spacebar-pages t
  61.   "*When this variable is non-nil, the following keys are in effect when
  62. point is in the output region.
  63.  
  64.  
  65. SPC      scroll-forward    DEL           scroll-backward
  66. TAB      previous-line     LFD or RET    next-line")
  67.  
  68. (defvar irc-maximum-size 20480
  69.   "*Maximum size that the *IRC* buffer can attain, in bytes.
  70. The default value of 20k represents an average of about 512 lines, or roughly
  71. 22 screens on a standard 24 line monitor.  A nil value will make the size of
  72. the buffer effectively unbounded as far as IRC is concerned; the real ceiling
  73. is 8Meg-1K characters.")
  74.  
  75. (defvar irc-mode-hook nil
  76.   "Hook to run after starting irc-mode but before connecting to the server.")
  77.  
  78. (defvar irc-no-auto-restart nil
  79.   "*If non-nil, don't restart an IRC session if it exits abnormally.")
  80.  
  81. (defvar irc-silent-bell nil
  82.   "*If non-nil, don't issue audible beeps for signal events.")
  83.  
  84. (defvar irc-pop-ratio 4
  85.   "*An integer value means to display the *IRC* buffer when a signal is issued.
  86. The number represents roughly how much of the Emacs screen to use when
  87. popping up the IRC window if only one window is visible.  The reciprocal
  88. is used, so a value of 1 causes the window to appear full-screen, 2 makes
  89. the window half of the screen, 3 makes it one third, et cetera.  If the value
  90. is not an integer then no attempt is made to show the *IRC* buffer if it
  91. is not already visible.")
  92.  
  93. (defvar irc-max-history 40
  94.   "*The maximum number of messages retained by irc-mode.
  95. This limits messages sent, not messages received.  They are stored to be
  96. easily recalled by irc-message-prev and irc-message-next (C-c C-p and C-c C-n
  97. by default).")
  98.  
  99. (defvar irc-conserve-space t
  100.   "*If this variable is set non-nil then the C client will be partially
  101. mimicked for message insertion.  Private messages will be prefixed by
  102. \"*SENDER*\" and public messages will be prefixed by \"<SENDER>\".  No blank
  103. lines are put in the buffer.")
  104.  
  105. (defvar irc-confirm nil
  106.   "If non-nil, provide confirmation for messages sent on IRC.
  107. It should be noted that confirmation only indicates where irc-mode
  108. tried to send the message, not whether it was actually received.
  109. Use the /CONFIRM command to change.")
  110.  
  111. ;; Rocker 30may90 patch for irc-parse-notice/away messages
  112. (defvar irc-see-old-shit nil
  113.   "If nil, one will not see away messages from old servers")
  114.  
  115. ;; Rocker 13july90 set to true for 2.5 and greater
  116. (defvar irc-no-motd t  
  117.   "*If non-nil, don't ask for the message of the day when first connecting.")
  118.  
  119. (defvar irc-processes nil
  120.   "All currently open streams to irc-servers are kept in this list.")
  121.  
  122. (make-variable-buffer-local
  123.  (defvar irc-signals '((private t) (invite t) (wall t)
  124.                        (public) (join) (nick) (topic) (user))
  125.    "Events in IRC that should get signalled when they occur."))
  126.  
  127. (make-variable-buffer-local
  128.  (defvar irc-ignores nil
  129.    "A list of users whose messages and events will be ignored."))
  130.  
  131. (make-variable-buffer-local
  132.  (defvar irc-notifies '(join nick topic)
  133.    "Events in IRC that should get a message when they occur."))
  134.  
  135. (make-variable-buffer-local
  136.  (defvar irc-message-history nil
  137.    ;; This includes both successfully and unsuccessfully sent messages.
  138.    "A list of messages which irc-mode has processed."))
  139.  
  140. (make-variable-buffer-local
  141.  (defvar irc-command-history nil
  142.    ;; This includes both successful and unsuccessful commands.
  143.    "A list of commands which irc-mode has processed."))
  144.  
  145. (make-variable-buffer-local
  146.  (defvar irc-default-to "*;"
  147.    "The default recipient of a message if no : or ; is provided."))
  148.  
  149. (defvar irc-use-kill-window t)
  150. (defvar irc-use-wall-window t)
  151. (defvar irc-use-ignore-window t)
  152.  
  153. (make-variable-buffer-local
  154.  (defvar irc-other-buffers ""
  155.    "Single character indicators of presence of other buffers relating to 
  156. this IRC session
  157. K == Kill window
  158. W == Wall/Wallops window
  159. I == Messages from ignored people window
  160.  
  161. Added by Dylan, 24 July 1990"))
  162.  
  163. (defvar irc-mode-map nil
  164.   "The keymap which irc-mode uses.
  165.  
  166. Currently set to: \\{irc-mode-map}")
  167.  
  168. (defvar irc-alias-alist
  169.   '(("N" . "names")    
  170.     ("W" . "who")      
  171.     ("?" . "help")     
  172.     ("L" . "list"))
  173.   "An association list of command aliases used in irc-mode.")
  174.  
  175. (defconst irc-command-alist
  176.   '(("WHO" . "who")           ; For a list of users and their channels
  177.     ("HELP" . "help")         ; Get help on the /COMMANDs
  178.     ("INFO" . "info")         ; Information about the IRC author
  179.     ("LIST" . "list")         ; Show a list of channels and topics
  180.     ("MOTD" . "motd")         ; Get the message of the day at a server
  181.     ("OOPS" . "oops")         ; Resend a misdirected message
  182.     ("OPER" . "oper")         ; Login as an IRC operator
  183.     ("QUIT" . "quit")         ; Exit IRC
  184.     ("SEND" . "send")         ; Set the implicit send list for messages
  185.     ("TIME" . "time")         ; Get the current time from a server
  186.     ("MSG" . "privmsg")       ; Send a private message to someone
  187.     ("ADMIN" . "admin")       ; Get information about IRC admins
  188.     ("LINKS" . "links")       ; Show which servers are in the IRC-net
  189.     ("NAMES" . "names")       ; Display names of users on each channel
  190.     ("QUOTE" . "quote")       ; Send raw text to the server
  191.     ("TOPIC" . "topic")       ; Change the topic of the current channel
  192.     ("USERS" . "users")       ; Show users signed on at a server
  193.     ("WHOIS" . "whois")       ; Get slightly longer information about a user
  194.     ("GWHO" . "gwho")         ; Ghost whois, Dylan 22July90
  195.     ("WHOWAS" . "whowas")     ; Whowas, Rocker 24may90 
  196.     ("STAMP" . "stamp")       ; Set time notification interval
  197.     ("FINGER" . "finger")     ; Show more info about user than whois
  198.     ("LUSERS" . "lusers")     ; Get the number of users and servers
  199.     ("INVITE" . "invite")     ; Ask another user to join your channel
  200.     ("NOTIFY" . "notify")     ; Change which events give notification
  201.     ("SIGNAL" . "signal")     ; Change which events give a signal
  202.     ("SUMMON" . "summon")     ; Ask a user not on IRC to join it
  203.     ("NICKNAME" . "nick")     ; Change your IRC nickname
  204.     ("CONFIRM" . "confirm")   ; Set message confirmation on or off
  205.     ("OPERWHO" . "operwho")   ; Get a list of the operators on IRC
  206.     ("VERSION" . "version")   ; Get current irc.el and ircd version
  207.     ("REDIRECT" . "redirect") ; Resend the last message to someone else
  208.     ("MODE" . "mode")         ; Setting Channel modes and checking (Rocker)
  209.     ("KICK" . "kick")         ; Kicking a user off a string channel (Rocker)
  210.     ("WALLOPS" . "wallops")   ; Send a wall just to the IRC Operatos
  211.     ("AWAY" . "away") ("HERE" . "here") ; Give some indication of your presence
  212.     ("JOIN" . "join") ("LEAVE" . "leave") ; Join or leave a channel
  213.     ("ALIAS" . "alias") ("UNALIAS" . "unalias") ; Add/remove command aliases
  214.     ("IGNORE" . "ignore") ("UNIGNORE" . "unignore")) ; (Un)Ignore a user
  215.   "An association list of the regular commands to which all users have access.
  216. Form is (\"COMMAND\" . \"function\") where \"function\" is that last element in
  217. an irc-do-* symbol.  See also irc-alias-alist and irc-operator-alist.")
  218.  
  219. (defconst irc-operator-alist
  220.   '(("KILL" . "kill")         ; Forcibly remove a user
  221.     ("WALL" . "wall")         ; Send a message to everyone on IRC
  222.     ("TRACE" . "trace")       ; Show the links between servers
  223.     ("REHASH" . "rehash")     ; Reread irc.conf
  224.     ("CONNECT" . "connect"))  ; force a connexion to another server
  225.  
  226.   ;; It is kept as a separate list so that regular users won't wonder how
  227.   ;; come the commands don't work for them.
  228.   "As association list of commands which only an IRC Operator can use.")
  229.  
  230. (defconst irc-version "IRC-mode Version 2.5Beta"
  231.   "The currently loaded version of irc-mode.")
  232.  
  233. ;; keymap
  234. (or irc-mode-map
  235.     (progn
  236.       (setq irc-mode-map (make-keymap))
  237.       (define-key irc-mode-map "\C-j" 'irc-process-input)
  238.       (define-key irc-mode-map "\C-m" 'irc-process-input)
  239.       (define-key irc-mode-map "\C-i"      'irc-tab)
  240.       (define-key irc-mode-map "\C-c\C-a"  'irc-do-alias)
  241.       (define-key irc-mode-map "\C-c\C-c"  'irc-do-names)
  242.       (define-key irc-mode-map "\C-c\C-f"  'irc-do-finger)
  243.       (define-key irc-mode-map "\C-c\C-h"  'irc-do-help)
  244.       (define-key irc-mode-map "\C-c\C-i"  'irc-do-invite)
  245.       (define-key irc-mode-map "\C-c\C-j"  'irc-do-join)
  246.       (define-key irc-mode-map "\C-c\C-l"  'irc-do-list)
  247.       (define-key irc-mode-map "\C-c\C-m"  'irc-history-menu)
  248.       (define-key irc-mode-map "\C-c\C-n"  'irc-message-next)
  249.       (define-key irc-mode-map "\C-c\C-o"  'irc-do-oops)
  250.       (define-key irc-mode-map "\C-c\C-p"  'irc-message-prev)
  251.       (define-key irc-mode-map "\C-c\C-q"  'irc-do-quote)
  252.       (define-key irc-mode-map "\C-c\C-r"  'irc-do-redirect)
  253.       (define-key irc-mode-map "\C-c\C-t"  'irc-do-topic)
  254.       (define-key irc-mode-map "\C-c\C-s"  'irc-do-send)
  255.       (define-key irc-mode-map "\C-c\C-u"  'irc-kill-input)
  256.       (define-key irc-mode-map "\C-c\C-v"  'irc-do-version)
  257.       (define-key irc-mode-map "\C-c\C-w"  'irc-do-who)
  258.       (define-key irc-mode-map "\C-c\C-?"  'irc-kill-input)
  259.       ;; it's nice to bind to a key while in development
  260.       ;; (define-key irc-mode-map "\C-c " 'irc-pong)
  261.       (define-key irc-mode-map "\C-c#" 'irc-do-lusers)
  262.       (define-key irc-mode-map "\C-ca" 'irc-do-admin)
  263.       (define-key irc-mode-map "\C-cc" 'irc-do-connect)
  264.       (define-key irc-mode-map "\C-ck" 'irc-do-quit)
  265.       (define-key irc-mode-map "\C-cl" 'irc-do-links)
  266.       (define-key irc-mode-map "\C-cn" 'irc-command-next)
  267.       (define-key irc-mode-map "\C-co" 'irc-do-operwho)
  268.       (define-key irc-mode-map "\C-cp" 'irc-command-prev)
  269.       (define-key irc-mode-map "\C-cq" 'irc-do-leave)
  270.       (define-key irc-mode-map "\C-cr" 'irc-raw-mode)
  271.       (define-key irc-mode-map "\C-cs" 'irc-do-summon)
  272.       (define-key irc-mode-map "\C-ct" 'irc-toggle-glob)
  273.       (define-key irc-mode-map "\C-cu" 'irc-do-users)
  274.       (define-key irc-mode-map "\C-?"  'irc-del-backward-char)
  275.       ;; make any self-inserting keys call irc-self-insert
  276.       (mapcar (function
  277.                (lambda (key)
  278.                  (define-key irc-mode-map key 'irc-self-insert)))
  279.               (where-is-internal 'self-insert-command nil nil))))
  280.  
  281. ;; filters (mostly irc-parse-*)
  282. ;; Filtering of server messages from reception to insertion in the buffer
  283. ;; are all done on this page.  In particular, if a new server message has
  284. ;; to be dealt with, it should be added in the irc-parse-server-msg function.
  285. (defun irc-filter (proc str)
  286.   "The filter for IRC streams."
  287.   (let (irc-ring-bell opoint npoint (ibuf (process-buffer proc)))
  288.     (save-excursion
  289.       (set-buffer ibuf)
  290.       (setq irc-scratch (concat irc-scratch str)
  291.             opoint      (marker-position irc-mark))
  292.       (save-excursion (irc-check-time))
  293.       ;; do as many lines as possible
  294.       (while (string-match "\n" irc-scratch)
  295.         (setq irc-scratch   (irc-parse-server-msg irc-scratch)
  296.               irc-ring-bell (cdr irc-scratch)
  297.               irc-scratch   (car irc-scratch)
  298.               npoint        (marker-position irc-mark))
  299.         (if (not irc-ring-bell) ()
  300.           (or irc-silent-bell (ding 'no-terminate))
  301.           (minibuffer-message " [Bell in %s]" (buffer-name ibuf))))
  302.       ;; do finger output
  303.       (while (string-match "\r?\n" irc-finger-scratch)
  304.         (irc-notice (substring irc-finger-scratch 0 (match-beginning 0)))
  305.         (setq irc-finger-scratch (substring irc-finger-scratch (match-end 0))
  306.               npoint             (marker-position irc-mark))))
  307.     (or (= npoint opoint) (irc-display ibuf irc-ring-bell))))
  308.  
  309. (defun irc-finger-filter (proc str)
  310.   ;; for irc-finger output.  unfortunately str just can't be prefixed and
  311.   ;; handed off to the regular filter.  
  312.   (save-excursion
  313.     (set-buffer (process-buffer proc))
  314.     (setq irc-finger-scratch (concat irc-finger-scratch str)))
  315.   (irc-filter proc ""))
  316.  
  317. (defun irc-parse-server-msg (str)
  318.   (let ((loc 0) (line (substring str 0 (string-match "\n" str))))
  319.     ;; need to double % signs or formatting later down the line will attempt
  320.     ;; to interpret them.
  321.     (while (string-match "%" line loc)
  322.       (setq line (concat (substring line 0 (match-end 0)) "%"
  323.                          (substring line (match-end 0)))
  324.             loc (1+ (match-end 0))))
  325.     (if irc-raw-mode (irc-insert line))
  326.     (cons
  327.      ;; the part of str not being parsed.
  328.      (substring str (1+ (string-match "\n" str)))
  329.      (condition-case aor
  330.          (cond
  331.           ;; each function here should return t or nil indicating whether
  332.           ;; to issue a signal.  Some of these regexps are fugly because of
  333.           ;; inconsistent protocol use by servers.  Jarkko is fixing it.
  334.           ((string-match "^PING" line) (irc-pong))
  335.           ((string= irc-raw-mode " Raw") nil)
  336.           ((string-match "^:\\S +\\s +MSG" line) (irc-parse-public line))
  337.           ((string-match "^:\\S +\\s +CHANNEL" line) (irc-parse-channel line))
  338.           ((string-match "^:\\S +\\s +INVITE" line) (irc-parse-invite line))
  339.           ((string-match "^:\\S +\\s +NICK" line) (irc-parse-nick line))
  340.           ((string-match "^:\\S +\\s +WALLOPS" line) (irc-parse-wallops line)) 
  341.           ((string-match "^:\\S +\\s +WALL" line) (irc-parse-wall line)) 
  342.           ((string-match "^:\\S +\\s +QUIT" line) (irc-parse-quit line))
  343.           ((string-match "^:\\S +\\s +KILL" line) (irc-parse-kill line))
  344.           ((string-match "^:\\S +\\s +TOPIC" line) (irc-parse-topic line))
  345.           ((string-match "^:\\S *\\s +3[0-9]+" line) (irc-parse-RPL line))
  346.           ((string-match "^:\\S *\\s +4[0-9]+" line) (irc-parse-ERR line))
  347.       ((string-match "^:\\S +\\s +MODE" line) (irc-parse-mode line))
  348.           ;; sigh.  just ^NOTICE was fine until someone used the protocol wrong
  349.           ((string-match "^\\(:[---A-Za-z0-9.]* \\)?NOTICE" line)
  350.        (irc-parse-notice line))
  351.       ;; patched to recognize :server.domain.etc NOTICE message
  352.           ;;  14 Apr 90 ckd@bu-pub.bu.edu
  353.           ;; ditto!!  private messages should just be for msgs between users!!
  354.           ((string-match "^\\(:\\S * +\\)?PRIVMSG" line) (irc-parse-priv line))
  355.           ((string-match "^ERROR" line) (irc-parse-error line))
  356.           ((string-match "^WHOREPLY" line) (irc-parse-whoreply line))
  357.           ((string-match "^NAMREPLY" line) (irc-parse-namreply line))
  358.           ((string-match "^LINREPLY" line) (irc-parse-linreply line))
  359.           ;; Just foist it off to the same error handler that will deal with
  360.           ;; anything else screwed-up with string-matches.  (Nate found
  361.           ;; a couple of occurences with numeric msgs from garfield.mit.edu.)
  362.           (t (signal 'args-out-of-range '(0 0))))
  363.        (args-out-of-range
  364.         (if irc-raw-mode ()
  365.           (irc-insert line)
  366.           (irc-insert
  367.        ;; Since I'm keeping it current...me
  368.            "(Please let rocker@bucsf.bu.edu know about this; it's a bug.)"))
  369.         nil)))))
  370.  
  371. (defun irc-parse-channel (str)
  372.   (let ((user
  373.      ;; Rocker 14july90  \\([-1-9]\\) to (.*) for string channels
  374.          (substring str 1 (string-match "\\s CHANNEL\\s +\\(.*\\)$" str)))
  375.         (channel (substring str (match-beginning 1) (match-end 1))))
  376.     ;; make sure that user is in the wholist since we have to take
  377.     ;; this sort of information where we can until Jarkko supports
  378.     ;; global ENTER/QUIT messages (which he might not do ...)
  379.     (irc-maintain-list 'irc-wholist user 'add)
  380.     (if (string= user irc-nick)
  381.         (progn
  382.           (if (string= "0" channel)
  383.               (irc-insert "You have left channel %s." irc-channel)
  384.             (irc-insert "You are now a member of channel %s." channel))
  385.           (setq irc-channel channel)
  386.           nil)                  ; don't issue a bell for our own join
  387.       (if (or (member-general user irc-ignores 'string=)
  388.               (not (memq 'join irc-notifies))) () ;put ignored messages into another buffer and set mode line to reflect presence of ignored messages (I somplace on line (only ignores, not joins...)
  389.         (if (string= "0" channel)
  390.             (irc-insert "*** %s has left channel %s ***" user irc-channel)
  391.           (irc-insert "*** %s has joined channel %s ***" user channel))
  392.         (irc-signal user 'join))))) ; check for signal for join
  393.  
  394. (defun irc-parse-invite (str)
  395.   (let ((user (substring str 1 (string-match "\\s +INVITE " str)))
  396.         (to (substring str (match-end 0)
  397.                        (string-match "\\s +" str (match-end 0))))
  398.         (channel (substring str (match-end 0))))
  399.     ;; glom a new name, if necessary
  400.     (irc-maintain-list 'irc-wholist user 'add)
  401.     (if (member-general user irc-ignores 'string=)
  402.         (irc-send (concat "PRIVMSG " user " :You are being ignored."))
  403.       (irc-insert "*** %s invites %s to join channel %s ***" user
  404.                   ;; i wish the downcases weren't necessary, but the servers
  405.                   ;; are inconsistent.  anyway, this should return "you" 99%
  406.                   ;; of the time; if it doesn't something weird is happening.
  407.                   (if (string= (downcase to) (downcase irc-nick)) "you" to)
  408.                   channel)
  409.       ;; What is The Right Thing here?  Only make /join . work for invitations
  410.       ;; to you or any one that you might have seen?  We'll do the latter.
  411.       (setq irc-last-invite channel)
  412.       (irc-signal user 'invite))))
  413.  
  414. (defun irc-parse-public (str)
  415.   ;; MSG is sent when someone has sent a message to a channel.  In reality,
  416.   ;; sometimes PRIVMSG is used but that is handled in irc-parse-priv.
  417.   (let ((user (substring str 1 (string-match "\\s MSG :" str)))
  418.         (msg (substring str (match-end 0)))
  419.         (time (if (and irc-message-stamp (not (eq 'private irc-message-stamp)))
  420.                   (concat " (" (irc-get-time) ") ")
  421.                 " ")))
  422.     ;; even here we can't guarantee that the sender has already been noted
  423.     ;; someplace else like join or nick -- the sender might be someplace
  424.     ;; else and sending to this channel with PRIVMSG.
  425.     (irc-maintain-list 'irc-wholist user 'add)
  426.     (if (member-general user irc-ignores 'string=) ()
  427.       (if irc-conserve-space
  428.           (irc-insert-message (concat "<" user ">" time msg) t)
  429.         (irc-insert "\n ->%sFrom %s to %s:" time user irc-channel)
  430.         (irc-insert-message msg))
  431.       (irc-signal user 'public))))
  432.  
  433. (defun irc-parse-priv (str)
  434.   ;; PRIVMSG is intended to be used for private message sent between users.
  435.   ;; This is not always the case at the moment; servers will use it like either
  436.   ;; NOTICE or MSG on occasion.
  437.   ;; This is really gross because it kludges in the fact that PRIVMSG can
  438.   ;; be used to send notification of a change of channel topic.  Actually,
  439.   ;; topic changes are handled poorly all around by the servers because
  440.   ;; only the person who changed the topic gets notification.
  441.   ;; Also have to kludge in the fact that TIME to a remote host gives back
  442.   ;; a PRIVMSG with no sender but with a leading :.  ARGHGHGHG!!
  443.   (let (from to msg time public)
  444.     (if (string-match "^:\\S +\\s +PRIVMSG\\s +" str)
  445.         ;; there was a sender.  this is a real private message.
  446.         (setq from (substring str 1 (string-match "\\s +PRIVMSG\\s +" str))
  447.               to (substring str (match-end 0)
  448.                             (string-match "\\s +:" str (match-end 0))))
  449.       (setq from nil          ; no sender.  schade.  broken protocol.
  450.             to (substring str 9 (string-match "\\s :" str))))
  451.     (setq msg (substring str (match-end 0)))
  452.     (if (not from)
  453.         ;; not really a private message.  whatever it was just show it
  454.         ;; and don't worry about signalling it.
  455.         (progn (irc-insert msg) nil)
  456.       ;; I loathe this concept.  JTYMLTK.
  457.       (setq public (string= to irc-channel))
  458.       ;; The way it used to be:
  459.       ;; (if (string= to irc-channel)
  460.       ;;     (irc-parse-public (concat ":" from " MSG :" msg)))
  461.       ;; sigh.  this function gets called too much.
  462.       (irc-maintain-list 'irc-wholist from 'add)
  463.       ;; skip the messages if sender is being ignored
  464.       (if (member-general from irc-ignores 'string=)
  465.           (progn
  466.             ;; a meager check to avoid infinite looping.  noticed this when
  467.             ;; someone ignored himself but it could happen with 2 different
  468.             ;; people using the client.  there should be some better form
  469.             ;; of detection of looping probably but for now this seems ok
  470.             (or (string= msg "You are being ignored.")
  471.                 (irc-send (concat "PRIVMSG " from " :You are being ignored.")))
  472.             nil)  ;; no signal
  473.         (setq irc-last-private (if public irc-last-private (concat from ":"))
  474.               time (if (and irc-message-stamp
  475.                             (or (and public
  476.                                      (not (eq 'private irc-message-stamp)))
  477.                                 (and (not public)
  478.                                      (not (eq 'public irc-message-stamp)))))
  479.                        (concat " (" (irc-get-time) ") ")
  480.                      " "))
  481.           (or irc-conserve-space
  482.               (irc-insert (if public
  483.                               (concat "\n ->%sSemi-private from %s to "
  484.                                       irc-channel ":")
  485.                             "\n >>%sPrivate message from %s:") time from))
  486.           (or public
  487.               (string= (downcase to) (downcase irc-nick))
  488.               ;; this should never show up.  if it does something is broken.
  489.           ;; has been seen with heat-seeking messages; this is OK --ckd
  490.               (irc-insert " (apparently to %s)" to))
  491.           (if irc-conserve-space
  492.               (irc-insert-message (concat (if public "(" "*")
  493.                                           from (if public ")" "*") time msg) t)
  494.             (irc-insert-message msg))
  495.           (irc-signal from (if public 'public 'private))))))
  496.  
  497. (defun irc-parse-quit (str)
  498.   ;; QUIT is currently sent by servers only to those clients which are on the
  499.   ;; same channel as the departing user.
  500.   (let ((user (substring str 1 (string-match "\\s +QUIT" str))))
  501.     (irc-maintain-list 'irc-wholist user 'remove)
  502.     (if (member-general user irc-ignores 'string=) ()
  503.       (irc-insert "*** %s has left IRC ***" user)
  504.       ;; currently just the join event; some modification will need to be made
  505.       ;; here when/if Jarkko has QUIT sent to everyone, not just the channel
  506.       (irc-signal user 'join))))
  507.  
  508. (defun irc-parse-wallops (str)
  509.   ;; WALLOPS is sent by a user to all IRC operators on IRC.  A WALLOPS
  510.   ;; message will always be displayed even if the sender is being ignored.
  511.   ;; currently shares WALL's signal status -- ckd 28 May 1990
  512.   (let ((user (substring str 1 (string-match "\\s +WALLOPS\\s +:" str)))
  513.         (msg (substring str (match-end 0))))
  514.     ;; sigh.  okay class, can anyone tell me why we're calling this yet again?
  515.     (irc-maintain-list 'irc-wholist user 'add)
  516.     (if irc-conserve-space
  517.         (irc-insert-message (concat "!" user "! (" (irc-get-time) ") " msg) t)
  518.       (irc-insert "\n !! Message from %s at %s to all OPERs:"
  519.                   user (irc-get-time))
  520.       (irc-insert-message msg))
  521.     (irc-signal user 'wall)))
  522.  
  523. (defun irc-parse-wall (str)
  524.   ;; WALL is sent by IRC operators to everyone on IRC.  A WALL message will
  525.   ;; always be displayed even if the sender is being ignored.
  526.   (let ((user (substring str 1 (string-match "\\s +WALL\\s +:" str)))
  527.         (msg (substring str (match-end 0))))
  528.     ;; sigh.  okay class, can anyone tell me why we're calling this yet again?
  529.     (irc-maintain-list 'irc-wholist user 'add)
  530.     (if irc-conserve-space
  531.         (irc-insert-message (concat "#" user "# (" (irc-get-time) ") " msg) t)
  532.       (irc-insert "\n ## Message from %s at %s to everyone:"
  533.                   user (irc-get-time))
  534.       (irc-insert-message msg))
  535.     (irc-signal user 'wall)))
  536.  
  537. (defun irc-parse-nick (str)
  538.   ;; NICK is sent when a user's nickname is changed, but it is only sent to the
  539.   ;; people on the same channel as the user.  If the person changing names is
  540.   ;; being ignored, this fact is tracked across the change.
  541.   (let ((old (substring str 1 (string-match "\\s NICK " str)))
  542.         (new (substring str (match-end 0))))
  543.     (irc-maintain-list 'irc-wholist old 'remove)
  544.     (irc-maintain-list 'irc-wholist new 'add)
  545.     (if (member-general old irc-ignores 'string=)
  546.         ;; track the 
  547.         (progn (irc-maintain-list 'irc-ignores old 'remove)
  548.                (irc-maintain-list 'irc-ignores new 'add)
  549.                nil)           ; no signal for ignored user
  550.       (if (or (not (memq 'nick irc-notifies)) (string= new irc-nick)) () 
  551.         (irc-insert "*** %s is now known as %s ***" old new)
  552.         (irc-signal old 'user)))))
  553.  
  554. (defun irc-parse-error (str)
  555. ;; ERROR is used when something bogus happens like an unparsable command
  556. ;; is issued to the server.  Usually this will not happen unless something
  557. ;; like /QUOTE is used.  This message is also used when a user attempts to
  558. ;; change to a name that already exists.
  559.   (string-match "\\s +:" str)
  560.   (irc-insert (substring str (match-end 0)))
  561.   (if (string-match
  562.        "Nickname\\s +\\S *\\s +\\(is \\)?\\(already\\|not\\s +chan\\|in use\\)"
  563.        str)
  564.       (progn
  565.         ;; either we couldn't change the current nickname
  566.         (setq irc-nick (or (get 'irc-nick 'o-nick)
  567.                            ;; or we never even had one
  568.                            "NO NAME YET (/NICK to set one)"))
  569.         (set-buffer-modified-p (buffer-modified-p))
  570.         (irc-insert (if (get 'irc-nick 'o-nick)
  571.                         "Hmmm ... looks like you're still \"%s\"."
  572.                       "%s") irc-nick)))
  573.   nil)
  574.  
  575. (defun irc-parse-notice (str)
  576.   ;; NOTICE is the catch-all for IRC messages; if it can't be classified as
  577.   ;; one of the other currently existing messages then the information is
  578.   ;; sent as NOTICE.  This message is overused, even when it another could be
  579.   ;; used instead.  For example, if an attempt is made to send to a nickname
  580.   ;; which is not on IRC the error reply is sent via NOTICE.
  581. ;; Rocker 30may90 patch for away messages
  582.   (setq irc-have-old-shit nil) ; reset it for next notice
  583.   (cond ((and (string-match "away" str) (string-match "^:\\(\\S +\\).*$" str))
  584.      (setq irc-have-old-shit t))
  585.     (t (string-match "\\s +:\\(.*\\)$" str)))
  586.   (let ((msg (substring str (match-beginning 1)(match-end 1))))
  587.     (cond
  588.      (irc-have-old-shit
  589.       (if irc-see-old-shit
  590.       (irc-insert "Here's an old dumb shit server ==> %s" msg)))
  591.      ((string-match "MOTD " msg) ; MOTD parser...make it look better
  592.       (setq tmp1 (substring msg (match-end 0)(length msg)))
  593.       (cond
  594.        ((string-match "^- \\(\\S +\\) \\(Message of the Day\\)" tmp1)
  595.     (irc-insert "-")
  596.     (irc-insert "- Server: %s" 
  597.             (substring tmp1 (match-beginning 1)(match-end 1)))
  598.     (irc-insert "- %s" (substring tmp1 (match-beginning 2)(match-end 2))))
  599.        ((string-match "command." tmp1)) ; do nothing and eat line
  600.        (t (irc-insert tmp1))))
  601.      (t (irc-insert msg)))
  602.     (cond
  603.      ((string-match "^\\*\\*\\* Error: No such nickname (\\(.+\\))$" msg)
  604.       ;; oops.  we sent to someone who wasn't really there.
  605.       (irc-maintain-list 'irc-wholist
  606.                          (substring msg (match-beginning 1) (match-end 1))
  607.                          'remove))
  608.      ((string-match "^Good afternoon, gentleman\\. I am a HAL 9000" msg)
  609.       ;; we've been granted operator priviledges.  the string is for mode-line
  610.       (setq irc-operator " Operator")
  611.       (set-buffer-modified-p (buffer-modified-p)))))
  612.   nil)
  613.  
  614. (defun irc-parse-topic (str)
  615.   ;; Rocker 28may90 add a : to next line patching topic error
  616.   (let ((user (substring str 1 (string-match "\\s +TOPIC\\s +:" str)))
  617.         (topic (substring str (match-end 0))))
  618.     (irc-maintain-list 'irc-wholist user 'add)
  619.     (if (not (memq 'topic irc-notifies)) ()
  620.       (irc-insert "*** %s has changed the topic of %s to \"%s\" ***"
  621.                   user irc-channel topic)
  622.       (irc-signal user 'topic))))
  623.  
  624. (defun irc-parse-kill (str)
  625.   ;; For a client this means its connexion will be closing momentarily.  This
  626.   ;; rather drastic turn of events will always get a signal
  627.   (let ((oper (substring str 1 (string-match "\\s +KILL\\s +" str)))
  628.         (user (substring str (match-end 0)
  629.                          (string-match "\\s +:" str (match-end 0))))
  630.         (path (substring str (match-end 0))))
  631.     (irc-maintain-list 'irc-wholist oper 'add)
  632.     (irc-maintain-list 'irc-wholist user 'remove)
  633.     (if (string= (downcase user) (downcase irc-nick))
  634.         (progn
  635.           (irc-insert "*** Your session has been killed by %s via path %s ***"
  636.                       oper path)
  637.           (setq irc-wanted-exit t))
  638.       (if (not irc-use-kill-window)
  639.       (irc-insert "*** %s has killed %s via path %s ***" oper user path)
  640.     (progn
  641.       (save-excursion
  642.         (set-buffer (get-buffer "irc-kill"))
  643.         (insert "*** %s has killed %s via path %s ***" oper user path))
  644.       (setq irc-kill-window t)
  645.       (setq irc-other-buffers " K")
  646.       (if irc-wall-window 
  647.           (setq irc-other-windows (concat irc-other-windows "W")))
  648.       (if irc-ignore-window
  649.           (setq irc-other-windows (concat irc-other-ignores "I"))))))
  650.     ;; ring the bloody bell.
  651.     t))
  652.  
  653. (defun irc-parse-whoreply (str)
  654.   (string-match "^WHOREPLY\\s +" str)
  655.   (setq str (substring str (match-end 0)))
  656.   (let (split wholine oper padlen)
  657.     ;; the elements of 'split' are:
  658.     ;; 0 - full name     2 - nickname     4 - hostname      6 - channel
  659.     ;; 1 - status        3 - server       5 - login name
  660.     (while (not (string-match "^:" str))
  661.       (setq split (cons (substring str 0 (string-match "\\(\\s +\\|$\\)" str))
  662.                         split)
  663.             str (substring str (match-end 0))))
  664.     (setq split (cons str split))
  665.     (if (string= (nth 1 split) "S") ()
  666.         ;; if it isn't the bogus header, add nick
  667.         (irc-maintain-list 'irc-wholist (nth 2 split) 'add))
  668.     (setq padlen (- 61 (length (nth 5 split)) (length (nth 4 split))
  669.                     (length (car split)))
  670.           ;; this should make the word-wrap work right.  note that things start
  671.           ;; breaking again if padlen is less than -79.  also is dependent on
  672.           ;; 80 column screens to get The Right Results.
  673.           padlen (if (> padlen 0) padlen (+ 77 padlen))
  674.           wholine
  675.           (concat
  676.            ;; People marked AWAY are preceded by a '-' (though AWAY currently
  677.            ;; doesn't propogate beyond local server).  Ignored users are '#'.
  678.            ;; Operators are '*'; away operators are '='.  '#' has priority over
  679.            ;; the others because if a user is being ignored then the other
  680.            ;; information about that user's status is not really relevant.
  681.            (if (member-general (nth 2 split) irc-ignores 'string=) "#"
  682.              (cond ((string= "H"  (nth 1 split)) " ")
  683.                    ((string= "H*" (nth 1 split)) "*")
  684.                    ((string= "G"  (nth 1 split)) "-")
  685.                    ((string= "G*" (nth 1 split)) "=")
  686.                    ((string= "H@" (nth 1 split)) "@")
  687.                    ((string= "G@" (nth 1 split)) "%%")
  688.                    ((string= "H*@" (nth 1 split)) "!")
  689.                    ((string= "G*@" (nth 1 split)) "?")
  690.                    ((string= "S"  (nth 1 split)) " ")
  691.                    (t (nth 1 split)))) ; no clue what it is; just use it
  692.            (nth 2 split)
  693.            ;; pad some spaces in
  694.            (make-string (- 10 (length (nth 2 split))) 32)
  695.            (format "%4s " (if (string-match "^+" (nth 6 split))
  696.                   (nth 6 split)
  697.                 (if (zerop (string-to-int (nth 6 split)))
  698.                 ;; bogus header; translate * to "Chan"
  699.                 ;; selecy chan status, 2.5 * = priv
  700.                 (if (string= "S" (nth 1 split)) "Chan" "")
  701.                   (nth 6 split))))
  702.        (substring (car split) 1)
  703.            (make-string padlen 32)
  704.            "<" (nth 5 split) "@" (nth 4 split) ">")
  705.           oper (string-match "\\*\\|^S$" (nth 1 split)))
  706.     (if irc-operwho
  707.         (if oper (irc-insert wholine))
  708.     (irc-insert wholine)))
  709.   nil)
  710.  
  711. (defun irc-parse-linreply (str)
  712.   ;; LINREPLY is used to answer a LINKS request to show the servers on-line.
  713.   ;; "Links" is a bit of a misnomer since little information regarding the
  714.   ;; actual structure of the IRCnet can be gained from these messages.
  715.   (string-match "^LINREPLY\\s +\\(\\S +\\)\\s +" str)
  716.   (irc-insert "Server: %s (%s)"
  717.               (substring str (match-beginning 1) (match-end 1))
  718.               (substring str (match-end 0)))
  719.   nil)
  720.  
  721. (defun irc-parse-namreply (str)
  722.   (string-match "^NAMREPLY\\s +\\S +\\s +\\(\\S +\\)\\s +" str)
  723.   (let* ((channel (substring str (match-beginning 1) (match-end 1)))
  724.          (users (substring str (match-end 0)))
  725.          (to-insert (format "%7s "
  726.                             (if (string= "*" channel) "Private" channel)))
  727.          nick)
  728.     ;; yet another source of information for irc-wholist.
  729.     (while (string-match "^\\(\\S +\\)\\(\\s \\|$\\)" users)
  730.       (setq nick (substring users 0 (match-end 1))
  731.             users (substring users (match-end 0)))
  732.       (irc-maintain-list 'irc-wholist nick 'add)
  733.       ;; parsing by name also means we can format a long line nicely
  734.       ;; question: why do programmers so frequently use "we" in comments?
  735.       (if (<= (+ (length to-insert) (length nick)) (- (window-width) 2))
  736.           (setq to-insert (concat to-insert " " nick))
  737.         (irc-insert to-insert)
  738.         (setq to-insert (format "         %s" nick))))
  739.     (irc-insert to-insert))
  740.   nil)
  741.  
  742. ;; Rocker 17july90  v2.5 addition
  743. (defun irc-parse-mode (str)
  744.   (string-match "^:\\(\\S +\\) MODE \\(\\S +\\) \\(\\S +\\)\\(.*\\)$" str)
  745.   (setq tmp1 (substring str (match-beginning 1)(match-end 1))  ; ChanOp
  746.     tmp2 (substring str (match-beginning 2)(match-end 2))  ; Channel
  747.     tmp3 (substring str (match-beginning 3)(match-end 3))  ; new mode
  748.     tmp4 (substring str (match-beginning 4)(match-end 4))) ; used for oping
  749.   (cond ((string-match "o" tmp3)
  750.      (setq tmp3
  751.            (concat (substring tmp3 0 (match-beginning 0))
  752.                (substring tmp3 (+ (match-beginning 0) 1)
  753.                        (length tmp3))))
  754.      (setq tmp4 (substring tmp4 1 (- (length tmp4) 1)))
  755.      (if (string= tmp1 tmp4)
  756.          (setq tmp4 "themself"))
  757.      (if (string-match "-" tmp3)
  758.          (irc-insert "%s has taken Channel Operator status from %s."
  759.              tmp1 tmp4)
  760.        (irc-insert "%s has given Channel Operator status to %s."
  761.                tmp1 tmp4))))
  762.   (if (>= (length tmp3) 2)
  763.       (irc-insert "%s has changed %s's mode to %s." tmp1 tmp2 tmp3)))
  764.  
  765. (defun irc-parse-ERR (str)
  766.   ;; Numeric control messages are used by newer servers to aid in generalized
  767.   ;; client design; while people are converting to the new servers the older
  768.   ;; irc-parse-error, irc-parse-notice, et al, functions are redundant with
  769.   ;; irc-parse-ERR and irc-parse-RPL.  Values used by this function are found
  770.   ;; in the IRC source file numeric.h.
  771.   ;; Note well that some things are still going to come out wrong because the
  772.   ;; servers are currently still doing things inconsistently.
  773.   (if (string-match "403" str) ; if 403 use alternate string-match
  774.       (string-match "^\\S +\\s +\\(4[0-9][0-9]\\) \\(\\S *\\)" str)
  775.     (string-match "^\\S +\\s +\\(4[0-9][0-9]\\)\\s \\S *\\s +\\(.*\\)$" str))
  776.   ;; we assume that the server and message are consistent for us; just
  777.   ;; worry about the numeric value and the rest of the line
  778.   (let ((num (string-to-int (substring str (match-beginning 1) (match-end 1))))
  779.         (txt                (substring str (match-beginning 2) (match-end 2)))
  780.         tmp1)
  781.     (cond
  782.      ((= num 401)                       ; ERR_NOSUCHNICK
  783.       (string-match "^\\S *" txt)
  784.       (setq tmp1 (substring txt (match-beginning 0) (match-end 0)))
  785.       ;; silly, brain-damaged broken protocol use.
  786.       (if (or (string= ":Hunting" tmp1) (string= "" tmp1)) ()
  787.         (irc-maintain-list 'irc-wholist tmp1 'remove)
  788.         (irc-insert "'%s' is not on IRC." tmp1)))
  789.      ((= num 402)                       ; ERR_NOSUCHSERVER
  790.       (string-match "^.*\\s :" txt)
  791.       (irc-insert "'%s' is not a server on the IRCnet now."
  792.                   (substring txt (match-beginning 0) (- (match-end 0) 2))))
  793.      ((= num 403)                       ; ERR_NOSUCHCHANNEL
  794.       (string-match "^\\S +" txt)
  795.       (irc-insert "Channel %s is not in use."
  796.                   (substring txt (match-beginning 0) (match-end 0))))
  797.      ((= num 404)                       ; ERR_CANNOTSENDTOCHAN
  798.       (irc-insert "Cannot send to that channel"))
  799.      ((= num 411)                       ; ERR_NORECIPIENT
  800.       (irc-insert "The last message had no recipient."))
  801.      ((= num 412)                       ; ERR_NOTEXTTOSEND
  802.       (irc-insert "The last message had no text to send."))
  803.      ((= num 421)                       ; ERR_UNKNOWNCOMMAND
  804.       (setq irc-have-old-shit nil) ; reset it for next notice
  805.       (if (string-match "MODE" txt)
  806.       (setq irc-have-old-shit t))
  807.       (cond
  808.        (irc-have-old-shit
  809.     (string-match ":\\(\\S +\\)" str)
  810.     (if irc-see-old-shit
  811.         (irc-insert "Ye Old Shitte Server >%s" 
  812.             (substring str (match-beginning 1)(match-end 1)))))
  813.        (t
  814.     (string-match "^\\(.*\\) Unknown :command$" txt) ; rocker new ":"
  815.     (irc-insert "Unknown server command: '%s'."
  816.             (substring txt (match-beginning 1) (match-end 1))))))
  817.      ((= num 431)                       ; ERR_NONICKNAMEGIVEN
  818.       (irc-insert "No nickname give to change to."))
  819.      ((= num 432)                       ; ERR_ERRONEUSNICKNAME
  820.       (irc-insert "Bad format for nickname change."))
  821.      ((= num 433)                       ; ERR_NICKNAMEINUSE
  822.       (string-match "^\\S + 433 \\(\\S *\\) \\(\\S +\\) " str)
  823.       (setq tmp1  (substring str (match-beginning 2) (match-end 2)))
  824.       (irc-maintain-list 'irc-wholist tmp1 'add)
  825.       (irc-insert "Nickname '%s' is already being used; please choose another."
  826.                   tmp1)
  827.       ;; either we couldn't change the current nickname
  828.       (setq irc-nick (if (/= (match-beginning 1) (match-end 1))
  829.                          (get 'irc-nick 'o-nick)
  830.                        ;; or we never even had one
  831.                        "NO NAME YET (/NICK to set one)"))
  832.       (set-buffer-modified-p (buffer-modified-p))
  833.       (irc-insert (if (= (match-beginning 1) (match-end 1)) "%s"
  834.                     "Hmmm ... looks like you're still \"%s\".") irc-nick))
  835.      ((= num 441)                       ; ERR_USERNOTINCHANNEL
  836.       (irc-insert "You're not on any channel."))
  837.      ((= num 442)
  838.       (string-match "^\\(\\S +\\) :" txt)
  839.       (irc-insert "'%s' is not on this channel."
  840.           (substring txt (match-beginning 1) (match-end 1))))
  841.      ((= num 451)                       ; ERR_NOTREGISTERED
  842.       (irc-insert "You haven't checked in yet.  Choose a nickname."))
  843.      ((= num 461)                       ; ERR_NEEDMOREPARAMS
  844.       (irc-insert "There weren't enough arguments for the last command."))
  845.      ((= num 462)                       ; ERR_ALREADYREGISTRED
  846.       (irc-insert "You've already registered."))
  847.      ((= num 463)                       ; ERR_NOPERMFORHOST
  848.       (irc-insert "Your host isn't permitted."))
  849.      ((= num 464)                       ; ERR_PASSWDMISMATCH
  850.       (irc-insert "That password is incorrect."))
  851.      ((= num 465)                       ; ERR_YOUREBANNEDCREEP
  852.       (irc-insert "You've been banned from IRC."))
  853.      ((= num 471)                       ; ERR_CHANNELISFULL
  854.       (string-match "^\\S +" txt)
  855.       (irc-insert "Channel %s is full."
  856.                   (substring txt (match-beginning 0) (match-end 0))))
  857.      ((= num 473)                       ; ERR_INVITEONLYCHANNEL
  858.       (string-match "^\\S +" txt)
  859.       (irc-insert "Sorry, %s is invite only."
  860.           (substring txt (match-beginning 0)(match-end 0))))
  861.      ((= num 481)                       ; ERR_NOPRIVILEGES
  862.       (if (or (string-match "channel OPER" txt) ; Dylan 22July90
  863.           (string-match "channel operator" txt)) ; Rocker 15July90
  864.       (irc-insert "You must be a Channel Operator to do that.")
  865.     (irc-insert "You must be an IRC Operator to do that.")))
  866.      ((= num 491)                       ; ERR_NOOPERHOST
  867.       (irc-insert "You can't be that IRC Operator from your host."))
  868.      (t                                 ; default
  869.       (irc-insert "Unrecognized ERR message follows; please tell rocker@bucsf.bu.edu:")
  870.       (irc-insert str))))
  871.   nil) ; no need for a bell, I suppose.
  872.  
  873. (defun irc-parse-RPL (str)
  874.   (string-match "^\\S +\\s +\\(3[0-9][0-9]\\)\\s \\S *\\s +\\(.*\\)$" str)
  875.   ;; we assume that the server and message are consistent for us; just
  876.   ;; worry about the numeric value and the rest of the line
  877.   (let ((num (string-to-int (substring str (match-beginning 1) (match-end 1))))
  878.         (txt                (substring str (match-beginning 2) (match-end 2)))
  879.         tmp1 tmp2 tmp3 tmp4)
  880.     (cond
  881.      ((= num 301)                       ; RPL_AWAY
  882.       (if (not (string-match "^\\(\\S +\\) :\\(.*\\)$" txt))
  883.           ;; Yo ho ho.  I hate this bug.
  884.           (irc-insert "Someone is away, \"%s\"." txt)
  885.         (setq tmp1  (substring txt (match-beginning 1) (match-end 1)))
  886.         (irc-insert "%s is away, \"%s\"." tmp1
  887.                     (substring txt (match-beginning 2) (match-end 2)))
  888.         (irc-maintain-list 'irc-wholist tmp1 'add)))
  889.      ((= num 311)                       ; RPL_WHOISUSER
  890.       (string-match
  891.        "^\\(\\S +\\) \\(\\S +\\) \\(\\S +\\) \\(\\S +\\) :\\(.*\\)$" txt)
  892.       (setq tmp1  (substring txt (match-beginning 1) (match-end 1))
  893.             tmp2  (substring txt (match-beginning 2) (match-end 2))
  894.             tmp3  (substring txt (match-beginning 3) (match-end 3)))
  895.       (irc-insert "%s is %s <%s@%s> on %s." tmp1
  896.                   (substring txt (match-beginning 5) (match-end 5))
  897.                   tmp2 tmp3
  898.                   (if (string= (setq tmp1 (substring txt (match-beginning 4)
  899.                                                      (match-end 4))) "*")
  900.                       "a private channel"
  901.                     (concat "channel " tmp1)))
  902.       (irc-maintain-list 'irc-wholist tmp1 'add)
  903.       (if irc-doing-finger (irc-finger (concat tmp2 "@" tmp3))))
  904.      ;; if fingering, then an extra blank line shows up if user is operator
  905.      ((= num 312)                       ; RPL_WHOISSERVER
  906.       (string-match "^\\(\\S +\\) :?\\(.*\\)$" txt)
  907.       (setq tmp1 (substring txt (match-beginning 1) (match-end 1))
  908.             tmp2 (substring txt (match-beginning 2) (match-end 2)))
  909.       (or (and (string= tmp1 "*") (string= tmp2 "*"))
  910.           (irc-insert "On server %s (%s)." tmp1 tmp2))
  911.       (if (and irc-doing-finger (not irc-conserve-space)) (irc-notice "")))
  912.      ((= num 313)                       ; RPL_WHOISOPERATOR
  913.       (string-match "^\\S +" txt)
  914.       (setq tmp1  (substring txt (match-beginning 0) (match-end 0)))
  915.       (irc-maintain-list 'irc-wholist tmp1 'add)
  916.       (irc-insert "%s is an IRC Operator." tmp1)
  917.       (if (and irc-doing-finger (not irc-conserve-space)) (irc-notice "")))
  918.    ;; Rocker patches /WHOWAS reply 314 24may90
  919.      ((= num 314)                       ; RPL_WHOWAS
  920.       (string-match
  921.        "^\\(\\S +\\) \\(\\S +\\) \\(\\S +\\) \\S + :\\(.*\\)$" txt)
  922.       (setq tmp1  (substring txt (match-beginning 1) (match-end 1))
  923.             tmp2  (substring txt (match-beginning 2) (match-end 2))
  924.             tmp3  (substring txt (match-beginning 3) (match-end 3)))
  925.       (irc-insert "%s was %s <%s@%s>" tmp1
  926.                   (substring txt (match-beginning 4) (match-end 4))
  927.                   tmp2 tmp3)
  928.       (irc-maintain-list 'irc-wholist tmp1 'add)
  929.       (if irc-doing-finger (irc-finger (concat tmp2 "@" tmp3))))
  930.      ;; if fingering, then an extra blank line shows up if user is operator
  931.    ;; Rocker patches annoying message 315 22may90
  932.      ((= num 315))                      ; RPL_WHO_PATCH
  933.    ;; Rocker patch Server 2.5 message 316 8july90
  934.      ((= num 316)                       ; RPL_WHOISCHANOP
  935.       (string-match "^\\(\\S +\\) :?\\(.*\\)$" txt)
  936.       (setq tmp1 (substring txt (match-beginning 1) (match-end 1)))
  937.       (irc-insert "%s is a Channel Operator." tmp1 ))
  938.      ((= num 321)                       ; RPL_LISTSTART
  939.       (irc-insert "Channel  Users Topic"))
  940.      ((= num 322)                       ; RPL_LIST
  941.       (string-match "^\\(\\S +\\) \\(\\S +\\) :\\(.*\\)$" txt)
  942.       (setq tmp1 (substring txt (match-beginning 1) (match-end 1)))
  943.       (irc-insert "%7s   %2s   %s"
  944.                   (if (string= tmp1 "*") "Private" tmp1)
  945.                   (substring txt (match-beginning 2) (match-end 2))
  946.                   (substring txt (match-beginning 3) (match-end 3))))
  947.      ((= num 323)                       ; RPL_LISTEND
  948.       (or irc-conserve-space (irc-insert "\n")))
  949.      ;; Rocker 15july90 for 2.5 server
  950. ;; Finish this FUCKIN SHIT
  951.      ((= num 324)                       ; RPL_CHANNELMODEIS
  952.       (setq options nil)
  953.       (cond ((string-match "^\+$" txt)
  954.          (irc-insert "No channel mode is selected."))
  955.         ((or (string-match "n" txt)(string-match "t" txt))
  956.          (irc-insert "The channel mode is %s" txt))
  957.         (t 
  958.          (and (string-match "i" txt)
  959.           (setq options (concat " invite only" options)))
  960.          (and (string-match "m" txt)
  961.           (setq options (concat " moderated" options)))
  962.          (and (string-match "s" txt)
  963.           (setq options (concat " secret" options)))
  964.          (and (string-match "p" txt)
  965.           (setq options (concat " private" options)))
  966.          (irc-insert "The channel mode is%s" options))))
  967.      ((= num 331)                       ; RPL_NOTOPIC
  968.       (irc-insert "No topic is set."))
  969.     ;; Rocker 28may90
  970.      ((= num 332)                       ; RPL_TOPIC
  971.       (string-match "^:\\(.*\\)$" txt)
  972.       (irc-insert "The topic is %s."
  973.                   (substring txt (match-beginning 1))))
  974.      ((= num 341)                       ; RPL_INVITING
  975.       (string-match "^:\\(\\S +\\) 341 \\S + \\(\\S +\\) \\(\\S +\\)" str)
  976.       (setq tmp1  (substring str (match-beginning 2) (match-end 2)))
  977.       (irc-insert "Server %s inviting %s to channel %s"
  978.                   (substring str (match-beginning 1) (match-end 1))
  979.                   tmp1
  980.                   (substring str (match-beginning 3) (match-end 3)))
  981.       (irc-maintain-list 'irc-wholist tmp1 'add))
  982.      ((= num 351)                       ; RPL_VERSION
  983.       (string-match "^\\(\\S +\\) \\(\\S +\\)" txt)
  984.       (setq tmp1 (substring txt (match-beginning 2) (match-end 2))
  985.         tmp2 (substring txt (match-beginning 1) (match-end 1)))
  986.       ;; Rocker 19july90 killing an annoying :
  987.       (if (string-match ":" tmp1)
  988.       (setq tmp1 (substring tmp1 (match-end 0))))
  989.       (irc-insert "%s is running IRC daemon version %s" tmp1 tmp2))
  990.      ((= num 361)                       ; RPL_KILLDONE
  991.       (string-match "^\\S +" txt)
  992.       (setq tmp1  (substring txt (match-beginning 0) (match-end 0)))
  993.       (irc-maintain-list 'irc-wholist tmp1 'remove)
  994.       (irc-insert "%s has been removed from IRC." tmp1))
  995.    ;; Rocker patches annoying message 365 22may90
  996.      ((= num 365))                      ; RPL_LINKS_PATCH
  997.    ;; Rocker patches annoying message 366 22may90
  998.      ((= num 366))                      ; RPL_NAME_PATCH
  999.      ((= num 371)                       ; RPL_INFO
  1000.       (irc-insert (substring txt 1)))
  1001.      ((= num 372)                       ; RPL_MOTD
  1002.       (if (string= "" txt)
  1003.           (if irc-motd-bug ()
  1004.               (irc-notice "(Sorry; a server bug prevents you from seeing this message of the day.)")
  1005.               (setq irc-motd-bug t))
  1006.         (setq irc-motd-bug nil)
  1007.         (irc-insert (substring txt 1))))
  1008.      ((= num 381)                       ; RPL_YOUREOPER
  1009.       (setq irc-operator " Operator")
  1010.       (set-buffer-modified-p (buffer-modified-p))
  1011.       (irc-insert "Welcome to the Twilight Zone."))
  1012.      ((= num 382)                       ; RPL_REHASHING
  1013.       (irc-insert "Rereading local ircd configuration information."))
  1014.      ((= num 391)                       ; RPL_TIME
  1015.       ;; this is mucked because of bogus time replies
  1016.       (if (string-match "^\\(\\S +\\) :\\(.*\\)$" txt)
  1017.           (irc-insert "It is %s at %s."
  1018.                       (substring txt (match-beginning 2) (match-end 2))
  1019.                       (substring txt (match-beginning 1) (match-end 1)))
  1020.         ;; ahahahahah.
  1021.         (irc-notice "Is is %s somewhere." txt)))
  1022.      (t                                 ; default
  1023.       (irc-insert "Unrecognized RPL message follows; please tell rocker@bucsf.bu.edu:")
  1024.       (irc-insert str))))
  1025.   nil) ; no bell rung
  1026.  
  1027. (defun irc-pong ()
  1028.   ;; it's interactive so it can be bound during testing.
  1029.   (interactive) (irc-send (concat "PONG " (system-name))) nil)
  1030.  
  1031. ;;; insertion routines
  1032. ;;; this is what handles the general insertion of text to the IRC buffer
  1033. (defun irc-notice (format &rest args)
  1034.   ;; Just like irc-insert, but calls irc-display when done.  This should be
  1035.   ;; used by all of the functions which insert text without being called in
  1036.   ;; response to server messages.
  1037.   (apply 'irc-insert (cons format args))
  1038.   (irc-display (current-buffer)))
  1039.  
  1040. (defun irc-insert-message (msg &optional pure-first)
  1041.   ;; Format MSG by word-wrapping into 5 characters less than the window-width
  1042.   ;; or less. If a word is too long to be split this way then it is broken at
  1043.   ;; the last character which would fit on the line and continued on the next
  1044.   ;; line as if white space had been there.  Each line is prefixed with the
  1045.   ;; string " - " and passed to irc-insert for the actual insertion into the
  1046.   ;; buffer.  Optional PURE-FIRST means to not prefix the first line inserted.
  1047.   ;; pure-first is used when irc-conserve-space is non-nil
  1048.   (let (line (lines 0))
  1049.     (while (> (length msg) (- (window-width) 5))
  1050.       (setq lines (1+ lines)
  1051.             line (substring msg 0 (- (window-width) 4))
  1052.             msg (substring msg (- (window-width) 4))
  1053.             line (irc-fix-wordwrap line msg)
  1054.             msg (cdr line)
  1055.             line (car line))
  1056.       (irc-insert (concat (if (not (and pure-first (= lines 1))) " - ") line)))
  1057.     (irc-insert (concat (if (not (and pure-first (zerop lines))) " - ") msg))))
  1058.  
  1059. (defun irc-insert (format &rest args)
  1060.   ;; Insert before irc-mark the string created by FORMAT with substituted ARGS.
  1061.   ;; Word-wrapping is done to make lines of length less than or equal to one
  1062.   ;; character less than the window-width.  If a word is too long to be wrapped
  1063.   ;; it is cut at the last column on the line as though white space were there.
  1064.   (let ((str (apply 'format format args)) (prefix "   ")
  1065.         fix line irc-win cur-win)
  1066.     (save-excursion
  1067.       (irc-truncate-buffer irc-maximum-size) ; trim buffer if needed
  1068.       (goto-char irc-mark)
  1069.       (while (> (length str) (1- (window-width)))
  1070.         (setq line (substring str 0 (1- (window-width)))
  1071.               str  (substring str (1- (window-width)))
  1072.               fix  (irc-fix-wordwrap line str)
  1073.               str  (concat prefix (if (string= "" (car fix)) str (cdr fix)))
  1074.               line (if (string= "" (car fix)) line (car fix)))
  1075.         (insert-before-markers (concat line "\n")))
  1076.       (insert-before-markers (concat str "\n")))))
  1077.  
  1078. (defun irc-fix-wordwrap (line1 line2)
  1079.   ;; With arguments LINE1 and LINE2 apply some simple heuristics to see if the
  1080.   ;; line which they originally formed was broken in an acceptable place.
  1081.   ;; Returns a dotted pair with LINE1 as the car and LINE2 as the cdr.
  1082.   (cond ((string-match "^\\s +" line2)
  1083.          ;; broke at whitespace; strip leading space from next line
  1084.          (setq line2 (substring line2 1)))
  1085.         ((string-match "\\s +$" line1)
  1086.          ;; trailing whitespace on line.  might as well just nuke it all.
  1087.          (setq line1 (substring line1 0 (match-beginning 0))))
  1088.         ((string-match "\\(\\s +\\)\\S +$" line1)
  1089.          ;; broke in a word, but it's wrappable.  just eat one space.
  1090.          (setq line2 (concat (substring line1 (1+ (match-beginning 1))) line2)
  1091.                line1 (substring line1 0 (match-beginning 0)))))
  1092.   (cons line1 line2))
  1093.  
  1094. ;; simple key functions -- self-insert, tab, destructive backspace
  1095. (defun irc-self-insert (arg)
  1096.   "Normally just inserts the typed character in the input region.
  1097. If point is in the output region, irc-spacebar-pages is non-nil and a space
  1098. is typed, scroll-up (aka window-forward) otherwise point moves to end of input
  1099. region and inserts the character.
  1100.  
  1101. If the character to be inserted is a colon or semi-colon and it is the first
  1102. non-white space character on the line then the input region is updated to
  1103. begin with the last explicit sendlist, irc-last-explicit.
  1104.  
  1105. Inserts the character ARG times if self-inserting.  An argument is not
  1106. passed to scroll-up if paging with the spacebar."
  1107.   (interactive "p")
  1108.   (let* ((in-region (>= (point) irc-mark))
  1109.          ;; it's times like this that i wish someone would tell me what
  1110.          ;; a good indentation style is for this expression
  1111.          (expand-colon
  1112.           (and
  1113.            (or (= last-input-char ?:) (= last-input-char ?\;))
  1114.            (string-match
  1115.             "^\\s *$"
  1116.             (buffer-substring irc-mark (if in-region (point) (point-max)))))))
  1117.     (if (not expand-colon)
  1118.         (if in-region (self-insert-command arg)
  1119.           (if (and irc-spacebar-pages (= last-input-char 32))
  1120.               ;; it's nice to be able to return to the input region just by
  1121.               ;; pounding on the spacebar repeatedly.
  1122.               (condition-case EOB (scroll-up nil)
  1123.                 (end-of-buffer (goto-char (point-max))))
  1124.             (goto-char (point-max))
  1125.             (self-insert-command arg)))
  1126.       (or in-region (goto-char (point-max)))
  1127.       ;; kill white space.  This also takes out previous lines in input region.
  1128.       (delete-region irc-mark (point))
  1129.       (insert (if (= last-input-char ?:) irc-last-private irc-last-explicit))
  1130.       ;; put in the extra characters if need be.
  1131.       (if (> arg 1) (self-insert-command (1- arg))))))
  1132.  
  1133. (defun irc-del-backward-char (arg)
  1134.   "If in the input region, delete ARG characters before point, restricting
  1135. deletion to the input region.  If in the output region and irc-spacebar-pages
  1136. then scroll-down (aka window-back) otherwise do nothing."
  1137.   (interactive "p")
  1138.   (if (> (point) irc-mark)
  1139.       ;; only delete as far back as irc-mark at most
  1140.       (if (> arg (- (point) irc-mark)) (delete-region (point) irc-mark)
  1141.         (delete-backward-char arg))
  1142.     (if (and (< (point) irc-mark) irc-spacebar-pages) (scroll-down nil)
  1143.       (ding))))
  1144.  
  1145. (defun irc-tab ()
  1146.   "If point is in the input region then tab-to-tab-stop.  If it is in the
  1147. output region, go to the previous line if irc-spacebar-pages; do nothing
  1148. otherwise."
  1149.   (interactive)
  1150.   (if (>= (point) irc-mark) (tab-to-tab-stop)
  1151.     (if irc-spacebar-pages (scroll-down 1)
  1152.       (ding))))
  1153.  
  1154. ;; top-level -- entry, sentinel and mode
  1155. (defun irc (new-buffer)
  1156.   "Enter the Internet Relay Chat conferencing system.
  1157. If no connexion to an irc-server is open, then one is started.  If no buffer
  1158. *IRC* exists then it is created otherwise the existing buffer is used.  If
  1159. a connexion is already active then the most recently started IRC session
  1160. is switched to in the current window.  This makes binding 'irc' to a key
  1161. much more convenient.
  1162.  
  1163. With prefix argument NEW-BUFFER, another *IRC* buffer is created and a
  1164. new IRC session is started.  This is provided so that multiple IRC
  1165. sessions can co-exist in one Emacs, which is sometimes a useful thing."
  1166.   (interactive "P")
  1167.   ;; the doc string lies a little.  if NEW-BUFFER is a buffer it is used.
  1168.   (let ((buffer (if new-buffer (if (bufferp new-buffer) new-buffer
  1169.                                  (generate-new-buffer "*IRC*"))
  1170.                   (get-buffer-create "*IRC*")))
  1171.         (nope (format "Sorry ... couldn't connect to %s at %s.\n\n"
  1172.                       irc-server irc-port))
  1173.         proc)
  1174.     (if (eq (selected-window) (minibuffer-window))
  1175.         (select-window (next-window)))
  1176.     (if (and (not new-buffer) irc-processes)
  1177.         ;; just head for the most recent session
  1178.         (switch-to-buffer (process-buffer (car irc-processes)))
  1179.       (switch-to-buffer buffer)
  1180.       (condition-case NOT-IRCED 
  1181.           (progn
  1182.             (goto-char (point-max))
  1183.             (insert
  1184.              (format
  1185.               ;; I really don't want to have to say copyright here, but it
  1186.               ;; seems I must.
  1187.               "%s for GNU Emacs.  Copyright (C) 1990 David C Lawrence.\n\n"
  1188.               irc-version))
  1189.             (irc-mode)
  1190.             (setq proc (open-network-stream "irc" buffer irc-server irc-port))
  1191.             (set-process-filter proc 'irc-filter)
  1192.             (set-process-sentinel proc 'irc-sentinel)
  1193.             (irc-send (format "USER %s %s %s %s"
  1194.                               (user-login-name) (system-name) irc-server
  1195.                               (or (getenv "IRCNAME") (getenv "NAME")
  1196.                                   (user-full-name))))
  1197.             (irc-send (concat "NICK " irc-nick))
  1198.             (or irc-no-motd (irc-send "MOTD"))
  1199.             ;; a new process, so initialize the variables.  they aren't set
  1200.             ;; in irc-mode so that irc-mode can be called at any time.
  1201.             (setq irc-away     nil    irc-channel "0"   irc-message-index -1
  1202.                   irc-operator nil    irc-scratch ""    irc-command-index -1
  1203.                   irc-finger-scratch ""                 irc-wanted-exit  nil
  1204.                   irc-last-explicit  "*;"               irc-last-private "*:"
  1205.                   irc-last-time      (irc-get-time)     irc-last-invite  "0"
  1206.                   irc-processes (cons proc irc-processes))
  1207.             (irc-normalise-time-stamp))
  1208.         (error (irc-notice nope))
  1209.         (quit  (irc-notice nope))))))
  1210.  
  1211. (defun irc-mode ()
  1212.   "To understand some documentation given with irc-mode variables and
  1213. functions, \"output region\" is defined as everything before the irc-mark.
  1214. irc-mark is a marker kept by irc-mode to know where to insert new text
  1215. >from IRC.  Text in the output region cannot be modified by the most common
  1216. methods of typing a self-inserting character or pressing delete.
  1217.  
  1218. The input region is everything which follows irc-mark.  It is what
  1219. gets processed by irc-mode when you type LFD or RET.  If irc-spacebar-pages
  1220. is non-nil, the following keys are in effect when the cursor is in the
  1221. output region:
  1222.  
  1223. SPC             scroll-forward       DEL     scroll-backward
  1224. LFD or RET      next-line            TAB     previous-line
  1225.  
  1226. Local keys:
  1227. \\{irc-mode-map}"
  1228.   (interactive)
  1229.   (kill-all-local-variables)
  1230.   (setq major-mode 'irc-mode mode-name "IRC" fill-column (- (window-width) 5))
  1231.   (make-local-variable 'irc-server)        ; for accurate restart
  1232.   (make-local-variable 'irc-port)          ; ditto
  1233.   (make-local-variable 'irc-away)          ; for the mode-line 
  1234.   (make-local-variable 'irc-channel)       ; for sendlists and broken PRIVMSGs
  1235.   (make-local-variable 'irc-wholist)       ; for sendlists
  1236.   (make-local-variable 'irc-operator)      ; for special priviledges
  1237.   (make-local-variable 'irc-message-index) ; for the message history
  1238.   (make-local-variable 'irc-command-index) ; for the command history
  1239.   (make-local-variable 'irc-scratch)       ; for accumulating server messages
  1240.   (make-local-variable 'irc-finger-scratch); for accumulating finger output
  1241.   (make-local-variable 'irc-operwho)       ; for the /operwho command
  1242.   (make-local-variable 'irc-last-explicit) ; for sendlist ; auto-expansion
  1243.   (make-local-variable 'irc-last-private)  ; for sendlist : auto-expansion
  1244.   (make-local-variable 'irc-last-invite)   ; for /join .
  1245.   (make-local-variable 'irc-last-stamp)    ; for time-sentinel
  1246.   (make-local-variable 'irc-last-time)     ; ditto
  1247.   (make-local-variable 'irc-total-time)    ; here too
  1248.   (make-local-variable 'irc-wanted-exit)   ; for a ding with abnormal exit
  1249.   (make-local-variable 'irc-doing-finger)  ; to grab user@host from /whois
  1250.   (make-local-variable 'irc-motd-bug)      ; to insert only one line of "sorry"
  1251.   (make-local-variable 'irc-raw-mode)      ; for debugging
  1252.   ;; make sure irc-last-time is initialised or else it causes errors from
  1253.   ;; irc-check-time if the connexion was not made but enter is pressed
  1254.   (or irc-last-time (setq irc-last-time (irc-get-time)))
  1255.   ;; too many ways to get unbalanced parens (most notably ":-)")
  1256.   (set (make-local-variable 'blink-matching-paren) nil)
  1257.   ;; closest we can come to "natural" terminal scrolling
  1258.   (set (make-local-variable 'scroll-step) 1)
  1259.   (set (make-local-variable 'mode-line-format)
  1260.        (list 
  1261.     (purecopy 
  1262.      (concat "-%" (int-to-string (+ 4 (length global-mode-string)))
  1263.          "b")) 
  1264.     'global-mode-string
  1265.     (purecopy " %[(") 'mode-name 'irc-operator 
  1266.     'irc-raw-mode 'irc-other-buffers ;Dylan 7/24/90
  1267.     (purecopy ")%]--") 'irc-nick (purecopy "- ")
  1268.     'irc-channel 'irc-away (purecopy "-%-")))
  1269.   (set-marker (set (make-local-variable 'irc-mark) (make-marker)) (point-max))
  1270.   (buffer-enable-undo)
  1271.   (irc-wrap-display-time)
  1272.   (turn-on-auto-fill)
  1273.   ;; "invisible subwindows" or whatever you would like to call them would be
  1274.   ;; nice.  That way I could make the output-region read-only.  The two things
  1275.   ;; most likely to screw up the buffer are backward-kill-word and kill-region
  1276.   (use-local-map irc-mode-map)
  1277.   (run-hooks 'irc-mode-hook))
  1278.  
  1279. (defun irc-sentinel (proc stat)
  1280.   ;; ignore anything but finished; i don't know what to do with the others
  1281.   (cond ((string= stat "finished\n")
  1282.          (save-excursion
  1283.            (setq irc-processes (delq proc irc-processes))
  1284.            (set-buffer (process-buffer proc))
  1285.            (save-excursion
  1286.              (irc-notice "\nIRC session finished at %s.\n" (irc-get-time)))
  1287.            (if irc-wanted-exit ()
  1288.              (or irc-silent-bell (ding 'no-terminate))
  1289.              (message "IRC session finished.")
  1290.              (if irc-no-auto-restart (sleep-for 1) ; emphasize the change
  1291.                ;; Save some information from this buffer so things can be
  1292.                ;; restored fairly cleanly after irc() runs.
  1293.                (let ((channel irc-channel)     (away irc-away)
  1294.                      (im  irc-message-history) (ic  irc-command-history)
  1295.                      (ilp irc-last-private)    (ili irc-last-invite))
  1296.                  (irc (current-buffer))
  1297.                  (or (string= "0" channel)
  1298.                      (irc-send (concat "CHANNEL " channel)))
  1299.                  (if away (irc-send (concat "AWAY " (substring away 2 -1))))
  1300.                  (setq irc-channel channel     irc-away            away
  1301.                        irc-message-history im  irc-command-history ic
  1302.                        irc-last-private    ilp irc-last-invite     ili)))))
  1303.          (if (eq (current-buffer) (process-buffer proc))
  1304.              (goto-char (point-max))))))
  1305.  
  1306. ;; processing input
  1307. (defun irc-process-input ()
  1308.   "If in the input region, parse it for messages and commands.
  1309. In the output region, next-line if irc-spacebar-pages, otherwise do nothing.
  1310.  
  1311. All of the lines in the input region are rejoined during processing to be
  1312. handled as one.  A command is any line starting with a / after leading
  1313. whitespace is stripped away; commands can not exceed 250 characters.  Messages
  1314. can be longer but they will be split into 250 character segments for IRC.  The
  1315. buffer will reflect how the message was sent if it needed to be broken; the
  1316. split(s) will be indicated by \" >>\" to mean that the message is continued."
  1317.   (interactive)
  1318.   ;; do the simple stuff for the output region
  1319.   (if (< (point) irc-mark) (if irc-spacebar-pages (scroll-up 1) (ding))
  1320.     (irc-check-time)
  1321.     ;; the input region is more work ...
  1322.     ;; first, toast extraneous spaces, tabs and newlines at end of input region
  1323.     (delete-region (goto-char (point-max))
  1324.                    (if (re-search-backward "[^ \t\n]" irc-mark t)
  1325.                        (1+ (point)) (point)))
  1326.     ;; nuke the white space at the beginning of input region, too
  1327.     (delete-region (goto-char irc-mark)
  1328.                    (progn (re-search-forward "\\s *") (point)))
  1329.     (setq irc-message-index -1 irc-command-index -1) ; reset history locations
  1330.     (let ((txt (buffer-substring irc-mark (point-max))) send ass)
  1331.       ;; check to see if the input region is empty
  1332.       (if (string= "" txt) (message "(nothing sent to the irc-server)")
  1333.         ;; convert newlines to spaces
  1334.         (while (string-match "\n" txt)
  1335.           (aset txt (match-beginning 0) 32))
  1336.         (if (string-match "^/" txt)  ; it's a command
  1337.             (if (< (length txt) 250)
  1338.                 (progn
  1339.                   (goto-char (point-max)) (insert "\n")
  1340.                   (set-marker irc-mark (point))
  1341.                   (irc-add-to-hist 'irc-command-history txt)
  1342.                   (irc-do-command (substring txt 1)))
  1343.               ;; can't use error because that kills the function
  1344.               (ding) (message "IRC commands can't exceed 250 characters."))
  1345.           ;; "a specified sendlist" -- was there one?
  1346.           (setq ass (irc-find-to txt 'explicit))
  1347.           (if (and ass (string-match "^[^:;]" txt))
  1348.               ;; a real sendlist was specified -- update irc-last-explicit
  1349.               (setq irc-last-explicit (irc-find-to txt)))
  1350.           (irc-add-to-hist 'irc-message-history
  1351.                            (concat (if (not ass) irc-default-to)
  1352.                                    (buffer-substring irc-mark (point-max))))
  1353.           (while (> (length txt) 250)
  1354.             (setq send (substring txt 0 250)
  1355.                   txt  (substring txt 250)
  1356.                   send (irc-fix-wordwrap send txt)
  1357.                   txt  (concat (if ass irc-last-explicit irc-default-to)
  1358.                                (cdr send))
  1359.                   send (concat (car send) " >>"))
  1360.             (goto-char (+ irc-mark (- (length send) 3)))
  1361.             (insert " >>\n" (if ass irc-last-explicit irc-default-to))
  1362.             (if (looking-at "[ \t]") (delete-char 1))
  1363.             (beginning-of-line)
  1364.             (set-marker irc-mark (point))
  1365.             (irc-do-msg send))
  1366.           (goto-char (point-max)) (insert "\n")
  1367.           (set-marker irc-mark (point))
  1368.           (irc-do-msg txt))))))
  1369.  
  1370. (defun irc-do-command (str)
  1371.   ;; Execute the "/" command of STR.  STR should not begin with a slash.
  1372.   ;; Commands are first looked up in the irc-alias-alist; if it is found there
  1373.   ;; then the alias gets passed recursively with any arguments the original
  1374.   ;; had.  The irc-command-alist is checked next and finally irc-operator-alist
  1375.   ;; is checked.  A command is considered found when it matches either
  1376.   ;; exactly or unambiguously starting at the first character.  That is, J
  1377.   ;; would match JOIN, but OIN would not match JOIN.
  1378.   (let* ((case-fold-search t)
  1379.          (cmd (substring str 0 (string-match "\\(\\s +\\|$\\)" str)))
  1380.          (text (substring str (match-end 0)))
  1381.          (ambig (irc-check-list
  1382.                  (mapcar 'car (append irc-alias-alist irc-command-alist
  1383.                                       (if irc-operator irc-operator-alist)))
  1384.                  cmd 'start-only))
  1385.          matches)
  1386.     ;; if no matches are found the command might still be a valid command
  1387.     ;; name hiding behind non-operator status.  i don't like messages that
  1388.     ;; lie and say "Unknown command '/REHASH'" so this should make it not lie.
  1389.     (if (and (not irc-operator) (null ambig))
  1390.         (setq ambig (irc-check-list (mapcar 'car irc-operator-alist) cmd t)))
  1391.     ;; first determine any ambiguities among the lists
  1392.     (if (null ambig)
  1393.         ;; no matches at all were found
  1394.         (irc-notice "Unknown command '/%s'.  Type /HELP for help."
  1395.                     (upcase cmd))
  1396.       ;; this is here for when a regular command gets aliased.  it shows up as
  1397.       ;; being ambiguous but it really isn't later on.
  1398.       (if (member-general (car ambig) (cdr ambig) 'string=)
  1399.           (setq ambig (cdr ambig)))
  1400.       (if (> (length ambig) 1)
  1401.           (irc-notice "Ambiguous command '/%s'.  Could be %s." (upcase cmd)
  1402.                       (irc-subst-comma
  1403.                        (mapconcat (function (lambda (arg)
  1404.                                               (concat "/" arg))) ambig ", ")
  1405.                        "or"))
  1406.         ;; alias list has highest priority
  1407.         (setq matches (irc-check-list (mapcar 'car irc-alias-alist) cmd t))
  1408.         ;; make sure matches is what we set out to looking for ...
  1409.         (if (and matches (string= (car matches) (car ambig)))
  1410.             ;; call this function again with the text as argument
  1411.             (irc-do-command
  1412.              (concat (cdr (assoc (car matches) irc-alias-alist))
  1413.                      ;; the servers won't grok trailing whitespace for some
  1414.                      ;; messages so only use it to separate an argument
  1415.                      (if (string< "" text) " ") text))
  1416.           ;; next try the command alist
  1417.           (setq matches (irc-check-list (mapcar 'car irc-command-alist) cmd t))
  1418.           (if matches
  1419.               ;; call the appropriate irc-do-* function
  1420.               (funcall (intern-soft
  1421.                         (concat "irc-do-"
  1422.                                 (cdr (assoc (car matches)
  1423.                                             irc-command-alist)))) text)
  1424.             ;; no matches yet.  last resort is the operator alist
  1425.             (setq matches (irc-check-list (mapcar 'car irc-operator-alist)
  1426.                                           cmd t))
  1427.             (if matches
  1428.                 (if irc-operator
  1429.                     (funcall (intern-soft
  1430.                               (concat "irc-do-"
  1431.                                       (cdr (assoc (car matches)
  1432.                                                   irc-operator-alist)))) text)
  1433.                   (irc-notice "Only IRC Operators can use the /%s command."
  1434.                               (upcase (car matches)))))))))))
  1435.  
  1436. (defun irc-send (str)
  1437.   ;; Send STR to process in the current buffer.  A CR-LFD pair is appended
  1438.   ;; automatically as per the 'official' IRC protocol but it seems unnecessary.
  1439.   (if irc-raw-mode (irc-notice str))
  1440.   (send-string (get-buffer-process (current-buffer)) (concat str "\r\n"))
  1441.   str)
  1442.  
  1443. ;; sending messages to people
  1444. (defun irc-do-privmsg (str)
  1445.   "Usage: /MSG recipient(s) message
  1446.  
  1447. This command is provided simply for compatability with the C client.  It is
  1448. preferable instead to just type the name of the user followed by a semi-colon
  1449. or colon and then the message.  That is, \"tale;hi!\" will send the message
  1450. \"hi!\" to the user with the nickname which unambiguously matches \"tale\".
  1451. A semi-colon or colon at the beginning of the line means to send to the last
  1452. recipient explicity specified; typing a semi-colon at the beginning of a line
  1453. expands it to the last recipient(s) specified while typing a colon at the
  1454. beginning of the line automatically expands to the last person to have sent
  1455. you a private message.  The recipients for a message can be a comma separated
  1456. list of users and/or channels."
  1457.   (irc-add-to-hist 'irc-message-history
  1458.    (irc-do-msg (concat
  1459.                 (setq irc-last-explicit
  1460.                       (concat (substring str 0
  1461.                                          (string-match "\\s +\\|$" str)) ";"))
  1462.                 (substring str (match-end 0))))))
  1463.  
  1464. (defun irc-do-msg (str)
  1465.   ;; this really is an indirect fucntion of the UI (ie, not through a /COMMAND)
  1466.   ;; so it isn't interactive
  1467.   (let (tolist (orig str) icw confirm)
  1468.     ;; map newlines to spaces
  1469.     (while (string-match "\n" str) (aset str (match-beginning 0) 32))
  1470.     (if (string-match "^[;:]" str)
  1471.         ;; a little bit of fill-in-the-blank
  1472.         (setq str (concat irc-last-explicit (substring str 1)))
  1473.       (if (not (irc-find-to str 'explicit))
  1474.           ;; prepend an implicit sendlist if need be
  1475.           (if irc-default-to (setq str (concat irc-default-to str))
  1476.             (irc-notice "You have no default sendlist."))))
  1477.     (if (irc-find-to str 'explicit)
  1478.         (setq icw (irc-find-to str)
  1479.               tolist (irc-burst-comma (substring icw 0 (1- (length icw))))
  1480.               str (irc-find-message str)
  1481.               ;; kill on leading space if it exists.  ie, "tale: hi" will
  1482.               ;; send "hi" as a message not " hi".  Note that blank messages
  1483.               ;; are not disallowed here but will almost assuredly get
  1484.               ;; an ERR_NOTEXTTOSEND message from the server.
  1485.               str (if (and (string< "" str) (= (aref str 0) 32))
  1486.                       (substring str 1) str)))
  1487.     (setq
  1488.      confirm
  1489.      (delq                              ; whee.  lisp indentation is fun.
  1490.       nil
  1491.       (mapcar (function
  1492.                (lambda (to)
  1493.                  (if (not (zerop (string-to-int to)))
  1494.                      (if (string= to irc-channel)
  1495.                          (progn (irc-send (concat "MSG :" str)) to)
  1496.                        ;; new in 1.2 -- you _can_ send to a channel you
  1497.                        ;; are not on.  This of course is the very same broken
  1498.                        ;; behaviour here that I moan about having to deal
  1499.                        ;; with in irc-parse-priv
  1500.                        (irc-send (concat "PRIVMSG " to " :" str))
  1501.                        to)
  1502.                    (setq icw (if irc-noglob nil
  1503.                                (irc-check-list irc-wholist to)))
  1504.                    (cond
  1505.                     ((string= to "*")
  1506.                      (if (string= "0" irc-channel)
  1507.                          (progn (irc-notice "You are not on any channel.") nil)
  1508.                        (irc-send (concat "MSG :" str))
  1509.                        irc-channel))
  1510.                     ((string= to "0")
  1511.                      (irc-notice "You can't send to channel 0.") nil)
  1512.                     ((= (length icw) 1)
  1513.                      (irc-send (concat "PRIVMSG " (car icw) " :" str))
  1514.                      (car icw))
  1515.                     ((not icw)
  1516.                      ;; wox.  no one found, but we'll do a nonomatch.  try
  1517.                      ;; sending it anyway and let the server bitch if necessary
  1518.                      (irc-maintain-list 'irc-wholist to 'add)
  1519.                      (irc-send (concat "PRIVMSG " to " :" str))
  1520.                      to)
  1521.                     (t
  1522.                      (irc-notice "Ambiguous recipient \"%s\"; could be %s."
  1523.                                  to (irc-subst-comma
  1524.                                      (mapconcat 'eval icw ", ") "or")) nil)))))
  1525.               tolist)))
  1526.     (if (and confirm irc-confirm)
  1527.         (irc-notice "(message sent to %s)"
  1528.                     (irc-subst-comma (mapconcat 'eval confirm ", ") "and"))
  1529.       (if (not confirm) (irc-notice "(message not sent)")))
  1530.     orig))
  1531.  
  1532. (defun irc-do-oops (&optional newto)    ; one of my favourites. 
  1533.   "Usage: /OOPS intended-recipient
  1534.  
  1535. Send irc-oops to recipient(s) of last message and resend message to
  1536. 'intended-recipient'.  This command is handy when you've just sent a message
  1537. to the wrong place and you want the person/people who saw it to know that they
  1538. should just disregard it.  The message which was originally sent then gets
  1539. forwarded to its proper destination."
  1540.   (interactive)
  1541.   ;; first do the oops message
  1542.   (irc-do-msg (concat (irc-find-to (car irc-message-history)) irc-oops))
  1543.   ;; then resend the original
  1544.   (irc-do-redirect (or newto "")))
  1545.  
  1546. (defun irc-do-redirect (newto)
  1547.   "Usage: /REDIRECT additional-recipient
  1548.  
  1549. Send to 'additional-recipient' the last message which you sent.  This 
  1550. command can be fairly easily duplicated using the history mechanism by hand
  1551. but it is provided to make it even easier."
  1552.   (interactive (list
  1553.                 (read-string
  1554.                  (format "New recipient(s)? %s"
  1555.                          (if irc-default-to
  1556.                              (concat "[RET for "
  1557.                                      (substring irc-default-to 0
  1558.                                                 (1- (length irc-default-to)))
  1559.                                      "] ")
  1560.                            "")))))
  1561.   (let ((msg (irc-find-message (car irc-message-history))) send)
  1562.     (if (not (string-match "^[a-zA-Z0-9-_,|{*]*$" newto))
  1563.         ;; perhaps crapping out here is too harsh
  1564.         (irc-notice "%s is not a valid sendlist.  Message not resent." newto)
  1565.       (if (and (not (interactive-p)) (string= "" newto))
  1566.           (call-interactively 'irc-do-redirect)
  1567.         (setq newto (if (string= "" newto) irc-default-to (concat newto ";"))
  1568.               msg   (concat newto msg)
  1569.               irc-last-explicit newto)
  1570.         (irc-add-to-hist 'irc-message-history msg)
  1571.         (while (> (length msg) 250)
  1572.           (setq send (substring msg 0 250)
  1573.                 msg  (substring msg 250)
  1574.                 send (irc-fix-wordwrap send msg)
  1575.                 msg  (concat newto (cdr send))
  1576.                 send (concat (car send) " >>"))
  1577.           (irc-do-msg send))
  1578.         (irc-do-msg msg)))))
  1579.  
  1580. ;; /commands for the server
  1581. (defun irc-do-quote (msg)
  1582.   "Usage: /QUOTE string
  1583.  
  1584. This command is used to send 'string' directly to the IRC server without
  1585. any local processing.  Warning: this has the potential to screw up some
  1586. things in irc-mode, particularly if it is used to change your nickname or
  1587. to switch channels."
  1588.   (interactive "sString to send to server: ")
  1589.   (if (string-match "^\\s *$" msg)
  1590.       (irc-notice "(nothing was sent to the IRC server)")
  1591.     (irc-send msg)))
  1592.  
  1593. (defun irc-do-who (channel)
  1594.   "Usage: /WHO [ channel | user ]
  1595.  
  1596. Get a list of the users on IRC.  Optional argument 'channel' means to show
  1597. just the users on that channel, with * representing the current channel.
  1598.  
  1599. Each user is indicated on a separate line with their nickname, channel, login
  1600. name, host and real name.  The first column indicates their status --
  1601. ' ' for here, '-' for away, '*' for an operator, '=' for an away operator
  1602. and '#' for someone being ignored.  Servers don't propogate the information
  1603. about who is away so you will probably only see people on your server
  1604. correctly marked regarding their presence.
  1605.  
  1606. Users who are either on a channel greater than 1000 or who are on no channel
  1607. have nothing listed in the Chan column.  Users who are on channels less than
  1608. zero do not appear in the list.
  1609.  
  1610. If a non-numeric argument 'user' is given then it is taken to be the nickname
  1611. of a user on IRC and more information, if available, is given about the person.
  1612.  
  1613. If this function is called interactively then the prefix argument is used
  1614. as the channel to query.  No argument means all of them and an argument of -
  1615. means the current channel."
  1616.   (interactive (if current-prefix-arg
  1617.                    (if (eq current-prefix-arg '-) '("*")
  1618.                      (list (int-to-string
  1619.                             (prefix-numeric-value current-prefix-arg))))
  1620.                  '("0")))
  1621.   ;; make * be the current channel, even though the server groks it.
  1622.   (if (string-match "^\\s *\\*\\(\\s .*\\)?$" channel)
  1623.       (setq channel irc-channel))
  1624.   (if (string-match "^\\s *$" channel)
  1625.       (setq channel "0"))
  1626.   ;; A simple choice to make; if channel isn't a number or nothing, try
  1627.   ;; doing a whois with the argument.
  1628.   ;; also if channel starts with +, it's a new-style string channel
  1629.   (if (not 
  1630.        (or (= (string-to-char channel) ?+)
  1631.        (numberp (car (read-from-string channel)))))
  1632.       (irc-do-whois channel)
  1633.     ;; if channel converts to 0 then we will get fresh information about
  1634.     ;; who is present.
  1635.     ;; unless... it's a string channel!  in which case is bad.
  1636.     ;; so... (if integerp channel...)
  1637.     (if (integerp channel)
  1638.     (if (zerop (string-to-int channel)) (setq irc-wholist nil)))
  1639.     (setq irc-operwho nil) ; make sure we aren't still expecting only opers
  1640.     (irc-send (concat "WHO " channel))
  1641.     ;; a gratuitous blank line only if called interactively.
  1642.     (if (and (not irc-conserve-space) (interactive-p)) (irc-notice ""))))
  1643.  
  1644. (defun irc-do-operwho (&optional cruft)
  1645.   "Usage: /OPERWHO
  1646.  
  1647. Get a list of the operators who are visible on IRC.  The format is the same
  1648. as for /WHO.  Arguments to this command are ignored."
  1649.   (interactive)
  1650.   (setq irc-operwho t)
  1651.   (setq irc-negwho nil)
  1652.   (irc-send "WHO")
  1653.   (if (and (not irc-conserve-space) (interactive-p)) (irc-notice "")))
  1654.  
  1655.  
  1656.  
  1657. (defun irc-do-whowas (user)
  1658.   ;; Rocker's addition 24may90
  1659.   "Usage: /WHOWAS user
  1660.  
  1661. Get a two line description of who and where 'user' was.  If user is not
  1662. provided it is read from the minibuffer with a completing-read."
  1663.   (interactive '(""))
  1664.   (setq user (irc-read-user "Who was who? " user)
  1665.         irc-doing-finger nil)
  1666.   (if (string< "" user) (irc-send (concat "WHOWAS " user)))
  1667.   (if (and (not irc-conserve-space) (interactive-p)) (irc-notice "")))
  1668.  
  1669. (defun irc-do-whois (user)
  1670.   "Usage: /WHOIS user
  1671.  
  1672. Get a two line description of who and where 'user' is.  If user is not
  1673. provided it is read from the minibuffer with a completing-read."
  1674.   (interactive '(""))
  1675.   (setq user (irc-read-user "Who is who? " user)
  1676.         irc-doing-finger nil)
  1677.   (if (string< "" user) (irc-send (concat "WHOIS " user)))
  1678.   (if (and (not irc-conserve-space) (interactive-p)) (irc-notice "")))
  1679.  
  1680. (defun irc-do-finger (user)
  1681.   "Usage: /FINGER { nickname | user@host }
  1682.  
  1683. Show more information about a user, using the Internet \"finger\" facility.
  1684. If the argument does not contain an `@' character then nickname globbing is 
  1685. done and /WHOIS is called to get the user@host.  The WHOIS data will also
  1686. be displayed."
  1687.   (interactive "sFinger who? ")
  1688.   (setq irc-doing-finger t)
  1689.   (if (string= "" user)
  1690.       (if (not (interactive-p)) (call-interactively 'irc-do-finger))
  1691.     (if (string-match "@" user) (irc-finger user)
  1692.       (setq user (irc-read-user "Finger who? " user))
  1693.       (if (string< "" user) (irc-send (concat "WHOIS " user))))))
  1694.  
  1695. (defun irc-do-list (&optional channel)
  1696.   "Usage: /LIST [ channel ]
  1697.  
  1698. Get a list of the discussions that are on IRC.  The optional channel argument
  1699. is supposed to show just that channel but this is not currently supported
  1700. by most servers."
  1701.   ;; according to Comms LIST can take an optional channel number.
  1702.   ;; don't believe it -- it doesn't.  I send one anyway just in case it
  1703.   ;; gets fixed; in the meantime servers seem to ignore any extra stuff
  1704.   (interactive)
  1705.   (irc-send (concat "LIST " channel))
  1706.   ;; put a blank line before the list if interactive
  1707.   (if (and (not irc-conserve-space) (interactive-p)) (irc-notice "")))
  1708.  
  1709. (defun irc-do-links (&optional pattern)
  1710.   "Usage: /LINKS [ pattern ]
  1711.  
  1712. Show the names of all the servers which can communicate with your server.
  1713. If ``pattern'' is provided then all servers matching it will be displayed.
  1714. ? and * act as Bourne shell wild-cards, matching one or any number of
  1715. characters respectively.  Example: \"/LINKS *.cs.*\" will display all servers
  1716. which have \"cs\" as one element of their domain name.
  1717.  
  1718. The links can go down isolating different parts of the IRC-net, so this
  1719. is a good way to find out how extensive it is at the moment."
  1720.   (interactive "sFind servers matching pattern: ")
  1721.   (irc-send (concat "LINKS " pattern))
  1722.   (if (and (not irc-conserve-space) (interactive-p)) (irc-notice "")))
  1723.  
  1724. (defun irc-do-lusers (&optional cruft) ; I still don't like the name
  1725.   "Usage: /LUSERS
  1726.  
  1727. Get the number of users and servers on your IRC network.  Arguments to this
  1728. command are ignored."
  1729.   (interactive)
  1730.   (irc-send "LUSERS")
  1731.   (if (and (not irc-conserve-space) (interactive-p)) (irc-notice "")))
  1732.  
  1733. (defun irc-do-motd (&optional server)
  1734.   "Usage: /MOTD [ server ]
  1735.  
  1736. Get the message of the day for 'server'.  If server is not provided, the
  1737. local server is queried for its message of the day.  If invoked interactively
  1738. then a prefix argument means to prompt for the server to query."
  1739.   (interactive (if current-prefix-arg
  1740.                    (list
  1741.                     (read-string "Get message of the day at which server? "))
  1742.                  '("")))
  1743.   (setq irc-motd-bug nil)
  1744.   (irc-send (concat "MOTD " server)))
  1745.  
  1746. (defun irc-do-admin (server)
  1747.   "Usage: /ADMIN [ server ]
  1748.  
  1749. Get information about the IRC administrator for 'server'; if server is not
  1750. supplied just query for the server to which you are connected."
  1751.   (interactive "sAdministrative info about which server? ")
  1752.   (irc-send (concat "ADMIN " server))
  1753.   (if (and (not irc-conserve-space) (interactive-p)) (irc-notice "")))
  1754.  
  1755. (defun irc-do-time (&optional server)
  1756.   "Usage: /TIME [ server ]
  1757.  
  1758. Get the current time on 'server'; is no server is provided use the one to which
  1759. you are connected.  When called with a interactively with a prefix-argument
  1760. the server name is read using the minibuffer.
  1761.  
  1762. Querying other servers can be handy given that people on IRC are spread out
  1763. through North America, Europe and Asia.  The question \"What time is it in
  1764. Finland?\" comes up so frequently that an alias -- /TF -- has been provided
  1765. by default to get the answer.  This alias should work as long as tut.fi is
  1766. connected to your IRC-net."
  1767.   (interactive (if current-prefix-arg
  1768.                    (list (read-string "Get time at which server? "))
  1769.                  '("")))
  1770.   (irc-send (concat "TIME " server)))
  1771.  
  1772. (defun irc-do-join (channel)
  1773.   "Usage: /JOIN channel
  1774.  
  1775. Join 'channel' on IRC.  If channel is not provided it is requested in the
  1776. minibuffer; when called interactively channel is set to the prefix argument if
  1777. one is present.  Use /LEAVE to exit the channel."
  1778.   (interactive (if current-prefix-arg
  1779.                    (list (int-to-string
  1780.                           (prefix-numeric-value current-prefix-arg)))
  1781.                  '("")))
  1782.   (if (string= channel "")
  1783.       (setq channel (read-string "Channel to join? ")))
  1784.   (if (string-match "^\\s *$" channel) () ; well, so much for that
  1785.     ;; make /join . go to the channel you've been invited to join.
  1786.     (if (string-match "^\\s *\\.\\s *$" channel)
  1787.         (setq channel irc-last-invite))
  1788.     (irc-send (concat "CHANNEL " channel))))
  1789.  
  1790. (defun irc-do-leave (&optional cruft)
  1791.   "Usage: /LEAVE
  1792.  
  1793. Leave your current channel and join no other.  Any arguments to this command
  1794. are ignored."
  1795.   (interactive)
  1796.   (irc-send "CHANNEL 0"))
  1797.  
  1798. (defun irc-do-nick (name)
  1799.   "Usage: /NICKNAME name
  1800.  
  1801. Change your nickname in IRC.  A nickname can contain alphanumeric characters,
  1802. underscores (_), hyphens (-) or the special characters vertical bar (|) and
  1803. left brace ({), which are alphabetic characters in Finnish.  The name cannot
  1804. start with a hyphen or number and only the first nine characters are used.
  1805.  
  1806. Unfortunately, due to the way confirmation from the servers work, it might be
  1807. falsely reported that your nickname was successfully changed when it was not.
  1808. The server will come back and say so and finally irc-mode will wise-up and
  1809. note that your nickname was not changed."
  1810.   (interactive "sNew nickname? ")
  1811.   (if (string= "" name) (if (interactive-p)
  1812.                             (irc-notice "Nickname not changed.")
  1813.                           (call-interactively 'irc-do-nick)))
  1814.   (while (not (string-match "^\\([a-zA-Z|{_][a-zA-Z0-9-_|{]*\\)?$" name))
  1815.     (setq name (read-string
  1816.                 (format "\"%s\" is not valid.  New nickname? " name))))
  1817.   (if (< (length name) 10) ()
  1818.     (if (y-or-n-p
  1819.          (format "\"%s\" is too long; shortened to \"%s\".  Okay? "
  1820.                  name (substring name 0 9)))
  1821.         (setq name (substring name 0 9))
  1822.       (setq name ""))
  1823.     (message "")) ;; silly y-or-n-p message stays around
  1824.   (if (string= name "") (irc-notice "Nickname not changed.")
  1825.     (irc-notice "You will now be known as \"%s\"." name)
  1826.     (put 'irc-nick 'o-nick irc-nick)
  1827.     (setq irc-nick name)
  1828.     (set-buffer-modified-p (buffer-modified-p))
  1829.     (irc-send (concat "NICK " name))))
  1830.  
  1831. (defun irc-do-quit (&optional text)
  1832.   "Usage: /QUIT
  1833.  
  1834. Exit IRC.  The connexion is closed but the buffer is left behind.
  1835. Arguments to this command are not ignored; if any are present then
  1836. the session is not exited as a safety precaution against seemingly
  1837. unintentional use of the command."
  1838.   (interactive)
  1839.   (if (and text (string< "" text))
  1840.       (irc-notice "/QUIT takes no arguments.")
  1841.     (setq irc-wanted-exit t)
  1842.     (irc-send "QUIT")))
  1843.  
  1844. (defun irc-do-away (&optional text)
  1845.   "Usage: /AWAY message
  1846.  
  1847. Mark yourself as away, giving TEXT to people who send you private messages.
  1848. Without any arguments it will just insert a message about your current status."
  1849.   (interactive "sReason for being away: ")
  1850.   (if (string= "" text)
  1851.       (if irc-away
  1852.           (irc-notice "You are marked as away: '%s'." irc-away)
  1853.         (irc-notice "You are not currently marked as being away."))
  1854.     (irc-send (concat "AWAY " text))
  1855.     (setq irc-away (concat " [" text "]")))
  1856.   (set-buffer-modified-p (buffer-modified-p)))
  1857.  
  1858. (defun irc-do-here (&optional cruft)
  1859.   "Usage: /HERE
  1860.  
  1861. Mark yourself as present (ie, not \"away\") on IRC.  Any arguments to this
  1862. command are ignored."
  1863.   (interactive)
  1864.   (irc-send "AWAY")
  1865.   (setq irc-away nil)
  1866.   (set-buffer-modified-p (buffer-modified-p)))
  1867.  
  1868. (defun irc-do-topic (topic)
  1869.   "Usage: /TOPIC [ topic ]
  1870.  
  1871. Make 'topic' the description of the current channel; this description is
  1872. shown to people looking at the channel listing.  With no argument then
  1873. the topic of the current channel will be displayed.  When called interactively
  1874. then a prefix argument means to prompt for the new topic."
  1875.   (interactive (list (if (or (null current-prefix-arg)
  1876.                              (string= "0" irc-channel)) ""
  1877.                        (read-string (format "Topic for channel %s? "
  1878.                                             irc-channel)))))
  1879.   (if (string= "0" irc-channel)
  1880.       (irc-notice "You aren't on any channel.")
  1881.     (irc-send (concat "TOPIC :" topic))))
  1882.  
  1883. (defun irc-do-oper (oper)
  1884.   "Usage: /OPER name password
  1885.  
  1886. Attempt to become an IRC Operator.  Can take the name of the operator
  1887. and the password as arguments.  If name is missing then it will be read
  1888. >from the minibuffer; if password is missing it will be read and hidden
  1889. in the minibuffer.
  1890.  
  1891. If you become an operator then the word \"Operator\" will appear in the
  1892. modeline along with the mode name."
  1893.   (interactive "sOperator name? ")
  1894.   (string-match "^\\s *\\(\\S *\\)\\s *\\(\\S *\\).*$" oper)
  1895.   (let* ((name   (substring oper (match-beginning 1) (match-end 1)))
  1896.          (passwd (substring oper (match-beginning 2) (match-end 2)))
  1897.          (noname (string= "" name)))
  1898.     (if (and (interactive-p) noname) () ; just drop right through
  1899.       (if noname (call-interactively 'irc-do-oper)
  1900.         (if (string= "" passwd)
  1901.             (setq passwd
  1902.                   (irc-read-passwd (format "Password for operator %s? "
  1903.                                            name))))
  1904.         (irc-send (concat "OPER " name " " passwd))))))
  1905.  
  1906. (defun irc-do-summon (user)
  1907.   "Usage: /SUMMON user
  1908.  
  1909. Summon a user not on IRC to join IRC.  The argument provided may either be
  1910. a user name on the local machine or user@server, where server is another
  1911. machine on the IRC-net.  The user must be signed on to the specified server."
  1912.   (interactive "sUser to summon to IRC? ")
  1913.   (let ((nouser (string-match "^\\s *$" user)))
  1914.     (if (and (interactive-p) nouser) ()  ; guess s/he didn't really mean it ...
  1915.       (if nouser (call-interactively 'irc-do-summon)
  1916.         (irc-send (concat "SUMMON " user))))))
  1917.  
  1918. (defun irc-do-users (host)
  1919.   "Usage: /USERS [ server ]
  1920.  
  1921. Get a list of the users signed on to 'server'.  If no server name is provided
  1922. then the server to which you are connected is used.  When called interactively
  1923. a prefix argument means to prompt for the server to query."
  1924.   (interactive (if current-prefix-arg
  1925.                    (list (read-string "List users on which host? "))
  1926.                  '("")))
  1927.   (if (and (not irc-conserve-space) (interactive-p)) (irc-notice ""))
  1928.   (irc-send (concat "USERS " host)))
  1929.  
  1930. (defun irc-do-info (&optional cruft)
  1931.   "Usage: /INFO
  1932.  
  1933. Show some information about the programmer of IRC.  Arguments to this command
  1934. are ignored."
  1935.   (interactive) (irc-send "INFO"))
  1936.  
  1937. (defun irc-do-invite (user)
  1938.   "Usage: /INVITE user [ channel ]
  1939.  
  1940. Ask 'user' on IRC to join 'channel'.  If channel is 0, * or not provided then
  1941. the invitation defaults to your current channel.  If you are not on any channel
  1942. and channel is 0 or not provided then no invitation is sent -- you can't
  1943. invite someone to go private.  When called interactively channel is set to
  1944. the prefix argument; with no argument or - the current channel is assumed."
  1945.   (interactive (list
  1946.                 (if (and current-prefix-arg (not (eq current-prefix-arg '-)))
  1947.                     (int-to-string (prefix-numeric-value current-prefix-arg))
  1948.                   irc-channel)))
  1949.   (if (interactive-p)
  1950.       (progn
  1951.         (if (and (string= "0" irc-channel) (string= "0" user))
  1952.             (setq user (read-string "Invite to which channel? ")))
  1953.         ;; this is so irc-read-user will force a completing-read
  1954.         ;; something needs to come up as "name" so that "channel" comes up in
  1955.         ;; the right place.  a tiny kludge but the results are the same
  1956.         (setq user (concat ". " user))))
  1957.   (string-match "^\\s *\\(\\S *\\)\\s *\\([-0-9]*\\).*$" user)
  1958.   (let* ((name    (substring user (match-beginning 1) (match-end 1)))
  1959.          (channel (substring user (match-beginning 2) (match-end 2)))
  1960.          (noname  (string= "" name)))
  1961.     (cond
  1962.      (noname (call-interactively 'irc-do-invite)) ; no arguments ...
  1963.      ((and (string= "0" irc-channel) (zerop (string-to-int channel)))
  1964.       (irc-notice "You are not on any channel.  No invitation sent."))
  1965.      (t (setq name
  1966.               (irc-read-user (format "Invite whom to channel %s? " channel)
  1967.                              (if (string= "." name) "" name)))
  1968.         (if (string< "" name)
  1969.             (irc-send (concat "INVITE " name " " channel)))))))
  1970.  
  1971. (defun irc-do-names (channel)
  1972.   "Usage: /NAMES [ channel ]
  1973.  
  1974. Show which channels everyone is on.  Optional argument 'channel' (which can
  1975. be provided as a prefix argument if called interactively) means to show
  1976. just the users on that channel.  * or an interactive prefix argument of -
  1977. means to show people on the current channel.
  1978.  
  1979. Each line starts with a column for the channel number and is followed by the
  1980. nicknames of the people on that channel.  Users who are on private channels
  1981. or who are not on any channel are listed as \"Private\".  Users who are
  1982. on secret channels (channels less than 0) are not shown at all."
  1983.   (interactive (if current-prefix-arg
  1984.                    (if (eq current-prefix-arg '-) '("*")
  1985.                      (list (int-to-string
  1986.                             (prefix-numeric-value current-prefix-arg))))
  1987.                  '("0")))
  1988.   ;; server doesn't understand * for current channel.  but we want to be
  1989.   ;; nice and consistent in the client so we take it.
  1990.   (if (string-match "^\\s *\\*\\(\\s .*\\)?$" channel)
  1991.       (setq channel irc-channel))
  1992.   ;; have to do some weird things here.  servers don't grok a NAMES 0
  1993.   ;; at all so have to make anything that appears to be 0 really disappear.
  1994.   ;; names also provides us with fresh information on who is around.
  1995.   (cond ((string-match "^\+" channel)) ; Rocker 14july90 accept string channels
  1996.     ((zerop (string-to-int channel))
  1997.      (setq irc-wholist nil channel "")))
  1998.   (irc-send (concat "NAMES " channel))
  1999.   (if (and (not irc-conserve-space) (interactive-p)) (irc-notice ""))
  2000.   ;; need a header here.  server is not gratuitous as in WHOREPLY.
  2001.   (irc-notice "Channel  Users"))
  2002.  
  2003. (defun irc-do-version (&optional server)
  2004.   "Usage: /VERSION [ server ]
  2005.  
  2006. Get the IRC daemon version of 'server'.  If server is not provided, the
  2007. local server is queried for its version and the current version of the Emacs
  2008. IRC client is also shown.  If invoked interactively then a prefix argument
  2009. means to prompt for the server to query.
  2010.  
  2011. Servers do not currently support remote querying of their version; in fact,
  2012. an argument of a valid server name to /VERSION will result in no output at
  2013. all.  The command accepts it, though, in vapid anticipation of the day when
  2014. it will have meaning."
  2015.   (interactive (if current-prefix-arg
  2016.                    (list
  2017.                     (read-string "Get message of the day at which server? "))
  2018.                  '("")))
  2019.   (if (string= "" server) (irc-notice irc-version))
  2020.   (irc-send (concat "VERSION " server)))
  2021.  
  2022. (defun irc-do-mode (modes)
  2023.   "Usage: /MODE [ modes ]
  2024.  
  2025. Change the Mode of the string channel or check the Mode of the string channel.
  2026. Channel can be abbreviated by inserting * instead.
  2027.  
  2028.      Possible modes flags  s - secret, p - private, l - limited,
  2029.                            m - moderated, n - no private messages to channel,
  2030.                            t - topic settable by Channel Operator only
  2031.  
  2032.      Ownerships to string channel can be given and taken away by
  2033.  
  2034.         Mode <channel> +o <nickname>   Mode <channel> -o <nickname>"
  2035.   (cond ((string-match "^\*" modes)
  2036.      (setq modes (concat irc-channel 
  2037.                  (substring modes (match-end 0))
  2038.                  ))))
  2039.   (irc-send (concat "MODE " modes)))
  2040.  
  2041. (defun irc-do-wallops (msg)
  2042.   "Usage: /WALLOPS [msg]
  2043.  
  2044. Allows one to send a broadcast message to all IRC Operators."
  2045.   (cond ((string-match "^\*" msg)
  2046.      (setq msg (substring msg (match-end 0)))))
  2047.   (irc-send (concat "WALLOPS " msg)))
  2048.  
  2049. ;;(defun irc-do-kick ()
  2050. ;;  "Usage: /KICK [channel] [user]
  2051. ;;
  2052. ;;The Kick command allows a Channel Operator to kick users off a string channel."
  2053. ;;  (interactive "sWho gets the boot? ")
  2054. ;;(if (string= ""
  2055.  
  2056. ;; Operator Commands
  2057. (defun irc-do-connect (server)
  2058.   "Usage: /CONNECT server [ port ]
  2059.  
  2060. This operator command will attempt to force a connexion from your server to
  2061. SERVER.  If PORT is not provided (which can be given as a prefix argument when
  2062. called interactively) then it is assumed to be 6667."
  2063.   (interactive
  2064.    (list (concat (read-string "Connect to which server? ") " "
  2065.                  (if current-prefix-arg
  2066.                      (int-to-string (prefix-numeric-value current-prefix-arg))
  2067.                    (read-string "At port (default 6667): ")))))
  2068.   (string-match "^\\s *\\(\\S *\\)\\s *\\([0-9]*\\).*$" server)
  2069.   (let ((name (substring server (match-beginning 1) (match-end 1)))
  2070.         (port (substring server (match-beginning 2) (match-end 2))))
  2071.     (if (string= "" name)
  2072.         (if (not (interactive-p))
  2073.             (call-interactively 'irc-do-connect))
  2074.       (irc-send (concat "CONNECT " name " "
  2075.                         (if (string= "" port) "6667" port))))))
  2076.  
  2077. (defun irc-do-kill (user)
  2078.   "Usage: /KILL user
  2079.  
  2080. Forcibly remove a user from IRC.  ``user'' must be specified exactly as the
  2081. nickname for the user to be removed appears; no nickname expansion is done.
  2082. This command is reserved for IRC Operators."
  2083.   (interactive "sNuke which user? ")
  2084.   (or (string= "" user) (irc-send (concat "KILL " user))))
  2085.  
  2086. (defun irc-do-rehash (&optional cruft)
  2087.   "Usage: /REHASH
  2088.  
  2089. Force the server to which you are connected to reread its irc.conf file.
  2090. Arguments are ignored.  This command is only available to IRC Operators."
  2091.   ;; what a joy this was to write
  2092.   (interactive) (irc-send "REHASH"))
  2093.  
  2094. (defun irc-do-trace (server)
  2095.   "Usage: /TRACE [ server ]
  2096.  
  2097. Find the route from the server to which you are attached to 'server'; if the
  2098. server argument is not provided then the servers to which the current server
  2099. is directly connected are listed.  This command is only available to IRC
  2100. Operators."
  2101.   (interactive (list (if current-prefix-arg
  2102.                          (read-string "Trace route to which server? ")
  2103.                        "")))
  2104.   (string-match "^\\s *\\(\\S *\\).*$" server)
  2105.   (irc-send (concat "TRACE "
  2106.                     (substring server (match-beginning 1) (match-end 1))))
  2107.   (if (interactive-p) (irc-notice "")))
  2108.  
  2109. (defun irc-do-wall (msg)
  2110.   "Usage: /WALL message
  2111.  
  2112. Send 'message' to everyone on IRC.  This can only be done by IRC Operators."
  2113.   (interactive "sMessage for everyone: ")
  2114.   (if (and (not (interactive-p)) (string= "" msg))
  2115.       (call-interactively irc-do-wall)
  2116.     (if (string< "" msg)
  2117.         (irc-send (concat "WALL " msg)))))
  2118.  
  2119. ;; /command just for the client
  2120. (defun irc-do-send (slist)
  2121.   "Usage: /SEND [ sendlist | - ]
  2122.  
  2123. Set the default sendlist for IRC messages.  This is a comma separated list
  2124. of the intended recipient(s) of messages which do not have an explicit
  2125. sendlist.  '-' as an argument means to disable the default sendlist; every
  2126. message sent then must have an explicit recipient provided with the message.
  2127. Without any arguments this command just displays the current default sendlist.
  2128.  
  2129. Each item specified is checked to see whether you can send there; ambiguous
  2130. references to users are not allowed nor are channels which you are not on.
  2131. \"*\" is always allowed and means to send to the current channel.
  2132. If no item in the new list can be set then the sendlist is not changed."
  2133.   (interactive "sDefault recipient(s) for messages? ")
  2134.   ;; blast some whitespace
  2135.   (setq slist (irc-nuke-whitespace slist))
  2136.   (let (matches)
  2137.     ;; first the easiest case
  2138.     (if (string= "-" slist) (setq irc-default-to nil)
  2139.       (setq matches
  2140.             (delq nil                   ; more indentation fun.  can someone
  2141.                   (mapcar               ; recommend a good style manual?
  2142.                    (function
  2143.                     (lambda (arg)
  2144.                       (setq matches (irc-check-list irc-wholist arg))
  2145.                       (cond
  2146.                        ((string= arg "*") arg)
  2147.                        ((string= arg "0")
  2148.                         (irc-notice "You can't send to channel 0.") nil)
  2149.                        ((not (zerop (string-to-int arg)))
  2150.                         (if (string= arg irc-channel) arg
  2151.                           (irc-notice "You are not on channel %s." arg) nil))
  2152.                        ((= (length matches) 1) (car matches))
  2153.                        ((eq matches nil) ;; Let it in anyway; maybe hidden
  2154.                         ;(irc-notice "No names found to match \"%s\"." arg)
  2155.                         arg)
  2156.                        (t
  2157.                         (irc-notice "Ambiguous recipient \"%s\"; could be %s."
  2158.                                     arg (irc-subst-comma
  2159.                                         (mapconcat 'eval matches ", ") "or"))
  2160.                         nil)))) (irc-burst-comma slist))))
  2161.       (if matches
  2162.           (setq irc-default-to (concat (mapconcat 'eval matches ",") ";"))
  2163.         (or (string= "" slist)  ; only print the error if tried to set it.
  2164.             (irc-notice "(no matches -- sendlist not changed)"))))
  2165.     (if (not irc-default-to) (irc-notice "Your default sendlist is disabled.")
  2166.       (irc-insert
  2167.        "You are sending to %s."
  2168.        (irc-subst-comma
  2169.         (mapconcat 'eval
  2170.                    (irc-burst-comma
  2171.                     (substring irc-default-to 0
  2172.                                (1- (length irc-default-to)))) ", ") "and")))))
  2173.  
  2174. (defun irc-do-notify (notify)
  2175.   "Usage: /NOTIFY [ [+]event | -event ] [...]
  2176.  
  2177. Set the list of events to notify you about with a message.  Notification
  2178. is a one-line message inserted when someone causes that event to occur.
  2179. Events are added with +event or simply event; they are removed with -event.
  2180. + adds all supported events and - removes all supported events.  More than
  2181. one event can be specified in the arguments.  In case of conflict, the argument
  2182. which appears later overrides the argument with which it conflicts.
  2183.  
  2184. Currently supported by /NOTIFY are the 'join', 'nick' and 'topic' events.
  2185. Join happens whenever someone enters or leaves a channel which you are on.
  2186. Nick occurs when someone changes nicknames; recognition of this event is
  2187. currently limited to when the person making the change is on the same channel.
  2188. Topic is the result of someone changing the topic of the channel you are on."
  2189.   (interactive "sNotify for events: ")
  2190.   ;; die scurvy whitespace
  2191.   (setq notify (irc-nuke-whitespace notify))
  2192.   (let ((recog '(join nick topic)) (str notify) sym off event)
  2193.     (while (string< "" notify)
  2194.       ;; multiple args are okay.  we'll do one at a time.
  2195.       (setq str (substring notify 0 (string-match "\\s +\\|$" notify))
  2196.             notify (substring notify (match-end 0)))
  2197.       (string-match "^\\([-+]?\\)\\(.*\\)$" str)
  2198.       (setq off (string= "-" (substring str (match-beginning 1) (match-end 1)))
  2199.             event (substring str (match-beginning 2) (match-end 2))
  2200.             sym (if (string= "" event) nil
  2201.                   (car (delq nil              ; do some minor pattern matching
  2202.                              (mapcar          ; to find the intended event
  2203.                               (function
  2204.                                (lambda (arg)
  2205.                                  (if (string-match
  2206.                                       (concat "^" (regexp-quote event))
  2207.                                       (prin1-to-string arg))
  2208.                                      arg))) recog)))))
  2209.       (cond
  2210.        ((and (string= "" event) off) (setq irc-notifies nil))
  2211.        ;; the only way for this to happen and not the above is str == "+"
  2212.        ((string= "" event) (setq irc-notifies recog))
  2213.        ((null sym) (irc-notice "Notify: Unknown argument '%s'." event))
  2214.        (t (setq irc-notifies (if off (delq sym irc-notifies)
  2215.                                (if (not (memq sym irc-notifies))  ; avoid
  2216.                                    (cons sym irc-notifies)        ; redundancy
  2217.                                  irc-notifies))))))
  2218.     (if irc-notifies
  2219.         (irc-notice "Notification is currently enabled for %s."
  2220.                     (irc-subst-comma (mapconcat 'prin1-to-string irc-notifies
  2221.                                                 ", ") "and"))
  2222.       (irc-notice "Notification is currently disabled."))))
  2223.  
  2224. (defun irc-do-confirm (str)
  2225.   "Usage: /CONFIRM [ + | - ]
  2226.  
  2227. Turn on message confirmation with + or off with -.  Any other arguments or no
  2228. arguments just gives a message about the current setting.
  2229.  
  2230. Message confirmation is a line indicating to whom a message was sent.
  2231. Occasionally this will say that a message has been sent to someone who
  2232. was not present but another message soon after will set the record straight."
  2233.   (interactive "sSet confimation on (+) or off (-)? ")
  2234.   ;; grab the first arg
  2235.   (string-match "^\\s *\\(\\S *\\).*$" str)
  2236.   (setq str (substring str (match-beginning 1) (match-end 1)))
  2237.   (cond ((string= str "+") (setq irc-confirm t))
  2238.         ((string= str "-") (setq irc-confirm nil)))
  2239.   (irc-notice "Message confirmation is %s." (if irc-confirm "on" "off")))
  2240.  
  2241. (defun irc-do-ignore (user)
  2242.   "Usage: /IGNORE user
  2243.  
  2244. Ignore another user on IRC.  Any events by this person (except for WALL)
  2245. are not displayed.  With no arguments a list of all currently ignored people.
  2246.  
  2247. IRC-mode will track the ignored user across nickname changes if it notices the
  2248. change.  If the user sends either a private message or an invitation to you
  2249. while being ignored a message will be sent to that person saying \"You are
  2250. being ignored.\"  To undo this command, use /UNIGNORE."
  2251.   (interactive '(""))
  2252.   (if (or (interactive-p) (not (string= "" user)))
  2253.       (setq user (irc-read-user "Ignore which user? " user)))
  2254.   (if (string= "" user)
  2255.       (if irc-ignores
  2256.           (irc-notice "You are currently ignoring %s."
  2257.                       (irc-subst-comma (mapconcat 'eval irc-ignores ", ")
  2258.                                        "and"))
  2259.         (irc-notice "You are not ignoring anyone."))
  2260.     (irc-notice "You are now ignoring %s." user)
  2261.     (irc-maintain-list 'irc-ignores user 'add)))
  2262.  
  2263. (defun irc-do-unignore (user)
  2264.   "Usage: /UNIGNORE user | + | -
  2265.  
  2266. Stop ignoring a user who has been /IGNOREd.  The special arguments + or -
  2267. mean to stop ignoring everyone who is being ignored."
  2268.   (interactive '(""))
  2269.   (if (null irc-ignores)
  2270.       (irc-notice "You are not ignoring anyone.")
  2271.     (if (string-match "^\\s *\\([-+]\\)\\(\\s |$\\)" user)
  2272.         (progn (setq irc-ignores nil)
  2273.                (irc-notice "You are no longer ignoring anyone."))
  2274.       (setq user (irc-read-user "Stop ignoring whom? " user irc-ignores))
  2275.       (if (string= "" user) ()
  2276.         (irc-notice "You are no longer ignoring %s." user)
  2277.         (irc-maintain-list 'irc-ignores user 'remove)))))
  2278.  
  2279. (defun irc-do-signal (sigs)
  2280.   "Usage: /SIGNAL [ + | - | [+]event | -event ] [...]
  2281.  
  2282. Set the events which will get signals (aks bells or dings) when they
  2283. occur.  Events supported are:
  2284.  
  2285.   private -- private messages      join   -- channel changes
  2286.   public  -- public messages       topic  -- channel topic changes
  2287.   wall    -- broadcast messages    nick   -- nickname changes
  2288.   invite  -- invitations
  2289.  
  2290. Without any arguments /SIGNAL simply prints a message about what signals
  2291. are currently enabled.  With event or +event turn on all signalling for that
  2292. event.  Remove all signals for an event with -event.  /SIGNAL + or /SIGNAL -
  2293. adds or removes all signals respectively.  Multiple arguments are accepted;
  2294. later ones take precedence over the ones which came before them.  For example,
  2295. '/SIGNAL - +w +i' would turn off all signals and then turn on signalling only
  2296. for wall messages and invitations."
  2297.   (interactive "sSet signal: ")
  2298.   ;; blow some whitespace away.  curiously this doesn't work correctly in debug
  2299.   (setq sigs (irc-nuke-whitespace sigs))
  2300.   (let ((recog '(private public wall invite join nick topic)) str sym
  2301.         on off event)
  2302.     (while (string< "" sigs)
  2303.       ;; take one argument at a time
  2304.       (setq str  (substring sigs 0 (string-match "\\s +\\|$" sigs))
  2305.             sigs (substring sigs (match-end 0)))
  2306.       (string-match "^\\([-+]?\\)\\(.*\\)$" str)
  2307.       (setq off (string= "-" (substring str (match-beginning 1) (match-end 1)))
  2308.             event (substring str (match-beginning 2) (match-end 2))
  2309.             sym (if (string= "" event) nil
  2310.                   (car (delq nil
  2311.                              (mapcar
  2312.                               (function
  2313.                                (lambda (arg)
  2314.                                  (if (string-match
  2315.                                       (concat "^" (regexp-quote event))
  2316.                                       (prin1-to-string arg))
  2317.                                      arg))) recog)))))
  2318.       (cond
  2319.        ((and (string= "" event) off)
  2320.         (setq irc-signals (mapcar 'list recog)))
  2321.        ((string= "" event)
  2322.         (setq irc-signals (mapcar
  2323.                            (function (lambda (arg) (list arg t))) recog)))
  2324.        ((null sym) (irc-notice "Signal: Unknown argument '%s'." event))
  2325.        (t (if off (setcdr (assoc sym irc-signals) nil)
  2326.             (setcdr (assoc sym irc-signals) '(t))))))
  2327.     (setq on (delq nil
  2328.                    (mapcar        ; test against t because I have plans
  2329.                     (function     ; to couple users and events
  2330.                      (lambda (arg)
  2331.                        (if (eq (nth 1 (assoc arg irc-signals)) t)
  2332.                            arg))) recog)))
  2333.     (if on
  2334.       (irc-notice (concat "Signalling is enabled for "
  2335.                           (irc-subst-comma
  2336.                            (mapconcat 'prin1-to-string on ", ") "and") "."))
  2337.       (irc-notice "All signalling is currently disabled."))))
  2338.  
  2339. (defun irc-do-stamp (stamp)
  2340.   "Usage: /STAMP [ + | - | [+]event | -event | interval ] [...]
  2341.  
  2342. Set time-stamping for IRC.  + means to turn it on for all messages from users
  2343. and - means to turn it off for them.  +event or just event will turn it on for
  2344. that class of message and -event means to disable it for those messages.  An
  2345. integer interval means to insert a message indicating the time every N minutes,
  2346. where N is the interval.  With no arguments simply insert a message indicating
  2347. the current time-stamps.
  2348.  
  2349. The current time in HH:MM format can appear two different ways in IRC.  One is
  2350. to have it associate with 'event'; two events, 'private' and 'public' messages,
  2351. are supported this way.  The other is to have it as a stand-alone message
  2352. indicating the current time.  Both can be very useful in noting when someone
  2353. actually sent you a message or when another event happened if you happen to be
  2354. away for a while.  The accuracy of the interval timer is currently limited to
  2355. 0-2 minutes beyond the interval if display-time is not running; accuracy is
  2356. greatly improved if it is.  It can be turned off by setting the interval to 0."
  2357.   (interactive "sSet time-stamp: ")
  2358.   ;; whee.  napalm would feel particularly good here.
  2359.   (setq stamp (irc-nuke-whitespace stamp))
  2360.   (let (str sym event off)
  2361.     (while (string< "" stamp)
  2362.       ;; as the args go marching one by one the last one stopped ... <ahem>
  2363.       (setq str   (substring stamp 0 (string-match "\\s +\\|$" stamp))
  2364.             stamp (substring stamp (match-end 0)))
  2365.       (string-match "^\\([-+]?\\)\\(.*\\)$" str)
  2366.       (setq off (string= "-" (substring str (match-beginning 1) (match-end 1)))
  2367.             event (substring str (match-beginning 2) (match-end 2))
  2368.             sym (cond ((string= "" event) nil)
  2369.                       ((string-match (concat "^" (regexp-quote event))
  2370.                                      "private") 'private)
  2371.                       ((string-match (concat "^" (regexp-quote event))
  2372.                                      "public")  'public)
  2373.                       ((natnump (car (read-from-string event)))
  2374.                        (car (read-from-string event)))))
  2375.       ;; the following cond is really what sets eveything
  2376.       (cond ((and (string= "" event) off) (setq irc-message-stamp nil))
  2377.             ((string= "" event) (setq irc-message-stamp t))
  2378.             ((null sym) (irc-notice "Stamp: Unknown argument '%s'." event))
  2379.             ((natnump sym) (setq irc-time-stamp sym))
  2380.             (off (setq irc-message-stamp
  2381.                        (car (delq sym (if (eq irc-message-stamp t)
  2382.                                           '(private public)
  2383.                                         (list irc-message-stamp))))))
  2384.             (t (setq irc-message-stamp
  2385.                      (cond ((null irc-message-stamp) sym)
  2386.                            ((or (eq irc-message-stamp t)
  2387.                                 (eq irc-message-stamp sym)) irc-message-stamp)
  2388.                            (t)))))))
  2389.   (irc-notice "%s messages get time-stamps.%s"
  2390.               (cond ((eq irc-message-stamp t) "Private and public")
  2391.                     ((null irc-message-stamp) "No")
  2392.                     (t (capitalize (prin1-to-string irc-message-stamp))))
  2393.               (if (zerop irc-time-stamp) ""
  2394.                 (irc-normalise-time-stamp)
  2395.                 (format "  The time interval is %d minutes." irc-time-stamp))))
  2396.  
  2397. (defun irc-do-alias (alias)
  2398.   "Usage: /ALIAS [ alias [ command [ args for command ]]]
  2399.  
  2400. Allow 'alias' to be equivalent to 'command'.
  2401. For example, \"/ALIAS tf time tut.fi\" will make typing \"/tf\" be equivalent
  2402. to having issued the command \"/time tut.fi\".  Aliases can only be made
  2403. to existing commands not other aliases.  They are also only recognized when
  2404. in the command name position of a line.  If given with no arguments then
  2405. all aliases are displayed; if given with just an alias name then the alias
  2406. with that name will be shown.  Aliases can be removed with /UNALIAS."
  2407.   (interactive "sName for alias? ")
  2408.   (if (interactive-p)
  2409.       (setq alias (concat alias " "
  2410.                           (read-string (format "Alias '%s' to which command? "
  2411.                                                alias)))))
  2412.   (setq alias (irc-nuke-whitespace alias))
  2413.   (string-match "^/?\\(\\S *\\)\\s */?\\(\\S *\\)\\s *\\(.*\\)$" alias)
  2414.   (let ((new (upcase (substring alias (match-beginning 1) (match-end 1))))
  2415.         (cmd (upcase (substring alias (match-beginning 2) (match-end 2))))
  2416.         (arg         (substring alias (match-beginning 3) (match-end 3)))
  2417.         match)
  2418.     (cond
  2419.      ((string= "" new)
  2420.       (let ((aliases irc-alias-alist))
  2421.         (while aliases
  2422.           (irc-notice "\"/%s\" is aliased to \"/%s\"."
  2423.                       (car (car aliases)) (cdr (car aliases)))
  2424.           (setq aliases (cdr aliases)))))
  2425.      ((string= "" cmd)
  2426.       (let ((alias (assoc new irc-alias-alist)))
  2427.         (if alias
  2428.             (irc-notice "\"/%s\" is aliased to \"/%s\"."
  2429.                         (car alias) (cdr alias))
  2430.           ;; this could possibly have done some matching to see whether
  2431.           ;; just an abbrev was being given, but we'll just take it as given
  2432.           (irc-notice "\"/%s\" is not aliased." new))))
  2433.      (t  ; okay, we've got at least a command.  let's try and make this as
  2434.          ; painless as possible. 
  2435.       (setq match
  2436.             (irc-check-list (mapcar 'car (append irc-command-alist
  2437.                                                  (if irc-operator
  2438.                                                      irc-operator-alist)))
  2439.                             cmd 'start-only))
  2440.       ;; try not to confuse a regular user with commands he couldn't use
  2441.       ;; anyway, but let him at it if that's what he really wants.  it'll
  2442.       ;; just come through as an error from the server in the long run ...
  2443.       (if (and (not match) (not irc-operator))
  2444.           (setq match (irc-check-list (mapcar 'car irc-operator) cmd t)))
  2445.       (if (/= (length match) 1)
  2446.           (if match
  2447.               (irc-notice "'/%s' is an ambiguous command.  Could be %s." cmd
  2448.                           (irc-subst-comma (mapconcat 'eval match ", ") "or"))
  2449.             (irc-notice "Command not found: '/%s'." cmd))
  2450.         (irc-change-alias new (concat (downcase (car match))
  2451.                                         ; no trailing space if no arg
  2452.                                       (if (string= "" arg) "" " ") arg) 'add)
  2453.         (irc-notice "\"/%s\" has been aliased to \"/%s\"." new 
  2454.                     (cdr (assoc new irc-alias-alist))))))))
  2455.  
  2456. (defun irc-do-unalias (alias)
  2457.   "Usage: /UNALIAS alias
  2458.  
  2459. Remove the 'alias' for a command."
  2460.   ;; well, that's a pretty dull doc string.
  2461.   (interactive (let ((completion-ignore-case t))
  2462.                  (list (completing-read "Unalias which command? "
  2463.                                         (cons '("" . "") irc-alias-alist)
  2464.                                         nil t))))
  2465.   (string-match "^\\s *\\(\\S *\\)\\s *$" alias)
  2466.   (setq alias (substring alias (match-beginning 1) (match-end 1)))
  2467.   (if (string= "" alias)
  2468.       (if (not (interactive-p))
  2469.           (call-interactively 'irc-do-unalias))
  2470.     (let ((match (irc-check-list (mapcar 'car irc-alias-alist) alias t)))
  2471.       (if (/= (length match) 1)
  2472.           (if match
  2473.               (irc-notice "'%s' is an ambiguous alias.  Could be %s."
  2474.                           (upcase alias)
  2475.                           (irc-subst-comma (mapconcat 'eval match ", ") "or"))
  2476.             (irc-notice "No alias found to match '%s'." (upcase alias)))
  2477.         (irc-change-alias (car match) nil 'remove)
  2478.         (irc-notice "'%s' is no longer aliased." (car match))))))
  2479.   
  2480. (defun irc-do-help (topic)
  2481.   "Usage: /HELP topic
  2482.  
  2483. Get the documentation for 'command'.  If no command is given then a list
  2484. of the possible topics is shown.  Note that commands for IRC Operators will
  2485. not appear in the help topics when not an IRC Operator."
  2486.   (interactive "sHelp for which command? ")
  2487.   (string-match "^\\s *\\(\\S *\\)\\s *$" topic)
  2488.   (setq topic (substring topic (match-beginning 1) (match-end 1)))
  2489.   (if (string= topic "")
  2490.       (let ((str "Help is available for the following IRC-mode commands:\n")
  2491.             (topics (mapcar 'car
  2492.                             (append irc-command-alist
  2493.                                     (if irc-operator irc-operator-alist)))))
  2494.         (while topics
  2495.           (setq str
  2496.                 (concat str
  2497.                         (format "\n%14s%14s%14s%14s%14s"
  2498.                                 (nth 0 topics)
  2499.                                 (or (nth 1 topics) "") (or (nth 2 topics) "")
  2500.                                 (or (nth 3 topics) "") (or (nth 4 topics) "")))
  2501.                 topics (nthcdr 5 topics)))
  2502.         (with-output-to-temp-buffer "*Help*" (princ str)))
  2503.     (let ((match (irc-check-list (mapcar 'car (append irc-command-alist
  2504.                                                       (if irc-operator
  2505.                                                           irc-operator-alist)))
  2506.                                  topic 'start-only)))
  2507.       (if (and (not match) (not irc-operator))
  2508.           (setq match  
  2509.                 (irc-check-list (mapcar 'car irc-operator-alist) topic t)))
  2510.       (if (/= (length match) 1)
  2511.           (if match
  2512.               (irc-notice "Ambiguous help topic '%s'; could be %s."
  2513.                           (upcase topic)
  2514.                           (irc-subst-comma (mapconcat 'eval match ", ") "or"))
  2515.             (irc-notice "No help is available for '%s'." (upcase topic)))
  2516.         (setq match (car match))
  2517.         (with-output-to-temp-buffer "*Help*"
  2518.           (princ (documentation
  2519.                   (intern-soft
  2520.                    (concat "irc-do-"
  2521.                            (cdr (assoc match
  2522.                                        (if (assoc match irc-command-alist)
  2523.                                            irc-command-alist
  2524.                                          irc-operator-alist))))))))))))
  2525.  
  2526. ;; miscellaneous irc-* commands
  2527. (defun irc-truncate-buffer (size)
  2528.   ;; Remove as many lines from the beginning of the buffer as is necessary
  2529.   ;; to get it under SIZE characters.  This function is used by irc-mode
  2530.   ;; to prevent an irc-session from consuming gross amounts of space.  A size
  2531.   ;; of nil means to use a presumably safe ceiling of 8Meg - 1K.
  2532.   (or size (setq size (* 1024 1023 8)))
  2533.   (if (< (buffer-size) size) ()
  2534.     (save-excursion
  2535.       ;; first go to the lowest point posssible that would do it
  2536.       (goto-char (- (point-max) size))
  2537.       ;; get to the end of this line
  2538.       (end-of-line)
  2539.       (if (< (point) irc-mark)
  2540.           ;; just to make sure we don't toast pending input
  2541.           (delete-region 1 (1+ (point)))
  2542.         (message "Warning: %s exceeding %s characters.  Couldn't truncate."
  2543.                  (buffer-name (current-buffer)) size)))))
  2544.  
  2545. (defun irc-display (buffer &optional force)
  2546.   ;; There are a hundred and one ways, at least, to handle all of the popping
  2547.   ;; up of irc buffers.  this is sort of simple right now, but hopefully
  2548.   ;; extensible if people start saying they want something different
  2549.   (let* ((begin (selected-window))
  2550.          (start (next-window begin 'no-mini))
  2551.          (next  start)
  2552.          (pop-up-windows t)
  2553.          ircwins allwins done scroll split)
  2554.     (while (not done)
  2555.       (select-window next)
  2556.       ;; the bowels of window/buffer manipulation are not fun.  i would
  2557.       ;; not be surprised if there are still latent bugs flying around, but
  2558.       ;; i hope i have tested it enough to prove it sufficiently robust.
  2559.       (set-buffer (window-buffer (selected-window)))
  2560.       (setq allwins (cons (list (current-buffer)
  2561.                                 (if (eq major-mode 'irc-mode) irc-mark)
  2562.                                 (point))
  2563.                           allwins)
  2564.             ircwins (if (eq (current-buffer) buffer)
  2565.                         (cons (cons (selected-window) (>= (point) irc-mark))
  2566.                               ircwins)
  2567.                       ircwins)
  2568.             next    (next-window next 'no-mini)
  2569.             done    (eq next start)))
  2570.     ;; the most common case.
  2571.     (if (= (length ircwins) 1)
  2572.         (setq scroll (select-window (car (car ircwins)))
  2573.               split  (and (= (length allwins) 1) (< (point) irc-mark)))
  2574.       (set-buffer buffer)
  2575.       (if ircwins
  2576.           (if (rassq t ircwins)
  2577.               (setq scroll (select-window (car (rassq t ircwins))))
  2578.             (select-window (car (nth (1- (length ircwins)) ircwins)))
  2579.             (setq scroll 'jump-max))
  2580.         (if (and irc-pop-ratio force)
  2581.             (if (= (length allwins) 1)
  2582.                 (setq split t)
  2583.               (select-window (get-largest-window))
  2584.               (if (> (window-height) (/ (screen-height) 2))
  2585.                   (setq split t)
  2586.                 (select-window begin)
  2587.                 (display-buffer buffer)
  2588.                 (select-window (get-buffer-window buffer))
  2589.                 (setq scroll 'jump-max))))))
  2590.     (if (and split (natnump irc-pop-ratio) (> irc-pop-ratio 1))
  2591.         (progn
  2592.           (setq scroll 'jump-max)
  2593.           (split-window nil (- (window-height)
  2594.                                (/ (window-height) irc-pop-ratio)))
  2595.           (other-window 1)
  2596.           (set-window-buffer (selected-window) buffer)))
  2597.     (if scroll
  2598.         (progn (if (and (eq scroll 'jump-max) (< (point) irc-mark))
  2599.                    (progn (goto-char irc-mark)
  2600.                           (set-window-start (selected-window) 0)))
  2601.                (or (pos-visible-in-window-p) (recenter -1))))
  2602.     (select-window begin)))
  2603.  
  2604. (defun irc-check-time ()
  2605.   ;; Check to see whether it is time to insert a current-time message
  2606.   (let* ((time (irc-get-time))
  2607.          (old-minute (string-to-int (substring irc-last-time 3)))
  2608.          (new-minute (string-to-int (substring time 3))))
  2609.   (if (zerop irc-time-stamp) ()
  2610.     ;; check the time sentinel
  2611.     (if (string= irc-last-time time) ()
  2612.       ;; time has gone stomping on by ...
  2613.       (setq new-minute (+ new-minute (if (< new-minute old-minute) 60 0))
  2614.             irc-last-time time
  2615.             irc-total-time (+ irc-total-time (- new-minute old-minute)))
  2616.       (if (< (- irc-total-time irc-last-stamp) irc-time-stamp) ()
  2617.         (setq irc-last-stamp (+ irc-last-stamp irc-time-stamp))
  2618.         ;; it's time for a new message
  2619.         (irc-notice "*** It is now %s ***" time)
  2620.         ;; might as well check to see if display-time is running
  2621.         (irc-wrap-display-time))))))
  2622.  
  2623. (defun irc-wrap-display-time ()
  2624.   "Set up a wrapper around the display-time-filter to hopefully provide a
  2625. little better accuracy for the time stamps."
  2626.   (if (and (fboundp 'display-time-filter)
  2627.            (not (fboundp 'original-display-time-filter)))
  2628.       (progn
  2629.         (fset 'original-display-time-filter
  2630.               (symbol-function 'display-time-filter))
  2631.         ;; a nested defun seems to do funny things to the byte-compiler, so
  2632.         ;; instead we find a way around it.
  2633.         (fset 'display-time-filter
  2634.               (function
  2635.                (lambda (proc str)
  2636.                  "
  2637. The filter for the display-time-process.  This function has been modified
  2638. for IRC-mode to call irc-check-time before calling the original
  2639. display-time-filter."
  2640.                  (save-excursion
  2641.                    (let (buf (procs irc-processes))
  2642.                      (while procs
  2643.                        (if (setq buf
  2644.                                  (buffer-name (process-buffer (car procs))))
  2645.                            (progn
  2646.                              (set-buffer buf)
  2647.                              (save-excursion (irc-check-time))))
  2648.                        (setq procs (cdr procs)))))
  2649.                  (original-display-time-filter proc str)))))))
  2650.  
  2651. (defun irc-read-passwd (&optional prompt)
  2652.   ;; Allow user to type a string without it showing.  Returns string.
  2653.   ;; If optional PROMPT non-nil, use it as the prompt string in the minibuffer.
  2654.   ;; this is based on a similar function in telnet.el
  2655.   ;; the major drawback is that while being prompted for a password
  2656.   ;; it stays in this routine until C-g, RET or LFD is typed.
  2657.   (let ((passwd "") (echo-keystrokes 0) (cursor-in-echo-area t) char)
  2658.     (if prompt (message "%s" prompt))
  2659.     (while (not (or (= (setq char (read-char)) 13) (= char 10)))
  2660.       ;; naughty bit.  take C-h to mean DEL.
  2661.       (if (or (= char 8) (= char 127))
  2662.           (if (> (length passwd) 0)
  2663.               (setq passwd (substring passwd 0 (1- (length passwd)))))
  2664.         (setq passwd (concat passwd (char-to-string char))))
  2665.       (if prompt (message (concat prompt (make-string (length passwd) ?*)))))
  2666.     (if prompt (message ""))
  2667.     passwd))
  2668.  
  2669. (defun irc-read-user (prompt user &optional list)
  2670.   ;; Prompting with PROMPT, read an IRC nickname from the minibuffer.
  2671.   ;; Second argument USER is a string which is checked for a non-ambiguous
  2672.   ;; match before the minibuffer read is done.  Optional third argument LIST
  2673.   ;; is a list to use for checking rather than the irc-wholist.
  2674.   ;; It returns either the name of a user or an empty string (\"\").  This
  2675.   ;; routine will allow for a non-matching name to be returned.
  2676.   (string-match "^\\s *\\(\\S *\\)" user) ; just want one name
  2677.   (setq user (substring user (match-beginning 1) (match-end 1)))
  2678.   (let ((completion-ignore-case t) (list (if list list irc-wholist))
  2679.         done null-okay match)
  2680.     (while (not done)
  2681.       (setq match (if (and (string< "" user) (not irc-noglob))
  2682.                       (irc-check-list list user)))
  2683.       ;; only do read if (a) ambiguous user or (b) no user but first pass
  2684.       (if (or (and (not (string= "" user)) (< (length match) 2))
  2685.               (and (string= "" user) null-okay))
  2686.           (setq done t)
  2687.         (setq user
  2688.               (completing-read (format "%s%s"
  2689.                                        (if (string= "" user) ""
  2690.                                          (concat "'" user "' is ambiguous.  "))
  2691.                                        prompt)
  2692.                                (mapcar 'list list) nil nil user)
  2693.               null-okay t)))
  2694.     (or match (irc-maintain-list 'irc-wholist user 'add))
  2695.     (or (car match) user)))
  2696.  
  2697. (defun irc-nuke-whitespace (str)
  2698.   ;; Returns argument with surrounding whitespace removed.
  2699.   ;; i hate stupid extra spaces when parsing
  2700.   (string-match "^\\s *" str)
  2701.   (substring str (match-end 0) (string-match "\\s *$" str)))
  2702.  
  2703. (defun irc-subst-comma (str newsep)
  2704.   ;; Return the string formed by substituting for the last ", " in STR
  2705.   ;; the string NEWSEP followed by a space.  For example:
  2706.   ;;  (irc-subst-comma "1, 2, 3" "or") => "1, 2 or 3"
  2707.   ;; This function is especially designed for making message from irc-mode
  2708.   ;; more grammatically correct and the strings which it operates on should
  2709.   ;; be carefully chosen so as to avoid possibly blowing away a comma that
  2710.   ;; really wasn't separating elements in a list."
  2711.   ;; did you know that example up there can't appear starting in column 0
  2712.   ;; without screwing up lisp-indent-line?  (when it was in the doc string)
  2713.   (if (string-match ", [^,]*$" str)
  2714.       (concat (substring str 0 (match-beginning 0)) " " newsep
  2715.               (substring str (1+ (match-beginning 0))))
  2716.     str))
  2717.  
  2718. (defun irc-get-time ()
  2719.   ;; Return the hour and minutes of the current time in the form "HH:MM".
  2720.   (substring (current-time-string) 11 16))
  2721.  
  2722. (defun irc-toggle-glob (arg)
  2723.   "Toggle globbing.  With positive ARG, enable globbing.  Negative disables.
  2724. ARG is the prefix argument when called interactively."
  2725.   (interactive "P")
  2726.   (setq arg (if arg (prefix-numeric-value arg))
  2727.         irc-noglob (cond ((natnump arg))
  2728.                          ((integerp arg) nil)
  2729.                          ((not irc-noglob))))
  2730.   (message "IRC-mode globbing is now %s."
  2731.            (if irc-noglob "disabled" "enabled")))
  2732.  
  2733. (defun irc-raw-mode (arg)
  2734.   "Toggle IRC raw mode.
  2735. This displays low level interaction between the client and the server and is
  2736. very useful when debugging a problem.  With prefix argument ARG positive, turn
  2737. it on; negative ARG means disable it.  With ARG of zero, turn on a similar
  2738. mode, \"Full\" mode.  When this latter is enabled then the server messages will
  2739. still be handed through the filters, otherwise they are not.  The ``This is
  2740. a bug'' messages are disabled when either mode is on.   No argument means to
  2741. toggle either mode off if enabled, or to turn on raw."
  2742.   (interactive "P")
  2743.   (setq arg (if arg (prefix-numeric-value arg))
  2744.         irc-raw-mode (cond ((zerop arg) " Full")
  2745.                            ((natnump arg) " Raw")
  2746.                            ((integerp arg) nil)
  2747.                            ((not irc-raw-mode) " Raw")))
  2748.   (set-buffer-modified-p (buffer-modified-p)))
  2749.  
  2750. (defun irc-change-alias (alias cmd add)
  2751.   "Modify ALIAS for CMD in the irc-alias-alist.  ADD non-nil means to put the
  2752. alias in the list, nil (or the symbol `remove') means to clear it.  This
  2753. function does no hand-holding like /ALIAS; its intended use is in
  2754. irc-mode-hook."
  2755.   (let ((entry (assoc (upcase alias) irc-alias-alist)))
  2756.     (if (or (null add) (eq add 'remove))
  2757.         (setq irc-alias-alist (delq entry irc-alias-alist))
  2758.       (if entry (setcdr entry cmd)
  2759.         (setq irc-alias-alist
  2760.               (cons (cons (upcase alias) cmd) irc-alias-alist))))))
  2761.  
  2762. (defun irc-signal (user event)
  2763.   ;; Return t if a ding should be issued for a USER/EVENT pair.
  2764.   ;; Currently only the event part of things is supported by /SIGNAL.
  2765.   (let ((signal (cdr (assoc event irc-signals))))
  2766.     (or (memq t signal) (member-general user signal 'string=)
  2767.         (member-general user (cdr (assoc 'user irc-signals)) 'string=))))
  2768.  
  2769. (defun irc-check-list (list item &optional start-only)
  2770.   ;; See if LIST has string ITEM.  Returns a list of possible matches.  The
  2771.   ;; list returned is based on the following precedence rules:  if there is an
  2772.   ;; exact match, it is returned.  If there are any strings in the list whose
  2773.   ;; beginning match the item, they are returned.  If that fails and optional
  2774.   ;; argument START-ONLY is missing or nil, strings which have the item match
  2775.   ;; anywhere are returned.  As a last resort, nil is returned.
  2776.   ;; This function is not case-sensitive.
  2777.   (let (return (case-fold-search t) (item (regexp-quote item)))
  2778.     (if (setq return
  2779.               (delq nil                         ; whole words
  2780.                     (mapcar (function   
  2781.                              (lambda (arg)
  2782.                                (if (string-match (concat "^" item "$") arg)
  2783.                                    arg))) list)))
  2784.         return
  2785.       (if (setq return
  2786.                 (delq nil                       ; beginnings
  2787.                       (mapcar (function
  2788.                                (lambda (arg)
  2789.                                  (if (string-match (concat "^" item) arg)
  2790.                                      arg))) list)))
  2791.           return
  2792.         (if start-only
  2793.             nil
  2794.           (delq nil
  2795.                 (mapcar (function               ; anywhere
  2796.                          (lambda (arg)        
  2797.                            (if (string-match (concat "." item) arg) arg)))
  2798.                         list)))))))
  2799.  
  2800. (defun irc-maintain-list (list item func)
  2801.   ;; Maintain a LIST of strings by adding or removing string ITEM.
  2802.   ;; Third argument FUNC should be 'add or t or to make sure the item is in
  2803.   ;; the list or 'remove or nil to make sure item is out of the list.
  2804.   (cond
  2805.    ((memq func '(add t))
  2806.     (if (member-general item (eval list) 'string=) ()
  2807.       ;; sigh.  with random adding of names via sending messages to people
  2808.       ;; that irc-mode doesn't know about a name can be here in the wrong
  2809.       ;; case.  this has the potential to screw things up big so we'll ditch
  2810.       ;; the old one in favour of whatever is being added.
  2811.       (let* ((case-fold-search t)
  2812.              (old (delq nil
  2813.                         (mapcar
  2814.                          (function
  2815.                           (lambda (arg)
  2816.                             (if (string-match (concat "^" (regexp-quote item)
  2817.                                                       "$") arg)
  2818.                                 arg))) (eval list)))))
  2819.         (while old
  2820.           (irc-maintain-list list (car old) 'remove)
  2821.           (setq old (cdr old)))
  2822.         (set list (cons item (eval list))))))
  2823.    ((memq func '(remove nil))
  2824.     (set list  
  2825.          (delq nil (mapcar (function (lambda (arg)
  2826.                                        (if (string= item arg) nil arg)))
  2827.                            (eval list)))))))
  2828.  
  2829. (defun irc-burst-comma (str)
  2830.   ;; Take a comma or space separated STR and return a list of its elements.
  2831.   ;; Example: "1, 2,3,4,  6  7" becomes the list ("7" "6" "4" "3" "2" "1")
  2832.   (let (list sub (beg 0))
  2833.     (string-match "" str)
  2834.     (while (string-match ",+\\|\\s +\\|,+\\s +" str beg)
  2835.       (if (not (string= (setq sub (substring str beg (match-beginning 0))) ""))
  2836.           (setq list (cons sub list)))
  2837.       (setq beg (match-end 0)))
  2838.     (if (/= (length str) beg) (cons (substring str beg) list) list)))
  2839.  
  2840. ;; miscellaneous other commands (usually from other sources)
  2841.  
  2842. ;; this makes up for not being able to provide a :test to memq.
  2843. ;; member-general by Bard Bloom <bard@theory.lcs.mit.com>
  2844. (defun member-general (x l comparison)
  2845.   "Is X a member of L under COMPARISON?"
  2846.   (let ((not-found t))
  2847.     (while (and l not-found)
  2848.       (setq not-found (not (funcall comparison x (car l)))
  2849.             l         (cdr-safe l)))
  2850.     (not not-found)))
  2851.  
  2852. ;; swiped from minibuf.el, but made exclusive to * Minibuf-n*.
  2853. (defun minibuffer-message (format &rest args)
  2854.   "Print a temporary message at the end of the Minibuffer.
  2855. After 2 seconds or when a key is typed, erase it."
  2856.   (if (zerop (minibuffer-depth)) (apply 'message format args)
  2857.     (let (p)
  2858.       (save-excursion
  2859.         (set-buffer (concat " *Minibuf-" (1- (minibuffer-depth)) "*"))
  2860.         (unwind-protect
  2861.             (progn
  2862.               (setq p (goto-char (point-max)))
  2863.               (insert (apply 'format format args))
  2864.               (sit-for 2))
  2865.           (delete-region p (point-max)))))))
  2866.  
  2867. ;; Original from:
  2868. ;; Date: Wed, 19 Jul 89 17:13:53 PDT
  2869. ;; From: Bill Trost <trost%tekcrl.labs.tek.com@RELAY.CS.NET>
  2870. ;; modified for use by IRC-mode, tale@turing.cs.rpi.edu Sun Feb  4 1990
  2871. (defun irc-finger (who)
  2872.   "Display information about users."
  2873.   (interactive "sFinger: ")
  2874.   (setq who (cond ((null who) "@localhost")
  2875.                   ((not (string-match "@" who)) (concat who "@localhost"))
  2876.                   (who)))
  2877.   (condition-case FINGER
  2878.       (let ((stream
  2879.              (open-network-stream "finger" (current-buffer)
  2880.                                   (substring who (1+ (string-match "@" who)))
  2881.                                   "finger")))
  2882.         (set-process-filter stream 'irc-finger-filter)
  2883.         (set-process-sentinel stream 'ignore)
  2884.         (send-string stream
  2885.                      (concat (substring who 0 (match-beginning 0)) "\n")))
  2886.     (error (irc-notice "Couldn't finger %s." who))))
  2887.  
  2888. (defun irc-find-to (str &optional explicit)
  2889.   ;; Find the part of STRING that will be interpreted as the sendlist.  If no
  2890.   ;; explicit list is found, irc-default-to is returned.  The string returned
  2891.   ;; is either : or ; terminated.
  2892.   ;; If optional EXPLICIT is non-nil, then return t if a sendlist was
  2893.   ;; explicitly specified, nil if the sendlist was implicit.
  2894.   (let ((matched (string-match "^[A-Za-z0-9-_|{,*]*[;:]" str)))
  2895.     (if matched (if explicit t (substring str 0 (match-end 0)))
  2896.       (if explicit nil irc-default-to))))
  2897.  
  2898. (defun irc-find-message (string)
  2899.   ;; Find the message that IRC will see if STR were sent.  For messages
  2900.   ;; sent with explicit lists, this is everything following the colon or
  2901.   ;; semi-colon.  For everything else, it is just the string.
  2902.   (substring string (length (irc-find-to string))))
  2903.  
  2904. (defun irc-normalise-time-stamp ()
  2905.   ;; Set irc-last-stamp so nice subdivisions of the hour are returned 
  2906.   ;; by irc-check-time
  2907.   (setq irc-total-time (string-to-int (substring irc-last-time 3))
  2908.         ;; this next bit of messiness just ups irc-last-stamp
  2909.         ;; in an effort to make nice numbers out of the time
  2910.         ;; stamps -- ie, if the time is now 13:53 with an interval
  2911.         ;; of 15 minutes, this makes it 13:45
  2912.         irc-last-stamp 0
  2913.         irc-last-stamp
  2914.         (if (zerop irc-time-stamp) 0
  2915.           (while (< (+ irc-last-stamp irc-time-stamp) irc-total-time)
  2916.             (setq irc-last-stamp (+ irc-last-stamp irc-time-stamp)))
  2917.           irc-last-stamp)))
  2918.  
  2919. ;; functions for the irc-*-history lists.  in v19 most of this is superceded
  2920. ;; by history.el
  2921. (defun irc-add-to-hist (list str)
  2922.   ;; At the head of LIST append STR.  LIST should be the name of the list.
  2923.   ;; The length of the list is limited to irc-max-history size.
  2924.   (set list (append (list str) (eval list)))
  2925.   (and (> (length (eval list)) irc-max-history)
  2926.        (set list (reverse (cdr (reverse (eval list)))))))
  2927.  
  2928. (defun irc-fetch-history (list index order)
  2929.   ;; Retrieve an entry from LIST, working from INDEX in direction ORDER.  LIST
  2930.   ;; should be quoted for message purposes.  INDEX should be quoted so it can
  2931.   ;; be maintained.  ORDER non-nil means to use previous entry, unless it is
  2932.   ;; the symbol 'next to get the next entry or a number to get an absolute
  2933.   ;; reference.  ORDER nil is equivalent to 'next.
  2934.   (let (str (eind (eval index)) (elist (eval list)))
  2935.     (cond
  2936.      ((numberp order)
  2937.       (setq str (nth order elist))
  2938.       (if str (set index order) (message "No entry %d in %s." order list)))
  2939.      ((or (not order) (eq order 'next))
  2940.       (if (= eind -1)
  2941.           (message "No next entry in %s." list)
  2942.         (set index (1- eind))
  2943.         (setq str (if (zerop eind) "" (nth (1- eind) elist)))))
  2944.      (t
  2945.       (if (>= (1+ eind) (length elist))
  2946.           (message "No previous entry in %s." list)
  2947.         (set index (1+ eind))
  2948.         (setq str (nth (1+ eind) elist)))))
  2949.     (if (null str) ()
  2950.       (delete-region irc-mark (goto-char (point-max)))
  2951.       (insert str)
  2952.       (goto-char irc-mark))))
  2953.    
  2954. (defun irc-message-prev (arg)
  2955.   "Select the previous message in the IRC history list.  ARG means
  2956. select that message out of the list (0 is the first)."
  2957.   (interactive "P")
  2958.   (irc-fetch-history 'irc-message-history 'irc-message-index (or arg 'prev)))
  2959.  
  2960. (defun irc-message-next (arg)
  2961.   "Select the next message in the IRC history list.  With prefix ARG
  2962. select that message out of the list (same as irc-message-prev if
  2963. called with a prefix arg)."
  2964.   (interactive "P")
  2965.   (irc-fetch-history 'irc-message-history 'irc-message-index (or arg 'next)))
  2966.  
  2967. ;; The following two functions could be merged with their irc-message-*
  2968. ;; counterparts, with behaviour determined by key-binding, but in the
  2969. ;; interests of flexibility for the user to chose personal bindings
  2970. ;; they are provided as seperate functions.
  2971. (defun irc-command-prev (arg)
  2972.     "Select the previous command in the IRC history list.  ARG means
  2973. select that message out of the list (0 is the first)."
  2974.   (interactive "P")
  2975.   (irc-fetch-history 'irc-command-history 'irc-command-index (or arg 'prev)))
  2976.  
  2977. (defun irc-command-next (arg)
  2978.   "Select the next command in the IRC history list.  With prefix ARG
  2979. select that command out of the list (same as irc-command-prev if
  2980. called with a prefix arg)."
  2981.   (interactive "P")
  2982.   (irc-fetch-history 'irc-command-history 'irc-command-index (or arg 'next)))
  2983.  
  2984. (defun irc-kill-input ()
  2985.   "Delete the input region and start out fresh.  This function is recommended
  2986. over any other way of killing the input-region interactively because it
  2987. also resets the index for the history list."
  2988.   (interactive)
  2989.   (delete-region irc-mark (goto-char (point-max)))
  2990.   (setq irc-message-index -1 irc-command-index -1))
  2991.  
  2992. (defun irc-history-menu (arg)
  2993.   "List in another buffer the history kept by irc-mode.  The history of
  2994. messages is shown by default, but a prefix argument means to show the
  2995. command history instead."
  2996.   (interactive "P")
  2997.   (let ((pop-up-windows t) (line 0)
  2998.         (hist (if arg irc-command-history irc-message-history)))
  2999.     (save-excursion
  3000.       (set-buffer (get-buffer-create "*IRC History*"))
  3001.       (fundamental-mode)
  3002.       (erase-buffer)
  3003.       (while hist
  3004.         (insert (format "%2d: %s\n" line (car hist)))
  3005.         (setq hist (cdr hist))
  3006.         (setq line (1+ line)))
  3007.       (if (zerop line)
  3008.           (insert "No messages have been sent to IRC yet."))
  3009.       (set-buffer-modified-p nil)
  3010.       (goto-char (point-min)))
  3011.     (display-buffer "*IRC History*")))
  3012.