Sync from ERC upstream
[bpt/emacs.git] / lisp / erc / erc.el
CommitLineData
597993cf
MB
1;; erc.el --- An Emacs Internet Relay Chat client
2
3;; Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
6904f7fe 4;; 2006, 2007 Free Software Foundation, Inc.
597993cf
MB
5
6;; Author: Alexander L. Belikoff (alexander@belikoff.net)
7;; Contributors: Sergey Berezin (sergey.berezin@cs.cmu.edu),
8;; Mario Lang (mlang@delysid.org),
9;; Alex Schroeder (alex@gnu.org)
10;; Andreas Fuchs (afs@void.at)
11;; Gergely Nagy (algernon@midgard.debian.net)
12;; David Edmondson (dme@dme.org)
0b6bb130 13;; Maintainer: Michael Olson (mwolson@gnu.org)
597993cf
MB
14;; Keywords: IRC, chat, client, Internet
15
16;; This file is part of GNU Emacs.
17
18;; GNU Emacs is free software; you can redistribute it and/or modify
19;; it under the terms of the GNU General Public License as published by
e0085d62 20;; the Free Software Foundation; either version 3, or (at your option)
597993cf
MB
21;; any later version.
22
23;; GNU Emacs is distributed in the hope that it will be useful,
24;; but WITHOUT ANY WARRANTY; without even the implied warranty of
25;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26;; GNU General Public License for more details.
27
28;; You should have received a copy of the GNU General Public License
29;; along with GNU Emacs; see the file COPYING. If not, write to the
30;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
31;; Boston, MA 02110-1301, USA.
32
33;;; Commentary:
34
83dc6995 35;; ERC is a powerful, modular, and extensible IRC client for Emacs.
597993cf
MB
36
37;; For more information, see the following URLs:
0b6bb130 38;; * http://sv.gnu.org/projects/erc/
83dc6995 39;; * http://www.emacswiki.org/cgi-bin/wiki/ERC
597993cf 40
0b6bb130
MB
41;; As of 2006-06-13, ERC development is now hosted on Savannah
42;; (http://sv.gnu.org/projects/erc). I invite everyone who wants to
43;; hack on it to contact me <mwolson@gnu.org> in order to get write
44;; access to the shared Arch archive.
597993cf
MB
45
46;; Installation:
47
48;; Put erc.el in your load-path, and put (require 'erc) in your .emacs.
49
50;; Configuration:
51
52;; Use M-x customize-group RET erc RET to get an overview
53;; of all the variables you can tweak.
54
55;; Usage:
56
57;; To connect to an IRC server, do
58;;
83dc6995 59;; M-x erc RET
597993cf
MB
60;;
61;; After you are connected to a server, you can use C-h m or have a look at
83dc6995 62;; the ERC menu.
597993cf
MB
63
64;;; History:
65;;
66
67;;; Code:
68
526dc846 69(defconst erc-version-string "Version 5.3 (devel)"
597993cf
MB
70 "ERC version. This is used by function `erc-version'.")
71
72(eval-when-compile (require 'cl))
73(require 'font-lock)
74(require 'pp)
75(require 'thingatpt)
76(require 'erc-compat)
597993cf
MB
77
78(defvar erc-official-location
0b6bb130 79 "http://emacswiki.org/cgi-bin/wiki/ERC (mailing list: erc-discuss@gnu.org)"
597993cf
MB
80 "Location of the ERC client on the Internet.")
81
82(defgroup erc nil
83 "Emacs Internet Relay Chat client."
0b6bb130 84 :link '(url-link "http://www.emacswiki.org/cgi-bin/wiki/ERC")
597993cf
MB
85 :prefix "erc-"
86 :group 'applications)
87
88(defgroup erc-buffers nil
89 "Creating new ERC buffers"
90 :group 'erc)
91
92(defgroup erc-display nil
93 "Settings for how various things are displayed"
94 :group 'erc)
95
96(defgroup erc-mode-line-and-header nil
97 "Displaying information in the mode-line and header"
98 :group 'erc-display)
99
100(defgroup erc-ignore nil
101 "Ignoring certain messages"
102 :group 'erc)
103
104(defgroup erc-query nil
105 "Using separate buffers for private discussions"
106 :group 'erc)
107
108(defgroup erc-quit-and-part nil
109 "Quitting and parting channels"
110 :group 'erc)
111
112(defgroup erc-paranoia nil
113 "Know what is sent and received; control the display of sensitive data."
114 :group 'erc)
115
116(defgroup erc-scripts nil
117 "Running scripts at startup and with /LOAD"
118 :group 'erc)
119
120(require 'erc-backend)
121
122;; compatibility with older ERC releases
123
124(if (fboundp 'defvaralias)
125 (progn
126 (defvaralias 'erc-announced-server-name 'erc-server-announced-name)
127 (erc-make-obsolete-variable 'erc-announced-server-name
128 'erc-server-announced-name
129 "ERC 5.1")
130 (defvaralias 'erc-process 'erc-server-process)
131 (erc-make-obsolete-variable 'erc-process 'erc-server-process "ERC 5.1")
132 (defvaralias 'erc-default-coding-system 'erc-server-coding-system)
133 (erc-make-obsolete-variable 'erc-default-coding-system
134 'erc-server-coding-system
135 "ERC 5.1"))
136 (message (concat "ERC: The function `defvaralias' is not bound. See the "
137 "NEWS file for variable name changes since ERC 5.0.4.")))
138
139(defalias 'erc-send-command 'erc-server-send)
140(erc-make-obsolete 'erc-send-command 'erc-server-send "ERC 5.1")
141
142;; tunable connection and authentication parameters
143
144(defcustom erc-server nil
0b6bb130 145 "IRC server to use if one is not provided.
597993cf
MB
146See function `erc-compute-server' for more details on connection
147parameters and authentication."
148 :group 'erc
0b6bb130
MB
149 :type '(choice (const :tag "None" nil)
150 (string :tag "Server")))
597993cf
MB
151
152(defcustom erc-port nil
0b6bb130
MB
153 "IRC port to use if not specified.
154
155This can be either a string or a number."
597993cf 156 :group 'erc
0b6bb130 157 :type '(choice (const :tag "None" nil)
f2c05698
MB
158 (integer :tag "Port number")
159 (string :tag "Port string")))
597993cf
MB
160
161(defcustom erc-nick nil
0b6bb130 162 "Nickname to use if one is not provided.
597993cf 163
0b6bb130 164This can be either a string, or a list of strings.
597993cf
MB
165In the latter case, if the first nick in the list is already in use,
166other nicks are tried in the list order.
167
168See function `erc-compute-nick' for more details on connection
169parameters and authentication."
170 :group 'erc
0b6bb130 171 :type '(choice (const :tag "None" nil)
597993cf 172 (string :tag "Nickname")
0b6bb130 173 (repeat (string :tag "Nickname"))))
597993cf
MB
174
175(defcustom erc-nick-uniquifier "`"
0b6bb130 176 "The string to append to the nick if it is already in use."
597993cf
MB
177 :group 'erc
178 :type 'string)
179
0b6bb130
MB
180(defcustom erc-try-new-nick-p t
181 "If the nickname you chose isn't available, and this option is non-nil,
182ERC should automatically attempt to connect with another nickname.
183
184You can manually set another nickname with the /NICK command."
597993cf
MB
185 :group 'erc
186 :type 'boolean)
187
188(defcustom erc-user-full-name nil
189 "User full name.
190
0b6bb130
MB
191This can be either a string or a function to call.
192
597993cf
MB
193See function `erc-compute-full-name' for more details on connection
194parameters and authentication."
195 :group 'erc
0b6bb130
MB
196 :type '(choice (const :tag "No name" nil)
197 (string :tag "Name")
198 (function :tag "Get from function"))
597993cf
MB
199 :set (lambda (sym val)
200 (if (functionp val)
201 (set sym (funcall val))
202 (set sym val))))
203
204(defvar erc-password nil
0b6bb130
MB
205 "Password to use when authenticating to an IRC server.
206It is not strictly necessary to provide this, since ERC will
207prompt you for it.")
597993cf
MB
208
209(defcustom erc-user-mode nil
210 "Initial user modes to be set after a connection is established."
211 :group 'erc
212 :type '(choice (const nil) string function))
213
214
215(defcustom erc-prompt-for-password t
216 "Asks before using the default password, or whether to enter a new one."
217 :group 'erc
218 :type 'boolean)
219
220(defcustom erc-warn-about-blank-lines t
221 "Warn the user if they attempt to send a blank line."
222 :group 'erc
223 :type 'boolean)
224
225(defcustom erc-send-whitespace-lines nil
226 "If set to non-nil, send lines consisting of only whitespace."
227 :group 'erc
228 :type 'boolean)
229
230(defcustom erc-hide-prompt nil
231 "If non-nil, do not display the prompt for commands.
232
233\(A command is any input starting with a '/').
234
235See also the variables `erc-prompt' and `erc-command-indicator'."
236 :group 'erc-display
237 :type 'boolean)
238
239;; tunable GUI stuff
240
241(defcustom erc-show-my-nick t
242 "If non-nil, display one's own nickname when sending a message.
243
244If non-nil, \"<nickname>\" will be shown.
245If nil, only \"> \" will be shown."
246 :group 'erc-display
247 :type 'boolean)
248
249(define-widget 'erc-message-type 'set
250 "A set of standard IRC Message types."
251 :args '((const "JOIN")
252 (const "KICK")
253 (const "NICK")
254 (const "PART")
255 (const "QUIT")
256 (const "MODE")
257 (repeat :inline t :tag "Others" (string :tag "IRC Message Type"))))
258
259(defcustom erc-hide-list nil
260 "*List of IRC type messages to hide.
261A typical value would be '(\"JOIN\" \"PART\" \"QUIT\")."
262 :group 'erc-ignore
263 :type 'erc-message-type)
264
265(defvar erc-session-password nil
266 "The password used for the current session.")
267(make-variable-buffer-local 'erc-session-password)
268
269(defcustom erc-disconnected-hook nil
270 "Run this hook with arguments (NICK IP REASON) when disconnected.
271This happens before automatic reconnection. Note, that
272`erc-server-QUIT-functions' might not be run when we disconnect,
273simply because we do not necessarily receive the QUIT event."
274 :group 'erc-hooks
275 :type 'hook)
276
277(defcustom erc-complete-functions nil
278 "These functions get called when the user hits TAB in ERC.
279Each function in turn is called until one returns non-nil to
280indicate it has handled the input."
281 :group 'erc-hooks
282 :type 'hook)
283
284(defcustom erc-join-hook nil
285 "Hook run when we join a channel. Hook functions are called
286without arguments, with the current buffer set to the buffer of
287the new channel.
288
289See also `erc-server-JOIN-functions', `erc-part-hook'."
290 :group 'erc-hooks
291 :type 'hook)
292
293(defcustom erc-quit-hook nil
294 "Hook run when processing a quit command directed at our nick.
295
296The hook receives one argument, the current PROCESS.
297See also `erc-server-QUIT-functions' and `erc-disconnected-hook'."
298 :group 'erc-hooks
299 :type 'hook)
300
301(defcustom erc-part-hook nil
302 "Hook run when processing a PART message directed at our nick.
303
304The hook receives one argument, the current BUFFER.
305See also `erc-server-QUIT-functions', `erc-quit-hook' and
306`erc-disconnected-hook'."
307 :group 'erc-hooks
308 :type 'hook)
309
310(defcustom erc-kick-hook nil
311 "Hook run when processing a KICK message directed at our nick.
312
313The hook receives one argument, the current BUFFER.
314See also `erc-server-PART-functions' and `erc-part-hook'."
315 :group 'erc-hooks
316 :type 'hook)
317
318(defcustom erc-nick-changed-functions nil
319 "List of functions run when your nick was successfully changed.
320
321Each function should accept two arguments, NEW-NICK and OLD-NICK."
322 :group 'erc-hooks
323 :type 'hook)
324
325(defcustom erc-connect-pre-hook '(erc-initialize-log-marker)
326 "Hook called just before `erc' calls `erc-connect'.
ff59d266 327Functions are passed a buffer as the first argument."
597993cf
MB
328 :group 'erc-hooks
329 :type 'hook)
330
331
332(defvar erc-channel-users nil
333 "A hash table of members in the current channel, which
334associates nicknames with cons cells of the form:
335\(USER . MEMBER-DATA) where USER is a pointer to an
336erc-server-user struct, and MEMBER-DATA is a pointer to an
337erc-channel-user struct.")
338(make-variable-buffer-local 'erc-channel-users)
339
340(defvar erc-server-users nil
341 "A hash table of users on the current server, which associates
342nicknames with erc-server-user struct instances.")
343(make-variable-buffer-local 'erc-server-users)
344
345(defun erc-downcase (string)
346 "Convert STRING to IRC standard conforming downcase."
347 (let ((s (downcase string))
348 (c '((?\[ . ?\{)
349 (?\] . ?\})
350 (?\\ . ?\|)
351 (?~ . ?^))))
352 (save-match-data
353 (while (string-match "[]\\[~]" s)
354 (aset s (match-beginning 0)
355 (cdr (assq (aref s (match-beginning 0)) c)))))
356 s))
357
01d9e862
MB
358(defmacro erc-with-server-buffer (&rest body)
359 "Execute BODY in the current ERC server buffer.
360If no server buffer exists, return nil."
361 (let ((buffer (make-symbol "buffer")))
362 `(let ((,buffer (erc-server-buffer)))
363 (when (buffer-live-p ,buffer)
364 (with-current-buffer ,buffer
365 ,@body)))))
366(put 'erc-with-server-buffer 'lisp-indent-function 0)
367(put 'erc-with-server-buffer 'edebug-form-spec '(body))
368
597993cf
MB
369(defstruct (erc-server-user (:type vector) :named)
370 ;; User data
371 nickname host login full-name info
372 ;; Buffers
373 ;;
374 ;; This is an alist of the form (BUFFER . CHANNEL-DATA), where
375 ;; CHANNEL-DATA is either nil or an erc-channel-user struct.
376 (buffers nil)
377 )
378
379(defstruct (erc-channel-user (:type vector) :named)
380 op voice
381 ;; Last message time (in the form of the return value of
382 ;; (current-time)
383 ;;
384 ;; This is useful for ordered name completion.
385 (last-message-time nil))
386
387(defsubst erc-get-channel-user (nick)
388 "Finds the (USER . CHANNEL-DATA) element corresponding to NICK
389in the current buffer's `erc-channel-users' hash table."
390 (gethash (erc-downcase nick) erc-channel-users))
391
392(defsubst erc-get-server-user (nick)
393 "Finds the USER corresponding to NICK in the current server's
394`erc-server-users' hash table."
ff59d266 395 (erc-with-server-buffer
597993cf
MB
396 (gethash (erc-downcase nick) erc-server-users)))
397
398(defsubst erc-add-server-user (nick user)
399 "This function is for internal use only.
400
401Adds USER with nickname NICK to the `erc-server-users' hash table."
ff59d266 402 (erc-with-server-buffer
597993cf
MB
403 (puthash (erc-downcase nick) user erc-server-users)))
404
405(defsubst erc-remove-server-user (nick)
406 "This function is for internal use only.
407
408Removes the user with nickname NICK from the `erc-server-users'
409hash table. This user is not removed from the
410`erc-channel-users' lists of other buffers.
411
412See also: `erc-remove-user'."
ff59d266 413 (erc-with-server-buffer
597993cf
MB
414 (remhash (erc-downcase nick) erc-server-users)))
415
416(defun erc-change-user-nickname (user new-nick)
417 "This function is for internal use only.
418
419Changes the nickname of USER to NEW-NICK in the
420`erc-server-users' hash table. The `erc-channel-users' lists of
421other buffers are also changed."
422 (let ((nick (erc-server-user-nickname user)))
423 (setf (erc-server-user-nickname user) new-nick)
ff59d266 424 (erc-with-server-buffer
597993cf
MB
425 (remhash (erc-downcase nick) erc-server-users)
426 (puthash (erc-downcase new-nick) user erc-server-users))
427 (dolist (buf (erc-server-user-buffers user))
428 (if (buffer-live-p buf)
429 (with-current-buffer buf
430 (let ((cdata (erc-get-channel-user nick)))
431 (remhash (erc-downcase nick) erc-channel-users)
432 (puthash (erc-downcase new-nick) cdata
433 erc-channel-users)))))))
434
435(defun erc-remove-channel-user (nick)
436 "This function is for internal use only.
437
438Removes the user with nickname NICK from the `erc-channel-users'
439list for this channel. If this user is not in the
440`erc-channel-users' list of any other buffers, the user is also
441removed from the server's `erc-server-users' list.
442
443See also: `erc-remove-server-user' and `erc-remove-user'."
444 (let ((channel-data (erc-get-channel-user nick)))
445 (when channel-data
446 (let ((user (car channel-data)))
447 (setf (erc-server-user-buffers user)
448 (delq (current-buffer)
449 (erc-server-user-buffers user)))
450 (remhash (erc-downcase nick) erc-channel-users)
451 (if (null (erc-server-user-buffers user))
452 (erc-remove-server-user nick))))))
453
454(defun erc-remove-user (nick)
455 "This function is for internal use only.
456
457Removes the user with nickname NICK from the `erc-server-users'
458list as well as from all `erc-channel-users' lists.
459
460See also: `erc-remove-server-user' and
461`erc-remove-channel-user'."
462 (let ((user (erc-get-server-user nick)))
463 (when user
464 (let ((buffers (erc-server-user-buffers user)))
465 (dolist (buf buffers)
466 (if (buffer-live-p buf)
467 (with-current-buffer buf
468 (remhash (erc-downcase nick) erc-channel-users)
469 (run-hooks 'erc-channel-members-changed-hook)))))
470 (erc-remove-server-user nick))))
471
472(defun erc-remove-channel-users ()
473 "This function is for internal use only.
474
475Removes all users in the current channel. This is called by
476`erc-server-PART' and `erc-server-QUIT'."
477 (when (and erc-server-connected
478 (erc-server-process-alive)
479 (hash-table-p erc-channel-users))
480 (maphash (lambda (nick cdata)
481 (erc-remove-channel-user nick))
482 erc-channel-users)
483 (clrhash erc-channel-users)))
484
485(defsubst erc-channel-user-op-p (nick)
a3d2f0a3 486 "Return t if NICK is an operator in the current channel."
597993cf
MB
487 (and nick
488 (hash-table-p erc-channel-users)
489 (let ((cdata (erc-get-channel-user nick)))
490 (and cdata (cdr cdata)
491 (erc-channel-user-op (cdr cdata))))))
492
493(defsubst erc-channel-user-voice-p (nick)
a3d2f0a3 494 "Return t if NICK has voice in the current channel."
597993cf
MB
495 (and nick
496 (hash-table-p erc-channel-users)
497 (let ((cdata (erc-get-channel-user nick)))
498 (and cdata (cdr cdata)
499 (erc-channel-user-voice (cdr cdata))))))
500
501(defun erc-get-channel-user-list ()
502 "Returns a list of users in the current channel. Each element
503of the list is of the form (USER . CHANNEL-DATA), where USER is
504an erc-server-user struct, and CHANNEL-DATA is either `nil' or an
505erc-channel-user struct.
506
507See also: `erc-sort-channel-users-by-activity'"
508 (let (users)
509 (if (hash-table-p erc-channel-users)
510 (maphash (lambda (nick cdata)
511 (setq users (cons cdata users)))
512 erc-channel-users))
513 users))
514
515(defun erc-get-server-nickname-list ()
516 "Returns a list of known nicknames on the current server."
ff59d266
MB
517 (erc-with-server-buffer
518 (let (nicks)
519 (when (hash-table-p erc-server-users)
520 (maphash (lambda (n user)
521 (setq nicks
522 (cons (erc-server-user-nickname user)
523 nicks)))
524 erc-server-users)
525 nicks))))
597993cf
MB
526
527(defun erc-get-channel-nickname-list ()
528 "Returns a list of known nicknames on the current channel."
529 (let (nicks)
530 (when (hash-table-p erc-channel-users)
531 (maphash (lambda (n cdata)
532 (setq nicks
533 (cons (erc-server-user-nickname (car cdata))
534 nicks)))
535 erc-channel-users)
536 nicks)))
537
538(defun erc-get-server-nickname-alist ()
539 "Returns an alist of known nicknames on the current server."
ff59d266
MB
540 (erc-with-server-buffer
541 (let (nicks)
542 (when (hash-table-p erc-server-users)
543 (maphash (lambda (n user)
544 (setq nicks
545 (cons (cons (erc-server-user-nickname user) nil)
546 nicks)))
547 erc-server-users)
548 nicks))))
597993cf
MB
549
550(defun erc-get-channel-nickname-alist ()
551 "Returns an alist of known nicknames on the current channel."
552 (let (nicks)
553 (when (hash-table-p erc-channel-users)
554 (maphash (lambda (n cdata)
555 (setq nicks
556 (cons (cons (erc-server-user-nickname (car cdata)) nil)
557 nicks)))
558 erc-channel-users)
559 nicks)))
560
561(defun erc-sort-channel-users-by-activity (list)
562 "Sorts LIST such that users which have spoken most recently are
563listed first. LIST must be of the form (USER . CHANNEL-DATA).
564
565See also: `erc-get-channel-user-list'."
566 (sort list
567 (lambda (x y)
568 (when (and
569 (cdr x) (cdr y))
570 (let ((tx (erc-channel-user-last-message-time (cdr x)))
571 (ty (erc-channel-user-last-message-time (cdr y))))
572 (if tx
573 (if ty
574 (time-less-p ty tx)
575 t)
576 nil))))))
577
578(defun erc-sort-channel-users-alphabetically (list)
579 "Sort LIST so that users' nicknames are in alphabetical order.
580LIST must be of the form (USER . CHANNEL-DATA).
581
582See also: `erc-get-channel-user-list'."
583 (sort list
584 (lambda (x y)
585 (when (and
586 (cdr x) (cdr y))
587 (let ((nickx (downcase (erc-server-user-nickname (car x))))
588 (nicky (downcase (erc-server-user-nickname (car y)))))
589 (if nickx
590 (if nicky
591 (string-lessp nickx nicky)
592 t)
593 nil))))))
594
595(defvar erc-channel-topic nil
596 "A topic string for the channel. Should only be used in channel-buffers.")
597(make-variable-buffer-local 'erc-channel-topic)
598
599(defvar erc-channel-modes nil
600 "List of strings representing channel modes.
601E.g. '(\"i\" \"m\" \"s\" \"b Quake!*@*\")
602\(not sure the ban list will be here, but why not)")
603(make-variable-buffer-local 'erc-channel-modes)
604
605(defvar erc-insert-marker nil
606 "The place where insertion of new text in erc buffers should happen.")
607(make-variable-buffer-local 'erc-insert-marker)
608
609(defvar erc-input-marker nil
610 "The marker where input should be inserted.")
611(make-variable-buffer-local 'erc-input-marker)
612
613(defun erc-string-no-properties (string)
614 "Return a copy of STRING will all text-properties removed."
615 (let ((newstring (copy-sequence string)))
616 (set-text-properties 0 (length newstring) nil newstring)
617 newstring))
618
619(defcustom erc-prompt "ERC>"
620 "Prompt used by ERC. Trailing whitespace is not required."
621 :group 'erc-display
622 :type '(choice string function))
623
624(defun erc-prompt ()
625 "Return the input prompt as a string.
626
627See also the variable `erc-prompt'."
628 (let ((prompt (if (functionp erc-prompt)
629 (funcall erc-prompt)
630 erc-prompt)))
631 (if (> (length prompt) 0)
632 (concat prompt " ")
633 prompt)))
634
635(defcustom erc-command-indicator nil
636 "Indicator used by ERC for showing commands.
637
638If non-nil, this will be used in the ERC buffer to indicate
639commands (i.e., input starting with a '/').
640
641If nil, the prompt will be constructed from the variable `erc-prompt'."
642 :group 'erc-display
643 :type '(choice (const nil) string function))
644
645(defun erc-command-indicator ()
646 "Return the command indicator prompt as a string.
647
648This only has any meaning if the variable `erc-command-indicator' is non-nil."
649 (and erc-command-indicator
650 (let ((prompt (if (functionp erc-command-indicator)
651 (funcall erc-command-indicator)
652 erc-command-indicator)))
653 (if (> (length prompt) 0)
654 (concat prompt " ")
655 prompt))))
656
657(defcustom erc-notice-prefix "*** "
658 "*Prefix for all notices."
659 :group 'erc-display
660 :type 'string)
661
662(defcustom erc-notice-highlight-type 'all
663 "*Determines how to highlight notices.
664See `erc-notice-prefix'.
665
666The following values are allowed:
667
668 'prefix - highlight notice prefix only
669 'all - highlight the entire notice
670
671Any other value disables notice's highlighting altogether."
672 :group 'erc-display
673 :type '(choice (const :tag "highlight notice prefix only" prefix)
674 (const :tag "highlight the entire notice" all)
675 (const :tag "don't highlight notices at all" nil)))
676
677(defcustom erc-echo-notice-hook nil
678 "*Specifies a list of functions to call to echo a private
679notice. Each function is called with four arguments, the string
680to display, the parsed server message, the target buffer (or
681nil), and the sender. The functions are called in order, until a
682function evaluates to non-nil. These hooks are called after
683those specified in `erc-echo-notice-always-hook'.
684
685See also: `erc-echo-notice-always-hook',
686`erc-echo-notice-in-default-buffer',
687`erc-echo-notice-in-target-buffer',
688`erc-echo-notice-in-minibuffer',
689`erc-echo-notice-in-server-buffer',
690`erc-echo-notice-in-active-non-server-buffer',
691`erc-echo-notice-in-active-buffer',
692`erc-echo-notice-in-user-buffers',
693`erc-echo-notice-in-user-and-target-buffers',
694`erc-echo-notice-in-first-user-buffer'"
695 :group 'erc-hooks
696 :type 'hook
697 :options '(erc-echo-notice-in-default-buffer
698 erc-echo-notice-in-target-buffer
699 erc-echo-notice-in-minibuffer
700 erc-echo-notice-in-server-buffer
701 erc-echo-notice-in-active-non-server-buffer
702 erc-echo-notice-in-active-buffer
703 erc-echo-notice-in-user-buffers
704 erc-echo-notice-in-user-and-target-buffers
705 erc-echo-notice-in-first-user-buffer))
706
707(defcustom erc-echo-notice-always-hook
708 '(erc-echo-notice-in-default-buffer)
709 "*Specifies a list of functions to call to echo a private
710notice. Each function is called with four arguments, the string
711to display, the parsed server message, the target buffer (or
712nil), and the sender. The functions are called in order, and all
713functions are called. These hooks are called before those
714specified in `erc-echo-notice-hook'.
715
716See also: `erc-echo-notice-hook',
717`erc-echo-notice-in-default-buffer',
718`erc-echo-notice-in-target-buffer',
719`erc-echo-notice-in-minibuffer',
720`erc-echo-notice-in-server-buffer',
721`erc-echo-notice-in-active-non-server-buffer',
722`erc-echo-notice-in-active-buffer',
723`erc-echo-notice-in-user-buffers',
724`erc-echo-notice-in-user-and-target-buffers',
725`erc-echo-notice-in-first-user-buffer'"
726 :group 'erc-hooks
727 :type 'hook
728 :options '(erc-echo-notice-in-default-buffer
729 erc-echo-notice-in-target-buffer
730 erc-echo-notice-in-minibuffer
731 erc-echo-notice-in-server-buffer
732 erc-echo-notice-in-active-non-server-buffer
733 erc-echo-notice-in-active-buffer
734 erc-echo-notice-in-user-buffers
735 erc-echo-notice-in-user-and-target-buffers
736 erc-echo-notice-in-first-user-buffer))
737
738;; other tunable parameters
739
740(defcustom erc-whowas-on-nosuchnick nil
741 "*If non-nil, do a whowas on a nick if no such nick."
742 :group 'erc
743 :type 'boolean)
744
745(defcustom erc-verbose-server-ping nil
746 "*If non-nil, show every time you get a PING or PONG from the server."
747 :group 'erc-paranoia
748 :type 'boolean)
749
750(defcustom erc-public-away-p nil
751 "*Let others know you are back when you are no longer marked away.
752This happens in this form:
753* <nick> is back (gone for <time>)
754
755Many consider it impolite to do so automatically."
756 :group 'erc
757 :type 'boolean)
758
759(defcustom erc-away-nickname nil
760 "*The nickname to take when you are marked as being away."
761 :group 'erc
762 :type '(choice (const nil)
763 string))
764
765(defcustom erc-paranoid nil
766 "If non-nil, then all incoming CTCP requests will be shown."
767 :group 'erc-paranoia
768 :type 'boolean)
769
770(defcustom erc-disable-ctcp-replies nil
771 "Disable replies to CTCP requests that require a reply.
772If non-nil, then all incoming CTCP requests that normally require
773an automatic reply (like VERSION or PING) will be ignored. Good to
774set if some hacker is trying to flood you away."
775 :group 'erc-paranoia
776 :type 'boolean)
777
778(defcustom erc-anonymous-login t
779 "Be paranoid, don't give away your machine name."
780 :group 'erc-paranoia
781 :type 'boolean)
782
783(defcustom erc-prompt-for-channel-key nil
a3d2f0a3 784 "Prompt for channel key when using `erc-join-channel' interactively."
597993cf
MB
785 :group 'erc
786 :type 'boolean)
787
788(defcustom erc-email-userid "user"
789 "Use this as your email user ID."
790 :group 'erc
791 :type 'string)
792
6904f7fe
MB
793(defcustom erc-system-name nil
794 "Use this as the name of your system.
795If nil, ERC will call `system-name' to get this information."
796 :group 'erc
797 :type '(choice (const :tag "Default system name" nil)
798 string))
799
597993cf
MB
800(defcustom erc-ignore-list nil
801 "*List of regexps matching user identifiers to ignore.
802
803A user identifier has the form \"nick!login@host\". If an
804identifier matches, the message from the person will not be
805processed."
806 :group 'erc-ignore
807 :type '(repeat regexp))
808(make-variable-buffer-local 'erc-ignore-list)
809
810(defcustom erc-ignore-reply-list nil
811 "*List of regexps matching user identifiers to ignore completely.
812
813This differs from `erc-ignore-list' in that it also ignores any
814messages directed at the user.
815
816A user identifier has the form \"nick!login@host\".
817
818If an identifier matches, or a message is addressed to a nick
819whose identifier matches, the message will not be processed.
820
821CAVEAT: ERC doesn't know about the user and host of anyone who
822was already in the channel when you joined, but never said
823anything, so it won't be able to match the user and host of those
a3d2f0a3 824people. You can update the ERC internal info using /WHO *."
597993cf
MB
825 :group 'erc-ignore
826 :type '(repeat regexp))
827
828(defvar erc-flood-protect t
829 "*If non-nil, flood protection is enabled.
830Flooding is sending too much information to the server in too
831short of an interval, which may cause the server to terminate the
832connection.
833
834See `erc-server-flood-margin' for other flood-related parameters.")
835
836;; Script parameters
837
838(defcustom erc-startup-file-list
526dc846
MO
839 (list (concat erc-user-emacs-directory ".ercrc.el")
840 (concat erc-user-emacs-directory ".ercrc")
841 "~/.ercrc.el" "~/.ercrc" ".ercrc.el" ".ercrc")
597993cf
MB
842 "List of files to try for a startup script.
843The first existent and readable one will get executed.
844
a3d2f0a3 845If the filename ends with `.el' it is presumed to be an Emacs Lisp
680a9905 846script and it gets (load)ed. Otherwise it is treated as a bunch of
a3d2f0a3 847regular IRC commands."
597993cf
MB
848 :group 'erc-scripts
849 :type '(repeat file))
850
851(defcustom erc-script-path nil
852 "List of directories to look for a script in /load command.
853The script is first searched in the current directory, then in each
854directory in the list."
855 :group 'erc-scripts
856 :type '(repeat directory))
857
858(defcustom erc-script-echo t
a3d2f0a3 859 "*If non-nil, echo the IRC script commands locally."
597993cf
MB
860 :group 'erc-scripts
861 :type 'boolean)
862
863(defvar erc-last-saved-position nil
864 "A marker containing the position the current buffer was last saved at.")
865(make-variable-buffer-local 'erc-last-saved-position)
866
867(defcustom erc-kill-buffer-on-part nil
868 "Kill the channel buffer on PART.
869This variable should probably stay nil, as ERC can reuse buffers if
870you rejoin them later."
871 :group 'erc-quit-and-part
872 :type 'boolean)
873
874(defcustom erc-kill-queries-on-quit nil
875 "Kill all query (also channel) buffers of this server on QUIT.
876See the variable `erc-kill-buffer-on-part' for details."
877 :group 'erc-quit-and-part
878 :type 'boolean)
879
880(defcustom erc-kill-server-buffer-on-quit nil
881 "Kill the server buffer of the process on QUIT."
882 :group 'erc-quit-and-part
883 :type 'boolean)
884
885(defcustom erc-quit-reason-various-alist nil
886 "Alist of possible arguments to the /quit command.
887
888Each element has the form:
889 (REGEXP RESULT)
890
891If REGEXP matches the argument to /quit, then its relevant RESULT
892will be used. RESULT may be either a string, or a function. If
893a function, it should return the quit message as a string.
894
895If no elements match, then the empty string is used.
896
897As an example:
898 (setq erc-quit-reason-various-alist
899 '((\"zippy\" erc-quit-reason-zippy)
900 (\"xmms\" dme:now-playing)
901 (\"version\" erc-quit-reason-normal)
902 (\"home\" \"Gone home !\")
0b6bb130 903 (\"^$\" \"Default Reason\")))
597993cf
MB
904If the user types \"/quit zippy\", then a Zippy the Pinhead quotation
905will be used as the quit message."
906 :group 'erc-quit-and-part
907 :type '(repeat (list regexp (choice (string) (function)))))
908
909(defcustom erc-part-reason-various-alist nil
910 "Alist of possible arguments to the /part command.
911
912Each element has the form:
913 (REGEXP RESULT)
914
915If REGEXP matches the argument to /part, then its relevant RESULT
916will be used. RESULT may be either a string, or a function. If
917a function, it should return the part message as a string.
918
919If no elements match, then the empty string is used.
920
921As an example:
922 (setq erc-part-reason-various-alist
923 '((\"zippy\" erc-part-reason-zippy)
924 (\"xmms\" dme:now-playing)
925 (\"version\" erc-part-reason-normal)
926 (\"home\" \"Gone home !\")
0b6bb130 927 (\"^$\" \"Default Reason\")))
597993cf
MB
928If the user types \"/part zippy\", then a Zippy the Pinhead quotation
929will be used as the part message."
930 :group 'erc-quit-and-part
931 :type '(repeat (list regexp (choice (string) (function)))))
932
933(defcustom erc-quit-reason 'erc-quit-reason-normal
934 "*A function which returns the reason for quitting.
935
936The function is passed a single argument, the string typed by the
937user after \"/quit\"."
938 :group 'erc-quit-and-part
939 :type '(choice (const erc-quit-reason-normal)
940 (const erc-quit-reason-zippy)
941 (const erc-quit-reason-various)
942 (symbol)))
943
944(defcustom erc-part-reason 'erc-part-reason-normal
945 "A function which returns the reason for parting a channel.
946
947The function is passed a single argument, the string typed by the
948user after \"/PART\"."
949 :group 'erc-quit-and-part
950 :type '(choice (const erc-part-reason-normal)
951 (const erc-part-reason-zippy)
952 (const erc-part-reason-various)
953 (symbol)))
954
955(defvar erc-grab-buffer-name "*erc-grab*"
956 "The name of the buffer created by `erc-grab-region'.")
957
958;; variables available for IRC scripts
959
960(defvar erc-user-information "ERC User"
961 "USER_INFORMATION IRC variable.")
962
963;; Hooks
964
965(defgroup erc-hooks nil
966 "Hook variables for fancy customizations of ERC."
967 :group 'erc)
968
969(defcustom erc-mode-hook nil
970 "Hook run after `erc-mode' setup is finished."
971 :group 'erc-hooks
972 :type 'hook
973 :options '(erc-add-scroll-to-bottom))
974
975(defcustom erc-timer-hook nil
976 "Put functions which should get called more or less periodically here.
977The idea is that servers always play ping pong with the client, and so there
978is no need for any idle-timer games with Emacs."
979 :group 'erc-hooks
980 :type 'hook)
981
982(defcustom erc-insert-pre-hook nil
983 "Hook called first when some text is inserted through `erc-display-line'.
984It gets called with one argument, STRING.
985To be able to modify the inserted text, use `erc-insert-modify-hook' instead.
986Filtering functions can set `erc-insert-this' to nil to avoid
987display of that particular string at all."
988 :group 'erc-hooks
989 :type 'hook)
990
991(defcustom erc-send-pre-hook nil
992 "Hook called first when some text is sent through `erc-send-current-line'.
993It gets called with one argument, STRING.
994
995To change the text that will be sent, set the variable STR which is
996used in `erc-send-current-line'.
997
998To change the text inserted into the buffer without changing the text
999that will be sent, use `erc-send-modify-hook' instead.
1000
1001Filtering functions can set `erc-send-this' to nil to avoid sending of
1002that particular string at all and `erc-insert-this' to prevent
1003inserting that particular string into the buffer.
1004
1005Note that it's useless to set `erc-send-this' to nil and
1006`erc-insert-this' to t. ERC is sane enough to not insert the text
1007anyway."
1008 :group 'erc-hooks
1009 :type 'hook)
1010
1011(defvar erc-insert-this t
1012 "Insert the text into the target buffer or not.
1013Functions on `erc-insert-pre-hook' can set this variable to nil
1014if they wish to avoid insertion of a particular string.")
1015
1016(defvar erc-send-this t
1017 "Send the text to the target or not.
1018Functions on `erc-send-pre-hook' can set this variable to nil
1019if they wish to avoid sending of a particular string.")
1020
1021(defcustom erc-insert-modify-hook ()
1022 "Insertion hook for functions that will change the text's appearance.
1023This hook is called just after `erc-insert-pre-hook' when the value
1024of `erc-insert-this' is t.
1025While this hook is run, narrowing is in effect and `current-buffer' is
1026the buffer where the text got inserted. One possible value to add here
1027is `erc-fill'."
1028 :group 'erc-hooks
1029 :type 'hook)
1030
1031(defcustom erc-insert-post-hook nil
1032 "This hook is called just after `erc-insert-modify-hook'.
1033At this point, all modifications from prior hook functions are done."
1034 :group 'erc-hooks
1035 :type 'hook
1036 :options '(erc-truncate-buffer
1037 erc-make-read-only
1038 erc-save-buffer-in-logs))
1039
1040(defcustom erc-send-modify-hook nil
1041 "Sending hook for functions that will change the text's appearance.
1042This hook is called just after `erc-send-pre-hook' when the values
1043of `erc-send-this' and `erc-insert-this' are both t.
1044While this hook is run, narrowing is in effect and `current-buffer' is
1045the buffer where the text got inserted.
1046
1047Note that no function in this hook can change the appearance of the
1048text that is sent. Only changing the sent text's appearance on the
1049sending user's screen is possible. One possible value to add here
1050is `erc-fill'."
1051 :group 'erc-hooks
1052 :type 'hook)
1053
1054(defcustom erc-send-post-hook nil
1055 "This hook is called just after `erc-send-modify-hook'.
1056At this point, all modifications from prior hook functions are done.
1057NOTE: The functions on this hook are called _before_ sending a command
1058to the server.
1059
a3d2f0a3 1060This function is called with narrowing, ala `erc-send-modify-hook'."
597993cf
MB
1061 :group 'erc-hooks
1062 :type 'hook
1063 :options '(erc-make-read-only))
1064
1065(defcustom erc-send-completed-hook
1066 (when (featurep 'emacspeak)
1067 (list (byte-compile
1068 (lambda (str)
1069 (emacspeak-auditory-icon 'select-object)))))
1070 "Hook called after a message has been parsed by ERC.
1071
1072The single argument to the functions is the unmodified string
1073which the local user typed."
1074 :group 'erc-hooks
1075 :type 'hook)
1076;; mode-specific tables
1077
1078(defvar erc-mode-syntax-table
1079 (let ((syntax-table (make-syntax-table)))
1080 (modify-syntax-entry ?\" ". " syntax-table)
1081 (modify-syntax-entry ?\\ ". " syntax-table)
1082 (modify-syntax-entry ?' "w " syntax-table)
1083 ;; Make dabbrev-expand useful for nick names
1084 (modify-syntax-entry ?< "." syntax-table)
1085 (modify-syntax-entry ?> "." syntax-table)
1086 syntax-table)
1087 "Syntax table used while in ERC mode.")
1088
1089(defvar erc-mode-abbrev-table nil
1090 "Abbrev table used while in ERC mode.")
1091(define-abbrev-table 'erc-mode-abbrev-table ())
1092
1093(defvar erc-mode-map
1094 (let ((map (make-sparse-keymap)))
1095 (define-key map "\C-m" 'erc-send-current-line)
1096 (define-key map "\C-a" 'erc-bol)
1097 (define-key map [home] 'erc-bol)
1098 (define-key map "\C-c\C-a" 'erc-bol)
1099 (define-key map "\C-c\C-b" 'erc-iswitchb)
1100 (define-key map "\C-c\C-c" 'erc-toggle-interpret-controls)
1101 (define-key map "\C-c\C-d" 'erc-input-action)
1102 (define-key map "\C-c\C-e" 'erc-toggle-ctcp-autoresponse)
1103 (define-key map "\C-c\C-f" 'erc-toggle-flood-control)
1104 (define-key map "\C-c\C-i" 'erc-invite-only-mode)
1105 (define-key map "\C-c\C-j" 'erc-join-channel)
1106 (define-key map "\C-c\C-n" 'erc-channel-names)
1107 (define-key map "\C-c\C-o" 'erc-get-channel-mode-from-keypress)
1108 (define-key map "\C-c\C-p" 'erc-part-from-channel)
1109 (define-key map "\C-c\C-q" 'erc-quit-server)
1110 (define-key map "\C-c\C-r" 'erc-remove-text-properties-region)
1111 (define-key map "\C-c\C-t" 'erc-set-topic)
1112 (define-key map "\C-c\C-u" 'erc-kill-input)
1113 (define-key map "\M-\t" 'ispell-complete-word)
1114 (define-key map "\t" 'erc-complete-word)
1115
1116 ;; Suppress `font-lock-fontify-block' key binding since it
1117 ;; destroys face properties.
1118 (if (fboundp 'command-remapping)
1119 (define-key map [remap font-lock-fontify-block] 'undefined)
1120 (substitute-key-definition
1121 'font-lock-fontify-block 'undefined map global-map))
1122
1123 map)
1124 "ERC keymap.")
1125
1126;; Faces
1127
1128; Honestly, I have a horrible sense of color and the "defaults" below
1129; are supposed to be really bad. But colors ARE required in IRC to
1130; convey different parts of conversation. If you think you know better
1131; defaults - send them to me.
1132
1133;; Now colors are a bit nicer, at least to my eyes.
1134;; You may still want to change them to better fit your background.-- S.B.
1135
1136(defgroup erc-faces nil
1137 "Faces for ERC."
1138 :group 'erc)
1139
1140(defface erc-default-face '((t))
1141 "ERC default face."
1142 :group 'erc-faces)
1143
1144(defface erc-direct-msg-face '((t (:foreground "IndianRed")))
1145 "ERC face used for messages you receive in the main erc buffer."
1146 :group 'erc-faces)
1147
c89e5cd7
MB
1148(defface erc-header-line
1149 '((t (:foreground "grey20" :background "grey90")))
1150 "ERC face used for the header line.
1151
1152This will only be used if `erc-header-line-face-method' is non-nil."
1153 :group 'erc-faces)
1154
597993cf
MB
1155(defface erc-input-face '((t (:foreground "brown")))
1156 "ERC face used for your input."
1157 :group 'erc-faces)
1158
1159(defface erc-prompt-face
c89e5cd7 1160 '((t (:bold t :foreground "Black" :background "lightBlue2")))
597993cf
MB
1161 "ERC face for the prompt."
1162 :group 'erc-faces)
1163
1164(defface erc-command-indicator-face
1165 '((t (:bold t)))
1166 "ERC face for the command indicator.
1167See the variable `erc-command-indicator'."
1168 :group 'erc-faces)
1169
1170(defface erc-notice-face '((t (:bold t :foreground "SlateBlue")))
1171 "ERC face for notices."
1172 :group 'erc-faces)
1173
1174(defface erc-action-face '((t (:bold t)))
1175 "ERC face for actions generated by /ME."
1176 :group 'erc-faces)
1177
1178(defface erc-error-face '((t (:foreground "red")))
1179 "ERC face for errors."
1180 :group 'erc-faces)
1181
83dc6995
MB
1182;; same default color as `erc-input-face'
1183(defface erc-my-nick-face '((t (:bold t :foreground "brown")))
1184 "ERC face for your current nickname in messages sent by you.
1185See also `erc-show-my-nick'."
1186 :group 'erc-faces)
1187
597993cf
MB
1188(defface erc-nick-default-face '((t (:bold t)))
1189 "ERC nickname default face."
1190 :group 'erc-faces)
1191
1192(defface erc-nick-msg-face '((t (:bold t :foreground "IndianRed")))
1193 "ERC nickname face for private messages."
1194 :group 'erc-faces)
1195
1196;; Debugging support
1197
1198(defvar erc-log-p nil
1199 "When set to t, generate debug messages in a separate debug buffer.")
1200
1201(defvar erc-debug-log-file (expand-file-name "ERC.debug")
1202 "Debug log file name.")
1203
1204(defvar erc-dbuf nil)
1205(make-variable-buffer-local 'erc-dbuf)
1206
1207(defmacro define-erc-module (name alias doc enable-body disable-body
1208 &optional local-p)
1209 "Define a new minor mode using ERC conventions.
1210Symbol NAME is the name of the module.
1211Symbol ALIAS is the alias to use, or nil.
1212DOC is the documentation string to use for the minor mode.
1213ENABLE-BODY is a list of expressions used to enable the mode.
1214DISABLE-BODY is a list of expressions used to disable the mode.
1215If LOCAL-P is non-nil, the mode will be created as a buffer-local
a3d2f0a3 1216mode, rather than a global one.
597993cf
MB
1217
1218This will define a minor mode called erc-NAME-mode, possibly
1219an alias erc-ALIAS-mode, as well as the helper functions
1220erc-NAME-enable, and erc-NAME-disable.
1221
1222Example:
1223
1224 ;;;###autoload (autoload 'erc-replace-mode \"erc-replace\")
1225 (define-erc-module replace nil
1226 \"This mode replaces incoming text according to `erc-replace-alist'.\"
1227 ((add-hook 'erc-insert-modify-hook
1228 'erc-replace-insert))
1229 ((remove-hook 'erc-insert-modify-hook
1230 'erc-replace-insert)))"
1231 (let* ((sn (symbol-name name))
1232 (mode (intern (format "erc-%s-mode" (downcase sn))))
1233 (group (intern (format "erc-%s" (downcase sn))))
1234 (enable (intern (format "erc-%s-enable" (downcase sn))))
1235 (disable (intern (format "erc-%s-disable" (downcase sn)))))
1236 `(progn
1237 (erc-define-minor-mode
1238 ,mode
1239 ,(format "Toggle ERC %S mode.
1240With arg, turn ERC %S mode on if and only if arg is positive.
1241%s" name name doc)
1242 nil nil nil
1243 :global ,(not local-p) :group (quote ,group)
1244 (if ,mode
1245 (,enable)
1246 (,disable)))
1247 (defun ,enable ()
1248 ,(format "Enable ERC %S mode."
1249 name)
1250 (interactive)
1251 (add-to-list 'erc-modules (quote ,name))
1252 (setq ,mode t)
1253 ,@enable-body)
1254 (defun ,disable ()
1255 ,(format "Disable ERC %S mode."
1256 name)
1257 (interactive)
1258 (setq erc-modules (delq (quote ,name) erc-modules))
1259 (setq ,mode nil)
1260 ,@disable-body)
1261 ,(when (and alias (not (eq name alias)))
1262 `(defalias
1263 (quote
1264 ,(intern
1265 (format "erc-%s-mode"
1266 (downcase (symbol-name alias)))))
1267 (quote
2e3ef421
MB
1268 ,mode)))
1269 ;; For find-function and find-variable.
1270 (put ',mode 'definition-name ',name)
1271 (put ',enable 'definition-name ',name)
1272 (put ',disable 'definition-name ',name))))
597993cf
MB
1273
1274(put 'define-erc-module 'doc-string-elt 3)
1275
1276(defun erc-once-with-server-event (event &rest forms)
1277 "Execute FORMS the next time EVENT occurs in the `current-buffer'.
1278
1279You should make sure that `current-buffer' is a server buffer.
1280
1281This function temporarily adds a function to EVENT's hook to
1282execute FORMS. After FORMS are run, the function is removed from
1283EVENT's hook. The last expression of FORMS should be either nil
a3d2f0a3 1284or t, where nil indicates that the other functions on EVENT's hook
597993cf
MB
1285should be run too, and t indicates that other functions should
1286not be run.
1287
1288Please be sure to use this function in server-buffers. In
1289channel-buffers it may not work at all, as it uses the LOCAL
1290argument of `add-hook' and `remove-hook' to ensure multiserver
1291capabilities."
1292 (unless (erc-server-buffer-p)
1293 (error
1294 "You should only run `erc-once-with-server-event' in a server buffer"))
ff59d266 1295 (let ((fun (make-symbol "fun"))
597993cf
MB
1296 (hook (erc-get-hook event)))
1297 (put fun 'erc-original-buffer (current-buffer))
1298 (fset fun `(lambda (proc parsed)
1299 (with-current-buffer (get ',fun 'erc-original-buffer)
1300 (remove-hook ',hook ',fun t))
1301 (fmakunbound ',fun)
1302 ,@forms))
1303 (add-hook hook fun nil t)
1304 fun))
1305
1306(defun erc-once-with-server-event-global (event &rest forms)
1307 "Execute FORMS the next time EVENT occurs in any server buffer.
1308
1309This function temporarily prepends a function to EVENT's hook to
1310execute FORMS. After FORMS are run, the function is removed from
1311EVENT's hook. The last expression of FORMS should be either nil
a3d2f0a3 1312or t, where nil indicates that the other functions on EVENT's hook
597993cf
MB
1313should be run too, and t indicates that other functions should
1314not be run.
1315
1316When FORMS execute, the current buffer is the server buffer associated with the
1317connection over which the data was received that triggered EVENT."
ff59d266 1318 (let ((fun (make-symbol "fun"))
597993cf
MB
1319 (hook (erc-get-hook event)))
1320 (fset fun `(lambda (proc parsed)
1321 (remove-hook ',hook ',fun)
1322 (fmakunbound ',fun)
1323 ,@forms))
1324 (add-hook hook fun nil nil)
1325 fun))
1326
1327(defmacro erc-log (string)
1328 "Logs STRING if logging is on (see `erc-log-p')."
1329 `(when erc-log-p
1330 (erc-log-aux ,string)))
1331
1332(defun erc-server-buffer ()
1333 "Return the server buffer for the current buffer's process.
1334The buffer-local variable `erc-server-process' is used to find
1335the process buffer."
1336 (and (erc-server-buffer-live-p)
1337 (process-buffer erc-server-process)))
1338
1339(defun erc-server-buffer-live-p ()
83dc6995 1340 "Return t if the server buffer has not been killed."
597993cf
MB
1341 (and (processp erc-server-process)
1342 (buffer-live-p (process-buffer erc-server-process))))
1343
1344(defun erc-server-buffer-p (&optional buffer)
1345 "Return non-nil if argument BUFFER is an ERC server buffer.
1346
1347If BUFFER is nil, the current buffer is used."
1348 (with-current-buffer (or buffer (current-buffer))
1349 (and (eq major-mode 'erc-mode)
1350 (null (erc-default-target)))))
1351
01d9e862
MB
1352(defun erc-open-server-buffer-p (&optional buffer)
1353 "Return non-nil if argument BUFFER is an ERC server buffer that
1354has an open IRC process.
1355
1356If BUFFER is nil, the current buffer is used."
1357 (and (erc-server-buffer-p)
1358 (erc-server-process-alive)))
1359
597993cf
MB
1360(defun erc-query-buffer-p (&optional buffer)
1361 "Return non-nil if BUFFER is an ERC query buffer.
1362If BUFFER is nil, the current buffer is used."
1363 (with-current-buffer (or buffer (current-buffer))
1364 (let ((target (erc-default-target)))
1365 (and (eq major-mode 'erc-mode)
1366 target
1367 (not (memq (aref target 0) '(?# ?& ?+ ?!)))))))
1368
1369(defun erc-ison-p (nick)
1370 "Return non-nil if NICK is online."
1371 (interactive "sNick: ")
ff59d266 1372 (erc-with-server-buffer
597993cf
MB
1373 (let ((erc-online-p 'unknown))
1374 (erc-once-with-server-event
1375 303
1376 `(let ((ison (split-string (aref parsed 3))))
1377 (setq erc-online-p (car (erc-member-ignore-case ,nick ison)))
1378 t))
1379 (erc-server-send (format "ISON %s" nick))
1380 (while (eq erc-online-p 'unknown) (accept-process-output))
1381 (if (interactive-p)
1382 (message "%s is %sonline"
1383 (or erc-online-p nick)
1384 (if erc-online-p "" "not "))
1385 erc-online-p))))
1386
1387(defun erc-log-aux (string)
1388 "Do the debug logging of STRING."
1389 (let ((cb (current-buffer))
1390 (point 1)
1391 (was-eob nil)
1392 (session-buffer (erc-server-buffer)))
1393 (if session-buffer
1394 (progn
1395 (set-buffer session-buffer)
1396 (if (not (and erc-dbuf (bufferp erc-dbuf) (buffer-live-p erc-dbuf)))
1397 (progn
1398 (setq erc-dbuf (get-buffer-create
1399 (concat "*ERC-DEBUG: "
1400 erc-session-server "*")))))
1401 (set-buffer erc-dbuf)
1402 (setq point (point))
1403 (setq was-eob (eobp))
1404 (goto-char (point-max))
1405 (insert (concat "** " string "\n"))
1406 (if was-eob (goto-char (point-max))
1407 (goto-char point))
1408 (set-buffer cb))
1409 (message "ERC: ** %s" string))))
1410
1411;; Last active buffer, to print server messages in the right place
1412
1413(defvar erc-active-buffer nil
1414 "The current active buffer, the one where the user typed the last command.
1415Defaults to the server buffer, and should only be set in the
a3d2f0a3 1416server buffer.")
597993cf
MB
1417(make-variable-buffer-local 'erc-active-buffer)
1418
1419(defun erc-active-buffer ()
1420 "Return the value of `erc-active-buffer' for the current server.
1421Defaults to the server buffer."
ff59d266 1422 (erc-with-server-buffer
0b6bb130 1423 (if (buffer-live-p erc-active-buffer)
2e3ef421
MB
1424 erc-active-buffer
1425 (setq erc-active-buffer (current-buffer)))))
597993cf
MB
1426
1427(defun erc-set-active-buffer (buffer)
1428 "Set the value of `erc-active-buffer' to BUFFER."
1429 (cond ((erc-server-buffer)
1430 (with-current-buffer (erc-server-buffer)
1431 (setq erc-active-buffer buffer)))
1432 (t (setq erc-active-buffer buffer))))
1433
1434;; Mode activation routines
1435
1436(defun erc-mode ()
1437 "Major mode for Emacs IRC.
1438Special commands:
1439
1440\\{erc-mode-map}
1441
1442Turning on `erc-mode' runs the hook `erc-mode-hook'."
1443 (kill-all-local-variables)
1444 (use-local-map erc-mode-map)
1445 (setq mode-name "ERC"
1446 major-mode 'erc-mode
1447 local-abbrev-table erc-mode-abbrev-table)
1448 (set-syntax-table erc-mode-syntax-table)
1449 (when (boundp 'next-line-add-newlines)
1450 (set (make-local-variable 'next-line-add-newlines) nil))
1451 (setq line-move-ignore-invisible t)
1452 (set (make-local-variable 'paragraph-separate)
1453 (concat "\C-l\\|\\(^" (regexp-quote (erc-prompt)) "\\)"))
1454 (set (make-local-variable 'paragraph-start)
1455 (concat "\\(" (regexp-quote (erc-prompt)) "\\)"))
1456 ;; Run the mode hooks
1457 (run-hooks 'erc-mode-hook))
1458
1459;; activation
1460
1461(defconst erc-default-server "irc.freenode.net"
1462 "IRC server to use if it cannot be detected otherwise.")
1463
526dc846 1464(defconst erc-default-port 6667
597993cf
MB
1465 "IRC port to use if it cannot be detected otherwise.")
1466
1467(defcustom erc-join-buffer 'buffer
1468 "Determines how to display the newly created IRC buffer.
1469'window - in another window,
1470'window-noselect - in another window, but don't select that one,
1471'frame - in another frame,
1472'bury - bury it in a new buffer,
a3d2f0a3 1473any other value - in place of the current buffer."
597993cf
MB
1474 :group 'erc-buffers
1475 :type '(choice (const window)
1476 (const window-noselect)
1477 (const frame)
1478 (const bury)
1479 (const buffer)))
1480
1481(defcustom erc-frame-alist nil
1482 "*Alist of frame parameters for creating erc frames.
a3d2f0a3 1483A value of nil means to use `default-frame-alist'."
597993cf
MB
1484 :group 'erc-buffers
1485 :type '(repeat (cons :format "%v"
1486 (symbol :tag "Parameter")
1487 (sexp :tag "Value"))))
1488
1489(defcustom erc-frame-dedicated-flag nil
1490 "*Non-nil means the erc frames are dedicated to that buffer.
1491This only has effect when `erc-join-buffer' is set to `frame'."
1492 :group 'erc-buffers
1493 :type 'boolean)
1494
526dc846
MO
1495(defcustom erc-reuse-frames t
1496 "*Determines whether new frames are always created.
1497Non-nil means that a new frame is not created to display an ERC
1498buffer if there is already a window displaying it. This only has
1499effect when `erc-join-buffer' is set to `frame'."
1500 :group 'erc-buffers
1501 :type 'boolean)
1502
597993cf
MB
1503(defun erc-channel-p (channel)
1504 "Return non-nil if CHANNEL seems to be an IRC channel name."
1505 (cond ((stringp channel)
1506 (memq (aref channel 0) '(?# ?& ?+ ?!)))
1507 ((and (bufferp channel) (buffer-live-p channel))
1508 (with-current-buffer channel
1509 (erc-channel-p (erc-default-target))))
1510 (t nil)))
1511
1512(defcustom erc-reuse-buffers t
1513 "*If nil, create new buffers on joining a channel/query.
1514If non-nil, a new buffer will only be created when you join
1515channels with same names on different servers, or have query buffers
1516open with nicks of the same name on different servers. Otherwise,
1517the existing buffers will be reused."
1518 :group 'erc-buffers
1519 :type 'boolean)
1520
1521(defun erc-normalize-port (port)
1522 "Normalize the port specification PORT to integer form.
1523PORT may be an integer, a string or a symbol. If it is a string or a
1524symbol, it may have these values:
1525* irc -> 194
1526* ircs -> 994
1527* ircd -> 6667
1528* ircd-dalnet -> 7000"
1529 (cond
1530 ((symbolp port)
1531 (erc-normalize-port (symbol-name port)))
1532 ((stringp port)
1533 (let ((port-nr (string-to-number port)))
1534 (cond
1535 ((> port-nr 0)
1536 port-nr)
1537 ((string-equal port "irc")
1538 194)
1539 ((string-equal port "ircs")
1540 994)
1541 ((string-equal port "ircd")
1542 6667)
1543 ((string-equal port "ircd-dalnet")
1544 7000)
1545 (t
1546 nil))))
1547 ((numberp port)
1548 port)
1549 (t
1550 nil)))
1551
1552(defun erc-port-equal (a b)
1553 "Check whether ports A and B are equal."
1554 (= (erc-normalize-port a) (erc-normalize-port b)))
1555
1556(defun erc-generate-new-buffer-name (server port target &optional proc)
1557 "Create a new buffer name based on the arguments."
1558 (when (numberp port) (setq port (number-to-string port)))
1559 (let* ((buf-name (or target
1560 (or (let ((name (concat server ":" port)))
1561 (when (> (length name) 1)
1562 name))
1563 ; This fallback should in fact never happen
1564 "*erc-server-buffer*"))))
1565 ;; Reuse existing buffers, but not if the buffer is a connected server
1566 ;; buffer and not if its associated with a different server than the
1567 ;; current ERC buffer.
1568 (if (and erc-reuse-buffers
1569 (get-buffer buf-name)
1570 (or target
1571 (with-current-buffer (get-buffer buf-name)
1572 (and (erc-server-buffer-p)
83dc6995 1573 (not (erc-server-process-alive)))))
597993cf
MB
1574 (with-current-buffer (get-buffer buf-name)
1575 (and (string= erc-session-server server)
1576 (erc-port-equal erc-session-port port))))
1577 buf-name
1578 (generate-new-buffer-name buf-name))))
1579
1580(defun erc-get-buffer-create (server port target &optional proc)
1581 "Create a new buffer based on the arguments."
1582 (get-buffer-create (erc-generate-new-buffer-name server port target proc)))
1583
1584
1585(defun erc-member-ignore-case (string list)
1586 "Return non-nil if STRING is a member of LIST.
1587
1588All strings are compared according to IRC protocol case rules, see
1589`erc-downcase'."
1590 (setq string (erc-downcase string))
1591 (catch 'result
1592 (while list
1593 (if (string= string (erc-downcase (car list)))
83dc6995
MB
1594 (throw 'result list)
1595 (setq list (cdr list))))))
597993cf
MB
1596
1597(defmacro erc-with-buffer (spec &rest body)
1598 "Execute BODY in the buffer associated with SPEC.
1599
1600SPEC should have the form
1601
1602 (TARGET [PROCESS])
1603
1604If TARGET is a buffer, use it. Otherwise, use the buffer
1605matching TARGET in the process specified by PROCESS.
1606
a3d2f0a3 1607If PROCESS is nil, use the current `erc-server-process'.
597993cf
MB
1608See `erc-get-buffer' for details.
1609
1610See also `with-current-buffer'.
1611
1612\(fn (TARGET [PROCESS]) BODY...)"
ff59d266
MB
1613 (let ((buf (make-symbol "buf"))
1614 (proc (make-symbol "proc"))
1615 (target (make-symbol "target"))
1616 (process (make-symbol "process")))
597993cf
MB
1617 `(let* ((,target ,(car spec))
1618 (,process ,(cadr spec))
1619 (,buf (if (bufferp ,target)
1620 ,target
1621 (let ((,proc (or ,process
1622 (and (processp erc-server-process)
1623 erc-server-process))))
1624 (if (and ,target ,proc)
1625 (erc-get-buffer ,target ,proc))))))
ff59d266 1626 (when (buffer-live-p ,buf)
597993cf
MB
1627 (with-current-buffer ,buf
1628 ,@body)))))
1629(put 'erc-with-buffer 'lisp-indent-function 1)
1630(put 'erc-with-buffer 'edebug-form-spec '((form &optional form) body))
1631
1632(defun erc-get-buffer (target &optional proc)
1633 "Return the buffer matching TARGET in the process PROC.
1634If PROC is not supplied, all processes are searched."
1635 (let ((downcased-target (erc-downcase target)))
1636 (catch 'buffer
1637 (erc-buffer-filter
1638 (lambda ()
1639 (let ((current (erc-default-target)))
1640 (and (stringp current)
1641 (string-equal downcased-target (erc-downcase current))
1642 (throw 'buffer (current-buffer)))))
1643 proc))))
1644
1645(defun erc-buffer-filter (predicate &optional proc)
1646 "Return a list of `erc-mode' buffers matching certain criteria.
1647PREDICATE is a function executed with each buffer, if it returns t, that buffer
1648is considered a valid match.
1649
1650PROC is either an `erc-server-process', identifying a certain
1651server connection, or nil which means all open connections."
1652 (save-excursion
1653 (delq
1654 nil
1655 (mapcar (lambda (buf)
0b6bb130
MB
1656 (when (buffer-live-p buf)
1657 (with-current-buffer buf
1658 (and (eq major-mode 'erc-mode)
1659 (or (not proc)
1660 (eq proc erc-server-process))
1661 (funcall predicate)
1662 buf))))
597993cf
MB
1663 (buffer-list)))))
1664
1665(defun erc-buffer-list (&optional predicate proc)
1666 "Return a list of ERC buffers.
1667PREDICATE is a function which executes with every buffer satisfying
1668the predicate. If PREDICATE is passed as nil, return a list of all ERC
1669buffers. If PROC is given, the buffers local variable `erc-server-process'
1670needs to match PROC."
1671 (unless predicate
1672 (setq predicate (lambda () t)))
1673 (erc-buffer-filter predicate proc))
1674
1675(defmacro erc-with-all-buffers-of-server (process pred &rest forms)
1676 "Execute FORMS in all buffers which have same process as this server.
1677FORMS will be evaluated in all buffers having the process PROCESS and
1678where PRED matches or in all buffers of the server process if PRED is
1679nil."
1680 ;; Make the evaluation have the correct order
ff59d266
MB
1681 (let ((pre (make-symbol "pre"))
1682 (pro (make-symbol "pro")))
88406d6e
MO
1683 `(let* ((,pro ,process)
1684 (,pre ,pred)
1685 (res (mapcar (lambda (buffer)
1686 (with-current-buffer buffer
1687 ,@forms))
1688 (erc-buffer-list ,pre
1689 ,pro))))
1690 ;; Silence the byte-compiler by binding the result of mapcar to
1691 ;; a variable.
1692 res)))
597993cf
MB
1693(put 'erc-with-all-buffers-of-server 'lisp-indent-function 1)
1694(put 'erc-with-all-buffers-of-server 'edebug-form-spec '(form form body))
1695
e3abf9d9 1696;; (iswitchb-mode) will autoload iswitchb.el
5ba3c59a
GM
1697(defvar iswitchb-temp-buflist)
1698(declare-function iswitchb-read-buffer "iswitchb"
1699 (prompt &optional default require-match start matches-set))
1700
597993cf
MB
1701(defun erc-iswitchb (&optional arg)
1702 "Use `iswitchb-read-buffer' to prompt for a ERC buffer to switch to.
1703When invoked with prefix argument, use all erc buffers. Without prefix
1704ARG, allow only buffers related to same session server.
1705If `erc-track-mode' is in enabled, put the last element of
1706`erc-modified-channels-alist' in front of the buffer list.
1707
1708Due to some yet unresolved reason, global function `iswitchb-mode'
1709needs to be active for this function to work."
1710 (interactive "P")
5ba3c59a 1711 (let ((enabled (bound-and-true-p iswitchb-mode)))
16e62ecc 1712 (or enabled (iswitchb-mode 1))
10dc9f9e
MB
1713 (unwind-protect
1714 (let ((iswitchb-make-buflist-hook
1715 (lambda ()
1716 (setq iswitchb-temp-buflist
1717 (mapcar 'buffer-name
1718 (erc-buffer-list
1719 nil
1720 (when arg erc-server-process)))))))
1721 (switch-to-buffer
1722 (iswitchb-read-buffer
1723 "Switch-to: "
1724 (if (boundp 'erc-modified-channels-alist)
1725 (buffer-name (caar (last erc-modified-channels-alist)))
1726 nil)
1727 t)))
1728 (or enabled (iswitchb-mode -1)))))
597993cf
MB
1729
1730(defun erc-channel-list (proc)
1731 "Return a list of channel buffers.
1732PROC is the process for the server connection. If PROC is nil, return
1733all channel buffers on all servers."
1734 (erc-buffer-filter
1735 (lambda ()
1736 (and (erc-default-target)
1737 (erc-channel-p (erc-default-target))))
1738 proc))
1739
1740(defun erc-buffer-list-with-nick (nick proc)
1741 "Return buffers containing NICK in the `erc-channel-users' list."
1742 (with-current-buffer (process-buffer proc)
1743 (let ((user (gethash (erc-downcase nick) erc-server-users)))
1744 (if user
1745 (erc-server-user-buffers user)
1746 nil))))
1747
1748;; Some local variables
1749
1750(defvar erc-default-recipients nil
1751 "List of default recipients of the current buffer.")
1752(make-variable-buffer-local 'erc-default-recipients)
1753
1754(defvar erc-session-user-full-name nil
1755 "Full name of the user on the current server.")
1756(make-variable-buffer-local 'erc-session-user-full-name)
1757
1758(defvar erc-channel-user-limit nil
1759 "Limit of users per channel.")
1760(make-variable-buffer-local 'erc-channel-user-limit)
1761
1762(defvar erc-channel-key nil
1763 "Key needed to join channel.")
1764(make-variable-buffer-local 'erc-channel-key)
1765
1766(defvar erc-invitation nil
1767 "Last invitation channel.")
1768(make-variable-buffer-local 'erc-invitation)
1769
1770(defvar erc-away nil
ff59d266
MB
1771 "Non-nil indicates that we are away.
1772
1773Use `erc-away-time' to access this if you might be in a channel
1774buffer rather than a server buffer.")
597993cf
MB
1775(make-variable-buffer-local 'erc-away)
1776
1777(defvar erc-channel-list nil
1778 "Server channel list.")
1779(make-variable-buffer-local 'erc-channel-list)
1780
1781(defvar erc-bad-nick nil
1782 "Non-nil indicates that we got a `nick in use' error while connecting.")
1783(make-variable-buffer-local 'erc-bad-nick)
1784
1785(defvar erc-logged-in nil
1786 "Non-nil indicates that we are logged in.")
1787(make-variable-buffer-local 'erc-logged-in)
1788
1789(defvar erc-default-nicks nil
1790 "The local copy of `erc-nick' - the list of nicks to choose from.")
1791(make-variable-buffer-local 'erc-default-nicks)
1792
1793(defvar erc-nick-change-attempt-count 0
1794 "Used to keep track of how many times an attempt at changing nick is made.")
1795(make-variable-buffer-local 'erc-nick-change-attempt-count)
1796
c6b99621
MB
1797(defun erc-migrate-modules (mods)
1798 "Migrate old names of ERC modules to new ones."
1799 ;; modify `transforms' to specify what needs to be changed
9cc8d0b6
MB
1800 ;; each item is in the format '(old . new)
1801 (let ((transforms '((pcomplete . completion))))
1802 (erc-delete-dups
1803 (mapcar (lambda (m) (or (cdr (assoc m transforms)) m))
1804 mods))))
1805
1806(defcustom erc-modules '(netsplit fill button match track completion readonly
597993cf 1807 ring autojoin noncommands irccontrols
6904f7fe 1808 stamp menu)
a3d2f0a3 1809 "A list of modules which ERC should enable.
597993cf
MB
1810If you set the value of this without using `customize' remember to call
1811\(erc-update-modules) after you change it. When using `customize', modules
1812removed from the list will be disabled."
c6b99621
MB
1813 :get (lambda (sym)
1814 ;; replace outdated names with their newer equivalents
1815 (erc-migrate-modules (symbol-value sym)))
597993cf
MB
1816 :set (lambda (sym val)
1817 ;; disable modules which have just been removed
1818 (when (and (boundp 'erc-modules) erc-modules val)
1819 (dolist (module erc-modules)
1820 (unless (member module val)
1821 (let ((f (intern-soft (format "erc-%s-mode" module))))
1822 (when (and (fboundp f) (boundp f) (symbol-value f))
1823 (message "Disabling `erc-%s'" module)
1824 (funcall f 0))))))
c6b99621 1825 (set sym val)
597993cf
MB
1826 ;; this test is for the case where erc hasn't been loaded yet
1827 (when (fboundp 'erc-update-modules)
1828 (erc-update-modules)))
9cc8d0b6
MB
1829 :type
1830 '(set
1831 :greedy t
ff59d266
MB
1832 (const :tag "autoaway: Set away status automatically" autoaway)
1833 (const :tag "autojoin: Join channels automatically" autojoin)
1834 (const :tag "button: Buttonize URLs, nicknames, and other text" button)
1835 (const :tag "capab: Mark unidentified users on servers supporting CAPAB"
1836 capab-identify)
1837 (const :tag "completion: Complete nicknames and commands (programmable)"
1838 completion)
1839 (const :tag "hecomplete: Complete nicknames and commands (old)" hecomplete)
1840 (const :tag "fill: Wrap long lines" fill)
1841 (const :tag "identd: Launch an identd server on port 8113" identd)
1842 (const :tag "irccontrols: Highlight or remove IRC control characters"
9cc8d0b6 1843 irccontrols)
ff59d266
MB
1844 (const :tag "log: Save buffers in logs" log)
1845 (const :tag "match: Highlight pals, fools, and other keywords" match)
1846 (const :tag "menu: Display a menu in ERC buffers" menu)
1847 (const :tag "netsplit: Detect netsplits" netsplit)
1848 (const :tag "noncommands: Don't display non-IRC commands after evaluation"
9cc8d0b6 1849 noncommands)
ff59d266
MB
1850 (const :tag
1851 "notify: Notify when the online status of certain users changes"
9cc8d0b6 1852 notify)
ff59d266
MB
1853 (const :tag "page: Process CTCP PAGE requests from IRC" page)
1854 (const :tag "readonly: Make displayed lines read-only" readonly)
1855 (const :tag "replace: Replace text in messages" replace)
1856 (const :tag "ring: Enable an input history" ring)
1857 (const :tag "scrolltobottom: Scroll to the bottom of the buffer"
1858 scrolltobottom)
1859 (const :tag "services: Identify to Nickserv (IRC Services) automatically"
9cc8d0b6 1860 services)
ff59d266
MB
1861 (const :tag "smiley: Convert smileys to pretty icons" smiley)
1862 (const :tag "sound: Play sounds when you receive CTCP SOUND requests"
9cc8d0b6 1863 sound)
ff59d266
MB
1864 (const :tag "stamp: Add timestamps to messages" stamp)
1865 (const :tag "spelling: Check spelling" spelling)
1866 (const :tag "track: Track channel activity in the mode-line" track)
1867 (const :tag "truncate: Truncate buffers to a certain size" truncate)
1868 (const :tag "unmorse: Translate morse code in messages" unmorse)
9cc8d0b6 1869 (repeat :tag "Others" :inline t symbol))
597993cf
MB
1870 :group 'erc)
1871
1872(defun erc-update-modules ()
1873 "Run this to enable erc-foo-mode for all modules in `erc-modules'."
1874 (let (req)
1875 (dolist (mod erc-modules)
1876 (setq req (concat "erc-" (symbol-name mod)))
1877 (cond
1878 ;; yuck. perhaps we should bring the filenames into sync?
6904f7fe
MB
1879 ((string= req "erc-capab-identify")
1880 (setq req "erc-capab"))
597993cf 1881 ((string= req "erc-completion")
9cc8d0b6 1882 (setq req "erc-pcomplete"))
c6b99621 1883 ((string= req "erc-pcomplete")
c6b99621
MB
1884 (setq mod 'completion))
1885 ((string= req "erc-autojoin")
9cc8d0b6 1886 (setq req "erc-join")))
597993cf
MB
1887 (condition-case nil
1888 (require (intern req))
1889 (error nil))
83dc6995
MB
1890 (let ((sym (intern-soft (concat "erc-" (symbol-name mod) "-mode"))))
1891 (if (fboundp sym)
1892 (funcall sym 1)
1893 (error "`%s' is not a known ERC module" mod))))))
597993cf
MB
1894
1895(defun erc-setup-buffer (buffer)
1896 "Consults `erc-join-buffer' to find out how to display `BUFFER'."
1897 (cond ((eq erc-join-buffer 'window)
1898 (if (active-minibuffer-window)
1899 (display-buffer buffer)
1900 (switch-to-buffer-other-window buffer)))
1901 ((eq erc-join-buffer 'window-noselect)
1902 (display-buffer buffer))
1903 ((eq erc-join-buffer 'bury)
1904 nil)
1905 ((eq erc-join-buffer 'frame)
526dc846
MO
1906 (when (or (not erc-reuse-frames)
1907 (not (get-buffer-window buffer t)))
1908 ((lambda (frame)
597993cf
MB
1909 (raise-frame frame)
1910 (select-frame frame))
1911 (make-frame (or erc-frame-alist
1912 default-frame-alist)))
1913 (switch-to-buffer buffer)
1914 (when erc-frame-dedicated-flag
526dc846 1915 (set-window-dedicated-p (selected-window) t))))
597993cf
MB
1916 (t
1917 (if (active-minibuffer-window)
1918 (display-buffer buffer)
1919 (switch-to-buffer buffer)))))
1920
83dc6995
MB
1921(defun erc-open (&optional server port nick full-name
1922 connect passwd tgt-list channel process)
ff59d266 1923 "Connect to SERVER on PORT as NICK with FULL-NAME.
597993cf
MB
1924
1925If CONNECT is non-nil, connect to the server. Otherwise assume
1926already connected and just create a separate buffer for the new
1927target CHANNEL.
1928
1929Use PASSWD as user password on the server. If TGT-LIST is
2c5a0575 1930non-nil, use it to initialize `erc-default-recipients'.
597993cf
MB
1931
1932Returns the buffer for the given server or channel."
1933 (let ((server-announced-name (when (and (boundp 'erc-session-server)
1934 (string= server erc-session-server))
1935 erc-server-announced-name))
1936 (connected-p (unless connect erc-server-connected))
1937 (buffer (erc-get-buffer-create server port channel))
1938 (old-buffer (current-buffer))
10dc9f9e 1939 old-point
597993cf 1940 continued-session)
6904f7fe 1941 (when connect (run-hook-with-args 'erc-before-connect server port nick))
597993cf
MB
1942 (erc-update-modules)
1943 (set-buffer buffer)
10dc9f9e 1944 (setq old-point (point))
597993cf
MB
1945 (erc-mode)
1946 (setq erc-server-announced-name server-announced-name)
1947 (setq erc-server-connected connected-p)
1948 ;; connection parameters
1949 (setq erc-server-process process)
1950 (setq erc-insert-marker (make-marker))
1951 (setq erc-input-marker (make-marker))
1952 ;; go to the end of the buffer and open a new line
1953 ;; (the buffer may have existed)
1954 (goto-char (point-max))
1955 (forward-line 0)
1956 (when (get-text-property (point) 'erc-prompt)
1957 (setq continued-session t)
1958 (set-marker erc-input-marker
1959 (or (next-single-property-change (point) 'erc-prompt)
1960 (point-max))))
1961 (unless continued-session
1962 (goto-char (point-max))
1963 (insert "\n"))
1964 (set-marker erc-insert-marker (point))
1965 ;; stack of default recipients
1966 (setq erc-default-recipients tgt-list)
1967 (setq erc-server-current-nick nil)
1968 ;; Initialize erc-server-users and erc-channel-users
1969 (if connect
1970 (progn ;; server buffer
1971 (setq erc-server-users
1972 (make-hash-table :test 'equal))
1973 (setq erc-channel-users nil))
1974 (progn ;; target buffer
1975 (setq erc-server-users nil)
1976 (setq erc-channel-users
1977 (make-hash-table :test 'equal))))
1978 ;; clear last incomplete line read
1979 (setq erc-server-filter-data nil)
1980 (setq erc-channel-topic "")
1981 ;; limit on the number of users on the channel (mode +l)
1982 (setq erc-channel-user-limit nil)
1983 (setq erc-channel-key nil)
1984 ;; last active buffer, defaults to this one
1985 (erc-set-active-buffer buffer)
1986 ;; last invitation channel
1987 (setq erc-invitation nil)
597993cf
MB
1988 ;; Server channel list
1989 (setq erc-channel-list ())
1990 ;; login-time 'nick in use' error
1991 (setq erc-bad-nick nil)
1992 ;; whether we have logged in
1993 (setq erc-logged-in nil)
1994 ;; The local copy of `erc-nick' - the list of nicks to choose
1995 (setq erc-default-nicks (if (consp erc-nick) erc-nick (list erc-nick)))
1996 ;; password stuff
1997 (setq erc-session-password passwd)
1998 ;; debug output buffer
1999 (setq erc-dbuf
2000 (when erc-log-p
2001 (get-buffer-create (concat "*ERC-DEBUG: " server "*"))))
ff59d266 2002 ;; set up prompt
597993cf
MB
2003 (unless continued-session
2004 (goto-char (point-max))
2005 (insert "\n"))
e7559e30 2006 (if continued-session
83dc6995 2007 (goto-char old-point)
597993cf
MB
2008 (set-marker erc-insert-marker (point))
2009 (erc-display-prompt)
2010 (goto-char (point-max)))
2011
ff59d266
MB
2012 (erc-determine-parameters server port nick full-name)
2013
2014 ;; Saving log file on exit
2015 (run-hook-with-args 'erc-connect-pre-hook buffer)
2016
2017 (when connect
2018 (erc-server-connect erc-session-server erc-session-port buffer))
2019 (erc-update-mode-line)
2020
597993cf
MB
2021 ;; Now display the buffer in a window as per user wishes.
2022 (unless (eq buffer old-buffer)
2023 (when erc-log-p
2024 ;; we can't log to debug buffer, it may not exist yet
2025 (message "erc: old buffer %s, switching to %s"
2026 old-buffer buffer))
2027 (erc-setup-buffer buffer))
2028
2029 buffer))
2030
ff59d266
MB
2031(defun erc-initialize-log-marker (buffer)
2032 "Initialize the `erc-last-saved-position' marker to a sensible position.
2033BUFFER is the current buffer."
2034 (with-current-buffer buffer
597993cf
MB
2035 (setq erc-last-saved-position (make-marker))
2036 (move-marker erc-last-saved-position
ff59d266 2037 (1- (marker-position erc-insert-marker)))))
597993cf
MB
2038
2039;; interactive startup
2040
2041(defvar erc-server-history-list nil
2042 "IRC server interactive selection history list.")
2043
2044(defvar erc-nick-history-list nil
2045 "Nickname interactive selection history list.")
2046
2047(defun erc-already-logged-in (server port nick)
2048 "Return the buffers corresponding to a NICK on PORT of a session SERVER.
2049This is determined by looking for the appropriate buffer and checking
2050whether the connection is still alive.
2051If no buffer matches, return nil."
2052 (erc-buffer-list
2053 (lambda ()
2054 (and (erc-server-process-alive)
2055 (string= erc-session-server server)
2056 (erc-port-equal erc-session-port port)
2057 (erc-current-nick-p nick)))))
2058
2059(if (not (fboundp 'read-passwd))
2060 (defun read-passwd (prompt)
09322f26 2061 "Substitute for `read-passwd' in early emacsen."
597993cf
MB
2062 (read-from-minibuffer prompt)))
2063
2064(defcustom erc-before-connect nil
2065 "Hook called before connecting to a server.
83dc6995 2066This hook gets executed before `erc' actually invokes `erc-mode'
597993cf
MB
2067with your input data. The functions in here get called with three
2068parameters, SERVER, PORT and NICK."
2069 :group 'erc-hooks
2070 :type 'hook)
2071
2072(defcustom erc-after-connect nil
2073 "Hook called after connecting to a server.
8c08b57a 2074This hook gets executed when an end of MOTD has been received. All
597993cf
MB
2075functions in here get called with the parameters SERVER and NICK."
2076 :group 'erc-hooks
2077 :type 'hook)
2078
2079;;;###autoload
2080(defun erc-select-read-args ()
2081 "Prompt the user for values of nick, server, port, and password."
2082 (let (user-input server port nick passwd)
2083 (setq user-input (read-from-minibuffer
2084 "IRC server: "
2085 (erc-compute-server) nil nil 'erc-server-history-list))
2086
2087 (if (string-match "\\(.*\\):\\(.*\\)\\'" user-input)
2088 (setq port (erc-string-to-port (match-string 2 user-input))
2089 user-input (match-string 1 user-input))
2090 (setq port
2091 (erc-string-to-port (read-from-minibuffer
2092 "IRC port: " (erc-port-to-string
2093 (erc-compute-port))))))
2094
2095 (if (string-match "\\`\\(.*\\)@\\(.*\\)" user-input)
2096 (setq nick (match-string 1 user-input)
2097 user-input (match-string 2 user-input))
2098 (setq nick
2099 (if (erc-already-logged-in server port nick)
2100 (read-from-minibuffer
2101 (erc-format-message 'nick-in-use ?n nick)
2102 nick
2103 nil nil 'erc-nick-history-list)
2104 (read-from-minibuffer
2105 "Nickname: " (erc-compute-nick nick)
2106 nil nil 'erc-nick-history-list))))
2107
2108 (setq server user-input)
2109
2110 (setq passwd (if erc-prompt-for-password
2111 (if (and erc-password
2112 (y-or-n-p "Use the default password? "))
2113 erc-password
2114 (read-passwd "Password: "))
2115 erc-password))
2116 (when (and passwd (string= "" passwd))
2117 (setq passwd nil))
2118
2119 (while (erc-already-logged-in server port nick)
2120 ;; hmm, this is a problem when using multiple connections to a bnc
2121 ;; with the same nick. Currently this code prevents using more than one
2122 ;; bnc with the same nick. actually it would be nice to have
2123 ;; bncs transparent, so that erc-compute-buffer-name displays
2124 ;; the server one is connected to.
2125 (setq nick (read-from-minibuffer
2126 (erc-format-message 'nick-in-use ?n nick)
2127 nick
2128 nil nil 'erc-nick-history-list)))
2129 (list :server server :port port :nick nick :password passwd)))
2130
2131;;;###autoload
83dc6995
MB
2132(defun* erc (&key (server (erc-compute-server))
2133 (port (erc-compute-port))
2134 (nick (erc-compute-nick))
2135 password
2136 (full-name (erc-compute-full-name)))
ff59d266
MB
2137 "ERC is a powerful, modular, and extensible IRC client.
2138This function is the main entry point for ERC.
2139
2140It permits you to select connection parameters, and then starts ERC.
2141
2142Non-interactively, it takes the keyword arguments
597993cf
MB
2143 (server (erc-compute-server))
2144 (port (erc-compute-port))
2145 (nick (erc-compute-nick))
2146 password
2147 (full-name (erc-compute-full-name)))
2148
2149That is, if called with
0b6bb130 2150
83dc6995 2151 (erc :server \"irc.freenode.net\" :full-name \"Harry S Truman\")
0b6bb130 2152
ff59d266 2153then the server and full-name will be set to those values, whereas
0b6bb130
MB
2154`erc-compute-port', `erc-compute-nick' and `erc-compute-full-name' will
2155be invoked for the values of the other parameters."
597993cf 2156 (interactive (erc-select-read-args))
83dc6995 2157 (erc-open server port nick full-name t password))
597993cf 2158
ff59d266 2159;;;###autoload
83dc6995 2160(defalias 'erc-select 'erc)
597993cf 2161
83dc6995 2162(defun erc-ssl (&rest r)
597993cf 2163 "Interactively select SSL connection parameters and run ERC.
83dc6995 2164Arguments are the same as for `erc'."
597993cf
MB
2165 (interactive (erc-select-read-args))
2166 (let ((erc-server-connect-function 'erc-open-ssl-stream))
83dc6995
MB
2167 (apply 'erc r)))
2168
2169(defalias 'erc-select-ssl 'erc-ssl)
597993cf 2170
f29263b3
DN
2171(declare-function open-ssl-stream "ext:ssl" (name buffer host service))
2172
597993cf
MB
2173(defun erc-open-ssl-stream (name buffer host port)
2174 "Open an SSL stream to an IRC server.
2175The process will be given the name NAME, its target buffer will be
8c08b57a 2176BUFFER. HOST and PORT specify the connection target."
526dc846
MO
2177 (when (condition-case nil
2178 (require 'ssl)
2179 (error (message "You don't have ssl.el. %s"
2180 "Try using `erc-tls' instead.")
2181 nil))
2182 (let ((proc (open-ssl-stream name buffer host port)))
597993cf
MB
2183 ;; Ugly hack, but it works for now. Problem is it is
2184 ;; very hard to detect when ssl is established, because s_client
2185 ;; doesn't give any CONNECTIONESTABLISHED kind of message, and
2186 ;; most IRC servers send nothing and wait for you to identify.
526dc846 2187 (sit-for 5)
597993cf
MB
2188 proc)))
2189
526dc846
MO
2190(defun erc-tls (&rest r)
2191 "Interactively select TLS connection parameters and run ERC.
2192Arguments are the same as for `erc'."
2193 (interactive (erc-select-read-args))
2194 (let ((erc-server-connect-function 'erc-open-tls-stream))
2195 (apply 'erc r)))
2196
f29263b3
DN
2197(declare-function open-tls-stream "tls" (name buffer host port))
2198
526dc846
MO
2199(defun erc-open-tls-stream (name buffer host port)
2200 "Open an TLS stream to an IRC server.
2201The process will be given the name NAME, its target buffer will be
2202BUFFER. HOST and PORT specify the connection target."
2203 (when (condition-case nil
2204 (require 'tls)
2205 (error (message "You don't have tls.el. %s"
2206 "Try using `erc-ssl' instead.")
2207 nil))
2208 (open-tls-stream name buffer host port)))
2209
2210;;; Displaying error messages
2211
2212(defun erc-error (&rest args)
2213 "Pass ARGS to `format', and display the result as an error message.
2214If `debug-on-error' is set to non-nil, then throw a real error with this
2215message instead, to make debugging easier."
2216 (if debug-on-error
2217 (apply #'error args)
2218 (apply #'message args)
2219 (beep)))
2220
597993cf
MB
2221;;; Debugging the protocol
2222
2223(defvar erc-debug-irc-protocol nil
2224 "If non-nil, log all IRC protocol traffic to the buffer \"*erc-protocol*\".
2225
2226The buffer is created if it doesn't exist.
2227
a3d2f0a3 2228NOTE: If this variable is non-nil, and you kill the only
597993cf
MB
2229visible \"*erc-protocol*\" buffer, it will be recreated shortly,
2230but you won't see it.
2231
2232WARNING: Do not set this variable directly! Instead, use the
2233function `erc-toggle-debug-irc-protocol' to toggle its value.")
2234
f29263b3
DN
2235(declare-function erc-network-name "erc-networks" ())
2236
597993cf
MB
2237(defun erc-log-irc-protocol (string &optional outbound)
2238 "Append STRING to the buffer *erc-protocol*.
2239
2240This only has any effect if `erc-debug-irc-protocol' is non-nil.
2241
2242The buffer is created if it doesn't exist.
2243
2244If OUTBOUND is non-nil, STRING is being sent to the IRC server
a3d2f0a3 2245and appears in face `erc-input-face' in the buffer."
597993cf
MB
2246 (when erc-debug-irc-protocol
2247 (let ((network-name (or (ignore-errors (erc-network-name))
2248 "???")))
2249 (with-current-buffer (get-buffer-create "*erc-protocol*")
2250 (save-excursion
2251 (goto-char (point-max))
2252 (let ((inhibit-read-only t))
2253 (insert (if (not outbound)
2254 ;; Cope with the fact that string might
2255 ;; contain multiple lines of text.
2256 (let ((lines (delete "" (split-string string
2257 "\n\\|\r\n")))
2258 (result ""))
2259 (dolist (line lines)
2260 (setq result (concat result network-name
2261 " << " line "\n")))
2262 result)
2263 (erc-propertize
2264 (concat network-name " >> " string
2265 (if (/= ?\n
2266 (aref string
2267 (1- (length string))))
2268 "\n"))
2269 'face 'erc-input-face)))))
2270 (let ((orig-win (selected-window))
2271 (debug-buffer-window (get-buffer-window (current-buffer) t)))
2272 (when debug-buffer-window
2273 (select-window debug-buffer-window)
2274 (when (= 1 (count-lines (point) (point-max)))
2275 (goto-char (point-max))
2276 (recenter -1))
2277 (select-window orig-win)))))))
2278
2279(defun erc-toggle-debug-irc-protocol (&optional arg)
2280 "Toggle the value of `erc-debug-irc-protocol'.
2281
2282If ARG is non-nil, show the *erc-protocol* buffer."
2283 (interactive "P")
2284 (let* ((buf (get-buffer-create "*erc-protocol*")))
2285 (with-current-buffer buf
645e533a 2286 (erc-view-mode-enter)
597993cf
MB
2287 (when (null (current-local-map))
2288 (let ((inhibit-read-only t))
2289 (insert (erc-make-notice "This buffer displays all IRC protocol traffic exchanged with each server.\n"))
2290 (insert (erc-make-notice "Kill this buffer to terminate protocol logging.\n\n")))
2291 (use-local-map (make-sparse-keymap))
2292 (local-set-key (kbd "RET") 'erc-toggle-debug-irc-protocol))
2293 (add-hook 'kill-buffer-hook
2294 #'(lambda () (setq erc-debug-irc-protocol nil))
2295 nil 'local)
2296 (goto-char (point-max))
2297 (let ((inhibit-read-only t))
2298 (insert (erc-make-notice
2299 (format "IRC protocol logging %s at %s -- Press ENTER to toggle logging.\n"
2300 (if erc-debug-irc-protocol "disabled" "enabled")
2301 (current-time-string))))))
2302 (setq erc-debug-irc-protocol (not erc-debug-irc-protocol))
2303 (if (and arg
2304 (not (get-buffer-window "*erc-protocol*" t)))
2305 (display-buffer buf t))
2306 (message "IRC protocol traffic logging %s (see buffer *erc-protocol*)."
2307 (if erc-debug-irc-protocol "enabled" "disabled"))))
2308
2309;;; I/O interface
2310
2311;; send interface
2312
2313(defun erc-send-action (tgt str &optional force)
2314 "Send CTCP ACTION information described by STR to TGT."
2315 (erc-send-ctcp-message tgt (format "ACTION %s" str) force)
2316 (erc-display-message
2317 nil 'input (current-buffer)
2318 'ACTION ?n (erc-current-nick) ?a str ?u "" ?h ""))
2319
2320;; Display interface
2321
2322(defun erc-string-invisible-p (string)
2323 "Check whether STRING is invisible or not.
2324I.e. any char in it has the `invisible' property set."
2325 (text-property-any 0 (length string) 'invisible t string))
2326
2327(defun erc-display-line-1 (string buffer)
2328 "Display STRING in `erc-mode' BUFFER.
8c08b57a 2329Auxiliary function used in `erc-display-line'. The line gets filtered to
597993cf
MB
2330interpret the control characters. Then, `erc-insert-pre-hook' gets called.
2331If `erc-insert-this' is still t, STRING gets inserted into the buffer.
2332Afterwards, `erc-insert-modify' and `erc-insert-post-hook' get called.
2333If STRING is nil, the function does nothing."
2334 (when string
2335 (save-excursion
2336 (set-buffer (or buffer (process-buffer erc-server-process)))
2337 (let ((insert-position (or (marker-position erc-insert-marker)
2338 (point-max))))
2339 (let ((string string) ;; FIXME! Can this be removed?
2340 (buffer-undo-list t)
2341 (inhibit-read-only t))
2342 (unless (string-match "\n$" string)
2343 (setq string (concat string "\n"))
2344 (when (erc-string-invisible-p string)
2345 (erc-put-text-properties 0 (length string) string
2346 '(invisible intangible))))
2347 (erc-log (concat "erc-display-line: " string
2348 (format "(%S)" string) " in buffer "
2349 (format "%s" buffer)))
2350 (setq erc-insert-this t)
2351 (run-hook-with-args 'erc-insert-pre-hook string)
2352 (if (null erc-insert-this)
2353 ;; Leave erc-insert-this set to t as much as possible. Fran
2354 ;; Litterio <franl> has seen erc-insert-this set to nil while
2355 ;; erc-send-pre-hook is running, which should never happen. This
2356 ;; may cure it.
2357 (setq erc-insert-this t)
2358 (save-excursion ;; to restore point in the new buffer
2359 (save-restriction
2360 (widen)
2361 (goto-char insert-position)
2362 (insert-before-markers string)
2363 ;; run insertion hook, with point at restored location
2364 (save-restriction
2365 (narrow-to-region insert-position (point))
2366 (run-hooks 'erc-insert-modify-hook)
2367 (run-hooks 'erc-insert-post-hook))))))
2368 (erc-update-undo-list (- (or (marker-position erc-insert-marker)
2369 (point-max))
2370 insert-position))))))
2371
2372(defun erc-update-undo-list (shift)
2373 ;; Translate buffer positions in buffer-undo-list by SHIFT.
2374 (unless (or (zerop shift) (atom buffer-undo-list))
2375 (let ((list buffer-undo-list) elt)
2376 (while list
2377 (setq elt (car list))
2378 (cond ((integerp elt) ; POSITION
2379 (incf (car list) shift))
2380 ((or (atom elt) ; nil, EXTENT
2381 ;; (eq t (car elt)) ; (t HIGH . LOW)
2382 (markerp (car elt))) ; (MARKER . DISTANCE)
2383 nil)
2384 ((integerp (car elt)) ; (BEGIN . END)
2385 (incf (car elt) shift)
2386 (incf (cdr elt) shift))
2387 ((stringp (car elt)) ; (TEXT . POSITION)
2388 (incf (cdr elt) (* (if (natnump (cdr elt)) 1 -1) shift)))
2389 ((null (car elt)) ; (nil PROPERTY VALUE BEG . END)
2390 (let ((cons (nthcdr 3 elt)))
2391 (incf (car cons) shift)
2392 (incf (cdr cons) shift)))
2393 ((and (featurep 'xemacs)
2394 (extentp (car elt))) ; (EXTENT START END)
2395 (incf (nth 1 elt) shift)
2396 (incf (nth 2 elt) shift)))
2397 (setq list (cdr list))))))
2398
2399(defvar erc-valid-nick-regexp "[]a-zA-Z^[;\\`_{}|][]^[;\\`_{}|a-zA-Z0-9-]*"
2400 "Regexp which matches all legal characters in a IRC nickname.")
2401
2402(defun erc-is-valid-nick-p (nick)
2403 "Check if NICK is a valid IRC nickname."
2404 (string-match (concat "^" erc-valid-nick-regexp "$") nick))
2405
2406(defun erc-display-line (string &optional buffer)
2407 "Display STRING in the ERC BUFFER.
2408All screen output must be done through this function. If BUFFER is nil
2409or omitted, the default ERC buffer for the `erc-session-server' is used.
2410The BUFFER can be an actual buffer, a list of buffers, 'all or 'active.
2411If BUFFER = 'all, the string is displayed in all the ERC buffers for the
2412current session. 'active means the current active buffer
8c08b57a
JB
2413\(`erc-active-buffer'). If the buffer can't be resolved, the current
2414buffer is used. `erc-display-line-1' is used to display STRING.
597993cf
MB
2415
2416If STRING is nil, the function does nothing."
2417 (let ((inhibit-point-motion-hooks t)
2418 new-bufs)
2419 (dolist (buf (cond
2420 ((bufferp buffer) (list buffer))
2421 ((listp buffer) buffer)
2422 ((processp buffer) (list (process-buffer buffer)))
2423 ((eq 'all buffer)
83dc6995
MB
2424 ;; Hmm, or all of the same session server?
2425 (erc-buffer-list nil erc-server-process))
597993cf
MB
2426 ((and (eq 'active buffer) (erc-active-buffer))
2427 (list (erc-active-buffer)))
2428 ((erc-server-buffer-live-p)
2429 (list (process-buffer erc-server-process)))
2430 (t (list (current-buffer)))))
2431 (when (buffer-live-p buf)
2432 (erc-display-line-1 string buf)
2433 (add-to-list 'new-bufs buf)))
2434 (when (null new-bufs)
2435 (if (erc-server-buffer-live-p)
2436 (erc-display-line-1 string (process-buffer erc-server-process))
2437 (erc-display-line-1 string (current-buffer))))))
2438
2439(defun erc-display-message-highlight (type string)
a3d2f0a3 2440 "Highlight STRING according to TYPE, where erc-TYPE-face is an ERC face.
597993cf 2441
a3d2f0a3 2442See also `erc-make-notice'."
597993cf
MB
2443 (cond ((eq type 'notice)
2444 (erc-make-notice string))
2445 (t
2446 (erc-put-text-property
2447 0 (length string)
2448 'face (or (intern-soft
2449 (concat "erc-" (symbol-name type) "-face"))
2450 "erc-default-face")
2451 string)
2452 string)))
2453
2454(defun erc-display-message (parsed type buffer msg &rest args)
2455 "Display MSG in BUFFER.
2456
2457ARGS, PARSED, and TYPE are used to format MSG sensibly.
2458
2459See also `erc-format-message' and `erc-display-line'."
2460 (let ((string (if (symbolp msg)
2461 (apply 'erc-format-message msg args)
2462 msg)))
2463 (setq string
2464 (cond
f2c05698
MB
2465 ((null type)
2466 string)
597993cf
MB
2467 ((listp type)
2468 (mapc (lambda (type)
2469 (setq string
2470 (erc-display-message-highlight type string)))
2471 type)
2472 string)
2473 ((symbolp type)
2474 (erc-display-message-highlight type string))))
2475
2476 (if (not (erc-response-p parsed))
2477 (erc-display-line string buffer)
2478 (unless (member (erc-response.command parsed) erc-hide-list)
f2c05698 2479 (erc-put-text-property 0 (length string) 'erc-parsed parsed string)
597993cf
MB
2480 (erc-put-text-property 0 (length string) 'rear-sticky t string)
2481 (erc-display-line string buffer)))))
2482
2483(defun erc-message-type-member (position list)
2484 "Return non-nil if the erc-parsed text-property at POSITION is in LIST.
2485
2486This function relies on the erc-parsed text-property being
2487present."
ff59d266 2488 (let ((prop-val (erc-get-parsed-vector position)))
597993cf
MB
2489 (and prop-val (member (erc-response.command prop-val) list))))
2490
2491(defvar erc-send-input-line-function 'erc-send-input-line)
2492(make-variable-buffer-local 'erc-send-input-line-function)
2493
2494(defun erc-send-input-line (target line &optional force)
2495 "Send LINE to TARGET.
2496
2497See also `erc-server-send'."
2498 (setq line (format "PRIVMSG %s :%s"
2499 target
2500 ;; If the line is empty, we still want to
2501 ;; send it - i.e. an empty pasted line.
2502 (if (string= line "\n")
2503 " \n"
2504 line)))
2505 (erc-server-send line force target))
2506
2507(defun erc-get-arglist (fun)
2508 "Return the argument list of a function without the parens."
2509 (let ((arglist (format "%S" (erc-function-arglist fun))))
2510 (if (string-match "^(\\(.*\\))$" arglist)
2511 (match-string 1 arglist)
2512 arglist)))
2513
526dc846
MO
2514(defun erc-command-no-process-p (str)
2515 "Return non-nil if STR is an ERC command that can be run when the process
2516is not alive, nil otherwise."
2517 (let ((fun (erc-extract-command-from-line str)))
2518 (and fun
2519 (symbolp (car fun))
2520 (get (car fun) 'process-not-needed))))
2521
597993cf
MB
2522(defun erc-command-name (cmd)
2523 "For CMD being the function name of a ERC command, something like
2524erc-cmd-FOO, this returns a string /FOO."
2525 (let ((command-name (symbol-name cmd)))
2526 (if (string-match "^erc-cmd-\\(.*\\)$" command-name)
2527 (concat "/" (match-string 1 command-name))
2528 command-name)))
2529
2530(defun erc-process-input-line (line &optional force no-command)
2531 "Translate LINE to an RFC1459 command and send it based.
2532Returns non-nil if the command is actually sent to the server, and nil
2533otherwise.
2534
2535If the command in the LINE is not bound as a function `erc-cmd-<COMMAND>',
a3d2f0a3 2536it is passed to `erc-cmd-default'. If LINE is not a command (i.e. doesn't
597993cf
MB
2537start with /<COMMAND>) then it is sent as a message.
2538
2539An optional FORCE argument forces sending the line when flood
2540protection is in effect. The optional NO-COMMAND argument prohibits
2541this function from interpreting the line as a command."
2542 (let ((command-list (erc-extract-command-from-line line)))
2543 (if (and command-list
2544 (not no-command))
2545 (let* ((cmd (nth 0 command-list))
2546 (args (nth 1 command-list)))
2547 (condition-case nil
2548 (if (listp args)
2549 (apply cmd args)
2550 (funcall cmd args))
2551 (wrong-number-of-arguments
2552 (erc-display-message nil 'error (current-buffer) 'incorrect-args
2553 ?c (erc-command-name cmd)
2554 ?u (or (erc-get-arglist cmd)
2555 "")
2556 ?d (format "%s\n"
2557 (or (documentation cmd) "")))
2558 nil)))
2559 (let ((r (erc-default-target)))
2560 (if r
2561 (funcall erc-send-input-line-function r line force)
2562 (erc-display-message nil 'error (current-buffer) 'no-target)
2563 nil)))))
2564
2565;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2566;; Input commands handlers
2567;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2568
2569(defun erc-cmd-AMSG (line)
2570 "Send LINE to all channels of the current server that you are on."
2571 (interactive "sSend to all channels you're on: ")
2572 (setq line (erc-trim-string line))
2573 (erc-with-all-buffers-of-server nil
2574 (lambda ()
2575 (erc-channel-p (erc-default-target)))
2576 (erc-send-message line)))
2577(put 'erc-cmd-AMSG 'do-not-parse-args t)
2578
2579(defun erc-cmd-SAY (line)
2580 "Send LINE to the current query or channel as a message, not a command.
2581
2582Use this when you want to send a message with a leading '/'. Note
2583that since multi-line messages are never a command, you don't
2584need this when pasting multiple lines of text."
2585 (if (string-match "^\\s-*$" line)
2586 nil
2587 (string-match "^ ?\\(.*\\)" line)
2588 (erc-process-input-line (match-string 1 line) nil t)))
2589(put 'erc-cmd-SAY 'do-not-parse-args t)
2590
2591(defun erc-cmd-SET (line)
2592 "Set the variable named by the first word in LINE to some VALUE.
2593VALUE is computed by evaluating the rest of LINE in Lisp."
2594 (cond
2595 ((string-match "^\\s-*\\(\\S-+\\)\\s-+\\(.*\\)$" line)
2596 (let ((var (read (concat "erc-" (match-string 1 line))))
2597 (val (read (match-string 2 line))))
2598 (if (boundp var)
2599 (progn
2600 (set var (eval val))
2601 (erc-display-message
2602 nil nil 'active (format "Set %S to %S" var val))
2603 t)
2604 (setq var (read (match-string 1 line)))
2605 (if (boundp var)
2606 (progn
2607 (set var (eval val))
2608 (erc-display-message
2609 nil nil 'active (format "Set %S to %S" var val))
2610 t)
2611 (erc-display-message nil 'error 'active 'variable-not-bound)
2612 nil))))
2613 ((string-match "^\\s-*$" line)
2614 (erc-display-line
2615 (concat "Available user variables:\n"
2616 (apply
2617 'concat
2618 (mapcar
2619 (lambda (var)
2620 (let ((val (symbol-value var)))
2621 (concat (format "%S:" var)
2622 (if (consp val)
2623 (concat "\n" (pp-to-string val))
2624 (format " %S\n" val)))))
2625 (apropos-internal "^erc-" 'user-variable-p))))
2626 (current-buffer)) t)
2627 (t nil)))
2628(defalias 'erc-cmd-VAR 'erc-cmd-SET)
2629(defalias 'erc-cmd-VARIABLE 'erc-cmd-SET)
2630(put 'erc-cmd-SET 'do-not-parse-args t)
526dc846 2631(put 'erc-cmd-SET 'process-not-needed t)
597993cf
MB
2632
2633(defun erc-cmd-default (line)
2634 "Fallback command.
2635
2636Commands for which no erc-cmd-xxx exists, are tunnelled through
2637this function. LINE is sent to the server verbatim, and
2638therefore has to contain the command itself as well."
2639 (erc-log (format "cmd: DEFAULT: %s" line))
2640 (erc-server-send (substring line 1))
2641 t)
2642
2643(defun erc-cmd-IGNORE (&optional user)
2644 "Ignore USER. This should be a regexp matching nick!user@host.
2645If no USER argument is specified, list the contents of `erc-ignore-list'."
2646 (if user
83dc6995
MB
2647 (let ((quoted (regexp-quote user)))
2648 (when (and (not (string= user quoted))
2649 (y-or-n-p (format "Use regexp-quoted form (%s) instead? "
2650 quoted)))
2651 (setq user quoted))
597993cf
MB
2652 (erc-display-line
2653 (erc-make-notice (format "Now ignoring %s" user))
2654 'active)
ff59d266
MB
2655 (erc-with-server-buffer (add-to-list 'erc-ignore-list user)))
2656 (if (null (erc-with-server-buffer erc-ignore-list))
597993cf
MB
2657 (erc-display-line (erc-make-notice "Ignore list is empty") 'active)
2658 (erc-display-line (erc-make-notice "Ignore list:") 'active)
2659 (mapc #'(lambda (item)
2660 (erc-display-line (erc-make-notice item)
2661 'active))
ff59d266 2662 (erc-with-server-buffer erc-ignore-list))))
597993cf
MB
2663 t)
2664
2665(defun erc-cmd-UNIGNORE (user)
2666 "Remove the user specified in USER from the ignore list."
ff59d266 2667 (let ((ignored-nick (car (erc-with-server-buffer
83dc6995
MB
2668 (erc-member-ignore-case (regexp-quote user)
2669 erc-ignore-list)))))
2670 (unless ignored-nick
2671 (if (setq ignored-nick (erc-ignored-user-p user))
2672 (unless (y-or-n-p (format "Remove this regexp (%s)? "
2673 ignored-nick))
2674 (setq ignored-nick nil))
597993cf
MB
2675 (erc-display-line
2676 (erc-make-notice (format "%s is not currently ignored!" user))
83dc6995
MB
2677 'active)))
2678 (when ignored-nick
597993cf
MB
2679 (erc-display-line
2680 (erc-make-notice (format "No longer ignoring %s" user))
83dc6995 2681 'active)
ff59d266 2682 (erc-with-server-buffer
83dc6995 2683 (setq erc-ignore-list (delete ignored-nick erc-ignore-list)))))
597993cf
MB
2684 t)
2685
2686(defun erc-cmd-CLEAR ()
2687 "Clear the window content."
2688 (recenter 0)
2689 t)
526dc846 2690(put 'erc-cmd-CLEAR 'process-not-needed t)
597993cf
MB
2691
2692(defun erc-cmd-OPS ()
2693 "Show the ops in the current channel."
2694 (interactive)
2695 (let ((ops nil))
2696 (if erc-channel-users
2697 (maphash (lambda (nick user-data)
2698 (let ((cuser (cdr user-data)))
2699 (if (and cuser
2700 (erc-channel-user-op cuser))
2701 (setq ops (cons (erc-server-user-nickname
2702 (car user-data))
2703 ops)))))
2704 erc-channel-users))
2705 (setq ops (sort ops 'string-lessp))
2706 (if ops
2707 (erc-display-message
2708 nil 'notice (current-buffer) 'ops
2709 ?i (length ops) ?s (if (> (length ops) 1) "s" "")
2710 ?o (mapconcat 'identity ops " "))
2711 (erc-display-message nil 'notice (current-buffer) 'ops-none)))
2712 t)
2713
2714(defun erc-cmd-COUNTRY (tld)
2715 "Display the country associated with the top level domain TLD."
2716 (require 'mail-extr)
2717 (let ((co (ignore-errors (what-domain tld))))
2718 (if co
2719 (erc-display-message
2720 nil 'notice 'active 'country ?c co ?d tld)
2721 (erc-display-message
2722 nil 'notice 'active 'country-unknown ?d tld))
2723 t))
526dc846 2724(put 'erc-cmd-COUNTRY 'process-not-needed t)
597993cf
MB
2725
2726(defun erc-cmd-AWAY (line)
2727 "Mark the user as being away, the reason being indicated by LINE.
2728If no reason is given, unset away status."
2729 (when (string-match "^\\s-*\\(.*\\)$" line)
2730 (let ((reason (match-string 1 line)))
2731 (erc-log (format "cmd: AWAY: %s" reason))
2732 (erc-server-send
2733 (if (string= reason "")
2734 "AWAY"
2735 (concat "AWAY :" reason))))
2736 t))
2737(put 'erc-cmd-AWAY 'do-not-parse-args t)
2738
2739(defun erc-cmd-GAWAY (line)
2740 "Mark the user as being away everywhere, the reason being indicated by LINE."
2741 ;; on all server buffers.
2742 (erc-with-all-buffers-of-server nil
ff59d266
MB
2743 #'erc-open-server-buffer-p
2744 (erc-cmd-AWAY line)))
597993cf
MB
2745(put 'erc-cmd-GAWAY 'do-not-parse-args t)
2746
2747(defun erc-cmd-CTCP (nick cmd &rest args)
2748 "Send a Client To Client Protocol message to NICK.
2749
2750CMD is the CTCP command, possible values being ECHO, FINGER, CLIENTINFO, TIME,
2751VERSION and so on. It is called with ARGS."
2752 (let ((str (concat cmd
2753 (when args
2754 (concat " " (mapconcat #'identity args " "))))))
2755 (erc-log (format "cmd: CTCP [%s]: [%s]" nick str))
2756 (erc-send-ctcp-message nick str)
2757 t))
2758
2759(defun erc-cmd-HELP (&optional func)
2760 "Popup help information.
2761
2762If FUNC contains a valid function or variable, help about that
2763will be displayed. If FUNC is empty, display an apropos about
a3d2f0a3 2764ERC commands. Otherwise, do `apropos' in the ERC namespace
597993cf
MB
2765\(\"erc-.*LINE\"\).
2766
2767Examples:
2768To find out about erc and bbdb, do
2769 /help bbdb.*
2770
2771For help about the WHOIS command, do:
2772 /help whois
2773
2774For a list of user commands (/join /part, ...):
2775 /help."
2776 (if func
2777 (let* ((sym (or (let ((sym (intern-soft
2778 (concat "erc-cmd-" (upcase func)))))
2779 (if (and sym (or (boundp sym) (fboundp sym)))
2780 sym
2781 nil))
2782 (let ((sym (intern-soft func)))
2783 (if (and sym (or (boundp sym) (fboundp sym)))
2784 sym
2785 nil))
2786 (let ((sym (intern-soft (concat "erc-" func))))
2787 (if (and sym (or (boundp sym) (fboundp sym)))
2788 sym
2789 nil)))))
2790 (if sym
2791 (cond
2792 ((boundp sym) (describe-variable sym))
2793 ((fboundp sym) (describe-function sym))
2794 (t nil))
2795 (apropos-command (concat "erc-.*" func) nil
2796 (lambda (x)
2797 (or (commandp x)
2798 (get x 'custom-type))))
2799 t))
2800 (apropos "erc-cmd-")
2801 (message "Type C-h m to get additional information about keybindings.")
2802 t))
2803
2804(defalias 'erc-cmd-H 'erc-cmd-HELP)
526dc846 2805(put 'erc-cmd-HELP 'process-not-needed t)
597993cf
MB
2806
2807(defun erc-cmd-JOIN (channel &optional key)
2808 "Join the channel given in CHANNEL, optionally with KEY.
2809If CHANNEL is specified as \"-invite\", join the channel to which you
2810were most recently invited. See also `invitation'."
2811 (let (chnl)
2812 (if (string= (upcase channel) "-INVITE")
2813 (if erc-invitation
2814 (setq chnl erc-invitation)
2815 (erc-display-message nil 'error (current-buffer) 'no-invitation))
2816 (setq chnl (erc-ensure-channel-name channel)))
2817 (when chnl
2818 ;; Prevent double joining of same channel on same server.
2819 (let ((joined-channels
2820 (mapcar #'(lambda (chanbuf)
2821 (with-current-buffer chanbuf (erc-default-target)))
2822 (erc-channel-list erc-server-process))))
2823 (if (erc-member-ignore-case chnl joined-channels)
2824 (switch-to-buffer (car (erc-member-ignore-case chnl
2825 joined-channels)))
2826 (erc-log (format "cmd: JOIN: %s" chnl))
2827 (if (and chnl key)
2828 (erc-server-send (format "JOIN %s %s" chnl key))
2829 (erc-server-send (format "JOIN %s" chnl)))))))
2830 t)
2831
2832(defalias 'erc-cmd-CHANNEL 'erc-cmd-JOIN)
2833(defalias 'erc-cmd-J 'erc-cmd-JOIN)
2834
2835(defvar erc-channel-new-member-names nil
2836 "If non-nil, a names list is currently being received.
2837
2838If non-nil, this variable is a hash-table that associates
2839received nicks with t.")
2840(make-variable-buffer-local 'erc-channel-new-member-names)
2841
2842(defun erc-cmd-NAMES (&optional channel)
2843 "Display the users in CHANNEL.
2844If CHANNEL is not specified, display the users in the current channel.
2845This function clears the channel name list first, then sends the
2846command."
2847 (let ((tgt (or (and (erc-channel-p channel) channel)
2848 (erc-default-target))))
2849 (if (and tgt (erc-channel-p tgt))
2850 (progn
2851 (erc-log (format "cmd: DEFAULT: NAMES %s" tgt))
2852 (erc-with-buffer
2853 (tgt)
2854 (erc-channel-begin-receiving-names))
2855 (erc-server-send (concat "NAMES " tgt)))
2856 (erc-display-message nil 'error (current-buffer) 'no-default-channel)))
2857 t)
2858(defalias 'erc-cmd-N 'erc-cmd-NAMES)
2859
2860(defun erc-cmd-KICK (target &optional reason-or-nick &rest reasonwords)
2861 "Kick the user indicated in LINE from the current channel.
2862LINE has the format: \"#CHANNEL NICK REASON\" or \"NICK REASON\"."
2863 (let ((reasonstring (mapconcat 'identity reasonwords " ")))
2864 (if (string= "" reasonstring)
2865 (setq reasonstring (format "Kicked by %s" (erc-current-nick))))
2866 (if (erc-channel-p target)
2867 (let ((nick reason-or-nick))
2868 (erc-log (format "cmd: KICK: %s/%s: %s" nick target reasonstring))
2869 (erc-server-send (format "KICK %s %s :%s" target nick reasonstring)
2870 nil target)
2871 t)
2872 (when target
2873 (let ((ch (erc-default-target)))
2874 (setq reasonstring (concat
2875 (if reason-or-nick (concat reason-or-nick " "))
2876 reasonstring))
2877 (if ch
2878 (progn
2879 (erc-log
2880 (format "cmd: KICK: %s/%s: %s" target ch reasonstring))
2881 (erc-server-send
2882 (format "KICK %s %s :%s" ch target reasonstring) nil ch))
2883 (erc-display-message nil 'error (current-buffer)
2884 'no-default-channel))
2885 t)))))
2886
2887(defvar erc-script-args nil)
2888
2889(defun erc-cmd-LOAD (line)
2890 "Load the script provided in the LINE.
a3d2f0a3
JB
2891If LINE continues beyond the file name, the rest of
2892it is put in a (local) variable `erc-script-args',
2893which can be used in Emacs Lisp scripts.
597993cf
MB
2894
2895The optional FORCE argument is ignored here - you can't force loading
2896a script after exceeding the flood threshold."
2897 (cond
2898 ((string-match "^\\s-*\\(\\S-+\\)\\(.*\\)$" line)
2899 (let* ((file-to-find (match-string 1 line))
2900 (erc-script-args (match-string 2 line))
2901 (file (erc-find-file file-to-find erc-script-path)))
2902 (erc-log (format "cmd: LOAD: %s" file-to-find))
2903 (cond
2904 ((not file)
2905 (erc-display-message nil 'error (current-buffer)
2906 'cannot-find-file ?f file-to-find))
2907 ((not (file-readable-p file))
2908 (erc-display-message nil 'error (current-buffer)
2909 'cannot-read-file ?f file))
2910 (t
2911 (message "Loading \'%s\'..." file)
2912 (erc-load-script file)
2913 (message "Loading \'%s\'...done" file))))
2914 t)
2915 (t nil)))
2916
2917(defun erc-cmd-WHOIS (user &optional server)
2918 "Display whois information for USER.
2919
2920If SERVER is non-nil, use that, rather than the current server."
2921 ;; FIXME: is the above docstring correct? -- Lawrence 2004-01-08
2922 (let ((send (if server
2923 (format "WHOIS %s %s" user server)
2924 (format "WHOIS %s" user))))
2925 (erc-log (format "cmd: %s" send))
2926 (erc-server-send send)
2927 t))
2928(defalias 'erc-cmd-WI 'erc-cmd-WHOIS)
2929
2930(defun erc-cmd-WHOAMI ()
2931 "Display whois information about yourself."
2932 (erc-cmd-WHOIS (erc-current-nick))
2933 t)
2934
2935(defun erc-cmd-IDLE (nick)
2936 "Show the length of time NICK has been idle."
ff59d266 2937 (let ((origbuf (current-buffer))
597993cf 2938 symlist)
ff59d266 2939 (erc-with-server-buffer
597993cf
MB
2940 (add-to-list 'symlist
2941 (cons (erc-once-with-server-event
2942 311 `(string= ,nick
2943 (second
2944 (erc-response.command-args parsed))))
2945 'erc-server-311-functions))
2946 (add-to-list 'symlist
2947 (cons (erc-once-with-server-event
2948 312 `(string= ,nick
2949 (second
2950 (erc-response.command-args parsed))))
2951 'erc-server-312-functions))
2952 (add-to-list 'symlist
2953 (cons (erc-once-with-server-event
2954 318 `(string= ,nick
2955 (second
2956 (erc-response.command-args parsed))))
2957 'erc-server-318-functions))
2958 (add-to-list 'symlist
2959 (cons (erc-once-with-server-event
2960 319 `(string= ,nick
2961 (second
2962 (erc-response.command-args parsed))))
2963 'erc-server-319-functions))
2964 (add-to-list 'symlist
2965 (cons (erc-once-with-server-event
2966 320 `(string= ,nick
2967 (second
2968 (erc-response.command-args parsed))))
2969 'erc-server-320-functions))
2970 (add-to-list 'symlist
2971 (cons (erc-once-with-server-event
2972 330 `(string= ,nick
2973 (second
2974 (erc-response.command-args parsed))))
2975 'erc-server-330-functions))
2976 (add-to-list 'symlist
2977 (cons (erc-once-with-server-event
2978 317
2979 `(let ((idleseconds
2980 (string-to-number
2981 (third
2982 (erc-response.command-args parsed)))))
2983 (erc-display-line
2984 (erc-make-notice
2985 (format "%s has been idle for %s."
2986 (erc-string-no-properties ,nick)
2987 (erc-seconds-to-string idleseconds)))
2988 ,origbuf))
2989 t)
2990 'erc-server-317-functions))
2991
2992 ;; Send the WHOIS command.
2993 (erc-cmd-WHOIS nick)
2994
2995 ;; Remove the uninterned symbols from the server hooks that did not run.
2996 (run-at-time 20 nil `(lambda ()
2997 (with-current-buffer ,(current-buffer)
2998 (dolist (sym ',symlist)
2999 (let ((hooksym (cdr sym))
3000 (funcsym (car sym)))
3001 (remove-hook hooksym funcsym t))))))))
3002 t)
3003
3004(defun erc-cmd-DESCRIBE (line)
3005 "Pose some action to a certain user.
3006LINE has the format \"USER ACTION\"."
3007 (cond
3008 ((string-match
3009 "^\\s-*\\(\\S-+\\)\\s-\\(.*\\)$" line)
3010 (let ((dst (match-string 1 line))
3011 (s (match-string 2 line)))
3012 (erc-log (format "cmd: DESCRIBE: [%s] %s" dst s))
3013 (erc-send-action dst s))
3014 t)
3015 (t nil)))
3016(put 'erc-cmd-DESCRIBE 'do-not-parse-args t)
3017
3018(defun erc-cmd-ME (line)
3019 "Send LINE as an action."
3020 (cond
3021 ((string-match "^\\s-\\(.*\\)$" line)
3022 (let ((s (match-string 1 line)))
3023 (erc-log (format "cmd: ME: %s" s))
3024 (erc-send-action (erc-default-target) s))
3025 t)
3026 (t nil)))
3027(put 'erc-cmd-ME 'do-not-parse-args t)
3028
b6675b2d
MO
3029(defun erc-cmd-ME\'S (line)
3030 "Do a /ME command, but add the string \" 's\" to the beginning."
3031 (erc-cmd-ME (concat " 's" line)))
3032(put 'erc-cmd-ME\'S 'do-not-parse-args t)
3033
597993cf
MB
3034(defun erc-cmd-LASTLOG (line)
3035 "Show all lines in the current buffer matching the regexp LINE.
3036
3037If a match spreads across multiple lines, all those lines are shown.
3038
3039The lines are shown in a buffer named `*Occur*'.
3040It serves as a menu to find any of the occurrences in this buffer.
3041\\[describe-mode] in that buffer will explain how.
3042
3043If LINE contains upper case characters (excluding those preceded by `\'),
3044the matching is case-sensitive."
3045 (occur line)
3046 t)
3047(put 'erc-cmd-LASTLOG 'do-not-parse-args t)
526dc846 3048(put 'erc-cmd-LASTLOG 'process-not-needed t)
597993cf
MB
3049
3050(defun erc-send-message (line &optional force)
3051 "Send LINE to the current channel or user and display it.
3052
3053See also `erc-message' and `erc-display-line'."
3054 (erc-message "PRIVMSG" (concat (erc-default-target) " " line) force)
3055 (erc-display-line
3056 (concat (erc-format-my-nick) line)
3057 (current-buffer))
3058 ;; FIXME - treat multiline, run hooks, or remove me?
3059 t)
3060
3061(defun erc-cmd-MODE (line)
3062 "Change or display the mode value of a channel or user.
3063The first word specifies the target. The rest is the mode string
3064to send.
3065
3066If only one word is given, display the mode of that target.
3067
3068A list of valid mode strings for Freenode may be found at
3069`http://freenode.net/using_the_network.shtml'."
3070 (cond
3071 ((string-match "^\\s-\\(.*\\)$" line)
3072 (let ((s (match-string 1 line)))
3073 (erc-log (format "cmd: MODE: %s" s))
3074 (erc-server-send (concat "MODE " line)))
3075 t)
3076 (t nil)))
3077(put 'erc-cmd-MODE 'do-not-parse-args t)
3078
3079(defun erc-cmd-NOTICE (channel-or-user &rest message)
3080 "Send a notice to the channel or user given as the first word.
3081The rest is the message to send."
3082 (erc-message "NOTICE" (concat channel-or-user " "
3083 (mapconcat #'identity message " "))))
3084
3085(defun erc-cmd-MSG (line)
3086 "Send a message to the channel or user given as the first word in LINE.
3087
3088The rest of LINE is the message to send."
3089 (erc-message "PRIVMSG" line))
3090
3091(defalias 'erc-cmd-M 'erc-cmd-MSG)
3092(put 'erc-cmd-MSG 'do-not-parse-args t)
3093
3094(defun erc-cmd-SQUERY (line)
3095 "Send a Service Query to the service given as the first word in LINE.
3096
3097The rest of LINE is the message to send."
3098 (erc-message "SQUERY" line))
3099
3100(defun erc-cmd-NICK (nick)
3101 "Change current nickname to NICK."
3102 (erc-log (format "cmd: NICK: %s (erc-bad-nick: %S)" nick erc-bad-nick))
ff59d266
MB
3103 (let ((nicklen (cdr (assoc "NICKLEN" (erc-with-server-buffer
3104 erc-server-parameters)))))
597993cf
MB
3105 (and nicklen (> (length nick) (string-to-number nicklen))
3106 (erc-display-message
3107 nil 'notice 'active 'nick-too-long
3108 ?i (length nick) ?l nicklen)))
3109 (erc-server-send (format "NICK %s" nick))
3110 (cond (erc-bad-nick
3111 (erc-set-current-nick nick)
3112 (erc-update-mode-line)
3113 (setq erc-bad-nick nil)))
3114 t)
3115
3116(defun erc-cmd-PART (line)
3117 "When LINE is an empty string, leave the current channel.
3118Otherwise leave the channel indicated by LINE."
3119 (cond
3120 ((string-match "^\\s-*\\([&#+!]\\S-+\\)\\s-?\\(.*\\)$" line)
3121 (let* ((ch (match-string 1 line))
3122 (msg (match-string 2 line))
3123 (reason (funcall erc-part-reason (if (equal msg "") nil msg))))
3124 (erc-log (format "cmd: PART: %s: %s" ch reason))
3125 (erc-server-send (if (string= reason "")
3126 (format "PART %s" ch)
3127 (format "PART %s :%s" ch reason))
3128 nil ch))
3129 t)
3130 ((string-match "^\\s-*\\(.*\\)$" line)
3131 (let* ((ch (erc-default-target))
3132 (msg (match-string 1 line))
3133 (reason (funcall erc-part-reason (if (equal msg "") nil msg))))
3134 (if (and ch (erc-channel-p ch))
3135 (progn
3136 (erc-log (format "cmd: PART: %s: %s" ch reason))
3137 (erc-server-send (if (string= reason "")
3138 (format "PART %s" ch)
3139 (format "PART %s :%s" ch reason))
3140 nil ch))
3141 (erc-display-message nil 'error (current-buffer) 'no-target)))
3142 t)
3143 (t nil)))
3144(put 'erc-cmd-PART 'do-not-parse-args t)
3145
3146(defalias 'erc-cmd-LEAVE 'erc-cmd-PART)
3147
3148(defun erc-cmd-PING (recipient)
3149 "Ping RECIPIENT."
3150 (let ((time (format "%f" (erc-current-time))))
3151 (erc-log (format "cmd: PING: %s" time))
3152 (erc-cmd-CTCP recipient "PING" time)))
3153
3154(defun erc-cmd-QUOTE (line)
3155 "Send LINE directly to the server.
3156All the text given as argument is sent to the sever as unmodified,
3157just as you provided it. Use this command with care!"
3158 (cond
c89e5cd7 3159 ((string-match "^ ?\\(.+\\)$" line)
597993cf
MB
3160 (erc-server-send (match-string 1 line)))
3161 (t nil)))
3162(put 'erc-cmd-QUOTE 'do-not-parse-args t)
3163
3164(defun erc-cmd-QUERY (&optional user)
3165 "Open a query with USER.
3166The type of query window/frame/etc will depend on the value of
3167`erc-join-buffer'. If USER is omitted, close the current query buffer if one
3168exists - except this is broken now ;-)"
3169 (interactive
3170 (list (read-from-minibuffer "Start a query with: " nil)))
3171 (let ((session-buffer (erc-server-buffer)))
3172 (if user
3173 (erc-query user session-buffer)
3174 ;; currently broken, evil hack to display help anyway
3175 ;(erc-delete-query))))
3176 (signal 'wrong-number-of-arguments ""))))
3177(defalias 'erc-cmd-Q 'erc-cmd-QUERY)
3178
3179(defun erc-quit-reason-normal (&optional s)
3180 "Normal quit message.
3181
3182If S is non-nil, it will be used as the quit reason."
3183 (or s
3184 (format "\C-bERC\C-b %s (IRC client for Emacs)"; - \C-b%s\C-b"
3185 erc-version-string) ; erc-official-location)
3186 ))
3187
3188(defun erc-quit-reason-zippy (&optional s)
3189 "Zippy quit message.
3190
3191If S is non-nil, it will be used as the quit reason."
3192 (or s
3193 (erc-replace-regexp-in-string "\n" "" (yow))))
3194
3195(defun erc-quit-reason-various (s)
3196 "Choose a quit reason based on S (a string)."
3197 (when (featurep 'xemacs) (require 'poe))
3198 (let ((res (car (assoc-default (or s "")
3199 erc-quit-reason-various-alist 'string-match))))
3200 (cond
3201 ((functionp res) (funcall res))
3202 ((stringp res) res)
0b6bb130
MB
3203 (s s)
3204 (t (erc-quit-reason-normal)))))
597993cf
MB
3205
3206(defun erc-part-reason-normal (&optional s)
3207 "Normal part message.
3208
3209If S is non-nil, it will be used as the quit reason."
3210 (or s
3211 (format "\C-bERC\C-b %s (IRC client for Emacs)"; - \C-b%s\C-b"
3212 erc-version-string) ; erc-official-location)
3213 ))
3214
3215(defun erc-part-reason-zippy (&optional s)
3216 "Zippy part message.
3217
3218If S is non-nil, it will be used as the quit reason."
3219 (or s
3220 (erc-replace-regexp-in-string "\n" "" (yow))))
3221
3222(defun erc-part-reason-various (s)
3223 "Choose a part reason based on S (a string)."
3224 (when (featurep 'xemacs) (require 'poe))
3225 (let ((res (car (assoc-default (or s "")
3226 erc-part-reason-various-alist 'string-match))))
3227 (cond
3228 ((functionp res) (funcall res))
3229 ((stringp res) res)
0b6bb130
MB
3230 (s s)
3231 (t (erc-part-reason-normal)))))
597993cf
MB
3232
3233(defun erc-cmd-QUIT (reason)
3234 "Disconnect from the current server.
3235If REASON is omitted, display a default quit message, otherwise display
3236the message given by REASON."
3237 (unless reason
3238 (setq reason ""))
3239 (cond
3240 ((string-match "^\\s-*\\(.*\\)$" reason)
3241 (let* ((s (match-string 1 reason))
3242 (buffer (erc-server-buffer))
ff59d266
MB
3243 (reason (funcall erc-quit-reason (if (equal s "") nil s)))
3244 server-proc)
597993cf
MB
3245 (with-current-buffer (if (and buffer
3246 (bufferp buffer))
3247 buffer
3248 (current-buffer))
3249 (erc-log (format "cmd: QUIT: %s" reason))
3250 (setq erc-server-quitting t)
3251 (erc-set-active-buffer (erc-server-buffer))
ff59d266 3252 (setq server-proc erc-server-process)
597993cf 3253 (erc-server-send (format "QUIT :%s" reason)))
ff59d266 3254 (run-hook-with-args 'erc-quit-hook server-proc)
597993cf 3255 (when erc-kill-queries-on-quit
ff59d266
MB
3256 (erc-kill-query-buffers server-proc))
3257 ;; if the process has not been killed within 4 seconds, kill it
3258 (run-at-time 4 nil
3259 (lambda (proc)
3260 (when (and (processp proc)
3261 (memq (process-status proc) '(run open)))
3262 (delete-process proc)))
3263 server-proc))
597993cf
MB
3264 t)
3265 (t nil)))
3266
3267(defalias 'erc-cmd-BYE 'erc-cmd-QUIT)
3268(defalias 'erc-cmd-EXIT 'erc-cmd-QUIT)
3269(defalias 'erc-cmd-SIGNOFF 'erc-cmd-QUIT)
3270(put 'erc-cmd-QUIT 'do-not-parse-args t)
526dc846 3271(put 'erc-cmd-QUIT 'process-not-needed t)
597993cf
MB
3272
3273(defun erc-cmd-GQUIT (reason)
3274 "Disconnect from all servers at once with the same quit REASON."
ff59d266 3275 (erc-with-all-buffers-of-server nil #'erc-open-server-buffer-p
526dc846
MO
3276 (erc-cmd-QUIT reason))
3277 (when erc-kill-queries-on-quit
3278 ;; if the query buffers have not been killed within 4 seconds,
3279 ;; kill them
3280 (run-at-time
3281 4 nil
3282 (lambda ()
3283 (dolist (buffer (erc-buffer-list (lambda (buf)
3284 (not (erc-server-buffer-p buf)))))
3285 (kill-buffer buffer)))))
3286 t)
597993cf
MB
3287
3288(defalias 'erc-cmd-GQ 'erc-cmd-GQUIT)
3289(put 'erc-cmd-GQUIT 'do-not-parse-args t)
526dc846 3290(put 'erc-cmd-GQUIT 'process-not-needed t)
597993cf 3291
10dc9f9e
MB
3292(defun erc-cmd-RECONNECT ()
3293 "Try to reconnect to the current IRC server."
526dc846 3294 (let ((buffer (erc-server-buffer))
ff59d266 3295 (process nil))
526dc846
MO
3296 (unless (buffer-live-p buffer)
3297 (setq buffer (current-buffer)))
3298 (with-current-buffer buffer
ff59d266
MB
3299 (setq erc-server-quitting nil)
3300 (setq erc-server-reconnecting t)
3301 (setq erc-server-reconnect-count 0)
3302 (setq process (get-buffer-process (erc-server-buffer)))
3303 (if process
3304 (delete-process process)
3305 (erc-server-reconnect))
3306 (setq erc-server-reconnecting nil)))
10dc9f9e 3307 t)
526dc846 3308(put 'erc-cmd-RECONNECT 'process-not-needed t)
10dc9f9e 3309
597993cf
MB
3310(defun erc-cmd-SERVER (server)
3311 "Connect to SERVER, leaving existing connection intact."
3312 (erc-log (format "cmd: SERVER: %s" server))
3313 (condition-case nil
83dc6995 3314 (erc :server server :nick (erc-current-nick))
597993cf 3315 (error
526dc846 3316 (erc-error "Cannot find host %s." server)))
597993cf 3317 t)
526dc846 3318(put 'erc-cmd-SERVER 'process-not-needed t)
597993cf
MB
3319
3320(eval-when-compile
3321 (defvar motif-version-string)
3322 (defvar gtk-version-string))
3323
3324(defun erc-cmd-SV ()
3325 "Say the current ERC and Emacs version into channel."
21bc768b 3326 (erc-send-message (format "I'm using ERC %s with %s %s (%s%s) of %s."
597993cf
MB
3327 erc-version-string
3328 (if (featurep 'xemacs) "XEmacs" "GNU Emacs")
3329 emacs-version
3330 system-configuration
3331 (concat
3332 (cond ((featurep 'motif)
3333 (concat ", " (substring
3334 motif-version-string 4)))
3335 ((featurep 'gtk)
3336 (concat ", GTK+ Version "
3337 gtk-version-string))
3338 ((featurep 'mac-carbon) ", Mac Carbon")
3339 ((featurep 'x-toolkit) ", X toolkit")
3340 (t ""))
3341 (if (and (boundp 'x-toolkit-scroll-bars)
3342 (memq x-toolkit-scroll-bars
3343 '(xaw xaw3d)))
3344 (format ", %s scroll bars"
3345 (capitalize (symbol-name
3346 x-toolkit-scroll-bars)))
3347 "")
3348 (if (featurep 'multi-tty) ", multi-tty" ""))
21bc768b 3349 erc-emacs-build-time))
597993cf
MB
3350 t)
3351
3352(defun erc-cmd-SM ()
3353 "Say the current ERC modes into channel."
3354 (erc-send-message (format "I'm using the following modules: %s!"
3355 (erc-modes)))
3356 t)
3357
597993cf
MB
3358(defun erc-cmd-DEOP (&rest people)
3359 "Remove the operator setting from user(s) given in PEOPLE."
3360 (when (> (length people) 0)
3361 (erc-server-send (concat "MODE " (erc-default-target)
3362 " -"
3363 (make-string (length people) ?o)
3364 " "
3365 (mapconcat 'identity people " ")))
3366 t))
3367
3368(defun erc-cmd-OP (&rest people)
3369 "Add the operator setting to users(s) given in PEOPLE."
3370 (when (> (length people) 0)
3371 (erc-server-send (concat "MODE " (erc-default-target)
3372 " +"
3373 (make-string (length people) ?o)
3374 " "
3375 (mapconcat 'identity people " ")))
3376 t))
3377
3378(defun erc-cmd-TIME (&optional line)
3379 "Request the current time and date from the current server."
3380 (cond
3381 ((and line (string-match "^\\s-*\\(.*\\)$" line))
3382 (let ((args (match-string 1 line)))
3383 (erc-log (format "cmd: TIME: %s" args))
3384 (erc-server-send (concat "TIME " args)))
3385 t)
3386 (t (erc-server-send "TIME"))))
3387(defalias 'erc-cmd-DATE 'erc-cmd-TIME)
3388
3389(defun erc-cmd-TOPIC (topic)
3390 "Set or request the topic for a channel.
3391LINE has the format: \"#CHANNEL TOPIC\", \"#CHANNEL\", \"TOPIC\"
3392or the empty string.
3393
3394If no #CHANNEL is given, the default channel is used. If TOPIC is
3395given, the channel topic is modified, otherwise the current topic will
3396be displayed."
3397 (cond
3398 ;; /topic #channel TOPIC
3399 ((string-match "^\\s-*\\([&#+!]\\S-+\\)\\s-\\(.*\\)$" topic)
3400 (let ((ch (match-string 1 topic))
3401 (topic (match-string 2 topic)))
3402 (erc-log (format "cmd: TOPIC [%s]: %s" ch topic))
3403 (erc-server-send (format "TOPIC %s :%s" ch topic) nil ch))
3404 t)
3405 ;; /topic #channel
3406 ((string-match "^\\s-*\\([&#+!]\\S-+\\)" topic)
3407 (let ((ch (match-string 1 topic)))
3408 (erc-server-send (format "TOPIC %s" ch) nil ch)
3409 t))
3410 ;; /topic
3411 ((string-match "^\\s-*$" topic)
3412 (let ((ch (erc-default-target)))
3413 (erc-server-send (format "TOPIC %s" ch) nil ch)
3414 t))
3415 ;; /topic TOPIC
3416 ((string-match "^\\s-*\\(.*\\)$" topic)
3417 (let ((ch (erc-default-target))
3418 (topic (match-string 1 topic)))
3419 (if (and ch (erc-channel-p ch))
3420 (progn
3421 (erc-log (format "cmd: TOPIC [%s]: %s" ch topic))
3422 (erc-server-send (format "TOPIC %s :%s" ch topic) nil ch))
3423 (erc-display-message nil 'error (current-buffer) 'no-target)))
3424 t)
3425 (t nil)))
3426(defalias 'erc-cmd-T 'erc-cmd-TOPIC)
3427(put 'erc-cmd-TOPIC 'do-not-parse-args t)
3428
3429(defun erc-cmd-APPENDTOPIC (topic)
3430 "Append TOPIC to the current channel topic, separated by a space."
3431 (let ((oldtopic erc-channel-topic))
3432 ;; display help when given no arguments
3433 (when (string-match "^\\s-*$" topic)
3434 (signal 'wrong-number-of-arguments nil))
3435 ;; strip trailing ^O
3436 (when (string-match "\\(.*\\)\C-o" oldtopic)
3437 (erc-cmd-TOPIC (concat (match-string 1 oldtopic) topic)))))
3438(defalias 'erc-cmd-AT 'erc-cmd-APPENDTOPIC)
3439(put 'erc-cmd-APPENDTOPIC 'do-not-parse-args t)
3440
3441(defun erc-cmd-CLEARTOPIC (&optional channel)
3442 "Clear the topic for a CHANNEL.
3443If CHANNEL is not specified, clear the topic for the default channel."
3444 (interactive "sClear topic of channel (RET is current channel): ")
3445 (let ((chnl (or (and (erc-channel-p channel) channel) (erc-default-target))))
3446 (when chnl
3447 (erc-server-send (format "TOPIC %s :" chnl))
3448 t)))
3449
3450;;; Banlists
3451
3452(defvar erc-channel-banlist nil
3453 "A list of bans seen for the current channel.
3454
3455Each ban is an alist of the form:
3456 (WHOSET . MASK)
3457
3458The property `received-from-server' indicates whether
3459or not the ban list has been requested from the server.")
3460(make-variable-buffer-local 'erc-channel-banlist)
3461(put 'erc-channel-banlist 'received-from-server nil)
3462
3463(defun erc-cmd-BANLIST ()
3464 "Pretty-print the contents of `erc-channel-banlist'.
3465
3466The ban list is fetched from the server if necessary."
3467 (let ((chnl (erc-default-target))
3468 (chnl-name (buffer-name)))
3469
3470 (cond
3471 ((not (erc-channel-p chnl))
3472 (erc-display-line (erc-make-notice "You're not on a channel\n")
3473 'active))
3474
3475 ((not (get 'erc-channel-banlist 'received-from-server))
3476 (let ((old-367-hook erc-server-367-functions))
3477 (setq erc-server-367-functions 'erc-banlist-store
3478 erc-channel-banlist nil)
3479 ;; fetch the ban list then callback
ff59d266 3480 (erc-with-server-buffer
597993cf
MB
3481 (erc-once-with-server-event
3482 368
3483 `(with-current-buffer ,chnl-name
3484 (put 'erc-channel-banlist 'received-from-server t)
3485 (setq erc-server-367-functions ',old-367-hook)
3486 (erc-cmd-BANLIST)
3487 t))
3488 (erc-server-send (format "MODE %s b" chnl)))))
3489
3490 ((null erc-channel-banlist)
3491 (erc-display-line (erc-make-notice
3492 (format "No bans for channel: %s\n" chnl))
3493 'active)
3494 (put 'erc-channel-banlist 'received-from-server nil))
3495
3496 (t
3497 (let* ((erc-fill-column (or (and (boundp 'erc-fill-column)
3498 erc-fill-column)
3499 (and (boundp 'fill-column)
3500 fill-column)
3501 (1- (window-width))))
a3d2f0a3 3502 (separator (make-string erc-fill-column ?=))
597993cf
MB
3503 (fmt (concat
3504 "%-" (number-to-string (/ erc-fill-column 2)) "s"
3505 "%" (number-to-string (/ erc-fill-column 2)) "s")))
3506
3507 (erc-display-line
3508 (erc-make-notice (format "Ban list for channel: %s\n"
3509 (erc-default-target)))
3510 'active)
3511
3512 (erc-display-line separator 'active)
3513 (erc-display-line (format fmt "Ban Mask" "Banned By") 'active)
3514 (erc-display-line separator 'active)
3515
3516 (mapc
3517 (lambda (x)
3518 (erc-display-line
3519 (format fmt
3520 (truncate-string-to-width (cdr x) (/ erc-fill-column 2))
3521 (if (car x)
3522 (truncate-string-to-width (car x) (/ erc-fill-column 2))
3523 ""))
3524 'active))
3525 erc-channel-banlist)
3526
3527 (erc-display-line (erc-make-notice "End of Ban list")
3528 'active)
3529 (put 'erc-channel-banlist 'received-from-server nil)))))
3530 t)
3531
3532(defalias 'erc-cmd-BL 'erc-cmd-BANLIST)
3533
3534(defun erc-cmd-MASSUNBAN ()
3535 "Mass Unban.
3536
3537Unban all currently banned users in the current channel."
3538 (let ((chnl (erc-default-target)))
3539 (cond
3540
3541 ((not (erc-channel-p chnl))
3542 (erc-display-line
3543 (erc-make-notice "You're not on a channel\n")
3544 'active))
3545
3546 ((not (get 'erc-channel-banlist 'received-from-server))
3547 (let ((old-367-hook erc-server-367-functions))
3548 (setq erc-server-367-functions 'erc-banlist-store)
3549 ;; fetch the ban list then callback
ff59d266 3550 (erc-with-server-buffer
597993cf
MB
3551 (erc-once-with-server-event
3552 368
3553 `(with-current-buffer ,chnl
3554 (put 'erc-channel-banlist 'received-from-server t)
3555 (setq erc-server-367-functions ,old-367-hook)
3556 (erc-cmd-MASSUNBAN)
3557 t))
3558 (erc-server-send (format "MODE %s b" chnl)))))
3559
3560 (t (let ((bans (mapcar 'cdr erc-channel-banlist)))
3561 (when bans
3562 ;; Glob the bans into groups of three, and carry out the unban.
3563 ;; eg. /mode #foo -bbb a*!*@* b*!*@* c*!*@*
3564 (mapc
3565 (lambda (x)
3566 (erc-server-send
3567 (format "MODE %s -%s %s" (erc-default-target)
a3d2f0a3 3568 (make-string (length x) ?b)
597993cf
MB
3569 (mapconcat 'identity x " "))))
3570 (erc-group-list bans 3))))
3571 t))))
3572
3573(defalias 'erc-cmd-MUB 'erc-cmd-MASSUNBAN)
3574
3575;;;; End of IRC commands
3576
3577(defun erc-ensure-channel-name (channel)
3578 "Return CHANNEL if it is a valid channel name.
3579Eventually add a # in front of it, if that turns it into a valid channel name."
3580 (if (erc-channel-p channel)
3581 channel
3582 (concat "#" channel)))
3583
3584(defun erc-grab-region (start end)
3585 "Copy the region between START and END in a recreatable format.
3586
3587Converts all the IRC text properties in each line of the region
3588into control codes and writes them to a separate buffer. The
3589resulting text may be used directly as a script to generate this
3590text again."
3591 (interactive "r")
3592 (erc-set-active-buffer (current-buffer))
3593 (save-excursion
3594 (let* ((cb (current-buffer))
3595 (buf (generate-new-buffer erc-grab-buffer-name))
3596 (region (buffer-substring start end))
3597 (lines (erc-split-multiline-safe region)))
3598 (set-buffer buf)
3599 (dolist (line lines)
3600 (insert (concat line "\n")))
3601 (set-buffer cb)
3602 (switch-to-buffer-other-window buf)))
3603 (message "erc-grab-region doesn't grab colors etc. anymore. If you use this, please tell the maintainers.")
3604 (ding))
3605
3606(defun erc-display-prompt (&optional buffer pos prompt face)
3607 "Display PROMPT in BUFFER at position POS.
3608Display an ERC prompt in BUFFER.
3609
3610If PROMPT is nil, one is constructed with the function `erc-prompt'.
3611If BUFFER is nil, the `current-buffer' is used.
3612If POS is nil, PROMPT will be displayed at `point'.
3613If FACE is non-nil, it will be used to propertize the prompt. If it is nil,
3614`erc-prompt-face' will be used."
3615 (let* ((prompt (or prompt (erc-prompt)))
3616 (l (length prompt))
3617 (ob (current-buffer)))
3618 ;; We cannot use save-excursion because we move point, therefore
3619 ;; we resort to the ol' ob trick to restore this.
3620 (when (and buffer (bufferp buffer))
3621 (set-buffer buffer))
3622
3623 ;; now save excursion again to store where point and mark are
3624 ;; in the current buffer
3625 (save-excursion
3626 (setq pos (or pos (point)))
3627 (goto-char pos)
3628 (when (> l 0)
3629 ;; Do not extend the text properties when typing at the end
3630 ;; of the prompt, but stuff typed in front of the prompt
3631 ;; shall remain part of the prompt.
3632 (setq prompt (erc-propertize prompt
3633 'start-open t ; XEmacs
3634 'rear-nonsticky t ; Emacs
3635 'erc-prompt t
3636 'front-sticky t
3637 'read-only t))
3638 (erc-put-text-property 0 (1- (length prompt))
3639 'face (or face 'erc-prompt-face)
3640 prompt)
3641 (insert prompt))
3642 ;; Set the input marker
3643 (set-marker erc-input-marker (point)))
3644
3645 ;; Now we are back at the old position. If the prompt was
3646 ;; inserted here or before us, advance point by the length of
3647 ;; the prompt.
3648 (when (or (not pos) (<= (point) pos))
3649 (forward-char l))
3650 ;; Clear the undo buffer now, so the user can undo his stuff,
3651 ;; but not the stuff we did. Sneaky!
3652 (setq buffer-undo-list nil)
3653 (set-buffer ob)))
3654
3655;; interactive operations
3656
3657(defun erc-input-message ()
3658 "Read input from the minibuffer."
3659 (interactive)
3660 (let ((minibuffer-allow-text-properties t)
3661 (read-map minibuffer-local-map))
3662 (insert (read-from-minibuffer "Message: "
3663 (string last-command-char) read-map))
3664 (erc-send-current-line)))
3665
3666(defvar erc-action-history-list ()
3667 "History list for interactive action input.")
3668
3669(defun erc-input-action ()
3670 "Interactively input a user action and send it to IRC."
3671 (interactive "")
3672 (erc-set-active-buffer (current-buffer))
3673 (let ((action (read-from-minibuffer
3674 "Action: " nil nil nil 'erc-action-history-list)))
3675 (if (not (string-match "^\\s-*$" action))
3676 (erc-send-action (erc-default-target) action))))
3677
3678(defun erc-join-channel (channel &optional key)
3679 "Join CHANNEL.
3680
3681If `point' is at the beginning of a channel name, use that as default."
3682 (interactive
3683 (list
21bc768b 3684 (let ((chnl (if (looking-at "\\([&#+!][^ \n]+\\)") (match-string 1) ""))
597993cf
MB
3685 (table (when (erc-server-buffer-live-p)
3686 (set-buffer (process-buffer erc-server-process))
3687 erc-channel-list)))
3688 (completing-read "Join channel: " table nil nil nil nil chnl))
b6675b2d 3689 (when (or current-prefix-arg erc-prompt-for-channel-key)
597993cf
MB
3690 (read-from-minibuffer "Channel key (RET for none): " nil))))
3691 (erc-cmd-JOIN channel (when (>= (length key) 1) key)))
3692
3693(defun erc-part-from-channel (reason)
3694 "Part from the current channel and prompt for a REASON."
3695 (interactive
3696 (list
3697 (if (and (boundp 'reason) (stringp reason) (not (string= reason "")))
3698 reason
83dc6995
MB
3699 (read-from-minibuffer (concat "Reason for leaving " (erc-default-target)
3700 ": ")))))
597993cf
MB
3701 (erc-cmd-PART (concat (erc-default-target)" " reason)))
3702
3703(defun erc-set-topic (topic)
3704 "Prompt for a TOPIC for the current channel."
3705 (interactive
3706 (list
3707 (read-from-minibuffer
3708 (concat "Set topic of " (erc-default-target) ": ")
3709 (when erc-channel-topic
3710 (cons (apply 'concat (butlast (split-string erc-channel-topic "\C-o")))
3711 0)))))
3712 (let ((topic-list (split-string topic "\C-o"))) ; strip off the topic setter
3713 (erc-cmd-TOPIC (concat (erc-default-target) " " (car topic-list)))))
3714
3715(defun erc-set-channel-limit (&optional limit)
3716 "Set a LIMIT for the current channel. Remove limit if nil.
3717Prompt for one if called interactively."
3718 (interactive (list (read-from-minibuffer
3719 (format "Limit for %s (RET to remove limit): "
3720 (erc-default-target)))))
3721 (let ((tgt (erc-default-target)))
3722 (if (and limit (>= (length limit) 1))
3723 (erc-server-send (format "MODE %s +l %s" tgt limit))
3724 (erc-server-send (format "MODE %s -l" tgt)))))
3725
3726(defun erc-set-channel-key (&optional key)
3727 "Set a KEY for the current channel. Remove key if nil.
3728Prompt for one if called interactively."
3729 (interactive (list (read-from-minibuffer
3730 (format "Key for %s (RET to remove key): "
3731 (erc-default-target)))))
3732 (let ((tgt (erc-default-target)))
3733 (if (and key (>= (length key) 1))
3734 (erc-server-send (format "MODE %s +k %s" tgt key))
3735 (erc-server-send (format "MODE %s -k" tgt)))))
3736
3737(defun erc-quit-server (reason)
3738 "Disconnect from current server after prompting for REASON.
3739`erc-quit-reason' works with this just like with `erc-cmd-QUIT'."
3740 (interactive (list (read-from-minibuffer
3741 (format "Reason for quitting %s: "
3742 (or erc-server-announced-name
3743 erc-session-server)))))
3744 (erc-cmd-QUIT reason))
3745
3746;; Movement of point
3747
3748(defun erc-bol ()
3749 "Move `point' to the beginning of the current line.
3750
3751This places `point' just after the prompt, or at the beginning of the line."
3752 (interactive)
3753 (forward-line 0)
3754 (when (get-text-property (point) 'erc-prompt)
3755 (goto-char erc-input-marker))
3756 (point))
3757
3758(defun erc-kill-input ()
3759 "Kill current input line using `erc-bol' followed by `kill-line'."
3760 (interactive)
3761 (when (and (erc-bol)
3762 (/= (point) (point-max))) ;; Prevent a (ding) and an error when
3763 ;; there's nothing to kill
3764 (if (boundp 'erc-input-ring-index)
3765 (setq erc-input-ring-index nil))
3766 (kill-line)))
3767
3768(defun erc-complete-word ()
3769 "Complete the word before point.
3770
3771This function uses `erc-complete-functions'."
3772 (interactive)
3773 (unless (run-hook-with-args-until-success 'erc-complete-functions)
3774 (beep)))
3775
3776;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3777;;
3778;; IRC SERVER INPUT HANDLING
3779;;
3780;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3781
3782;;;; New Input parsing
3783
3784; Stolen from ZenIRC. I just wanna test this code, so here is
3785; experiment area.
3786
3787(defcustom erc-default-server-hook '(erc-debug-missing-hooks
3788 erc-default-server-handler)
3789 "*Default for server messages which aren't covered by `erc-server-hooks'."
3790 :group 'erc-server-hooks
3791 :type 'hook)
3792
3793(defun erc-default-server-handler (proc parsed)
3794 "Default server handler.
3795
3796Displays PROC and PARSED appropriately using `erc-display-message'."
3797 (erc-display-message
3798 parsed 'notice proc
3799 (mapconcat
3800 'identity
3801 (let (res)
3802 (mapc #'(lambda (x)
3803 (if (stringp x)
3804 (setq res (append res (list x)))))
3805 parsed)
3806 res)
3807 " ")))
3808
3809(defvar erc-server-vectors
3810 '(["msgtype" "sender" "to" "arg1" "arg2" "arg3" "..."])
3811 "List of received server messages which ERC does not specifically handle.
3812See `erc-debug-missing-hooks'.")
3813;(make-variable-buffer-local 'erc-server-vectors)
3814
3815(defun erc-debug-missing-hooks (proc parsed)
3816 "Add PARSED server message ERC does not yet handle to `erc-server-vectors'.
3817These vectors can be helpful when adding new server message handlers to ERC.
3818See `erc-default-server-hook'."
3819 (nconc erc-server-vectors (list parsed))
3820 nil)
3821
3822(defun erc-query (target server)
3823 "Open a query buffer on TARGET, using SERVER.
3824To change how this query window is displayed, use `let' to bind
3825`erc-join-buffer' before calling this."
3826 (unless (and server
3827 (buffer-live-p server)
3828 (set-buffer server))
3829 (error "Couldn't switch to server buffer"))
83dc6995
MB
3830 (let ((buf (erc-open erc-session-server
3831 erc-session-port
3832 (erc-current-nick)
3833 erc-session-user-full-name
3834 nil
3835 nil
3836 (list target)
3837 target
3838 erc-server-process)))
597993cf
MB
3839 (unless buf
3840 (error "Couldn't open query window"))
3841 (erc-update-mode-line)
3842 buf))
3843
ff59d266 3844(defcustom erc-auto-query 'bury
597993cf
MB
3845 "If non-nil, create a query buffer each time you receive a private message.
3846
3847If the buffer doesn't already exist it is created. This can be
3848set to a symbol, to control how the new query window should
3849appear. See the documentation for `erc-join-buffer' for
3850available choices."
3851 :group 'erc-query
3852 :type '(choice (const nil)
3853 (const buffer)
3854 (const window)
3855 (const window-noselect)
3856 (const bury)
3857 (const frame)))
3858
3859(defcustom erc-query-on-unjoined-chan-privmsg t
3860 "If non-nil create query buffer on receiving any PRIVMSG at all.
3861This includes PRIVMSGs directed to channels. If you are using an IRC
3862bouncer, such as dircproxy, to keep a log of channels when you are
3863disconnected, you should set this option to t."
3864 :group 'erc-query
3865 :type 'boolean)
3866
3867(defcustom erc-format-query-as-channel-p t
3868 "If non-nil, format text from others in a query buffer like in a channel,
3869otherwise format like a private message."
3870 :group 'erc-query
3871 :type 'boolean)
3872
3873(defcustom erc-minibuffer-notice nil
3874 "If non-nil, print ERC notices for the user in the minibuffer.
3875Only happens when the session buffer isn't visible."
3876 :group 'erc-display
3877 :type 'boolean)
3878
3879(defcustom erc-minibuffer-ignored nil
3880 "If non-nil, print a message in the minibuffer if we ignored something."
3881 :group 'erc-ignore
3882 :type 'boolean)
3883
3884(defun erc-wash-quit-reason (reason nick login host)
3885 "Remove duplicate text from quit REASON.
3886Specifically in relation to NICK (user@host) information. Returns REASON
3887unmodified if nothing can be removed.
3888E.g. \"Read error to Nick [user@some.host]: 110\" would be shortened to
3889\"Read error: 110\". The same applies for \"Ping Timeout\"."
3890 (setq nick (regexp-quote nick)
3891 login (regexp-quote login)
3892 host (regexp-quote host))
3893 (or (when (string-match (concat "^\\(Read error\\) to "
3894 nick "\\[" host "\\]: "
3895 "\\(.+\\)$") reason)
3896 (concat (match-string 1 reason) ": " (match-string 2 reason)))
3897 (when (string-match (concat "^\\(Ping timeout\\) for "
3898 nick "\\[" host "\\]$") reason)
3899 (match-string 1 reason))
3900 reason))
3901
3902(defun erc-nickname-in-use (nick reason)
3903 "If NICK is unavailable, tell the user the REASON.
3904
3905See also `erc-display-error-notice'."
0b6bb130 3906 (if (or (not erc-try-new-nick-p)
597993cf
MB
3907 ;; how many default-nicks are left + one more try...
3908 (eq erc-nick-change-attempt-count
3909 (if (consp erc-nick)
3910 (+ (length erc-nick) 1)
3911 1)))
3912 (erc-display-error-notice
3913 nil
3914 (format "Nickname %s is %s, try another." nick reason))
3915 (setq erc-nick-change-attempt-count (+ erc-nick-change-attempt-count 1))
3916 (let ((newnick (nth 1 erc-default-nicks))
3917 (nicklen (cdr (assoc "NICKLEN"
ff59d266 3918 (erc-with-server-buffer
597993cf
MB
3919 erc-server-parameters)))))
3920 (setq erc-bad-nick t)
3921 ;; try to use a different nick
3922 (if erc-default-nicks
3923 (setq erc-default-nicks (cdr erc-default-nicks)))
3924 (if (not newnick)
3925 (setq newnick (concat (truncate-string-to-width
3926 nick
3927 (if (and erc-server-connected nicklen)
0b6bb130
MB
3928 (- (string-to-number nicklen)
3929 (length erc-nick-uniquifier))
597993cf
MB
3930 ;; rfc2812 max nick length = 9
3931 ;; we must assume this is the
3932 ;; server's setting if we haven't
3933 ;; established a connection yet
0b6bb130 3934 (- 9 (length erc-nick-uniquifier))))
597993cf
MB
3935 erc-nick-uniquifier)))
3936 (erc-cmd-NICK newnick)
3937 (erc-display-error-notice
3938 nil
3939 (format "Nickname %s is %s, trying %s"
3940 nick reason newnick)))))
3941
3942;;; Server messages
3943
3944(defgroup erc-server-hooks nil
3945 "Server event callbacks.
a3d2f0a3 3946Every server event - like numeric replies - has its own hook.
597993cf
MB
3947Those hooks are all called using `run-hook-with-args-until-success'.
3948They receive as first argument the process object from where the event
3949originated from,
3950and as second argument the event parsed as a vector."
3951 :group 'erc-hooks)
3952
3953(defun erc-display-server-message (proc parsed)
3954 "Display the message sent by the server as a notice."
3955 (erc-display-message
3956 parsed 'notice 'active (erc-response.contents parsed)))
3957
3958(defun erc-auto-query (proc parsed)
3959 ;; FIXME: This needs more documentation, unless it's not a user function --
3960 ;; Lawrence 2004-01-08
3961 "Put this on `erc-server-PRIVMSG-functions'."
3962 (when erc-auto-query
3963 (let* ((nick (car (erc-parse-user (erc-response.sender parsed))))
3964 (target (car (erc-response.command-args parsed)))
3965 (msg (erc-response.contents parsed))
3966 (query (if (not erc-query-on-unjoined-chan-privmsg)
3967 nick
3968 (if (erc-current-nick-p target)
3969 nick
3970 target))))
3971 (and (not (erc-ignored-user-p (erc-response.sender parsed)))
3972 (or erc-query-on-unjoined-chan-privmsg
3973 (string= target (erc-current-nick)))
3974 (not (erc-get-buffer query proc))
3975 (not (erc-is-message-ctcp-and-not-action-p msg))
3976 (let ((erc-join-buffer erc-auto-query))
3977 (erc-cmd-QUERY query))
3978 nil))))
3979
3980(defun erc-is-message-ctcp-p (message)
3981 "Check if MESSAGE is a CTCP message or not."
3982 (string-match "^\C-a\\([^\C-a]*\\)\C-a?$" message))
3983
3984(defun erc-is-message-ctcp-and-not-action-p (message)
3985 "Check if MESSAGE is a CTCP message or not."
3986 (and (erc-is-message-ctcp-p message)
3987 (not (string-match "^\C-a\\ACTION.*\C-a$" message))))
3988
3989(defun erc-format-privmessage (nick msg privp msgp)
3990 "Format a PRIVMSG in an insertible fashion."
3991 (let* ((mark-s (if msgp (if privp "*" "<") "-"))
3992 (mark-e (if msgp (if privp "*" ">") "-"))
3993 (str (format "%s%s%s %s" mark-s nick mark-e msg))
3994 (nick-face (if privp 'erc-nick-msg-face 'erc-nick-default-face))
3995 (msg-face (if privp 'erc-direct-msg-face 'erc-default-face)))
3996 ;; add text properties to text before the nick, the nick and after the nick
3997 (erc-put-text-property 0 (length mark-s) 'face msg-face str)
3998 (erc-put-text-property (length mark-s) (+ (length mark-s) (length nick))
3999 'face nick-face str)
4000 (erc-put-text-property (+ (length mark-s) (length nick)) (length str)
4001 'face msg-face str)
4002 str))
4003
4004(defcustom erc-format-nick-function 'erc-format-nick
4005 "*Function to format a nickname for message display."
4006 :group 'erc-display
4007 :type 'function)
4008
4009(defun erc-format-nick (&optional user channel-data)
83dc6995
MB
4010 "Return the nickname of USER.
4011See also `erc-format-nick-function'."
4012 (when user (erc-server-user-nickname user)))
597993cf
MB
4013
4014(defun erc-format-@nick (&optional user channel-data)
83dc6995
MB
4015 "Format the nickname of USER showing if USER is an operator or has voice.
4016Operators have \"@\" and users with voice have \"+\" as a prefix.
4017Use CHANNEL-DATA to determine op and voice status.
4018See also `erc-format-nick-function'."
4019 (when user
4020 (let ((op (and channel-data (erc-channel-user-op channel-data) "@"))
4021 (voice (and channel-data (erc-channel-user-voice channel-data) "+")))
4022 (concat voice op (erc-server-user-nickname user)))))
597993cf
MB
4023
4024(defun erc-format-my-nick ()
a3d2f0a3 4025 "Return the beginning of this user's message, correctly propertized."
597993cf
MB
4026 (if erc-show-my-nick
4027 (let ((open "<")
4028 (close "> ")
4029 (nick (erc-current-nick)))
4030 (concat
4031 (erc-propertize open 'face 'erc-default-face)
83dc6995 4032 (erc-propertize nick 'face 'erc-my-nick-face)
597993cf
MB
4033 (erc-propertize close 'face 'erc-default-face)))
4034 (let ((prefix "> "))
4035 (erc-propertize prefix 'face 'erc-default-face))))
4036
4037(defun erc-echo-notice-in-default-buffer (s parsed buffer sender)
4038 "Echos a private notice in the default buffer, namely the
4039target buffer specified by BUFFER, or there is no target buffer,
4040the server buffer. This function is designed to be added to
4041either `erc-echo-notice-hook' or `erc-echo-notice-always-hook',
4042and always returns t."
4043 (erc-display-message parsed nil buffer s)
4044 t)
4045
4046(defun erc-echo-notice-in-target-buffer (s parsed buffer sender)
4047 "Echos a private notice in BUFFER, if BUFFER is non-nil. This
4048function is designed to be added to either `erc-echo-notice-hook'
81bb49ce 4049or `erc-echo-notice-always-hook', and returns non-nil if BUFFER
597993cf
MB
4050is non-nil."
4051 (if buffer
4052 (progn (erc-display-message parsed nil buffer s) t)
4053 nil))
4054
4055(defun erc-echo-notice-in-minibuffer (s parsed buffer sender)
4056 "Echos a private notice in the minibuffer. This function is
4057designed to be added to either `erc-echo-notice-hook' or
4058`erc-echo-notice-always-hook', and always returns t."
4059 (message "%s" (concat "NOTICE: " s))
4060 t)
4061
4062(defun erc-echo-notice-in-server-buffer (s parsed buffer sender)
4063 "Echos a private notice in the server buffer. This function is
4064designed to be added to either `erc-echo-notice-hook' or
4065`erc-echo-notice-always-hook', and always returns t."
4066 (erc-display-message parsed nil nil s)
4067 t)
4068
4069(defun erc-echo-notice-in-active-non-server-buffer (s parsed buffer sender)
4070 "Echos a private notice in the active buffer if the active
4071buffer is not the server buffer. This function is designed to be
4072added to either `erc-echo-notice-hook' or
81bb49ce 4073`erc-echo-notice-always-hook', and returns non-nil if the active
597993cf
MB
4074buffer is not the server buffer."
4075 (if (not (eq (erc-server-buffer) (erc-active-buffer)))
4076 (progn (erc-display-message parsed nil 'active s) t)
4077 nil))
4078
4079(defun erc-echo-notice-in-active-buffer (s parsed buffer sender)
4080 "Echos a private notice in the active buffer. This function is
4081designed to be added to either `erc-echo-notice-hook' or
4082`erc-echo-notice-always-hook', and always returns t."
4083 (erc-display-message parsed nil 'active s)
4084 t)
4085
4086(defun erc-echo-notice-in-user-buffers (s parsed buffer sender)
4087 "Echos a private notice in all of the buffers for which SENDER
4088is a member. This function is designed to be added to either
4089`erc-echo-notice-hook' or `erc-echo-notice-always-hook', and
81bb49ce 4090returns non-nil if there is at least one buffer for which the
597993cf
MB
4091sender is a member.
4092
4093See also: `erc-echo-notice-in-first-user-buffer',
a3d2f0a3 4094`erc-buffer-list-with-nick'."
597993cf
MB
4095 (let ((buffers (erc-buffer-list-with-nick sender erc-server-process)))
4096 (if buffers
4097 (progn (erc-display-message parsed nil buffers s) t)
4098 nil)))
4099
4100(defun erc-echo-notice-in-user-and-target-buffers (s parsed buffer sender)
4101 "Echos a private notice in BUFFER and in all of the buffers for
4102which SENDER is a member. This function is designed to be added
4103to either `erc-echo-notice-hook' or
81bb49ce 4104`erc-echo-notice-always-hook', and returns non-nil if there is
597993cf
MB
4105at least one buffer for which the sender is a member or the
4106default target.
4107
4108See also: `erc-echo-notice-in-user-buffers',
a3d2f0a3 4109`erc-buffer-list-with-nick'."
597993cf
MB
4110 (let ((buffers (erc-buffer-list-with-nick sender erc-server-process)))
4111 (add-to-list 'buffers buffer)
4112 (if buffers
4113 (progn (erc-display-message parsed nil buffers s) t)
4114 nil)))
4115
4116(defun erc-echo-notice-in-first-user-buffer (s parsed buffer sender)
4117 "Echos a private notice in one of the buffers for which SENDER
4118is a member. This function is designed to be added to either
4119`erc-echo-notice-hook' or `erc-echo-notice-always-hook', and
81bb49ce 4120returns non-nil if there is at least one buffer for which the
597993cf
MB
4121sender is a member.
4122
4123See also: `erc-echo-notice-in-user-buffers',
a3d2f0a3 4124`erc-buffer-list-with-nick'."
597993cf
MB
4125 (let ((buffers (erc-buffer-list-with-nick sender erc-server-process)))
4126 (if buffers
4127 (progn (erc-display-message parsed nil (car buffers) s) t)
4128 nil)))
4129
4130;;; Ban manipulation
4131
4132(defun erc-banlist-store (proc parsed)
4133 "Record ban entries for a channel."
4134 (multiple-value-bind (channel mask whoset)
4135 (cdr (erc-response.command-args parsed))
4136 ;; Determine to which buffer the message corresponds
4137 (let ((buffer (erc-get-buffer channel proc)))
4138 (with-current-buffer buffer
4139 (unless (member (cons whoset mask) erc-channel-banlist)
4140 (setq erc-channel-banlist (cons (cons whoset mask)
4141 erc-channel-banlist))))))
4142 nil)
4143
4144(defun erc-banlist-finished (proc parsed)
4145 "Record that we have received the banlist."
4146 (let* ((channel (second (erc-response.command-args parsed)))
4147 (buffer (erc-get-buffer channel proc)))
4148 (with-current-buffer buffer
4149 (put 'erc-channel-banlist 'received-from-server t)))
4150 t) ; suppress the 'end of banlist' message
4151
4152(defun erc-banlist-update (proc parsed)
4153 "Check MODE commands for bans and update the banlist appropriately."
4154 ;; FIXME: Possibly incorrect. -- Lawrence 2004-05-11
4155 (let* ((tgt (first (erc-response.command-args parsed)))
4156 (mode (erc-response.contents parsed))
4157 (whoset (erc-response.sender parsed))
4158 (buffer (erc-get-buffer tgt proc)))
4159 (when buffer
4160 (with-current-buffer buffer
4161 (cond ((not (get 'erc-channel-banlist 'received-from-server)) nil)
4162 ((string-match "^\\([+-]\\)b" mode)
4163 ;; This is a ban
4164 (cond
4165 ((string-match "^-" mode)
4166 ;; Remove the unbanned masks from the ban list
4167 (setq erc-channel-banlist
4168 (erc-delete-if
4169 #'(lambda (y)
4170 (member (upcase (cdr y))
4171 (mapcar #'upcase
4172 (cdr (split-string mode)))))
4173 erc-channel-banlist)))
4174 ((string-match "^+" mode)
4175 ;; Add the banned mask(s) to the ban list
4176 (mapc
4177 (lambda (mask)
4178 (unless (member (cons whoset mask) erc-channel-banlist)
4179 (setq erc-channel-banlist
4180 (cons (cons whoset mask) erc-channel-banlist))))
4181 (cdr (split-string mode))))))))))
4182 nil)
4183
4184;; used for the banlist cmds
4185(defun erc-group-list (list n)
4186 "Group LIST into sublists of length N."
4187 (cond ((null list) nil)
4188 ((null (nthcdr n list)) (list list))
4189 (t (cons (erc-subseq list 0 n) (erc-group-list (nthcdr n list) n)))))
4190
4191
4192;;; MOTD numreplies
4193
4194(defun erc-handle-login ()
4195 "Handle the logging in process of connection."
4196 (unless erc-logged-in
4197 (setq erc-logged-in t)
4198 (message "Logging in as \'%s\'... done" (erc-current-nick))
4199 ;; execute a startup script
4200 (let ((f (erc-select-startup-file)))
4201 (when f
4202 (erc-load-script f)))))
4203
4204(defun erc-connection-established (proc parsed)
4205 "Run just after connection.
4206
a3d2f0a3 4207Set user modes and run `erc-after-connect' hook."
ff59d266
MB
4208 (with-current-buffer (process-buffer proc)
4209 (unless erc-server-connected ; only once per session
4210 (let ((server (or erc-server-announced-name
4211 (erc-response.sender parsed)))
4212 (nick (car (erc-response.command-args parsed)))
4213 (buffer (process-buffer proc)))
4214 (setq erc-server-connected t)
4215 (erc-update-mode-line)
4216 (erc-set-initial-user-mode nick buffer)
4217 (erc-server-setup-periodical-ping buffer)
4218 (run-hook-with-args 'erc-after-connect server nick)))))
4219
4220(defun erc-set-initial-user-mode (nick buffer)
4221 "If `erc-user-mode' is non-nil for NICK, set the user modes.
4222The server buffer is given by BUFFER."
4223 (with-current-buffer buffer
4224 (when erc-user-mode
4225 (let ((mode (if (functionp erc-user-mode)
4226 (funcall erc-user-mode)
4227 erc-user-mode)))
4228 (when (stringp mode)
4229 (erc-log (format "changing mode for %s to %s" nick mode))
4230 (erc-server-send (format "MODE %s %s" nick mode)))))))
597993cf
MB
4231
4232(defun erc-display-error-notice (parsed string)
4233 "Display STRING as an error notice.
4234
4235See also `erc-display-message'."
4236 (erc-display-message
4237 parsed '(notice error) 'active string))
4238
4239(defun erc-process-ctcp-query (proc parsed nick login host)
4240 ;; FIXME: This needs a proper docstring -- Lawrence 2004-01-08
4241 "Process a CTCP query."
4242 (let ((queries (delete "" (split-string (erc-response.contents parsed)
4243 "\C-a"))))
4244 (if (> (length queries) 4)
4245 (erc-display-message
4246 parsed (list 'notice 'error) proc 'ctcp-too-many)
4247 (if (= 0 (length queries))
4248 (erc-display-message
4249 parsed (list 'notice 'error) proc
4250 'ctcp-empty ?n nick)
4251 (while queries
4252 (let* ((type (upcase (car (split-string (car queries)))))
4253 (hook (intern-soft (concat "erc-ctcp-query-" type "-hook"))))
4254 (if (and hook (boundp hook))
4255 (if (string-equal type "ACTION")
4256 (run-hook-with-args-until-success
4257 hook proc parsed nick login host
4258 (car (erc-response.command-args parsed))
4259 (car queries))
4260 (when erc-paranoid
4261 (if (erc-current-nick-p
4262 (car (erc-response.command-args parsed)))
4263 (erc-display-message
4264 parsed 'error 'active 'ctcp-request
4265 ?n nick ?u login ?h host ?r (car queries))
4266 (erc-display-message
4267 parsed 'error 'active 'ctcp-request-to
4268 ?n nick ?u login ?h host ?r (car queries)
4269 ?t (car (erc-response.command-args parsed)))))
4270 (run-hook-with-args-until-success
4271 hook proc nick login host
4272 (car (erc-response.command-args parsed))
4273 (car queries)))
4274 (erc-display-message
4275 parsed (list 'notice 'error) proc
4276 'undefined-ctcp)))
4277 (setq queries (cdr queries)))))))
4278
4279(defvar erc-ctcp-query-ACTION-hook '(erc-ctcp-query-ACTION))
4280
4281(defun erc-ctcp-query-ACTION (proc parsed nick login host to msg)
4282 "Respond to a CTCP ACTION query."
4283 (when (string-match "^ACTION\\s-\\(.*\\)\\s-*$" msg)
4284 (let ((s (match-string 1 msg))
4285 (buf (or (erc-get-buffer to proc)
4286 (erc-get-buffer nick proc)
4287 (process-buffer proc))))
4288 (erc-display-message
4289 parsed 'action buf
4290 'ACTION ?n nick ?u login ?h host ?a s))))
4291
4292(defvar erc-ctcp-query-CLIENTINFO-hook '(erc-ctcp-query-CLIENTINFO))
4293
4294(defun erc-ctcp-query-CLIENTINFO (proc nick login host to msg)
4295 "Respond to a CTCP CLIENTINFO query."
4296 (when (string-match "^CLIENTINFO\\(\\s-*\\|\\s-+.*\\)$" msg)
4297 (let ((s (erc-client-info (erc-trim-string (match-string 1 msg)))))
4298 (unless erc-disable-ctcp-replies
4299 (erc-send-ctcp-notice nick (format "CLIENTINFO %s" s)))))
4300 nil)
4301
4302(defvar erc-ctcp-query-ECHO-hook '(erc-ctcp-query-ECHO))
4303(defun erc-ctcp-query-ECHO (proc nick login host to msg)
4304 "Respond to a CTCP ECHO query."
4305 (when (string-match "^ECHO\\s-+\\(.*\\)\\s-*$" msg)
4306 (let ((s (match-string 1 msg)))
4307 (unless erc-disable-ctcp-replies
4308 (erc-send-ctcp-notice nick (format "ECHO %s" s)))))
4309 nil)
4310
4311(defvar erc-ctcp-query-FINGER-hook '(erc-ctcp-query-FINGER))
4312(defun erc-ctcp-query-FINGER (proc nick login host to msg)
4313 "Respond to a CTCP FINGER query."
4314 (unless erc-disable-ctcp-replies
4315 (let ((s (if erc-anonymous-login
4316 (format "FINGER I'm %s." (erc-current-nick))
4317 (format "FINGER %s (%s@%s)."
4318 (user-full-name)
4319 (user-login-name)
4320 (system-name))))
4321 (ns (erc-time-diff erc-server-last-sent-time (erc-current-time))))
4322 (when (> ns 0)
4323 (setq s (concat s " Idle for " (erc-sec-to-time ns))))
4324 (erc-send-ctcp-notice nick s)))
4325 nil)
4326
4327(defvar erc-ctcp-query-PING-hook '(erc-ctcp-query-PING))
4328(defun erc-ctcp-query-PING (proc nick login host to msg)
4329 "Respond to a CTCP PING query."
4330 (when (string-match "^PING\\s-+\\(.*\\)" msg)
4331 (unless erc-disable-ctcp-replies
4332 (let ((arg (match-string 1 msg)))
4333 (erc-send-ctcp-notice nick (format "PING %s" arg)))))
4334 nil)
4335
4336(defvar erc-ctcp-query-TIME-hook '(erc-ctcp-query-TIME))
4337(defun erc-ctcp-query-TIME (proc nick login host to msg)
4338 "Respond to a CTCP TIME query."
4339 (unless erc-disable-ctcp-replies
4340 (erc-send-ctcp-notice nick (format "TIME %s" (current-time-string))))
4341 nil)
4342
4343(defvar erc-ctcp-query-USERINFO-hook '(erc-ctcp-query-USERINFO))
4344(defun erc-ctcp-query-USERINFO (proc nick login host to msg)
4345 "Respond to a CTCP USERINFO query."
4346 (unless erc-disable-ctcp-replies
4347 (erc-send-ctcp-notice nick (format "USERINFO %s" erc-user-information)))
4348 nil)
4349
4350(defvar erc-ctcp-query-VERSION-hook '(erc-ctcp-query-VERSION))
4351(defun erc-ctcp-query-VERSION (proc nick login host to msg)
4352 "Respond to a CTCP VERSION query."
4353 (unless erc-disable-ctcp-replies
4354 (erc-send-ctcp-notice
4355 nick (format
4356 "VERSION \C-bERC\C-b %s - an IRC client for emacs (\C-b%s\C-b)"
4357 erc-version-string
4358 erc-official-location)))
4359 nil)
4360
4361(defun erc-process-ctcp-reply (proc parsed nick login host msg)
4362 "Process MSG as a CTCP reply."
4363 (let* ((type (car (split-string msg)))
4364 (hook (intern (concat "erc-ctcp-reply-" type "-hook"))))
4365 (if (boundp hook)
4366 (run-hook-with-args-until-success
4367 hook proc nick login host
4368 (car (erc-response.command-args parsed)) msg)
4369 (erc-display-message
4370 parsed 'notice 'active
4371 'CTCP-UNKNOWN ?n nick ?u login ?h host ?m msg))))
4372
4373(defvar erc-ctcp-reply-ECHO-hook '(erc-ctcp-reply-ECHO))
4374(defun erc-ctcp-reply-ECHO (proc nick login host to msg)
4375 "Handle a CTCP ECHO reply."
4376 (when (string-match "^ECHO\\s-+\\(.*\\)\\s-*$" msg)
4377 (let ((message (match-string 1 msg)))
4378 (erc-display-message
4379 nil '(notice action) 'active
4380 'CTCP-ECHO ?n nick ?m message)))
4381 nil)
4382
4383(defvar erc-ctcp-reply-CLIENTINFO-hook '(erc-ctcp-reply-CLIENTINFO))
4384(defun erc-ctcp-reply-CLIENTINFO (proc nick login host to msg)
4385 "Handle a CTCP CLIENTINFO reply."
4386 (when (string-match "^CLIENTINFO\\s-+\\(.*\\)\\s-*$" msg)
4387 (let ((message (match-string 1 msg)))
4388 (erc-display-message
4389 nil 'notice 'active
4390 'CTCP-CLIENTINFO ?n nick ?m message)))
4391 nil)
4392
4393(defvar erc-ctcp-reply-FINGER-hook '(erc-ctcp-reply-FINGER))
4394(defun erc-ctcp-reply-FINGER (proc nick login host to msg)
4395 "Handle a CTCP FINGER reply."
4396 (when (string-match "^FINGER\\s-+\\(.*\\)\\s-*$" msg)
4397 (let ((message (match-string 1 msg)))
4398 (erc-display-message
4399 nil 'notice 'active
4400 'CTCP-FINGER ?n nick ?m message)))
4401 nil)
4402
4403(defvar erc-ctcp-reply-PING-hook '(erc-ctcp-reply-PING))
4404(defun erc-ctcp-reply-PING (proc nick login host to msg)
4405 "Handle a CTCP PING reply."
4406 (if (not (string-match "^PING\\s-+\\([0-9.]+\\)" msg))
4407 nil
4408 (let ((time (match-string 1 msg)))
4409 (condition-case nil
4410 (let ((delta (erc-time-diff (string-to-number time)
4411 (erc-current-time))))
4412 (erc-display-message
4413 nil 'notice 'active
4414 'CTCP-PING ?n nick
4415 ?t (erc-sec-to-time delta)))
4416 (range-error
4417 (erc-display-message
4418 nil 'error 'active
4419 'bad-ping-response ?n nick ?t time))))))
4420
4421(defvar erc-ctcp-reply-TIME-hook '(erc-ctcp-reply-TIME))
4422(defun erc-ctcp-reply-TIME (proc nick login host to msg)
4423 "Handle a CTCP TIME reply."
4424 (when (string-match "^TIME\\s-+\\(.*\\)\\s-*$" msg)
4425 (let ((message (match-string 1 msg)))
4426 (erc-display-message
4427 nil 'notice 'active
4428 'CTCP-TIME ?n nick ?m message)))
4429 nil)
4430
4431(defvar erc-ctcp-reply-VERSION-hook '(erc-ctcp-reply-VERSION))
4432(defun erc-ctcp-reply-VERSION (proc nick login host to msg)
4433 "Handle a CTCP VERSION reply."
4434 (when (string-match "^VERSION\\s-+\\(.*\\)\\s-*$" msg)
4435 (let ((message (match-string 1 msg)))
4436 (erc-display-message
4437 nil 'notice 'active
4438 'CTCP-VERSION ?n nick ?m message)))
4439 nil)
4440
4441(defun erc-process-away (proc away-p)
6904f7fe
MB
4442 "Toggle the away status of the user depending on the value of AWAY-P.
4443
4444If nil, set the user as away.
4445If non-nil, return from being away."
597993cf
MB
4446 (let ((sessionbuf (process-buffer proc)))
4447 (when sessionbuf
4448 (with-current-buffer sessionbuf
4449 (when erc-away-nickname
4450 (erc-log (format "erc-process-away: away-nick: %s, away-p: %s"
4451 erc-away-nickname away-p))
4452 (erc-cmd-NICK (if away-p
4453 erc-away-nickname
4454 erc-nick)))
4455 (cond
4456 (away-p
ff59d266 4457 (setq erc-away (current-time)))
597993cf
MB
4458 (t
4459 (let ((away-time erc-away))
4460 ;; away must be set to NIL BEFORE sending anything to prevent
4461 ;; an infinite recursion
ff59d266 4462 (setq erc-away nil)
597993cf
MB
4463 (save-excursion
4464 (set-buffer (erc-active-buffer))
4465 (when erc-public-away-p
4466 (erc-send-action
4467 (erc-default-target)
4468 (if away-time
4469 (format "is back (gone for %s)"
4470 (erc-sec-to-time
4471 (erc-time-diff
4472 (erc-emacs-time-to-erc-time away-time)
4473 (erc-current-time))))
4474 "is back")))))))))
4475 (erc-update-mode-line)))
4476
4477;;;; List of channel members handling
4478
4479(defun erc-channel-begin-receiving-names ()
4480 "Internal function.
4481
4482Used when a channel names list is about to be received. Should
4483be called with the current buffer set to the channel buffer.
4484
4485See also `erc-channel-end-receiving-names'."
4486 (setq erc-channel-new-member-names (make-hash-table :test 'equal)))
4487
4488(defun erc-channel-end-receiving-names ()
4489 "Internal function.
4490
4491Used to fix `erc-channel-users' after a channel names list has been
4492received. Should be called with the current buffer set to the
4493channel buffer.
4494
4495See also `erc-channel-begin-receiving-names'."
4496 (maphash (lambda (nick user)
4497 (if (null (gethash nick erc-channel-new-member-names))
4498 (erc-remove-channel-user nick)))
4499 erc-channel-users)
4500 (setq erc-channel-new-member-names nil))
4501
526dc846
MO
4502(defun erc-parse-prefix ()
4503 "Return an alist of valid prefix character types and their representations.
4504Example: (operator) o => @, (voiced) v => +."
4505 (let ((str (or (cdr (assoc "PREFIX" (erc-with-server-buffer
4506 erc-server-parameters)))
4507 ;; provide a sane default
4508 "(ov)@+"))
4509 types chars)
4510 (when (string-match "^(\\([^)]+\\))\\(.+\\)$" str)
4511 (setq types (match-string 1 str)
4512 chars (match-string 2 str))
4513 (let ((len (min (length types) (length chars)))
4514 (i 0)
4515 (alist nil))
4516 (while (< i len)
4517 (setq alist (cons (cons (elt types i) (elt chars i))
4518 alist))
4519 (setq i (1+ i)))
4520 alist))))
4521
597993cf
MB
4522(defun erc-channel-receive-names (names-string)
4523 "This function is for internal use only.
4524
4525Update `erc-channel-users' according to NAMES-STRING.
4526NAMES-STRING is a string listing some of the names on the
4527channel."
526dc846
MO
4528 (let (prefix op-ch voice-ch names name op voice)
4529 (setq prefix (erc-parse-prefix))
4530 (setq op-ch (cdr (assq ?o prefix))
4531 voice-ch (cdr (assq ?v prefix)))
4532 ;; We need to delete "" because in XEmacs, (split-string "a ")
4533 ;; returns ("a" "").
4534 (setq names (delete "" (split-string names-string)))
4535 (let ((erc-channel-members-changed-hook nil))
4536 (dolist (item names)
4537 (let ((updatep t)
4538 ch)
4539 (if (rassq (elt item 0) prefix)
4540 (cond ((= (length item) 1)
4541 (setq updatep nil))
4542 ((eq (elt item 0) op-ch)
4543 (setq name (substring item 1)
4544 op 'on
4545 voice 'off))
4546 ((eq (elt item 0) voice-ch)
4547 (setq name (substring item 1)
4548 op 'off
4549 voice 'on))
4550 (t (setq name (substring item 1)
4551 op 'off
4552 voice 'off)))
4553 (setq name item
4554 op 'off
4555 voice 'off))
4556 (when updatep
4557 (puthash (erc-downcase name) t
4558 erc-channel-new-member-names)
4559 (erc-update-current-channel-member
4560 name name t op voice)))))
597993cf
MB
4561 (run-hooks 'erc-channel-members-changed-hook)))
4562
4563(defcustom erc-channel-members-changed-hook nil
4564 "*This hook is called every time the variable `channel-members' changes.
4565The buffer where the change happened is current while this hook is called."
4566 :group 'erc-hooks
4567 :type 'hook)
4568
4569(defun erc-update-user-nick (nick &optional new-nick
4570 host login full-name info)
4571 "Updates the stored user information for the user with nickname
4572NICK.
4573
a3d2f0a3 4574See also: `erc-update-user'."
597993cf
MB
4575 (erc-update-user (erc-get-server-user nick) new-nick
4576 host login full-name info))
4577
4578(defun erc-update-user (user &optional new-nick
4579 host login full-name info)
4580 "Update user info for USER. USER must be an erc-server-user
4581struct. Any of NEW-NICK, HOST, LOGIN, FULL-NAME, INFO which are
4582non-nil and not equal to the existing values for USER are used to
4583replace the stored values in USER.
4584
a3d2f0a3 4585If, and only if, a change is made,
597993cf 4586`erc-channel-members-changed-hook' is run for each channel for
a3d2f0a3 4587which USER is a member, and t is returned."
597993cf
MB
4588 (let (changed)
4589 (when user
4590 (when (and new-nick
4591 (not (equal (erc-server-user-nickname user)
4592 new-nick)))
4593 (setq changed t)
4594 (erc-change-user-nickname user new-nick))
4595 (when (and host
4596 (not (equal (erc-server-user-host user) host)))
4597 (setq changed t)
4598 (setf (erc-server-user-host user) host))
4599 (when (and login
4600 (not (equal (erc-server-user-login user) login)))
4601 (setq changed t)
4602 (setf (erc-server-user-login user) login))
4603 (when (and full-name
4604 (not (equal (erc-server-user-full-name user)
4605 full-name)))
4606 (setq changed t)
4607 (setf (erc-server-user-full-name user) full-name))
4608 (when (and info
4609 (not (equal (erc-server-user-info user) info)))
4610 (setq changed t)
4611 (setf (erc-server-user-info user) info))
4612 (if changed
4613 (dolist (buf (erc-server-user-buffers user))
4614 (if (buffer-live-p buf)
4615 (with-current-buffer buf
4616 (run-hooks 'erc-channel-members-changed-hook))))))
4617 changed))
4618
4619(defun erc-update-current-channel-member
4620 (nick new-nick &optional add op voice host login full-name info
4621 update-message-time)
4622 "Updates the stored user information for the user with nickname
4623NICK. `erc-update-user' is called to handle changes to nickname,
a3d2f0a3
JB
4624HOST, LOGIN, FULL-NAME, and INFO. If OP or VOICE are non-nil,
4625they must be equal to either `on' or `off', in which case the
4626operator or voice status of the user in the current channel is
4627changed accordingly. If UPDATE-MESSAGE-TIME is non-nil, the
597993cf
MB
4628last-message-time of the user in the current channel is set
4629to (current-time).
4630
4631If ADD is non-nil, the user will be added with the specified
4632information if it is not already present in the user or channel
4633lists.
4634
4635If, and only if, changes are made, or the user is added,
a3d2f0a3 4636`erc-channel-members-updated-hook' is run, and t is returned.
597993cf
MB
4637
4638See also: `erc-update-user' and `erc-update-channel-member'."
4639 (let* (changed user-changed
4640 (channel-data (erc-get-channel-user nick))
4641 (cuser (if channel-data (cdr channel-data)))
4642 (user (if channel-data (car channel-data)
4643 (erc-get-server-user nick))))
4644 (if cuser
4645 (progn
4646 (erc-log (format "update-member: user = %S, cuser = %S" user cuser))
4647 (when (and op
4648 (not (eq (erc-channel-user-op cuser) op)))
4649 (setq changed t)
4650 (setf (erc-channel-user-op cuser)
4651 (cond ((eq op 'on) t)
526dc846
MO
4652 ((eq op 'off) nil)
4653 (t op))))
597993cf
MB
4654 (when (and voice
4655 (not (eq (erc-channel-user-voice cuser) voice)))
4656 (setq changed t)
4657 (setf (erc-channel-user-voice cuser)
4658 (cond ((eq voice 'on) t)
526dc846
MO
4659 ((eq voice 'off) nil)
4660 (t voice))))
597993cf
MB
4661 (when update-message-time
4662 (setf (erc-channel-user-last-message-time cuser) (current-time)))
4663 (setq user-changed
4664 (erc-update-user user new-nick
4665 host login full-name info)))
4666 (when add
4667 (if (null user)
4668 (progn
4669 (setq user (make-erc-server-user
4670 :nickname nick
4671 :host host
4672 :full-name full-name
4673 :login login
4674 :info info
4675 :buffers (list (current-buffer))))
4676 (erc-add-server-user nick user))
4677 (setf (erc-server-user-buffers user)
4678 (cons (current-buffer)
4679 (erc-server-user-buffers user))))
4680 (setq cuser (make-erc-channel-user
4681 :op (cond ((eq op 'on) t)
526dc846
MO
4682 ((eq op 'off) nil)
4683 (t op))
597993cf 4684 :voice (cond ((eq voice 'on) t)
526dc846
MO
4685 ((eq voice 'off) nil)
4686 (t voice))
597993cf
MB
4687 :last-message-time
4688 (if update-message-time (current-time))))
4689 (puthash (erc-downcase nick) (cons user cuser)
4690 erc-channel-users)
4691 (setq changed t)))
4692 (when (and changed (null user-changed))
4693 (run-hooks 'erc-channel-members-changed-hook))
4694 (or changed user-changed add)))
4695
4696(defun erc-update-channel-member (channel nick new-nick
4697 &optional add op voice host login
4698 full-name info update-message-time)
4699 "Updates user and channel information for the user with
4700nickname NICK in channel CHANNEL.
4701
a3d2f0a3 4702See also: `erc-update-current-channel-member'."
597993cf
MB
4703 (erc-with-buffer
4704 (channel)
4705 (erc-update-current-channel-member nick new-nick add op voice host
4706 login full-name info
4707 update-message-time)))
4708
4709(defun erc-remove-current-channel-member (nick)
a3d2f0a3
JB
4710 "Remove NICK from current channel membership list.
4711Runs `erc-channel-members-changed-hook'."
597993cf
MB
4712 (let ((channel-data (erc-get-channel-user nick)))
4713 (when channel-data
4714 (erc-remove-channel-user nick)
4715 (run-hooks 'erc-channel-members-changed-hook))))
4716
4717(defun erc-remove-channel-member (channel nick)
4718 "Remove NICK from CHANNEL's membership list.
4719
4720See also `erc-remove-current-channel-member'."
4721 (erc-with-buffer
4722 (channel)
4723 (erc-remove-current-channel-member nick)))
4724
4725(defun erc-update-channel-topic (channel topic &optional modify)
4726 "Find a buffer for CHANNEL and set the TOPIC for it.
4727
4728If optional MODIFY is 'append or 'prepend, then append or prepend the
4729TOPIC string to the current topic."
4730 (erc-with-buffer (channel)
4731 (cond ((eq modify 'append)
4732 (setq erc-channel-topic (concat erc-channel-topic topic)))
4733 ((eq modify 'prepend)
4734 (setq erc-channel-topic (concat topic erc-channel-topic)))
4735 (t (setq erc-channel-topic topic)))
4736 (erc-update-mode-line-buffer (current-buffer))))
4737
4738(defun erc-set-modes (tgt mode-string)
4739 "Set the modes for the TGT provided as MODE-STRING."
4740 (let* ((modes (erc-parse-modes mode-string))
4741 (add-modes (nth 0 modes))
4742 (remove-modes (nth 1 modes))
4743 ;; list of triples: (mode-char 'on/'off argument)
4744 (arg-modes (nth 2 modes)))
4745 (cond ((erc-channel-p tgt); channel modes
83dc6995 4746 (let ((buf (and erc-server-process
597993cf
MB
4747 (erc-get-buffer tgt erc-server-process))))
4748 (when buf
4749 (with-current-buffer buf
4750 (setq erc-channel-modes add-modes)
4751 (setq erc-channel-user-limit nil)
4752 (setq erc-channel-key nil)
4753 (while arg-modes
4754 (let ((mode (nth 0 (car arg-modes)))
4755 (onoff (nth 1 (car arg-modes)))
4756 (arg (nth 2 (car arg-modes))))
4757 (cond ((string-match "^[Ll]" mode)
4758 (erc-update-channel-limit tgt onoff arg))
4759 ((string-match "^[Kk]" mode)
4760 (erc-update-channel-key tgt onoff arg))
4761 (t nil)))
4762 (setq arg-modes (cdr arg-modes)))
4763 (erc-update-mode-line-buffer buf)))))
4764 ;; we do not keep our nick's modes yet
4765 ;;(t (setq erc-user-modes add-modes))
4766 )
4767 ))
4768
4769(defun erc-sort-strings (list-of-strings)
4770 "Sort LIST-OF-STRINGS in lexicographic order.
4771
4772Side-effect free."
4773 (sort (copy-sequence list-of-strings) 'string<))
4774
4775(defun erc-parse-modes (mode-string)
4776 "Parse MODE-STRING into a list.
4777
4778Returns a list of three elements:
4779
4780 (ADD-MODES REMOVE-MODES ARG-MODES).
4781
4782The add-modes and remove-modes are lists of single-character strings
4783for modes without parameters to add and remove respectively. The
4784arg-modes is a list of triples of the form:
4785
4786 (MODE-CHAR ON/OFF ARGUMENT)."
4787 (if (string-match "^\\s-*\\(\\S-+\\)\\(\\s-.*$\\|$\\)" mode-string)
4788 (let ((chars (mapcar 'char-to-string (match-string 1 mode-string)))
4789 ;; arguments in channel modes
4790 (args-str (match-string 2 mode-string))
4791 (args nil)
4792 (add-modes nil)
4793 (remove-modes nil)
4794 (arg-modes nil); list of triples: (mode-char 'on/'off argument)
4795 (add-p t))
4796 ;; make the argument list
4797 (while (string-match "^\\s-*\\(\\S-+\\)\\(\\s-+.*$\\|$\\)" args-str)
4798 (setq args (cons (match-string 1 args-str) args))
4799 (setq args-str (match-string 2 args-str)))
4800 (setq args (nreverse args))
4801 ;; collect what modes changed, and match them with arguments
4802 (while chars
4803 (cond ((string= (car chars) "+") (setq add-p t))
4804 ((string= (car chars) "-") (setq add-p nil))
4805 ((string-match "^[ovbOVB]" (car chars))
4806 (setq arg-modes (cons (list (car chars)
4807 (if add-p 'on 'off)
4808 (if args (car args) nil))
4809 arg-modes))
4810 (if args (setq args (cdr args))))
4811 ((string-match "^[LlKk]" (car chars))
4812 (setq arg-modes (cons (list (car chars)
4813 (if add-p 'on 'off)
4814 (if (and add-p args)
4815 (car args) nil))
4816 arg-modes))
4817 (if (and add-p args) (setq args (cdr args))))
4818 (add-p (setq add-modes (cons (car chars) add-modes)))
4819 (t (setq remove-modes (cons (car chars) remove-modes))))
4820 (setq chars (cdr chars)))
4821 (setq add-modes (nreverse add-modes))
4822 (setq remove-modes (nreverse remove-modes))
4823 (setq arg-modes (nreverse arg-modes))
4824 (list add-modes remove-modes arg-modes))
4825 nil))
4826
4827(defun erc-update-modes (tgt mode-string &optional nick host login)
4828 "Update the mode information for TGT, provided as MODE-STRING.
4829Optional arguments: NICK, HOST and LOGIN - the attributes of the
4830person who changed the modes."
4831 (let* ((modes (erc-parse-modes mode-string))
4832 (add-modes (nth 0 modes))
4833 (remove-modes (nth 1 modes))
4834 ;; list of triples: (mode-char 'on/'off argument)
4835 (arg-modes (nth 2 modes)))
4836 ;; now parse the modes changes and do the updates
4837 (cond ((erc-channel-p tgt); channel modes
83dc6995 4838 (let ((buf (and erc-server-process
597993cf
MB
4839 (erc-get-buffer tgt erc-server-process))))
4840 (when buf
4841 ;; FIXME! This used to have an original buffer
4842 ;; variable, but it never switched back to the original
4843 ;; buffer. Is this wanted behavior?
4844 (set-buffer buf)
4845 (if (not (boundp 'erc-channel-modes))
4846 (setq erc-channel-modes nil))
4847 (while remove-modes
4848 (setq erc-channel-modes (delete (car remove-modes)
4849 erc-channel-modes)
4850 remove-modes (cdr remove-modes)))
4851 (while add-modes
4852 (setq erc-channel-modes (cons (car add-modes)
4853 erc-channel-modes)
4854 add-modes (cdr add-modes)))
4855 (setq erc-channel-modes (erc-sort-strings erc-channel-modes))
4856 (while arg-modes
4857 (let ((mode (nth 0 (car arg-modes)))
4858 (onoff (nth 1 (car arg-modes)))
4859 (arg (nth 2 (car arg-modes))))
4860 (cond ((string-match "^[oO]" mode)
4861 (erc-update-channel-member tgt arg arg nil onoff))
4862 ((string-match "^[Vv]" mode)
4863 (erc-update-channel-member tgt arg arg nil nil
4864 onoff))
4865 ((string-match "^[Ll]" mode)
4866 (erc-update-channel-limit tgt onoff arg))
4867 ((string-match "^[Kk]" mode)
4868 (erc-update-channel-key tgt onoff arg))
4869 (t nil)); only ops are tracked now
4870 (setq arg-modes (cdr arg-modes))))
4871 (erc-update-mode-line buf))))
4872 ;; nick modes - ignored at this point
4873 (t nil))))
4874
4875(defun erc-update-channel-limit (channel onoff n)
4876 ;; FIXME: what does ONOFF actually do? -- Lawrence 2004-01-08
4877 "Update CHANNEL's user limit to N."
4878 (if (or (not (eq onoff 'on))
4879 (and (stringp n) (string-match "^[0-9]+$" n)))
4880 (erc-with-buffer
4881 (channel)
4882 (cond ((eq onoff 'on) (setq erc-channel-user-limit (string-to-number n)))
4883 (t (setq erc-channel-user-limit nil))))))
4884
4885(defun erc-update-channel-key (channel onoff key)
4886 "Update CHANNEL's key to KEY if ONOFF is 'on or to nil if it's 'off."
4887 (erc-with-buffer
4888 (channel)
4889 (cond ((eq onoff 'on) (setq erc-channel-key key))
4890 (t (setq erc-channel-key nil)))))
4891
4892(defun erc-handle-user-status-change (type nlh &optional l)
4893 "Handle changes in any user's status.
4894
4895So far, only nick change is handled.
4896
4897Generally, the TYPE argument is a symbol describing the change type, NLH is
4898a list containing the original nickname, login name and hostname for the user,
4899and L is a list containing additional TYPE-specific arguments.
4900
4901So far the following TYPE/L pairs are supported:
4902
4903 Event TYPE L
4904
4905 nickname change 'nick (NEW-NICK)"
4906 (erc-log (format "user-change: type: %S nlh: %S l: %S" type nlh l))
4907 (cond
4908 ;; nickname change
4909 ((equal type 'nick)
4910 t)
4911 (t
4912 nil)))
4913
4914(defun erc-highlight-notice (s)
4915 "Highlight notice message S and return it.
a3d2f0a3 4916See also variable `erc-notice-highlight-type'."
597993cf
MB
4917 (cond
4918 ((eq erc-notice-highlight-type 'prefix)
4919 (erc-put-text-property 0 (length erc-notice-prefix)
4920 'face 'erc-notice-face s)
4921 s)
4922 ((eq erc-notice-highlight-type 'all)
4923 (erc-put-text-property 0 (length s) 'face 'erc-notice-face s)
4924 s)
4925 (t s)))
4926
4927(defun erc-make-notice (message)
4928 "Notify the user of MESSAGE."
4929 (when erc-minibuffer-notice
4930 (message "%s" message))
4931 (erc-highlight-notice (concat erc-notice-prefix message)))
4932
4933(defun erc-highlight-error (s)
4934 "Highlight error message S and return it."
4935 (erc-put-text-property 0 (length s) 'face 'erc-error-face s)
4936 s)
4937
4938(defun erc-put-text-property (start end property value &optional object)
4939 "Set text-property for an object (usually a string).
4940START and END define the characters covered.
4941PROPERTY is the text-property set, usually the symbol `face'.
4942VALUE is the value for the text-property, usually a face symbol such as
4943the face `bold' or `erc-pal-face'.
4944OBJECT is a string which will be modified and returned.
4945OBJECT is modified without being copied first.
4946
4947You can redefine or `defadvice' this function in order to add
4948EmacsSpeak support."
4949 (put-text-property start end property value object))
4950
4951(defun erc-list (thing)
4952 "Return THING if THING is a list, or a list with THING as its element."
4953 (if (listp thing)
4954 thing
4955 (list thing)))
4956
4957(defun erc-parse-user (string)
4958 "Parse STRING as a user specification (nick!login@host).
4959
4960Return a list of the three separate tokens."
4961 (cond
21bc768b 4962 ((string-match "^\\([^!\n]*\\)!\\([^@\n]*\\)@\\(.*\\)$" string)
597993cf
MB
4963 (list (match-string 1 string)
4964 (match-string 2 string)
4965 (match-string 3 string)))
4966 ;; Some bogus bouncers send Nick!(null), try to live with that.
21bc768b 4967 ((string-match "^\\([^!\n]*\\)!\\(.*\\)$" string)
597993cf
MB
4968 (list (match-string 1 string)
4969 ""
4970 (match-string 2 string)))
4971 (t
4972 (list string "" ""))))
4973
4974(defun erc-extract-nick (string)
4975 "Return the nick corresponding to a user specification STRING.
4976
4977See also `erc-parse-user'."
4978 (car (erc-parse-user string)))
4979
4980(defun erc-put-text-properties (start end properties
4981 &optional object value-list)
4982 "Set text-properties for OBJECT.
4983
4984START and END describe positions in OBJECT.
4985If VALUE-LIST is nil, set each property in PROPERTIES to t, else set
4986each property to the corresponding value in VALUE-LIST."
4987 (unless value-list
4988 (setq value-list (mapcar (lambda (x)
4989 t)
4990 properties)))
4991 (mapcar* (lambda (prop value)
4992 (erc-put-text-property start end prop value object))
4993 properties value-list))
4994
4995;;; Input area handling:
4996
4997(defun erc-beg-of-input-line ()
4998 "Return the value of `point' at the beginning of the input line.
4999
5000Specifically, return the position of `erc-insert-marker'."
5001 (or (and (boundp 'erc-insert-marker)
5002 (markerp erc-insert-marker))
5003 (error "erc-insert-marker has no value, please report a bug"))
5004 (marker-position erc-insert-marker))
5005
5006(defun erc-end-of-input-line ()
5007 "Return the value of `point' at the end of the input line."
5008 (point-max))
5009
5010(defun erc-send-current-line ()
5011 "Parse current line and send it to IRC."
5012 (interactive)
5013 (save-restriction
5014 (widen)
526dc846
MO
5015 (if (< (point) (erc-beg-of-input-line))
5016 (erc-error "Point is not in the input area")
597993cf
MB
5017 (let ((inhibit-read-only t)
5018 (str (erc-user-input))
5019 (old-buf (current-buffer)))
526dc846
MO
5020 (if (and (not (erc-server-buffer-live-p))
5021 (not (erc-command-no-process-p str)))
5022 (erc-error "ERC: No process running")
5023 (erc-set-active-buffer (current-buffer))
5024
5025 ;; Kill the input and the prompt
5026 (delete-region (erc-beg-of-input-line)
5027 (erc-end-of-input-line))
5028
5029 (unwind-protect
5030 (erc-send-input str)
5031 ;; Fix the buffer if the command didn't kill it
5032 (when (buffer-live-p old-buf)
5033 (with-current-buffer old-buf
5034 (save-restriction
5035 (widen)
5036 (goto-char (point-max))
5037 (when (processp erc-server-process)
5038 (set-marker (process-mark erc-server-process) (point)))
5039 (set-marker erc-insert-marker (point))
5040 (let ((buffer-modified (buffer-modified-p)))
5041 (erc-display-prompt)
5042 (set-buffer-modified-p buffer-modified))))))
5043
5044 ;; Only when last hook has been run...
5045 (run-hook-with-args 'erc-send-completed-hook str))))))
597993cf
MB
5046
5047(defun erc-user-input ()
5048 "Return the input of the user in the current buffer."
5049 (buffer-substring
5050 erc-input-marker
5051 (erc-end-of-input-line)))
5052
b6675b2d 5053(defvar erc-command-regexp "^/\\([A-Za-z']+\\)\\(\\s-+.*\\|\\s-*\\)$"
6904f7fe
MB
5054 "Regular expression used for matching commands in ERC.")
5055
597993cf
MB
5056(defun erc-send-input (input)
5057 "Treat INPUT as typed in by the user. It is assumed that the input
5058and the prompt is already deleted.
81bb49ce 5059This returns non-nil only if we actually send anything."
597993cf
MB
5060 ;; Handle different kinds of inputs
5061 (cond
5062 ;; Ignore empty input
5063 ((if erc-send-whitespace-lines
5064 (string= input "")
5065 (string-match "\\`[ \t\r\f\n]*\\'" input))
5066 (when erc-warn-about-blank-lines
5067 (message "Blank line - ignoring...")
5068 (beep))
5069 nil)
5070 (t
5071 (let ((str input)
5072 (erc-insert-this t))
5073 (setq erc-send-this t)
5074 (run-hook-with-args 'erc-send-pre-hook input)
5075 (when erc-send-this
5076 (if (or (string-match "\n" str)
6904f7fe 5077 (not (string-match erc-command-regexp str)))
597993cf
MB
5078 (mapc
5079 (lambda (line)
5080 (mapc
5081 (lambda (line)
5082 ;; Insert what has to be inserted for this.
5083 (erc-display-msg line)
5084 (erc-process-input-line (concat line "\n")
5085 (null erc-flood-protect) t))
c89e5cd7
MB
5086 (or (and erc-flood-protect (erc-split-line line))
5087 (list line))))
597993cf
MB
5088 (split-string str "\n"))
5089 ;; Insert the prompt along with the command.
5090 (erc-display-command str)
5091 (erc-process-input-line (concat str "\n") t nil))
5092 t)))))
5093
5094(defun erc-display-command (line)
5095 (when erc-insert-this
5096 (let ((insert-position (point)))
5097 (unless erc-hide-prompt
5098 (erc-display-prompt nil nil (erc-command-indicator)
5099 (and (erc-command-indicator)
5100 'erc-command-indicator-face)))
5101 (let ((beg (point)))
5102 (insert line)
5103 (erc-put-text-property beg (point)
5104 'face 'erc-command-indicator-face)
5105 (insert "\n"))
526dc846
MO
5106 (when (processp erc-server-process)
5107 (set-marker (process-mark erc-server-process) (point)))
597993cf
MB
5108 (set-marker erc-insert-marker (point))
5109 (save-excursion
5110 (save-restriction
5111 (narrow-to-region insert-position (point))
5112 (run-hooks 'erc-send-modify-hook)
5113 (run-hooks 'erc-send-post-hook))))))
5114
5115(defun erc-display-msg (line)
5116 "Display LINE as a message of the user to the current target at the
5117current position."
5118 (when erc-insert-this
5119 (let ((insert-position (point)))
5120 (insert (erc-format-my-nick))
5121 (let ((beg (point)))
5122 (insert line)
5123 (erc-put-text-property beg (point)
5124 'face 'erc-input-face))
5125 (insert "\n")
526dc846
MO
5126 (when (processp erc-server-process)
5127 (set-marker (process-mark erc-server-process) (point)))
597993cf
MB
5128 (set-marker erc-insert-marker (point))
5129 (save-excursion
5130 (save-restriction
5131 (narrow-to-region insert-position (point))
5132 (run-hooks 'erc-send-modify-hook)
5133 (run-hooks 'erc-send-post-hook))))))
5134
5135(defun erc-command-symbol (command)
a3d2f0a3 5136 "Return the ERC command symbol for COMMAND if it exists and is bound."
597993cf
MB
5137 (let ((cmd (intern-soft (format "erc-cmd-%s" (upcase command)))))
5138 (when (fboundp cmd) cmd)))
5139
5140(defun erc-extract-command-from-line (line)
5141 "Extract command and args from the input LINE.
5142If no command was given, return nil. If command matches, return a
5143list of the form: (command args) where both elements are strings."
6904f7fe 5144 (when (string-match erc-command-regexp line)
597993cf
MB
5145 (let* ((cmd (erc-command-symbol (match-string 1 line)))
5146 ;; note: return is nil, we apply this simply for side effects
5147 (canon-defun (while (and cmd (symbolp (symbol-function cmd)))
5148 (setq cmd (symbol-function cmd))))
5149 (cmd-fun (or cmd #'erc-cmd-default))
5150 (arg (if cmd
5151 (if (get cmd-fun 'do-not-parse-args)
5152 (format "%s" (match-string 2 line))
5153 (delete "" (split-string (erc-trim-string
5154 (match-string 2 line)) " ")))
5155 line)))
5156 (list cmd-fun arg))))
5157
5158(defun erc-split-multiline-safe (string)
5159 "Split STRING, containing multiple lines and return them in a list.
5160Do it only for STRING as the complete input, do not carry unfinished
5161strings over to the next call."
5162 (let ((l ())
5163 (i0 0)
5164 (doit t))
5165 (while doit
5166 (let ((i (string-match "\r?\n" string i0))
5167 (s (substring string i0)))
5168 (cond (i (setq l (cons (substring string i0 i) l))
5169 (setq i0 (match-end 0)))
5170 ((> (length s) 0)
5171 (setq l (cons s l))(setq doit nil))
5172 (t (setq doit nil)))))
5173 (nreverse l)))
5174
5175;; nick handling
5176
5177(defun erc-set-current-nick (nick)
5178 "Set the current nickname to NICK."
ff59d266
MB
5179 (with-current-buffer (if (buffer-live-p (erc-server-buffer))
5180 (erc-server-buffer)
5181 (current-buffer))
597993cf
MB
5182 (setq erc-server-current-nick nick)))
5183
5184(defun erc-current-nick ()
5185 "Return the current nickname."
5186 (with-current-buffer (if (buffer-live-p (erc-server-buffer))
5187 (erc-server-buffer)
5188 (current-buffer))
5189 erc-server-current-nick))
5190
5191(defun erc-current-nick-p (nick)
5192 "Return non-nil if NICK is the current nickname."
5193 (erc-nick-equal-p nick (erc-current-nick)))
5194
5195(defun erc-nick-equal-p (nick1 nick2)
5196 "Return non-nil if NICK1 and NICK2 are the same.
5197
5198This matches strings according to the IRC protocol's case convention.
5199
5200See also `erc-downcase'."
5201 (string= (erc-downcase nick1)
5202 (erc-downcase nick2)))
5203
5204;; default target handling
5205
5206(defun erc-default-target ()
5207 "Return the current default target (as a character string) or nil if none."
5208 (let ((tgt (car erc-default-recipients)))
5209 (cond
5210 ((not tgt) nil)
5211 ((listp tgt) (cdr tgt))
5212 (t tgt))))
5213
5214(defun erc-add-default-channel (channel)
5215 "Add CHANNEL to the default channel list."
5216
5217 (let ((d1 (car erc-default-recipients))
5218 (d2 (cdr erc-default-recipients))
5219 (chl (downcase channel)))
5220 (setq erc-default-recipients
5221 (cons chl erc-default-recipients))))
5222
5223(defun erc-delete-default-channel (channel &optional buffer)
5224 "Delete CHANNEL from the default channel list."
5225 (let ((ob (current-buffer)))
5226 (with-current-buffer (if (and buffer
5227 (bufferp buffer))
5228 buffer
5229 (current-buffer))
5230 (setq erc-default-recipients (delete (downcase channel)
5231 erc-default-recipients)))))
5232
5233(defun erc-add-query (nickname)
5234 "Add QUERY'd NICKNAME to the default channel list.
5235
a3d2f0a3 5236The previous default target of QUERY type gets removed."
597993cf
MB
5237 (let ((d1 (car erc-default-recipients))
5238 (d2 (cdr erc-default-recipients))
5239 (qt (cons 'QUERY (downcase nickname))))
5240 (if (and (listp d1)
5241 (eq (car d1) 'QUERY))
5242 (setq erc-default-recipients (cons qt d2))
5243 (setq erc-default-recipients (cons qt erc-default-recipients)))))
5244
5245(defun erc-delete-query ()
5246 "Delete the topmost target if it is a QUERY."
5247
5248 (let ((d1 (car erc-default-recipients))
5249 (d2 (cdr erc-default-recipients)))
5250 (if (and (listp d1)
5251 (eq (car d1) 'QUERY))
5252 (setq erc-default-recipients d2)
5253 (error "Current target is not a QUERY"))))
5254
5255(defun erc-ignored-user-p (spec)
5256 "Return non-nil if SPEC matches something in `erc-ignore-list'.
5257
5258Takes a full SPEC of a user in the form \"nick!login@host\", and
5259matches against all the regexp's in `erc-ignore-list'. If any
5260match, returns that regexp."
83dc6995 5261 (catch 'found
ff59d266 5262 (dolist (ignored (erc-with-server-buffer erc-ignore-list))
83dc6995
MB
5263 (if (string-match ignored spec)
5264 (throw 'found ignored)))))
597993cf
MB
5265
5266(defun erc-ignored-reply-p (msg tgt proc)
5267 ;; FIXME: this docstring needs fixing -- Lawrence 2004-01-08
5268 "Return non-nil if MSG matches something in `erc-ignore-reply-list'.
5269
5270Takes a message MSG to a channel and returns non-nil if the addressed
5271user matches any regexp in `erc-ignore-reply-list'."
5272 (let ((target-nick (erc-message-target msg)))
5273 (if (not target-nick)
5274 nil
5275 (erc-with-buffer (tgt proc)
5276 (let ((user (erc-get-server-user target-nick)))
5277 (when user
5278 (erc-list-match erc-ignore-reply-list
5279 (erc-user-spec user))))))))
5280
5281(defun erc-message-target (msg)
5282 "Return the addressed target in MSG.
5283
5284The addressed target is the string before the first colon in MSG."
21bc768b 5285 (if (string-match "^\\([^: \n]*\\):" msg)
597993cf
MB
5286 (match-string 1 msg)
5287 nil))
5288
5289(defun erc-user-spec (user)
5290 "Create a nick!user@host spec from a user struct."
5291 (let ((nick (erc-server-user-nickname user))
5292 (host (erc-server-user-host user))
5293 (login (erc-server-user-login user)))
5294 (concat (if nick
5295 nick
5296 "")
5297 "!"
5298 (if login
5299 login
5300 "")
5301 "@"
5302 (if host
5303 host
5304 ""))))
5305
5306(defun erc-list-match (lst str)
5307 "Return non-nil if any regexp in LST matches STR."
5308 (memq nil (mapcar (lambda (regexp)
5309 (not (string-match regexp str)))
5310 lst)))
5311
5312;; other "toggles"
5313
5314(defun erc-toggle-ctcp-autoresponse (&optional arg)
5315 "Toggle automatic CTCP replies (like VERSION and PING).
5316
5317If ARG is positive, turns CTCP replies on.
5318
5319If ARG is non-nil and not positive, turns CTCP replies off."
5320 (interactive "P")
5321 (cond ((and (numberp arg) (> arg 0))
5322 (setq erc-disable-ctcp-replies t))
5323 (arg (setq erc-disable-ctcp-replies nil))
5324 (t (setq erc-disable-ctcp-replies (not erc-disable-ctcp-replies))))
5325 (message "ERC CTCP replies are %s" (if erc-disable-ctcp-replies "OFF" "ON")))
5326
5327(defun erc-toggle-flood-control (&optional arg)
5328 "Toggle use of flood control on sent messages.
5329
0b6bb130
MB
5330If ARG is positive, use flood control.
5331If ARG is non-nil and not positive, do not use flood control.
597993cf
MB
5332
5333See `erc-server-flood-margin' for an explanation of the available
5334flood control parameters."
5335 (interactive "P")
0b6bb130
MB
5336 (cond ((and (numberp arg) (> arg 0))
5337 (setq erc-flood-protect t))
5338 (arg (setq erc-flood-protect nil))
5339 (t (setq erc-flood-protect (not erc-flood-protect))))
597993cf
MB
5340 (message "ERC flood control is %s"
5341 (cond (erc-flood-protect "ON")
5342 (t "OFF"))))
5343
5344;; Some useful channel and nick commands for fast key bindings
5345
5346(defun erc-invite-only-mode (&optional arg)
5347 "Turn on the invite only mode (+i) for the current channel.
5348
5349If ARG is non-nil, turn this mode off (-i).
5350
5351This command is sent even if excess flood is detected."
5352 (interactive "P")
5353 (erc-set-active-buffer (current-buffer))
5354 (let ((tgt (erc-default-target))
5355 (erc-force-send t))
5356 (cond ((or (not tgt) (not (erc-channel-p tgt)))
5357 (erc-display-message nil 'error (current-buffer) 'no-target))
5358 (arg (erc-load-irc-script-lines (list (concat "/mode " tgt " -i"))
5359 t))
5360 (t (erc-load-irc-script-lines (list (concat "/mode " tgt " +i"))
5361 t)))))
5362
5363(defun erc-get-channel-mode-from-keypress (key)
5364 "Read a key sequence and call the corresponding channel mode function.
0b6bb130 5365After doing C-c C-o, type in a channel mode letter.
597993cf
MB
5366
5367C-g means quit.
0b6bb130 5368RET lets you type more than one mode at a time.
597993cf
MB
5369If \"l\" is pressed, `erc-set-channel-limit' gets called.
5370If \"k\" is pressed, `erc-set-channel-key' gets called.
5371Anything else will be sent to `erc-toggle-channel-mode'."
5372 (interactive "kChannel mode (RET to set more than one): ")
5373 (when (featurep 'xemacs)
5374 (setq key (char-to-string (event-to-character (aref key 0)))))
5375 (cond ((equal key "\C-g")
5376 (keyboard-quit))
5377 ((equal key "\C-m")
5378 (erc-insert-mode-command))
5379 ((equal key "l")
5380 (call-interactively 'erc-set-channel-limit))
5381 ((equal key "k")
5382 (call-interactively 'erc-set-channel-key))
5383 (t (erc-toggle-channel-mode key))))
5384
5385(defun erc-toggle-channel-mode (mode &optional channel)
5386 "Toggle channel MODE.
5387
5388If CHANNEL is non-nil, toggle MODE for that channel, otherwise use
5389`erc-default-target'."
5390 (interactive "P")
5391 (erc-set-active-buffer (current-buffer))
5392 (let ((tgt (or channel (erc-default-target)))
5393 (erc-force-send t))
5394 (cond ((or (null tgt) (null (erc-channel-p tgt)))
5395 (erc-display-message nil 'error 'active 'no-target))
5396 ((member mode erc-channel-modes)
5397 (erc-log (format "%s: Toggle mode %s OFF" tgt mode))
5398 (message "Toggle channel mode %s OFF" mode)
5399 (erc-server-send (format "MODE %s -%s" tgt mode)))
5400 (t (erc-log (format "%s: Toggle channel mode %s ON" tgt mode))
5401 (message "Toggle channel mode %s ON" mode)
5402 (erc-server-send (format "MODE %s +%s" tgt mode))))))
5403
5404(defun erc-insert-mode-command ()
5405 "Insert the line \"/mode <current target> \" at `point'."
5406 (interactive)
5407 (let ((tgt (erc-default-target)))
5408 (if tgt (insert (concat "/mode " tgt " "))
5409 (erc-display-message nil 'error (current-buffer) 'no-target))))
5410
5411(defun erc-channel-names ()
5412 "Run \"/names #channel\" in the current channel."
5413 (interactive)
5414 (erc-set-active-buffer (current-buffer))
5415 (let ((tgt (erc-default-target)))
5416 (if tgt (erc-load-irc-script-lines (list (concat "/names " tgt)))
5417 (erc-display-message nil 'error (current-buffer) 'no-target))))
5418
5419(defun erc-remove-text-properties-region (start end &optional object)
5420 "Clears the region (START,END) in OBJECT from all colors, etc."
5421 (interactive "r")
5422 (save-excursion
5423 (let ((inhibit-read-only t))
5424 (set-text-properties start end nil object))))
5425
5426;; script execution and startup
5427
5428(defun erc-find-file (file &optional path)
5429 "Search for a FILE in the filesystem.
5430First the `default-directory' is searched for FILE, then any directories
5431specified in the list PATH.
5432
5433If FILE is found, return the path to it."
5434 (let ((filepath file))
5435 (if (file-readable-p filepath) filepath
5436 (progn
5437 (while (and path
5438 (progn (setq filepath (expand-file-name file (car path)))
5439 (not (file-readable-p filepath))))
5440 (setq path (cdr path)))
5441 (if path filepath nil)))))
5442
5443(defun erc-select-startup-file ()
5444 "Select an ERC startup file.
5445See also `erc-startup-file-list'."
b5bc193f
MB
5446 (catch 'found
5447 (dolist (f erc-startup-file-list)
26cced44 5448 (setq f (convert-standard-filename f))
b5bc193f
MB
5449 (when (file-readable-p f)
5450 (throw 'found f)))))
597993cf
MB
5451
5452(defun erc-find-script-file (file)
5453 "Search for FILE in `default-directory', and any in `erc-script-path'."
5454 (erc-find-file file erc-script-path))
5455
5456(defun erc-load-script (file)
5457 "Load a script from FILE.
5458
5459FILE must be the full name, it is not searched in the
5460`erc-script-path'. If the filename ends with `.el', then load it
a3d2f0a3
JB
5461as an Emacs Lisp program. Otherwise, treat it as a regular IRC
5462script."
597993cf
MB
5463 (erc-log (concat "erc-load-script: " file))
5464 (cond
5465 ((string-match "\\.el$" file)
5466 (load file))
5467 (t
5468 (erc-load-irc-script file))))
5469
5470(defun erc-process-script-line (line &optional args)
5471 "Process an IRC script LINE.
5472
5473Does script-specific substitutions (script arguments, current nick,
a3d2f0a3 5474server, etc.) in LINE and returns it.
597993cf
MB
5475
5476Substitutions are: %C and %c = current target (channel or nick),
5477%S %s = current server, %N %n = my current nick, and %x is x verbatim,
5478where x is any other character;
5479$* = the entire argument string, $1 = the first argument, $2 = the second,
a3d2f0a3 5480and so on."
597993cf
MB
5481 (if (not args) (setq args ""))
5482 (let* ((arg-esc-regexp "\\(\\$\\(\\*\\|[1-9][0-9]*\\)\\)\\([^0-9]\\|$\\)")
5483 (percent-regexp "\\(%.\\)")
5484 (esc-regexp (concat arg-esc-regexp "\\|" percent-regexp))
5485 (tgt (erc-default-target))
5486 (server (and (boundp 'erc-session-server) erc-session-server))
5487 (nick (erc-current-nick))
5488 (res "")
5489 (tmp nil)
5490 (arg-list nil)
5491 (arg-num 0))
5492 (if (not tgt) (setq tgt ""))
5493 (if (not server) (setq server ""))
5494 (if (not nick) (setq nick ""))
5495 ;; First, compute the argument list
5496 (setq tmp args)
5497 (while (string-match "^\\s-*\\(\\S-+\\)\\(\\s-+.*$\\|$\\)" tmp)
5498 (setq arg-list (cons (match-string 1 tmp) arg-list))
5499 (setq tmp (match-string 2 tmp)))
5500 (setq arg-list (nreverse arg-list))
5501 (setq arg-num (length arg-list))
5502 ;; now do the substitution
5503 (setq tmp (string-match esc-regexp line))
5504 (while tmp
5505 ;;(message "beginning of while: tmp=%S" tmp)
5506 (let* ((hd (substring line 0 tmp))
5507 (esc "")
5508 (subst "")
5509 (tail (substring line tmp)))
5510 (cond ((string-match (concat "^" arg-esc-regexp) tail)
5511 (setq esc (match-string 1 tail))
5512 (setq tail (substring tail (match-end 1))))
5513 ((string-match (concat "^" percent-regexp) tail)
5514 (setq esc (match-string 1 tail))
5515 (setq tail (substring tail (match-end 1)))))
5516 ;;(message "hd=%S, esc=%S, tail=%S, arg-num=%S" hd esc tail arg-num)
5517 (setq res (concat res hd))
5518 (setq subst
5519 (cond ((string= esc "") "")
5520 ((string-match "^\\$\\*$" esc) args)
5521 ((string-match "^\\$\\([0-9]+\\)$" esc)
5522 (let ((n (string-to-number (match-string 1 esc))))
5523 (message "n = %S, integerp(n)=%S" n (integerp n))
5524 (if (<= n arg-num) (nth (1- n) arg-list) "")))
5525 ((string-match "^%[Cc]$" esc) tgt)
5526 ((string-match "^%[Ss]$" esc) server)
5527 ((string-match "^%[Nn]$" esc) nick)
5528 ((string-match "^%\\(.\\)$" esc) (match-string 1 esc))
5529 (t (erc-log (format "BUG in erc-process-script-line: bad escape sequence: %S\n" esc))
5530 (message "BUG IN ERC: esc=%S" esc)
5531 "")))
5532 (setq line tail)
5533 (setq tmp (string-match esc-regexp line))
5534 (setq res (concat res subst))
5535 ;;(message "end of while: line=%S, res=%S, tmp=%S" line res tmp)
5536 ))
5537 (setq res (concat res line))
5538 res))
5539
5540(defun erc-load-irc-script (file &optional force)
5541 "Load an IRC script from FILE."
5542 (erc-log (concat "erc-load-script: " file))
5543 (let ((str (with-temp-buffer
5544 (insert-file-contents file)
5545 (buffer-string))))
5546 (erc-load-irc-script-lines (erc-split-multiline-safe str) force)))
5547
5548(defun erc-load-irc-script-lines (lines &optional force noexpand)
5549 "Load IRC script LINES (a list of strings).
5550
5551If optional NOEXPAND is non-nil, do not expand script-specific
5552sequences, process the lines verbatim. Use this for multiline
5553user input."
5554 (let* ((cb (current-buffer))
5555 (pnt (point))
5556 (s "")
5557 (sp (or (erc-command-indicator) (erc-prompt)))
5558 (args (and (boundp 'erc-script-args) erc-script-args)))
5559 (if (and args (string-match "^ " args))
5560 (setq args (substring args 1)))
5561 ;; prepare the prompt string for echo
5562 (erc-put-text-property 0 (length sp)
5563 'face 'erc-command-indicator-face sp)
5564 (while lines
5565 (setq s (car lines))
5566 (erc-log (concat "erc-load-script: CMD: " s))
5567 (unless (string-match "^\\s-*$" s)
5568 (let ((line (if noexpand s (erc-process-script-line s args))))
5569 (if (and (erc-process-input-line line force)
5570 erc-script-echo)
5571 (progn
5572 (erc-put-text-property 0 (length line)
5573 'face 'erc-input-face line)
5574 (erc-display-line (concat sp line) cb)))))
5575 (setq lines (cdr lines)))))
5576
5577;; authentication
5578
5579(defun erc-login ()
5580 "Perform user authentication at the IRC server."
5581 (erc-log (format "login: nick: %s, user: %s %s %s :%s"
5582 (erc-current-nick)
5583 (user-login-name)
6904f7fe 5584 (or erc-system-name (system-name))
597993cf
MB
5585 erc-session-server
5586 erc-session-user-full-name))
5587 (if erc-session-password
5588 (erc-server-send (format "PASS %s" erc-session-password))
5589 (message "Logging in without password"))
5590 (erc-server-send (format "NICK %s" (erc-current-nick)))
5591 (erc-server-send
5592 (format "USER %s %s %s :%s"
5593 ;; hacked - S.B.
5594 (if erc-anonymous-login erc-email-userid (user-login-name))
5595 "0" "*"
5596 erc-session-user-full-name))
5597 (erc-update-mode-line))
5598
5599;; connection properties' heuristics
5600
5601(defun erc-determine-parameters (&optional server port nick name)
5602 "Determine the connection and authentication parameters.
5603Sets the buffer local variables:
5604
a3d2f0a3
JB
5605- `erc-session-server'
5606- `erc-session-port'
5607- `erc-session-full-name'
5608- `erc-server-current-nick'"
597993cf
MB
5609 (setq erc-session-server (erc-compute-server server)
5610 erc-session-port (or port erc-default-port)
5611 erc-session-user-full-name (erc-compute-full-name name))
5612 (erc-set-current-nick (erc-compute-nick nick)))
5613
5614(defun erc-compute-server (&optional server)
5615 "Return an IRC server name.
5616
0b6bb130
MB
5617This tries a number of increasingly more default methods until a
5618non-nil value is found.
597993cf 5619
83dc6995 5620- SERVER (the argument passed to this function)
0b6bb130 5621- The `erc-server' option
597993cf 5622- The value of the IRCSERVER environment variable
0b6bb130 5623- The `erc-default-server' variable"
597993cf
MB
5624 (or server
5625 erc-server
5626 (getenv "IRCSERVER")
5627 erc-default-server))
5628
5629(defun erc-compute-nick (&optional nick)
0b6bb130 5630 "Return user's IRC nick.
597993cf 5631
0b6bb130
MB
5632This tries a number of increasingly more default methods until a
5633non-nil value is found.
597993cf 5634
0b6bb130
MB
5635- NICK (the argument passed to this function)
5636- The `erc-nick' option
597993cf 5637- The value of the IRCNICK environment variable
0b6bb130 5638- The result from the `user-login-name' function"
597993cf
MB
5639 (or nick
5640 (if (consp erc-nick) (car erc-nick) erc-nick)
5641 (getenv "IRCNICK")
5642 (user-login-name)))
5643
5644
5645(defun erc-compute-full-name (&optional full-name)
0b6bb130 5646 "Return user's full name.
597993cf 5647
0b6bb130
MB
5648This tries a number of increasingly more default methods until a
5649non-nil value is found.
597993cf 5650
0b6bb130
MB
5651- FULL-NAME (the argument passed to this function)
5652- The `erc-user-full-name' option
597993cf 5653- The value of the IRCNAME environment variable
0b6bb130 5654- The result from the `user-full-name' function"
597993cf
MB
5655 (or full-name
5656 erc-user-full-name
5657 (getenv "IRCNAME")
5658 (if erc-anonymous-login "unknown" nil)
5659 (user-full-name)))
5660
5661(defun erc-compute-port (&optional port)
5662 "Return a port for an IRC server.
5663
0b6bb130
MB
5664This tries a number of increasingly more default methods until a
5665non-nil value is found.
597993cf 5666
0b6bb130
MB
5667- PORT (the argument passed to this function)
5668- The `erc-port' option
5669- The `erc-default-port' variable"
5670 (or port erc-port erc-default-port))
597993cf
MB
5671
5672;; time routines
5673
5674(defun erc-string-to-emacs-time (string)
5675 "Convert the long number represented by STRING into an Emacs format.
5676Returns a list of the form (HIGH LOW), compatible with Emacs time format."
5677 (let* ((n (string-to-number (concat string ".0"))))
5678 (list (truncate (/ n 65536))
5679 (truncate (mod n 65536)))))
5680
5681(defun erc-emacs-time-to-erc-time (time)
5682 "Convert Emacs TIME to a number of seconds since the epoch."
5683 (when time
5684 (+ (* (nth 0 time) 65536.0) (nth 1 time))))
5685; (round (+ (* (nth 0 tm) 65536.0) (nth 1 tm))))
5686
5687(defun erc-current-time ()
5688 "Return the `current-time' as a number of seconds since the epoch.
5689
5690See also `erc-emacs-time-to-erc-time'."
5691 (erc-emacs-time-to-erc-time (current-time)))
5692
5693(defun erc-time-diff (t1 t2)
5694 "Return the time difference in seconds between T1 and T2."
5695 (abs (- t2 t1)))
5696
5697(defun erc-time-gt (t1 t2)
5698 "Check whether T1 > T2."
5699 (> t1 t2))
5700
5701(defun erc-sec-to-time (ns)
5702 "Convert NS to a time string HH:MM.SS."
5703 (setq ns (truncate ns))
5704 (format "%02d:%02d.%02d"
5705 (/ ns 3600)
5706 (/ (% ns 3600) 60)
5707 (% ns 60)))
5708
5709(defun erc-seconds-to-string (seconds)
5710 "Convert a number of SECONDS into an English phrase."
5711 (let (days hours minutes format-args output)
5712 (setq days (/ seconds 86400)
5713 seconds (% seconds 86400)
5714 hours (/ seconds 3600)
5715 seconds (% seconds 3600)
5716 minutes (/ seconds 60)
5717 seconds (% seconds 60)
5718 format-args (if (> days 0)
5719 `("%d days, %d hours, %d minutes, %d seconds"
5720 ,days ,hours ,minutes ,seconds)
5721 (if (> hours 0)
5722 `("%d hours, %d minutes, %d seconds"
5723 ,hours ,minutes ,seconds)
5724 (if (> minutes 0)
5725 `("%d minutes, %d seconds" ,minutes ,seconds)
5726 `("%d seconds" ,seconds))))
5727 output (apply 'format format-args))
5728 ;; Change all "1 units" to "1 unit".
5729 (while (string-match "\\([^0-9]\\|^\\)1 \\S-+\\(s\\)" output)
5730 (setq output (erc-replace-match-subexpression-in-string
5731 "" output (match-string 2 output) 2 (match-beginning 2))))
5732 output))
5733
5734
5735;; info
5736
5737(defconst erc-clientinfo-alist
5738 '(("ACTION" . "is used to inform about one's current activity")
5739 ("CLIENTINFO" . "gives help on CTCP commands supported by client")
5740 ("ECHO" . "echoes its arguments back")
5741 ("FINGER" . "shows user's name, location, and idle time")
5742 ("PING" . "measures delay between peers")
5743 ("TIME" . "shows client-side time")
5744 ("USERINFO" . "shows information provided by a user")
5745 ("VERSION" . "shows client type and version"))
5746 "Alist of CTCP CLIENTINFO for ERC commands.")
5747
5748(defun erc-client-info (s)
5749 "Return CTCP CLIENTINFO on command S.
a3d2f0a3 5750If S is nil or an empty string then return general CLIENTINFO."
597993cf
MB
5751 (if (or (not s) (string= s ""))
5752 (concat
5753 (apply #'concat
5754 (mapcar (lambda (e)
5755 (concat (car e) " "))
5756 erc-clientinfo-alist))
5757 ": use CLIENTINFO <COMMAND> to get more specific information")
5758 (let ((h (assoc (upcase s) erc-clientinfo-alist)))
5759 (if h
5760 (concat s " " (cdr h))
5761 (concat s ": unknown command")))))
5762
5763;; Hook functions
5764
5765(defun erc-directory-writable-p (dir)
5766 "Determine whether DIR is a writable directory.
5767If it doesn't exist, create it."
5768 (unless (file-attributes dir) (make-directory dir))
5769 (or (file-accessible-directory-p dir) (error "Cannot access %s" dir)))
5770
5771(defun erc-kill-query-buffers (process)
5772 "Kill all buffers of PROCESS."
5773 ;; here, we only want to match the channel buffers, to avoid
5774 ;; "selecting killed buffers" b0rkage.
5775 (erc-with-all-buffers-of-server process
5776 (lambda ()
5777 (not (erc-server-buffer-p)))
5778 (kill-buffer (current-buffer))))
5779
5780(defun erc-nick-at-point ()
5781 "Give information about the nickname at `point'.
5782
5783If called interactively, give a human readable message in the
5784minibuffer. If called programatically, return the corresponding
5785entry of `channel-members'."
5786 (interactive)
5787 (require 'thingatpt)
5788 (let* ((word (word-at-point))
5789 (channel-data (erc-get-channel-user word))
5790 (cuser (cdr channel-data))
5791 (user (if channel-data
5792 (car channel-data)
5793 (erc-get-server-user word)))
5794 host login full-name info nick op voice)
5795 (when user
5796 (setq nick (erc-server-user-nickname user)
5797 host (erc-server-user-host user)
5798 login (erc-server-user-login user)
5799 full-name (erc-server-user-full-name user)
5800 info (erc-server-user-info user))
5801 (if cuser
5802 (setq op (erc-channel-user-op cuser)
5803 voice (erc-channel-user-voice cuser)))
5804 (if (interactive-p)
5805 (message "%s is %s@%s%s%s"
5806 nick login host
5807 (if full-name (format " (%s)" full-name) "")
5808 (if (or op voice)
5809 (format " and is +%s%s on %s"
5810 (if op "o" "")
5811 (if voice "v" "")
5812 (erc-default-target))
5813 ""))
5814 user))))
5815
ff59d266
MB
5816(defun erc-away-time ()
5817 "Return non-nil if the current ERC process is set away.
5818
5819In particular, the time that we were set away is returned.
5820See `current-time' for details on the time format."
5821 (erc-with-server-buffer erc-away))
597993cf
MB
5822
5823;; Mode line handling
5824
5825(defcustom erc-mode-line-format "%s %a"
5826 "A string to be formatted and shown in the mode-line in `erc-mode'.
5827
5828The string is formatted using `format-spec' and the result is set as the value
5829of `mode-line-buffer-identification'.
5830
5831The following characters are replaced:
5832%a: String indicating away status or \"\" if you are not away
6904f7fe 5833%l: The estimated lag time to the server
597993cf
MB
5834%m: The modes of the channel
5835%n: The current nick name
5836%o: The topic of the channel
5837%p: The session port
5838%t: The name of the target (channel, nickname, or servername:port)
5839%s: In the server-buffer, this gets filled with the value of
5840 `erc-server-announced-name', in a channel, the value of
5841 (erc-default-target) also get concatenated."
5842 :group 'erc-mode-line-and-header
5843 :type 'string)
5844
6904f7fe 5845(defcustom erc-header-line-format "%n on %t (%m,%l) %o"
597993cf 5846 "A string to be formatted and shown in the header-line in `erc-mode'.
6904f7fe 5847Only used starting in Emacs 21.
597993cf 5848
ff59d266
MB
5849Set this to nil if you do not want the header line to be
5850displayed.
5851
597993cf
MB
5852See `erc-mode-line-format' for which characters are can be used."
5853 :group 'erc-mode-line-and-header
ff59d266
MB
5854 :set (lambda (sym val)
5855 (set sym val)
5856 (when (fboundp 'erc-update-mode-line)
5857 (erc-update-mode-line nil)))
5858 :type '(choice (const :tag "Disabled" nil)
5859 string))
597993cf
MB
5860
5861(defcustom erc-header-line-uses-help-echo-p t
5862 "Show the contents of the header line in the echo area or as a tooltip
5863when you move point into the header line."
5864 :group 'erc-mode-line-and-header
5865 :type 'boolean)
5866
c89e5cd7
MB
5867(defcustom erc-header-line-face-method nil
5868 "Determine what method to use when colorizing the header line text.
5869
5870If nil, don't colorize the header text.
5871If given a function, call it and use the resulting face name.
5872Otherwise, use the `erc-header-line' face."
5873 :group 'erc-mode-line-and-header
5874 :type '(choice (const :tag "Don't colorize" nil)
5875 (const :tag "Use the erc-header-line face" t)
5876 (function :tag "Call a function")))
5877
597993cf 5878(defcustom erc-show-channel-key-p t
84ae33df 5879 "Show the channel key in the header line."
597993cf
MB
5880 :group 'erc-paranoia
5881 :type 'boolean)
5882
5883(defcustom erc-common-server-suffixes
5884 '(("openprojects.net$" . "OPN")
ff59d266
MB
5885 ("freenode.net$" . "freenode")
5886 ("oftc.net$" . "OFTC"))
597993cf
MB
5887 "Alist of common server name suffixes.
5888This variable is used in mode-line display to save screen
5889real estate. Set it to nil if you want to avoid changing
5890displayed hostnames."
5891 :group 'erc-mode-line-and-header
5892 :type 'alist)
5893
5894(defcustom erc-mode-line-away-status-format
5895 "(AWAY since %a %b %d %H:%M) "
5896 "When you're away on a server, this is shown in the mode line.
5897This should be a string with substitution variables recognized by
a3d2f0a3 5898`format-time-string'."
597993cf
MB
5899 :group 'erc-mode-line-and-header
5900 :type 'string)
5901
5902(defun erc-shorten-server-name (server-name)
5903 "Shorten SERVER-NAME according to `erc-common-server-suffixes'."
5904 (if (stringp server-name)
5905 (with-temp-buffer
5906 (insert server-name)
5907 (let ((alist erc-common-server-suffixes))
5908 (while alist
5909 (goto-char (point-min))
5910 (if (re-search-forward (caar alist) nil t)
5911 (replace-match (cdar alist)))
5912 (setq alist (cdr alist))))
5913 (buffer-string))))
5914
5915(defun erc-format-target ()
5916 "Return the name of the target (channel or nickname or servername:port)."
5917 (let ((target (erc-default-target)))
5918 (or target
5919 (concat (erc-shorten-server-name
5920 (or erc-server-announced-name
5921 erc-session-server))
5922 ":" (erc-port-to-string erc-session-port)))))
5923
5924(defun erc-format-target-and/or-server ()
5925 "Return the server name or the current target and server name combined."
5926 (let ((server-name (erc-shorten-server-name
5927 (or erc-server-announced-name
5928 erc-session-server))))
5929 (cond ((erc-default-target)
5930 (concat (erc-string-no-properties (erc-default-target))
5931 "@" server-name))
5932 (server-name server-name)
5933 (t (buffer-name (current-buffer))))))
5934
5935(defun erc-format-away-status ()
5936 "Return a formatted `erc-mode-line-away-status-format'
5937if `erc-away' is non-nil."
ff59d266 5938 (let ((a (erc-away-time)))
597993cf
MB
5939 (if a
5940 (format-time-string erc-mode-line-away-status-format a)
5941 "")))
5942
5943(defun erc-format-channel-modes ()
6904f7fe
MB
5944 "Return the current channel's modes."
5945 (concat (apply 'concat
5946 "+" erc-channel-modes)
5947 (cond ((and erc-channel-user-limit erc-channel-key)
5948 (if erc-show-channel-key-p
5949 (format "lk %.0f %s" erc-channel-user-limit
5950 erc-channel-key)
5951 (format "kl %.0f" erc-channel-user-limit)))
5952 (erc-channel-user-limit
5953 ;; Emacs has no bignums
5954 (format "l %.0f" erc-channel-user-limit))
5955 (erc-channel-key
5956 (if erc-show-channel-key-p
5957 (format "k %s" erc-channel-key)
5958 "k"))
5959 (t nil))))
5960
5961(defun erc-format-lag-time ()
5962 "Return the estimated lag time to server, `erc-server-lag'."
ff59d266 5963 (let ((lag (erc-with-server-buffer erc-server-lag)))
6904f7fe
MB
5964 (cond (lag (format "lag:%.0f" lag))
5965 (t ""))))
597993cf 5966
c2fd78e0
GM
5967;; erc-goodies is required at end of this file.
5968(declare-function erc-controls-strip "erc-goodies" (str))
5969
597993cf
MB
5970(defun erc-update-mode-line-buffer (buffer)
5971 "Update the mode line in a single ERC buffer BUFFER."
5972 (with-current-buffer buffer
5973 (let ((spec (format-spec-make
5974 ?a (erc-format-away-status)
6904f7fe 5975 ?l (erc-format-lag-time)
597993cf
MB
5976 ?m (erc-format-channel-modes)
5977 ?n (or (erc-current-nick) "")
5978 ?o (erc-controls-strip erc-channel-topic)
5979 ?p (erc-port-to-string erc-session-port)
5980 ?s (erc-format-target-and/or-server)
5981 ?t (erc-format-target)))
5982 (process-status (cond ((and (erc-server-process-alive)
5983 (not erc-server-connected))
5984 ":connecting")
5985 ((erc-server-process-alive)
5986 "")
5987 (t
c89e5cd7
MB
5988 ": CLOSED")))
5989 (face (cond ((eq erc-header-line-face-method nil)
5990 nil)
5991 ((functionp erc-header-line-face-method)
5992 (funcall erc-header-line-face-method))
5993 (t
16ce5c2c 5994 'erc-header-line))))
597993cf
MB
5995 (cond ((featurep 'xemacs)
5996 (setq modeline-buffer-identification
5997 (list (format-spec erc-mode-line-format spec)))
5998 (setq modeline-process (list process-status)))
5999 (t
6000 (setq mode-line-buffer-identification
6001 (list (format-spec erc-mode-line-format spec)))
6002 (setq mode-line-process (list process-status))))
6003 (when (boundp 'header-line-format)
6004 (let ((header (if erc-header-line-format
6005 (format-spec erc-header-line-format spec)
6006 nil)))
6007 (cond ((null header)
6008 (setq header-line-format nil))
6009 (erc-header-line-uses-help-echo-p
6010 (let ((help-echo (with-temp-buffer
6011 (insert header)
6012 (fill-region (point-min) (point-max))
6013 (buffer-string))))
6014 (setq header-line-format
6015 (erc-replace-regexp-in-string
6016 "%"
6017 "%%"
c89e5cd7
MB
6018 (if face
6019 (erc-propertize header 'help-echo help-echo
6020 'face face)
6021 (erc-propertize header 'help-echo help-echo))))))
6904f7fe
MB
6022 (t (setq header-line-format
6023 (if face
6024 (erc-propertize header 'face face)
6025 header)))))))
597993cf
MB
6026 (if (featurep 'xemacs)
6027 (redraw-modeline)
6028 (force-mode-line-update))))
6029
6030(defun erc-update-mode-line (&optional buffer)
6031 "Update the mode line in BUFFER.
6032
6033If BUFFER is nil, update the mode line in all ERC buffers."
6034 (if (and buffer (bufferp buffer))
6035 (erc-update-mode-line-buffer buffer)
6036 (dolist (buf (erc-buffer-list))
6037 (when (buffer-live-p buf)
6038 (erc-update-mode-line-buffer buf)))))
6039
6040;; Miscellaneous
6041
6042(defun erc-port-to-string (p)
6043 "Convert port P to a string.
6044P may be an integer or a service name."
6045 (if (integerp p)
6046 (int-to-string p)
6047 p))
6048
6049(defun erc-string-to-port (s)
6050 "Convert string S to either an integer port number or a service name."
83dc6995
MB
6051 (if (numberp s)
6052 s
6053 (let ((n (string-to-number s)))
6054 (if (= n 0)
6055 s
6056 n))))
597993cf
MB
6057
6058(defun erc-version (&optional here)
6059 "Show the version number of ERC in the minibuffer.
6060If optional argument HERE is non-nil, insert version number at point."
6061 (interactive "P")
6062 (let ((version-string
67e53305 6063 (format "ERC %s (GNU Emacs %s)" erc-version-string emacs-version)))
597993cf
MB
6064 (if here
6065 (insert version-string)
6066 (if (interactive-p)
6067 (message "%s" version-string)
6068 version-string))))
6069
597993cf
MB
6070(defun erc-modes (&optional here)
6071 "Show the active ERC modes in the minibuffer.
6072If optional argument HERE is non-nil, insert version number at point."
6073 (interactive "P")
6074 (let ((string
6075 (mapconcat 'identity
6076 (let (modes (case-fold-search nil))
6077 (dolist (var (apropos-internal "^erc-.*mode$"))
6078 (when (and (boundp var)
6079 (symbol-value var))
6080 (setq modes (cons (symbol-name var)
6081 modes))))
6082 modes)
6083 ", ")))
6084 (if here
6085 (insert string)
6086 (if (interactive-p)
6087 (message "%s" string)
6088 string))))
6089
597993cf
MB
6090(defun erc-trim-string (s)
6091 "Trim leading and trailing spaces off S."
6092 (cond
6093 ((not (stringp s)) nil)
6094 ((string-match "^\\s-*$" s)
6095 "")
6096 ((string-match "^\\s-*\\(.*\\S-\\)\\s-*$" s)
6097 (match-string 1 s))
6098 (t
6099 s)))
6100
6101(defun erc-arrange-session-in-multiple-windows ()
6102 "Open a window for every non-server buffer related to `erc-session-server'.
6103
6104All windows are opened in the current frame."
6105 (interactive)
83dc6995
MB
6106 (unless erc-server-process
6107 (error "No erc-server-process found in current buffer"))
597993cf
MB
6108 (let ((bufs (erc-buffer-list nil erc-server-process)))
6109 (when bufs
6110 (delete-other-windows)
6111 (switch-to-buffer (car bufs))
6112 (setq bufs (cdr bufs))
6113 (while bufs
6114 (split-window)
b5bc193f
MB
6115 (other-window 1)
6116 (switch-to-buffer (car bufs))
597993cf
MB
6117 (setq bufs (cdr bufs))
6118 (balance-windows)))))
6119
6120(defun erc-popup-input-buffer ()
a3d2f0a3 6121 "Provide an input buffer."
597993cf
MB
6122 (interactive)
6123 (let ((buffer-name (generate-new-buffer-name "*ERC input*"))
6124 (mode (intern
6125 (completing-read
6126 "Mode: "
6127 (mapcar (lambda (e)
6128 (list (symbol-name e)))
6129 (apropos-internal "-mode$" 'commandp))
6130 nil t))))
6131 (pop-to-buffer (make-indirect-buffer (current-buffer) buffer-name))
6132 (funcall mode)
6133 (narrow-to-region (point) (point))
6134 (shrink-window-if-larger-than-buffer)))
6135
6136;;; Message catalog
6137
6138(defun erc-make-message-variable-name (catalog entry)
6139 "Create a variable name corresponding to CATALOG's ENTRY."
6140 (intern (concat "erc-message-"
6141 (symbol-name catalog) "-" (symbol-name entry))))
6142
6143(defun erc-define-catalog-entry (catalog entry format-spec)
6144 "Set CATALOG's ENTRY to FORMAT-SPEC."
6145 (set (erc-make-message-variable-name catalog entry)
6146 format-spec))
6147
6148(defun erc-define-catalog (catalog entries)
6149 "Define a CATALOG according to ENTRIES."
6150 (dolist (entry entries)
6151 (erc-define-catalog-entry catalog (car entry) (cdr entry))))
6152
6153(erc-define-catalog
6154 'english
6155 '((bad-ping-response . "Unexpected PING response from %n (time %t)")
6156 (bad-syntax . "Error occurred - incorrect usage?\n%c %u\n%d")
6157 (incorrect-args . "Incorrect arguments. Usage:\n%c %u\n%d")
6158 (cannot-find-file . "Cannot find file %f")
6159 (cannot-read-file . "Cannot read file %f")
6160 (connect . "Connecting to %S:%p... ")
6161 (country . "%c")
6162 (country-unknown . "%d: No such domain")
6163 (ctcp-empty . "Illegal empty CTCP query received from %n. Ignoring.")
6164 (ctcp-request . "==> CTCP request from %n (%u@%h): %r")
6165 (ctcp-request-to . "==> CTCP request from %n (%u@%h) to %t: %r")
6166 (ctcp-too-many . "Too many CTCP queries in single message. Ignoring")
6167 (flood-ctcp-off . "FLOOD PROTECTION: Automatic CTCP responses turned off.")
f2c05698
MB
6168 (flood-strict-mode
6169 . "FLOOD PROTECTION: Switched to Strict Flood Control mode.")
6170 (disconnected . "\n\nConnection failed! Re-establishing connection...\n")
6171 (disconnected-noreconnect
6172 . "\n\nConnection failed! Not re-establishing connection.\n")
6173 (finished . "\n\n*** ERC finished ***\n")
6174 (terminated . "\n\n*** ERC terminated: %e\n")
597993cf
MB
6175 (login . "Logging in as \'%n\'...")
6176 (nick-in-use . "%n is in use. Choose new nickname: ")
f2c05698
MB
6177 (nick-too-long
6178 . "WARNING: Nick length (%i) exceeds max NICKLEN(%l) defined by server")
597993cf
MB
6179 (no-default-channel . "No default channel")
6180 (no-invitation . "You've got no invitation")
6181 (no-target . "No target")
6182 (ops . "%i operator%s: %o")
6183 (ops-none . "No operators in this channel.")
6184 (undefined-ctcp . "Undefined CTCP query received. Silently ignored")
6185 (variable-not-bound . "Variable not bound!")
6186 (ACTION . "* %n %a")
6187 (CTCP-CLIENTINFO . "Client info for %n: %m")
6188 (CTCP-ECHO . "Echo %n: %m")
6189 (CTCP-FINGER . "Finger info for %n: %m")
6190 (CTCP-PING . "Ping time to %n is %t")
6191 (CTCP-TIME . "Time by %n is %m")
6192 (CTCP-UNKNOWN . "Unknown CTCP message from %n (%u@%h): %m")
6193 (CTCP-VERSION . "Version for %n is %m")
6194 (ERROR . "==> ERROR from %s: %c\n")
6195 (INVITE . "%n (%u@%h) invites you to channel %c")
6196 (JOIN . "%n (%u@%h) has joined channel %c")
6197 (JOIN-you . "You have joined channel %c")
6198 (KICK . "%n (%u@%h) has kicked %k off channel %c: %r")
6199 (KICK-you . "You have been kicked off channel %c by %n (%u@%h): %r")
6200 (KICK-by-you . "You have kicked %k off channel %c: %r")
6201 (MODE . "%n (%u@%h) has changed mode for %t to %m")
6202 (MODE-nick . "%n has changed mode for %t to %m")
6203 (NICK . "%n (%u@%h) is now known as %N")
6204 (NICK-you . "Your new nickname is %N")
6205 (PART . erc-message-english-PART)
6206 (PING . "PING from server (last: %s sec. ago)")
6207 (PONG . "PONG from %h (%i second%s)")
6208 (QUIT . "%n (%u@%h) has quit: %r")
6209 (TOPIC . "%n (%u@%h) has set the topic for %c: \"%T\"")
6210 (WALLOPS . "Wallops from %n: %m")
6211 (s004 . "%s %v %U %C")
6212 (s221 . "User modes for %n: %m")
6213 (s252 . "%i operator(s) online")
6214 (s253 . "%i unknown connection(s)")
6215 (s254 . "%i channels formed")
1c36df97 6216 (s275 . "%n %m")
597993cf
MB
6217 (s301 . "%n is AWAY: %r")
6218 (s303 . "Is online: %n")
6219 (s305 . "%m")
6220 (s306 . "%m")
2131c501 6221 (s307 . "%n %m")
597993cf
MB
6222 (s311 . "%n is %f (%u@%h)")
6223 (s312 . "%n is/was on server %s (%c)")
6224 (s313 . "%n is an IRC operator")
6225 (s314 . "%n was %f (%u@%h)")
6226 (s317 . "%n has been idle for %i")
6227 (s317-on-since . "%n has been idle for %i, on since %t")
6228 (s319 . "%n is on channel(s): %c")
6229 (s320 . "%n is an identified user")
6230 (s321 . "Channel Users Topic")
6231 (s322 . "%c [%u] %t")
6232 (s324 . "%c modes: %m")
6233 (s329 . "%c was created on %t")
6234 (s330 . "%n %a %i")
6235 (s331 . "No topic is set for %c")
6236 (s332 . "Topic for %c: %T")
6237 (s333 . "%c: topic set by %n, %t")
6238 (s341 . "Inviting %n to channel %c")
6239 (s352 . "%-11c %-10n %-4a %u@%h (%f)")
6240 (s353 . "Users on %c: %u")
83dc6995
MB
6241 (s367 . "Ban for %b on %c")
6242 (s367-set-by . "Ban for %b on %c set by %s on %t")
597993cf
MB
6243 (s368 . "Banlist of %c ends.")
6244 (s379 . "%c: Forwarded to %f")
6245 (s391 . "The time at %s is %t")
6246 (s401 . "%n: No such nick/channel")
6247 (s403 . "%c: No such channel")
6248 (s404 . "%c: Cannot send to channel")
6249 (s405 . "%c: You have joined too many channels")
6250 (s406 . "%n: There was no such nickname")
6251 (s412 . "No text to send")
6252 (s421 . "%c: Unknown command")
6253 (s431 . "No nickname given")
6254 (s432 . "%n is an erroneous nickname")
6255 (s442 . "%c: You're not on that channel")
6256 (s445 . "SUMMON has been disabled")
6257 (s446 . "USERS has been disabled")
6258 (s451 . "You have not registered")
6259 (s461 . "%c: not enough parameters")
6260 (s462 . "Unauthorized command (already registered)")
6261 (s463 . "Your host isn't among the privileged")
6262 (s464 . "Password incorrect")
6263 (s465 . "You are banned from this server")
6264 (s474 . "You can't join %c because you're banned (+b)")
6265 (s475 . "You must specify the correct channel key (+k) to join %c")
6266 (s481 . "Permission Denied - You're not an IRC operator")
6267 (s482 . "You need to be a channel operator of %c to do that")
6268 (s483 . "You can't kill a server!")
6269 (s484 . "Your connection is restricted!")
6270 (s485 . "You're not the original channel operator")
6271 (s491 . "No O-lines for your host")
6272 (s501 . "Unknown MODE flag")
6273 (s502 . "You can't change modes for other users")))
6274
6275(defun erc-message-english-PART (&rest args)
6276 "Format a proper PART message.
6277
6278This function is an example on what could be done with formatting
6279functions."
6280 (let ((nick (cadr (memq ?n args)))
6281 (user (cadr (memq ?u args)))
6282 (host (cadr (memq ?h args)))
6283 (channel (cadr (memq ?c args)))
6284 (reason (cadr (memq ?r args))))
6285 (if (string= nick (erc-current-nick))
6286 (format "You have left channel %s" channel)
6287 (format "%s (%s@%s) has left channel %s%s"
6288 nick user host channel
6289 (if (not (string= reason ""))
ff59d266
MB
6290 (format ": %s"
6291 (erc-replace-regexp-in-string "%" "%%" reason))
597993cf
MB
6292 "")))))
6293
6294
6295(defvar erc-current-message-catalog 'english)
6296(make-variable-buffer-local 'erc-current-message-catalog)
6297
6298(defun erc-retrieve-catalog-entry (entry &optional catalog)
6299 "Retrieve ENTRY from CATALOG.
6300
6301If CATALOG is nil, `erc-current-message-catalog' is used.
6302
6303If ENTRY is nil in CATALOG, it is retrieved from the fallback,
6304english, catalog."
6305 (unless catalog (setq catalog erc-current-message-catalog))
6306 (let ((var (erc-make-message-variable-name catalog entry)))
6307 (if (boundp var)
6308 (symbol-value var)
6309 (when (boundp (erc-make-message-variable-name 'english entry))
6310 (symbol-value (erc-make-message-variable-name 'english entry))))))
6311
6312(defun erc-format-message (msg &rest args)
6313 "Format MSG according to ARGS.
6314
6315See also `format-spec'."
6316 (when (eq (logand (length args) 1) 1) ; oddp
6317 (error "Obscure usage of this function appeared"))
6318 (let ((entry (erc-retrieve-catalog-entry msg)))
6319 (when (not entry)
6320 (error "No format spec for message %s" msg))
6321 (when (functionp entry)
6322 (setq entry (apply entry args)))
6323 (format-spec entry (apply 'format-spec-make args))))
6324
6325;;; Various hook functions
6326
6327(add-hook 'kill-buffer-hook 'erc-kill-buffer-function)
6328
6329(defcustom erc-kill-server-hook '(erc-kill-server)
6330 "*Invoked whenever a server-buffer is killed via `kill-buffer'."
6331 :group 'erc-hooks
6332 :type 'hook)
6333
6334(defcustom erc-kill-channel-hook '(erc-kill-channel)
6335 "*Invoked whenever a channel-buffer is killed via `kill-buffer'."
6336 :group 'erc-hooks
6337 :type 'hook)
6338
6339(defcustom erc-kill-buffer-hook nil
6340 "*Hook run whenever a non-server or channel buffer is killed.
6341
6342See also `kill-buffer'."
6343 :group 'erc-hooks
6344 :type 'hook)
6345
6346(defun erc-kill-buffer-function ()
6347 "Function to call when an ERC buffer is killed.
6348This function should be on `kill-buffer-hook'.
6349When the current buffer is in `erc-mode', this function will run
6350one of the following hooks:
6351`erc-kill-server-hook' if the server buffer was killed,
6352`erc-kill-channel-hook' if a channel buffer was killed,
6353or `erc-kill-buffer-hook' if any other buffer."
6354 (when (eq major-mode 'erc-mode)
6355 (erc-remove-channel-users)
6356 (cond
6357 ((eq (erc-server-buffer) (current-buffer))
6358 (run-hooks 'erc-kill-server-hook))
6359 ((erc-channel-p (erc-default-target))
6360 (run-hooks 'erc-kill-channel-hook))
6361 (t
6362 (run-hooks 'erc-kill-buffer-hook)))))
6363
6364(defun erc-kill-server ()
6365 "Sends a QUIT command to the server when the server buffer is killed.
6366This function should be on `erc-kill-server-hook'."
6367 (when (erc-server-process-alive)
6368 (setq erc-server-quitting t)
6369 (erc-server-send (format "QUIT :%s" (funcall erc-quit-reason nil)))))
6370
6371(defun erc-kill-channel ()
6372 "Sends a PART command to the server when the channel buffer is killed.
6373This function should be on `erc-kill-channel-hook'."
6374 (when (erc-server-process-alive)
6375 (let ((tgt (erc-default-target)))
6376 (erc-server-send (format "PART %s :%s" tgt
6377 (funcall erc-part-reason nil))
6378 nil tgt))))
6379
9cc8d0b6
MB
6380;;; Dealing with `erc-parsed'
6381
6904f7fe
MB
6382(defun erc-find-parsed-property ()
6383 "Find the next occurrence of the `erc-parsed' text property."
6384 (text-property-not-all (point-min) (point-max) 'erc-parsed nil))
6385
ff59d266
MB
6386(defun erc-restore-text-properties ()
6387 "Restore the property 'erc-parsed for the region."
6388 (let ((parsed-posn (erc-find-parsed-property)))
6389 (put-text-property
6390 (point-min) (point-max)
6391 'erc-parsed (when parsed-posn (erc-get-parsed-vector parsed-posn)))))
6392
9cc8d0b6
MB
6393(defun erc-get-parsed-vector (point)
6394 "Return the whole parsed vector on POINT."
6395 (get-text-property point 'erc-parsed))
6396
6397(defun erc-get-parsed-vector-nick (vect)
6398 "Return nickname in the parsed vector VECT."
6399 (let* ((untreated-nick (and vect (erc-response.sender vect)))
6400 (maybe-nick (when untreated-nick
6401 (car (split-string untreated-nick "!")))))
6402 (when (and (not (null maybe-nick))
6403 (erc-is-valid-nick-p maybe-nick))
6404 untreated-nick)))
6405
6406(defun erc-get-parsed-vector-type (vect)
6407 "Return message type in the parsed vector VECT."
6408 (and vect
6409 (erc-response.command vect)))
6410
0b6bb130
MB
6411;; Teach url.el how to open irc:// URLs with ERC.
6412;; To activate, customize `url-irc-function' to `url-irc-erc'.
6413
6414;;;###autoload
6415(defun erc-handle-irc-url (host port channel user password)
6416 "Use ERC to IRC on HOST:PORT in CHANNEL as USER with PASSWORD.
6417If ERC is already connected to HOST:PORT, simply /join CHANNEL.
6418Otherwise, connect to HOST:PORT as USER and /join CHANNEL."
6419 (let ((server-buffer
6420 (car (erc-buffer-filter
6421 (lambda ()
6422 (and (string-equal erc-session-server host)
6423 (= erc-session-port port)
ff59d266 6424 (erc-open-server-buffer-p)))))))
0b6bb130
MB
6425 (with-current-buffer (or server-buffer (current-buffer))
6426 (if (and server-buffer channel)
6427 (erc-cmd-JOIN channel)
83dc6995
MB
6428 (erc-open host port (or user (erc-compute-nick)) (erc-compute-full-name)
6429 (not server-buffer) password nil channel
6430 (when server-buffer
6431 (get-buffer-process server-buffer)))))))
0b6bb130 6432
597993cf
MB
6433(provide 'erc)
6434
6435;;; Deprecated. We might eventually stop requiring the goodies automatically.
6436;;; IMPORTANT: This require must appear _after_ the above (provide 'erc) to
6437;;; avoid a recursive require error when byte-compiling the entire package.
6438(require 'erc-goodies)
6439
6440;;; erc.el ends here
6441;;
6442;; Local Variables:
6443;; outline-regexp: ";;+"
6444;; indent-tabs-mode: t
6445;; tab-width: 8
6446;; End:
6447
6448;; arch-tag: d19587f6-627e-48c1-8d86-58595fa3eca3