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